Page MenuHomeDevelopment

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/DisCatSharp.ApplicationCommands/ApplicationCommandsConfiguration.cs b/DisCatSharp.ApplicationCommands/ApplicationCommandsConfiguration.cs
index 2650b8692..9384dc8fa 100644
--- a/DisCatSharp.ApplicationCommands/ApplicationCommandsConfiguration.cs
+++ b/DisCatSharp.ApplicationCommands/ApplicationCommandsConfiguration.cs
@@ -1,102 +1,103 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// A configuration for a <see cref="ApplicationCommandsExtension"/>
-/// </summary>
-public class ApplicationCommandsConfiguration
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// <para>Sets the service provider.</para>
- /// <para>Objects in this provider are used when instantiating application command modules. This allows passing data around without resorting to static members.</para>
- /// <para>Defaults to null.</para>
+ /// A configuration for a <see cref="ApplicationCommandsExtension"/>
/// </summary>
- public IServiceProvider ServiceProvider { internal get; set; }
+ public class ApplicationCommandsConfiguration
+ {
+ /// <summary>
+ /// <para>Sets the service provider.</para>
+ /// <para>Objects in this provider are used when instantiating application command modules. This allows passing data around without resorting to static members.</para>
+ /// <para>Defaults to null.</para>
+ /// </summary>
+ public IServiceProvider ServiceProvider { internal get; set; }
- /// <summary>
- /// <para>Sets whether to enable default help command.</para>
- /// <para>Disabling this will allow you to make your own help command.</para>
- /// <para>
- /// </para>
- /// <para>Defaults to true.</para>
- /// </summary>
- public bool EnableDefaultHelp { internal get; set; } = true;
+ /// <summary>
+ /// <para>Sets whether to enable default help command.</para>
+ /// <para>Disabling this will allow you to make your own help command.</para>
+ /// <para>
+ /// </para>
+ /// <para>Defaults to true.</para>
+ /// </summary>
+ public bool EnableDefaultHelp { internal get; set; } = true;
- /// <summary>
- /// Debugs the startup.
- /// </summary>
- public bool DebugStartup { internal get; set; } = false;
+ /// <summary>
+ /// Debugs the startup.
+ /// </summary>
+ public bool DebugStartup { internal get; set; } = false;
- /// <summary>
- /// Enable localization features.
- /// </summary>
- public bool EnableLocalization { internal get; set; } = false;
+ /// <summary>
+ /// Enable localization features.
+ /// </summary>
+ public bool EnableLocalization { internal get; set; } = false;
- /// <summary>
- /// Manual override.
- /// <note type="warning">DO NOT USE THIS!</note>
- /// </summary>
- public bool ManualOverride { internal get; set; } = false;
+ /// <summary>
+ /// Manual override.
+ /// <note type="warning">DO NOT USE THIS!</note>
+ /// </summary>
+ public bool ManualOverride { internal get; set; } = false;
- /// <summary>
- /// Automatically defer all responses.
- /// <note type="note">If you enable this, you can't use CreateResponse. Use EditResponse instead.</note>
- /// </summary>
- public bool AutoDefer { internal get; set; } = false;
+ /// <summary>
+ /// Automatically defer all responses.
+ /// <note type="note">If you enable this, you can't use CreateResponse. Use EditResponse instead.</note>
+ /// </summary>
+ public bool AutoDefer { internal get; set; } = false;
- /// <summary>
- /// Checks through all guilds.
- /// <note type="warning">This will take quite a while, when the bot is on more than 1k guilds.</note>
- /// </summary>
- public bool CheckAllGuilds { internal get; set; } = false;
+ /// <summary>
+ /// Checks through all guilds.
+ /// <note type="warning">This will take quite a while, when the bot is on more than 1k guilds.</note>
+ /// </summary>
+ public bool CheckAllGuilds { internal get; set; } = false;
- /// <summary>
- /// Initializes a new instance of the <see cref="ApplicationCommandsConfiguration"/> class.
- /// </summary>
- /// <param name="provider">The service provider.</param>
- [ActivatorUtilitiesConstructor]
- public ApplicationCommandsConfiguration(IServiceProvider provider = null)
- {
- this.ServiceProvider = provider;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ApplicationCommandsConfiguration"/> class.
+ /// </summary>
+ /// <param name="provider">The service provider.</param>
+ [ActivatorUtilitiesConstructor]
+ public ApplicationCommandsConfiguration(IServiceProvider provider = null)
+ {
+ this.ServiceProvider = provider;
+ }
- /// <summary>
- /// Creates a new instance of <see cref="ApplicationCommandsConfiguration"/>, copying the properties of another configuration.
- /// </summary>
- /// <param name="acc">Configuration the properties of which are to be copied.</param>
- public ApplicationCommandsConfiguration(ApplicationCommandsConfiguration acc)
- {
- this.EnableDefaultHelp = acc.EnableDefaultHelp;
- this.ServiceProvider = acc.ServiceProvider;
- this.DebugStartup = acc.DebugStartup;
- this.CheckAllGuilds = acc.CheckAllGuilds;
- this.ManualOverride = acc.ManualOverride;
- this.AutoDefer = acc.AutoDefer;
- this.EnableLocalization = acc.EnableLocalization;
+ /// <summary>
+ /// Creates a new instance of <see cref="ApplicationCommandsConfiguration"/>, copying the properties of another configuration.
+ /// </summary>
+ /// <param name="acc">Configuration the properties of which are to be copied.</param>
+ public ApplicationCommandsConfiguration(ApplicationCommandsConfiguration acc)
+ {
+ this.EnableDefaultHelp = acc.EnableDefaultHelp;
+ this.ServiceProvider = acc.ServiceProvider;
+ this.DebugStartup = acc.DebugStartup;
+ this.CheckAllGuilds = acc.CheckAllGuilds;
+ this.ManualOverride = acc.ManualOverride;
+ this.AutoDefer = acc.AutoDefer;
+ this.EnableLocalization = acc.EnableLocalization;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs b/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs
index 5b8fa1c14..59e2934ac 100644
--- a/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs
+++ b/DisCatSharp.ApplicationCommands/ApplicationCommandsExtension.cs
@@ -1,1821 +1,1822 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using DisCatSharp.ApplicationCommands.Attributes;
using DisCatSharp.ApplicationCommands.EventArgs;
using DisCatSharp.Common;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.EventArgs;
using DisCatSharp.Exceptions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// A class that handles slash commands for a client.
-/// </summary>
-public sealed class ApplicationCommandsExtension : BaseExtension
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// A list of methods for top level commands.
- /// </summary>
- private static List<CommandMethod> s_commandMethods { get; set; } = new();
-
- /// <summary>
- /// List of groups.
- /// </summary>
- private static List<GroupCommand> s_groupCommands { get; set; } = new();
-
- /// <summary>
- /// List of groups with subgroups.
- /// </summary>
- private static List<SubGroupCommand> s_subGroupCommands { get; set; } = new();
-
- /// <summary>
- /// List of context menus.
- /// </summary>
- private static List<ContextMenuCommand> s_contextMenuCommands { get; set; } = new();
-
- /// <summary>
- /// List of global commands on discords backend.
- /// </summary>
- internal static List<DiscordApplicationCommand> GlobalDiscordCommands { get; set; }
-
- /// <summary>
- /// List of guild commands on discords backend.
- /// </summary>
- internal static Dictionary<ulong, List<DiscordApplicationCommand>> GuildDiscordCommands { get; set; }
-
- /// <summary>
- /// Singleton modules.
- /// </summary>
- private static List<object> s_singletonModules { get; set; } = new();
-
- /// <summary>
- /// List of modules to register.
- /// </summary>
- private readonly List<KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>> _updateList = new();
-
- /// <summary>
- /// Configuration for Discord.
- /// </summary>
- internal static ApplicationCommandsConfiguration Configuration;
-
- /// <summary>
- /// Discord client.
- /// </summary>
- internal static DiscordClient ClientInternal;
-
- /// <summary>
- /// Set to true if anything fails when registering.
- /// </summary>
- private static bool s_errored { get; set; }
-
- /// <summary>
- /// Gets a list of registered commands. The key is the guild id (null if global).
+ /// A class that handles slash commands for a client.
/// </summary>
- public static IReadOnlyList<KeyValuePair<ulong?, IReadOnlyList<DiscordApplicationCommand>>> RegisteredCommands
- => s_registeredCommands;
- private static readonly List<KeyValuePair<ulong?, IReadOnlyList<DiscordApplicationCommand>>> s_registeredCommands = new();
-
- /// <summary>
- /// Gets a list of registered global commands.
- /// </summary>
- public static IReadOnlyList<DiscordApplicationCommand> GlobalCommands
- => GlobalCommandsInternal;
- internal static readonly List<DiscordApplicationCommand> GlobalCommandsInternal = new();
-
- /// <summary>
- /// Gets a list of registered guild commands mapped by guild id.
- /// </summary>
- public static IReadOnlyDictionary<ulong, IReadOnlyList<DiscordApplicationCommand>> GuildCommands
- => GuildCommandsInternal;
- internal static readonly Dictionary<ulong, IReadOnlyList<DiscordApplicationCommand>> GuildCommandsInternal = new();
-
- /// <summary>
- /// Gets the registration count.
- /// </summary>
- private static int s_registrationCount { get; set; }
-
- /// <summary>
- /// Gets the expected count.
- /// </summary>
- private static int s_expectedCount { get; set; }
-
- /// <summary>
- /// Gets the guild ids where the applications.commands scope is missing.
- /// </summary>
- private IReadOnlyList<ulong> _missingScopeGuildIds;
-
- /// <summary>
- /// Gets whether debug is enabled.
- /// </summary>
- internal static bool DebugEnabled { get; set; }
-
- internal static LogLevel ApplicationCommandsLogLevel
- => DebugEnabled ? LogLevel.Debug : LogLevel.Trace;
-
- /// <summary>
- /// Gets whether check through all guilds is enabled.
- /// </summary>
- internal static bool CheckAllGuilds { get; set; }
-
- /// <summary>
- /// Gets whether the registration check should be manually overriden.
- /// </summary>
- internal static bool ManOr { get; set; }
-
- /// <summary>
- /// Gets whether interactions should be automatically deffered.
- /// </summary>
- internal static bool AutoDeferEnabled { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ApplicationCommandsExtension"/> class.
- /// </summary>
- /// <param name="configuration">The configuration.</param>
- internal ApplicationCommandsExtension(ApplicationCommandsConfiguration configuration = null)
+ public sealed class ApplicationCommandsExtension : BaseExtension
{
- Configuration = configuration;
- DebugEnabled = configuration?.DebugStartup ?? false;
- CheckAllGuilds = configuration?.CheckAllGuilds ?? false;
- ManOr = configuration?.ManualOverride ?? false;
- AutoDeferEnabled = configuration?.AutoDefer ?? false;
- }
-
- /// <summary>
- /// Runs setup.
- /// <note type="caution">DO NOT RUN THIS MANUALLY. DO NOT DO ANYTHING WITH THIS.</note>
- /// </summary>
- /// <param name="client">The client to setup on.</param>
- protected internal override void Setup(DiscordClient client)
- {
- if (this.Client != null)
- throw new InvalidOperationException("What did I tell you?");
-
- this.Client = client;
- ClientInternal = client;
-
- this._slashError = new AsyncEvent<ApplicationCommandsExtension, SlashCommandErrorEventArgs>("SLASHCOMMAND_ERRORED", TimeSpan.Zero, null);
- this._slashExecuted = new AsyncEvent<ApplicationCommandsExtension, SlashCommandExecutedEventArgs>("SLASHCOMMAND_EXECUTED", TimeSpan.Zero, null);
- this._contextMenuErrored = new AsyncEvent<ApplicationCommandsExtension, ContextMenuErrorEventArgs>("CONTEXTMENU_ERRORED", TimeSpan.Zero, null);
- this._contextMenuExecuted = new AsyncEvent<ApplicationCommandsExtension, ContextMenuExecutedEventArgs>("CONTEXTMENU_EXECUTED", TimeSpan.Zero, null);
- this._applicationCommandsModuleReady = new AsyncEvent<ApplicationCommandsExtension, ApplicationCommandsModuleReadyEventArgs>("APPLICATION_COMMANDS_MODULE_READY", TimeSpan.Zero, null);
- this._applicationCommandsModuleStartupFinished = new AsyncEvent<ApplicationCommandsExtension, ApplicationCommandsModuleStartupFinishedEventArgs>("APPLICATION_COMMANDS_MODULE_STARTUP_FINISHED", TimeSpan.Zero, null);
- this._globalApplicationCommandsRegistered = new AsyncEvent<ApplicationCommandsExtension, GlobalApplicationCommandsRegisteredEventArgs>("GLOBAL_COMMANDS_REGISTERED", TimeSpan.Zero, null);
- this._guildApplicationCommandsRegistered = new AsyncEvent<ApplicationCommandsExtension, GuildApplicationCommandsRegisteredEventArgs>("GUILD_COMMANDS_REGISTERED", TimeSpan.Zero, null);
-
- this.Client.GuildDownloadCompleted += async (c, e) => await this.UpdateAsync();
- this.Client.InteractionCreated += this.CatchInteractionsOnStartup;
- this.Client.ContextMenuInteractionCreated += this.CatchContextMenuInteractionsOnStartup;
- }
+ /// <summary>
+ /// A list of methods for top level commands.
+ /// </summary>
+ private static List<CommandMethod> s_commandMethods { get; set; } = new();
+
+ /// <summary>
+ /// List of groups.
+ /// </summary>
+ private static List<GroupCommand> s_groupCommands { get; set; } = new();
+
+ /// <summary>
+ /// List of groups with subgroups.
+ /// </summary>
+ private static List<SubGroupCommand> s_subGroupCommands { get; set; } = new();
+
+ /// <summary>
+ /// List of context menus.
+ /// </summary>
+ private static List<ContextMenuCommand> s_contextMenuCommands { get; set; } = new();
+
+ /// <summary>
+ /// List of global commands on discords backend.
+ /// </summary>
+ internal static List<DiscordApplicationCommand> GlobalDiscordCommands { get; set; }
+
+ /// <summary>
+ /// List of guild commands on discords backend.
+ /// </summary>
+ internal static Dictionary<ulong, List<DiscordApplicationCommand>> GuildDiscordCommands { get; set; }
+
+ /// <summary>
+ /// Singleton modules.
+ /// </summary>
+ private static List<object> s_singletonModules { get; set; } = new();
+
+ /// <summary>
+ /// List of modules to register.
+ /// </summary>
+ private readonly List<KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>> _updateList = new();
+
+ /// <summary>
+ /// Configuration for Discord.
+ /// </summary>
+ internal static ApplicationCommandsConfiguration Configuration;
+
+ /// <summary>
+ /// Discord client.
+ /// </summary>
+ internal static DiscordClient ClientInternal;
+
+ /// <summary>
+ /// Set to true if anything fails when registering.
+ /// </summary>
+ private static bool s_errored { get; set; }
+
+ /// <summary>
+ /// Gets a list of registered commands. The key is the guild id (null if global).
+ /// </summary>
+ public static IReadOnlyList<KeyValuePair<ulong?, IReadOnlyList<DiscordApplicationCommand>>> RegisteredCommands
+ => s_registeredCommands;
+ private static readonly List<KeyValuePair<ulong?, IReadOnlyList<DiscordApplicationCommand>>> s_registeredCommands = new();
+
+ /// <summary>
+ /// Gets a list of registered global commands.
+ /// </summary>
+ public static IReadOnlyList<DiscordApplicationCommand> GlobalCommands
+ => GlobalCommandsInternal;
+ internal static readonly List<DiscordApplicationCommand> GlobalCommandsInternal = new();
+
+ /// <summary>
+ /// Gets a list of registered guild commands mapped by guild id.
+ /// </summary>
+ public static IReadOnlyDictionary<ulong, IReadOnlyList<DiscordApplicationCommand>> GuildCommands
+ => GuildCommandsInternal;
+ internal static readonly Dictionary<ulong, IReadOnlyList<DiscordApplicationCommand>> GuildCommandsInternal = new();
+
+ /// <summary>
+ /// Gets the registration count.
+ /// </summary>
+ private static int s_registrationCount { get; set; }
+
+ /// <summary>
+ /// Gets the expected count.
+ /// </summary>
+ private static int s_expectedCount { get; set; }
+
+ /// <summary>
+ /// Gets the guild ids where the applications.commands scope is missing.
+ /// </summary>
+ private IReadOnlyList<ulong> _missingScopeGuildIds;
+
+ /// <summary>
+ /// Gets whether debug is enabled.
+ /// </summary>
+ internal static bool DebugEnabled { get; set; }
+
+ internal static LogLevel ApplicationCommandsLogLevel
+ => DebugEnabled ? LogLevel.Debug : LogLevel.Trace;
+
+ /// <summary>
+ /// Gets whether check through all guilds is enabled.
+ /// </summary>
+ internal static bool CheckAllGuilds { get; set; }
+
+ /// <summary>
+ /// Gets whether the registration check should be manually overriden.
+ /// </summary>
+ internal static bool ManOr { get; set; }
+
+ /// <summary>
+ /// Gets whether interactions should be automatically deffered.
+ /// </summary>
+ internal static bool AutoDeferEnabled { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ApplicationCommandsExtension"/> class.
+ /// </summary>
+ /// <param name="configuration">The configuration.</param>
+ internal ApplicationCommandsExtension(ApplicationCommandsConfiguration configuration = null)
+ {
+ Configuration = configuration;
+ DebugEnabled = configuration?.DebugStartup ?? false;
+ CheckAllGuilds = configuration?.CheckAllGuilds ?? false;
+ ManOr = configuration?.ManualOverride ?? false;
+ AutoDeferEnabled = configuration?.AutoDefer ?? false;
+ }
- private async Task CatchInteractionsOnStartup(DiscordClient sender, InteractionCreateEventArgs e)
- => await e.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent("Attention: This application is still starting up. Application commands are unavailable for now."));
+ /// <summary>
+ /// Runs setup.
+ /// <note type="caution">DO NOT RUN THIS MANUALLY. DO NOT DO ANYTHING WITH THIS.</note>
+ /// </summary>
+ /// <param name="client">The client to setup on.</param>
+ protected internal override void Setup(DiscordClient client)
+ {
+ if (this.Client != null)
+ throw new InvalidOperationException("What did I tell you?");
+
+ this.Client = client;
+ ClientInternal = client;
+
+ this._slashError = new AsyncEvent<ApplicationCommandsExtension, SlashCommandErrorEventArgs>("SLASHCOMMAND_ERRORED", TimeSpan.Zero, null);
+ this._slashExecuted = new AsyncEvent<ApplicationCommandsExtension, SlashCommandExecutedEventArgs>("SLASHCOMMAND_EXECUTED", TimeSpan.Zero, null);
+ this._contextMenuErrored = new AsyncEvent<ApplicationCommandsExtension, ContextMenuErrorEventArgs>("CONTEXTMENU_ERRORED", TimeSpan.Zero, null);
+ this._contextMenuExecuted = new AsyncEvent<ApplicationCommandsExtension, ContextMenuExecutedEventArgs>("CONTEXTMENU_EXECUTED", TimeSpan.Zero, null);
+ this._applicationCommandsModuleReady = new AsyncEvent<ApplicationCommandsExtension, ApplicationCommandsModuleReadyEventArgs>("APPLICATION_COMMANDS_MODULE_READY", TimeSpan.Zero, null);
+ this._applicationCommandsModuleStartupFinished = new AsyncEvent<ApplicationCommandsExtension, ApplicationCommandsModuleStartupFinishedEventArgs>("APPLICATION_COMMANDS_MODULE_STARTUP_FINISHED", TimeSpan.Zero, null);
+ this._globalApplicationCommandsRegistered = new AsyncEvent<ApplicationCommandsExtension, GlobalApplicationCommandsRegisteredEventArgs>("GLOBAL_COMMANDS_REGISTERED", TimeSpan.Zero, null);
+ this._guildApplicationCommandsRegistered = new AsyncEvent<ApplicationCommandsExtension, GuildApplicationCommandsRegisteredEventArgs>("GUILD_COMMANDS_REGISTERED", TimeSpan.Zero, null);
+
+ this.Client.GuildDownloadCompleted += async (c, e) => await this.UpdateAsync();
+ this.Client.InteractionCreated += this.CatchInteractionsOnStartup;
+ this.Client.ContextMenuInteractionCreated += this.CatchContextMenuInteractionsOnStartup;
+ }
- private async Task CatchContextMenuInteractionsOnStartup(DiscordClient sender, ContextMenuInteractionCreateEventArgs e)
- => await e.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent("Attention: This application is still starting up. Context menu commands are unavailable for now."));
+ private async Task CatchInteractionsOnStartup(DiscordClient sender, InteractionCreateEventArgs e)
+ => await e.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent("Attention: This application is still starting up. Application commands are unavailable for now."));
- private void FinishedRegistration()
- {
- this.Client.InteractionCreated -= this.CatchInteractionsOnStartup;
- this.Client.ContextMenuInteractionCreated -= this.CatchContextMenuInteractionsOnStartup;
+ private async Task CatchContextMenuInteractionsOnStartup(DiscordClient sender, ContextMenuInteractionCreateEventArgs e)
+ => await e.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent("Attention: This application is still starting up. Context menu commands are unavailable for now."));
- this.Client.InteractionCreated += this.InteractionHandler;
- this.Client.ContextMenuInteractionCreated += this.ContextMenuHandler;
- }
- /// <summary>
- /// Cleans the module for a new start of the bot.
- /// DO NOT USE IF YOU DON'T KNOW WHAT IT DOES.
- /// </summary>
- public void CleanModule()
- {
- this._updateList.Clear();
- s_singletonModules.Clear();
- s_errored = false;
- s_expectedCount = 0;
- s_registrationCount = 0;
- s_commandMethods.Clear();
- s_groupCommands.Clear();
- s_contextMenuCommands.Clear();
- s_subGroupCommands.Clear();
- s_singletonModules.Clear();
- s_registeredCommands.Clear();
- GlobalCommandsInternal.Clear();
- GuildCommandsInternal.Clear();
- }
+ private void FinishedRegistration()
+ {
+ this.Client.InteractionCreated -= this.CatchInteractionsOnStartup;
+ this.Client.ContextMenuInteractionCreated -= this.CatchContextMenuInteractionsOnStartup;
- /// <summary>
- /// Registers a command class.
- /// </summary>
- /// <typeparam name="T">The command class to register.</typeparam>
- public void RegisterGlobalCommands<T>() where T : ApplicationCommandsModule
- {
- if (this.Client.ShardId == 0)
- this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(null, new ApplicationCommandsModuleConfiguration(typeof(T))));
- }
- /// <summary>
- /// Registers a command class.
- /// </summary>
- /// <param name="type">The <see cref="System.Type"/> of the command class to register.</param>
- public void RegisterGlobalCommands(Type type)
- {
- if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type))
- throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type));
- //If sharding, only register for shard 0
- if (this.Client.ShardId == 0)
- this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(null, new ApplicationCommandsModuleConfiguration(type)));
- }
+ this.Client.InteractionCreated += this.InteractionHandler;
+ this.Client.ContextMenuInteractionCreated += this.ContextMenuHandler;
+ }
+ /// <summary>
+ /// Cleans the module for a new start of the bot.
+ /// DO NOT USE IF YOU DON'T KNOW WHAT IT DOES.
+ /// </summary>
+ public void CleanModule()
+ {
+ this._updateList.Clear();
+ s_singletonModules.Clear();
+ s_errored = false;
+ s_expectedCount = 0;
+ s_registrationCount = 0;
+ s_commandMethods.Clear();
+ s_groupCommands.Clear();
+ s_contextMenuCommands.Clear();
+ s_subGroupCommands.Clear();
+ s_singletonModules.Clear();
+ s_registeredCommands.Clear();
+ GlobalCommandsInternal.Clear();
+ GuildCommandsInternal.Clear();
+ }
- /// <summary>
- /// Cleans all guild application commands.
- /// <note type="caution">You normally don't need to execute it.</note>
- /// </summary>
- public async Task CleanGuildCommandsAsync()
- {
- foreach (var guild in this.Client.Guilds.Values)
+ /// <summary>
+ /// Registers a command class.
+ /// </summary>
+ /// <typeparam name="T">The command class to register.</typeparam>
+ public void RegisterGlobalCommands<T>() where T : ApplicationCommandsModule
{
- await this.Client.BulkOverwriteGuildApplicationCommandsAsync(guild.Id, Array.Empty<DiscordApplicationCommand>());
+ if (this.Client.ShardId == 0)
+ this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(null, new ApplicationCommandsModuleConfiguration(typeof(T))));
+ }
+ /// <summary>
+ /// Registers a command class.
+ /// </summary>
+ /// <param name="type">The <see cref="System.Type"/> of the command class to register.</param>
+ public void RegisterGlobalCommands(Type type)
+ {
+ if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type))
+ throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type));
+ //If sharding, only register for shard 0
+ if (this.Client.ShardId == 0)
+ this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(null, new ApplicationCommandsModuleConfiguration(type)));
}
- }
- /// <summary>
- /// Cleans the global application commands.
- /// <note type="caution">You normally don't need to execute it.</note>
- /// </summary>
- public async Task CleanGlobalCommandsAsync()
- => await this.Client.BulkOverwriteGlobalApplicationCommandsAsync(Array.Empty<DiscordApplicationCommand>());
+ /// <summary>
+ /// Cleans all guild application commands.
+ /// <note type="caution">You normally don't need to execute it.</note>
+ /// </summary>
+ public async Task CleanGuildCommandsAsync()
+ {
+ foreach (var guild in this.Client.Guilds.Values)
+ {
+ await this.Client.BulkOverwriteGuildApplicationCommandsAsync(guild.Id, Array.Empty<DiscordApplicationCommand>());
+ }
+ }
- /// <summary>
- /// Registers a command class with permission and translation setup.
- /// </summary>
- /// <typeparam name="T">The command class to register.</typeparam>
- /// <param name="guildId">The guild id to register it on.</param>
- /// <param name="translationSetup">A callback to setup translations with.</param>
- public void RegisterGuildCommands<T>(ulong guildId, Action<ApplicationCommandsTranslationContext> translationSetup = null) where T : ApplicationCommandsModule
- {
- if (this.Client.ShardId == 0)
- this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(guildId, new ApplicationCommandsModuleConfiguration(typeof(T), translationSetup)));
- }
+ /// <summary>
+ /// Cleans the global application commands.
+ /// <note type="caution">You normally don't need to execute it.</note>
+ /// </summary>
+ public async Task CleanGlobalCommandsAsync()
+ => await this.Client.BulkOverwriteGlobalApplicationCommandsAsync(Array.Empty<DiscordApplicationCommand>());
+
+ /// <summary>
+ /// Registers a command class with permission and translation setup.
+ /// </summary>
+ /// <typeparam name="T">The command class to register.</typeparam>
+ /// <param name="guildId">The guild id to register it on.</param>
+ /// <param name="translationSetup">A callback to setup translations with.</param>
+ public void RegisterGuildCommands<T>(ulong guildId, Action<ApplicationCommandsTranslationContext> translationSetup = null) where T : ApplicationCommandsModule
+ {
+ if (this.Client.ShardId == 0)
+ this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(guildId, new ApplicationCommandsModuleConfiguration(typeof(T), translationSetup)));
+ }
- /// <summary>
- /// Registers a command class with permission and translation setup.
- /// </summary>
- /// <param name="type">The <see cref="System.Type"/> of the command class to register.</param>
- /// <param name="guildId">The guild id to register it on.</param>
- /// <param name="translationSetup">A callback to setup translations with.</param>
- public void RegisterGuildCommands(Type type, ulong guildId, Action<ApplicationCommandsTranslationContext> translationSetup = null)
- {
- if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type))
- throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type));
- //If sharding, only register for shard 0
- if (this.Client.ShardId == 0)
- this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(guildId, new ApplicationCommandsModuleConfiguration(type, translationSetup)));
- }
+ /// <summary>
+ /// Registers a command class with permission and translation setup.
+ /// </summary>
+ /// <param name="type">The <see cref="System.Type"/> of the command class to register.</param>
+ /// <param name="guildId">The guild id to register it on.</param>
+ /// <param name="translationSetup">A callback to setup translations with.</param>
+ public void RegisterGuildCommands(Type type, ulong guildId, Action<ApplicationCommandsTranslationContext> translationSetup = null)
+ {
+ if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type))
+ throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type));
+ //If sharding, only register for shard 0
+ if (this.Client.ShardId == 0)
+ this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(guildId, new ApplicationCommandsModuleConfiguration(type, translationSetup)));
+ }
- /// <summary>
- /// Registers a command class with permission setup but without a guild id.
- /// </summary>
- /// <typeparam name="T">The command class to register.</typeparam>
- /// <param name="translationSetup">A callback to setup translations with.</param>
- public void RegisterGlobalCommands<T>(Action<ApplicationCommandsTranslationContext> translationSetup = null) where T : ApplicationCommandsModule
+ /// <summary>
+ /// Registers a command class with permission setup but without a guild id.
+ /// </summary>
+ /// <typeparam name="T">The command class to register.</typeparam>
+ /// <param name="translationSetup">A callback to setup translations with.</param>
+ public void RegisterGlobalCommands<T>(Action<ApplicationCommandsTranslationContext> translationSetup = null) where T : ApplicationCommandsModule
{
if (this.Client.ShardId == 0)
this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(null, new ApplicationCommandsModuleConfiguration(typeof(T), translationSetup)));
}
- /// <summary>
- /// Registers a command class with permission setup but without a guild id.
- /// </summary>
- /// <param name="type">The <see cref="System.Type"/> of the command class to register.</param>
- /// <param name="translationSetup">A callback to setup translations with.</param>
- public void RegisterGlobalCommands(Type type, Action<ApplicationCommandsTranslationContext> translationSetup = null)
+ /// <summary>
+ /// Registers a command class with permission setup but without a guild id.
+ /// </summary>
+ /// <param name="type">The <see cref="System.Type"/> of the command class to register.</param>
+ /// <param name="translationSetup">A callback to setup translations with.</param>
+ public void RegisterGlobalCommands(Type type, Action<ApplicationCommandsTranslationContext> translationSetup = null)
{
if (!typeof(ApplicationCommandsModule).IsAssignableFrom(type))
throw new ArgumentException("Command classes have to inherit from ApplicationCommandsModule", nameof(type));
//If sharding, only register for shard 0
if (this.Client.ShardId == 0)
this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>(null, new ApplicationCommandsModuleConfiguration(type, translationSetup)));
}
- /// <summary>
- /// Fired when the application commands module is ready.
- /// </summary>
- public event AsyncEventHandler<ApplicationCommandsExtension, ApplicationCommandsModuleReadyEventArgs> ApplicationCommandsModuleReady
- {
- add => this._applicationCommandsModuleReady.Register(value);
- remove => this._applicationCommandsModuleReady.Unregister(value);
- }
- private AsyncEvent<ApplicationCommandsExtension, ApplicationCommandsModuleReadyEventArgs> _applicationCommandsModuleReady;
+ /// <summary>
+ /// Fired when the application commands module is ready.
+ /// </summary>
+ public event AsyncEventHandler<ApplicationCommandsExtension, ApplicationCommandsModuleReadyEventArgs> ApplicationCommandsModuleReady
+ {
+ add => this._applicationCommandsModuleReady.Register(value);
+ remove => this._applicationCommandsModuleReady.Unregister(value);
+ }
+ private AsyncEvent<ApplicationCommandsExtension, ApplicationCommandsModuleReadyEventArgs> _applicationCommandsModuleReady;
- /// <summary>
- /// Fired when the application commands modules startup is finished.
- /// </summary>
- public event AsyncEventHandler<ApplicationCommandsExtension, ApplicationCommandsModuleStartupFinishedEventArgs> ApplicationCommandsModuleStartupFinished
- {
- add => this._applicationCommandsModuleStartupFinished.Register(value);
- remove => this._applicationCommandsModuleStartupFinished.Unregister(value);
- }
- private AsyncEvent<ApplicationCommandsExtension, ApplicationCommandsModuleStartupFinishedEventArgs> _applicationCommandsModuleStartupFinished;
+ /// <summary>
+ /// Fired when the application commands modules startup is finished.
+ /// </summary>
+ public event AsyncEventHandler<ApplicationCommandsExtension, ApplicationCommandsModuleStartupFinishedEventArgs> ApplicationCommandsModuleStartupFinished
+ {
+ add => this._applicationCommandsModuleStartupFinished.Register(value);
+ remove => this._applicationCommandsModuleStartupFinished.Unregister(value);
+ }
+ private AsyncEvent<ApplicationCommandsExtension, ApplicationCommandsModuleStartupFinishedEventArgs> _applicationCommandsModuleStartupFinished;
- /// <summary>
- /// Fired when guild commands are registered on a guild.
- /// </summary>
- public event AsyncEventHandler<ApplicationCommandsExtension, GuildApplicationCommandsRegisteredEventArgs> GuildApplicationCommandsRegistered
- {
- add => this._guildApplicationCommandsRegistered.Register(value);
- remove => this._guildApplicationCommandsRegistered.Unregister(value);
- }
- private AsyncEvent<ApplicationCommandsExtension, GuildApplicationCommandsRegisteredEventArgs> _guildApplicationCommandsRegistered;
+ /// <summary>
+ /// Fired when guild commands are registered on a guild.
+ /// </summary>
+ public event AsyncEventHandler<ApplicationCommandsExtension, GuildApplicationCommandsRegisteredEventArgs> GuildApplicationCommandsRegistered
+ {
+ add => this._guildApplicationCommandsRegistered.Register(value);
+ remove => this._guildApplicationCommandsRegistered.Unregister(value);
+ }
+ private AsyncEvent<ApplicationCommandsExtension, GuildApplicationCommandsRegisteredEventArgs> _guildApplicationCommandsRegistered;
- /// <summary>
- /// Fired when the global commands are registered.
- /// </summary>
- public event AsyncEventHandler<ApplicationCommandsExtension, GlobalApplicationCommandsRegisteredEventArgs> GlobalApplicationCommandsRegistered
- {
- add => this._globalApplicationCommandsRegistered.Register(value);
- remove => this._globalApplicationCommandsRegistered.Unregister(value);
- }
- private AsyncEvent<ApplicationCommandsExtension, GlobalApplicationCommandsRegisteredEventArgs> _globalApplicationCommandsRegistered;
+ /// <summary>
+ /// Fired when the global commands are registered.
+ /// </summary>
+ public event AsyncEventHandler<ApplicationCommandsExtension, GlobalApplicationCommandsRegisteredEventArgs> GlobalApplicationCommandsRegistered
+ {
+ add => this._globalApplicationCommandsRegistered.Register(value);
+ remove => this._globalApplicationCommandsRegistered.Unregister(value);
+ }
+ private AsyncEvent<ApplicationCommandsExtension, GlobalApplicationCommandsRegisteredEventArgs> _globalApplicationCommandsRegistered;
- /// <summary>
- /// Used for RegisterCommands and the <see cref="DisCatSharp.DiscordClient.GuildDownloadCompleted"/> event.
- /// </summary>
- internal async Task UpdateAsync()
- {
- //Only update for shard 0
- if (this.Client.ShardId == 0)
+ /// <summary>
+ /// Used for RegisterCommands and the <see cref="DisCatSharp.DiscordClient.GuildDownloadCompleted"/> event.
+ /// </summary>
+ internal async Task UpdateAsync()
{
- GlobalDiscordCommands = new();
- GuildDiscordCommands = new();
+ //Only update for shard 0
+ if (this.Client.ShardId == 0)
+ {
+ GlobalDiscordCommands = new();
+ GuildDiscordCommands = new();
- var commandsPending = this._updateList.Select(x => x.Key).Distinct();
- s_expectedCount = commandsPending.Count();
+ var commandsPending = this._updateList.Select(x => x.Key).Distinct();
+ s_expectedCount = commandsPending.Count();
- this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Expected Count: {s_expectedCount}");
+ this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Expected Count: {s_expectedCount}");
- List<ulong> failedGuilds = new();
- IEnumerable<DiscordApplicationCommand> globalCommands = null;
- globalCommands = await this.Client.GetGlobalApplicationCommandsAsync(Configuration?.EnableLocalization ?? false) ?? null;
- var guilds = CheckAllGuilds ? this.Client.Guilds?.Keys : this._updateList.Select(x => x.Key)?.Distinct().Where(x => x != null)?.Select(x => x.Value);
+ List<ulong> failedGuilds = new();
+ IEnumerable<DiscordApplicationCommand> globalCommands = null;
+ globalCommands = await this.Client.GetGlobalApplicationCommandsAsync(Configuration?.EnableLocalization ?? false) ?? null;
+ var guilds = CheckAllGuilds ? this.Client.Guilds?.Keys : this._updateList.Select(x => x.Key)?.Distinct().Where(x => x != null)?.Select(x => x.Value);
- foreach (var guild in guilds)
- {
- IEnumerable<DiscordApplicationCommand> commands = null;
- var unauthorized = false;
- try
+ foreach (var guild in guilds)
{
- commands = await this.Client.GetGuildApplicationCommandsAsync(guild, Configuration?.EnableLocalization ?? false) ?? null;
+ IEnumerable<DiscordApplicationCommand> commands = null;
+ var unauthorized = false;
+ try
+ {
+ commands = await this.Client.GetGuildApplicationCommandsAsync(guild, Configuration?.EnableLocalization ?? false) ?? null;
+ }
+ catch (UnauthorizedException)
+ {
+ unauthorized = true;
+ }
+ finally
+ {
+ if (!unauthorized && commands != null && commands.Any())
+ GuildDiscordCommands.Add(guild, commands.ToList());
+ else if (!unauthorized)
+ GuildDiscordCommands.Add(guild, null);
+ else
+ failedGuilds.Add(guild);
+ }
}
- catch (UnauthorizedException)
+
+ //Default should be to add the help and slash commands can be added without setting any configuration
+ //so this should still add the default help
+ if (Configuration is null || (Configuration is not null && Configuration.EnableDefaultHelp))
{
- unauthorized = true;
+ this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>
+ (null, new ApplicationCommandsModuleConfiguration(typeof(DefaultHelpModule))));
+ commandsPending = this._updateList.Select(x => x.Key).Distinct();
}
- finally
+
+ if (globalCommands != null && globalCommands.Any())
+ GlobalDiscordCommands.AddRange(globalCommands);
+
+ foreach (var key in commandsPending.ToList())
{
- if (!unauthorized && commands != null && commands.Any())
- GuildDiscordCommands.Add(guild, commands.ToList());
- else if (!unauthorized)
- GuildDiscordCommands.Add(guild, null);
- else
- failedGuilds.Add(guild);
+ this.Client.Logger.LogInformation(key.HasValue ? $"Registering commands in guild {key.Value}" : "Registering global commands.");
+ await this.RegisterCommands(this._updateList.Where(x => x.Key == key).Select(x => x.Value), key);
}
- }
- //Default should be to add the help and slash commands can be added without setting any configuration
- //so this should still add the default help
- if (Configuration is null || (Configuration is not null && Configuration.EnableDefaultHelp))
- {
- this._updateList.Add(new KeyValuePair<ulong?, ApplicationCommandsModuleConfiguration>
- (null, new ApplicationCommandsModuleConfiguration(typeof(DefaultHelpModule))));
- commandsPending = this._updateList.Select(x => x.Key).Distinct();
- }
+ this._missingScopeGuildIds = failedGuilds;
- if (globalCommands != null && globalCommands.Any())
- GlobalDiscordCommands.AddRange(globalCommands);
-
- foreach (var key in commandsPending.ToList())
- {
- this.Client.Logger.LogInformation(key.HasValue ? $"Registering commands in guild {key.Value}" : "Registering global commands.");
- await this.RegisterCommands(this._updateList.Where(x => x.Key == key).Select(x => x.Value), key);
+ await this._applicationCommandsModuleReady.InvokeAsync(this, new ApplicationCommandsModuleReadyEventArgs(Configuration?.ServiceProvider)
+ {
+ Handled = true,
+ GuildsWithoutScope = failedGuilds
+ });
}
-
- this._missingScopeGuildIds = failedGuilds;
-
- await this._applicationCommandsModuleReady.InvokeAsync(this, new ApplicationCommandsModuleReadyEventArgs(Configuration?.ServiceProvider)
- {
- Handled = true,
- GuildsWithoutScope = failedGuilds
- });
}
- }
- /// <summary>
- /// Method for registering commands for a target from modules.
- /// </summary>
- /// <param name="types">The types.</param>
- /// <param name="guildId">The optional guild id.</param>
- private async Task RegisterCommands(IEnumerable<ApplicationCommandsModuleConfiguration> types, ulong? guildId)
- {
- //Initialize empty lists to be added to the global ones at the end
- var commandMethods = new List<CommandMethod>();
- var groupCommands = new List<GroupCommand>();
- var subGroupCommands = new List<SubGroupCommand>();
- var contextMenuCommands = new List<ContextMenuCommand>();
- var updateList = new List<DiscordApplicationCommand>();
-
- var commandTypeSources = new List<KeyValuePair<Type, Type>>();
-
- //Iterates over all the modules
- foreach (var config in types)
+ /// <summary>
+ /// Method for registering commands for a target from modules.
+ /// </summary>
+ /// <param name="types">The types.</param>
+ /// <param name="guildId">The optional guild id.</param>
+ private async Task RegisterCommands(IEnumerable<ApplicationCommandsModuleConfiguration> types, ulong? guildId)
{
- var type = config.Type;
- try
- {
- var module = type.GetTypeInfo();
- var classes = new List<TypeInfo>();
+ //Initialize empty lists to be added to the global ones at the end
+ var commandMethods = new List<CommandMethod>();
+ var groupCommands = new List<GroupCommand>();
+ var subGroupCommands = new List<SubGroupCommand>();
+ var contextMenuCommands = new List<ContextMenuCommand>();
+ var updateList = new List<DiscordApplicationCommand>();
- var ctx = new ApplicationCommandsTranslationContext(type, module.FullName);
- config.Translations?.Invoke(ctx);
+ var commandTypeSources = new List<KeyValuePair<Type, Type>>();
- //Add module to classes list if it's a group
- if (module.GetCustomAttribute<SlashCommandGroupAttribute>() != null)
- {
- classes.Add(module);
- }
- else
+ //Iterates over all the modules
+ foreach (var config in types)
+ {
+ var type = config.Type;
+ try
{
- //Otherwise add the nested groups
- classes = module.DeclaredNestedTypes.Where(x => x.GetCustomAttribute<SlashCommandGroupAttribute>() != null).ToList();
- }
+ var module = type.GetTypeInfo();
+ var classes = new List<TypeInfo>();
- List<GroupTranslator> groupTranslations = null;
+ var ctx = new ApplicationCommandsTranslationContext(type, module.FullName);
+ config.Translations?.Invoke(ctx);
- if (!string.IsNullOrEmpty(ctx.Translations))
- {
- groupTranslations = JsonConvert.DeserializeObject<List<GroupTranslator>>(ctx.Translations);
- }
+ //Add module to classes list if it's a group
+ if (module.GetCustomAttribute<SlashCommandGroupAttribute>() != null)
+ {
+ classes.Add(module);
+ }
+ else
+ {
+ //Otherwise add the nested groups
+ classes = module.DeclaredNestedTypes.Where(x => x.GetCustomAttribute<SlashCommandGroupAttribute>() != null).ToList();
+ }
- var slashGroupsTuple = NestedCommandWorker.ParseSlashGroupsAsync(type, classes, guildId, groupTranslations).Result;
+ List<GroupTranslator> groupTranslations = null;
- if (slashGroupsTuple.applicationCommands != null && slashGroupsTuple.applicationCommands.Any())
- updateList.AddRange(slashGroupsTuple.applicationCommands);
+ if (!string.IsNullOrEmpty(ctx.Translations))
+ {
+ groupTranslations = JsonConvert.DeserializeObject<List<GroupTranslator>>(ctx.Translations);
+ }
- if (slashGroupsTuple.commandTypeSources != null && slashGroupsTuple.commandTypeSources.Any())
- commandTypeSources.AddRange(slashGroupsTuple.commandTypeSources);
+ var slashGroupsTuple = NestedCommandWorker.ParseSlashGroupsAsync(type, classes, guildId, groupTranslations).Result;
- if (slashGroupsTuple.singletonModules != null && slashGroupsTuple.singletonModules.Any())
- s_singletonModules.AddRange(slashGroupsTuple.singletonModules);
+ if (slashGroupsTuple.applicationCommands != null && slashGroupsTuple.applicationCommands.Any())
+ updateList.AddRange(slashGroupsTuple.applicationCommands);
- if (slashGroupsTuple.groupCommands != null && slashGroupsTuple.groupCommands.Any())
- groupCommands.AddRange(slashGroupsTuple.groupCommands);
+ if (slashGroupsTuple.commandTypeSources != null && slashGroupsTuple.commandTypeSources.Any())
+ commandTypeSources.AddRange(slashGroupsTuple.commandTypeSources);
- if (slashGroupsTuple.subGroupCommands != null && slashGroupsTuple.subGroupCommands.Any())
- subGroupCommands.AddRange(slashGroupsTuple.subGroupCommands);
+ if (slashGroupsTuple.singletonModules != null && slashGroupsTuple.singletonModules.Any())
+ s_singletonModules.AddRange(slashGroupsTuple.singletonModules);
- //Handles methods and context menus, only if the module isn't a group itself
- if (module.GetCustomAttribute<SlashCommandGroupAttribute>() == null)
- {
- List<CommandTranslator> commandTranslations = null;
+ if (slashGroupsTuple.groupCommands != null && slashGroupsTuple.groupCommands.Any())
+ groupCommands.AddRange(slashGroupsTuple.groupCommands);
- if (!string.IsNullOrEmpty(ctx.Translations))
+ if (slashGroupsTuple.subGroupCommands != null && slashGroupsTuple.subGroupCommands.Any())
+ subGroupCommands.AddRange(slashGroupsTuple.subGroupCommands);
+
+ //Handles methods and context menus, only if the module isn't a group itself
+ if (module.GetCustomAttribute<SlashCommandGroupAttribute>() == null)
{
- commandTranslations = JsonConvert.DeserializeObject<List<CommandTranslator>>(ctx.Translations);
- }
+ List<CommandTranslator> commandTranslations = null;
+
+ if (!string.IsNullOrEmpty(ctx.Translations))
+ {
+ commandTranslations = JsonConvert.DeserializeObject<List<CommandTranslator>>(ctx.Translations);
+ }
- //Slash commands
- var methods = module.DeclaredMethods.Where(x => x.GetCustomAttribute<SlashCommandAttribute>() != null);
+ //Slash commands
+ var methods = module.DeclaredMethods.Where(x => x.GetCustomAttribute<SlashCommandAttribute>() != null);
- var slashCommands = CommandWorker.ParseBasicSlashCommandsAsync(type, methods, guildId, commandTranslations).Result;
+ var slashCommands = CommandWorker.ParseBasicSlashCommandsAsync(type, methods, guildId, commandTranslations).Result;
- if (slashCommands.applicationCommands != null && slashCommands.applicationCommands.Any())
- updateList.AddRange(slashCommands.applicationCommands);
+ if (slashCommands.applicationCommands != null && slashCommands.applicationCommands.Any())
+ updateList.AddRange(slashCommands.applicationCommands);
- if (slashCommands.commandTypeSources != null && slashCommands.commandTypeSources.Any())
- commandTypeSources.AddRange(slashCommands.commandTypeSources);
+ if (slashCommands.commandTypeSources != null && slashCommands.commandTypeSources.Any())
+ commandTypeSources.AddRange(slashCommands.commandTypeSources);
- if (slashCommands.commandMethods != null && slashCommands.commandMethods.Any())
- commandMethods.AddRange(slashCommands.commandMethods);
+ if (slashCommands.commandMethods != null && slashCommands.commandMethods.Any())
+ commandMethods.AddRange(slashCommands.commandMethods);
- //Context Menus
- var contextMethods = module.DeclaredMethods.Where(x => x.GetCustomAttribute<ContextMenuAttribute>() != null);
+ //Context Menus
+ var contextMethods = module.DeclaredMethods.Where(x => x.GetCustomAttribute<ContextMenuAttribute>() != null);
- var contextCommands = CommandWorker.ParseContextMenuCommands(type, contextMethods, commandTranslations).Result;
+ var contextCommands = CommandWorker.ParseContextMenuCommands(type, contextMethods, commandTranslations).Result;
- if (contextCommands.applicationCommands != null && contextCommands.applicationCommands.Any())
- updateList.AddRange(contextCommands.applicationCommands);
+ if (contextCommands.applicationCommands != null && contextCommands.applicationCommands.Any())
+ updateList.AddRange(contextCommands.applicationCommands);
- if (contextCommands.commandTypeSources != null && contextCommands.commandTypeSources.Any())
- commandTypeSources.AddRange(contextCommands.commandTypeSources);
+ if (contextCommands.commandTypeSources != null && contextCommands.commandTypeSources.Any())
+ commandTypeSources.AddRange(contextCommands.commandTypeSources);
- if (contextCommands.contextMenuCommands != null && contextCommands.contextMenuCommands.Any())
- contextMenuCommands.AddRange(contextCommands.contextMenuCommands);
+ if (contextCommands.contextMenuCommands != null && contextCommands.contextMenuCommands.Any())
+ contextMenuCommands.AddRange(contextCommands.contextMenuCommands);
- //Accounts for lifespans
- if (module.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>() != null && module.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>().Lifespan == ApplicationCommandModuleLifespan.Singleton)
- {
- s_singletonModules.Add(CreateInstance(module, Configuration?.ServiceProvider));
+ //Accounts for lifespans
+ if (module.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>() != null && module.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>().Lifespan == ApplicationCommandModuleLifespan.Singleton)
+ {
+ s_singletonModules.Add(CreateInstance(module, Configuration?.ServiceProvider));
+ }
}
}
+ catch (Exception ex)
+ {
+ if (ex is BadRequestException brex)
+ this.Client.Logger.LogCritical(brex, $"There was an error registering application commands: {brex.JsonMessage}");
+ else
+ this.Client.Logger.LogCritical(ex, $"There was an error parsing the application commands");
+ s_errored = true;
+ }
}
- catch (Exception ex)
- {
- if (ex is BadRequestException brex)
- this.Client.Logger.LogCritical(brex, $"There was an error registering application commands: {brex.JsonMessage}");
- else
- this.Client.Logger.LogCritical(ex, $"There was an error parsing the application commands");
- s_errored = true;
- }
- }
- if (!s_errored)
- {
- try
+ if (!s_errored)
{
- List<DiscordApplicationCommand> commands = new();
-
try
{
- if (guildId == null)
+ List<DiscordApplicationCommand> commands = new();
+
+ try
{
- if (updateList != null && updateList.Any())
+ if (guildId == null)
{
- var regCommands = RegistrationWorker.RegisterGlobalCommandsAsync(updateList).Result;
- var actualCommands = regCommands.Distinct().ToList();
- commands.AddRange(actualCommands);
- GlobalCommandsInternal.AddRange(actualCommands);
- }
- else
- {
- foreach (var cmd in GlobalDiscordCommands)
+ if (updateList != null && updateList.Any())
{
- try
- {
- await this.Client.DeleteGlobalApplicationCommandAsync(cmd.Id);
- }
- catch (NotFoundException)
- {
- this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Could not delete global command {cmd.Id}. Please clean up manually");
- }
+ var regCommands = RegistrationWorker.RegisterGlobalCommandsAsync(updateList).Result;
+ var actualCommands = regCommands.Distinct().ToList();
+ commands.AddRange(actualCommands);
+ GlobalCommandsInternal.AddRange(actualCommands);
}
- }
- }
- else
- {
- if (updateList != null && updateList.Any())
- {
- var regCommands = RegistrationWorker.RegisterGuilldCommandsAsync(guildId.Value, updateList).Result;
- var actualCommands = regCommands.Distinct().ToList();
- commands.AddRange(actualCommands);
- GuildCommandsInternal.Add(guildId.Value, actualCommands);
- if (this.Client.Guilds.TryGetValue(guildId.Value, out var guild))
+ else
{
- guild.InternalRegisteredApplicationCommands = new();
- guild.InternalRegisteredApplicationCommands.AddRange(actualCommands);
+ foreach (var cmd in GlobalDiscordCommands)
+ {
+ try
+ {
+ await this.Client.DeleteGlobalApplicationCommandAsync(cmd.Id);
+ }
+ catch (NotFoundException)
+ {
+ this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Could not delete global command {cmd.Id}. Please clean up manually");
+ }
+ }
}
-
}
else
{
- foreach (var cmd in GuildDiscordCommands[guildId.Value])
+ if (updateList != null && updateList.Any())
{
- try
+ var regCommands = RegistrationWorker.RegisterGuilldCommandsAsync(guildId.Value, updateList).Result;
+ var actualCommands = regCommands.Distinct().ToList();
+ commands.AddRange(actualCommands);
+ GuildCommandsInternal.Add(guildId.Value, actualCommands);
+ if (this.Client.Guilds.TryGetValue(guildId.Value, out var guild))
{
- await this.Client.DeleteGuildApplicationCommandAsync(guildId.Value, cmd.Id);
+ guild.InternalRegisteredApplicationCommands = new();
+ guild.InternalRegisteredApplicationCommands.AddRange(actualCommands);
}
- catch (NotFoundException)
+
+ }
+ else
+ {
+ foreach (var cmd in GuildDiscordCommands[guildId.Value])
{
- this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Could not delete guild command {cmd.Id} in guild {guildId.Value}. Please clean up manually");
+ try
+ {
+ await this.Client.DeleteGuildApplicationCommandAsync(guildId.Value, cmd.Id);
+ }
+ catch (NotFoundException)
+ {
+ this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Could not delete guild command {cmd.Id} in guild {guildId.Value}. Please clean up manually");
+ }
}
}
}
}
- }
- catch (UnauthorizedException ex)
- {
- this.Client.Logger.LogError($"Could not register application commands for guild {guildId}.\nError: {ex.JsonMessage}");
- return;
- }
- //Creates a guild command if a guild id is specified, otherwise global
- //Checks against the ids and adds them to the command method lists
- foreach (var command in commands)
- {
- if (commandMethods.GetFirstValueWhere(x => x.Name == command.Name, out var com))
- com.CommandId = command.Id;
- else if (groupCommands.GetFirstValueWhere(x => x.Name == command.Name, out var groupCom))
- groupCom.CommandId = command.Id;
- else if (subGroupCommands.GetFirstValueWhere(x => x.Name == command.Name, out var subCom))
- subCom.CommandId = command.Id;
- else if (contextMenuCommands.GetFirstValueWhere(x => x.Name == command.Name, out var cmCom))
- cmCom.CommandId = command.Id;
- }
+ catch (UnauthorizedException ex)
+ {
+ this.Client.Logger.LogError($"Could not register application commands for guild {guildId}.\nError: {ex.JsonMessage}");
+ return;
+ }
+ //Creates a guild command if a guild id is specified, otherwise global
+ //Checks against the ids and adds them to the command method lists
+ foreach (var command in commands)
+ {
+ if (commandMethods.GetFirstValueWhere(x => x.Name == command.Name, out var com))
+ com.CommandId = command.Id;
+ else if (groupCommands.GetFirstValueWhere(x => x.Name == command.Name, out var groupCom))
+ groupCom.CommandId = command.Id;
+ else if (subGroupCommands.GetFirstValueWhere(x => x.Name == command.Name, out var subCom))
+ subCom.CommandId = command.Id;
+ else if (contextMenuCommands.GetFirstValueWhere(x => x.Name == command.Name, out var cmCom))
+ cmCom.CommandId = command.Id;
+ }
- //Adds to the global lists finally
- s_commandMethods.AddRange(commandMethods);
- s_groupCommands.AddRange(groupCommands);
- s_subGroupCommands.AddRange(subGroupCommands);
- s_contextMenuCommands.AddRange(contextMenuCommands);
+ //Adds to the global lists finally
+ s_commandMethods.AddRange(commandMethods);
+ s_groupCommands.AddRange(groupCommands);
+ s_subGroupCommands.AddRange(subGroupCommands);
+ s_contextMenuCommands.AddRange(contextMenuCommands);
- s_registeredCommands.Add(new KeyValuePair<ulong?, IReadOnlyList<DiscordApplicationCommand>>(guildId, commands.ToList()));
+ s_registeredCommands.Add(new KeyValuePair<ulong?, IReadOnlyList<DiscordApplicationCommand>>(guildId, commands.ToList()));
- foreach (var command in commandMethods)
- {
- var app = types.First(t => t.Type == command.Method.DeclaringType);
- }
+ foreach (var command in commandMethods)
+ {
+ var app = types.First(t => t.Type == command.Method.DeclaringType);
+ }
- this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Expected Count: {s_expectedCount}\nCurrent Count: {s_registrationCount}");
+ this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Expected Count: {s_expectedCount}\nCurrent Count: {s_registrationCount}");
- if (guildId.HasValue)
- {
- await this._guildApplicationCommandsRegistered.InvokeAsync(this, new GuildApplicationCommandsRegisteredEventArgs(Configuration?.ServiceProvider)
+ if (guildId.HasValue)
{
- Handled = true,
- GuildId = guildId.Value,
- RegisteredCommands = GuildCommandsInternal.Any(c => c.Key == guildId.Value) ? GuildCommandsInternal.FirstOrDefault(c => c.Key == guildId.Value).Value : null
- });
+ await this._guildApplicationCommandsRegistered.InvokeAsync(this, new GuildApplicationCommandsRegisteredEventArgs(Configuration?.ServiceProvider)
+ {
+ Handled = true,
+ GuildId = guildId.Value,
+ RegisteredCommands = GuildCommandsInternal.Any(c => c.Key == guildId.Value) ? GuildCommandsInternal.FirstOrDefault(c => c.Key == guildId.Value).Value : null
+ });
+ }
+ else
+ {
+ await this._globalApplicationCommandsRegistered.InvokeAsync(this, new GlobalApplicationCommandsRegisteredEventArgs(Configuration?.ServiceProvider)
+ {
+ Handled = true,
+ RegisteredCommands = GlobalCommandsInternal
+ });
+ }
+
+ s_registrationCount++;
+ this.CheckRegistrationStartup(ManOr);
}
- else
+ catch (Exception ex)
{
- await this._globalApplicationCommandsRegistered.InvokeAsync(this, new GlobalApplicationCommandsRegisteredEventArgs(Configuration?.ServiceProvider)
- {
- Handled = true,
- RegisteredCommands = GlobalCommandsInternal
- });
+ if (ex is BadRequestException brex)
+ this.Client.Logger.LogCritical(brex, $"There was an error registering application commands: {brex.JsonMessage}");
+ else
+ this.Client.Logger.LogCritical(ex, $"There was an general error registering application commands");
+ s_errored = true;
}
-
- s_registrationCount++;
- this.CheckRegistrationStartup(ManOr);
- }
- catch (Exception ex)
- {
- if (ex is BadRequestException brex)
- this.Client.Logger.LogCritical(brex, $"There was an error registering application commands: {brex.JsonMessage}");
- else
- this.Client.Logger.LogCritical(ex, $"There was an general error registering application commands");
- s_errored = true;
}
}
- }
- private async void CheckRegistrationStartup(bool man = false)
- {
- this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Checking counts...\n\nExpected Count: {s_expectedCount}\nCurrent Count: {s_registrationCount}");
-
- if ((s_registrationCount == s_expectedCount) || man)
+ private async void CheckRegistrationStartup(bool man = false)
{
- await this._applicationCommandsModuleStartupFinished.InvokeAsync(this, new ApplicationCommandsModuleStartupFinishedEventArgs(Configuration?.ServiceProvider)
+ this.Client.Logger.Log(ApplicationCommandsLogLevel, $"Checking counts...\n\nExpected Count: {s_expectedCount}\nCurrent Count: {s_registrationCount}");
+
+ if ((s_registrationCount == s_expectedCount) || man)
{
- Handled = true,
- RegisteredGlobalCommands = GlobalCommandsInternal,
- RegisteredGuildCommands = GuildCommandsInternal,
- GuildsWithoutScope = this._missingScopeGuildIds
- });
+ await this._applicationCommandsModuleStartupFinished.InvokeAsync(this, new ApplicationCommandsModuleStartupFinishedEventArgs(Configuration?.ServiceProvider)
+ {
+ Handled = true,
+ RegisteredGlobalCommands = GlobalCommandsInternal,
+ RegisteredGuildCommands = GuildCommandsInternal,
+ GuildsWithoutScope = this._missingScopeGuildIds
+ });
- this.FinishedRegistration();
+ this.FinishedRegistration();
+ }
}
- }
- /// <summary>
- /// Interaction handler.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task InteractionHandler(DiscordClient client, InteractionCreateEventArgs e)
- {
- _ = Task.Run(async () =>
+ /// <summary>
+ /// Interaction handler.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task InteractionHandler(DiscordClient client, InteractionCreateEventArgs e)
{
- if (e.Interaction.Type == InteractionType.ApplicationCommand)
+ _ = Task.Run(async () =>
{
- //Creates the context
- var context = new InteractionContext
+ if (e.Interaction.Type == InteractionType.ApplicationCommand)
{
- Interaction = e.Interaction,
- Channel = e.Interaction.Channel,
- Guild = e.Interaction.Guild,
- User = e.Interaction.User,
- Client = client,
- ApplicationCommandsExtension = this,
- CommandName = e.Interaction.Data.Name,
- InteractionId = e.Interaction.Id,
- Token = e.Interaction.Token,
- Services = Configuration?.ServiceProvider,
- ResolvedUserMentions = e.Interaction.Data.Resolved?.Users?.Values.ToList(),
- ResolvedRoleMentions = e.Interaction.Data.Resolved?.Roles?.Values.ToList(),
- ResolvedChannelMentions = e.Interaction.Data.Resolved?.Channels?.Values.ToList(),
- ResolvedAttachments = e.Interaction.Data.Resolved?.Attachments?.Values.ToList(),
- Type = ApplicationCommandType.ChatInput,
- Locale = e.Interaction.Locale,
- GuildLocale = e.Interaction.GuildLocale
- };
+ //Creates the context
+ var context = new InteractionContext
+ {
+ Interaction = e.Interaction,
+ Channel = e.Interaction.Channel,
+ Guild = e.Interaction.Guild,
+ User = e.Interaction.User,
+ Client = client,
+ ApplicationCommandsExtension = this,
+ CommandName = e.Interaction.Data.Name,
+ InteractionId = e.Interaction.Id,
+ Token = e.Interaction.Token,
+ Services = Configuration?.ServiceProvider,
+ ResolvedUserMentions = e.Interaction.Data.Resolved?.Users?.Values.ToList(),
+ ResolvedRoleMentions = e.Interaction.Data.Resolved?.Roles?.Values.ToList(),
+ ResolvedChannelMentions = e.Interaction.Data.Resolved?.Channels?.Values.ToList(),
+ ResolvedAttachments = e.Interaction.Data.Resolved?.Attachments?.Values.ToList(),
+ Type = ApplicationCommandType.ChatInput,
+ Locale = e.Interaction.Locale,
+ GuildLocale = e.Interaction.GuildLocale
+ };
+
+ try
+ {
+ if (s_errored)
+ throw new InvalidOperationException("Slash commands failed to register properly on startup.");
- try
- {
- if (s_errored)
- throw new InvalidOperationException("Slash commands failed to register properly on startup.");
+ var methods = s_commandMethods.Where(x => x.CommandId == e.Interaction.Data.Id);
+ var groups = s_groupCommands.Where(x => x.CommandId == e.Interaction.Data.Id);
+ var subgroups = s_subGroupCommands.Where(x => x.CommandId == e.Interaction.Data.Id);
+ if (!methods.Any() && !groups.Any() && !subgroups.Any())
+ throw new InvalidOperationException("A slash command was executed, but no command was registered for it.");
- var methods = s_commandMethods.Where(x => x.CommandId == e.Interaction.Data.Id);
- var groups = s_groupCommands.Where(x => x.CommandId == e.Interaction.Data.Id);
- var subgroups = s_subGroupCommands.Where(x => x.CommandId == e.Interaction.Data.Id);
- if (!methods.Any() && !groups.Any() && !subgroups.Any())
- throw new InvalidOperationException("A slash command was executed, but no command was registered for it.");
+ if (methods.Any())
+ {
+ var method = methods.First().Method;
- if (methods.Any())
- {
- var method = methods.First().Method;
+ var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options);
- var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options);
+ await this.RunCommandAsync(context, method, args);
+ }
+ else if (groups.Any())
+ {
+ var command = e.Interaction.Data.Options.First();
+ var method = groups.First().Methods.First(x => x.Key == command.Name).Value;
- await this.RunCommandAsync(context, method, args);
- }
- else if (groups.Any())
- {
- var command = e.Interaction.Data.Options.First();
- var method = groups.First().Methods.First(x => x.Key == command.Name).Value;
+ var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options.First().Options);
- var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options.First().Options);
+ await this.RunCommandAsync(context, method, args);
+ }
+ else if (subgroups.Any())
+ {
+ var command = e.Interaction.Data.Options.First();
+ var group = subgroups.First().SubCommands.First(x => x.Name == command.Name);
- await this.RunCommandAsync(context, method, args);
- }
- else if (subgroups.Any())
- {
- var command = e.Interaction.Data.Options.First();
- var group = subgroups.First().SubCommands.First(x => x.Name == command.Name);
+ var method = group.Methods.First(x => x.Key == command.Options.First().Name).Value;
- var method = group.Methods.First(x => x.Key == command.Options.First().Name).Value;
+ var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options.First().Options.First().Options);
- var args = await this.ResolveInteractionCommandParameters(e, context, method, e.Interaction.Data.Options.First().Options.First().Options);
+ await this.RunCommandAsync(context, method, args);
+ }
- await this.RunCommandAsync(context, method, args);
+ await this._slashExecuted.InvokeAsync(this, new SlashCommandExecutedEventArgs(this.Client.ServiceProvider) { Context = context });
+ }
+ catch (Exception ex)
+ {
+ await this._slashError.InvokeAsync(this, new SlashCommandErrorEventArgs(this.Client.ServiceProvider) { Context = context, Exception = ex });
}
-
- await this._slashExecuted.InvokeAsync(this, new SlashCommandExecutedEventArgs(this.Client.ServiceProvider) { Context = context });
}
- catch (Exception ex)
+ else if (e.Interaction.Type == InteractionType.AutoComplete)
{
- await this._slashError.InvokeAsync(this, new SlashCommandErrorEventArgs(this.Client.ServiceProvider) { Context = context, Exception = ex });
- }
- }
- else if (e.Interaction.Type == InteractionType.AutoComplete)
- {
- if (s_errored)
- throw new InvalidOperationException("Slash commands failed to register properly on startup.");
+ if (s_errored)
+ throw new InvalidOperationException("Slash commands failed to register properly on startup.");
- var methods = s_commandMethods.Where(x => x.CommandId == e.Interaction.Data.Id);
- var groups = s_groupCommands.Where(x => x.CommandId == e.Interaction.Data.Id);
- var subgroups = s_subGroupCommands.Where(x => x.CommandId == e.Interaction.Data.Id);
- if (!methods.Any() && !groups.Any() && !subgroups.Any())
- throw new InvalidOperationException("An autocomplete interaction was created, but no command was registered for it.");
+ var methods = s_commandMethods.Where(x => x.CommandId == e.Interaction.Data.Id);
+ var groups = s_groupCommands.Where(x => x.CommandId == e.Interaction.Data.Id);
+ var subgroups = s_subGroupCommands.Where(x => x.CommandId == e.Interaction.Data.Id);
+ if (!methods.Any() && !groups.Any() && !subgroups.Any())
+ throw new InvalidOperationException("An autocomplete interaction was created, but no command was registered for it.");
- try
- {
- if (methods.Any())
+ try
{
- var focusedOption = e.Interaction.Data.Options.First(o => o.Focused);
- var method = methods.First().Method;
+ if (methods.Any())
+ {
+ var focusedOption = e.Interaction.Data.Options.First(o => o.Focused);
+ var method = methods.First().Method;
- var option = method.GetParameters().Skip(1).First(p => p.GetCustomAttribute<OptionAttribute>().Name == focusedOption.Name);
- var provider = option.GetCustomAttribute<AutocompleteAttribute>().ProviderType;
- var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider));
- var providerInstance = Activator.CreateInstance(provider);
+ var option = method.GetParameters().Skip(1).First(p => p.GetCustomAttribute<OptionAttribute>().Name == focusedOption.Name);
+ var provider = option.GetCustomAttribute<AutocompleteAttribute>().ProviderType;
+ var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider));
+ var providerInstance = Activator.CreateInstance(provider);
- var context = new AutocompleteContext
+ var context = new AutocompleteContext
+ {
+ Interaction = e.Interaction,
+ Client = this.Client,
+ Services = Configuration?.ServiceProvider,
+ ApplicationCommandsExtension = this,
+ Guild = e.Interaction.Guild,
+ Channel = e.Interaction.Channel,
+ User = e.Interaction.User,
+ Options = e.Interaction.Data.Options.ToList(),
+ FocusedOption = focusedOption,
+ Locale = e.Interaction.Locale,
+ GuildLocale = e.Interaction.GuildLocale
+ };
+
+ var choices = await (Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>>) providerMethod.Invoke(providerInstance, new[] { context });
+ await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices));
+ }
+ else if (groups.Any())
{
- Interaction = e.Interaction,
- Client = this.Client,
- Services = Configuration?.ServiceProvider,
- ApplicationCommandsExtension = this,
- Guild = e.Interaction.Guild,
- Channel = e.Interaction.Channel,
- User = e.Interaction.User,
- Options = e.Interaction.Data.Options.ToList(),
- FocusedOption = focusedOption,
- Locale = e.Interaction.Locale,
- GuildLocale = e.Interaction.GuildLocale
- };
-
- var choices = await (Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>>) providerMethod.Invoke(providerInstance, new[] { context });
- await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices));
- }
- else if (groups.Any())
- {
- var command = e.Interaction.Data.Options.First();
- var group = groups.First().Methods.First(x => x.Key == command.Name).Value;
+ var command = e.Interaction.Data.Options.First();
+ var group = groups.First().Methods.First(x => x.Key == command.Name).Value;
- var focusedOption = command.Options.First(o => o.Focused);
- var option = group.GetParameters().Skip(1).First(p => p.GetCustomAttribute<OptionAttribute>().Name == focusedOption.Name);
- var provider = option.GetCustomAttribute<AutocompleteAttribute>().ProviderType;
- var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider));
- var providerInstance = Activator.CreateInstance(provider);
+ var focusedOption = command.Options.First(o => o.Focused);
+ var option = group.GetParameters().Skip(1).First(p => p.GetCustomAttribute<OptionAttribute>().Name == focusedOption.Name);
+ var provider = option.GetCustomAttribute<AutocompleteAttribute>().ProviderType;
+ var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider));
+ var providerInstance = Activator.CreateInstance(provider);
- var context = new AutocompleteContext
- {
- Interaction = e.Interaction,
- Services = Configuration?.ServiceProvider,
- ApplicationCommandsExtension = this,
- Guild = e.Interaction.Guild,
- Channel = e.Interaction.Channel,
- User = e.Interaction.User,
- Options = command.Options.ToList(),
- FocusedOption = focusedOption,
- Locale = e.Interaction.Locale,
- GuildLocale = e.Interaction.GuildLocale
- };
-
- var choices = await (Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>>) providerMethod.Invoke(providerInstance, new[] { context });
- await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices));
- }
- else if (subgroups.Any())
+ var context = new AutocompleteContext
+ {
+ Interaction = e.Interaction,
+ Services = Configuration?.ServiceProvider,
+ ApplicationCommandsExtension = this,
+ Guild = e.Interaction.Guild,
+ Channel = e.Interaction.Channel,
+ User = e.Interaction.User,
+ Options = command.Options.ToList(),
+ FocusedOption = focusedOption,
+ Locale = e.Interaction.Locale,
+ GuildLocale = e.Interaction.GuildLocale
+ };
+
+ var choices = await (Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>>) providerMethod.Invoke(providerInstance, new[] { context });
+ await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices));
+ }
+ else if (subgroups.Any())
{
var command = e.Interaction.Data.Options.First();
var group = subgroups.First().SubCommands.First(x => x.Name == command.Name).Methods.First(x => x.Key == command.Options.First().Name).Value;
var focusedOption = command.Options.First().Options.First(o => o.Focused);
var option = group.GetParameters().Skip(1).First(p => p.GetCustomAttribute<OptionAttribute>().Name == focusedOption.Name);
var provider = option.GetCustomAttribute<AutocompleteAttribute>().ProviderType;
var providerMethod = provider.GetMethod(nameof(IAutocompleteProvider.Provider));
var providerInstance = Activator.CreateInstance(provider);
var context = new AutocompleteContext
{
Interaction = e.Interaction,
Services = Configuration?.ServiceProvider,
ApplicationCommandsExtension = this,
Guild = e.Interaction.Guild,
Channel = e.Interaction.Channel,
User = e.Interaction.User,
Options = command.Options.First().Options.ToList(),
FocusedOption = focusedOption,
- Locale = e.Interaction.Locale,
- GuildLocale = e.Interaction.GuildLocale
- };
+ Locale = e.Interaction.Locale,
+ GuildLocale = e.Interaction.GuildLocale
+ };
var choices = await (Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>>) providerMethod.Invoke(providerInstance, new[] { context });
await e.Interaction.CreateResponseAsync(InteractionResponseType.AutoCompleteResult, new DiscordInteractionResponseBuilder().AddAutoCompleteChoices(choices));
}
+ }
+ catch (Exception ex)
+ {
+ this.Client.Logger.LogError(ex, "Error in autocomplete interaction");
+ }
}
- catch (Exception ex)
- {
- this.Client.Logger.LogError(ex, "Error in autocomplete interaction");
- }
- }
- });
- return Task.CompletedTask;
- }
+ });
+ return Task.CompletedTask;
+ }
- /// <summary>
- /// Context menu handler.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task ContextMenuHandler(DiscordClient client, ContextMenuInteractionCreateEventArgs e)
- {
- _ = Task.Run(async () =>
+ /// <summary>
+ /// Context menu handler.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task ContextMenuHandler(DiscordClient client, ContextMenuInteractionCreateEventArgs e)
{
- //Creates the context
- var context = new ContextMenuContext
- {
- Interaction = e.Interaction,
- Channel = e.Interaction.Channel,
- Client = client,
- Services = Configuration?.ServiceProvider,
- CommandName = e.Interaction.Data.Name,
- ApplicationCommandsExtension = this,
- Guild = e.Interaction.Guild,
- InteractionId = e.Interaction.Id,
- User = e.Interaction.User,
- Token = e.Interaction.Token,
- TargetUser = e.TargetUser,
- TargetMessage = e.TargetMessage,
- Type = e.Type,
- Locale = e.Interaction.Locale,
- GuildLocale = e.Interaction.GuildLocale
- };
-
- try
+ _ = Task.Run(async () =>
{
- if (s_errored)
- throw new InvalidOperationException("Context menus failed to register properly on startup.");
+ //Creates the context
+ var context = new ContextMenuContext
+ {
+ Interaction = e.Interaction,
+ Channel = e.Interaction.Channel,
+ Client = client,
+ Services = Configuration?.ServiceProvider,
+ CommandName = e.Interaction.Data.Name,
+ ApplicationCommandsExtension = this,
+ Guild = e.Interaction.Guild,
+ InteractionId = e.Interaction.Id,
+ User = e.Interaction.User,
+ Token = e.Interaction.Token,
+ TargetUser = e.TargetUser,
+ TargetMessage = e.TargetMessage,
+ Type = e.Type,
+ Locale = e.Interaction.Locale,
+ GuildLocale = e.Interaction.GuildLocale
+ };
- //Gets the method for the command
- var method = s_contextMenuCommands.FirstOrDefault(x => x.CommandId == e.Interaction.Data.Id);
+ try
+ {
+ if (s_errored)
+ throw new InvalidOperationException("Context menus failed to register properly on startup.");
- if (method == null)
- throw new InvalidOperationException("A context menu was executed, but no command was registered for it.");
+ //Gets the method for the command
+ var method = s_contextMenuCommands.FirstOrDefault(x => x.CommandId == e.Interaction.Data.Id);
- await this.RunCommandAsync(context, method.Method, new[] { context });
+ if (method == null)
+ throw new InvalidOperationException("A context menu was executed, but no command was registered for it.");
- await this._contextMenuExecuted.InvokeAsync(this, new ContextMenuExecutedEventArgs(this.Client.ServiceProvider) { Context = context });
- }
- catch (Exception ex)
- {
- await this._contextMenuErrored.InvokeAsync(this, new ContextMenuErrorEventArgs(this.Client.ServiceProvider) { Context = context, Exception = ex });
- }
- });
+ await this.RunCommandAsync(context, method.Method, new[] { context });
- return Task.CompletedTask;
- }
+ await this._contextMenuExecuted.InvokeAsync(this, new ContextMenuExecutedEventArgs(this.Client.ServiceProvider) { Context = context });
+ }
+ catch (Exception ex)
+ {
+ await this._contextMenuErrored.InvokeAsync(this, new ContextMenuErrorEventArgs(this.Client.ServiceProvider) { Context = context, Exception = ex });
+ }
+ });
- /// <summary>
- /// Runs a command.
- /// </summary>
- /// <param name="context">The base context.</param>
- /// <param name="method">The method info.</param>
- /// <param name="args">The arguments.</param>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0066:Convert switch statement to expression", Justification = "<Pending>")]
- internal async Task RunCommandAsync(BaseContext context, MethodInfo method, IEnumerable<object> args)
- {
- object classInstance;
+ return Task.CompletedTask;
+ }
- //Accounts for lifespans
- var moduleLifespan = (method.DeclaringType.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>() != null ? method.DeclaringType.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>()?.Lifespan : ApplicationCommandModuleLifespan.Transient) ?? ApplicationCommandModuleLifespan.Transient;
- switch (moduleLifespan)
+ /// <summary>
+ /// Runs a command.
+ /// </summary>
+ /// <param name="context">The base context.</param>
+ /// <param name="method">The method info.</param>
+ /// <param name="args">The arguments.</param>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0066:Convert switch statement to expression", Justification = "<Pending>")]
+ internal async Task RunCommandAsync(BaseContext context, MethodInfo method, IEnumerable<object> args)
{
- case ApplicationCommandModuleLifespan.Scoped:
- //Accounts for static methods and adds DI
- classInstance = method.IsStatic ? ActivatorUtilities.CreateInstance(Configuration?.ServiceProvider.CreateScope().ServiceProvider, method.DeclaringType) : CreateInstance(method.DeclaringType, Configuration?.ServiceProvider.CreateScope().ServiceProvider);
- break;
-
- case ApplicationCommandModuleLifespan.Transient:
- //Accounts for static methods and adds DI
- classInstance = method.IsStatic ? ActivatorUtilities.CreateInstance(Configuration?.ServiceProvider, method.DeclaringType) : CreateInstance(method.DeclaringType, Configuration?.ServiceProvider);
- break;
-
- //If singleton, gets it from the singleton list
- case ApplicationCommandModuleLifespan.Singleton:
- classInstance = s_singletonModules.First(x => ReferenceEquals(x.GetType(), method.DeclaringType));
- break;
-
- default:
- throw new Exception($"An unknown {nameof(ApplicationCommandModuleLifespanAttribute)} scope was specified on command {context.CommandName}");
- }
+ object classInstance;
+
+ //Accounts for lifespans
+ var moduleLifespan = (method.DeclaringType.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>() != null ? method.DeclaringType.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>()?.Lifespan : ApplicationCommandModuleLifespan.Transient) ?? ApplicationCommandModuleLifespan.Transient;
+ switch (moduleLifespan)
+ {
+ case ApplicationCommandModuleLifespan.Scoped:
+ //Accounts for static methods and adds DI
+ classInstance = method.IsStatic ? ActivatorUtilities.CreateInstance(Configuration?.ServiceProvider.CreateScope().ServiceProvider, method.DeclaringType) : CreateInstance(method.DeclaringType, Configuration?.ServiceProvider.CreateScope().ServiceProvider);
+ break;
+
+ case ApplicationCommandModuleLifespan.Transient:
+ //Accounts for static methods and adds DI
+ classInstance = method.IsStatic ? ActivatorUtilities.CreateInstance(Configuration?.ServiceProvider, method.DeclaringType) : CreateInstance(method.DeclaringType, Configuration?.ServiceProvider);
+ break;
+
+ //If singleton, gets it from the singleton list
+ case ApplicationCommandModuleLifespan.Singleton:
+ classInstance = s_singletonModules.First(x => ReferenceEquals(x.GetType(), method.DeclaringType));
+ break;
+
+ default:
+ throw new Exception($"An unknown {nameof(ApplicationCommandModuleLifespanAttribute)} scope was specified on command {context.CommandName}");
+ }
- ApplicationCommandsModule module = null;
- if (classInstance is ApplicationCommandsModule mod)
- module = mod;
+ ApplicationCommandsModule module = null;
+ if (classInstance is ApplicationCommandsModule mod)
+ module = mod;
- // Slash commands
- if (context is InteractionContext slashContext)
- {
- await this.RunPreexecutionChecksAsync(method, slashContext);
+ // Slash commands
+ if (context is InteractionContext slashContext)
+ {
+ await this.RunPreexecutionChecksAsync(method, slashContext);
- var shouldExecute = await (module?.BeforeSlashExecutionAsync(slashContext) ?? Task.FromResult(true));
+ var shouldExecute = await (module?.BeforeSlashExecutionAsync(slashContext) ?? Task.FromResult(true));
- if (shouldExecute)
+ if (shouldExecute)
+ {
+ if (AutoDeferEnabled)
+ await context.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource);
+ await (Task)method.Invoke(classInstance, args.ToArray());
+
+ await (module?.AfterSlashExecutionAsync(slashContext) ?? Task.CompletedTask);
+ }
+ }
+ // Context menus
+ if (context is ContextMenuContext contextMenuContext)
{
- if (AutoDeferEnabled)
- await context.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource);
- await (Task)method.Invoke(classInstance, args.ToArray());
+ await this.RunPreexecutionChecksAsync(method, contextMenuContext);
+
+ var shouldExecute = await (module?.BeforeContextMenuExecutionAsync(contextMenuContext) ?? Task.FromResult(true));
+
+ if (shouldExecute)
+ {
+ if (AutoDeferEnabled)
+ await context.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource);
+ await (Task)method.Invoke(classInstance, args.ToArray());
- await (module?.AfterSlashExecutionAsync(slashContext) ?? Task.CompletedTask);
+ await (module?.AfterContextMenuExecutionAsync(contextMenuContext) ?? Task.CompletedTask);
+ }
}
}
- // Context menus
- if (context is ContextMenuContext contextMenuContext)
+
+ /// <summary>
+ /// Property injection
+ /// </summary>
+ /// <param name="t">The type.</param>
+ /// <param name="services">The services.</param>
+ internal static object CreateInstance(Type t, IServiceProvider services)
{
- await this.RunPreexecutionChecksAsync(method, contextMenuContext);
+ var ti = t.GetTypeInfo();
+ var constructors = ti.DeclaredConstructors
+ .Where(xci => xci.IsPublic)
+ .ToArray();
- var shouldExecute = await (module?.BeforeContextMenuExecutionAsync(contextMenuContext) ?? Task.FromResult(true));
+ if (constructors.Length != 1)
+ throw new ArgumentException("Specified type does not contain a public constructor or contains more than one public constructor.");
- if (shouldExecute)
- {
- if (AutoDeferEnabled)
- await context.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource);
- await (Task)method.Invoke(classInstance, args.ToArray());
+ var constructor = constructors[0];
+ var constructorArgs = constructor.GetParameters();
+ var args = new object[constructorArgs.Length];
- await (module?.AfterContextMenuExecutionAsync(contextMenuContext) ?? Task.CompletedTask);
- }
- }
- }
+ if (constructorArgs.Length != 0 && services == null)
+ throw new InvalidOperationException("Dependency collection needs to be specified for parameterized constructors.");
- /// <summary>
- /// Property injection
- /// </summary>
- /// <param name="t">The type.</param>
- /// <param name="services">The services.</param>
- internal static object CreateInstance(Type t, IServiceProvider services)
- {
- var ti = t.GetTypeInfo();
- var constructors = ti.DeclaredConstructors
- .Where(xci => xci.IsPublic)
- .ToArray();
+ // inject via constructor
+ if (constructorArgs.Length != 0)
+ for (var i = 0; i < args.Length; i++)
+ args[i] = services.GetRequiredService(constructorArgs[i].ParameterType);
- if (constructors.Length != 1)
- throw new ArgumentException("Specified type does not contain a public constructor or contains more than one public constructor.");
+ var moduleInstance = Activator.CreateInstance(t, args);
- var constructor = constructors[0];
- var constructorArgs = constructor.GetParameters();
- var args = new object[constructorArgs.Length];
+ // inject into properties
+ var props = t.GetRuntimeProperties().Where(xp => xp.CanWrite && xp.SetMethod != null && !xp.SetMethod.IsStatic && xp.SetMethod.IsPublic);
+ foreach (var prop in props)
+ {
+ if (prop.GetCustomAttribute<DontInjectAttribute>() != null)
+ continue;
- if (constructorArgs.Length != 0 && services == null)
- throw new InvalidOperationException("Dependency collection needs to be specified for parameterized constructors.");
+ var service = services.GetService(prop.PropertyType);
+ if (service == null)
+ continue;
- // inject via constructor
- if (constructorArgs.Length != 0)
- for (var i = 0; i < args.Length; i++)
- args[i] = services.GetRequiredService(constructorArgs[i].ParameterType);
+ prop.SetValue(moduleInstance, service);
+ }
- var moduleInstance = Activator.CreateInstance(t, args);
+ // inject into fields
+ var fields = t.GetRuntimeFields().Where(xf => !xf.IsInitOnly && !xf.IsStatic && xf.IsPublic);
+ foreach (var field in fields)
+ {
+ if (field.GetCustomAttribute<DontInjectAttribute>() != null)
+ continue;
- // inject into properties
- var props = t.GetRuntimeProperties().Where(xp => xp.CanWrite && xp.SetMethod != null && !xp.SetMethod.IsStatic && xp.SetMethod.IsPublic);
- foreach (var prop in props)
- {
- if (prop.GetCustomAttribute<DontInjectAttribute>() != null)
- continue;
+ var service = services.GetService(field.FieldType);
+ if (service == null)
+ continue;
- var service = services.GetService(prop.PropertyType);
- if (service == null)
- continue;
+ field.SetValue(moduleInstance, service);
+ }
- prop.SetValue(moduleInstance, service);
+ return moduleInstance;
}
- // inject into fields
- var fields = t.GetRuntimeFields().Where(xf => !xf.IsInitOnly && !xf.IsStatic && xf.IsPublic);
- foreach (var field in fields)
+ /// <summary>
+ /// Resolves the slash command parameters.
+ /// </summary>
+ /// <param name="e">The event arguments.</param>
+ /// <param name="context">The interaction context.</param>
+ /// <param name="method">The method info.</param>
+ /// <param name="options">The options.</param>
+ private async Task<List<object>> ResolveInteractionCommandParameters(InteractionCreateEventArgs e, InteractionContext context, MethodInfo method, IEnumerable<DiscordInteractionDataOption> options)
{
- if (field.GetCustomAttribute<DontInjectAttribute>() != null)
- continue;
+ var args = new List<object> { context };
+ var parameters = method.GetParameters().Skip(1);
+
+ for (var i = 0; i < parameters.Count(); i++)
+ {
+ var parameter = parameters.ElementAt(i);
- var service = services.GetService(field.FieldType);
- if (service == null)
- continue;
+ //Accounts for optional arguments without values given
+ if (parameter.IsOptional && (options == null || (!options?.Any(x => x.Name == parameter.GetCustomAttribute<OptionAttribute>().Name.ToLower()) ?? true)))
+ args.Add(parameter.DefaultValue);
+ else
+ {
+ var option = options.Single(x => x.Name == parameter.GetCustomAttribute<OptionAttribute>().Name.ToLower());
+
+ if (parameter.ParameterType == typeof(string))
+ args.Add(option.Value.ToString());
+ else if (parameter.ParameterType.IsEnum)
+ args.Add(Enum.Parse(parameter.ParameterType, (string)option.Value));
+ else if (parameter.ParameterType == typeof(long) || parameter.ParameterType == typeof(long?))
+ args.Add((long?)option.Value);
+ else if (parameter.ParameterType == typeof(bool) || parameter.ParameterType == typeof(bool?))
+ args.Add((bool?)option.Value);
+ else if (parameter.ParameterType == typeof(double) || parameter.ParameterType == typeof(double?))
+ args.Add((double?)option.Value);
+ else if (parameter.ParameterType == typeof(int) || parameter.ParameterType == typeof(int?))
+ args.Add((int?)option.Value);
+ else if (parameter.ParameterType == typeof(DiscordAttachment))
+ {
+ //Checks through resolved
+ if (e.Interaction.Data.Resolved.Attachments != null &&
+ e.Interaction.Data.Resolved.Attachments.TryGetValue((ulong)option.Value, out var attachment))
+ args.Add(attachment);
+ else
+ args.Add(new DiscordAttachment() { Id = (ulong)option.Value, Discord = this.Client.ApiClient.Discord });
+ }
+ else if (parameter.ParameterType == typeof(DiscordUser))
+ {
+ //Checks through resolved
+ if (e.Interaction.Data.Resolved.Members != null &&
+ e.Interaction.Data.Resolved.Members.TryGetValue((ulong)option.Value, out var member))
+ args.Add(member);
+ else if (e.Interaction.Data.Resolved.Users != null &&
+ e.Interaction.Data.Resolved.Users.TryGetValue((ulong)option.Value, out var user))
+ args.Add(user);
+ else
+ args.Add(await this.Client.GetUserAsync((ulong)option.Value));
+ }
+ else if (parameter.ParameterType == typeof(DiscordChannel))
+ {
+ //Checks through resolved
+ if (e.Interaction.Data.Resolved.Channels != null &&
+ e.Interaction.Data.Resolved.Channels.TryGetValue((ulong)option.Value, out var channel))
+ args.Add(channel);
+ else
+ args.Add(e.Interaction.Guild.GetChannel((ulong)option.Value));
+ }
+ else if (parameter.ParameterType == typeof(DiscordRole))
+ {
+ //Checks through resolved
+ if (e.Interaction.Data.Resolved.Roles != null &&
+ e.Interaction.Data.Resolved.Roles.TryGetValue((ulong)option.Value, out var role))
+ args.Add(role);
+ else
+ args.Add(e.Interaction.Guild.GetRole((ulong)option.Value));
+ }
+ else if (parameter.ParameterType == typeof(SnowflakeObject))
+ {
+ //Checks through resolved
+ if (e.Interaction.Data.Resolved.Roles != null && e.Interaction.Data.Resolved.Roles.TryGetValue((ulong)option.Value, out var role))
+ args.Add(role);
+ else if (e.Interaction.Data.Resolved.Members != null && e.Interaction.Data.Resolved.Members.TryGetValue((ulong)option.Value, out var member))
+ args.Add(member);
+ else if (e.Interaction.Data.Resolved.Users != null && e.Interaction.Data.Resolved.Users.TryGetValue((ulong)option.Value, out var user))
+ args.Add(user);
+ else
+ throw new ArgumentException("Error resolving mentionable option.");
+ }
+ else
+ throw new ArgumentException($"Error resolving interaction.");
+ }
+ }
- field.SetValue(moduleInstance, service);
+ return args;
}
- return moduleInstance;
- }
-
- /// <summary>
- /// Resolves the slash command parameters.
- /// </summary>
- /// <param name="e">The event arguments.</param>
- /// <param name="context">The interaction context.</param>
- /// <param name="method">The method info.</param>
- /// <param name="options">The options.</param>
- private async Task<List<object>> ResolveInteractionCommandParameters(InteractionCreateEventArgs e, InteractionContext context, MethodInfo method, IEnumerable<DiscordInteractionDataOption> options)
- {
- var args = new List<object> { context };
- var parameters = method.GetParameters().Skip(1);
-
- for (var i = 0; i < parameters.Count(); i++)
+ /// <summary>
+ /// Runs the pre-execution checks.
+ /// </summary>
+ /// <param name="method">The method info.</param>
+ /// <param name="context">The base context.</param>
+ private async Task RunPreexecutionChecksAsync(MethodInfo method, BaseContext context)
{
- var parameter = parameters.ElementAt(i);
-
- //Accounts for optional arguments without values given
- if (parameter.IsOptional && (options == null || (!options?.Any(x => x.Name == parameter.GetCustomAttribute<OptionAttribute>().Name.ToLower()) ?? true)))
- args.Add(parameter.DefaultValue);
- else
+ if (context is InteractionContext ctx)
{
- var option = options.Single(x => x.Name == parameter.GetCustomAttribute<OptionAttribute>().Name.ToLower());
-
- if (parameter.ParameterType == typeof(string))
- args.Add(option.Value.ToString());
- else if (parameter.ParameterType.IsEnum)
- args.Add(Enum.Parse(parameter.ParameterType, (string)option.Value));
- else if (parameter.ParameterType == typeof(long) || parameter.ParameterType == typeof(long?))
- args.Add((long?)option.Value);
- else if (parameter.ParameterType == typeof(bool) || parameter.ParameterType == typeof(bool?))
- args.Add((bool?)option.Value);
- else if (parameter.ParameterType == typeof(double) || parameter.ParameterType == typeof(double?))
- args.Add((double?)option.Value);
- else if (parameter.ParameterType == typeof(int) || parameter.ParameterType == typeof(int?))
- args.Add((int?)option.Value);
- else if (parameter.ParameterType == typeof(DiscordAttachment))
- {
- //Checks through resolved
- if (e.Interaction.Data.Resolved.Attachments != null &&
- e.Interaction.Data.Resolved.Attachments.TryGetValue((ulong)option.Value, out var attachment))
- args.Add(attachment);
- else
- args.Add(new DiscordAttachment() { Id = (ulong)option.Value, Discord = this.Client.ApiClient.Discord });
- }
- else if (parameter.ParameterType == typeof(DiscordUser))
+ //Gets all attributes from parent classes as well and stuff
+ var attributes = new List<SlashCheckBaseAttribute>();
+ attributes.AddRange(method.GetCustomAttributes<SlashCheckBaseAttribute>(true));
+ attributes.AddRange(method.DeclaringType.GetCustomAttributes<SlashCheckBaseAttribute>());
+ if (method.DeclaringType.DeclaringType != null)
{
- //Checks through resolved
- if (e.Interaction.Data.Resolved.Members != null &&
- e.Interaction.Data.Resolved.Members.TryGetValue((ulong)option.Value, out var member))
- args.Add(member);
- else if (e.Interaction.Data.Resolved.Users != null &&
- e.Interaction.Data.Resolved.Users.TryGetValue((ulong)option.Value, out var user))
- args.Add(user);
- else
- args.Add(await this.Client.GetUserAsync((ulong)option.Value));
+ attributes.AddRange(method.DeclaringType.DeclaringType.GetCustomAttributes<SlashCheckBaseAttribute>());
+ if (method.DeclaringType.DeclaringType.DeclaringType != null)
+ {
+ attributes.AddRange(method.DeclaringType.DeclaringType.DeclaringType.GetCustomAttributes<SlashCheckBaseAttribute>());
+ }
}
- else if (parameter.ParameterType == typeof(DiscordChannel))
+
+ var dict = new Dictionary<SlashCheckBaseAttribute, bool>();
+ foreach (var att in attributes)
{
- //Checks through resolved
- if (e.Interaction.Data.Resolved.Channels != null &&
- e.Interaction.Data.Resolved.Channels.TryGetValue((ulong)option.Value, out var channel))
- args.Add(channel);
- else
- args.Add(e.Interaction.Guild.GetChannel((ulong)option.Value));
+ //Runs the check and adds the result to a list
+ var result = await att.ExecuteChecksAsync(ctx);
+ dict.Add(att, result);
}
- else if (parameter.ParameterType == typeof(DiscordRole))
+
+ //Checks if any failed, and throws an exception
+ if (dict.Any(x => x.Value == false))
+ throw new SlashExecutionChecksFailedException { FailedChecks = dict.Where(x => x.Value == false).Select(x => x.Key).ToList() };
+ }
+ if (context is ContextMenuContext cMctx)
+ {
+ var attributes = new List<ContextMenuCheckBaseAttribute>();
+ attributes.AddRange(method.GetCustomAttributes<ContextMenuCheckBaseAttribute>(true));
+ attributes.AddRange(method.DeclaringType.GetCustomAttributes<ContextMenuCheckBaseAttribute>());
+ if (method.DeclaringType.DeclaringType != null)
{
- //Checks through resolved
- if (e.Interaction.Data.Resolved.Roles != null &&
- e.Interaction.Data.Resolved.Roles.TryGetValue((ulong)option.Value, out var role))
- args.Add(role);
- else
- args.Add(e.Interaction.Guild.GetRole((ulong)option.Value));
+ attributes.AddRange(method.DeclaringType.DeclaringType.GetCustomAttributes<ContextMenuCheckBaseAttribute>());
+ if (method.DeclaringType.DeclaringType.DeclaringType != null)
+ {
+ attributes.AddRange(method.DeclaringType.DeclaringType.DeclaringType.GetCustomAttributes<ContextMenuCheckBaseAttribute>());
+ }
}
- else if (parameter.ParameterType == typeof(SnowflakeObject))
+
+ var dict = new Dictionary<ContextMenuCheckBaseAttribute, bool>();
+ foreach (var att in attributes)
{
- //Checks through resolved
- if (e.Interaction.Data.Resolved.Roles != null && e.Interaction.Data.Resolved.Roles.TryGetValue((ulong)option.Value, out var role))
- args.Add(role);
- else if (e.Interaction.Data.Resolved.Members != null && e.Interaction.Data.Resolved.Members.TryGetValue((ulong)option.Value, out var member))
- args.Add(member);
- else if (e.Interaction.Data.Resolved.Users != null && e.Interaction.Data.Resolved.Users.TryGetValue((ulong)option.Value, out var user))
- args.Add(user);
- else
- throw new ArgumentException("Error resolving mentionable option.");
+ //Runs the check and adds the result to a list
+ var result = await att.ExecuteChecksAsync(cMctx);
+ dict.Add(att, result);
}
- else
- throw new ArgumentException($"Error resolving interaction.");
+
+ //Checks if any failed, and throws an exception
+ if (dict.Any(x => x.Value == false))
+ throw new ContextMenuExecutionChecksFailedException { FailedChecks = dict.Where(x => x.Value == false).Select(x => x.Key).ToList() };
}
}
- return args;
- }
-
- /// <summary>
- /// Runs the pre-execution checks.
- /// </summary>
- /// <param name="method">The method info.</param>
- /// <param name="context">The base context.</param>
- private async Task RunPreexecutionChecksAsync(MethodInfo method, BaseContext context)
- {
- if (context is InteractionContext ctx)
+ /// <summary>
+ /// Gets the choice attributes from choice provider.
+ /// </summary>
+ /// <param name="customAttributes">The custom attributes.</param>
+ /// <param name="guildId">The optional guild id</param>
+ private static async Task<List<DiscordApplicationCommandOptionChoice>> GetChoiceAttributesFromProvider(IEnumerable<ChoiceProviderAttribute> customAttributes, ulong? guildId = null)
{
- //Gets all attributes from parent classes as well and stuff
- var attributes = new List<SlashCheckBaseAttribute>();
- attributes.AddRange(method.GetCustomAttributes<SlashCheckBaseAttribute>(true));
- attributes.AddRange(method.DeclaringType.GetCustomAttributes<SlashCheckBaseAttribute>());
- if (method.DeclaringType.DeclaringType != null)
+ var choices = new List<DiscordApplicationCommandOptionChoice>();
+ foreach (var choiceProviderAttribute in customAttributes)
{
- attributes.AddRange(method.DeclaringType.DeclaringType.GetCustomAttributes<SlashCheckBaseAttribute>());
- if (method.DeclaringType.DeclaringType.DeclaringType != null)
+ var method = choiceProviderAttribute.ProviderType.GetMethod(nameof(IChoiceProvider.Provider));
+
+ if (method == null)
+ throw new ArgumentException("ChoiceProviders must inherit from IChoiceProvider.");
+ else
{
- attributes.AddRange(method.DeclaringType.DeclaringType.DeclaringType.GetCustomAttributes<SlashCheckBaseAttribute>());
+ var instance = Activator.CreateInstance(choiceProviderAttribute.ProviderType);
+
+ // Abstract class offers more properties that can be set
+ if (choiceProviderAttribute.ProviderType.IsSubclassOf(typeof(ChoiceProvider)))
+ {
+ choiceProviderAttribute.ProviderType.GetProperty(nameof(ChoiceProvider.GuildId))
+ ?.SetValue(instance, guildId);
+
+ choiceProviderAttribute.ProviderType.GetProperty(nameof(ChoiceProvider.Services))
+ ?.SetValue(instance, Configuration.ServiceProvider);
+ }
+
+ //Gets the choices from the method
+ var result = await (Task<IEnumerable<DiscordApplicationCommandOptionChoice>>)method.Invoke(instance, null);
+
+ if (result.Any())
+ {
+ choices.AddRange(result);
+ }
}
}
- var dict = new Dictionary<SlashCheckBaseAttribute, bool>();
- foreach (var att in attributes)
+ return choices;
+ }
+
+ /// <summary>
+ /// Gets the choice attributes from enum parameter.
+ /// </summary>
+ /// <param name="enumParam">The enum parameter.</param>
+ private static List<DiscordApplicationCommandOptionChoice> GetChoiceAttributesFromEnumParameter(Type enumParam)
+ {
+ var choices = new List<DiscordApplicationCommandOptionChoice>();
+ foreach (Enum enumValue in Enum.GetValues(enumParam))
{
- //Runs the check and adds the result to a list
- var result = await att.ExecuteChecksAsync(ctx);
- dict.Add(att, result);
+ choices.Add(new DiscordApplicationCommandOptionChoice(enumValue.GetName(), enumValue.ToString()));
}
+ return choices;
+ }
- //Checks if any failed, and throws an exception
- if (dict.Any(x => x.Value == false))
- throw new SlashExecutionChecksFailedException { FailedChecks = dict.Where(x => x.Value == false).Select(x => x.Key).ToList() };
+ /// <summary>
+ /// Gets the parameter type.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ private static ApplicationCommandOptionType GetParameterType(Type type)
+ {
+ var parameterType = type == typeof(string)
+ ? ApplicationCommandOptionType.String
+ : type == typeof(long) || type == typeof(long?) || type == typeof(int) || type == typeof(int?)
+ ? ApplicationCommandOptionType.Integer
+ : type == typeof(bool) || type == typeof(bool?)
+ ? ApplicationCommandOptionType.Boolean
+ : type == typeof(double) || type == typeof(double?)
+ ? ApplicationCommandOptionType.Number
+ : type == typeof(DiscordAttachment)
+ ? ApplicationCommandOptionType.Attachment
+ : type == typeof(DiscordChannel)
+ ? ApplicationCommandOptionType.Channel
+ : type == typeof(DiscordUser)
+ ? ApplicationCommandOptionType.User
+ : type == typeof(DiscordRole)
+ ? ApplicationCommandOptionType.Role
+ : type == typeof(SnowflakeObject)
+ ? ApplicationCommandOptionType.Mentionable
+ : type.IsEnum
+ ? ApplicationCommandOptionType.String
+ : throw new ArgumentException("Cannot convert type! Argument types must be string, int, long, bool, double, DiscordChannel, DiscordUser, DiscordRole, SnowflakeObject, DiscordAttachment or an Enum.");
+ return parameterType;
}
- if (context is ContextMenuContext cMctx)
+
+ /// <summary>
+ /// Gets the choice attributes from parameter.
+ /// </summary>
+ /// <param name="choiceAttributes">The choice attributes.</param>
+ private static List<DiscordApplicationCommandOptionChoice> GetChoiceAttributesFromParameter(IEnumerable<ChoiceAttribute> choiceAttributes) =>
+ !choiceAttributes.Any()
+ ? null
+ : choiceAttributes.Select(att => new DiscordApplicationCommandOptionChoice(att.Name, att.Value)).ToList();
+
+ /// <summary>
+ /// Parses the parameters.
+ /// </summary>
+ /// <param name="parameters">The parameters.</param>
+ /// <param name="guildId">The optional guild id.</param>
+ internal static async Task<List<DiscordApplicationCommandOption>> ParseParametersAsync(ParameterInfo[] parameters, ulong? guildId)
{
- var attributes = new List<ContextMenuCheckBaseAttribute>();
- attributes.AddRange(method.GetCustomAttributes<ContextMenuCheckBaseAttribute>(true));
- attributes.AddRange(method.DeclaringType.GetCustomAttributes<ContextMenuCheckBaseAttribute>());
- if (method.DeclaringType.DeclaringType != null)
+ var options = new List<DiscordApplicationCommandOption>();
+ foreach (var parameter in parameters)
{
- attributes.AddRange(method.DeclaringType.DeclaringType.GetCustomAttributes<ContextMenuCheckBaseAttribute>());
- if (method.DeclaringType.DeclaringType.DeclaringType != null)
+ //Gets the attribute
+ var optionAttribute = parameter.GetCustomAttribute<OptionAttribute>();
+ if (optionAttribute == null)
+ throw new ArgumentException("Arguments must have the Option attribute!");
+
+ var minimumValue = parameter.GetCustomAttribute<MinimumAttribute>()?.Value ?? null;
+ var maximumValue = parameter.GetCustomAttribute<MaximumAttribute>()?.Value ?? null;
+
+
+ var autocompleteAttribute = parameter.GetCustomAttribute<AutocompleteAttribute>();
+ if (optionAttribute.Autocomplete && autocompleteAttribute == null)
+ throw new ArgumentException("Autocomplete options must have the Autocomplete attribute!");
+ if (!optionAttribute.Autocomplete && autocompleteAttribute != null)
+ throw new ArgumentException("Setting an autocomplete provider requires the option to have autocomplete set to true!");
+
+ //Sets the type
+ var type = parameter.ParameterType;
+ var parameterType = GetParameterType(type);
+
+ //Handles choices
+ //From attributes
+ var choices = GetChoiceAttributesFromParameter(parameter.GetCustomAttributes<ChoiceAttribute>());
+ //From enums
+ if (parameter.ParameterType.IsEnum)
{
- attributes.AddRange(method.DeclaringType.DeclaringType.DeclaringType.GetCustomAttributes<ContextMenuCheckBaseAttribute>());
+ choices = GetChoiceAttributesFromEnumParameter(parameter.ParameterType);
+ }
+ //From choice provider
+ var choiceProviders = parameter.GetCustomAttributes<ChoiceProviderAttribute>();
+ if (choiceProviders.Any())
+ {
+ choices = await GetChoiceAttributesFromProvider(choiceProviders, guildId);
}
- }
- var dict = new Dictionary<ContextMenuCheckBaseAttribute, bool>();
- foreach (var att in attributes)
- {
- //Runs the check and adds the result to a list
- var result = await att.ExecuteChecksAsync(cMctx);
- dict.Add(att, result);
+ var channelTypes = parameter.GetCustomAttribute<ChannelTypesAttribute>()?.ChannelTypes ?? null;
+
+ options.Add(new DiscordApplicationCommandOption(optionAttribute.Name, optionAttribute.Description, parameterType, !parameter.IsOptional, choices, null, channelTypes, optionAttribute.Autocomplete, minimumValue, maximumValue));
}
- //Checks if any failed, and throws an exception
- if (dict.Any(x => x.Value == false))
- throw new ContextMenuExecutionChecksFailedException { FailedChecks = dict.Where(x => x.Value == false).Select(x => x.Key).ToList() };
+ return options;
}
- }
- /// <summary>
- /// Gets the choice attributes from choice provider.
- /// </summary>
- /// <param name="customAttributes">The custom attributes.</param>
- /// <param name="guildId">The optional guild id</param>
- private static async Task<List<DiscordApplicationCommandOptionChoice>> GetChoiceAttributesFromProvider(IEnumerable<ChoiceProviderAttribute> customAttributes, ulong? guildId = null)
- {
- var choices = new List<DiscordApplicationCommandOptionChoice>();
- foreach (var choiceProviderAttribute in customAttributes)
+ /// <summary>
+ /// <para>Refreshes your commands, used for refreshing choice providers or applying commands registered after the ready event on the discord client.
+ /// Should only be run on the slash command extension linked to shard 0 if sharding.</para>
+ /// <para>Not recommended and should be avoided since it can make slash commands be unresponsive for a while.</para>
+ /// </summary>
+ public async Task RefreshCommandsAsync()
{
- var method = choiceProviderAttribute.ProviderType.GetMethod(nameof(IChoiceProvider.Provider));
-
- if (method == null)
- throw new ArgumentException("ChoiceProviders must inherit from IChoiceProvider.");
- else
+ s_commandMethods.Clear();
+ s_groupCommands.Clear();
+ s_subGroupCommands.Clear();
+ s_registeredCommands.Clear();
+ s_contextMenuCommands.Clear();
+ GlobalDiscordCommands.Clear();
+ GuildDiscordCommands.Clear();
+ GuildCommandsInternal.Clear();
+ GlobalCommandsInternal.Clear();
+ GlobalDiscordCommands = null;
+ GuildDiscordCommands = null;
+ s_errored = false;
+
+ if (Configuration != null && Configuration.EnableDefaultHelp)
{
- var instance = Activator.CreateInstance(choiceProviderAttribute.ProviderType);
-
- // Abstract class offers more properties that can be set
- if (choiceProviderAttribute.ProviderType.IsSubclassOf(typeof(ChoiceProvider)))
- {
- choiceProviderAttribute.ProviderType.GetProperty(nameof(ChoiceProvider.GuildId))
- ?.SetValue(instance, guildId);
-
- choiceProviderAttribute.ProviderType.GetProperty(nameof(ChoiceProvider.Services))
- ?.SetValue(instance, Configuration.ServiceProvider);
- }
-
- //Gets the choices from the method
- var result = await (Task<IEnumerable<DiscordApplicationCommandOptionChoice>>)method.Invoke(instance, null);
-
- if (result.Any())
- {
- choices.AddRange(result);
- }
+ this._updateList.RemoveAll(x => x.Value.Type == typeof(DefaultHelpModule));
}
- }
- return choices;
- }
+ await this.UpdateAsync();
+ }
- /// <summary>
- /// Gets the choice attributes from enum parameter.
- /// </summary>
- /// <param name="enumParam">The enum parameter.</param>
- private static List<DiscordApplicationCommandOptionChoice> GetChoiceAttributesFromEnumParameter(Type enumParam)
- {
- var choices = new List<DiscordApplicationCommandOptionChoice>();
- foreach (Enum enumValue in Enum.GetValues(enumParam))
+ /// <summary>
+ /// Fires when the execution of a slash command fails.
+ /// </summary>
+ public event AsyncEventHandler<ApplicationCommandsExtension, SlashCommandErrorEventArgs> SlashCommandErrored
{
- choices.Add(new DiscordApplicationCommandOptionChoice(enumValue.GetName(), enumValue.ToString()));
+ add => this._slashError.Register(value);
+ remove => this._slashError.Unregister(value);
}
- return choices;
- }
-
- /// <summary>
- /// Gets the parameter type.
- /// </summary>
- /// <param name="type">The type.</param>
- private static ApplicationCommandOptionType GetParameterType(Type type)
- {
- var parameterType = type == typeof(string)
- ? ApplicationCommandOptionType.String
- : type == typeof(long) || type == typeof(long?) || type == typeof(int) || type == typeof(int?)
- ? ApplicationCommandOptionType.Integer
- : type == typeof(bool) || type == typeof(bool?)
- ? ApplicationCommandOptionType.Boolean
- : type == typeof(double) || type == typeof(double?)
- ? ApplicationCommandOptionType.Number
- : type == typeof(DiscordAttachment)
- ? ApplicationCommandOptionType.Attachment
- : type == typeof(DiscordChannel)
- ? ApplicationCommandOptionType.Channel
- : type == typeof(DiscordUser)
- ? ApplicationCommandOptionType.User
- : type == typeof(DiscordRole)
- ? ApplicationCommandOptionType.Role
- : type == typeof(SnowflakeObject)
- ? ApplicationCommandOptionType.Mentionable
- : type.IsEnum
- ? ApplicationCommandOptionType.String
- : throw new ArgumentException("Cannot convert type! Argument types must be string, int, long, bool, double, DiscordChannel, DiscordUser, DiscordRole, SnowflakeObject, DiscordAttachment or an Enum.");
- return parameterType;
- }
+ private AsyncEvent<ApplicationCommandsExtension, SlashCommandErrorEventArgs> _slashError;
- /// <summary>
- /// Gets the choice attributes from parameter.
- /// </summary>
- /// <param name="choiceAttributes">The choice attributes.</param>
- private static List<DiscordApplicationCommandOptionChoice> GetChoiceAttributesFromParameter(IEnumerable<ChoiceAttribute> choiceAttributes) =>
- !choiceAttributes.Any()
- ? null
- : choiceAttributes.Select(att => new DiscordApplicationCommandOptionChoice(att.Name, att.Value)).ToList();
-
- /// <summary>
- /// Parses the parameters.
- /// </summary>
- /// <param name="parameters">The parameters.</param>
- /// <param name="guildId">The optional guild id.</param>
- internal static async Task<List<DiscordApplicationCommandOption>> ParseParametersAsync(ParameterInfo[] parameters, ulong? guildId)
- {
- var options = new List<DiscordApplicationCommandOption>();
- foreach (var parameter in parameters)
+ /// <summary>
+ /// Fires when the execution of a slash command is successful.
+ /// </summary>
+ public event AsyncEventHandler<ApplicationCommandsExtension, SlashCommandExecutedEventArgs> SlashCommandExecuted
{
- //Gets the attribute
- var optionAttribute = parameter.GetCustomAttribute<OptionAttribute>();
- if (optionAttribute == null)
- throw new ArgumentException("Arguments must have the Option attribute!");
-
- var minimumValue = parameter.GetCustomAttribute<MinimumAttribute>()?.Value ?? null;
- var maximumValue = parameter.GetCustomAttribute<MaximumAttribute>()?.Value ?? null;
-
-
- var autocompleteAttribute = parameter.GetCustomAttribute<AutocompleteAttribute>();
- if (optionAttribute.Autocomplete && autocompleteAttribute == null)
- throw new ArgumentException("Autocomplete options must have the Autocomplete attribute!");
- if (!optionAttribute.Autocomplete && autocompleteAttribute != null)
- throw new ArgumentException("Setting an autocomplete provider requires the option to have autocomplete set to true!");
-
- //Sets the type
- var type = parameter.ParameterType;
- var parameterType = GetParameterType(type);
-
- //Handles choices
- //From attributes
- var choices = GetChoiceAttributesFromParameter(parameter.GetCustomAttributes<ChoiceAttribute>());
- //From enums
- if (parameter.ParameterType.IsEnum)
- {
- choices = GetChoiceAttributesFromEnumParameter(parameter.ParameterType);
- }
- //From choice provider
- var choiceProviders = parameter.GetCustomAttributes<ChoiceProviderAttribute>();
- if (choiceProviders.Any())
- {
- choices = await GetChoiceAttributesFromProvider(choiceProviders, guildId);
- }
-
- var channelTypes = parameter.GetCustomAttribute<ChannelTypesAttribute>()?.ChannelTypes ?? null;
+ add => this._slashExecuted.Register(value);
+ remove => this._slashExecuted.Unregister(value);
+ }
+ private AsyncEvent<ApplicationCommandsExtension, SlashCommandExecutedEventArgs> _slashExecuted;
- options.Add(new DiscordApplicationCommandOption(optionAttribute.Name, optionAttribute.Description, parameterType, !parameter.IsOptional, choices, null, channelTypes, optionAttribute.Autocomplete, minimumValue, maximumValue));
+ /// <summary>
+ /// Fires when the execution of a context menu fails.
+ /// </summary>
+ public event AsyncEventHandler<ApplicationCommandsExtension, ContextMenuErrorEventArgs> ContextMenuErrored
+ {
+ add => this._contextMenuErrored.Register(value);
+ remove => this._contextMenuErrored.Unregister(value);
}
+ private AsyncEvent<ApplicationCommandsExtension, ContextMenuErrorEventArgs> _contextMenuErrored;
- return options;
+ /// <summary>
+ /// Fire when the execution of a context menu is successful.
+ /// </summary>
+ public event AsyncEventHandler<ApplicationCommandsExtension, ContextMenuExecutedEventArgs> ContextMenuExecuted
+ {
+ add => this._contextMenuExecuted.Register(value);
+ remove => this._contextMenuExecuted.Unregister(value);
+ }
+ private AsyncEvent<ApplicationCommandsExtension, ContextMenuExecutedEventArgs> _contextMenuExecuted;
}
/// <summary>
- /// <para>Refreshes your commands, used for refreshing choice providers or applying commands registered after the ready event on the discord client.
- /// Should only be run on the slash command extension linked to shard 0 if sharding.</para>
- /// <para>Not recommended and should be avoided since it can make slash commands be unresponsive for a while.</para>
+ /// Holds configuration data for setting up an application command.
/// </summary>
- public async Task RefreshCommandsAsync()
+ internal class ApplicationCommandsModuleConfiguration
{
- s_commandMethods.Clear();
- s_groupCommands.Clear();
- s_subGroupCommands.Clear();
- s_registeredCommands.Clear();
- s_contextMenuCommands.Clear();
- GlobalDiscordCommands.Clear();
- GuildDiscordCommands.Clear();
- GuildCommandsInternal.Clear();
- GlobalCommandsInternal.Clear();
- GlobalDiscordCommands = null;
- GuildDiscordCommands = null;
- s_errored = false;
-
- if (Configuration != null && Configuration.EnableDefaultHelp)
+ /// <summary>
+ /// The type of the command module.
+ /// </summary>
+ public Type Type { get; }
+
+ /// <summary>
+ /// The translation setup.
+ /// </summary>
+ public Action<ApplicationCommandsTranslationContext> Translations { get; }
+
+ /// <summary>
+ /// Creates a new command configuration.
+ /// </summary>
+ /// <param name="type">The type of the command module.</param>
+ /// <param name="translations">The translation setup callback.</param>
+ public ApplicationCommandsModuleConfiguration(Type type, Action<ApplicationCommandsTranslationContext> translations = null)
{
- this._updateList.RemoveAll(x => x.Value.Type == typeof(DefaultHelpModule));
+ this.Type = type;
+ this.Translations = translations;
}
-
- await this.UpdateAsync();
}
/// <summary>
- /// Fires when the execution of a slash command fails.
+ /// Links a command to its original command module.
/// </summary>
- public event AsyncEventHandler<ApplicationCommandsExtension, SlashCommandErrorEventArgs> SlashCommandErrored
+ internal class ApplicationCommandSourceLink
{
- add => this._slashError.Register(value);
- remove => this._slashError.Unregister(value);
+ /// <summary>
+ /// The command.
+ /// </summary>
+ public DiscordApplicationCommand ApplicationCommand { get; set; }
+
+ /// <summary>
+ /// The base/root module the command is contained in.
+ /// </summary>
+ public Type RootCommandContainerType { get; set; }
+
+ /// <summary>
+ /// The direct group the command is contained in.
+ /// </summary>
+ public Type CommandContainerType { get; set; }
}
- private AsyncEvent<ApplicationCommandsExtension, SlashCommandErrorEventArgs> _slashError;
/// <summary>
- /// Fires when the execution of a slash command is successful.
+ /// The command method.
/// </summary>
- public event AsyncEventHandler<ApplicationCommandsExtension, SlashCommandExecutedEventArgs> SlashCommandExecuted
+ internal class CommandMethod
{
- add => this._slashExecuted.Register(value);
- remove => this._slashExecuted.Unregister(value);
+ /// <summary>
+ /// Gets or sets the command id.
+ /// </summary>
+ public ulong CommandId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the method.
+ /// </summary>
+ public MethodInfo Method { get; set; }
}
- private AsyncEvent<ApplicationCommandsExtension, SlashCommandExecutedEventArgs> _slashExecuted;
/// <summary>
- /// Fires when the execution of a context menu fails.
+ /// The group command.
/// </summary>
- public event AsyncEventHandler<ApplicationCommandsExtension, ContextMenuErrorEventArgs> ContextMenuErrored
+ internal class GroupCommand
{
- add => this._contextMenuErrored.Register(value);
- remove => this._contextMenuErrored.Unregister(value);
+ /// <summary>
+ /// Gets or sets the command id.
+ /// </summary>
+ public ulong CommandId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the methods.
+ /// </summary>
+ public List<KeyValuePair<string, MethodInfo>> Methods { get; set; } = null;
}
- private AsyncEvent<ApplicationCommandsExtension, ContextMenuErrorEventArgs> _contextMenuErrored;
/// <summary>
- /// Fire when the execution of a context menu is successful.
+ /// The sub group command.
/// </summary>
- public event AsyncEventHandler<ApplicationCommandsExtension, ContextMenuExecutedEventArgs> ContextMenuExecuted
+ internal class SubGroupCommand
{
- add => this._contextMenuExecuted.Register(value);
- remove => this._contextMenuExecuted.Unregister(value);
+ /// <summary>
+ /// Gets or sets the command id.
+ /// </summary>
+ public ulong CommandId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the sub commands.
+ /// </summary>
+ public List<GroupCommand> SubCommands { get; set; } = new();
}
- private AsyncEvent<ApplicationCommandsExtension, ContextMenuExecutedEventArgs> _contextMenuExecuted;
-}
-
-/// <summary>
-/// Holds configuration data for setting up an application command.
-/// </summary>
-internal class ApplicationCommandsModuleConfiguration
-{
- /// <summary>
- /// The type of the command module.
- /// </summary>
- public Type Type { get; }
-
- /// <summary>
- /// The translation setup.
- /// </summary>
- public Action<ApplicationCommandsTranslationContext> Translations { get; }
/// <summary>
- /// Creates a new command configuration.
+ /// The context menu command.
/// </summary>
- /// <param name="type">The type of the command module.</param>
- /// <param name="translations">The translation setup callback.</param>
- public ApplicationCommandsModuleConfiguration(Type type, Action<ApplicationCommandsTranslationContext> translations = null)
+ internal class ContextMenuCommand
{
- this.Type = type;
- this.Translations = translations;
+ /// <summary>
+ /// Gets or sets the command id.
+ /// </summary>
+ public ulong CommandId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the method.
+ /// </summary>
+ public MethodInfo Method { get; set; }
}
-}
-
-/// <summary>
-/// Links a command to its original command module.
-/// </summary>
-internal class ApplicationCommandSourceLink
-{
- /// <summary>
- /// The command.
- /// </summary>
- public DiscordApplicationCommand ApplicationCommand { get; set; }
-
- /// <summary>
- /// The base/root module the command is contained in.
- /// </summary>
- public Type RootCommandContainerType { get; set; }
-
- /// <summary>
- /// The direct group the command is contained in.
- /// </summary>
- public Type CommandContainerType { get; set; }
-}
-
-/// <summary>
-/// The command method.
-/// </summary>
-internal class CommandMethod
-{
- /// <summary>
- /// Gets or sets the command id.
- /// </summary>
- public ulong CommandId { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the method.
- /// </summary>
- public MethodInfo Method { get; set; }
-}
-
-/// <summary>
-/// The group command.
-/// </summary>
-internal class GroupCommand
-{
- /// <summary>
- /// Gets or sets the command id.
- /// </summary>
- public ulong CommandId { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the methods.
- /// </summary>
- public List<KeyValuePair<string, MethodInfo>> Methods { get; set; } = null;
-}
-
-/// <summary>
-/// The sub group command.
-/// </summary>
-internal class SubGroupCommand
-{
- /// <summary>
- /// Gets or sets the command id.
- /// </summary>
- public ulong CommandId { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the sub commands.
- /// </summary>
- public List<GroupCommand> SubCommands { get; set; } = new();
-}
-
-/// <summary>
-/// The context menu command.
-/// </summary>
-internal class ContextMenuCommand
-{
- /// <summary>
- /// Gets or sets the command id.
- /// </summary>
- public ulong CommandId { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- public string Name { get; set; }
+ #region Default Help
/// <summary>
- /// Gets or sets the method.
+ /// Represents the default help module.
/// </summary>
- public MethodInfo Method { get; set; }
-}
-
-#region Default Help
-/// <summary>
-/// Represents the default help module.
-/// </summary>
-internal class DefaultHelpModule : ApplicationCommandsModule
-{
- public class DefaultHelpAutoCompleteProvider : IAutocompleteProvider
+ internal class DefaultHelpModule : ApplicationCommandsModule
{
- public async Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>> Provider(AutocompleteContext context)
+ public class DefaultHelpAutoCompleteProvider : IAutocompleteProvider
{
- var options = new List<DiscordApplicationCommandAutocompleteChoice>();
-
- IEnumerable<DiscordApplicationCommand> slashCommands = null;
- var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync();
- if (context.Guild != null)
+ public async Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>> Provider(AutocompleteContext context)
{
- var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id);
- await Task.WhenAll(globalCommandsTask, guildCommandsTask);
- slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result)
- .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
- .GroupBy(ac => ac.Name).Select(x => x.First())
- .Where(ac => ac.Name.StartsWith(context.Options[0].Value.ToString(), StringComparison.OrdinalIgnoreCase))
- .ToList();
- }
- else
- {
- await Task.WhenAll(globalCommandsTask);
- slashCommands = globalCommandsTask.Result
- .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
- .GroupBy(ac => ac.Name).Select(x => x.First())
- .Where(ac => ac.Name.StartsWith(context.Options[0].Value.ToString(), StringComparison.OrdinalIgnoreCase))
- .ToList();
- }
+ var options = new List<DiscordApplicationCommandAutocompleteChoice>();
- foreach (var sc in slashCommands.Take(25))
- {
- options.Add(new DiscordApplicationCommandAutocompleteChoice(sc.Name, sc.Name.Trim()));
+ IEnumerable<DiscordApplicationCommand> slashCommands = null;
+ var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync();
+ if (context.Guild != null)
+ {
+ var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id);
+ await Task.WhenAll(globalCommandsTask, guildCommandsTask);
+ slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result)
+ .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
+ .GroupBy(ac => ac.Name).Select(x => x.First())
+ .Where(ac => ac.Name.StartsWith(context.Options[0].Value.ToString(), StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ }
+ else
+ {
+ await Task.WhenAll(globalCommandsTask);
+ slashCommands = globalCommandsTask.Result
+ .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
+ .GroupBy(ac => ac.Name).Select(x => x.First())
+ .Where(ac => ac.Name.StartsWith(context.Options[0].Value.ToString(), StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ }
+
+ foreach (var sc in slashCommands.Take(25))
+ {
+ options.Add(new DiscordApplicationCommandAutocompleteChoice(sc.Name, sc.Name.Trim()));
+ }
+ return options.AsEnumerable();
}
- return options.AsEnumerable();
}
- }
- public class DefaultHelpAutoCompleteLevelOneProvider : IAutocompleteProvider
- {
- public async Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>> Provider(AutocompleteContext context)
+ public class DefaultHelpAutoCompleteLevelOneProvider : IAutocompleteProvider
{
- var options = new List<DiscordApplicationCommandAutocompleteChoice>();
- IEnumerable<DiscordApplicationCommand> slashCommands = null;
- var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync();
- if (context.Guild != null)
+ public async Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>> Provider(AutocompleteContext context)
{
- var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id);
- await Task.WhenAll(globalCommandsTask, guildCommandsTask);
- slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result)
- .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
- .GroupBy(ac => ac.Name).Select(x => x.First());
- }
- else
- {
- await Task.WhenAll(globalCommandsTask);
- slashCommands = globalCommandsTask.Result
- .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
- .GroupBy(ac => ac.Name).Select(x => x.First());
- }
+ var options = new List<DiscordApplicationCommandAutocompleteChoice>();
+ IEnumerable<DiscordApplicationCommand> slashCommands = null;
+ var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync();
+ if (context.Guild != null)
+ {
+ var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id);
+ await Task.WhenAll(globalCommandsTask, guildCommandsTask);
+ slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result)
+ .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
+ .GroupBy(ac => ac.Name).Select(x => x.First());
+ }
+ else
+ {
+ await Task.WhenAll(globalCommandsTask);
+ slashCommands = globalCommandsTask.Result
+ .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
+ .GroupBy(ac => ac.Name).Select(x => x.First());
+ }
- var command = slashCommands.FirstOrDefault(ac =>
- ac.Name.Equals(context.Options[0].Value.ToString().Trim(),StringComparison.OrdinalIgnoreCase));
- if (command is null || command.Options is null)
- {
- options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command"));
- }
- else
- {
- var opt = command.Options.Where(c => c.Type is ApplicationCommandOptionType.SubCommandGroup or ApplicationCommandOptionType.SubCommand
- && c.Name.StartsWith(context.Options[1].Value.ToString(), StringComparison.InvariantCultureIgnoreCase)).ToList();
- foreach (var option in opt.Take(25))
+ var command = slashCommands.FirstOrDefault(ac =>
+ ac.Name.Equals(context.Options[0].Value.ToString().Trim(),StringComparison.OrdinalIgnoreCase));
+ if (command is null || command.Options is null)
+ {
+ options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command"));
+ }
+ else
{
- options.Add(new DiscordApplicationCommandAutocompleteChoice(option.Name, option.Name.Trim()));
+ var opt = command.Options.Where(c => c.Type is ApplicationCommandOptionType.SubCommandGroup or ApplicationCommandOptionType.SubCommand
+ && c.Name.StartsWith(context.Options[1].Value.ToString(), StringComparison.InvariantCultureIgnoreCase)).ToList();
+ foreach (var option in opt.Take(25))
+ {
+ options.Add(new DiscordApplicationCommandAutocompleteChoice(option.Name, option.Name.Trim()));
+ }
}
+ return options.AsEnumerable();
}
- return options.AsEnumerable();
}
- }
- public class DefaultHelpAutoCompleteLevelTwoProvider : IAutocompleteProvider
- {
- public async Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>> Provider(AutocompleteContext context)
+ public class DefaultHelpAutoCompleteLevelTwoProvider : IAutocompleteProvider
{
- var options = new List<DiscordApplicationCommandAutocompleteChoice>();
- IEnumerable<DiscordApplicationCommand> slashCommands = null;
- var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync();
- if (context.Guild != null)
- {
- var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id);
- await Task.WhenAll(globalCommandsTask, guildCommandsTask);
- slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result)
- .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
- .GroupBy(ac => ac.Name).Select(x => x.First());
- }
- else
+ public async Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>> Provider(AutocompleteContext context)
{
- await Task.WhenAll(globalCommandsTask);
- slashCommands = globalCommandsTask.Result
- .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
- .GroupBy(ac => ac.Name).Select(x => x.First());
- }
+ var options = new List<DiscordApplicationCommandAutocompleteChoice>();
+ IEnumerable<DiscordApplicationCommand> slashCommands = null;
+ var globalCommandsTask = context.Client.GetGlobalApplicationCommandsAsync();
+ if (context.Guild != null)
+ {
+ var guildCommandsTask = context.Client.GetGuildApplicationCommandsAsync(context.Guild.Id);
+ await Task.WhenAll(globalCommandsTask, guildCommandsTask);
+ slashCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result)
+ .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
+ .GroupBy(ac => ac.Name).Select(x => x.First());
+ }
+ else
+ {
+ await Task.WhenAll(globalCommandsTask);
+ slashCommands = globalCommandsTask.Result
+ .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
+ .GroupBy(ac => ac.Name).Select(x => x.First());
+ }
- var command = slashCommands.FirstOrDefault(ac =>
- ac.Name.Equals(context.Options[0].Value.ToString().Trim(), StringComparison.OrdinalIgnoreCase));
- if (command.Options is null)
- {
- options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command"));
- return options.AsEnumerable();
- }
- var foundCommand = command.Options.FirstOrDefault(op => op.Name.Equals(context.Options[1].Value.ToString().Trim(), StringComparison.OrdinalIgnoreCase));
- if (foundCommand is null || foundCommand.Options is null)
- {
- options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command"));
- }
- else
- {
- var opt = foundCommand.Options.Where(x => x.Type == ApplicationCommandOptionType.SubCommand &&
- x.Name.StartsWith(context.Options[2].Value.ToString(), StringComparison.OrdinalIgnoreCase)).ToList();
- foreach (var option in opt.Take(25))
+ var command = slashCommands.FirstOrDefault(ac =>
+ ac.Name.Equals(context.Options[0].Value.ToString().Trim(), StringComparison.OrdinalIgnoreCase));
+ if (command.Options is null)
+ {
+ options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command"));
+ return options.AsEnumerable();
+ }
+ var foundCommand = command.Options.FirstOrDefault(op => op.Name.Equals(context.Options[1].Value.ToString().Trim(), StringComparison.OrdinalIgnoreCase));
+ if (foundCommand is null || foundCommand.Options is null)
+ {
+ options.Add(new DiscordApplicationCommandAutocompleteChoice("no_options_for_this_command", "no_options_for_this_command"));
+ }
+ else
{
- options.Add(new DiscordApplicationCommandAutocompleteChoice(option.Name, option.Name.Trim()));
+ var opt = foundCommand.Options.Where(x => x.Type == ApplicationCommandOptionType.SubCommand &&
+ x.Name.StartsWith(context.Options[2].Value.ToString(), StringComparison.OrdinalIgnoreCase)).ToList();
+ foreach (var option in opt.Take(25))
+ {
+ options.Add(new DiscordApplicationCommandAutocompleteChoice(option.Name, option.Name.Trim()));
+ }
}
+ return options.AsEnumerable();
}
- return options.AsEnumerable();
}
- }
- [SlashCommand("help", "Displays command help")]
- internal async Task DefaultHelpAsync(InteractionContext ctx,
- [Autocomplete(typeof(DefaultHelpAutoCompleteProvider))]
- [Option("option_one", "top level command to provide help for", true)] string commandName,
- [Autocomplete(typeof(DefaultHelpAutoCompleteLevelOneProvider))]
- [Option("option_two", "subgroup or command to provide help for", true)] string commandOneName = null,
- [Autocomplete(typeof(DefaultHelpAutoCompleteLevelTwoProvider))]
- [Option("option_three", "command to provide help for", true)] string commandTwoName = null)
- {
- var globalCommandsTask = ctx.Client.GetGlobalApplicationCommandsAsync();
- var guildCommandsTask= ctx.Client.GetGuildApplicationCommandsAsync(ctx.Guild.Id);
+ [SlashCommand("help", "Displays command help")]
+ internal async Task DefaultHelpAsync(InteractionContext ctx,
+ [Autocomplete(typeof(DefaultHelpAutoCompleteProvider))]
+ [Option("option_one", "top level command to provide help for", true)] string commandName,
+ [Autocomplete(typeof(DefaultHelpAutoCompleteLevelOneProvider))]
+ [Option("option_two", "subgroup or command to provide help for", true)] string commandOneName = null,
+ [Autocomplete(typeof(DefaultHelpAutoCompleteLevelTwoProvider))]
+ [Option("option_three", "command to provide help for", true)] string commandTwoName = null)
+ {
+ var globalCommandsTask = ctx.Client.GetGlobalApplicationCommandsAsync();
+ var guildCommandsTask= ctx.Client.GetGuildApplicationCommandsAsync(ctx.Guild.Id);
- await Task.WhenAll(globalCommandsTask, guildCommandsTask);
+ await Task.WhenAll(globalCommandsTask, guildCommandsTask);
- var applicationCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result)
- .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
- .GroupBy(ac => ac.Name).Select(x => x.First())
- .ToList();
- if (applicationCommands.Count < 1)
- {
- await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder()
- .WithContent($"There are no slash commands for guild {ctx.Guild.Name}").AsEphemeral(true));
- return;
- }
- if (commandTwoName is not null && !commandTwoName.Equals("no_options_for_this_command"))
- {
- var commandsWithSubCommands = applicationCommands.FindAll(ac => ac.Options is not null && ac.Options.Any(op => op.Type == ApplicationCommandOptionType.SubCommandGroup));
- var cmdParent = commandsWithSubCommands.FirstOrDefault(cm => cm.Options.Any(op => op.Name.Equals(commandOneName))).Options
- .FirstOrDefault(opt => opt.Name.Equals(commandOneName,StringComparison.OrdinalIgnoreCase));
- var cmd = cmdParent.Options.FirstOrDefault(op => op.Name.Equals(commandTwoName,StringComparison.OrdinalIgnoreCase));
- var discordEmbed = new DiscordEmbedBuilder
+ var applicationCommands = globalCommandsTask.Result.Concat(guildCommandsTask.Result)
+ .Where(ac => !ac.Name.Equals("help", StringComparison.OrdinalIgnoreCase))
+ .GroupBy(ac => ac.Name).Select(x => x.First())
+ .ToList();
+ if (applicationCommands.Count < 1)
{
- Title = "Help",
- Description = $"{Formatter.InlineCode(cmd.Name)}: {cmd.Description ?? "No description provided."}"
- };
- if (cmd.Options is not null)
+ await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder()
+ .WithContent($"There are no slash commands for guild {ctx.Guild.Name}").AsEphemeral(true));
+ return;
+ }
+ if (commandTwoName is not null && !commandTwoName.Equals("no_options_for_this_command"))
{
- var commandOptions = cmd.Options.ToList();
- var sb = new StringBuilder();
+ var commandsWithSubCommands = applicationCommands.FindAll(ac => ac.Options is not null && ac.Options.Any(op => op.Type == ApplicationCommandOptionType.SubCommandGroup));
+ var cmdParent = commandsWithSubCommands.FirstOrDefault(cm => cm.Options.Any(op => op.Name.Equals(commandOneName))).Options
+ .FirstOrDefault(opt => opt.Name.Equals(commandOneName,StringComparison.OrdinalIgnoreCase));
+ var cmd = cmdParent.Options.FirstOrDefault(op => op.Name.Equals(commandTwoName,StringComparison.OrdinalIgnoreCase));
+ var discordEmbed = new DiscordEmbedBuilder
+ {
+ Title = "Help",
+ Description = $"{Formatter.InlineCode(cmd.Name)}: {cmd.Description ?? "No description provided."}"
+ };
+ if (cmd.Options is not null)
+ {
+ var commandOptions = cmd.Options.ToList();
+ var sb = new StringBuilder();
- foreach (var option in commandOptions)
- sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n');
+ foreach (var option in commandOptions)
+ sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n');
- sb.Append('\n');
- discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim()));
+ sb.Append('\n');
+ discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim()));
+ }
+ await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource,
+ new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true));
}
- await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource,
- new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true));
- }
- else if (commandOneName is not null && commandTwoName is null && !commandOneName.Equals("no_options_for_this_command"))
- {
- var commandsWithOptions = applicationCommands.FindAll(ac => ac.Options is not null && ac.Options.All(op => op.Type == ApplicationCommandOptionType.SubCommand));
- var subCommandParent = commandsWithOptions.FirstOrDefault(cm => cm.Name.Equals(commandName,StringComparison.OrdinalIgnoreCase));
- var subCommand = subCommandParent.Options.FirstOrDefault(op => op.Name.Equals(commandOneName,StringComparison.OrdinalIgnoreCase));
- var discordEmbed = new DiscordEmbedBuilder
+ else if (commandOneName is not null && commandTwoName is null && !commandOneName.Equals("no_options_for_this_command"))
{
- Title = "Help",
- Description = $"{Formatter.InlineCode(subCommand.Name)}: {subCommand.Description ?? "No description provided."}"
- };
- if (subCommand.Options is not null)
- {
- var commandOptions = subCommand.Options.ToList();
- var sb = new StringBuilder();
+ var commandsWithOptions = applicationCommands.FindAll(ac => ac.Options is not null && ac.Options.All(op => op.Type == ApplicationCommandOptionType.SubCommand));
+ var subCommandParent = commandsWithOptions.FirstOrDefault(cm => cm.Name.Equals(commandName,StringComparison.OrdinalIgnoreCase));
+ var subCommand = subCommandParent.Options.FirstOrDefault(op => op.Name.Equals(commandOneName,StringComparison.OrdinalIgnoreCase));
+ var discordEmbed = new DiscordEmbedBuilder
+ {
+ Title = "Help",
+ Description = $"{Formatter.InlineCode(subCommand.Name)}: {subCommand.Description ?? "No description provided."}"
+ };
+ if (subCommand.Options is not null)
+ {
+ var commandOptions = subCommand.Options.ToList();
+ var sb = new StringBuilder();
- foreach (var option in commandOptions)
- sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n');
+ foreach (var option in commandOptions)
+ sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n');
- sb.Append('\n');
- discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim()));
- }
- await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource,
- new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true));
- }
- else
- {
- var command = applicationCommands.FirstOrDefault(cm => cm.Name.Equals(commandName, StringComparison.OrdinalIgnoreCase));
- if (command is null)
- {
- await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder()
- .WithContent($"No command called {commandName} in guild {ctx.Guild.Name}").AsEphemeral(true));
- return;
+ sb.Append('\n');
+ discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim()));
+ }
+ await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource,
+ new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true));
}
- var discordEmbed = new DiscordEmbedBuilder
- {
- Title = "Help",
- Description = $"{Formatter.InlineCode(command.Name)}: {command.Description ?? "No description provided."}"
- };
- if (command.Options is not null)
+ else
{
- var commandOptions = command.Options.ToList();
- var sb = new StringBuilder();
+ var command = applicationCommands.FirstOrDefault(cm => cm.Name.Equals(commandName, StringComparison.OrdinalIgnoreCase));
+ if (command is null)
+ {
+ await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder()
+ .WithContent($"No command called {commandName} in guild {ctx.Guild.Name}").AsEphemeral(true));
+ return;
+ }
+ var discordEmbed = new DiscordEmbedBuilder
+ {
+ Title = "Help",
+ Description = $"{Formatter.InlineCode(command.Name)}: {command.Description ?? "No description provided."}"
+ };
+ if (command.Options is not null)
+ {
+ var commandOptions = command.Options.ToList();
+ var sb = new StringBuilder();
- foreach (var option in commandOptions)
- sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n');
+ foreach (var option in commandOptions)
+ sb.Append('`').Append(option.Name).Append(" (").Append(")`: ").Append(option.Description ?? "No description provided.").Append('\n');
- sb.Append('\n');
- discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim()));
+ sb.Append('\n');
+ discordEmbed.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim()));
+ }
+ await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource,
+ new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true));
}
- await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource,
- new DiscordInteractionResponseBuilder().AddEmbed(discordEmbed).AsEphemeral(true));
}
}
+ #endregion
}
-#endregion
diff --git a/DisCatSharp.ApplicationCommands/ApplicationCommandsModule.cs b/DisCatSharp.ApplicationCommands/ApplicationCommandsModule.cs
index 9789c3887..dcf9377da 100644
--- a/DisCatSharp.ApplicationCommands/ApplicationCommandsModule.cs
+++ b/DisCatSharp.ApplicationCommands/ApplicationCommandsModule.cs
@@ -1,63 +1,64 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a base class for application command modules
-/// </summary>
-public abstract class ApplicationCommandsModule
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Called before the execution of a slash command in the module.
+ /// Represents a base class for application command modules
/// </summary>
- /// <param name="ctx">The context.</param>
- /// <returns> Whether or not to execute the slash command.</returns>
- public virtual Task<bool> BeforeSlashExecutionAsync(InteractionContext ctx)
- => Task.FromResult(true);
+ public abstract class ApplicationCommandsModule
+ {
+ /// <summary>
+ /// Called before the execution of a slash command in the module.
+ /// </summary>
+ /// <param name="ctx">The context.</param>
+ /// <returns> Whether or not to execute the slash command.</returns>
+ public virtual Task<bool> BeforeSlashExecutionAsync(InteractionContext ctx)
+ => Task.FromResult(true);
- /// <summary>
- /// Called after the execution of a slash command in the module.
- /// </summary>
- /// <param name="ctx">The context.</param>
- /// <returns></returns>
- public virtual Task AfterSlashExecutionAsync(InteractionContext ctx)
- => Task.CompletedTask;
+ /// <summary>
+ /// Called after the execution of a slash command in the module.
+ /// </summary>
+ /// <param name="ctx">The context.</param>
+ /// <returns></returns>
+ public virtual Task AfterSlashExecutionAsync(InteractionContext ctx)
+ => Task.CompletedTask;
- /// <summary>
- /// Called before the execution of a context menu in the module.
- /// </summary>
- /// <param name="ctx">The context.</param>
- /// <returns> Whether or not to execute the slash command. </returns>
- public virtual Task<bool> BeforeContextMenuExecutionAsync(ContextMenuContext ctx)
- => Task.FromResult(true);
+ /// <summary>
+ /// Called before the execution of a context menu in the module.
+ /// </summary>
+ /// <param name="ctx">The context.</param>
+ /// <returns> Whether or not to execute the slash command. </returns>
+ public virtual Task<bool> BeforeContextMenuExecutionAsync(ContextMenuContext ctx)
+ => Task.FromResult(true);
- /// <summary>
- /// Called after the execution of a context menu in the module.
- /// </summary>
- /// <param name="ctx">The context.</param>
- /// <returns></returns>
- public virtual Task AfterContextMenuExecutionAsync(ContextMenuContext ctx)
- => Task.CompletedTask;
+ /// <summary>
+ /// Called after the execution of a context menu in the module.
+ /// </summary>
+ /// <param name="ctx">The context.</param>
+ /// <returns></returns>
+ public virtual Task AfterContextMenuExecutionAsync(ContextMenuContext ctx)
+ => Task.CompletedTask;
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/ApplicationCommandModuleLifespanAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/ApplicationCommandModuleLifespanAttribute.cs
index 2b5588c78..9da35c626 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/ApplicationCommandModuleLifespanAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/ApplicationCommandModuleLifespanAttribute.cs
@@ -1,67 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands;
-
-/// <summary>
-/// Defines this application command module's lifespan. Module lifespans are transient by default.
-/// </summary>
-[AttributeUsage(AttributeTargets.Class)]
-public class ApplicationCommandModuleLifespanAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
- /// <summary>
- /// Gets the lifespan.
- /// </summary>
- public ApplicationCommandModuleLifespan Lifespan { get; }
/// <summary>
- /// Defines this application command module's lifespan.
+ /// Defines this application command module's lifespan. Module lifespans are transient by default.
/// </summary>
- /// <param name="lifespan">The lifespan of the module. Module lifespans are transient by default.</param>
- public ApplicationCommandModuleLifespanAttribute(ApplicationCommandModuleLifespan lifespan)
+ [AttributeUsage(AttributeTargets.Class)]
+ public class ApplicationCommandModuleLifespanAttribute : Attribute
{
- this.Lifespan = lifespan;
+ /// <summary>
+ /// Gets the lifespan.
+ /// </summary>
+ public ApplicationCommandModuleLifespan Lifespan { get; }
+
+ /// <summary>
+ /// Defines this application command module's lifespan.
+ /// </summary>
+ /// <param name="lifespan">The lifespan of the module. Module lifespans are transient by default.</param>
+ public ApplicationCommandModuleLifespanAttribute(ApplicationCommandModuleLifespan lifespan)
+ {
+ this.Lifespan = lifespan;
+ }
}
-}
-/// <summary>
-/// Represents a application command module lifespan.
-/// </summary>
-public enum ApplicationCommandModuleLifespan
-{
/// <summary>
- /// Whether this module should be initiated every time a command is run, with dependencies injected from a scope.
+ /// Represents a application command module lifespan.
/// </summary>
- Scoped,
+ public enum ApplicationCommandModuleLifespan
+ {
+ /// <summary>
+ /// Whether this module should be initiated every time a command is run, with dependencies injected from a scope.
+ /// </summary>
+ Scoped,
- /// <summary>
- /// Whether this module should be initiated every time a command is run.
- /// </summary>
- Transient,
+ /// <summary>
+ /// Whether this module should be initiated every time a command is run.
+ /// </summary>
+ Transient,
- /// <summary>
- /// Whether this module should be initiated at startup.
- /// </summary>
- Singleton
+ /// <summary>
+ /// Whether this module should be initiated at startup.
+ /// </summary>
+ Singleton
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuAttribute.cs
index 00c18d671..d1c90d4fc 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuAttribute.cs
@@ -1,88 +1,90 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Enums;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Marks this method as a context menu.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method)]
-public sealed class ContextMenuAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
- /// <summary>
- /// Gets the name of this context menu.
- /// </summary>
- public string Name { get; internal set; }
/// <summary>
- /// Gets the type of this context menu.
+ /// Marks this method as a context menu.
/// </summary>
- public ApplicationCommandType Type { get; internal set; }
+ [AttributeUsage(AttributeTargets.Method)]
+ public sealed class ContextMenuAttribute : Attribute
+ {
+ /// <summary>
+ /// Gets the name of this context menu.
+ /// </summary>
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the commands needed permissions.
- /// </summary>
- public Permissions? DefaultMemberPermissions { get; internal set; }
+ /// <summary>
+ /// Gets the type of this context menu.
+ /// </summary>
+ public ApplicationCommandType Type { get; internal set; }
- /// <summary>
- /// Gets whether the command can be used in direct messages.
- /// </summary>
- internal bool? DmPermission { get; set; }
+ /// <summary>
+ /// Gets the commands needed permissions.
+ /// </summary>
+ public Permissions? DefaultMemberPermissions { get; internal set; }
- /// <summary>
- /// Marks this method as a context menu.
- /// </summary>
- /// <param name="type">The type of the context menu.</param>
- /// <param name="name">The name of the context menu.</param>
- public ContextMenuAttribute(ApplicationCommandType type, string name)
- {
- if (type == ApplicationCommandType.ChatInput)
- throw new ArgumentException("Context menus cannot be of type ChatInput (Slash).");
-
- this.Type = type;
- this.Name = name;
- this.DefaultMemberPermissions = null;
- this.DmPermission = null;
- }
+ /// <summary>
+ /// Gets whether the command can be used in direct messages.
+ /// </summary>
+ internal bool? DmPermission { get; set; }
+ /// <summary>
+ /// Marks this method as a context menu.
+ /// </summary>
+ /// <param name="type">The type of the context menu.</param>
+ /// <param name="name">The name of the context menu.</param>
+ public ContextMenuAttribute(ApplicationCommandType type, string name)
+ {
+ if (type == ApplicationCommandType.ChatInput)
+ throw new ArgumentException("Context menus cannot be of type ChatInput (Slash).");
+
+ this.Type = type;
+ this.Name = name;
+ this.DefaultMemberPermissions = null;
+ this.DmPermission = null;
+ }
- /// <summary>
- /// Marks this method as a context menu.
- /// </summary>
- /// <param name="type">The type of the context menu.</param>
- /// <param name="name">The name of the context menu.</param>
- /// <param name="defaultMemberPermissions">The default member permissions.</param>
- public ContextMenuAttribute(ApplicationCommandType type, string name, long defaultMemberPermissions)
- {
- if (type == ApplicationCommandType.ChatInput)
- throw new ArgumentException("Context menus cannot be of type ChatInput (Slash).");
- this.Type = type;
- this.Name = name;
- this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
- this.DmPermission = null;
+ /// <summary>
+ /// Marks this method as a context menu.
+ /// </summary>
+ /// <param name="type">The type of the context menu.</param>
+ /// <param name="name">The name of the context menu.</param>
+ /// <param name="defaultMemberPermissions">The default member permissions.</param>
+ public ContextMenuAttribute(ApplicationCommandType type, string name, long defaultMemberPermissions)
+ {
+ if (type == ApplicationCommandType.ChatInput)
+ throw new ArgumentException("Context menus cannot be of type ChatInput (Slash).");
+
+ this.Type = type;
+ this.Name = name;
+ this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
+ this.DmPermission = null;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuCheckBaseAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuCheckBaseAttribute.cs
index 35e3d26f8..057945423 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuCheckBaseAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/ContextMenu/ContextMenuCheckBaseAttribute.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// The base class for a pre-execution check for a context menu.
-/// </summary>
-public abstract class ContextMenuCheckBaseAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Checks whether this command can be executed within the current context.
+ /// The base class for a pre-execution check for a context menu.
/// </summary>
- /// <param name="ctx">The context.</param>
- /// <returns>Whether the checks passed.</returns>
- public abstract Task<bool> ExecuteChecksAsync(ContextMenuContext ctx);
+ public abstract class ContextMenuCheckBaseAttribute : Attribute
+ {
+ /// <summary>
+ /// Checks whether this command can be executed within the current context.
+ /// </summary>
+ /// <param name="ctx">The context.</param>
+ /// <returns>Whether the checks passed.</returns>
+ public abstract Task<bool> ExecuteChecksAsync(ContextMenuContext ctx);
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/DontInjectAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/DontInjectAttribute.cs
index e71d1cc1b..c3011b5df 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/DontInjectAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/DontInjectAttribute.cs
@@ -1,32 +1,33 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands;
-
-/// <summary>
-/// Prevents this field or property from having its value injected by dependency injection.
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public class DontInjectAttribute : Attribute
-{ }
+namespace DisCatSharp.ApplicationCommands
+{
+ /// <summary>
+ /// Prevents this field or property from having its value injected by dependency injection.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public class DontInjectAttribute : Attribute
+ { }
+}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/AutocompleteAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/AutocompleteAttribute.cs
index a64df8873..d2aaee1ce 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/AutocompleteAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/AutocompleteAttribute.cs
@@ -1,46 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands.Attributes;
-
-/// <summary>
-/// The autocomplete attribute.
-/// </summary>
-[AttributeUsage(AttributeTargets.Parameter)]
-public class AutocompleteAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
- /// <summary>
- /// The type of the provider.
- /// </summary>
- public Type ProviderType { get; }
/// <summary>
- /// Adds an autocomplete provider to this command option.
+ /// The autocomplete attribute.
/// </summary>
- /// <param name="providerType">The type of the provider.</param>
- public AutocompleteAttribute(Type providerType)
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class AutocompleteAttribute : Attribute
{
- this.ProviderType = providerType;
+ /// <summary>
+ /// The type of the provider.
+ /// </summary>
+ public Type ProviderType { get; }
+
+ /// <summary>
+ /// Adds an autocomplete provider to this command option.
+ /// </summary>
+ /// <param name="providerType">The type of the provider.</param>
+ public AutocompleteAttribute(Type providerType)
+ {
+ this.ProviderType = providerType;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChannelTypesAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChannelTypesAttribute.cs
index 8060f26b4..09735feaf 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChannelTypesAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChannelTypesAttribute.cs
@@ -1,47 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Defines allowed channel types for a channel parameter.
-/// </summary>
-[AttributeUsage(AttributeTargets.Parameter)]
-public class ChannelTypesAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
- /// <summary>
- /// Allowed channel types.
- /// </summary>
- public IEnumerable<ChannelType> ChannelTypes { get; }
-
/// <summary>
/// Defines allowed channel types for a channel parameter.
/// </summary>
- /// <param name="channelTypes">The channel types to allow.</param>
- public ChannelTypesAttribute(params ChannelType[] channelTypes)
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class ChannelTypesAttribute : Attribute
{
- this.ChannelTypes = channelTypes;
+ /// <summary>
+ /// Allowed channel types.
+ /// </summary>
+ public IEnumerable<ChannelType> ChannelTypes { get; }
+
+ /// <summary>
+ /// Defines allowed channel types for a channel parameter.
+ /// </summary>
+ /// <param name="channelTypes">The channel types to allow.</param>
+ public ChannelTypesAttribute(params ChannelType[] channelTypes)
+ {
+ this.ChannelTypes = channelTypes;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceAttribute.cs
index 73bce8e6f..d2fe0ee3c 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceAttribute.cs
@@ -1,86 +1,87 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands;
-
-/// <summary>
-/// Adds a choice for this slash command option
-/// </summary>
-[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)]
-public class ChoiceAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the name of the choice
+ /// Adds a choice for this slash command option
/// </summary>
- public string Name { get; }
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)]
+ public class ChoiceAttribute : Attribute
+ {
+ /// <summary>
+ /// Gets the name of the choice
+ /// </summary>
+ public string Name { get; }
- /// <summary>
- /// Gets the value of the choice
- /// </summary>
- public object Value { get; }
+ /// <summary>
+ /// Gets the value of the choice
+ /// </summary>
+ public object Value { get; }
- /// <summary>
- /// Adds a choice to the slash command option
- /// </summary>
- /// <param name="name">The name of the choice.</param>
- /// <param name="value">The value of the choice.</param>
- public ChoiceAttribute(string name, string value)
- {
- this.Name = name;
- this.Value = value;
- }
+ /// <summary>
+ /// Adds a choice to the slash command option
+ /// </summary>
+ /// <param name="name">The name of the choice.</param>
+ /// <param name="value">The value of the choice.</param>
+ public ChoiceAttribute(string name, string value)
+ {
+ this.Name = name;
+ this.Value = value;
+ }
- /// <summary>
- /// Adds a choice to the slash command option
- /// </summary>
- /// <param name="name">The name of the choice.</param>
- /// <param name="value">The value of the choice.</param>
- public ChoiceAttribute(string name, long value)
- {
- this.Name = name;
- this.Value = value;
- }
+ /// <summary>
+ /// Adds a choice to the slash command option
+ /// </summary>
+ /// <param name="name">The name of the choice.</param>
+ /// <param name="value">The value of the choice.</param>
+ public ChoiceAttribute(string name, long value)
+ {
+ this.Name = name;
+ this.Value = value;
+ }
- /// <summary>
- /// Adds a choice to the slash command option
- /// </summary>
- /// <param name="name">The name of the choice.</param>
- /// <param name="value">The value of the choice.</param>
- public ChoiceAttribute(string name, int value)
- {
- this.Name = name;
- this.Value = value;
- }
+ /// <summary>
+ /// Adds a choice to the slash command option
+ /// </summary>
+ /// <param name="name">The name of the choice.</param>
+ /// <param name="value">The value of the choice.</param>
+ public ChoiceAttribute(string name, int value)
+ {
+ this.Name = name;
+ this.Value = value;
+ }
- /// <summary>
- /// Adds a choice to the slash command option
- /// </summary>
- /// <param name="name">The name of the choice.</param>
- /// <param name="value">The value of the choice.</param>
- public ChoiceAttribute(string name, double value)
- {
- this.Name = name;
- this.Value = value;
+ /// <summary>
+ /// Adds a choice to the slash command option
+ /// </summary>
+ /// <param name="name">The name of the choice.</param>
+ /// <param name="value">The value of the choice.</param>
+ public ChoiceAttribute(string name, double value)
+ {
+ this.Name = name;
+ this.Value = value;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceNameAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceNameAttribute.cs
index 0aaf8d820..3d66c6499 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceNameAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceNameAttribute.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands;
-
-/// <summary>
-/// Sets the name for this enum choice.
-/// </summary>
-[AttributeUsage(AttributeTargets.All)]
-public class ChoiceNameAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
- /// <summary>
- /// The name.
- /// </summary>
- public string Name { get; set; }
-
/// <summary>
/// Sets the name for this enum choice.
/// </summary>
- /// <param name="name">The name for this enum choice.</param>
- public ChoiceNameAttribute(string name)
+ [AttributeUsage(AttributeTargets.All)]
+ public class ChoiceNameAttribute : Attribute
{
- this.Name = name;
+ /// <summary>
+ /// The name.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Sets the name for this enum choice.
+ /// </summary>
+ /// <param name="name">The name for this enum choice.</param>
+ public ChoiceNameAttribute(string name)
+ {
+ this.Name = name;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProvider.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProvider.cs
index 0f845d05c..f72a1bd51 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProvider.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProvider.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Implementation of <see cref="IChoiceProvider"/> with access to service collection.
-/// </summary>
-public abstract class ChoiceProvider : IChoiceProvider
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Sets the choices for the slash command.
+ /// Implementation of <see cref="IChoiceProvider"/> with access to service collection.
/// </summary>
- public abstract Task<IEnumerable<DiscordApplicationCommandOptionChoice>> Provider();
+ public abstract class ChoiceProvider : IChoiceProvider
+ {
+ /// <summary>
+ /// Sets the choices for the slash command.
+ /// </summary>
+ public abstract Task<IEnumerable<DiscordApplicationCommandOptionChoice>> Provider();
- /// <summary>
- /// Sets the service provider.
- /// </summary>
- public IServiceProvider Services { get; set; }
+ /// <summary>
+ /// Sets the service provider.
+ /// </summary>
+ public IServiceProvider Services { get; set; }
- /// <summary>
- /// The optional ID of the Guild the command got registered for.
- /// </summary>
- public ulong? GuildId { get; set; }
+ /// <summary>
+ /// The optional ID of the Guild the command got registered for.
+ /// </summary>
+ public ulong? GuildId { get; set; }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProviderAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProviderAttribute.cs
index a2f861a37..bc660e461 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProviderAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/ChoiceProviderAttribute.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands;
-
-/// <summary>
-/// Sets a IChoiceProvider for a command options. ChoiceProviders can be used to provide
-/// DiscordApplicationCommandOptionChoice from external sources such as a database.
-/// </summary>
-[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)]
-public class ChoiceProviderAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
-
/// <summary>
- /// The type of the provider.
+ /// Sets a IChoiceProvider for a command options. ChoiceProviders can be used to provide
+ /// DiscordApplicationCommandOptionChoice from external sources such as a database.
/// </summary>
- public Type ProviderType { get; }
-
- /// <summary>
- /// Adds a choice provider to this command.
- /// </summary>
- /// <param name="providerType">The type of the provider.</param>
- public ChoiceProviderAttribute(Type providerType)
+ [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true)]
+ public class ChoiceProviderAttribute : Attribute
{
- this.ProviderType = providerType;
+
+ /// <summary>
+ /// The type of the provider.
+ /// </summary>
+ public Type ProviderType { get; }
+
+ /// <summary>
+ /// Adds a choice provider to this command.
+ /// </summary>
+ /// <param name="providerType">The type of the provider.</param>
+ public ChoiceProviderAttribute(Type providerType)
+ {
+ this.ProviderType = providerType;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IAutocompleteProvider.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IAutocompleteProvider.cs
index 7c54b20bf..7c0a817ca 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IAutocompleteProvider.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IAutocompleteProvider.cs
@@ -1,40 +1,41 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// The autocomplete provider.
-/// </summary>
-public interface IAutocompleteProvider
+namespace DisCatSharp.ApplicationCommands.Attributes
{
/// <summary>
- /// Provider the autocompletion.
+ /// The autocomplete provider.
/// </summary>
- /// <param name="context">The context.</param>
- Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>> Provider(AutocompleteContext context);
+ public interface IAutocompleteProvider
+ {
+ /// <summary>
+ /// Provider the autocompletion.
+ /// </summary>
+ /// <param name="context">The context.</param>
+ Task<IEnumerable<DiscordApplicationCommandAutocompleteChoice>> Provider(AutocompleteContext context);
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IChoiceProvider.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IChoiceProvider.cs
index c6a7c02d6..40099da81 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IChoiceProvider.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/IChoiceProvider.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// All choice providers must inherit from this interface
-/// </summary>
-public interface IChoiceProvider
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Sets the choices for the slash command
+ /// All choice providers must inherit from this interface
/// </summary>
- Task<IEnumerable<DiscordApplicationCommandOptionChoice>> Provider();
+ public interface IChoiceProvider
+ {
+ /// <summary>
+ /// Sets the choices for the slash command
+ /// </summary>
+ Task<IEnumerable<DiscordApplicationCommandOptionChoice>> Provider();
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/MinimumMaximumAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/MinimumMaximumAttribute.cs
index 0b93261fd..38369bf8a 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/MinimumMaximumAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/MinimumMaximumAttribute.cs
@@ -1,97 +1,98 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Sets a minimum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
-/// </summary>
-[AttributeUsage(AttributeTargets.Parameter)]
-public class MinimumAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
- /// <summary>
- /// The value.
- /// </summary>
- public object Value { get; internal set; }
-
/// <summary>
/// Sets a minimum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
/// </summary>
- public MinimumAttribute(int value)
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class MinimumAttribute : Attribute
{
- this.Value = value;
- }
+ /// <summary>
+ /// The value.
+ /// </summary>
+ public object Value { get; internal set; }
- /// <summary>
- /// Sets a minimum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
- /// </summary>
- public MinimumAttribute(long value)
- {
- this.Value = value;
- }
+ /// <summary>
+ /// Sets a minimum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
+ /// </summary>
+ public MinimumAttribute(int value)
+ {
+ this.Value = value;
+ }
- /// <summary>
- /// Sets a minimum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
- /// </summary>
- public MinimumAttribute(double value)
- {
- this.Value = value;
- }
-}
+ /// <summary>
+ /// Sets a minimum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
+ /// </summary>
+ public MinimumAttribute(long value)
+ {
+ this.Value = value;
+ }
-/// <summary>
-/// Sets a maximum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
-/// </summary>
-[AttributeUsage(AttributeTargets.Parameter)]
-public class MaximumAttribute : Attribute
-{
- /// <summary>
- /// The value.
- /// </summary>
- public object Value { get; internal set; }
-
- /// <summary>
- /// Sets a maximum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
- /// </summary>
- public MaximumAttribute(int value)
- {
- this.Value = value;
+ /// <summary>
+ /// Sets a minimum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
+ /// </summary>
+ public MinimumAttribute(double value)
+ {
+ this.Value = value;
+ }
}
/// <summary>
/// Sets a maximum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
/// </summary>
- public MaximumAttribute(long value)
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class MaximumAttribute : Attribute
{
- this.Value = value;
- }
+ /// <summary>
+ /// The value.
+ /// </summary>
+ public object Value { get; internal set; }
- /// <summary>
- /// Sets a maximum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
- /// </summary>
- public MaximumAttribute(double value)
- {
- this.Value = value;
+ /// <summary>
+ /// Sets a maximum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
+ /// </summary>
+ public MaximumAttribute(int value)
+ {
+ this.Value = value;
+ }
+
+ /// <summary>
+ /// Sets a maximum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
+ /// </summary>
+ public MaximumAttribute(long value)
+ {
+ this.Value = value;
+ }
+
+ /// <summary>
+ /// Sets a maximum value for this slash command option. Only valid for <see cref="int"/>, <see cref="long"/> or <see cref="double"/> parameters.
+ /// </summary>
+ public MaximumAttribute(double value)
+ {
+ this.Value = value;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/OptionAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/OptionAttribute.cs
index 8bc823d53..0ed398790 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/OptionAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/OptionAttribute.cs
@@ -1,65 +1,66 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands;
-
-/// <summary>
-/// Marks this parameter as an option for a slash command
-/// </summary>
-[AttributeUsage(AttributeTargets.Parameter)]
-public class OptionAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the name of this option.
+ /// Marks this parameter as an option for a slash command
/// </summary>
- public string Name;
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class OptionAttribute : Attribute
+ {
+ /// <summary>
+ /// Gets the name of this option.
+ /// </summary>
+ public string Name;
- /// <summary>
- /// Gets the description of this option.
- /// </summary>
- public string Description;
+ /// <summary>
+ /// Gets the description of this option.
+ /// </summary>
+ public string Description;
- /// <summary>
- /// Whether to autocomplete this option.
- /// </summary>
- public bool Autocomplete;
+ /// <summary>
+ /// Whether to autocomplete this option.
+ /// </summary>
+ public bool Autocomplete;
- /// <summary>
- /// Initializes a new instance of the <see cref="OptionAttribute"/> class.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="description">The description.</param>
- /// <param name="autocomplete">If true, autocomplete.</param>
- public OptionAttribute(string name, string description, bool autocomplete = false)
- {
- if (name.Length > 32)
- throw new ArgumentException("Slash command option names cannot go over 32 characters.");
- else if (description.Length > 100)
- throw new ArgumentException("Slash command option descriptions cannot go over 100 characters.");
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OptionAttribute"/> class.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="autocomplete">If true, autocomplete.</param>
+ public OptionAttribute(string name, string description, bool autocomplete = false)
+ {
+ if (name.Length > 32)
+ throw new ArgumentException("Slash command option names cannot go over 32 characters.");
+ else if (description.Length > 100)
+ throw new ArgumentException("Slash command option descriptions cannot go over 100 characters.");
- this.Name = name.ToLower();
- this.Description = description;
- this.Autocomplete = autocomplete;
+ this.Name = name.ToLower();
+ this.Description = description;
+ this.Autocomplete = autocomplete;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireBotPermissionsAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireBotPermissionsAttribute.cs
index 349e19471..41e40ea23 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireBotPermissionsAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireBotPermissionsAttribute.cs
@@ -1,74 +1,75 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Defines that usage of this application command is only possible when the bot is granted a specific permission.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class ApplicationCommandRequireBotPermissionsAttribute : SlashCheckBaseAttribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
- /// <summary>
- /// Gets the permissions required by this attribute.
- /// </summary>
- public Permissions Permissions { get; }
-
- /// <summary>
- /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
- /// </summary>
- public bool IgnoreDms { get; } = true;
-
/// <summary>
/// Defines that usage of this application command is only possible when the bot is granted a specific permission.
/// </summary>
- /// <param name="permissions">Permissions required to execute this command.</param>
- /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
- public ApplicationCommandRequireBotPermissionsAttribute(Permissions permissions, bool ignoreDms = true)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class ApplicationCommandRequireBotPermissionsAttribute : SlashCheckBaseAttribute
{
- this.Permissions = permissions;
- this.IgnoreDms = ignoreDms;
- }
+ /// <summary>
+ /// Gets the permissions required by this attribute.
+ /// </summary>
+ public Permissions Permissions { get; }
- /// <summary>
- /// Runs checks.
- /// </summary>
- public override async Task<bool> ExecuteChecksAsync(InteractionContext ctx)
- {
- if (ctx.Guild == null)
- return this.IgnoreDms;
+ /// <summary>
+ /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
+ /// </summary>
+ public bool IgnoreDms { get; } = true;
+
+ /// <summary>
+ /// Defines that usage of this application command is only possible when the bot is granted a specific permission.
+ /// </summary>
+ /// <param name="permissions">Permissions required to execute this command.</param>
+ /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
+ public ApplicationCommandRequireBotPermissionsAttribute(Permissions permissions, bool ignoreDms = true)
+ {
+ this.Permissions = permissions;
+ this.IgnoreDms = ignoreDms;
+ }
+
+ /// <summary>
+ /// Runs checks.
+ /// </summary>
+ public override async Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ {
+ if (ctx.Guild == null)
+ return this.IgnoreDms;
- var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false);
- if (bot == null)
- return false;
+ var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false);
+ if (bot == null)
+ return false;
- if (bot.Id == ctx.Guild.OwnerId)
- return true;
+ if (bot.Id == ctx.Guild.OwnerId)
+ return true;
- var pbot = ctx.Channel.PermissionsFor(bot);
+ var pbot = ctx.Channel.PermissionsFor(bot);
- return (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
+ return (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDirectMessageAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDirectMessageAttribute.cs
index 6d142ecac..c50d060bc 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDirectMessageAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDirectMessageAttribute.cs
@@ -1,47 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Defines that this application command is only usable within a direct message channel.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class ApplicationCommandRequireDirectMessageAttribute : SlashCheckBaseAttribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
/// <summary>
- /// Defines that this command is only usable within a direct message channel.
+ /// Defines that this application command is only usable within a direct message channel.
/// </summary>
- public ApplicationCommandRequireDirectMessageAttribute()
- { }
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class ApplicationCommandRequireDirectMessageAttribute : SlashCheckBaseAttribute
+ {
+ /// <summary>
+ /// Defines that this command is only usable within a direct message channel.
+ /// </summary>
+ public ApplicationCommandRequireDirectMessageAttribute()
+ { }
- /// <summary>
- /// Runs checks.
- /// </summary>
- public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
- => Task.FromResult(ctx.Channel is DiscordDmChannel);
+ /// <summary>
+ /// Runs checks.
+ /// </summary>
+ public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ => Task.FromResult(ctx.Channel is DiscordDmChannel);
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDisCatSharpDeveloperAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDisCatSharpDeveloperAttribute.cs
index c88512cb0..d6a24261b 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDisCatSharpDeveloperAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireDisCatSharpDeveloperAttribute.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Defines that this application command is restricted to the owner of the bot.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class ApplicationCommandRequireDisCatSharpDeveloperAttribute : SlashCheckBaseAttribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
/// <summary>
/// Defines that this application command is restricted to the owner of the bot.
/// </summary>
- public ApplicationCommandRequireDisCatSharpDeveloperAttribute()
- { }
-
- /// <summary>
- /// Runs checks.
- /// </summary>
- public override async Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class ApplicationCommandRequireDisCatSharpDeveloperAttribute : SlashCheckBaseAttribute
{
- var team = (await ctx.Client.GetLibraryDevelopmentTeamAsync()).Developers;
- return team?.Any(x => x.Id == ctx.User.Id) ?? false;
+ /// <summary>
+ /// Defines that this application command is restricted to the owner of the bot.
+ /// </summary>
+ public ApplicationCommandRequireDisCatSharpDeveloperAttribute()
+ { }
+
+ /// <summary>
+ /// Runs checks.
+ /// </summary>
+ public override async Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ {
+ var team = (await ctx.Client.GetLibraryDevelopmentTeamAsync()).Developers;
+ return team?.Any(x => x.Id == ctx.User.Id) ?? false;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireGuildAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireGuildAttribute.cs
index 4fa63b5ac..d72c6b80a 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireGuildAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireGuildAttribute.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Defines that this application command is only usable within a guild.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class ApplicationCommandRequireGuildAttribute : SlashCheckBaseAttribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
/// <summary>
- /// Defines that this command is only usable within a guild.
+ /// Defines that this application command is only usable within a guild.
/// </summary>
- public ApplicationCommandRequireGuildAttribute()
- { }
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class ApplicationCommandRequireGuildAttribute : SlashCheckBaseAttribute
+ {
+ /// <summary>
+ /// Defines that this command is only usable within a guild.
+ /// </summary>
+ public ApplicationCommandRequireGuildAttribute()
+ { }
- /// <summary>
- /// Runs checks.
- /// </summary>
- public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
- => Task.FromResult(ctx.Guild != null);
+ /// <summary>
+ /// Runs checks.
+ /// </summary>
+ public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ => Task.FromResult(ctx.Guild != null);
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireNsfwAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireNsfwAttribute.cs
index 8887bf110..7c25b081e 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireNsfwAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireNsfwAttribute.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Defines that this application command is only usable within a guild.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class ApplicationCommandRequireNsfwAttribute : SlashCheckBaseAttribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
/// <summary>
- /// Defines that this command is only usable within a guild.
+ /// Defines that this application command is only usable within a guild.
/// </summary>
- public ApplicationCommandRequireNsfwAttribute()
- { }
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class ApplicationCommandRequireNsfwAttribute : SlashCheckBaseAttribute
+ {
+ /// <summary>
+ /// Defines that this command is only usable within a guild.
+ /// </summary>
+ public ApplicationCommandRequireNsfwAttribute()
+ { }
- /// <summary>
- /// Runs checks.
- /// </summary>
- public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
- => Task.FromResult(ctx.Channel.Guild == null || ctx.Channel.IsNsfw);
+ /// <summary>
+ /// Runs checks.
+ /// </summary>
+ public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ => Task.FromResult(ctx.Channel.Guild == null || ctx.Channel.IsNsfw);
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerAttribute.cs
index 099b511cf..46f9b9b34 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerAttribute.cs
@@ -1,51 +1,52 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Defines that this application command is restricted to the owner of the bot.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class ApplicationCommandRequireOwnerAttribute : SlashCheckBaseAttribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
/// <summary>
/// Defines that this application command is restricted to the owner of the bot.
/// </summary>
- public ApplicationCommandRequireOwnerAttribute()
- { }
-
- /// <summary>
- /// Runs checks.
- /// </summary>
- public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class ApplicationCommandRequireOwnerAttribute : SlashCheckBaseAttribute
{
- var app = ctx.Client.CurrentApplication;
- var me = ctx.Client.CurrentUser;
+ /// <summary>
+ /// Defines that this application command is restricted to the owner of the bot.
+ /// </summary>
+ public ApplicationCommandRequireOwnerAttribute()
+ { }
+
+ /// <summary>
+ /// Runs checks.
+ /// </summary>
+ public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ {
+ var app = ctx.Client.CurrentApplication;
+ var me = ctx.Client.CurrentUser;
- return app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id);
+ return app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id);
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerOrIdAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerOrIdAttribute.cs
index 27d30038c..614915592 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerOrIdAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireOwnerOrIdAttribute.cs
@@ -1,66 +1,67 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Requires ownership of the bot or a whitelisted id to execute this command.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class ApplicationCommandRequireOwnerOrIdAttribute : SlashCheckBaseAttribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
/// <summary>
- /// Allowed user ids
+ /// Requires ownership of the bot or a whitelisted id to execute this command.
/// </summary>
- public IReadOnlyList<ulong> UserIds { get; }
-
- /// <summary>
- /// Defines that usage of this command is restricted to the owner or whitelisted ids of the bot.
- /// </summary>
- /// <param name="userIds">List of allowed user ids</param>
- public ApplicationCommandRequireOwnerOrIdAttribute(params ulong[] userIds)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class ApplicationCommandRequireOwnerOrIdAttribute : SlashCheckBaseAttribute
{
- this.UserIds = new ReadOnlyCollection<ulong>(userIds);
- }
+ /// <summary>
+ /// Allowed user ids
+ /// </summary>
+ public IReadOnlyList<ulong> UserIds { get; }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>s
- public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
- {
- var app = ctx.Client.CurrentApplication;
- var me = ctx.Client.CurrentUser;
+ /// <summary>
+ /// Defines that usage of this command is restricted to the owner or whitelisted ids of the bot.
+ /// </summary>
+ /// <param name="userIds">List of allowed user ids</param>
+ public ApplicationCommandRequireOwnerOrIdAttribute(params ulong[] userIds)
+ {
+ this.UserIds = new ReadOnlyCollection<ulong>(userIds);
+ }
+
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>s
+ public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ {
+ var app = ctx.Client.CurrentApplication;
+ var me = ctx.Client.CurrentUser;
- var owner = app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id);
+ var owner = app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id);
- var allowed = this.UserIds.Contains(ctx.User.Id);
+ var allowed = this.UserIds.Contains(ctx.User.Id);
- return allowed ? Task.FromResult(true) : owner;
+ return allowed ? Task.FromResult(true) : owner;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequirePermissionsAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequirePermissionsAttribute.cs
index cc8ff89ec..cdcf09590 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequirePermissionsAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequirePermissionsAttribute.cs
@@ -1,84 +1,85 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Defines that usage of this application command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class ApplicationCommandRequirePermissionsAttribute : SlashCheckBaseAttribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
/// <summary>
- /// Gets the permissions required by this attribute.
+ /// Defines that usage of this application command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions.
/// </summary>
- public Permissions Permissions { get; }
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class ApplicationCommandRequirePermissionsAttribute : SlashCheckBaseAttribute
+ {
+ /// <summary>
+ /// Gets the permissions required by this attribute.
+ /// </summary>
+ public Permissions Permissions { get; }
- /// <summary>
- /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
- /// </summary>
- public bool IgnoreDms { get; } = true;
+ /// <summary>
+ /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
+ /// </summary>
+ public bool IgnoreDms { get; } = true;
- /// <summary>
- /// Defines that usage of this command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions.
- /// </summary>
- /// <param name="permissions">Permissions required to execute this command.</param>
- /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
- public ApplicationCommandRequirePermissionsAttribute(Permissions permissions, bool ignoreDms = true)
- {
- this.Permissions = permissions;
- this.IgnoreDms = ignoreDms;
- }
+ /// <summary>
+ /// Defines that usage of this command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions.
+ /// </summary>
+ /// <param name="permissions">Permissions required to execute this command.</param>
+ /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
+ public ApplicationCommandRequirePermissionsAttribute(Permissions permissions, bool ignoreDms = true)
+ {
+ this.Permissions = permissions;
+ this.IgnoreDms = ignoreDms;
+ }
- /// <summary>
- /// Runs checks.
- /// </summary>
- public override async Task<bool> ExecuteChecksAsync(InteractionContext ctx)
- {
- if (ctx.Guild == null)
- return this.IgnoreDms;
+ /// <summary>
+ /// Runs checks.
+ /// </summary>
+ public override async Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ {
+ if (ctx.Guild == null)
+ return this.IgnoreDms;
- var usr = ctx.Member;
- if (usr == null)
- return false;
- var pusr = ctx.Channel.PermissionsFor(usr);
+ var usr = ctx.Member;
+ if (usr == null)
+ return false;
+ var pusr = ctx.Channel.PermissionsFor(usr);
- var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false);
- if (bot == null)
- return false;
- var pbot = ctx.Channel.PermissionsFor(bot);
+ var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false);
+ if (bot == null)
+ return false;
+ var pbot = ctx.Channel.PermissionsFor(bot);
- var usrok = ctx.Guild.OwnerId == usr.Id;
- var botok = ctx.Guild.OwnerId == bot.Id;
+ var usrok = ctx.Guild.OwnerId == usr.Id;
+ var botok = ctx.Guild.OwnerId == bot.Id;
- if (!usrok)
- usrok = (pusr & Permissions.Administrator) != 0 || (pusr & this.Permissions) == this.Permissions;
+ if (!usrok)
+ usrok = (pusr & Permissions.Administrator) != 0 || (pusr & this.Permissions) == this.Permissions;
- if (!botok)
- botok = (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
+ if (!botok)
+ botok = (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
- return usrok && botok;
+ return usrok && botok;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireUserPermissionsAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireUserPermissionsAttribute.cs
index 4a5bc1187..1dc7e7a3d 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireUserPermissionsAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/RequireUserPermissionsAttribute.cs
@@ -1,76 +1,77 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands.Attributes;
-
-/// <summary>
-/// Defines that usage of this application command is restricted to members with specified permissions.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class ApplicationCommandRequireUserPermissionsAttribute : SlashCheckBaseAttribute
+namespace DisCatSharp.ApplicationCommands.Attributes
{
/// <summary>
- /// Gets the permissions required by this attribute.
+ /// Defines that usage of this application command is restricted to members with specified permissions.
/// </summary>
- public Permissions Permissions { get; }
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class ApplicationCommandRequireUserPermissionsAttribute : SlashCheckBaseAttribute
+ {
+ /// <summary>
+ /// Gets the permissions required by this attribute.
+ /// </summary>
+ public Permissions Permissions { get; }
- /// <summary>
- /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
- /// </summary>
- public bool IgnoreDms { get; } = true;
+ /// <summary>
+ /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
+ /// </summary>
+ public bool IgnoreDms { get; } = true;
- /// <summary>
- /// Defines that usage of this command is restricted to members with specified permissions.
- /// </summary>
- /// <param name="permissions">Permissions required to execute this command.</param>
- /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
- public ApplicationCommandRequireUserPermissionsAttribute(Permissions permissions, bool ignoreDms = true)
- {
- this.Permissions = permissions;
- this.IgnoreDms = ignoreDms;
- }
+ /// <summary>
+ /// Defines that usage of this command is restricted to members with specified permissions.
+ /// </summary>
+ /// <param name="permissions">Permissions required to execute this command.</param>
+ /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
+ public ApplicationCommandRequireUserPermissionsAttribute(Permissions permissions, bool ignoreDms = true)
+ {
+ this.Permissions = permissions;
+ this.IgnoreDms = ignoreDms;
+ }
- /// <summary>
- /// Runs checks.
- /// </summary>
- public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
- {
- if (ctx.Guild == null)
- return Task.FromResult(this.IgnoreDms);
+ /// <summary>
+ /// Runs checks.
+ /// </summary>
+ public override Task<bool> ExecuteChecksAsync(InteractionContext ctx)
+ {
+ if (ctx.Guild == null)
+ return Task.FromResult(this.IgnoreDms);
- var usr = ctx.Member;
- if (usr == null)
- return Task.FromResult(false);
+ var usr = ctx.Member;
+ if (usr == null)
+ return Task.FromResult(false);
- if (usr.Id == ctx.Guild.OwnerId)
- return Task.FromResult(true);
+ if (usr.Id == ctx.Guild.OwnerId)
+ return Task.FromResult(true);
- var pusr = ctx.Channel.PermissionsFor(usr);
+ var pusr = ctx.Channel.PermissionsFor(usr);
- return (pusr & Permissions.Administrator) != 0
- ? Task.FromResult(true)
- : (pusr & this.Permissions) == this.Permissions ? Task.FromResult(true) : Task.FromResult(false);
+ return (pusr & Permissions.Administrator) != 0
+ ? Task.FromResult(true)
+ : (pusr & this.Permissions) == this.Permissions ? Task.FromResult(true) : Task.FromResult(false);
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCheckBaseAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCheckBaseAttribute.cs
index 0fcac2c69..d1cbd01ec 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCheckBaseAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCheckBaseAttribute.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// The base class for a pre-execution check for a application command.
-/// </summary>
-public abstract class SlashCheckBaseAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Checks whether this command can be executed within the current context.
+ /// The base class for a pre-execution check for a application command.
/// </summary>
- /// <param name="ctx">The context.</param>
- /// <returns>Whether the checks passed.</returns>
- public abstract Task<bool> ExecuteChecksAsync(InteractionContext ctx);
+ public abstract class SlashCheckBaseAttribute : Attribute
+ {
+ /// <summary>
+ /// Checks whether this command can be executed within the current context.
+ /// </summary>
+ /// <param name="ctx">The context.</param>
+ /// <returns>Whether the checks passed.</returns>
+ public abstract Task<bool> ExecuteChecksAsync(InteractionContext ctx);
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandAttribute.cs
index 03d952ead..ed2e0fc35 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandAttribute.cs
@@ -1,108 +1,109 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands;
-
-/// <summary>
-/// Marks this method as a slash command
-/// </summary>
-[AttributeUsage(AttributeTargets.Method)]
-public class SlashCommandAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the name of this command
+ /// Marks this method as a slash command
/// </summary>
- public string Name { get; set; }
+ [AttributeUsage(AttributeTargets.Method)]
+ public class SlashCommandAttribute : Attribute
+ {
+ /// <summary>
+ /// Gets the name of this command
+ /// </summary>
+ public string Name { get; set; }
- /// <summary>
- /// Gets the description of this command
- /// </summary>
- public string Description { get; set; }
+ /// <summary>
+ /// Gets the description of this command
+ /// </summary>
+ public string Description { get; set; }
- /// <summary>
- /// Gets the needed permission of this command
- /// </summary>
- public Permissions? DefaultMemberPermissions { get; set; }
+ /// <summary>
+ /// Gets the needed permission of this command
+ /// </summary>
+ public Permissions? DefaultMemberPermissions { get; set; }
- /// <summary>
- /// Gets the dm permission of this command
- /// </summary>
- public bool? DmPermission { get; set; }
+ /// <summary>
+ /// Gets the dm permission of this command
+ /// </summary>
+ public bool? DmPermission { get; set; }
- /// <summary>
- /// Marks this method as a slash command
- /// </summary>
- /// <param name="name">The name of this slash command.</param>
- /// <param name="description">The description of this slash command.</param>
- public SlashCommandAttribute(string name, string description)
- {
- this.Name = name.ToLower();
- this.Description = description;
- this.DefaultMemberPermissions = null;
- this.DmPermission = null;
- }
+ /// <summary>
+ /// Marks this method as a slash command
+ /// </summary>
+ /// <param name="name">The name of this slash command.</param>
+ /// <param name="description">The description of this slash command.</param>
+ public SlashCommandAttribute(string name, string description)
+ {
+ this.Name = name.ToLower();
+ this.Description = description;
+ this.DefaultMemberPermissions = null;
+ this.DmPermission = null;
+ }
- /// <summary>
- /// Marks this method as a slash command
- /// </summary>
- /// <param name="name">The name of this slash command.</param>
- /// <param name="description">The description of this slash command.</param>
- /// <param name="defaultMemberPermissions">The default member permissions.</param>
- public SlashCommandAttribute(string name, string description, long defaultMemberPermissions)
- {
- this.Name = name.ToLower();
- this.Description = description;
- this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
- this.DmPermission = null;
- }
+ /// <summary>
+ /// Marks this method as a slash command
+ /// </summary>
+ /// <param name="name">The name of this slash command.</param>
+ /// <param name="description">The description of this slash command.</param>
+ /// <param name="defaultMemberPermissions">The default member permissions.</param>
+ public SlashCommandAttribute(string name, string description, long defaultMemberPermissions)
+ {
+ this.Name = name.ToLower();
+ this.Description = description;
+ this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
+ this.DmPermission = null;
+ }
- /// <summary>
- /// Marks this method as a slash command
- /// </summary>
- /// <param name="name">The name of this slash command.</param>
- /// <param name="description">The description of this slash command.</param>
- /// <param name="dmPermission">The dm permission.</param>
- public SlashCommandAttribute(string name, string description, bool dmPermission)
- {
- this.Name = name.ToLower();
- this.Description = description;
- this.DefaultMemberPermissions = null;
- this.DmPermission = dmPermission;
- }
+ /// <summary>
+ /// Marks this method as a slash command
+ /// </summary>
+ /// <param name="name">The name of this slash command.</param>
+ /// <param name="description">The description of this slash command.</param>
+ /// <param name="dmPermission">The dm permission.</param>
+ public SlashCommandAttribute(string name, string description, bool dmPermission)
+ {
+ this.Name = name.ToLower();
+ this.Description = description;
+ this.DefaultMemberPermissions = null;
+ this.DmPermission = dmPermission;
+ }
- /// <summary>
- /// Marks this method as a slash command
- /// </summary>
- /// <param name="name">The name of this slash command.</param>
- /// <param name="description">The description of this slash command.</param>
- /// <param name="defaultMemberPermissions">The default member permissions.</param>
- /// <param name="dmPermission">The dm permission.</param>
- public SlashCommandAttribute(string name, string description, long defaultMemberPermissions, bool dmPermission)
- {
- this.Name = name.ToLower();
- this.Description = description;
- this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
- this.DmPermission = dmPermission;
+ /// <summary>
+ /// Marks this method as a slash command
+ /// </summary>
+ /// <param name="name">The name of this slash command.</param>
+ /// <param name="description">The description of this slash command.</param>
+ /// <param name="defaultMemberPermissions">The default member permissions.</param>
+ /// <param name="dmPermission">The dm permission.</param>
+ public SlashCommandAttribute(string name, string description, long defaultMemberPermissions, bool dmPermission)
+ {
+ this.Name = name.ToLower();
+ this.Description = description;
+ this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
+ this.DmPermission = dmPermission;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandGroupAttribute.cs b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandGroupAttribute.cs
index e8d67f2a1..70b6d34a0 100644
--- a/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandGroupAttribute.cs
+++ b/DisCatSharp.ApplicationCommands/Attributes/SlashCommand/SlashCommandGroupAttribute.cs
@@ -1,108 +1,109 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands;
-
-/// <summary>
-/// Marks this class a slash command group
-/// </summary>
-[AttributeUsage(AttributeTargets.Class)]
-public class SlashCommandGroupAttribute : Attribute
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the name of this slash command group
+ /// Marks this class a slash command group
/// </summary>
- public string Name { get; set; }
+ [AttributeUsage(AttributeTargets.Class)]
+ public class SlashCommandGroupAttribute : Attribute
+ {
+ /// <summary>
+ /// Gets the name of this slash command group
+ /// </summary>
+ public string Name { get; set; }
- /// <summary>
- /// Gets the description of this slash command group
- /// </summary>
- public string Description { get; set; }
+ /// <summary>
+ /// Gets the description of this slash command group
+ /// </summary>
+ public string Description { get; set; }
- /// <summary>
- /// Gets the needed permission of this slash command group
- /// </summary>
- public Permissions? DefaultMemberPermissions { get; set; }
+ /// <summary>
+ /// Gets the needed permission of this slash command group
+ /// </summary>
+ public Permissions? DefaultMemberPermissions { get; set; }
- /// <summary>
- /// Gets the dm permission of this slash command group
- /// </summary>
- public bool? DmPermission { get; set; }
+ /// <summary>
+ /// Gets the dm permission of this slash command group
+ /// </summary>
+ public bool? DmPermission { get; set; }
- /// <summary>
- /// Marks this class as a slash command group
- /// </summary>
- /// <param name="name">The name of this slash command group.</param>
- /// <param name="description">The description of this slash command group.</param>
- public SlashCommandGroupAttribute(string name, string description)
- {
- this.Name = name.ToLower();
- this.Description = description;
- this.DefaultMemberPermissions = null;
- this.DmPermission = null;
- }
+ /// <summary>
+ /// Marks this class as a slash command group
+ /// </summary>
+ /// <param name="name">The name of this slash command group.</param>
+ /// <param name="description">The description of this slash command group.</param>
+ public SlashCommandGroupAttribute(string name, string description)
+ {
+ this.Name = name.ToLower();
+ this.Description = description;
+ this.DefaultMemberPermissions = null;
+ this.DmPermission = null;
+ }
- /// <summary>
- /// Marks this method as a slash command group
- /// </summary>
- /// <param name="name">The name of this slash command.</param>
- /// <param name="description">The description of this slash command.</param>
- /// <param name="defaultMemberPermissions">The default member permissions.</param>
- public SlashCommandGroupAttribute(string name, string description, long defaultMemberPermissions)
- {
- this.Name = name.ToLower();
- this.Description = description;
- this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
- this.DmPermission = null;
- }
+ /// <summary>
+ /// Marks this method as a slash command group
+ /// </summary>
+ /// <param name="name">The name of this slash command.</param>
+ /// <param name="description">The description of this slash command.</param>
+ /// <param name="defaultMemberPermissions">The default member permissions.</param>
+ public SlashCommandGroupAttribute(string name, string description, long defaultMemberPermissions)
+ {
+ this.Name = name.ToLower();
+ this.Description = description;
+ this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
+ this.DmPermission = null;
+ }
- /// <summary>
- /// Marks this method as a slash command group
- /// </summary>
- /// <param name="name">The name of this slash command.</param>
- /// <param name="description">The description of this slash command.</param>
- /// <param name="dmPermission">The dm permission.</param>
- public SlashCommandGroupAttribute(string name, string description, bool dmPermission)
- {
- this.Name = name.ToLower();
- this.Description = description;
- this.DefaultMemberPermissions = null;
- this.DmPermission = dmPermission;
- }
+ /// <summary>
+ /// Marks this method as a slash command group
+ /// </summary>
+ /// <param name="name">The name of this slash command.</param>
+ /// <param name="description">The description of this slash command.</param>
+ /// <param name="dmPermission">The dm permission.</param>
+ public SlashCommandGroupAttribute(string name, string description, bool dmPermission)
+ {
+ this.Name = name.ToLower();
+ this.Description = description;
+ this.DefaultMemberPermissions = null;
+ this.DmPermission = dmPermission;
+ }
- /// <summary>
- /// Marks this method as a slash command group
- /// </summary>
- /// <param name="name">The name of this slash command.</param>
- /// <param name="description">The description of this slash command.</param>
- /// <param name="defaultMemberPermissions">The default member permissions.</param>
- /// <param name="dmPermission">The dm permission.</param>
- public SlashCommandGroupAttribute(string name, string description, long defaultMemberPermissions, bool dmPermission)
- {
- this.Name = name.ToLower();
- this.Description = description;
- this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
- this.DmPermission = dmPermission;
+ /// <summary>
+ /// Marks this method as a slash command group
+ /// </summary>
+ /// <param name="name">The name of this slash command.</param>
+ /// <param name="description">The description of this slash command.</param>
+ /// <param name="defaultMemberPermissions">The default member permissions.</param>
+ /// <param name="dmPermission">The dm permission.</param>
+ public SlashCommandGroupAttribute(string name, string description, long defaultMemberPermissions, bool dmPermission)
+ {
+ this.Name = name.ToLower();
+ this.Description = description;
+ this.DefaultMemberPermissions = (Permissions)defaultMemberPermissions;
+ this.DmPermission = dmPermission;
+ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs
index 94add1c26..831a4f56a 100644
--- a/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs
+++ b/DisCatSharp.ApplicationCommands/Checks/ApplicationCommandEqualityChecks.cs
@@ -1,276 +1,277 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
-namespace DisCatSharp.ApplicationCommands;
-
-internal static class ApplicationCommandEqualityChecks
+namespace DisCatSharp.ApplicationCommands
{
- /// <summary>
- /// Whether two application commands are equal.
- /// </summary>
- /// <param name="ac1">Source command.</param>
- /// <param name="targetApplicationCommand">Command to check against.</param>
- internal static bool IsEqualTo(this DiscordApplicationCommand ac1, DiscordApplicationCommand targetApplicationCommand)
+ internal static class ApplicationCommandEqualityChecks
{
- if (targetApplicationCommand is null || ac1 is null)
- return false;
-
- DiscordApplicationCommand sourceApplicationCommand = new(
- ac1.Name, ac1.Description, ac1.Options,
- ac1.Type,
- ac1.NameLocalizations, ac1.DescriptionLocalizations
- );
+ /// <summary>
+ /// Whether two application commands are equal.
+ /// </summary>
+ /// <param name="ac1">Source command.</param>
+ /// <param name="targetApplicationCommand">Command to check against.</param>
+ internal static bool IsEqualTo(this DiscordApplicationCommand ac1, DiscordApplicationCommand targetApplicationCommand)
+ {
+ if (targetApplicationCommand is null || ac1 is null)
+ return false;
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC Change Check] Command {ac1.Name}\n\n[{JsonConvert.SerializeObject(sourceApplicationCommand)},{JsonConvert.SerializeObject(targetApplicationCommand)}]\n\n");
+ DiscordApplicationCommand sourceApplicationCommand = new(
+ ac1.Name, ac1.Description, ac1.Options,
+ ac1.Type,
+ ac1.NameLocalizations, ac1.DescriptionLocalizations
+ );
- return ac1.Type == targetApplicationCommand.Type && sourceApplicationCommand.SoftEqual(targetApplicationCommand, ac1.Type, ApplicationCommandsExtension.Configuration?.EnableLocalization ?? false);
- }
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC Change Check] Command {ac1.Name}\n\n[{JsonConvert.SerializeObject(sourceApplicationCommand)},{JsonConvert.SerializeObject(targetApplicationCommand)}]\n\n");
- /// <summary>
- /// Checks softly whether two <see cref="DisCatSharp.Entities.DiscordApplicationCommand"/>s are the same.
- /// Excluding id, application id and version here.
- /// </summary>
- /// <param name="source">Source application command.</param>
- /// <param name="target">Application command to check against.</param>
- /// <param name="type">The application command type.</param>
- /// <param name="localizationEnabled">Whether localization is enabled.</param>
- internal static bool SoftEqual(this DiscordApplicationCommand source, DiscordApplicationCommand target, ApplicationCommandType type, bool localizationEnabled = false)
- {
- return localizationEnabled
- ? type switch
- {
- ApplicationCommandType.ChatInput => DeepEqual(source, target, localizationEnabled),
- _ => (source.Name == target.Name)
- && (source.Type == target.Type) && (source.NameLocalizations == target.NameLocalizations)
- && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) && (source.DmPermission == target.DmPermission)
- }
- : type switch {
- ApplicationCommandType.ChatInput => DeepEqual(source, target),
- _ => (source.Name == target.Name)
- && (source.Type == target.Type)
- && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) && (source.DmPermission == target.DmPermission)
- };
- }
+ return ac1.Type == targetApplicationCommand.Type && sourceApplicationCommand.SoftEqual(targetApplicationCommand, ac1.Type, ApplicationCommandsExtension.Configuration?.EnableLocalization ?? false);
+ }
- /// <summary>
- /// Checks deeply whether two <see cref="DisCatSharp.Entities.DiscordApplicationCommand"/>s are the same.
- /// Excluding id, application id and version here.
- /// </summary>
- /// <param name="source">Source application command.</param>
- /// <param name="target">Application command to check against.</param>
- /// <param name="localizationEnabled">Whether localization is enabled.</param>
- internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicationCommand target, bool localizationEnabled = false)
- {
- var rootCheck = (source.Name == target.Name) && (source.Description == target.Description) && (source.Type == target.Type)
- && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) && (source.DmPermission == target.DmPermission);
- if (localizationEnabled)
- rootCheck = rootCheck && (source.NameLocalizations == target.NameLocalizations) && (source.DescriptionLocalizations == target.DescriptionLocalizations);
-
- if (source.Options == null && target.Options == null)
- return rootCheck;
- else if ((source.Options != null && target.Options == null) || (source.Options == null && target.Options != null))
- return false;
- else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup))
+ /// <summary>
+ /// Checks softly whether two <see cref="DisCatSharp.Entities.DiscordApplicationCommand"/>s are the same.
+ /// Excluding id, application id and version here.
+ /// </summary>
+ /// <param name="source">Source application command.</param>
+ /// <param name="target">Application command to check against.</param>
+ /// <param name="type">The application command type.</param>
+ /// <param name="localizationEnabled">Whether localization is enabled.</param>
+ internal static bool SoftEqual(this DiscordApplicationCommand source, DiscordApplicationCommand target, ApplicationCommandType type, bool localizationEnabled = false)
{
- List<DiscordApplicationCommandOption> minimalSourceOptions = new();
- List<DiscordApplicationCommandOption> minimalTargetOptions = new();
+ return localizationEnabled
+ ? type switch
+ {
+ ApplicationCommandType.ChatInput => DeepEqual(source, target, localizationEnabled),
+ _ => (source.Name == target.Name)
+ && (source.Type == target.Type) && (source.NameLocalizations == target.NameLocalizations)
+ && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) && (source.DmPermission == target.DmPermission)
+ }
+ : type switch {
+ ApplicationCommandType.ChatInput => DeepEqual(source, target),
+ _ => (source.Name == target.Name)
+ && (source.Type == target.Type)
+ && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) && (source.DmPermission == target.DmPermission)
+ };
+ }
- foreach (var option in source.Options)
+ /// <summary>
+ /// Checks deeply whether two <see cref="DisCatSharp.Entities.DiscordApplicationCommand"/>s are the same.
+ /// Excluding id, application id and version here.
+ /// </summary>
+ /// <param name="source">Source application command.</param>
+ /// <param name="target">Application command to check against.</param>
+ /// <param name="localizationEnabled">Whether localization is enabled.</param>
+ internal static bool DeepEqual(DiscordApplicationCommand source, DiscordApplicationCommand target, bool localizationEnabled = false)
+ {
+ var rootCheck = (source.Name == target.Name) && (source.Description == target.Description) && (source.Type == target.Type)
+ && (source.DefaultMemberPermissions == target.DefaultMemberPermissions) && (source.DmPermission == target.DmPermission);
+ if (localizationEnabled)
+ rootCheck = rootCheck && (source.NameLocalizations == target.NameLocalizations) && (source.DescriptionLocalizations == target.DescriptionLocalizations);
+
+ if (source.Options == null && target.Options == null)
+ return rootCheck;
+ else if ((source.Options != null && target.Options == null) || (source.Options == null && target.Options != null))
+ return false;
+ else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommandGroup))
{
- List<DiscordApplicationCommandOption> minimalSubSourceOptions = new();
+ List<DiscordApplicationCommandOption> minimalSourceOptions = new();
+ List<DiscordApplicationCommandOption> minimalTargetOptions = new();
- foreach (var subOption in option.Options)
+ foreach (var option in source.Options)
{
- List<DiscordApplicationCommandOption> minimalSubSubSourceOptions = null;
+ List<DiscordApplicationCommandOption> minimalSubSourceOptions = new();
- if (subOption.Options != null)
+ foreach (var subOption in option.Options)
{
- minimalSubSubSourceOptions = new();
+ List<DiscordApplicationCommandOption> minimalSubSubSourceOptions = null;
- foreach (var subSubOption in subOption.Options)
+ if (subOption.Options != null)
{
- minimalSubSubSourceOptions.Add(new DiscordApplicationCommandOption(
- subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required ?? false,
- subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete ?? false,
- subSubOption.MinimumValue, subSubOption.MaximumValue,
- localizationEnabled ? subSubOption.NameLocalizations : null,
- localizationEnabled ? subSubOption.DescriptionLocalizations : null
+ minimalSubSubSourceOptions = new();
+
+ foreach (var subSubOption in subOption.Options)
+ {
+ minimalSubSubSourceOptions.Add(new DiscordApplicationCommandOption(
+ subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required ?? false,
+ subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete ?? false,
+ subSubOption.MinimumValue, subSubOption.MaximumValue,
+ localizationEnabled ? subSubOption.NameLocalizations : null,
+ localizationEnabled ? subSubOption.DescriptionLocalizations : null
+ ));
+ }
+
+ minimalSubSourceOptions.Add(new DiscordApplicationCommandOption(
+ subOption.Name, subOption.Description, subOption.Type,
+ options: minimalSubSubSourceOptions,
+ nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null,
+ descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null
));
}
- minimalSubSourceOptions.Add(new DiscordApplicationCommandOption(
- subOption.Name, subOption.Description, subOption.Type,
- options: minimalSubSubSourceOptions,
- nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null,
- descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null
- ));
}
+ minimalSourceOptions.Add(new DiscordApplicationCommandOption(
+ option.Name, option.Description, option.Type,
+ options: minimalSubSourceOptions,
+ nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
+ descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
+ ));
}
- minimalSourceOptions.Add(new DiscordApplicationCommandOption(
- option.Name, option.Description, option.Type,
- options: minimalSubSourceOptions,
- nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
- descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
- ));
- }
-
- foreach (var option in target.Options)
- {
- List<DiscordApplicationCommandOption> minimalSubTargetOptions = new();
-
- foreach (var subOption in option.Options)
+ foreach (var option in target.Options)
{
- List<DiscordApplicationCommandOption> minimalSubSubTargetOptions = null;
+ List<DiscordApplicationCommandOption> minimalSubTargetOptions = new();
- if (subOption.Options != null && subOption.Options.Any())
+ foreach (var subOption in option.Options)
{
- minimalSubSubTargetOptions = new();
+ List<DiscordApplicationCommandOption> minimalSubSubTargetOptions = null;
- foreach (var subSubOption in subOption.Options)
+ if (subOption.Options != null && subOption.Options.Any())
{
- minimalSubSubTargetOptions.Add(new DiscordApplicationCommandOption(
- subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required ?? false,
- subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete ?? false,
- subSubOption.MinimumValue, subSubOption.MaximumValue,
- localizationEnabled ? subSubOption.NameLocalizations : null,
- localizationEnabled ? subSubOption.DescriptionLocalizations : null
+ minimalSubSubTargetOptions = new();
+
+ foreach (var subSubOption in subOption.Options)
+ {
+ minimalSubSubTargetOptions.Add(new DiscordApplicationCommandOption(
+ subSubOption.Name, subSubOption.Description, subSubOption.Type, subSubOption.Required ?? false,
+ subSubOption.Choices, null, subSubOption.ChannelTypes, subSubOption.AutoComplete ?? false,
+ subSubOption.MinimumValue, subSubOption.MaximumValue,
+ localizationEnabled ? subSubOption.NameLocalizations : null,
+ localizationEnabled ? subSubOption.DescriptionLocalizations : null
+ ));
+ }
+
+ minimalSubTargetOptions.Add(new DiscordApplicationCommandOption(
+ subOption.Name, subOption.Description, subOption.Type,
+ options: minimalSubSubTargetOptions,
+ nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null,
+ descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null
));
}
-
- minimalSubTargetOptions.Add(new DiscordApplicationCommandOption(
- subOption.Name, subOption.Description, subOption.Type,
- options: minimalSubSubTargetOptions,
- nameLocalizations: localizationEnabled ? subOption.NameLocalizations : null,
- descriptionLocalizations: localizationEnabled ? subOption.DescriptionLocalizations : null
- ));
}
+
+ minimalTargetOptions.Add(new DiscordApplicationCommandOption(
+ option.Name, option.Description, option.Type,
+ options: minimalSubTargetOptions,
+ nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
+ descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
+ ));
}
- minimalTargetOptions.Add(new DiscordApplicationCommandOption(
- option.Name, option.Description, option.Type,
- options: minimalSubTargetOptions,
- nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
- descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
- ));
+ return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions);
}
-
- return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions);
- }
- else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand))
- {
- List<DiscordApplicationCommandOption> minimalSourceOptions = new();
- List<DiscordApplicationCommandOption> minimalTargetOptions = new();
-
- foreach (var option in source.Options)
+ else if (source.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand) && target.Options.Any(o => o.Type == ApplicationCommandOptionType.SubCommand))
{
- List<DiscordApplicationCommandOption> minimalSubSourceOptions =null;
+ List<DiscordApplicationCommandOption> minimalSourceOptions = new();
+ List<DiscordApplicationCommandOption> minimalTargetOptions = new();
- if (option.Options != null)
+ foreach (var option in source.Options)
{
- minimalSubSourceOptions = new();
+ List<DiscordApplicationCommandOption> minimalSubSourceOptions =null;
- foreach (var subOption in option.Options)
+ if (option.Options != null)
{
- minimalSubSourceOptions.Add(new DiscordApplicationCommandOption(
- subOption.Name, subOption.Description, subOption.Type, subOption.Required ?? false,
- subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete ?? false,
- subOption.MinimumValue, subOption.MaximumValue,
- localizationEnabled ? subOption.NameLocalizations : null,
- localizationEnabled ? subOption.DescriptionLocalizations : null
- ));
- }
- }
+ minimalSubSourceOptions = new();
- minimalSourceOptions.Add(new DiscordApplicationCommandOption(
- option.Name, option.Description, option.Type,
- options: minimalSubSourceOptions,
- nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
- descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
- ));
- }
+ foreach (var subOption in option.Options)
+ {
+ minimalSubSourceOptions.Add(new DiscordApplicationCommandOption(
+ subOption.Name, subOption.Description, subOption.Type, subOption.Required ?? false,
+ subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete ?? false,
+ subOption.MinimumValue, subOption.MaximumValue,
+ localizationEnabled ? subOption.NameLocalizations : null,
+ localizationEnabled ? subOption.DescriptionLocalizations : null
+ ));
+ }
+ }
- foreach (var option in target.Options)
- {
- List<DiscordApplicationCommandOption> minimalSubTargetOptions = null;
+ minimalSourceOptions.Add(new DiscordApplicationCommandOption(
+ option.Name, option.Description, option.Type,
+ options: minimalSubSourceOptions,
+ nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
+ descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
+ ));
+ }
- if (option.Options != null && option.Options.Any())
+ foreach (var option in target.Options)
{
- minimalSubTargetOptions = new();
+ List<DiscordApplicationCommandOption> minimalSubTargetOptions = null;
- foreach (var subOption in option.Options)
+ if (option.Options != null && option.Options.Any())
{
- minimalSubTargetOptions.Add(new DiscordApplicationCommandOption(
- subOption.Name, subOption.Description, subOption.Type, subOption.Required ?? false,
- subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete ?? false,
- subOption.MinimumValue, subOption.MaximumValue,
- localizationEnabled ? subOption.NameLocalizations : null,
- localizationEnabled ? subOption.DescriptionLocalizations : null
- ));
+ minimalSubTargetOptions = new();
+
+ foreach (var subOption in option.Options)
+ {
+ minimalSubTargetOptions.Add(new DiscordApplicationCommandOption(
+ subOption.Name, subOption.Description, subOption.Type, subOption.Required ?? false,
+ subOption.Choices, null, subOption.ChannelTypes, subOption.AutoComplete ?? false,
+ subOption.MinimumValue, subOption.MaximumValue,
+ localizationEnabled ? subOption.NameLocalizations : null,
+ localizationEnabled ? subOption.DescriptionLocalizations : null
+ ));
+ }
}
+
+ minimalTargetOptions.Add(new DiscordApplicationCommandOption(
+ option.Name, option.Description, option.Type,
+ options: minimalSubTargetOptions,
+ nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
+ descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
+ ));
}
- minimalTargetOptions.Add(new DiscordApplicationCommandOption(
- option.Name, option.Description, option.Type,
- options: minimalSubTargetOptions,
- nameLocalizations: localizationEnabled ? option.NameLocalizations : null,
- descriptionLocalizations: localizationEnabled ? option.DescriptionLocalizations : null
- ));
+ return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions);
+ }
+ else
+ {
+ List<DiscordApplicationCommandOption> minimalSourceOptions = new();
+ List<DiscordApplicationCommandOption> minimalTargetOptions = new();
+
+ foreach (var option in source.Options)
+ minimalSourceOptions.Add(new DiscordApplicationCommandOption(
+ option.Name, option.Description, option.Type, option.Required ?? false,
+ option.Choices, null, option.ChannelTypes, option.AutoComplete ?? false, option.MinimumValue, option.MaximumValue,
+ localizationEnabled ? option.NameLocalizations : null,
+ localizationEnabled ? option.DescriptionLocalizations : null
+ ));
+
+ foreach (var option in target.Options)
+ minimalTargetOptions.Add(new DiscordApplicationCommandOption(
+ option.Name, option.Description, option.Type, option.Required ?? false,
+ option.Choices, null, option.ChannelTypes, option.AutoComplete ?? false, option.MinimumValue, option.MaximumValue,
+ localizationEnabled ? option.NameLocalizations : null,
+ localizationEnabled ? option.DescriptionLocalizations : null
+ ));
+
+ return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions);
}
-
- return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions);
- }
- else
- {
- List<DiscordApplicationCommandOption> minimalSourceOptions = new();
- List<DiscordApplicationCommandOption> minimalTargetOptions = new();
-
- foreach (var option in source.Options)
- minimalSourceOptions.Add(new DiscordApplicationCommandOption(
- option.Name, option.Description, option.Type, option.Required ?? false,
- option.Choices, null, option.ChannelTypes, option.AutoComplete ?? false, option.MinimumValue, option.MaximumValue,
- localizationEnabled ? option.NameLocalizations : null,
- localizationEnabled ? option.DescriptionLocalizations : null
- ));
-
- foreach (var option in target.Options)
- minimalTargetOptions.Add(new DiscordApplicationCommandOption(
- option.Name, option.Description, option.Type, option.Required ?? false,
- option.Choices, null, option.ChannelTypes, option.AutoComplete ?? false, option.MinimumValue, option.MaximumValue,
- localizationEnabled ? option.NameLocalizations : null,
- localizationEnabled ? option.DescriptionLocalizations : null
- ));
-
- return rootCheck && JsonConvert.SerializeObject(minimalSourceOptions) == JsonConvert.SerializeObject(minimalTargetOptions);
}
}
}
diff --git a/DisCatSharp.ApplicationCommands/Context/ApplicationCommandsTranslationContext.cs b/DisCatSharp.ApplicationCommands/Context/ApplicationCommandsTranslationContext.cs
index 300060de8..115e2f62c 100644
--- a/DisCatSharp.ApplicationCommands/Context/ApplicationCommandsTranslationContext.cs
+++ b/DisCatSharp.ApplicationCommands/Context/ApplicationCommandsTranslationContext.cs
@@ -1,60 +1,61 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.ApplicationCommands;
-
-/// <summary>
-/// The application commands translation context.
-/// </summary>
-public class ApplicationCommandsTranslationContext
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the type.
+ /// The application commands translation context.
/// </summary>
- public Type Type { get; }
+ public class ApplicationCommandsTranslationContext
+ {
+ /// <summary>
+ /// Gets the type.
+ /// </summary>
+ public Type Type { get; }
- /// <summary>
- /// Gets the name.
- /// </summary>
- public string Name { get; }
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ public string Name { get; }
- /// <summary>
- /// Gets the translation json.
- /// </summary>
- internal string Translations { get; set; }
+ /// <summary>
+ /// Gets the translation json.
+ /// </summary>
+ internal string Translations { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ApplicationCommandsTranslationContext"/> class.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="name">The name.</param>
- internal ApplicationCommandsTranslationContext(Type type, string name)
- {
- this.Type = type;
- this.Name = name;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ApplicationCommandsTranslationContext"/> class.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="name">The name.</param>
+ internal ApplicationCommandsTranslationContext(Type type, string name)
+ {
+ this.Type = type;
+ this.Name = name;
+ }
- public void AddTranslation(string translationJson)
- => this.Translations = translationJson;
+ public void AddTranslation(string translationJson)
+ => this.Translations = translationJson;
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Context/AutocompleteContext.cs b/DisCatSharp.ApplicationCommands/Context/AutocompleteContext.cs
index 5c48aae0b..8c48bb9fb 100644
--- a/DisCatSharp.ApplicationCommands/Context/AutocompleteContext.cs
+++ b/DisCatSharp.ApplicationCommands/Context/AutocompleteContext.cs
@@ -1,99 +1,100 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a context for an autocomplete interaction.
-/// </summary>
-public class AutocompleteContext
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// The interaction created.
- /// </summary>
- public DiscordInteraction Interaction { get; internal set; }
-
- /// <summary>
- /// Gets the client for this interaction.
- /// </summary>
- public DiscordClient Client { get; internal set; }
-
- /// <summary>
- /// Gets the guild this interaction was executed in.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Gets the channel this interaction was executed in.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the user which executed this interaction.
- /// </summary>
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Gets the member which executed this interaction, or null if the command is in a DM.
- /// </summary>
- public DiscordMember Member
- => this.User is DiscordMember member ? member : null;
-
- /// <summary>
- /// Gets the invoking user locale.
- /// </summary>
- public string Locale { get; internal set; }
-
- /// <summary>
- /// Gets the guild locale if applicable.
- /// </summary>
- public string GuildLocale { get; internal set; }
-
- /// <summary>
- /// Gets the slash command module this interaction was created in.
- /// </summary>
- public ApplicationCommandsExtension ApplicationCommandsExtension { get; internal set; }
-
- /// <summary>
- /// <para>Gets the service provider.</para>
- /// <para>This allows passing data around without resorting to static members.</para>
- /// <para>Defaults to an empty service provider.</para>
- /// </summary>
- public IServiceProvider Services { get; internal set; } = new ServiceCollection().BuildServiceProvider(true);
-
- /// <summary>
- /// The options already provided.
- /// </summary>
- public IReadOnlyList<DiscordInteractionDataOption> Options { get; internal set; }
-
- /// <summary>
- /// The option to autocomplete.
+ /// Represents a context for an autocomplete interaction.
/// </summary>
- public DiscordInteractionDataOption FocusedOption { get; internal set; }
+ public class AutocompleteContext
+ {
+ /// <summary>
+ /// The interaction created.
+ /// </summary>
+ public DiscordInteraction Interaction { get; internal set; }
+
+ /// <summary>
+ /// Gets the client for this interaction.
+ /// </summary>
+ public DiscordClient Client { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild this interaction was executed in.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Gets the channel this interaction was executed in.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the user which executed this interaction.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the member which executed this interaction, or null if the command is in a DM.
+ /// </summary>
+ public DiscordMember Member
+ => this.User is DiscordMember member ? member : null;
+
+ /// <summary>
+ /// Gets the invoking user locale.
+ /// </summary>
+ public string Locale { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild locale if applicable.
+ /// </summary>
+ public string GuildLocale { get; internal set; }
+
+ /// <summary>
+ /// Gets the slash command module this interaction was created in.
+ /// </summary>
+ public ApplicationCommandsExtension ApplicationCommandsExtension { get; internal set; }
+
+ /// <summary>
+ /// <para>Gets the service provider.</para>
+ /// <para>This allows passing data around without resorting to static members.</para>
+ /// <para>Defaults to an empty service provider.</para>
+ /// </summary>
+ public IServiceProvider Services { get; internal set; } = new ServiceCollection().BuildServiceProvider(true);
+
+ /// <summary>
+ /// The options already provided.
+ /// </summary>
+ public IReadOnlyList<DiscordInteractionDataOption> Options { get; internal set; }
+
+ /// <summary>
+ /// The option to autocomplete.
+ /// </summary>
+ public DiscordInteractionDataOption FocusedOption { get; internal set; }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Context/BaseContext.cs b/DisCatSharp.ApplicationCommands/Context/BaseContext.cs
index 73bf2c39c..a73d7f162 100644
--- a/DisCatSharp.ApplicationCommands/Context/BaseContext.cs
+++ b/DisCatSharp.ApplicationCommands/Context/BaseContext.cs
@@ -1,198 +1,199 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a base context for application command contexts.
-/// </summary>
-public class BaseContext
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the interaction that was created.
- /// </summary>
- public DiscordInteraction Interaction { get; internal set; }
-
- /// <summary>
- /// Gets the client for this interaction.
- /// </summary>
- public DiscordClient Client { get; internal set; }
-
- /// <summary>
- /// Gets the guild this interaction was executed in.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Gets the channel this interaction was executed in.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the user which executed this interaction.
- /// </summary>
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Gets the member which executed this interaction, or null if the command is in a DM.
- /// </summary>
- public DiscordMember Member
- => this.User is DiscordMember member ? member : null;
-
- /// <summary>
- /// Gets the application command module this interaction was created in.
- /// </summary>
- public ApplicationCommandsExtension ApplicationCommandsExtension { get; internal set; }
-
- /// <summary>
- /// Gets the token for this interaction.
- /// </summary>
- public string Token { get; internal set; }
-
- /// <summary>
- /// Gets the id for this interaction.
- /// </summary>
- public ulong InteractionId { get; internal set; }
-
- /// <summary>
- /// Gets the name of the command.
- /// </summary>
- public string CommandName { get; internal set; }
-
- /// <summary>
- /// Gets the invoking user locale.
- /// </summary>
- public string Locale { get; internal set; }
-
- /// <summary>
- /// Gets the guild locale if applicable.
- /// </summary>
- public string GuildLocale { get; internal set; }
-
- /// <summary>
- /// Gets the type of this interaction.
- /// </summary>
- public ApplicationCommandType Type { get; internal set; }
-
- /// <summary>
- /// <para>Gets the service provider.</para>
- /// <para>This allows passing data around without resorting to static members.</para>
- /// <para>Defaults to an empty service provider.</para>
- /// </summary>
- public IServiceProvider Services { get; internal set; } = new ServiceCollection().BuildServiceProvider(true);
-
- /// <summary>
- /// Creates a response to this interaction.
- /// <para>You must create a response within 3 seconds of this interaction being executed; if the command has the potential to take more than 3 seconds, create a <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/> at the start, and edit the response later.</para>
- /// </summary>
- /// <param name="type">The type of the response.</param>
- /// <param name="builder">The data to be sent, if any.</param>
- /// <returns></returns>
- public Task CreateResponseAsync(InteractionResponseType type, DiscordInteractionResponseBuilder builder = null)
- => this.Interaction.CreateResponseAsync(type, builder);
-
- /// <summary>
- /// Creates a modal response to this interaction.
- /// </summary>
- /// <param name="builder">The data to send.</param>
- public Task CreateModalResponseAsync(DiscordInteractionModalBuilder builder)
- => this.Interaction.Type != InteractionType.Ping && this.Interaction.Type != InteractionType.ModalSubmit ? this.Interaction.CreateInteractionModalResponseAsync(builder) : throw new NotSupportedException("You can't respond to an PING with a modal.");
-
- /// <summary>
- /// Edits the interaction response.
- /// </summary>
- /// <param name="builder">The data to edit the response with.</param>
- /// <returns></returns>
- public Task<DiscordMessage> EditResponseAsync(DiscordWebhookBuilder builder)
- => this.Interaction.EditOriginalResponseAsync(builder);
-
- /// <summary>
- /// Deletes the interaction response.
- /// </summary>
- /// <returns></returns>
- public Task DeleteResponseAsync()
- => this.Interaction.DeleteOriginalResponseAsync();
-
- /// <summary>
- /// Creates a follow up message to the interaction.
- /// </summary>
- /// <param name="builder">The message to be sent, in the form of a webhook.</param>
- /// <returns>The created message.</returns>
- public Task<DiscordMessage> FollowUpAsync(DiscordFollowupMessageBuilder builder)
- => this.Interaction.CreateFollowupMessageAsync(builder);
-
- /// <summary>
- /// Creates a follow up message to the interaction.
- /// </summary>
- /// <param name="content">The content of the message to be sent.</param>
- /// <returns>The created message.</returns>
- public Task<DiscordMessage> FollowUpAsync(string content)
- => this.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(content));
-
- /// <summary>
- /// Edits a followup message.
- /// </summary>
- /// <param name="followupMessageId">The id of the followup message to edit.</param>
- /// <param name="builder">The webhook builder.</param>
- /// <returns>The created message.</returns>
- public Task<DiscordMessage> EditFollowupAsync(ulong followupMessageId, DiscordWebhookBuilder builder)
- => this.Interaction.EditFollowupMessageAsync(followupMessageId, builder);
-
- /// <summary>
- /// Edits a followup message.
- /// </summary>
- /// <param name="followupMessageId">The id of the followup message to edit.</param>
- /// <param name="content">The content of the webhook.</param>
- /// <returns>The created message.</returns>
- public Task<DiscordMessage> EditFollowupAsync(ulong followupMessageId, string content)
- => this.EditFollowupAsync(followupMessageId, new DiscordWebhookBuilder().WithContent(content));
-
- /// <summary>
- /// Deletes a followup message.
- /// </summary>
- /// <param name="followupMessageId">The id of the followup message to delete.</param>
- /// <returns></returns>
- public Task DeleteFollowupAsync(ulong followupMessageId)
- => this.Interaction.DeleteFollowupMessageAsync(followupMessageId);
-
- /// <summary>
- /// Gets the followup message.
- /// </summary>
- /// <param name="followupMessageId">The followup message id.</param>
- public Task<DiscordMessage> GetFollowupMessageAsync(ulong followupMessageId)
- => this.Interaction.GetFollowupMessageAsync(followupMessageId);
-
- /// <summary>
- /// Gets the original interaction response.
- /// </summary>
- /// <returns>The original interaction response.</returns>
- public Task<DiscordMessage> GetOriginalResponseAsync()
- => this.Interaction.GetOriginalResponseAsync();
+ /// Represents a base context for application command contexts.
+ /// </summary>
+ public class BaseContext
+ {
+ /// <summary>
+ /// Gets the interaction that was created.
+ /// </summary>
+ public DiscordInteraction Interaction { get; internal set; }
+
+ /// <summary>
+ /// Gets the client for this interaction.
+ /// </summary>
+ public DiscordClient Client { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild this interaction was executed in.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Gets the channel this interaction was executed in.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the user which executed this interaction.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the member which executed this interaction, or null if the command is in a DM.
+ /// </summary>
+ public DiscordMember Member
+ => this.User is DiscordMember member ? member : null;
+
+ /// <summary>
+ /// Gets the application command module this interaction was created in.
+ /// </summary>
+ public ApplicationCommandsExtension ApplicationCommandsExtension { get; internal set; }
+
+ /// <summary>
+ /// Gets the token for this interaction.
+ /// </summary>
+ public string Token { get; internal set; }
+
+ /// <summary>
+ /// Gets the id for this interaction.
+ /// </summary>
+ public ulong InteractionId { get; internal set; }
+
+ /// <summary>
+ /// Gets the name of the command.
+ /// </summary>
+ public string CommandName { get; internal set; }
+
+ /// <summary>
+ /// Gets the invoking user locale.
+ /// </summary>
+ public string Locale { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild locale if applicable.
+ /// </summary>
+ public string GuildLocale { get; internal set; }
+
+ /// <summary>
+ /// Gets the type of this interaction.
+ /// </summary>
+ public ApplicationCommandType Type { get; internal set; }
+
+ /// <summary>
+ /// <para>Gets the service provider.</para>
+ /// <para>This allows passing data around without resorting to static members.</para>
+ /// <para>Defaults to an empty service provider.</para>
+ /// </summary>
+ public IServiceProvider Services { get; internal set; } = new ServiceCollection().BuildServiceProvider(true);
+
+ /// <summary>
+ /// Creates a response to this interaction.
+ /// <para>You must create a response within 3 seconds of this interaction being executed; if the command has the potential to take more than 3 seconds, create a <see cref="InteractionResponseType.DeferredChannelMessageWithSource"/> at the start, and edit the response later.</para>
+ /// </summary>
+ /// <param name="type">The type of the response.</param>
+ /// <param name="builder">The data to be sent, if any.</param>
+ /// <returns></returns>
+ public Task CreateResponseAsync(InteractionResponseType type, DiscordInteractionResponseBuilder builder = null)
+ => this.Interaction.CreateResponseAsync(type, builder);
+
+ /// <summary>
+ /// Creates a modal response to this interaction.
+ /// </summary>
+ /// <param name="builder">The data to send.</param>
+ public Task CreateModalResponseAsync(DiscordInteractionModalBuilder builder)
+ => this.Interaction.Type != InteractionType.Ping && this.Interaction.Type != InteractionType.ModalSubmit ? this.Interaction.CreateInteractionModalResponseAsync(builder) : throw new NotSupportedException("You can't respond to an PING with a modal.");
+
+ /// <summary>
+ /// Edits the interaction response.
+ /// </summary>
+ /// <param name="builder">The data to edit the response with.</param>
+ /// <returns></returns>
+ public Task<DiscordMessage> EditResponseAsync(DiscordWebhookBuilder builder)
+ => this.Interaction.EditOriginalResponseAsync(builder);
+
+ /// <summary>
+ /// Deletes the interaction response.
+ /// </summary>
+ /// <returns></returns>
+ public Task DeleteResponseAsync()
+ => this.Interaction.DeleteOriginalResponseAsync();
+
+ /// <summary>
+ /// Creates a follow up message to the interaction.
+ /// </summary>
+ /// <param name="builder">The message to be sent, in the form of a webhook.</param>
+ /// <returns>The created message.</returns>
+ public Task<DiscordMessage> FollowUpAsync(DiscordFollowupMessageBuilder builder)
+ => this.Interaction.CreateFollowupMessageAsync(builder);
+
+ /// <summary>
+ /// Creates a follow up message to the interaction.
+ /// </summary>
+ /// <param name="content">The content of the message to be sent.</param>
+ /// <returns>The created message.</returns>
+ public Task<DiscordMessage> FollowUpAsync(string content)
+ => this.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(content));
+
+ /// <summary>
+ /// Edits a followup message.
+ /// </summary>
+ /// <param name="followupMessageId">The id of the followup message to edit.</param>
+ /// <param name="builder">The webhook builder.</param>
+ /// <returns>The created message.</returns>
+ public Task<DiscordMessage> EditFollowupAsync(ulong followupMessageId, DiscordWebhookBuilder builder)
+ => this.Interaction.EditFollowupMessageAsync(followupMessageId, builder);
+
+ /// <summary>
+ /// Edits a followup message.
+ /// </summary>
+ /// <param name="followupMessageId">The id of the followup message to edit.</param>
+ /// <param name="content">The content of the webhook.</param>
+ /// <returns>The created message.</returns>
+ public Task<DiscordMessage> EditFollowupAsync(ulong followupMessageId, string content)
+ => this.EditFollowupAsync(followupMessageId, new DiscordWebhookBuilder().WithContent(content));
+
+ /// <summary>
+ /// Deletes a followup message.
+ /// </summary>
+ /// <param name="followupMessageId">The id of the followup message to delete.</param>
+ /// <returns></returns>
+ public Task DeleteFollowupAsync(ulong followupMessageId)
+ => this.Interaction.DeleteFollowupMessageAsync(followupMessageId);
+
+ /// <summary>
+ /// Gets the followup message.
+ /// </summary>
+ /// <param name="followupMessageId">The followup message id.</param>
+ public Task<DiscordMessage> GetFollowupMessageAsync(ulong followupMessageId)
+ => this.Interaction.GetFollowupMessageAsync(followupMessageId);
+
+ /// <summary>
+ /// Gets the original interaction response.
+ /// </summary>
+ /// <returns>The original interaction response.</returns>
+ public Task<DiscordMessage> GetOriginalResponseAsync()
+ => this.Interaction.GetOriginalResponseAsync();
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Context/ContextMenuContext.cs b/DisCatSharp.ApplicationCommands/Context/ContextMenuContext.cs
index 41d73ad4c..2494f8175 100644
--- a/DisCatSharp.ApplicationCommands/Context/ContextMenuContext.cs
+++ b/DisCatSharp.ApplicationCommands/Context/ContextMenuContext.cs
@@ -1,47 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a context for a context menu.
-/// </summary>
-public sealed class ContextMenuContext : BaseContext
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// The user this command targets, if applicable.
+ /// Represents a context for a context menu.
/// </summary>
- public DiscordUser TargetUser { get; internal set; }
+ public sealed class ContextMenuContext : BaseContext
+ {
+ /// <summary>
+ /// The user this command targets, if applicable.
+ /// </summary>
+ public DiscordUser TargetUser { get; internal set; }
- /// <summary>
- /// The member this command targets, if applicable.
- /// </summary>
- public DiscordMember TargetMember
- => this.TargetUser is DiscordMember member ? member : null;
+ /// <summary>
+ /// The member this command targets, if applicable.
+ /// </summary>
+ public DiscordMember TargetMember
+ => this.TargetUser is DiscordMember member ? member : null;
- /// <summary>
- /// The message this command targets, if applicable.
- /// </summary>
- public DiscordMessage TargetMessage { get; internal set; }
+ /// <summary>
+ /// The message this command targets, if applicable.
+ /// </summary>
+ public DiscordMessage TargetMessage { get; internal set; }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Context/InteractionContext.cs b/DisCatSharp.ApplicationCommands/Context/InteractionContext.cs
index df19b3e85..0634b07f3 100644
--- a/DisCatSharp.ApplicationCommands/Context/InteractionContext.cs
+++ b/DisCatSharp.ApplicationCommands/Context/InteractionContext.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a context for an interaction.
-/// </summary>
-public sealed class InteractionContext : BaseContext
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the users mentioned in the command parameters.
+ /// Represents a context for an interaction.
/// </summary>
- public IReadOnlyList<DiscordUser> ResolvedUserMentions { get; internal set; }
+ public sealed class InteractionContext : BaseContext
+ {
+ /// <summary>
+ /// Gets the users mentioned in the command parameters.
+ /// </summary>
+ public IReadOnlyList<DiscordUser> ResolvedUserMentions { get; internal set; }
- /// <summary>
- /// Gets the roles mentioned in the command parameters.
- /// </summary>
- public IReadOnlyList<DiscordRole> ResolvedRoleMentions { get; internal set; }
+ /// <summary>
+ /// Gets the roles mentioned in the command parameters.
+ /// </summary>
+ public IReadOnlyList<DiscordRole> ResolvedRoleMentions { get; internal set; }
- /// <summary>
- /// Gets the channels mentioned in the command parameters.
- /// </summary>
- public IReadOnlyList<DiscordChannel> ResolvedChannelMentions { get; internal set; }
+ /// <summary>
+ /// Gets the channels mentioned in the command parameters.
+ /// </summary>
+ public IReadOnlyList<DiscordChannel> ResolvedChannelMentions { get; internal set; }
- /// <summary>
- /// Gets the attachments in the command parameters, if applicable.
- /// </summary>
- public IReadOnlyList<DiscordAttachment> ResolvedAttachments { get; internal set; }
+ /// <summary>
+ /// Gets the attachments in the command parameters, if applicable.
+ /// </summary>
+ public IReadOnlyList<DiscordAttachment> ResolvedAttachments { get; internal set; }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Entities/ChoiceTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/ChoiceTranslator.cs
index 6f9ab002c..eb0bb777c 100644
--- a/DisCatSharp.ApplicationCommands/Entities/ChoiceTranslator.cs
+++ b/DisCatSharp.ApplicationCommands/Entities/ChoiceTranslator.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a choice translator.
-/// </summary>
-internal class ChoiceTranslator
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the choice name.
+ /// Represents a choice translator.
/// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
+ internal class ChoiceTranslator
+ {
+ /// <summary>
+ /// Gets the choice name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
- /// <summary>
- /// Gets the choice name translations.
- /// </summary>
- [JsonProperty("name_translations")]
- internal Dictionary<string, string> NameTranslationsDictionary { get; set; }
- [JsonIgnore]
- public DiscordApplicationCommandLocalization NameTranslations
- => new(this.NameTranslationsDictionary);
+ /// <summary>
+ /// Gets the choice name translations.
+ /// </summary>
+ [JsonProperty("name_translations")]
+ internal Dictionary<string, string> NameTranslationsDictionary { get; set; }
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization NameTranslations
+ => new(this.NameTranslationsDictionary);
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Entities/CommandTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/CommandTranslator.cs
index df5572718..4bd5672e9 100644
--- a/DisCatSharp.ApplicationCommands/Entities/CommandTranslator.cs
+++ b/DisCatSharp.ApplicationCommands/Entities/CommandTranslator.cs
@@ -1,73 +1,74 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a command translator.
-/// </summary>
-internal class CommandTranslator
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the command name.
+ /// Represents a command translator.
/// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
+ internal class CommandTranslator
+ {
+ /// <summary>
+ /// Gets the command name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
- /// <summary>
- /// Gets the application command type.
- /// Used to determine whether it is an translator for context menu or not.
- /// </summary>
- [JsonProperty("type")]
- public ApplicationCommandType Type { get; set; }
+ /// <summary>
+ /// Gets the application command type.
+ /// Used to determine whether it is an translator for context menu or not.
+ /// </summary>
+ [JsonProperty("type")]
+ public ApplicationCommandType Type { get; set; }
- /// <summary>
- /// Gets the command name translations.
- /// </summary>
- [JsonProperty("name_translations")]
- internal Dictionary<string, string> NameTranslationDictionary { get; set; }
- [JsonIgnore]
- public DiscordApplicationCommandLocalization NameTranslations
- => new(this.NameTranslationDictionary);
+ /// <summary>
+ /// Gets the command name translations.
+ /// </summary>
+ [JsonProperty("name_translations")]
+ internal Dictionary<string, string> NameTranslationDictionary { get; set; }
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization NameTranslations
+ => new(this.NameTranslationDictionary);
- /// <summary>
- /// Gets the command description translations.
- /// </summary>
- [JsonProperty("description_translations")]
- internal Dictionary<string, string> DescriptionTranslationDictionary { get; set; }
- [JsonIgnore]
- public DiscordApplicationCommandLocalization DescriptionTranslations
- => new(this.DescriptionTranslationDictionary);
+ /// <summary>
+ /// Gets the command description translations.
+ /// </summary>
+ [JsonProperty("description_translations")]
+ internal Dictionary<string, string> DescriptionTranslationDictionary { get; set; }
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization DescriptionTranslations
+ => new(this.DescriptionTranslationDictionary);
- /// <summary>
- /// Gets the option translators, if applicable.
- /// </summary>
- [JsonProperty("options")]
- public List<OptionTranslator> Options { get; set; }
+ /// <summary>
+ /// Gets the option translators, if applicable.
+ /// </summary>
+ [JsonProperty("options")]
+ public List<OptionTranslator> Options { get; set; }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Entities/GroupTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/GroupTranslator.cs
index 8e4d04b3e..f50268c9d 100644
--- a/DisCatSharp.ApplicationCommands/Entities/GroupTranslator.cs
+++ b/DisCatSharp.ApplicationCommands/Entities/GroupTranslator.cs
@@ -1,71 +1,72 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a group translator.
-/// </summary>
-internal class GroupTranslator
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the group name.
+ /// Represents a group translator.
/// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
+ internal class GroupTranslator
+ {
+ /// <summary>
+ /// Gets the group name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
- /// <summary>
- /// Gets the group name translations.
- /// </summary>
- [JsonProperty("name_translations")]
- internal Dictionary<string, string> NameTranslationsDictionary { get; set; }
- [JsonIgnore]
- public DiscordApplicationCommandLocalization NameTranslations
- => new(this.NameTranslationsDictionary);
+ /// <summary>
+ /// Gets the group name translations.
+ /// </summary>
+ [JsonProperty("name_translations")]
+ internal Dictionary<string, string> NameTranslationsDictionary { get; set; }
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization NameTranslations
+ => new(this.NameTranslationsDictionary);
- /// <summary>
- /// Gets the group description translations.
- /// </summary>
- [JsonProperty("description_translations")]
- internal Dictionary<string, string> DescriptionTranslationsDictionary { get; set; }
- [JsonIgnore]
- public DiscordApplicationCommandLocalization DescriptionTranslations
- => new(this.DescriptionTranslationsDictionary);
+ /// <summary>
+ /// Gets the group description translations.
+ /// </summary>
+ [JsonProperty("description_translations")]
+ internal Dictionary<string, string> DescriptionTranslationsDictionary { get; set; }
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization DescriptionTranslations
+ => new(this.DescriptionTranslationsDictionary);
- /// <summary>
- /// Gets the sub group translators, if applicable.
- /// </summary>
- [JsonProperty("groups")]
- public List<SubGroupTranslator> SubGroups { get; set; }
+ /// <summary>
+ /// Gets the sub group translators, if applicable.
+ /// </summary>
+ [JsonProperty("groups")]
+ public List<SubGroupTranslator> SubGroups { get; set; }
- /// <summary>
- /// Gets the command translators, if applicable.
- /// </summary>
- [JsonProperty("commands")]
- public List<CommandTranslator> Commands { get; set; }
+ /// <summary>
+ /// Gets the command translators, if applicable.
+ /// </summary>
+ [JsonProperty("commands")]
+ public List<CommandTranslator> Commands { get; set; }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Entities/OptionTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/OptionTranslator.cs
index be8525313..cffe3e0ac 100644
--- a/DisCatSharp.ApplicationCommands/Entities/OptionTranslator.cs
+++ b/DisCatSharp.ApplicationCommands/Entities/OptionTranslator.cs
@@ -1,65 +1,66 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a option translator.
-/// </summary>
-internal class OptionTranslator
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the option name.
+ /// Represents a option translator.
/// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
+ internal class OptionTranslator
+ {
+ /// <summary>
+ /// Gets the option name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
- /// <summary>
- /// Gets the option name translations.
- /// </summary>
- [JsonProperty("name_translations")]
- internal Dictionary<string, string> NameTranslationsDictionary { get; set; }
- [JsonIgnore]
- public DiscordApplicationCommandLocalization NameTranslations
- => new(this.NameTranslationsDictionary);
+ /// <summary>
+ /// Gets the option name translations.
+ /// </summary>
+ [JsonProperty("name_translations")]
+ internal Dictionary<string, string> NameTranslationsDictionary { get; set; }
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization NameTranslations
+ => new(this.NameTranslationsDictionary);
- /// <summary>
- /// Gets the option description translations.
- /// </summary>
- [JsonProperty("description_translations")]
- internal Dictionary<string, string> DescriptionTranslationsDictionary { get; set; }
- [JsonIgnore]
- public DiscordApplicationCommandLocalization DescriptionTranslations
- => new(this.DescriptionTranslationsDictionary);
+ /// <summary>
+ /// Gets the option description translations.
+ /// </summary>
+ [JsonProperty("description_translations")]
+ internal Dictionary<string, string> DescriptionTranslationsDictionary { get; set; }
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization DescriptionTranslations
+ => new(this.DescriptionTranslationsDictionary);
- /// <summary>
- /// Gets the choice translators, if applicable.
- /// </summary>
- [JsonProperty("choices")]
- public List<ChoiceTranslator> Choices { get; set; }
+ /// <summary>
+ /// Gets the choice translators, if applicable.
+ /// </summary>
+ [JsonProperty("choices")]
+ public List<ChoiceTranslator> Choices { get; set; }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Entities/SubGroupTranslator.cs b/DisCatSharp.ApplicationCommands/Entities/SubGroupTranslator.cs
index 5639c4c04..5af69fdc3 100644
--- a/DisCatSharp.ApplicationCommands/Entities/SubGroupTranslator.cs
+++ b/DisCatSharp.ApplicationCommands/Entities/SubGroupTranslator.cs
@@ -1,65 +1,66 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a sub group translator.
-/// </summary>
-internal class SubGroupTranslator
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Gets the sub group name.
+ /// Represents a sub group translator.
/// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
+ internal class SubGroupTranslator
+ {
+ /// <summary>
+ /// Gets the sub group name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
- /// <summary>
- /// Gets the sub group name translations.
- /// </summary>
- [JsonProperty("name_translations")]
- internal Dictionary<string, string> NameTranslationsDictionary { get; set; }
- [JsonIgnore]
- public DiscordApplicationCommandLocalization NameTranslations
- => new(this.NameTranslationsDictionary);
+ /// <summary>
+ /// Gets the sub group name translations.
+ /// </summary>
+ [JsonProperty("name_translations")]
+ internal Dictionary<string, string> NameTranslationsDictionary { get; set; }
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization NameTranslations
+ => new(this.NameTranslationsDictionary);
- /// <summary>
- /// Gets the sub group description translations.
- /// </summary>
- [JsonProperty("description_translations")]
- internal Dictionary<string, string> DescriptionTranslationsDictionary { get; set; }
- [JsonIgnore]
- public DiscordApplicationCommandLocalization DescriptionTranslations
- => new(this.DescriptionTranslationsDictionary);
+ /// <summary>
+ /// Gets the sub group description translations.
+ /// </summary>
+ [JsonProperty("description_translations")]
+ internal Dictionary<string, string> DescriptionTranslationsDictionary { get; set; }
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization DescriptionTranslations
+ => new(this.DescriptionTranslationsDictionary);
- /// <summary>
- /// Gets the command translators.
- /// </summary>
- [JsonProperty("commands")]
- public List<CommandTranslator> Commands { get; set; }
+ /// <summary>
+ /// Gets the command translators.
+ /// </summary>
+ [JsonProperty("commands")]
+ public List<CommandTranslator> Commands { get; set; }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs
index 9e2a35929..6aeb86e2e 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs
@@ -1,50 +1,52 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.ApplicationCommands.EventArgs;
-
-/// <summary>
-/// Represents arguments for a <see cref="ApplicationCommandsExtension.ContextMenuErrored"/>
-/// </summary>
-public class ContextMenuErrorEventArgs : DiscordEventArgs
+namespace DisCatSharp.ApplicationCommands.EventArgs
{
- /// <summary>
- /// The context of the command.
- /// </summary>
- public ContextMenuContext Context { get; internal set; }
/// <summary>
- /// The exception thrown.
+ /// Represents arguments for a <see cref="ApplicationCommandsExtension.ContextMenuErrored"/>
/// </summary>
- public Exception Exception { get; internal set; }
+ public class ContextMenuErrorEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// The context of the command.
+ /// </summary>
+ public ContextMenuContext Context { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ContextMenuErrorEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- public ContextMenuErrorEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// The exception thrown.
+ /// </summary>
+ public Exception Exception { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ContextMenuErrorEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public ContextMenuErrorEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs
index 763cdd115..d6cc94976 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.ApplicationCommands.EventArgs;
-
-/// <summary>
-/// Represents the arguments for a <see cref="ApplicationCommandsExtension.ContextMenuExecuted"/> event
-/// </summary>
-public sealed class ContextMenuExecutedEventArgs : DiscordEventArgs
+namespace DisCatSharp.ApplicationCommands.EventArgs
{
/// <summary>
- /// The context of the command.
+ /// Represents the arguments for a <see cref="ApplicationCommandsExtension.ContextMenuExecuted"/> event
/// </summary>
- public ContextMenuContext Context { get; internal set; }
+ public sealed class ContextMenuExecutedEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// The context of the command.
+ /// </summary>
+ public ContextMenuContext Context { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ContextMenuExecutedEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- public ContextMenuExecutedEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ContextMenuExecutedEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public ContextMenuExecutedEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleReadyEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleReadyEventArgs.cs
index 5825aaf22..345687a72 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleReadyEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleReadyEventArgs.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.ApplicationCommands.EventArgs;
-
-/// <summary>
-/// Represents arguments for a <see cref="ApplicationCommandsExtension.ApplicationCommandsModuleReady"/> event.
-/// </summary>
-public class ApplicationCommandsModuleReadyEventArgs : DiscordEventArgs
+namespace DisCatSharp.ApplicationCommands.EventArgs
{
/// <summary>
- /// Gets a list of all guild ids missing the application commands scope.
+ /// Represents arguments for a <see cref="ApplicationCommandsExtension.ApplicationCommandsModuleReady"/> event.
/// </summary>
- public IReadOnlyList<ulong> GuildsWithoutScope { get; internal set; }
+ public class ApplicationCommandsModuleReadyEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets a list of all guild ids missing the application commands scope.
+ /// </summary>
+ public IReadOnlyList<ulong> GuildsWithoutScope { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ApplicationCommandsModuleReadyEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- internal ApplicationCommandsModuleReadyEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ApplicationCommandsModuleReadyEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ internal ApplicationCommandsModuleReadyEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleStartupFinishedEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleStartupFinishedEventArgs.cs
index 9b6ceb90a..435a5134e 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleStartupFinishedEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/Module/ApplicationCommandsModuleStartupFinishedEventArgs.cs
@@ -1,57 +1,58 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using DisCatSharp.EventArgs;
-namespace DisCatSharp.ApplicationCommands.EventArgs;
-
-/// <summary>
-/// Represents arguments for a <see cref="ApplicationCommandsExtension.ApplicationCommandsModuleStartupFinished"/> event.
-/// </summary>
-public class ApplicationCommandsModuleStartupFinishedEventArgs : DiscordEventArgs
+namespace DisCatSharp.ApplicationCommands.EventArgs
{
/// <summary>
- /// Gets a list of all guild ids missing the application commands scope.
+ /// Represents arguments for a <see cref="ApplicationCommandsExtension.ApplicationCommandsModuleStartupFinished"/> event.
/// </summary>
- public IReadOnlyList<ulong> GuildsWithoutScope { get; internal set; }
+ public class ApplicationCommandsModuleStartupFinishedEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets a list of all guild ids missing the application commands scope.
+ /// </summary>
+ public IReadOnlyList<ulong> GuildsWithoutScope { get; internal set; }
- /// <summary>
- /// Gets all registered global commands.
- /// </summary>
- public IReadOnlyList<DiscordApplicationCommand> RegisteredGlobalCommands { get; internal set; }
+ /// <summary>
+ /// Gets all registered global commands.
+ /// </summary>
+ public IReadOnlyList<DiscordApplicationCommand> RegisteredGlobalCommands { get; internal set; }
- /// <summary>
- /// Gets all registered guild commands mapped by guild id.
- /// </summary>
- public IReadOnlyDictionary<ulong, IReadOnlyList<DiscordApplicationCommand>> RegisteredGuildCommands { get; internal set; }
+ /// <summary>
+ /// Gets all registered guild commands mapped by guild id.
+ /// </summary>
+ public IReadOnlyDictionary<ulong, IReadOnlyList<DiscordApplicationCommand>> RegisteredGuildCommands { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ApplicationCommandsModuleStartupFinishedEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- internal ApplicationCommandsModuleStartupFinishedEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ApplicationCommandsModuleStartupFinishedEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ internal ApplicationCommandsModuleStartupFinishedEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/Module/GlobalApplicationCommandsRegisteredEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/Module/GlobalApplicationCommandsRegisteredEventArgs.cs
index 391051bae..d3bd7e0bc 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/Module/GlobalApplicationCommandsRegisteredEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/Module/GlobalApplicationCommandsRegisteredEventArgs.cs
@@ -1,47 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using DisCatSharp.EventArgs;
-namespace DisCatSharp.ApplicationCommands.EventArgs;
-
-/// <summary>
-/// Represents arguments for a <see cref="ApplicationCommandsExtension.GlobalApplicationCommandsRegistered"/> event.
-/// </summary>
-public class GlobalApplicationCommandsRegisteredEventArgs : DiscordEventArgs
+namespace DisCatSharp.ApplicationCommands.EventArgs
{
/// <summary>
- /// Gets all registered global commands.
+ /// Represents arguments for a <see cref="ApplicationCommandsExtension.GlobalApplicationCommandsRegistered"/> event.
/// </summary>
- public IReadOnlyList<DiscordApplicationCommand> RegisteredCommands { get; internal set; }
+ public class GlobalApplicationCommandsRegisteredEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets all registered global commands.
+ /// </summary>
+ public IReadOnlyList<DiscordApplicationCommand> RegisteredCommands { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GlobalApplicationCommandsRegisteredEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- internal GlobalApplicationCommandsRegisteredEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GlobalApplicationCommandsRegisteredEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ internal GlobalApplicationCommandsRegisteredEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/Module/GuildApplicationCommandsRegisteredEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/Module/GuildApplicationCommandsRegisteredEventArgs.cs
index 7eee4ec19..cd078da81 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/Module/GuildApplicationCommandsRegisteredEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/Module/GuildApplicationCommandsRegisteredEventArgs.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using DisCatSharp.EventArgs;
-namespace DisCatSharp.ApplicationCommands.EventArgs;
-
-/// <summary>
-/// Represents arguments for a <see cref="ApplicationCommandsExtension.GuildApplicationCommandsRegistered"/> event.
-/// </summary>
-public class GuildApplicationCommandsRegisteredEventArgs : DiscordEventArgs
+namespace DisCatSharp.ApplicationCommands.EventArgs
{
/// <summary>
- /// Gets the target guild id.
+ /// Represents arguments for a <see cref="ApplicationCommandsExtension.GuildApplicationCommandsRegistered"/> event.
/// </summary>
- public ulong GuildId { get; internal set; }
+ public class GuildApplicationCommandsRegisteredEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the target guild id.
+ /// </summary>
+ public ulong GuildId { get; internal set; }
- /// <summary>
- /// Gets all registered guild commands.
- /// </summary>
- public IReadOnlyList<DiscordApplicationCommand> RegisteredCommands { get; internal set; }
+ /// <summary>
+ /// Gets all registered guild commands.
+ /// </summary>
+ public IReadOnlyList<DiscordApplicationCommand> RegisteredCommands { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildApplicationCommandsRegisteredEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- internal GuildApplicationCommandsRegisteredEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildApplicationCommandsRegisteredEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ internal GuildApplicationCommandsRegisteredEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs
index cbb544bcc..0be5f0543 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.ApplicationCommands.EventArgs;
-
-/// <summary>
-/// Represents arguments for a <see cref="ApplicationCommandsExtension.SlashCommandErrored"/> event
-/// </summary>
-public class SlashCommandErrorEventArgs : DiscordEventArgs
+namespace DisCatSharp.ApplicationCommands.EventArgs
{
/// <summary>
- /// The context of the command.
+ /// Represents arguments for a <see cref="ApplicationCommandsExtension.SlashCommandErrored"/> event
/// </summary>
- public InteractionContext Context { get; internal set; }
+ public class SlashCommandErrorEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// The context of the command.
+ /// </summary>
+ public InteractionContext Context { get; internal set; }
- /// <summary>
- /// The exception thrown.
- /// </summary>
- public Exception Exception { get; internal set; }
+ /// <summary>
+ /// The exception thrown.
+ /// </summary>
+ public Exception Exception { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="SlashCommandErrorEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- public SlashCommandErrorEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SlashCommandErrorEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public SlashCommandErrorEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs
index 6286a78d5..2ffeb91e1 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.ApplicationCommands.EventArgs;
-
-/// <summary>
-/// Represents the arguments for a <see cref="ApplicationCommandsExtension.SlashCommandExecuted"/> event
-/// </summary>
-public class SlashCommandExecutedEventArgs : DiscordEventArgs
+namespace DisCatSharp.ApplicationCommands.EventArgs
{
/// <summary>
- /// The context of the command.
+ /// Represents the arguments for a <see cref="ApplicationCommandsExtension.SlashCommandExecuted"/> event
/// </summary>
- public InteractionContext Context { get; internal set; }
+ public class SlashCommandExecutedEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// The context of the command.
+ /// </summary>
+ public InteractionContext Context { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="SlashCommandExecutedEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- public SlashCommandExecutedEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SlashCommandExecutedEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public SlashCommandExecutedEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Exceptions/ContextMenu/ContextMenuExecutionChecksFailedException.cs b/DisCatSharp.ApplicationCommands/Exceptions/ContextMenu/ContextMenuExecutionChecksFailedException.cs
index 06e5d2307..3cb9017d6 100644
--- a/DisCatSharp.ApplicationCommands/Exceptions/ContextMenu/ContextMenuExecutionChecksFailedException.cs
+++ b/DisCatSharp.ApplicationCommands/Exceptions/ContextMenu/ContextMenuExecutionChecksFailedException.cs
@@ -1,37 +1,38 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Thrown when a pre-execution check for a context menu command fails.
-/// </summary>
-public sealed class ContextMenuExecutionChecksFailedException : Exception
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// The list of failed checks.
+ /// Thrown when a pre-execution check for a context menu command fails.
/// </summary>
- public IReadOnlyList<ContextMenuCheckBaseAttribute> FailedChecks;
+ public sealed class ContextMenuExecutionChecksFailedException : Exception
+ {
+ /// <summary>
+ /// The list of failed checks.
+ /// </summary>
+ public IReadOnlyList<ContextMenuCheckBaseAttribute> FailedChecks;
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/Exceptions/SlashCommand/SlashExecutionChecksFailedException.cs b/DisCatSharp.ApplicationCommands/Exceptions/SlashCommand/SlashExecutionChecksFailedException.cs
index 45f6f4b06..bb2790554 100644
--- a/DisCatSharp.ApplicationCommands/Exceptions/SlashCommand/SlashExecutionChecksFailedException.cs
+++ b/DisCatSharp.ApplicationCommands/Exceptions/SlashCommand/SlashExecutionChecksFailedException.cs
@@ -1,37 +1,38 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Thrown when a pre-execution check for a slash command fails.
-/// </summary>
-public class SlashExecutionChecksFailedException : Exception
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// The list of failed checks.
+ /// Thrown when a pre-execution check for a slash command fails.
/// </summary>
- public IReadOnlyList<SlashCheckBaseAttribute> FailedChecks;
+ public class SlashExecutionChecksFailedException : Exception
+ {
+ /// <summary>
+ /// The list of failed checks.
+ /// </summary>
+ public IReadOnlyList<SlashCheckBaseAttribute> FailedChecks;
+ }
}
diff --git a/DisCatSharp.ApplicationCommands/ExtensionMethods.cs b/DisCatSharp.ApplicationCommands/ExtensionMethods.cs
index 94e8c41a8..84adaf1b0 100644
--- a/DisCatSharp.ApplicationCommands/ExtensionMethods.cs
+++ b/DisCatSharp.ApplicationCommands/ExtensionMethods.cs
@@ -1,109 +1,110 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Defines various extension methods for application commands.
-/// </summary>
-public static class ExtensionMethods
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Enables application commands on this <see cref="DiscordClient"/>.
+ /// Defines various extension methods for application commands.
/// </summary>
- /// <param name="client">Client to enable application commands for.</param>
- /// <param name="config">Configuration to use.</param>
- /// <returns>Created <see cref="ApplicationCommandsExtension"/>.</returns>
- public static ApplicationCommandsExtension UseApplicationCommands(this DiscordClient client,
- ApplicationCommandsConfiguration config = null)
+ public static class ExtensionMethods
{
- if (client.GetExtension<ApplicationCommandsExtension>() != null)
- throw new InvalidOperationException("Application commands are already enabled for that client.");
+ /// <summary>
+ /// Enables application commands on this <see cref="DiscordClient"/>.
+ /// </summary>
+ /// <param name="client">Client to enable application commands for.</param>
+ /// <param name="config">Configuration to use.</param>
+ /// <returns>Created <see cref="ApplicationCommandsExtension"/>.</returns>
+ public static ApplicationCommandsExtension UseApplicationCommands(this DiscordClient client,
+ ApplicationCommandsConfiguration config = null)
+ {
+ if (client.GetExtension<ApplicationCommandsExtension>() != null)
+ throw new InvalidOperationException("Application commands are already enabled for that client.");
- var scomm = new ApplicationCommandsExtension(config);
- client.AddExtension(scomm);
- return scomm;
- }
+ var scomm = new ApplicationCommandsExtension(config);
+ client.AddExtension(scomm);
+ return scomm;
+ }
- /// <summary>
- /// Gets the application commands module for this client.
- /// </summary>
- /// <param name="client">Client to get application commands for.</param>
- /// <returns>The module, or null if not activated.</returns>
- public static ApplicationCommandsExtension GetApplicationCommands(this DiscordClient client)
- => client.GetExtension<ApplicationCommandsExtension>();
+ /// <summary>
+ /// Gets the application commands module for this client.
+ /// </summary>
+ /// <param name="client">Client to get application commands for.</param>
+ /// <returns>The module, or null if not activated.</returns>
+ public static ApplicationCommandsExtension GetApplicationCommands(this DiscordClient client)
+ => client.GetExtension<ApplicationCommandsExtension>();
- /// <summary>
- /// Enables application commands on this <see cref="DiscordShardedClient"/>.
- /// </summary>
- /// <param name="client">Client to enable application commands on.</param>
- /// <param name="config">Configuration to use.</param>
- /// <returns>A dictionary of created <see cref="ApplicationCommandsExtension"/> with the key being the shard id.</returns>
- public static async Task<IReadOnlyDictionary<int, ApplicationCommandsExtension>> UseApplicationCommandsAsync(this DiscordShardedClient client, ApplicationCommandsConfiguration config = null)
- {
- var modules = new Dictionary<int, ApplicationCommandsExtension>();
- await (Task)client.GetType().GetMethod("InitializeShardsAsync", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(client, null);
- foreach (var shard in client.ShardClients.Values)
+ /// <summary>
+ /// Enables application commands on this <see cref="DiscordShardedClient"/>.
+ /// </summary>
+ /// <param name="client">Client to enable application commands on.</param>
+ /// <param name="config">Configuration to use.</param>
+ /// <returns>A dictionary of created <see cref="ApplicationCommandsExtension"/> with the key being the shard id.</returns>
+ public static async Task<IReadOnlyDictionary<int, ApplicationCommandsExtension>> UseApplicationCommandsAsync(this DiscordShardedClient client, ApplicationCommandsConfiguration config = null)
{
- var scomm = shard.GetApplicationCommands();
- if (scomm == null)
- scomm = shard.UseApplicationCommands(config);
+ var modules = new Dictionary<int, ApplicationCommandsExtension>();
+ await (Task)client.GetType().GetMethod("InitializeShardsAsync", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(client, null);
+ foreach (var shard in client.ShardClients.Values)
+ {
+ var scomm = shard.GetApplicationCommands();
+ if (scomm == null)
+ scomm = shard.UseApplicationCommands(config);
- modules[shard.ShardId] = scomm;
- }
+ modules[shard.ShardId] = scomm;
+ }
- return modules;
- }
+ return modules;
+ }
- /// <summary>
- /// Gets the name from the <see cref="ChoiceNameAttribute"/> for this enum value.
- /// </summary>
- /// <returns>The name.</returns>
- public static string GetName<T>(this T e) where T : IConvertible
- {
- if (e is Enum)
+ /// <summary>
+ /// Gets the name from the <see cref="ChoiceNameAttribute"/> for this enum value.
+ /// </summary>
+ /// <returns>The name.</returns>
+ public static string GetName<T>(this T e) where T : IConvertible
{
- var type = e.GetType();
- var values = Enum.GetValues(type);
-
- foreach (int val in values)
+ if (e is Enum)
{
- if (val == e.ToInt32(CultureInfo.InvariantCulture))
+ var type = e.GetType();
+ var values = Enum.GetValues(type);
+
+ foreach (int val in values)
{
- var memInfo = type.GetMember(type.GetEnumName(val));
+ if (val == e.ToInt32(CultureInfo.InvariantCulture))
+ {
+ var memInfo = type.GetMember(type.GetEnumName(val));
- return memInfo[0]
- .GetCustomAttributes(typeof(ChoiceNameAttribute), false)
- .FirstOrDefault() is ChoiceNameAttribute nameAttribute ? nameAttribute.Name : type.GetEnumName(val);
+ return memInfo[0]
+ .GetCustomAttributes(typeof(ChoiceNameAttribute), false)
+ .FirstOrDefault() is ChoiceNameAttribute nameAttribute ? nameAttribute.Name : type.GetEnumName(val);
+ }
}
}
+ return null;
}
- return null;
}
}
diff --git a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs
index 44522b890..c45d03468 100644
--- a/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs
+++ b/DisCatSharp.ApplicationCommands/Workers/ApplicationCommandWorker.cs
@@ -1,385 +1,386 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Reflection;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a <see cref="CommandWorker"/>.
-/// </summary>
-internal class CommandWorker
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Parses context menu application commands.
+ /// Represents a <see cref="CommandWorker"/>.
/// </summary>
- /// <param name="type">The type.</param>
- /// <param name="methods">List of method infos.</param>
- /// <param name="translator">The optional command translations.</param>
- /// <returns>Too much.</returns>
- internal static Task<
- (
- List<DiscordApplicationCommand> applicationCommands,
- List<KeyValuePair<Type, Type>> commandTypeSources,
- List<ContextMenuCommand> contextMenuCommands,
- bool withLocalization
- )
- > ParseContextMenuCommands(Type type, IEnumerable<MethodInfo> methods, List<CommandTranslator> translator = null)
+ internal class CommandWorker
{
- List<DiscordApplicationCommand> commands = new();
- List<KeyValuePair<Type, Type>> commandTypeSources = new();
- List<ContextMenuCommand> contextMenuCommands = new();
-
-
- foreach (var contextMethod in methods)
+ /// <summary>
+ /// Parses context menu application commands.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="methods">List of method infos.</param>
+ /// <param name="translator">The optional command translations.</param>
+ /// <returns>Too much.</returns>
+ internal static Task<
+ (
+ List<DiscordApplicationCommand> applicationCommands,
+ List<KeyValuePair<Type, Type>> commandTypeSources,
+ List<ContextMenuCommand> contextMenuCommands,
+ bool withLocalization
+ )
+ > ParseContextMenuCommands(Type type, IEnumerable<MethodInfo> methods, List<CommandTranslator> translator = null)
{
- var contextAttribute = contextMethod.GetCustomAttribute<ContextMenuAttribute>();
+ List<DiscordApplicationCommand> commands = new();
+ List<KeyValuePair<Type, Type>> commandTypeSources = new();
+ List<ContextMenuCommand> contextMenuCommands = new();
- DiscordApplicationCommandLocalization nameLocalizations = null;
- var commandTranslation = translator?.Single(c => c.Name == contextAttribute.Name && c.Type == contextAttribute.Type);
- if (commandTranslation != null)
- nameLocalizations = commandTranslation.NameTranslations;
+ foreach (var contextMethod in methods)
+ {
+ var contextAttribute = contextMethod.GetCustomAttribute<ContextMenuAttribute>();
- var command = new DiscordApplicationCommand(contextAttribute.Name, null, null, contextAttribute.Type, nameLocalizations, null, contextAttribute.DefaultMemberPermissions, contextAttribute.DmPermission);
+ DiscordApplicationCommandLocalization nameLocalizations = null;
- var parameters = contextMethod.GetParameters();
- if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.FirstOrDefault()?.ParameterType, typeof(ContextMenuContext)))
- throw new ArgumentException($"The first argument must be a ContextMenuContext!");
- if (parameters.Length > 1)
- throw new ArgumentException($"A context menu cannot have parameters!");
+ var commandTranslation = translator?.Single(c => c.Name == contextAttribute.Name && c.Type == contextAttribute.Type);
+ if (commandTranslation != null)
+ nameLocalizations = commandTranslation.NameTranslations;
- contextMenuCommands.Add(new ContextMenuCommand { Method = contextMethod, Name = contextAttribute.Name });
+ var command = new DiscordApplicationCommand(contextAttribute.Name, null, null, contextAttribute.Type, nameLocalizations, null, contextAttribute.DefaultMemberPermissions, contextAttribute.DmPermission);
- commands.Add(command);
- commandTypeSources.Add(new KeyValuePair<Type, Type>(type, type));
- }
+ var parameters = contextMethod.GetParameters();
+ if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.FirstOrDefault()?.ParameterType, typeof(ContextMenuContext)))
+ throw new ArgumentException($"The first argument must be a ContextMenuContext!");
+ if (parameters.Length > 1)
+ throw new ArgumentException($"A context menu cannot have parameters!");
- return Task.FromResult((commands, commandTypeSources, contextMenuCommands, translator != null));
- }
+ contextMenuCommands.Add(new ContextMenuCommand { Method = contextMethod, Name = contextAttribute.Name });
- /// <summary>
- /// Parses single application commands.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="methods">List of method infos.</param>
- /// <param name="guildId">The optional guild id.</param>
- /// <param name="translator">The optional command translations.</param>
- /// <returns>Too much.</returns>
- internal static async Task<
- (
- List<DiscordApplicationCommand> applicationCommands,
- List<KeyValuePair<Type, Type>> commandTypeSources,
- List<CommandMethod> commandMethods,
- bool withLocalization
- )
- > ParseBasicSlashCommandsAsync(Type type, IEnumerable<MethodInfo> methods, ulong? guildId = null, List<CommandTranslator> translator = null)
- {
- List<DiscordApplicationCommand> commands = new();
- List<KeyValuePair<Type, Type>> commandTypeSources = new();
- List<CommandMethod> commandMethods = new();
+ commands.Add(command);
+ commandTypeSources.Add(new KeyValuePair<Type, Type>(type, type));
+ }
+
+ return Task.FromResult((commands, commandTypeSources, contextMenuCommands, translator != null));
+ }
- foreach (var method in methods)
+ /// <summary>
+ /// Parses single application commands.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="methods">List of method infos.</param>
+ /// <param name="guildId">The optional guild id.</param>
+ /// <param name="translator">The optional command translations.</param>
+ /// <returns>Too much.</returns>
+ internal static async Task<
+ (
+ List<DiscordApplicationCommand> applicationCommands,
+ List<KeyValuePair<Type, Type>> commandTypeSources,
+ List<CommandMethod> commandMethods,
+ bool withLocalization
+ )
+ > ParseBasicSlashCommandsAsync(Type type, IEnumerable<MethodInfo> methods, ulong? guildId = null, List<CommandTranslator> translator = null)
{
- var commandAttribute = method.GetCustomAttribute<SlashCommandAttribute>();
+ List<DiscordApplicationCommand> commands = new();
+ List<KeyValuePair<Type, Type>> commandTypeSources = new();
+ List<CommandMethod> commandMethods = new();
- var parameters = method.GetParameters();
- if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.FirstOrDefault()?.ParameterType, typeof(InteractionContext)))
- throw new ArgumentException($"The first argument must be an InteractionContext!");
- parameters = parameters.Skip(1).ToArray();
- var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters, guildId);
+ foreach (var method in methods)
+ {
+ var commandAttribute = method.GetCustomAttribute<SlashCommandAttribute>();
- commandMethods.Add(new CommandMethod { Method = method, Name = commandAttribute.Name });
+ var parameters = method.GetParameters();
+ if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.FirstOrDefault()?.ParameterType, typeof(InteractionContext)))
+ throw new ArgumentException($"The first argument must be an InteractionContext!");
+ parameters = parameters.Skip(1).ToArray();
+ var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters, guildId);
- DiscordApplicationCommandLocalization nameLocalizations = null;
- DiscordApplicationCommandLocalization descriptionLocalizations = null;
- List<DiscordApplicationCommandOption> localizedOptions = null;
+ commandMethods.Add(new CommandMethod { Method = method, Name = commandAttribute.Name });
- var commandTranslation = translator?.Single(c => c.Name == commandAttribute.Name && c.Type == ApplicationCommandType.ChatInput);
+ DiscordApplicationCommandLocalization nameLocalizations = null;
+ DiscordApplicationCommandLocalization descriptionLocalizations = null;
+ List<DiscordApplicationCommandOption> localizedOptions = null;
- if (commandTranslation != null && commandTranslation.Options != null)
- {
- localizedOptions = new List<DiscordApplicationCommandOption>(options.Count);
- foreach (var option in options)
+ var commandTranslation = translator?.Single(c => c.Name == commandAttribute.Name && c.Type == ApplicationCommandType.ChatInput);
+
+ if (commandTranslation != null && commandTranslation.Options != null)
{
- var choices = option.Choices != null ? new List<DiscordApplicationCommandOptionChoice>(option.Choices.Count) : null;
- if (option.Choices != null)
+ localizedOptions = new List<DiscordApplicationCommandOption>(options.Count);
+ foreach (var option in options)
{
- foreach (var choice in option.Choices)
+ var choices = option.Choices != null ? new List<DiscordApplicationCommandOptionChoice>(option.Choices.Count) : null;
+ if (option.Choices != null)
{
- choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, commandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations));
+ foreach (var choice in option.Choices)
+ {
+ choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, commandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations));
+ }
}
+
+ localizedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required,
+ choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue,
+ commandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, commandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations
+ ));
}
- localizedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required,
- choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue,
- commandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, commandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations
- ));
+ nameLocalizations = commandTranslation.NameTranslations;
+ descriptionLocalizations = commandTranslation.DescriptionTranslations;
}
- nameLocalizations = commandTranslation.NameTranslations;
- descriptionLocalizations = commandTranslation.DescriptionTranslations;
+ var payload = new DiscordApplicationCommand(commandAttribute.Name, commandAttribute.Description, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (options != null && options.Any() ? options : null), ApplicationCommandType.ChatInput, nameLocalizations, descriptionLocalizations, commandAttribute.DefaultMemberPermissions, commandAttribute.DmPermission);
+ commands.Add(payload);
+ commandTypeSources.Add(new KeyValuePair<Type, Type>(type, type));
}
- var payload = new DiscordApplicationCommand(commandAttribute.Name, commandAttribute.Description, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (options != null && options.Any() ? options : null), ApplicationCommandType.ChatInput, nameLocalizations, descriptionLocalizations, commandAttribute.DefaultMemberPermissions, commandAttribute.DmPermission);
- commands.Add(payload);
- commandTypeSources.Add(new KeyValuePair<Type, Type>(type, type));
+ return (commands, commandTypeSources, commandMethods, translator != null);
}
-
- return (commands, commandTypeSources, commandMethods, translator != null);
}
-}
-/// <summary>
-/// Represents a <see cref="NestedCommandWorker"/>.
-/// </summary>
-internal class NestedCommandWorker
-{
/// <summary>
- /// Parses application command groups.
+ /// Represents a <see cref="NestedCommandWorker"/>.
/// </summary>
- /// <param name="type">The type.</param>
- /// <param name="types">List of type infos.</param>
- /// <param name="guildId">The optional guild id.</param>
- /// <param name="translator">The optional group translations.</param>
- /// <returns>Too much.</returns>
- internal static async Task<
- (
- List<DiscordApplicationCommand> applicationCommands,
- List<KeyValuePair<Type, Type>> commandTypeSources,
- List<object> singletonModules,
- List<GroupCommand> groupCommands,
- List<SubGroupCommand> subGroupCommands,
- bool withLocalization
- )
- > ParseSlashGroupsAsync(Type type, List<TypeInfo> types, ulong? guildId = null, List<GroupTranslator> translator = null)
+ internal class NestedCommandWorker
{
- List<DiscordApplicationCommand> commands = new();
- List<KeyValuePair<Type, Type>> commandTypeSources = new();
- List<GroupCommand> groupCommands = new();
- List<SubGroupCommand> subGroupCommands = new();
- List<object> singletonModules = new();
-
- //Handles groups
- foreach (var subclassInfo in types)
+ /// <summary>
+ /// Parses application command groups.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="types">List of type infos.</param>
+ /// <param name="guildId">The optional guild id.</param>
+ /// <param name="translator">The optional group translations.</param>
+ /// <returns>Too much.</returns>
+ internal static async Task<
+ (
+ List<DiscordApplicationCommand> applicationCommands,
+ List<KeyValuePair<Type, Type>> commandTypeSources,
+ List<object> singletonModules,
+ List<GroupCommand> groupCommands,
+ List<SubGroupCommand> subGroupCommands,
+ bool withLocalization
+ )
+ > ParseSlashGroupsAsync(Type type, List<TypeInfo> types, ulong? guildId = null, List<GroupTranslator> translator = null)
{
- //Gets the attribute and methods in the group
- var groupAttribute = subclassInfo.GetCustomAttribute<SlashCommandGroupAttribute>();
- var submethods = subclassInfo.DeclaredMethods.Where(x => x.GetCustomAttribute<SlashCommandAttribute>() != null).ToList();
- var subclasses = subclassInfo.DeclaredNestedTypes.Where(x => x.GetCustomAttribute<SlashCommandGroupAttribute>() != null).ToList();
- if (subclasses.Any() && submethods.Any())
+ List<DiscordApplicationCommand> commands = new();
+ List<KeyValuePair<Type, Type>> commandTypeSources = new();
+ List<GroupCommand> groupCommands = new();
+ List<SubGroupCommand> subGroupCommands = new();
+ List<object> singletonModules = new();
+
+ //Handles groups
+ foreach (var subclassInfo in types)
{
- throw new ArgumentException("Slash command groups cannot have both subcommands and subgroups!");
- }
+ //Gets the attribute and methods in the group
+ var groupAttribute = subclassInfo.GetCustomAttribute<SlashCommandGroupAttribute>();
+ var submethods = subclassInfo.DeclaredMethods.Where(x => x.GetCustomAttribute<SlashCommandAttribute>() != null).ToList();
+ var subclasses = subclassInfo.DeclaredNestedTypes.Where(x => x.GetCustomAttribute<SlashCommandGroupAttribute>() != null).ToList();
+ if (subclasses.Any() && submethods.Any())
+ {
+ throw new ArgumentException("Slash command groups cannot have both subcommands and subgroups!");
+ }
- DiscordApplicationCommandLocalization nameLocalizations = null;
- DiscordApplicationCommandLocalization descriptionLocalizations = null;
+ DiscordApplicationCommandLocalization nameLocalizations = null;
+ DiscordApplicationCommandLocalization descriptionLocalizations = null;
- if (translator != null)
- {
- var commandTranslation = translator.Single(c => c.Name == groupAttribute.Name);
- if (commandTranslation != null)
+ if (translator != null)
{
- nameLocalizations = commandTranslation.NameTranslations;
- descriptionLocalizations = commandTranslation.DescriptionTranslations;
+ var commandTranslation = translator.Single(c => c.Name == groupAttribute.Name);
+ if (commandTranslation != null)
+ {
+ nameLocalizations = commandTranslation.NameTranslations;
+ descriptionLocalizations = commandTranslation.DescriptionTranslations;
+ }
}
- }
- //Initializes the command
- var payload = new DiscordApplicationCommand(groupAttribute.Name, groupAttribute.Description, nameLocalizations: nameLocalizations, descriptionLocalizations: descriptionLocalizations, defaultMemberPermissions: groupAttribute.DefaultMemberPermissions, dmPermission: groupAttribute.DmPermission);
- commandTypeSources.Add(new KeyValuePair<Type, Type>(type, type));
+ //Initializes the command
+ var payload = new DiscordApplicationCommand(groupAttribute.Name, groupAttribute.Description, nameLocalizations: nameLocalizations, descriptionLocalizations: descriptionLocalizations, defaultMemberPermissions: groupAttribute.DefaultMemberPermissions, dmPermission: groupAttribute.DmPermission);
+ commandTypeSources.Add(new KeyValuePair<Type, Type>(type, type));
- var commandMethods = new List<KeyValuePair<string, MethodInfo>>();
- //Handles commands in the group
- foreach (var submethod in submethods)
- {
- var commandAttribute = submethod.GetCustomAttribute<SlashCommandAttribute>();
-
- //Gets the parameters and accounts for InteractionContext
- var parameters = submethod.GetParameters();
- if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.First().ParameterType, typeof(InteractionContext)))
- throw new ArgumentException($"The first argument must be an InteractionContext!");
- parameters = parameters.Skip(1).ToArray();
+ var commandMethods = new List<KeyValuePair<string, MethodInfo>>();
+ //Handles commands in the group
+ foreach (var submethod in submethods)
+ {
+ var commandAttribute = submethod.GetCustomAttribute<SlashCommandAttribute>();
- var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters, guildId);
+ //Gets the parameters and accounts for InteractionContext
+ var parameters = submethod.GetParameters();
+ if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.First().ParameterType, typeof(InteractionContext)))
+ throw new ArgumentException($"The first argument must be an InteractionContext!");
+ parameters = parameters.Skip(1).ToArray();
- DiscordApplicationCommandLocalization subNameLocalizations = null;
- DiscordApplicationCommandLocalization subDescriptionLocalizations = null;
- List<DiscordApplicationCommandOption> localizedOptions = null;
+ var options = await ApplicationCommandsExtension.ParseParametersAsync(parameters, guildId);
- var commandTranslation = translator?.Single(c => c.Name == payload.Name);
+ DiscordApplicationCommandLocalization subNameLocalizations = null;
+ DiscordApplicationCommandLocalization subDescriptionLocalizations = null;
+ List<DiscordApplicationCommandOption> localizedOptions = null;
- if (commandTranslation?.Commands != null)
- {
+ var commandTranslation = translator?.Single(c => c.Name == payload.Name);
- var subCommandTranslation = commandTranslation.Commands.Single(sc => sc.Name == commandAttribute.Name);
- if (subCommandTranslation.Options != null)
+ if (commandTranslation?.Commands != null)
{
- localizedOptions = new List<DiscordApplicationCommandOption>(options.Count);
- foreach (var option in options)
+
+ var subCommandTranslation = commandTranslation.Commands.Single(sc => sc.Name == commandAttribute.Name);
+ if (subCommandTranslation.Options != null)
{
- var choices = option.Choices != null ? new List<DiscordApplicationCommandOptionChoice>(option.Choices.Count) : null;
- if (option.Choices != null)
+ localizedOptions = new List<DiscordApplicationCommandOption>(options.Count);
+ foreach (var option in options)
{
- foreach (var choice in option.Choices)
+ var choices = option.Choices != null ? new List<DiscordApplicationCommandOptionChoice>(option.Choices.Count) : null;
+ if (option.Choices != null)
{
- choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, subCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations));
+ foreach (var choice in option.Choices)
+ {
+ choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, subCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations));
+ }
}
- }
- localizedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required,
- choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue,
- subCommandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, subCommandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations
- ));
+ localizedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required,
+ choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue,
+ subCommandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, subCommandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations
+ ));
+ }
}
- }
- subNameLocalizations = subCommandTranslation.NameTranslations;
- subDescriptionLocalizations = subCommandTranslation.DescriptionTranslations;
- }
+ subNameLocalizations = subCommandTranslation.NameTranslations;
+ subDescriptionLocalizations = subCommandTranslation.DescriptionTranslations;
+ }
- //Creates the subcommand and adds it to the main command
- var subpayload = new DiscordApplicationCommandOption(commandAttribute.Name, commandAttribute.Description, ApplicationCommandOptionType.SubCommand, null, null, localizedOptions ?? options, nameLocalizations: subNameLocalizations, descriptionLocalizations: subDescriptionLocalizations);
- payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options?.Append(subpayload) ?? new[] { subpayload }, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations, defaultMemberPermissions: payload.DefaultMemberPermissions, dmPermission: payload.DmPermission);
- commandTypeSources.Add(new KeyValuePair<Type, Type>(subclassInfo, type));
+ //Creates the subcommand and adds it to the main command
+ var subpayload = new DiscordApplicationCommandOption(commandAttribute.Name, commandAttribute.Description, ApplicationCommandOptionType.SubCommand, null, null, localizedOptions ?? options, nameLocalizations: subNameLocalizations, descriptionLocalizations: subDescriptionLocalizations);
+ payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options?.Append(subpayload) ?? new[] { subpayload }, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations, defaultMemberPermissions: payload.DefaultMemberPermissions, dmPermission: payload.DmPermission);
+ commandTypeSources.Add(new KeyValuePair<Type, Type>(subclassInfo, type));
- //Adds it to the method lists
- commandMethods.Add(new KeyValuePair<string, MethodInfo>(commandAttribute.Name, submethod));
- groupCommands.Add(new GroupCommand { Name = groupAttribute.Name, Methods = commandMethods });
- }
+ //Adds it to the method lists
+ commandMethods.Add(new KeyValuePair<string, MethodInfo>(commandAttribute.Name, submethod));
+ groupCommands.Add(new GroupCommand { Name = groupAttribute.Name, Methods = commandMethods });
+ }
- var command = new SubGroupCommand { Name = groupAttribute.Name };
- //Handles subgroups
- foreach (var subclass in subclasses)
- {
- var subgroupAttribute = subclass.GetCustomAttribute<SlashCommandGroupAttribute>();
- var subsubmethods = subclass.DeclaredMethods.Where(x => x.GetCustomAttribute<SlashCommandAttribute>() != null);
+ var command = new SubGroupCommand { Name = groupAttribute.Name };
+ //Handles subgroups
+ foreach (var subclass in subclasses)
+ {
+ var subgroupAttribute = subclass.GetCustomAttribute<SlashCommandGroupAttribute>();
+ var subsubmethods = subclass.DeclaredMethods.Where(x => x.GetCustomAttribute<SlashCommandAttribute>() != null);
- var options = new List<DiscordApplicationCommandOption>();
+ var options = new List<DiscordApplicationCommandOption>();
- var currentMethods = new List<KeyValuePair<string, MethodInfo>>();
+ var currentMethods = new List<KeyValuePair<string, MethodInfo>>();
- DiscordApplicationCommandLocalization subNameLocalizations = null;
- DiscordApplicationCommandLocalization subDescriptionLocalizations = null;
+ DiscordApplicationCommandLocalization subNameLocalizations = null;
+ DiscordApplicationCommandLocalization subDescriptionLocalizations = null;
- if (translator != null)
- {
- var commandTranslation = translator.Single(c => c.Name == payload.Name);
- if (commandTranslation != null && commandTranslation.SubGroups != null)
+ if (translator != null)
{
+ var commandTranslation = translator.Single(c => c.Name == payload.Name);
+ if (commandTranslation != null && commandTranslation.SubGroups != null)
+ {
- var subCommandTranslation = commandTranslation.SubGroups.Single(sc => sc.Name == subgroupAttribute.Name);
+ var subCommandTranslation = commandTranslation.SubGroups.Single(sc => sc.Name == subgroupAttribute.Name);
- if (subCommandTranslation != null)
- {
- subNameLocalizations = subCommandTranslation.NameTranslations;
- subDescriptionLocalizations = subCommandTranslation.DescriptionTranslations;
+ if (subCommandTranslation != null)
+ {
+ subNameLocalizations = subCommandTranslation.NameTranslations;
+ subDescriptionLocalizations = subCommandTranslation.DescriptionTranslations;
+ }
}
}
- }
- //Similar to the one for regular groups
- foreach (var subsubmethod in subsubmethods)
- {
- var suboptions = new List<DiscordApplicationCommandOption>();
- var commatt = subsubmethod.GetCustomAttribute<SlashCommandAttribute>();
- var parameters = subsubmethod.GetParameters();
- if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.First().ParameterType, typeof(InteractionContext)))
- throw new ArgumentException($"The first argument must be an InteractionContext!");
+ //Similar to the one for regular groups
+ foreach (var subsubmethod in subsubmethods)
+ {
+ var suboptions = new List<DiscordApplicationCommandOption>();
+ var commatt = subsubmethod.GetCustomAttribute<SlashCommandAttribute>();
+ var parameters = subsubmethod.GetParameters();
+ if (parameters.Length == 0 || parameters == null || !ReferenceEquals(parameters.First().ParameterType, typeof(InteractionContext)))
+ throw new ArgumentException($"The first argument must be an InteractionContext!");
- parameters = parameters.Skip(1).ToArray();
- suboptions = suboptions.Concat(await ApplicationCommandsExtension.ParseParametersAsync(parameters, guildId)).ToList();
+ parameters = parameters.Skip(1).ToArray();
+ suboptions = suboptions.Concat(await ApplicationCommandsExtension.ParseParametersAsync(parameters, guildId)).ToList();
- DiscordApplicationCommandLocalization subSubNameLocalizations = null;
- DiscordApplicationCommandLocalization subSubDescriptionLocalizations = null;
- List<DiscordApplicationCommandOption> localizedOptions = null;
+ DiscordApplicationCommandLocalization subSubNameLocalizations = null;
+ DiscordApplicationCommandLocalization subSubDescriptionLocalizations = null;
+ List<DiscordApplicationCommandOption> localizedOptions = null;
- var commandTranslation = translator?.Single(c => c.Name == payload.Name);
+ var commandTranslation = translator?.Single(c => c.Name == payload.Name);
- var subCommandTranslation = commandTranslation?.SubGroups?.Single(sc => sc.Name == commatt.Name);
+ var subCommandTranslation = commandTranslation?.SubGroups?.Single(sc => sc.Name == commatt.Name);
- var subSubCommandTranslation = subCommandTranslation?.Commands.Single(sc => sc.Name == commatt.Name);
+ var subSubCommandTranslation = subCommandTranslation?.Commands.Single(sc => sc.Name == commatt.Name);
- if (subSubCommandTranslation != null && subSubCommandTranslation.Options != null)
- {
- localizedOptions = new List<DiscordApplicationCommandOption>(suboptions.Count);
- foreach (var option in suboptions)
+ if (subSubCommandTranslation != null && subSubCommandTranslation.Options != null)
{
- var choices = option.Choices != null ? new List<DiscordApplicationCommandOptionChoice>(option.Choices.Count) : null;
- if (option.Choices != null)
+ localizedOptions = new List<DiscordApplicationCommandOption>(suboptions.Count);
+ foreach (var option in suboptions)
{
- foreach (var choice in option.Choices)
+ var choices = option.Choices != null ? new List<DiscordApplicationCommandOptionChoice>(option.Choices.Count) : null;
+ if (option.Choices != null)
{
- choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, subSubCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations));
+ foreach (var choice in option.Choices)
+ {
+ choices.Add(new DiscordApplicationCommandOptionChoice(choice.Name, choice.Value, subSubCommandTranslation.Options.Single(o => o.Name == option.Name).Choices.Single(c => c.Name == choice.Name).NameTranslations));
+ }
}
+
+ localizedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required,
+ choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue,
+ subSubCommandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, subSubCommandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations
+ ));
}
- localizedOptions.Add(new DiscordApplicationCommandOption(option.Name, option.Description, option.Type, option.Required,
- choices, option.Options, option.ChannelTypes, option.AutoComplete, option.MinimumValue, option.MaximumValue,
- subSubCommandTranslation.Options.Single(o => o.Name == option.Name).NameTranslations, subSubCommandTranslation.Options.Single(o => o.Name == option.Name).DescriptionTranslations
- ));
+ subSubNameLocalizations = subSubCommandTranslation.NameTranslations;
+ subSubDescriptionLocalizations = subSubCommandTranslation.DescriptionTranslations;
}
- subSubNameLocalizations = subSubCommandTranslation.NameTranslations;
- subSubDescriptionLocalizations = subSubCommandTranslation.DescriptionTranslations;
+ var subsubpayload = new DiscordApplicationCommandOption(commatt.Name, commatt.Description, ApplicationCommandOptionType.SubCommand, null, null, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (suboptions != null && suboptions.Any() ? suboptions : null), nameLocalizations: subSubNameLocalizations, descriptionLocalizations: subSubDescriptionLocalizations);
+ options.Add(subsubpayload);
+ commandMethods.Add(new KeyValuePair<string, MethodInfo>(commatt.Name, subsubmethod));
+ currentMethods.Add(new KeyValuePair<string, MethodInfo>(commatt.Name, subsubmethod));
}
- var subsubpayload = new DiscordApplicationCommandOption(commatt.Name, commatt.Description, ApplicationCommandOptionType.SubCommand, null, null, (localizedOptions != null && localizedOptions.Any() ? localizedOptions : null) ?? (suboptions != null && suboptions.Any() ? suboptions : null), nameLocalizations: subSubNameLocalizations, descriptionLocalizations: subSubDescriptionLocalizations);
- options.Add(subsubpayload);
- commandMethods.Add(new KeyValuePair<string, MethodInfo>(commatt.Name, subsubmethod));
- currentMethods.Add(new KeyValuePair<string, MethodInfo>(commatt.Name, subsubmethod));
- }
+ //Adds the group to the command and method lists
+ var subpayload = new DiscordApplicationCommandOption(subgroupAttribute.Name, subgroupAttribute.Description, ApplicationCommandOptionType.SubCommandGroup, null, null, options, nameLocalizations: subNameLocalizations, descriptionLocalizations: subDescriptionLocalizations);
+ command.SubCommands.Add(new GroupCommand { Name = subgroupAttribute.Name, Methods = currentMethods });
+ payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options?.Append(subpayload) ?? new[] { subpayload }, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations, defaultMemberPermissions: payload.DefaultMemberPermissions, dmPermission: payload.DmPermission);
+ commandTypeSources.Add(new KeyValuePair<Type, Type>(subclass, type));
- //Adds the group to the command and method lists
- var subpayload = new DiscordApplicationCommandOption(subgroupAttribute.Name, subgroupAttribute.Description, ApplicationCommandOptionType.SubCommandGroup, null, null, options, nameLocalizations: subNameLocalizations, descriptionLocalizations: subDescriptionLocalizations);
- command.SubCommands.Add(new GroupCommand { Name = subgroupAttribute.Name, Methods = currentMethods });
- payload = new DiscordApplicationCommand(payload.Name, payload.Description, payload.Options?.Append(subpayload) ?? new[] { subpayload }, nameLocalizations: payload.NameLocalizations, descriptionLocalizations: payload.DescriptionLocalizations, defaultMemberPermissions: payload.DefaultMemberPermissions, dmPermission: payload.DmPermission);
- commandTypeSources.Add(new KeyValuePair<Type, Type>(subclass, type));
+ //Accounts for lifespans for the sub group
+ if (subclass.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>() != null && subclass.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>().Lifespan == ApplicationCommandModuleLifespan.Singleton)
+ {
+ singletonModules.Add(ApplicationCommandsExtension.CreateInstance(subclass, ApplicationCommandsExtension.Configuration?.ServiceProvider));
+ }
+ }
+ if (command.SubCommands.Any()) subGroupCommands.Add(command);
+ commands.Add(payload);
- //Accounts for lifespans for the sub group
- if (subclass.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>() != null && subclass.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>().Lifespan == ApplicationCommandModuleLifespan.Singleton)
+ //Accounts for lifespans
+ if (subclassInfo.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>() != null && subclassInfo.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>().Lifespan == ApplicationCommandModuleLifespan.Singleton)
{
- singletonModules.Add(ApplicationCommandsExtension.CreateInstance(subclass, ApplicationCommandsExtension.Configuration?.ServiceProvider));
+ singletonModules.Add(ApplicationCommandsExtension.CreateInstance(subclassInfo, ApplicationCommandsExtension.Configuration?.ServiceProvider));
}
}
- if (command.SubCommands.Any()) subGroupCommands.Add(command);
- commands.Add(payload);
- //Accounts for lifespans
- if (subclassInfo.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>() != null && subclassInfo.GetCustomAttribute<ApplicationCommandModuleLifespanAttribute>().Lifespan == ApplicationCommandModuleLifespan.Singleton)
- {
- singletonModules.Add(ApplicationCommandsExtension.CreateInstance(subclassInfo, ApplicationCommandsExtension.Configuration?.ServiceProvider));
- }
+ return (commands, commandTypeSources, singletonModules, groupCommands, subGroupCommands, translator != null);
}
-
- return (commands, commandTypeSources, singletonModules, groupCommands, subGroupCommands, translator != null);
}
}
diff --git a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs
index dc764cc86..7a2fe6781 100644
--- a/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs
+++ b/DisCatSharp.ApplicationCommands/Workers/RegistrationWorker.cs
@@ -1,578 +1,579 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Common;
using DisCatSharp.Entities;
using DisCatSharp.Exceptions;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.ApplicationCommands;
-
-/// <summary>
-/// Represents a <see cref="RegistrationWorker"/>.
-/// </summary>
-internal class RegistrationWorker
+namespace DisCatSharp.ApplicationCommands
{
/// <summary>
- /// Registers the global commands.
+ /// Represents a <see cref="RegistrationWorker"/>.
/// </summary>
- /// <param name="commands">The command list.</param>
- /// <returns>A list of registered commands.</returns>
- internal static async Task<List<DiscordApplicationCommand>> RegisterGlobalCommandsAsync(List<DiscordApplicationCommand> commands)
+ internal class RegistrationWorker
{
- var (changedCommands, unchangedCommands) = BuildGlobalOverwriteList(commands);
- var globalCommandsCreateList = BuildGlobalCreateList(commands);
- var globalCommandsDeleteList = BuildGlobalDeleteList(commands);
-
- if (globalCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull())
+ /// <summary>
+ /// Registers the global commands.
+ /// </summary>
+ /// <param name="commands">The command list.</param>
+ /// <returns>A list of registered commands.</returns>
+ internal static async Task<List<DiscordApplicationCommand>> RegisterGlobalCommandsAsync(List<DiscordApplicationCommand> commands)
{
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating, re-using and overwriting application commands.");
+ var (changedCommands, unchangedCommands) = BuildGlobalOverwriteList(commands);
+ var globalCommandsCreateList = BuildGlobalCreateList(commands);
+ var globalCommandsDeleteList = BuildGlobalDeleteList(commands);
- foreach (var cmd in globalCommandsCreateList)
+ if (globalCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull())
{
- var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGlobalApplicationCommandAsync(cmd);
- commands.Add(discordBackendCommand);
- }
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating, re-using and overwriting application commands.");
- foreach (var cmd in changedCommands)
- {
- var command = cmd.Value;
- var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGlobalApplicationCommandAsync(cmd.Key, action =>
+ foreach (var cmd in globalCommandsCreateList)
{
- action.Name = command.Name;
- action.NameLocalizations = command.NameLocalizations;
- action.Description = command.Description;
- action.DescriptionLocalizations = command.DescriptionLocalizations;
- if(command.Options != null && command.Options.Any())
- action.Options = Entities.Optional.Some(command.Options);
- if (command.DefaultMemberPermissions.HasValue)
- action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
- if (command.DmPermission.HasValue)
- action.DmPermission = command.DmPermission.Value;
- });
-
- commands.Add(discordBackendCommand);
- }
+ var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGlobalApplicationCommandAsync(cmd);
+ commands.Add(discordBackendCommand);
+ }
- commands.AddRange(unchangedCommands);
- }
- else if (globalCommandsCreateList.NotEmptyAndNotNull() && (unchangedCommands.NotEmptyAndNotNull() || changedCommands.NotEmptyAndNotNull()))
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating, re-using and overwriting application commands.");
+ foreach (var cmd in changedCommands)
+ {
+ var command = cmd.Value;
+ var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGlobalApplicationCommandAsync(cmd.Key, action =>
+ {
+ action.Name = command.Name;
+ action.NameLocalizations = command.NameLocalizations;
+ action.Description = command.Description;
+ action.DescriptionLocalizations = command.DescriptionLocalizations;
+ if(command.Options != null && command.Options.Any())
+ action.Options = Entities.Optional.Some(command.Options);
+ if (command.DefaultMemberPermissions.HasValue)
+ action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
+ if (command.DmPermission.HasValue)
+ action.DmPermission = command.DmPermission.Value;
+ });
- foreach (var cmd in globalCommandsCreateList)
- {
- var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGlobalApplicationCommandAsync(cmd);
- commands.Add(discordBackendCommand);
+ commands.Add(discordBackendCommand);
+ }
+
+ commands.AddRange(unchangedCommands);
}
+ else if (globalCommandsCreateList.NotEmptyAndNotNull() && (unchangedCommands.NotEmptyAndNotNull() || changedCommands.NotEmptyAndNotNull()))
+ {
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating, re-using and overwriting application commands.");
+
+ foreach (var cmd in globalCommandsCreateList)
+ {
+ var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGlobalApplicationCommandAsync(cmd);
+ commands.Add(discordBackendCommand);
+ }
- if (changedCommands.NotEmptyAndNotNull())
+ if (changedCommands.NotEmptyAndNotNull())
+ {
+ foreach (var cmd in changedCommands)
+ {
+ var command = cmd.Value;
+ var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGlobalApplicationCommandAsync(cmd.Key, action =>
+ {
+ action.Name = command.Name;
+ action.NameLocalizations = command.NameLocalizations;
+ action.Description = command.Description;
+ action.DescriptionLocalizations = command.DescriptionLocalizations;
+ if(command.Options != null && command.Options.Any())
+ action.Options = Entities.Optional.Some(command.Options);
+ if (command.DefaultMemberPermissions.HasValue)
+ action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
+ if (command.DmPermission.HasValue)
+ action.DmPermission = command.DmPermission.Value;
+ });
+
+ commands.Add(discordBackendCommand);
+ }
+ }
+
+ if (unchangedCommands.NotEmptyAndNotNull())
+ commands.AddRange(unchangedCommands);
+ }
+ else if (globalCommandsCreateList.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull())
{
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Editing & re-using application commands.");
+
foreach (var cmd in changedCommands)
{
var command = cmd.Value;
var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGlobalApplicationCommandAsync(cmd.Key, action =>
{
action.Name = command.Name;
action.NameLocalizations = command.NameLocalizations;
action.Description = command.Description;
action.DescriptionLocalizations = command.DescriptionLocalizations;
if(command.Options != null && command.Options.Any())
action.Options = Entities.Optional.Some(command.Options);
if (command.DefaultMemberPermissions.HasValue)
action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
if (command.DmPermission.HasValue)
action.DmPermission = command.DmPermission.Value;
});
commands.Add(discordBackendCommand);
}
- }
- if (unchangedCommands.NotEmptyAndNotNull())
commands.AddRange(unchangedCommands);
- }
- else if (globalCommandsCreateList.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull())
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Editing & re-using application commands.");
-
- foreach (var cmd in changedCommands)
+ }
+ else if (globalCommandsCreateList.EmptyOrNull() && changedCommands.NotEmptyAndNotNull() && unchangedCommands.EmptyOrNull())
{
- var command = cmd.Value;
- var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGlobalApplicationCommandAsync(cmd.Key, action =>
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Overwriting all application commands.");
+
+ List<DiscordApplicationCommand> overwriteList = new();
+ foreach (var overwrite in changedCommands)
{
- action.Name = command.Name;
- action.NameLocalizations = command.NameLocalizations;
- action.Description = command.Description;
- action.DescriptionLocalizations = command.DescriptionLocalizations;
- if(command.Options != null && command.Options.Any())
- action.Options = Entities.Optional.Some(command.Options);
- if (command.DefaultMemberPermissions.HasValue)
- action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
- if (command.DmPermission.HasValue)
- action.DmPermission = command.DmPermission.Value;
- });
-
- commands.Add(discordBackendCommand);
+ var cmd = overwrite.Value;
+ cmd.Id = overwrite.Key;
+ overwriteList.Add(cmd);
+ }
+ var discordBackendCommands = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGlobalApplicationCommandsAsync(overwriteList);
+ commands.AddRange(discordBackendCommands);
}
+ else if (globalCommandsCreateList.NotEmptyAndNotNull() && changedCommands.EmptyOrNull() && unchangedCommands.EmptyOrNull())
+ {
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating all application commands.");
- commands.AddRange(unchangedCommands);
- }
- else if (globalCommandsCreateList.EmptyOrNull() && changedCommands.NotEmptyAndNotNull() && unchangedCommands.EmptyOrNull())
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Overwriting all application commands.");
-
- List<DiscordApplicationCommand> overwriteList = new();
- foreach (var overwrite in changedCommands)
+ var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGlobalApplicationCommandsAsync(globalCommandsCreateList);
+ commands.AddRange(cmds);
+ }
+ else if (globalCommandsCreateList.EmptyOrNull() && changedCommands.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull())
{
- var cmd = overwrite.Value;
- cmd.Id = overwrite.Key;
- overwriteList.Add(cmd);
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Re-using all application commands.");
+
+ commands.AddRange(unchangedCommands);
}
- var discordBackendCommands = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGlobalApplicationCommandsAsync(overwriteList);
- commands.AddRange(discordBackendCommands);
- }
- else if (globalCommandsCreateList.NotEmptyAndNotNull() && changedCommands.EmptyOrNull() && unchangedCommands.EmptyOrNull())
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Creating all application commands.");
- var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGlobalApplicationCommandsAsync(globalCommandsCreateList);
- commands.AddRange(cmds);
- }
- else if (globalCommandsCreateList.EmptyOrNull() && changedCommands.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull())
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Re-using all application commands.");
+ if (globalCommandsDeleteList.NotEmptyAndNotNull())
+ {
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Deleting missing application commands.");
+
+ foreach (var cmdId in globalCommandsDeleteList)
+ {
+ try
+ {
+ await ApplicationCommandsExtension.ClientInternal.DeleteGlobalApplicationCommandAsync(cmdId);
+ }
+ catch (NotFoundException)
+ {
+ ApplicationCommandsExtension.ClientInternal.Logger.LogError($"Could not delete global command {cmdId}. Please clean up manually");
+ }
+ }
+ }
- commands.AddRange(unchangedCommands);
+ return commands.NotEmptyAndNotNull() ? commands : null;
}
- if (globalCommandsDeleteList.NotEmptyAndNotNull())
+ /// <summary>
+ /// Registers the guild commands.
+ /// </summary>
+ /// <param name="guildId">The target guild id.</param>
+ /// <param name="commands">The command list.</param>
+ /// <returns>A list of registered commands.</returns>
+ internal static async Task<List<DiscordApplicationCommand>> RegisterGuilldCommandsAsync(ulong guildId, List<DiscordApplicationCommand> commands)
{
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GLOBAL] Deleting missing application commands.");
+ var (changedCommands, unchangedCommands) = BuildGuildOverwriteList(guildId, commands);
+ var guildCommandsCreateList = BuildGuildCreateList(guildId, commands);
+ var guildCommandsDeleteList = BuildGuildDeleteList(guildId, commands);
- foreach (var cmdId in globalCommandsDeleteList)
+ if (guildCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull())
{
- try
- {
- await ApplicationCommandsExtension.ClientInternal.DeleteGlobalApplicationCommandAsync(cmdId);
- }
- catch (NotFoundException)
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guildId}");
+
+ foreach (var cmd in guildCommandsCreateList)
{
- ApplicationCommandsExtension.ClientInternal.Logger.LogError($"Could not delete global command {cmdId}. Please clean up manually");
+ var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGuildApplicationCommandAsync(guildId, cmd);
+ commands.Add(discordBackendCommand);
}
- }
- }
- return commands.NotEmptyAndNotNull() ? commands : null;
- }
-
- /// <summary>
- /// Registers the guild commands.
- /// </summary>
- /// <param name="guildId">The target guild id.</param>
- /// <param name="commands">The command list.</param>
- /// <returns>A list of registered commands.</returns>
- internal static async Task<List<DiscordApplicationCommand>> RegisterGuilldCommandsAsync(ulong guildId, List<DiscordApplicationCommand> commands)
- {
- var (changedCommands, unchangedCommands) = BuildGuildOverwriteList(guildId, commands);
- var guildCommandsCreateList = BuildGuildCreateList(guildId, commands);
- var guildCommandsDeleteList = BuildGuildDeleteList(guildId, commands);
+ foreach (var cmd in changedCommands)
+ {
+ var command = cmd.Value;
+ var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGuildApplicationCommandAsync(guildId, cmd.Key, action =>
+ {
+ action.Name = command.Name;
+ action.NameLocalizations = command.NameLocalizations;
+ action.Description = command.Description;
+ action.DescriptionLocalizations = command.DescriptionLocalizations;
+ if(command.Options != null && command.Options.Any())
+ action.Options = Entities.Optional.Some(command.Options);
+ if (command.DefaultMemberPermissions.HasValue)
+ action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
+ if (command.DmPermission.HasValue)
+ action.DmPermission = command.DmPermission.Value;
+ });
- if (guildCommandsCreateList.NotEmptyAndNotNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull())
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guildId}");
+ commands.Add(discordBackendCommand);
+ }
- foreach (var cmd in guildCommandsCreateList)
- {
- var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGuildApplicationCommandAsync(guildId, cmd);
- commands.Add(discordBackendCommand);
+ commands.AddRange(unchangedCommands);
}
-
- foreach (var cmd in changedCommands)
+ else if (guildCommandsCreateList.NotEmptyAndNotNull() && (unchangedCommands.NotEmptyAndNotNull() || changedCommands.NotEmptyAndNotNull()))
{
- var command = cmd.Value;
- var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGuildApplicationCommandAsync(guildId, cmd.Key, action =>
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guildId}");
+
+ foreach (var cmd in guildCommandsCreateList)
{
- action.Name = command.Name;
- action.NameLocalizations = command.NameLocalizations;
- action.Description = command.Description;
- action.DescriptionLocalizations = command.DescriptionLocalizations;
- if(command.Options != null && command.Options.Any())
- action.Options = Entities.Optional.Some(command.Options);
- if (command.DefaultMemberPermissions.HasValue)
- action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
- if (command.DmPermission.HasValue)
- action.DmPermission = command.DmPermission.Value;
- });
-
- commands.Add(discordBackendCommand);
- }
+ var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGuildApplicationCommandAsync(guildId, cmd);
+ commands.Add(discordBackendCommand);
+ }
- commands.AddRange(unchangedCommands);
- }
- else if (guildCommandsCreateList.NotEmptyAndNotNull() && (unchangedCommands.NotEmptyAndNotNull() || changedCommands.NotEmptyAndNotNull()))
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating, re-using and overwriting application commands. Guild ID: {guildId}");
+ if (changedCommands.NotEmptyAndNotNull())
+ {
+ foreach (var cmd in changedCommands)
+ {
+ var command = cmd.Value;
+ var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGuildApplicationCommandAsync(guildId, cmd.Key, action =>
+ {
+ action.Name = command.Name;
+ action.NameLocalizations = command.NameLocalizations;
+ action.Description = command.Description;
+ action.DescriptionLocalizations = command.DescriptionLocalizations;
+ if(command.Options != null && command.Options.Any())
+ action.Options = Entities.Optional.Some(command.Options);
+ if (command.DefaultMemberPermissions.HasValue)
+ action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
+ if (command.DmPermission.HasValue)
+ action.DmPermission = command.DmPermission.Value;
+ });
+
+ commands.Add(discordBackendCommand);
+ }
+ }
- foreach (var cmd in guildCommandsCreateList)
- {
- var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.CreateGuildApplicationCommandAsync(guildId, cmd);
- commands.Add(discordBackendCommand);
+ if (unchangedCommands.NotEmptyAndNotNull())
+ commands.AddRange(unchangedCommands);
}
-
- if (changedCommands.NotEmptyAndNotNull())
+ else if (guildCommandsCreateList.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull())
{
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Editing & re-using application commands. Guild ID: {guildId}");
+
foreach (var cmd in changedCommands)
{
var command = cmd.Value;
var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGuildApplicationCommandAsync(guildId, cmd.Key, action =>
{
action.Name = command.Name;
action.NameLocalizations = command.NameLocalizations;
action.Description = command.Description;
action.DescriptionLocalizations = command.DescriptionLocalizations;
if(command.Options != null && command.Options.Any())
action.Options = Entities.Optional.Some(command.Options);
if (command.DefaultMemberPermissions.HasValue)
action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
if (command.DmPermission.HasValue)
action.DmPermission = command.DmPermission.Value;
});
commands.Add(discordBackendCommand);
}
- }
- if (unchangedCommands.NotEmptyAndNotNull())
commands.AddRange(unchangedCommands);
- }
- else if (guildCommandsCreateList.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull() && changedCommands.NotEmptyAndNotNull())
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Editing & re-using application commands. Guild ID: {guildId}");
-
- foreach (var cmd in changedCommands)
+ }
+ else if (guildCommandsCreateList.EmptyOrNull() && changedCommands.NotEmptyAndNotNull() && unchangedCommands.EmptyOrNull())
{
- var command = cmd.Value;
- var discordBackendCommand = await ApplicationCommandsExtension.ClientInternal.EditGuildApplicationCommandAsync(guildId, cmd.Key, action =>
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Overwriting all application commands. Guild ID: {guildId}");
+
+ List<DiscordApplicationCommand> overwriteList = new();
+ foreach (var overwrite in changedCommands)
{
- action.Name = command.Name;
- action.NameLocalizations = command.NameLocalizations;
- action.Description = command.Description;
- action.DescriptionLocalizations = command.DescriptionLocalizations;
- if(command.Options != null && command.Options.Any())
- action.Options = Entities.Optional.Some(command.Options);
- if (command.DefaultMemberPermissions.HasValue)
- action.DefaultMemberPermissions = command.DefaultMemberPermissions.Value;
- if (command.DmPermission.HasValue)
- action.DmPermission = command.DmPermission.Value;
- });
-
- commands.Add(discordBackendCommand);
+ var cmd = overwrite.Value;
+ cmd.Id = overwrite.Key;
+ overwriteList.Add(cmd);
+ }
+ var discordBackendCommands = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGuildApplicationCommandsAsync(guildId, overwriteList);
+ commands.AddRange(discordBackendCommands);
}
-
- commands.AddRange(unchangedCommands);
- }
- else if (guildCommandsCreateList.EmptyOrNull() && changedCommands.NotEmptyAndNotNull() && unchangedCommands.EmptyOrNull())
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Overwriting all application commands. Guild ID: {guildId}");
-
- List<DiscordApplicationCommand> overwriteList = new();
- foreach (var overwrite in changedCommands)
+ else if (guildCommandsCreateList.NotEmptyAndNotNull() && changedCommands.EmptyOrNull() && unchangedCommands.EmptyOrNull())
{
- var cmd = overwrite.Value;
- cmd.Id = overwrite.Key;
- overwriteList.Add(cmd);
- }
- var discordBackendCommands = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGuildApplicationCommandsAsync(guildId, overwriteList);
- commands.AddRange(discordBackendCommands);
- }
- else if (guildCommandsCreateList.NotEmptyAndNotNull() && changedCommands.EmptyOrNull() && unchangedCommands.EmptyOrNull())
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating all application commands. Guild ID: {guildId}");
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Creating all application commands. Guild ID: {guildId}");
- var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGuildApplicationCommandsAsync(guildId, guildCommandsCreateList);
- commands.AddRange(cmds);
- }
- else if (guildCommandsCreateList.EmptyOrNull() && changedCommands.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull())
- {
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Re-using all application commands Guild ID: {guildId}.");
+ var cmds = await ApplicationCommandsExtension.ClientInternal.BulkOverwriteGuildApplicationCommandsAsync(guildId, guildCommandsCreateList);
+ commands.AddRange(cmds);
+ }
+ else if (guildCommandsCreateList.EmptyOrNull() && changedCommands.EmptyOrNull() && unchangedCommands.NotEmptyAndNotNull())
+ {
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Re-using all application commands Guild ID: {guildId}.");
- commands.AddRange(unchangedCommands);
- }
+ commands.AddRange(unchangedCommands);
+ }
- if (guildCommandsDeleteList.NotEmptyAndNotNull())
- {
- foreach (var cmdId in guildCommandsDeleteList)
+ if (guildCommandsDeleteList.NotEmptyAndNotNull())
{
- ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Deleting missing application commands. Guild ID: {guildId}");
- try
- {
- await ApplicationCommandsExtension.ClientInternal.DeleteGuildApplicationCommandAsync(guildId, cmdId);
- }
- catch (NotFoundException)
+ foreach (var cmdId in guildCommandsDeleteList)
{
- ApplicationCommandsExtension.ClientInternal.Logger.LogError($"Could not delete guild command {cmdId} in guild {guildId}. Please clean up manually");
+ ApplicationCommandsExtension.ClientInternal.Logger.Log(ApplicationCommandsExtension.ApplicationCommandsLogLevel, $"[AC GUILD] Deleting missing application commands. Guild ID: {guildId}");
+ try
+ {
+ await ApplicationCommandsExtension.ClientInternal.DeleteGuildApplicationCommandAsync(guildId, cmdId);
+ }
+ catch (NotFoundException)
+ {
+ ApplicationCommandsExtension.ClientInternal.Logger.LogError($"Could not delete guild command {cmdId} in guild {guildId}. Please clean up manually");
+ }
}
}
- }
- return commands.NotEmptyAndNotNull() ? commands : null;
- }
+ return commands.NotEmptyAndNotNull() ? commands : null;
+ }
- /// <summary>
- /// Builds a list of guild command ids to be deleted on discords backend.
- /// </summary>
- /// <param name="guildId">The guild id these commands belong to.</param>
- /// <param name="updateList">The command list.</param>
- /// <returns>A list of command ids.</returns>
- private static List<ulong> BuildGuildDeleteList(ulong guildId, List<DiscordApplicationCommand> updateList)
- {
- List<DiscordApplicationCommand> discord;
+ /// <summary>
+ /// Builds a list of guild command ids to be deleted on discords backend.
+ /// </summary>
+ /// <param name="guildId">The guild id these commands belong to.</param>
+ /// <param name="updateList">The command list.</param>
+ /// <returns>A list of command ids.</returns>
+ private static List<ulong> BuildGuildDeleteList(ulong guildId, List<DiscordApplicationCommand> updateList)
+ {
+ List<DiscordApplicationCommand> discord;
- if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any()
- || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord)
- )
- return null;
+ if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any()
+ || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord)
+ )
+ return null;
- List<ulong> invalidCommandIds = new();
+ List<ulong> invalidCommandIds = new();
- if (discord == null)
- return null;
+ if (discord == null)
+ return null;
- if (updateList == null)
- {
- foreach (var cmd in discord)
+ if (updateList == null)
{
- invalidCommandIds.Add(cmd.Id);
+ foreach (var cmd in discord)
+ {
+ invalidCommandIds.Add(cmd.Id);
+ }
}
- }
- else
- {
- foreach (var cmd in discord)
+ else
{
- if (!updateList.Any(ul => ul.Name == cmd.Name))
- invalidCommandIds.Add(cmd.Id);
+ foreach (var cmd in discord)
+ {
+ if (!updateList.Any(ul => ul.Name == cmd.Name))
+ invalidCommandIds.Add(cmd.Id);
+ }
}
- }
- return invalidCommandIds;
- }
+ return invalidCommandIds;
+ }
- /// <summary>
- /// Builds a list of guild commands to be created on discords backend.
- /// </summary>
- /// <param name="guildId">The guild id these commands belong to.</param>
- /// <param name="updateList">The command list.</param>
- /// <returns></returns>
- private static List<DiscordApplicationCommand> BuildGuildCreateList(ulong guildId, List<DiscordApplicationCommand> updateList)
- {
- List<DiscordApplicationCommand> discord;
+ /// <summary>
+ /// Builds a list of guild commands to be created on discords backend.
+ /// </summary>
+ /// <param name="guildId">The guild id these commands belong to.</param>
+ /// <param name="updateList">The command list.</param>
+ /// <returns></returns>
+ private static List<DiscordApplicationCommand> BuildGuildCreateList(ulong guildId, List<DiscordApplicationCommand> updateList)
+ {
+ List<DiscordApplicationCommand> discord;
- if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any()
- || updateList == null || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord)
- )
- return updateList;
+ if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any()
+ || updateList == null || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord)
+ )
+ return updateList;
- List<DiscordApplicationCommand> newCommands = new();
+ List<DiscordApplicationCommand> newCommands = new();
- if (discord == null)
- return updateList;
+ if (discord == null)
+ return updateList;
- foreach (var cmd in updateList)
- {
- if (discord.All(d => d.Name != cmd.Name))
+ foreach (var cmd in updateList)
{
- newCommands.Add(cmd);
+ if (discord.All(d => d.Name != cmd.Name))
+ {
+ newCommands.Add(cmd);
+ }
}
- }
- return newCommands;
- }
+ return newCommands;
+ }
- /// <summary>
- /// Builds a list of guild commands to be overwritten on discords backend.
- /// </summary>
- /// <param name="guildId">The guild id these commands belong to.</param>
- /// <param name="updateList">The command list.</param>
- /// <returns>A dictionary of command id and command.</returns>
- private static (
- Dictionary<ulong, DiscordApplicationCommand> changedCommands,
- List<DiscordApplicationCommand> unchangedCommands
- ) BuildGuildOverwriteList(ulong guildId, List<DiscordApplicationCommand> updateList)
- {
- List<DiscordApplicationCommand> discord;
+ /// <summary>
+ /// Builds a list of guild commands to be overwritten on discords backend.
+ /// </summary>
+ /// <param name="guildId">The guild id these commands belong to.</param>
+ /// <param name="updateList">The command list.</param>
+ /// <returns>A dictionary of command id and command.</returns>
+ private static (
+ Dictionary<ulong, DiscordApplicationCommand> changedCommands,
+ List<DiscordApplicationCommand> unchangedCommands
+ ) BuildGuildOverwriteList(ulong guildId, List<DiscordApplicationCommand> updateList)
+ {
+ List<DiscordApplicationCommand> discord;
- if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any()
- || ApplicationCommandsExtension.GuildDiscordCommands.All(l => l.Key != guildId) || updateList == null
- || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord)
- )
- return (null, null);
+ if (ApplicationCommandsExtension.GuildDiscordCommands == null || !ApplicationCommandsExtension.GuildDiscordCommands.Any()
+ || ApplicationCommandsExtension.GuildDiscordCommands.All(l => l.Key != guildId) || updateList == null
+ || !ApplicationCommandsExtension.GuildDiscordCommands.GetFirstValueByKey(guildId, out discord)
+ )
+ return (null, null);
- Dictionary<ulong, DiscordApplicationCommand> updateCommands = new();
- List<DiscordApplicationCommand> unchangedCommands = new();
+ Dictionary<ulong, DiscordApplicationCommand> updateCommands = new();
+ List<DiscordApplicationCommand> unchangedCommands = new();
- if (discord == null)
- return (null, null);
+ if (discord == null)
+ return (null, null);
- foreach (var cmd in updateList)
- {
- if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command))
+ foreach (var cmd in updateList)
{
- if (command.IsEqualTo(cmd))
- {
- if (ApplicationCommandsExtension.DebugEnabled)
- ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} unchanged");
- cmd.Id = command.Id;
- cmd.ApplicationId = command.ApplicationId;
- cmd.Version = command.Version;
- unchangedCommands.Add(cmd);
- }
- else
+ if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command))
{
- if (ApplicationCommandsExtension.DebugEnabled)
- ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} changed");
- updateCommands.Add(command.Id, cmd);
+ if (command.IsEqualTo(cmd))
+ {
+ if (ApplicationCommandsExtension.DebugEnabled)
+ ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} unchanged");
+ cmd.Id = command.Id;
+ cmd.ApplicationId = command.ApplicationId;
+ cmd.Version = command.Version;
+ unchangedCommands.Add(cmd);
+ }
+ else
+ {
+ if (ApplicationCommandsExtension.DebugEnabled)
+ ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} changed");
+ updateCommands.Add(command.Id, cmd);
+ }
}
}
- }
- return (updateCommands, unchangedCommands);
- }
+ return (updateCommands, unchangedCommands);
+ }
- /// <summary>
- /// Builds a list of global command ids to be deleted on discords backend.
- /// </summary>
- /// <param name="updateList">The command list.</param>
- /// <returns>A list of command ids.</returns>
- private static List<ulong> BuildGlobalDeleteList(List<DiscordApplicationCommand> updateList = null)
- {
- if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any()
- || ApplicationCommandsExtension.GlobalDiscordCommands == null
- )
- return null;
+ /// <summary>
+ /// Builds a list of global command ids to be deleted on discords backend.
+ /// </summary>
+ /// <param name="updateList">The command list.</param>
+ /// <returns>A list of command ids.</returns>
+ private static List<ulong> BuildGlobalDeleteList(List<DiscordApplicationCommand> updateList = null)
+ {
+ if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any()
+ || ApplicationCommandsExtension.GlobalDiscordCommands == null
+ )
+ return null;
- var discord = ApplicationCommandsExtension.GlobalDiscordCommands;
+ var discord = ApplicationCommandsExtension.GlobalDiscordCommands;
- List<ulong> invalidCommandIds = new();
+ List<ulong> invalidCommandIds = new();
- if (discord == null)
- return null;
+ if (discord == null)
+ return null;
- if (updateList == null)
- {
- foreach (var cmd in discord)
+ if (updateList == null)
{
- invalidCommandIds.Add(cmd.Id);
+ foreach (var cmd in discord)
+ {
+ invalidCommandIds.Add(cmd.Id);
+ }
}
- }
- else
- {
- foreach (var cmd in discord)
+ else
{
- if (updateList.All(ul => ul.Name != cmd.Name))
- invalidCommandIds.Add(cmd.Id);
+ foreach (var cmd in discord)
+ {
+ if (updateList.All(ul => ul.Name != cmd.Name))
+ invalidCommandIds.Add(cmd.Id);
+ }
}
- }
- return invalidCommandIds;
- }
+ return invalidCommandIds;
+ }
- /// <summary>
- /// Builds a list of global commands to be created on discords backend.
- /// </summary>
- /// <param name="updateList">The command list.</param>
- /// <returns>A list of commands.</returns>
- private static List<DiscordApplicationCommand> BuildGlobalCreateList(List<DiscordApplicationCommand> updateList)
- {
- if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() || updateList == null)
- return updateList;
+ /// <summary>
+ /// Builds a list of global commands to be created on discords backend.
+ /// </summary>
+ /// <param name="updateList">The command list.</param>
+ /// <returns>A list of commands.</returns>
+ private static List<DiscordApplicationCommand> BuildGlobalCreateList(List<DiscordApplicationCommand> updateList)
+ {
+ if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any() || updateList == null)
+ return updateList;
- var discord = ApplicationCommandsExtension.GlobalDiscordCommands;
+ var discord = ApplicationCommandsExtension.GlobalDiscordCommands;
- List<DiscordApplicationCommand> newCommands = new();
+ List<DiscordApplicationCommand> newCommands = new();
- if (discord == null)
- return updateList;
+ if (discord == null)
+ return updateList;
- foreach (var cmd in updateList)
- {
- if (discord.All(d => d.Name != cmd.Name))
+ foreach (var cmd in updateList)
{
- newCommands.Add(cmd);
+ if (discord.All(d => d.Name != cmd.Name))
+ {
+ newCommands.Add(cmd);
+ }
}
- }
- return newCommands;
- }
+ return newCommands;
+ }
- /// <summary>
- /// Builds a list of global commands to be overwritten on discords backend.
- /// </summary>
- /// <param name="updateList">The command list.</param>
- /// <returns>A dictionary of command ids and commands.</returns>
- private static (
- Dictionary<ulong, DiscordApplicationCommand> changedCommands,
- List<DiscordApplicationCommand> unchangedCommands
- ) BuildGlobalOverwriteList(List<DiscordApplicationCommand> updateList)
- {
- if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any()
- || updateList == null || ApplicationCommandsExtension.GlobalDiscordCommands == null
- )
- return (null, null);
+ /// <summary>
+ /// Builds a list of global commands to be overwritten on discords backend.
+ /// </summary>
+ /// <param name="updateList">The command list.</param>
+ /// <returns>A dictionary of command ids and commands.</returns>
+ private static (
+ Dictionary<ulong, DiscordApplicationCommand> changedCommands,
+ List<DiscordApplicationCommand> unchangedCommands
+ ) BuildGlobalOverwriteList(List<DiscordApplicationCommand> updateList)
+ {
+ if (ApplicationCommandsExtension.GlobalDiscordCommands == null || !ApplicationCommandsExtension.GlobalDiscordCommands.Any()
+ || updateList == null || ApplicationCommandsExtension.GlobalDiscordCommands == null
+ )
+ return (null, null);
- var discord = ApplicationCommandsExtension.GlobalDiscordCommands;
+ var discord = ApplicationCommandsExtension.GlobalDiscordCommands;
- if (discord == null)
- return (null, null);
+ if (discord == null)
+ return (null, null);
- Dictionary<ulong, DiscordApplicationCommand> updateCommands = new();
- List<DiscordApplicationCommand> unchangedCommands = new();
- foreach (var cmd in updateList)
- {
- if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command))
+ Dictionary<ulong, DiscordApplicationCommand> updateCommands = new();
+ List<DiscordApplicationCommand> unchangedCommands = new();
+ foreach (var cmd in updateList)
{
- if (command.IsEqualTo(cmd))
+ if (discord.GetFirstValueWhere(d => d.Name == cmd.Name, out var command))
{
- if (ApplicationCommandsExtension.DebugEnabled)
- ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} unchanged");
- cmd.Id = command.Id;
- cmd.ApplicationId = command.ApplicationId;
- cmd.Version = command.Version;
- unchangedCommands.Add(cmd);
- }
- else
- {
- if (ApplicationCommandsExtension.DebugEnabled)
- ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} changed");
- updateCommands.Add(command.Id, cmd);
+ if (command.IsEqualTo(cmd))
+ {
+ if (ApplicationCommandsExtension.DebugEnabled)
+ ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} unchanged");
+ cmd.Id = command.Id;
+ cmd.ApplicationId = command.ApplicationId;
+ cmd.Version = command.Version;
+ unchangedCommands.Add(cmd);
+ }
+ else
+ {
+ if (ApplicationCommandsExtension.DebugEnabled)
+ ApplicationCommandsExtension.ClientInternal.Logger.LogDebug($"[AC] Command {cmd.Name} changed");
+ updateCommands.Add(command.Id, cmd);
+ }
}
}
- }
- return (updateCommands, unchangedCommands);
+ return (updateCommands, unchangedCommands);
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/AliasesAttribute.cs b/DisCatSharp.CommandsNext/Attributes/AliasesAttribute.cs
index 613127b2c..f4c4b6399 100644
--- a/DisCatSharp.CommandsNext/Attributes/AliasesAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/AliasesAttribute.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Adds aliases to this command or group.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
-public sealed class AliasesAttribute : Attribute
+namespace DisCatSharp.CommandsNext.Attributes
{
- /// <summary>
- /// Gets this group's aliases.
- /// </summary>
- public IReadOnlyList<string> Aliases { get; }
-
/// <summary>
/// Adds aliases to this command or group.
/// </summary>
- /// <param name="aliases">Aliases to add to this command or group.</param>
- public AliasesAttribute(params string[] aliases)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
+ public sealed class AliasesAttribute : Attribute
{
- if (aliases.Any(xa => xa == null || xa.Any(xc => char.IsWhiteSpace(xc))))
- throw new ArgumentException("Aliases cannot contain whitespace characters or null strings.", nameof(aliases));
+ /// <summary>
+ /// Gets this group's aliases.
+ /// </summary>
+ public IReadOnlyList<string> Aliases { get; }
+
+ /// <summary>
+ /// Adds aliases to this command or group.
+ /// </summary>
+ /// <param name="aliases">Aliases to add to this command or group.</param>
+ public AliasesAttribute(params string[] aliases)
+ {
+ if (aliases.Any(xa => xa == null || xa.Any(xc => char.IsWhiteSpace(xc))))
+ throw new ArgumentException("Aliases cannot contain whitespace characters or null strings.", nameof(aliases));
- this.Aliases = new ReadOnlyCollection<string>(aliases);
+ this.Aliases = new ReadOnlyCollection<string>(aliases);
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/CheckBaseAttribute.cs b/DisCatSharp.CommandsNext/Attributes/CheckBaseAttribute.cs
index b3b040a39..b58306637 100644
--- a/DisCatSharp.CommandsNext/Attributes/CheckBaseAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/CheckBaseAttribute.cs
@@ -1,41 +1,42 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Represents a base for all command pre-execution check attributes.
-/// </summary>
-[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
-public abstract class CheckBaseAttribute : Attribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Asynchronously checks whether this command can be executed within given context.
+ /// Represents a base for all command pre-execution check attributes.
/// </summary>
- /// <param name="ctx">Context to check execution ability for.</param>
- /// <param name="help">Whether this check is being executed from help or not. This can be used to probe whether command can be run without setting off certain fail conditions (such as cooldowns).</param>
- /// <returns>Whether the command can be executed in given context.</returns>
- public abstract Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help);
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
+ public abstract class CheckBaseAttribute : Attribute
+ {
+ /// <summary>
+ /// Asynchronously checks whether this command can be executed within given context.
+ /// </summary>
+ /// <param name="ctx">Context to check execution ability for.</param>
+ /// <param name="help">Whether this check is being executed from help or not. This can be used to probe whether command can be run without setting off certain fail conditions (such as cooldowns).</param>
+ /// <returns>Whether the command can be executed in given context.</returns>
+ public abstract Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/CommandAttribute.cs b/DisCatSharp.CommandsNext/Attributes/CommandAttribute.cs
index 02758619f..e1d7f4175 100644
--- a/DisCatSharp.CommandsNext/Attributes/CommandAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/CommandAttribute.cs
@@ -1,74 +1,75 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Marks this method as a command.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method)]
-public sealed class CommandAttribute : Attribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Gets the name of this command.
- /// </summary>
- public string Name { get; }
-
- /// <summary>
- /// Marks this method as a command, using the method's name as command name.
+ /// Marks this method as a command.
/// </summary>
- public CommandAttribute()
+ [AttributeUsage(AttributeTargets.Method)]
+ public sealed class CommandAttribute : Attribute
{
- this.Name = null;
- }
+ /// <summary>
+ /// Gets the name of this command.
+ /// </summary>
+ public string Name { get; }
- /// <summary>
- /// Marks this method as a command with specified name.
- /// </summary>
- /// <param name="name">Name of this command.</param>
- public CommandAttribute(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- throw new ArgumentNullException(nameof(name), "Command names cannot be null, empty, or all-whitespace.");
+ /// <summary>
+ /// Marks this method as a command, using the method's name as command name.
+ /// </summary>
+ public CommandAttribute()
+ {
+ this.Name = null;
+ }
+
+ /// <summary>
+ /// Marks this method as a command with specified name.
+ /// </summary>
+ /// <param name="name">Name of this command.</param>
+ public CommandAttribute(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentNullException(nameof(name), "Command names cannot be null, empty, or all-whitespace.");
- if (name.Any(xc => char.IsWhiteSpace(xc)))
- throw new ArgumentException("Command names cannot contain whitespace characters.", nameof(name));
+ if (name.Any(xc => char.IsWhiteSpace(xc)))
+ throw new ArgumentException("Command names cannot contain whitespace characters.", nameof(name));
- this.Name = name;
+ this.Name = name;
+ }
}
-}
-/// <summary>
-/// Marks this method as a group command.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method)]
-public sealed class GroupCommandAttribute : Attribute
-{
/// <summary>
/// Marks this method as a group command.
/// </summary>
- public GroupCommandAttribute()
- { }
+ [AttributeUsage(AttributeTargets.Method)]
+ public sealed class GroupCommandAttribute : Attribute
+ {
+ /// <summary>
+ /// Marks this method as a group command.
+ /// </summary>
+ public GroupCommandAttribute()
+ { }
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/CooldownAttribute.cs b/DisCatSharp.CommandsNext/Attributes/CooldownAttribute.cs
index 5c8367bce..6319210c8 100644
--- a/DisCatSharp.CommandsNext/Attributes/CooldownAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/CooldownAttribute.cs
@@ -1,332 +1,333 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines a cooldown for this command. This allows you to define how many times can users execute a specific command
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
-public sealed class CooldownAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Gets the maximum number of uses before this command triggers a cooldown for its bucket.
- /// </summary>
- public int MaxUses { get; }
-
- /// <summary>
- /// Gets the time after which the cooldown is reset.
- /// </summary>
- public TimeSpan Reset { get; }
-
- /// <summary>
- /// Gets the type of the cooldown bucket. This determines how cooldowns are applied.
- /// </summary>
- public CooldownBucketType BucketType { get; }
-
- /// <summary>
- /// Gets the cooldown buckets for this command.
- /// </summary>
- private readonly ConcurrentDictionary<string, CommandCooldownBucket> _buckets;
-
- /// <summary>
- /// Defines a cooldown for this command. This means that users will be able to use the command a specific number of times before they have to wait to use it again.
- /// </summary>
- /// <param name="maxUses">Number of times the command can be used before triggering a cooldown.</param>
- /// <param name="resetAfter">Number of seconds after which the cooldown is reset.</param>
- /// <param name="bucketType">Type of cooldown bucket. This allows controlling whether the bucket will be cooled down per user, guild, channel, or globally.</param>
- public CooldownAttribute(int maxUses, double resetAfter, CooldownBucketType bucketType)
- {
- this.MaxUses = maxUses;
- this.Reset = TimeSpan.FromSeconds(resetAfter);
- this.BucketType = bucketType;
- this._buckets = new ConcurrentDictionary<string, CommandCooldownBucket>();
- }
-
- /// <summary>
- /// Gets a cooldown bucket for given command context.
- /// </summary>
- /// <param name="ctx">Command context to get cooldown bucket for.</param>
- /// <returns>Requested cooldown bucket, or null if one wasn't present.</returns>
- public CommandCooldownBucket GetBucket(CommandContext ctx)
- {
- var bid = this.GetBucketId(ctx, out _, out _, out _);
- this._buckets.TryGetValue(bid, out var bucket);
- return bucket;
- }
-
- /// <summary>
- /// Calculates the cooldown remaining for given command context.
- /// </summary>
- /// <param name="ctx">Context for which to calculate the cooldown.</param>
- /// <returns>Remaining cooldown, or zero if no cooldown is active.</returns>
- public TimeSpan GetRemainingCooldown(CommandContext ctx)
- {
- var bucket = this.GetBucket(ctx);
- return bucket == null ? TimeSpan.Zero : bucket.RemainingUses > 0 ? TimeSpan.Zero : bucket.ResetsAt - DateTimeOffset.UtcNow;
- }
-
- /// <summary>
- /// Calculates bucket ID for given command context.
+ /// Defines a cooldown for this command. This allows you to define how many times can users execute a specific command
/// </summary>
- /// <param name="ctx">Context for which to calculate bucket ID for.</param>
- /// <param name="userId">ID of the user with which this bucket is associated.</param>
- /// <param name="channelId">ID of the channel with which this bucket is associated.</param>
- /// <param name="guildId">ID of the guild with which this bucket is associated.</param>
- /// <returns>Calculated bucket ID.</returns>
- private string GetBucketId(CommandContext ctx, out ulong userId, out ulong channelId, out ulong guildId)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
+ public sealed class CooldownAttribute : CheckBaseAttribute
{
- userId = 0ul;
- if ((this.BucketType & CooldownBucketType.User) != 0)
- userId = ctx.User.Id;
-
- channelId = 0ul;
- if ((this.BucketType & CooldownBucketType.Channel) != 0)
- channelId = ctx.Channel.Id;
- if ((this.BucketType & CooldownBucketType.Guild) != 0 && ctx.Guild == null)
- channelId = ctx.Channel.Id;
-
- guildId = 0ul;
- if (ctx.Guild != null && (this.BucketType & CooldownBucketType.Guild) != 0)
- guildId = ctx.Guild.Id;
-
- var bid = CommandCooldownBucket.MakeId(userId, channelId, guildId);
- return bid;
- }
-
- /// <summary>
- /// Executes a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- {
- if (help)
- return true;
-
- var bid = this.GetBucketId(ctx, out var usr, out var chn, out var gld);
- if (!this._buckets.TryGetValue(bid, out var bucket))
+ /// <summary>
+ /// Gets the maximum number of uses before this command triggers a cooldown for its bucket.
+ /// </summary>
+ public int MaxUses { get; }
+
+ /// <summary>
+ /// Gets the time after which the cooldown is reset.
+ /// </summary>
+ public TimeSpan Reset { get; }
+
+ /// <summary>
+ /// Gets the type of the cooldown bucket. This determines how cooldowns are applied.
+ /// </summary>
+ public CooldownBucketType BucketType { get; }
+
+ /// <summary>
+ /// Gets the cooldown buckets for this command.
+ /// </summary>
+ private readonly ConcurrentDictionary<string, CommandCooldownBucket> _buckets;
+
+ /// <summary>
+ /// Defines a cooldown for this command. This means that users will be able to use the command a specific number of times before they have to wait to use it again.
+ /// </summary>
+ /// <param name="maxUses">Number of times the command can be used before triggering a cooldown.</param>
+ /// <param name="resetAfter">Number of seconds after which the cooldown is reset.</param>
+ /// <param name="bucketType">Type of cooldown bucket. This allows controlling whether the bucket will be cooled down per user, guild, channel, or globally.</param>
+ public CooldownAttribute(int maxUses, double resetAfter, CooldownBucketType bucketType)
{
- bucket = new CommandCooldownBucket(this.MaxUses, this.Reset, usr, chn, gld);
- this._buckets.AddOrUpdate(bid, bucket, (k, v) => bucket);
+ this.MaxUses = maxUses;
+ this.Reset = TimeSpan.FromSeconds(resetAfter);
+ this.BucketType = bucketType;
+ this._buckets = new ConcurrentDictionary<string, CommandCooldownBucket>();
}
- return await bucket.DecrementUseAsync().ConfigureAwait(false);
- }
-}
-
-/// <summary>
-/// Defines how are command cooldowns applied.
-/// </summary>
-public enum CooldownBucketType : int
-{
- /// <summary>
- /// Denotes that the command will have its cooldown applied per-user.
- /// </summary>
- User = 1,
-
- /// <summary>
- /// Denotes that the command will have its cooldown applied per-channel.
- /// </summary>
- Channel = 2,
-
- /// <summary>
- /// Denotes that the command will have its cooldown applied per-guild. In DMs, this applies the cooldown per-channel.
- /// </summary>
- Guild = 4,
-
- /// <summary>
- /// Denotes that the command will have its cooldown applied globally.
- /// </summary>
- Global = 0
-}
-
-/// <summary>
-/// Represents a cooldown bucket for commands.
-/// </summary>
-public sealed class CommandCooldownBucket : IEquatable<CommandCooldownBucket>
-{
- /// <summary>
- /// Gets the ID of the user with whom this cooldown is associated.
- /// </summary>
- public ulong UserId { get; }
-
- /// <summary>
- /// Gets the ID of the channel with which this cooldown is associated.
- /// </summary>
- public ulong ChannelId { get; }
-
- /// <summary>
- /// Gets the ID of the guild with which this cooldown is associated.
- /// </summary>
- public ulong GuildId { get; }
-
- /// <summary>
- /// Gets the ID of the bucket. This is used to distinguish between cooldown buckets.
- /// </summary>
- public string BucketId { get; }
-
- /// <summary>
- /// Gets the remaining number of uses before the cooldown is triggered.
- /// </summary>
- public int RemainingUses
- => Volatile.Read(ref this._remainingUses);
+ /// <summary>
+ /// Gets a cooldown bucket for given command context.
+ /// </summary>
+ /// <param name="ctx">Command context to get cooldown bucket for.</param>
+ /// <returns>Requested cooldown bucket, or null if one wasn't present.</returns>
+ public CommandCooldownBucket GetBucket(CommandContext ctx)
+ {
+ var bid = this.GetBucketId(ctx, out _, out _, out _);
+ this._buckets.TryGetValue(bid, out var bucket);
+ return bucket;
+ }
- private int _remainingUses;
+ /// <summary>
+ /// Calculates the cooldown remaining for given command context.
+ /// </summary>
+ /// <param name="ctx">Context for which to calculate the cooldown.</param>
+ /// <returns>Remaining cooldown, or zero if no cooldown is active.</returns>
+ public TimeSpan GetRemainingCooldown(CommandContext ctx)
+ {
+ var bucket = this.GetBucket(ctx);
+ return bucket == null ? TimeSpan.Zero : bucket.RemainingUses > 0 ? TimeSpan.Zero : bucket.ResetsAt - DateTimeOffset.UtcNow;
+ }
- /// <summary>
- /// Gets the maximum number of times this command can be used in given timespan.
- /// </summary>
- public int MaxUses { get; }
+ /// <summary>
+ /// Calculates bucket ID for given command context.
+ /// </summary>
+ /// <param name="ctx">Context for which to calculate bucket ID for.</param>
+ /// <param name="userId">ID of the user with which this bucket is associated.</param>
+ /// <param name="channelId">ID of the channel with which this bucket is associated.</param>
+ /// <param name="guildId">ID of the guild with which this bucket is associated.</param>
+ /// <returns>Calculated bucket ID.</returns>
+ private string GetBucketId(CommandContext ctx, out ulong userId, out ulong channelId, out ulong guildId)
+ {
+ userId = 0ul;
+ if ((this.BucketType & CooldownBucketType.User) != 0)
+ userId = ctx.User.Id;
+
+ channelId = 0ul;
+ if ((this.BucketType & CooldownBucketType.Channel) != 0)
+ channelId = ctx.Channel.Id;
+ if ((this.BucketType & CooldownBucketType.Guild) != 0 && ctx.Guild == null)
+ channelId = ctx.Channel.Id;
+
+ guildId = 0ul;
+ if (ctx.Guild != null && (this.BucketType & CooldownBucketType.Guild) != 0)
+ guildId = ctx.Guild.Id;
+
+ var bid = CommandCooldownBucket.MakeId(userId, channelId, guildId);
+ return bid;
+ }
- /// <summary>
- /// Gets the date and time at which the cooldown resets.
- /// </summary>
- public DateTimeOffset ResetsAt { get; internal set; }
+ /// <summary>
+ /// Executes a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ {
+ if (help)
+ return true;
- /// <summary>
- /// Gets the time after which this cooldown resets.
- /// </summary>
- public TimeSpan Reset { get; internal set; }
+ var bid = this.GetBucketId(ctx, out var usr, out var chn, out var gld);
+ if (!this._buckets.TryGetValue(bid, out var bucket))
+ {
+ bucket = new CommandCooldownBucket(this.MaxUses, this.Reset, usr, chn, gld);
+ this._buckets.AddOrUpdate(bid, bucket, (k, v) => bucket);
+ }
- /// <summary>
- /// Gets the semaphore used to lock the use value.
- /// </summary>
- private readonly SemaphoreSlim _usageSemaphore;
+ return await bucket.DecrementUseAsync().ConfigureAwait(false);
+ }
+ }
/// <summary>
- /// Creates a new command cooldown bucket.
+ /// Defines how are command cooldowns applied.
/// </summary>
- /// <param name="maxUses">Maximum number of uses for this bucket.</param>
- /// <param name="resetAfter">Time after which this bucket resets.</param>
- /// <param name="userId">ID of the user with which this cooldown is associated.</param>
- /// <param name="channelId">ID of the channel with which this cooldown is associated.</param>
- /// <param name="guildId">ID of the guild with which this cooldown is associated.</param>
- internal CommandCooldownBucket(int maxUses, TimeSpan resetAfter, ulong userId = 0, ulong channelId = 0, ulong guildId = 0)
+ public enum CooldownBucketType : int
{
- this._remainingUses = maxUses;
- this.MaxUses = maxUses;
- this.ResetsAt = DateTimeOffset.UtcNow + resetAfter;
- this.Reset = resetAfter;
- this.UserId = userId;
- this.ChannelId = channelId;
- this.GuildId = guildId;
- this.BucketId = MakeId(userId, channelId, guildId);
- this._usageSemaphore = new SemaphoreSlim(1, 1);
+ /// <summary>
+ /// Denotes that the command will have its cooldown applied per-user.
+ /// </summary>
+ User = 1,
+
+ /// <summary>
+ /// Denotes that the command will have its cooldown applied per-channel.
+ /// </summary>
+ Channel = 2,
+
+ /// <summary>
+ /// Denotes that the command will have its cooldown applied per-guild. In DMs, this applies the cooldown per-channel.
+ /// </summary>
+ Guild = 4,
+
+ /// <summary>
+ /// Denotes that the command will have its cooldown applied globally.
+ /// </summary>
+ Global = 0
}
/// <summary>
- /// Decrements the remaining use counter.
+ /// Represents a cooldown bucket for commands.
/// </summary>
- /// <returns>Whether decrement succeeded or not.</returns>
- internal async Task<bool> DecrementUseAsync()
+ public sealed class CommandCooldownBucket : IEquatable<CommandCooldownBucket>
{
- await this._usageSemaphore.WaitAsync().ConfigureAwait(false);
-
- // if we're past reset time...
- var now = DateTimeOffset.UtcNow;
- if (now >= this.ResetsAt)
+ /// <summary>
+ /// Gets the ID of the user with whom this cooldown is associated.
+ /// </summary>
+ public ulong UserId { get; }
+
+ /// <summary>
+ /// Gets the ID of the channel with which this cooldown is associated.
+ /// </summary>
+ public ulong ChannelId { get; }
+
+ /// <summary>
+ /// Gets the ID of the guild with which this cooldown is associated.
+ /// </summary>
+ public ulong GuildId { get; }
+
+ /// <summary>
+ /// Gets the ID of the bucket. This is used to distinguish between cooldown buckets.
+ /// </summary>
+ public string BucketId { get; }
+
+ /// <summary>
+ /// Gets the remaining number of uses before the cooldown is triggered.
+ /// </summary>
+ public int RemainingUses
+ => Volatile.Read(ref this._remainingUses);
+
+ private int _remainingUses;
+
+ /// <summary>
+ /// Gets the maximum number of times this command can be used in given timespan.
+ /// </summary>
+ public int MaxUses { get; }
+
+ /// <summary>
+ /// Gets the date and time at which the cooldown resets.
+ /// </summary>
+ public DateTimeOffset ResetsAt { get; internal set; }
+
+ /// <summary>
+ /// Gets the time after which this cooldown resets.
+ /// </summary>
+ public TimeSpan Reset { get; internal set; }
+
+ /// <summary>
+ /// Gets the semaphore used to lock the use value.
+ /// </summary>
+ private readonly SemaphoreSlim _usageSemaphore;
+
+ /// <summary>
+ /// Creates a new command cooldown bucket.
+ /// </summary>
+ /// <param name="maxUses">Maximum number of uses for this bucket.</param>
+ /// <param name="resetAfter">Time after which this bucket resets.</param>
+ /// <param name="userId">ID of the user with which this cooldown is associated.</param>
+ /// <param name="channelId">ID of the channel with which this cooldown is associated.</param>
+ /// <param name="guildId">ID of the guild with which this cooldown is associated.</param>
+ internal CommandCooldownBucket(int maxUses, TimeSpan resetAfter, ulong userId = 0, ulong channelId = 0, ulong guildId = 0)
{
- // ...do the reset and set a new reset time
- Interlocked.Exchange(ref this._remainingUses, this.MaxUses);
- this.ResetsAt = now + this.Reset;
+ this._remainingUses = maxUses;
+ this.MaxUses = maxUses;
+ this.ResetsAt = DateTimeOffset.UtcNow + resetAfter;
+ this.Reset = resetAfter;
+ this.UserId = userId;
+ this.ChannelId = channelId;
+ this.GuildId = guildId;
+ this.BucketId = MakeId(userId, channelId, guildId);
+ this._usageSemaphore = new SemaphoreSlim(1, 1);
}
- // check if we have any uses left, if we do...
- var success = false;
- if (this.RemainingUses > 0)
+ /// <summary>
+ /// Decrements the remaining use counter.
+ /// </summary>
+ /// <returns>Whether decrement succeeded or not.</returns>
+ internal async Task<bool> DecrementUseAsync()
{
- // ...decrement, and return success...
- Interlocked.Decrement(ref this._remainingUses);
- success = true;
+ await this._usageSemaphore.WaitAsync().ConfigureAwait(false);
+
+ // if we're past reset time...
+ var now = DateTimeOffset.UtcNow;
+ if (now >= this.ResetsAt)
+ {
+ // ...do the reset and set a new reset time
+ Interlocked.Exchange(ref this._remainingUses, this.MaxUses);
+ this.ResetsAt = now + this.Reset;
+ }
+
+ // check if we have any uses left, if we do...
+ var success = false;
+ if (this.RemainingUses > 0)
+ {
+ // ...decrement, and return success...
+ Interlocked.Decrement(ref this._remainingUses);
+ success = true;
+ }
+
+ // ...otherwise just fail
+ this._usageSemaphore.Release();
+ return success;
}
- // ...otherwise just fail
- this._usageSemaphore.Release();
- return success;
- }
-
- /// <summary>
- /// Returns a string representation of this command cooldown bucket.
- /// </summary>
- /// <returns>String representation of this command cooldown bucket.</returns>
- public override string ToString() => $"Command bucket {this.BucketId}";
-
- /// <summary>
- /// Checks whether this <see cref="CommandCooldownBucket"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="CommandCooldownBucket"/>.</returns>
- public override bool Equals(object obj) => this.Equals(obj as CommandCooldownBucket);
-
- /// <summary>
- /// Checks whether this <see cref="CommandCooldownBucket"/> is equal to another <see cref="CommandCooldownBucket"/>.
- /// </summary>
- /// <param name="other"><see cref="CommandCooldownBucket"/> to compare to.</param>
- /// <returns>Whether the <see cref="CommandCooldownBucket"/> is equal to this <see cref="CommandCooldownBucket"/>.</returns>
- public bool Equals(CommandCooldownBucket other) => other is not null && (ReferenceEquals(this, other) || (this.UserId == other.UserId && this.ChannelId == other.ChannelId && this.GuildId == other.GuildId));
-
- /// <summary>
- /// Gets the hash code for this <see cref="CommandCooldownBucket"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="CommandCooldownBucket"/>.</returns>
- public override int GetHashCode() => HashCode.Combine(this.UserId, this.ChannelId, this.GuildId);
+ /// <summary>
+ /// Returns a string representation of this command cooldown bucket.
+ /// </summary>
+ /// <returns>String representation of this command cooldown bucket.</returns>
+ public override string ToString() => $"Command bucket {this.BucketId}";
+
+ /// <summary>
+ /// Checks whether this <see cref="CommandCooldownBucket"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="CommandCooldownBucket"/>.</returns>
+ public override bool Equals(object obj) => this.Equals(obj as CommandCooldownBucket);
+
+ /// <summary>
+ /// Checks whether this <see cref="CommandCooldownBucket"/> is equal to another <see cref="CommandCooldownBucket"/>.
+ /// </summary>
+ /// <param name="other"><see cref="CommandCooldownBucket"/> to compare to.</param>
+ /// <returns>Whether the <see cref="CommandCooldownBucket"/> is equal to this <see cref="CommandCooldownBucket"/>.</returns>
+ public bool Equals(CommandCooldownBucket other) => other is not null && (ReferenceEquals(this, other) || (this.UserId == other.UserId && this.ChannelId == other.ChannelId && this.GuildId == other.GuildId));
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="CommandCooldownBucket"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="CommandCooldownBucket"/>.</returns>
+ public override int GetHashCode() => HashCode.Combine(this.UserId, this.ChannelId, this.GuildId);
+
+ /// <summary>
+ /// Gets whether the two <see cref="CommandCooldownBucket"/> objects are equal.
+ /// </summary>
+ /// <param name="bucket1">First bucket to compare.</param>
+ /// <param name="bucket2">Second bucket to compare.</param>
+ /// <returns>Whether the two buckets are equal.</returns>
+ public static bool operator ==(CommandCooldownBucket bucket1, CommandCooldownBucket bucket2)
+ {
+ var null1 = bucket1 is null;
+ var null2 = bucket2 is null;
- /// <summary>
- /// Gets whether the two <see cref="CommandCooldownBucket"/> objects are equal.
- /// </summary>
- /// <param name="bucket1">First bucket to compare.</param>
- /// <param name="bucket2">Second bucket to compare.</param>
- /// <returns>Whether the two buckets are equal.</returns>
- public static bool operator ==(CommandCooldownBucket bucket1, CommandCooldownBucket bucket2)
- {
- var null1 = bucket1 is null;
- var null2 = bucket2 is null;
+ return (null1 && null2) || (null1 == null2 && null1.Equals(null2));
+ }
- return (null1 && null2) || (null1 == null2 && null1.Equals(null2));
+ /// <summary>
+ /// Gets whether the two <see cref="CommandCooldownBucket"/> objects are not equal.
+ /// </summary>
+ /// <param name="bucket1">First bucket to compare.</param>
+ /// <param name="bucket2">Second bucket to compare.</param>
+ /// <returns>Whether the two buckets are not equal.</returns>
+ public static bool operator !=(CommandCooldownBucket bucket1, CommandCooldownBucket bucket2)
+ => !(bucket1 == bucket2);
+
+ /// <summary>
+ /// Creates a bucket ID from given bucket parameters.
+ /// </summary>
+ /// <param name="userId">ID of the user with which this cooldown is associated.</param>
+ /// <param name="channelId">ID of the channel with which this cooldown is associated.</param>
+ /// <param name="guildId">ID of the guild with which this cooldown is associated.</param>
+ /// <returns>Generated bucket ID.</returns>
+ public static string MakeId(ulong userId = 0, ulong channelId = 0, ulong guildId = 0)
+ => $"{userId.ToString(CultureInfo.InvariantCulture)}:{channelId.ToString(CultureInfo.InvariantCulture)}:{guildId.ToString(CultureInfo.InvariantCulture)}";
}
-
- /// <summary>
- /// Gets whether the two <see cref="CommandCooldownBucket"/> objects are not equal.
- /// </summary>
- /// <param name="bucket1">First bucket to compare.</param>
- /// <param name="bucket2">Second bucket to compare.</param>
- /// <returns>Whether the two buckets are not equal.</returns>
- public static bool operator !=(CommandCooldownBucket bucket1, CommandCooldownBucket bucket2)
- => !(bucket1 == bucket2);
-
- /// <summary>
- /// Creates a bucket ID from given bucket parameters.
- /// </summary>
- /// <param name="userId">ID of the user with which this cooldown is associated.</param>
- /// <param name="channelId">ID of the channel with which this cooldown is associated.</param>
- /// <param name="guildId">ID of the guild with which this cooldown is associated.</param>
- /// <returns>Generated bucket ID.</returns>
- public static string MakeId(ulong userId = 0, ulong channelId = 0, ulong guildId = 0)
- => $"{userId.ToString(CultureInfo.InvariantCulture)}:{channelId.ToString(CultureInfo.InvariantCulture)}:{guildId.ToString(CultureInfo.InvariantCulture)}";
}
diff --git a/DisCatSharp.CommandsNext/Attributes/DescriptionAttribute.cs b/DisCatSharp.CommandsNext/Attributes/DescriptionAttribute.cs
index 4f90fa19c..26977ecb6 100644
--- a/DisCatSharp.CommandsNext/Attributes/DescriptionAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/DescriptionAttribute.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Attributes;
-
-/// <summary>
-/// Gives this command, group, or argument a description, which is used when listing help.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Parameter)]
-public sealed class DescriptionAttribute : Attribute
+namespace DisCatSharp.CommandsNext.Attributes
{
- /// <summary>
- /// Gets the description for this command, group, or argument.
- /// </summary>
- public string Description { get; }
-
/// <summary>
/// Gives this command, group, or argument a description, which is used when listing help.
/// </summary>
- /// <param name="description"></param>
- public DescriptionAttribute(string description)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Parameter)]
+ public sealed class DescriptionAttribute : Attribute
{
- this.Description = description;
+ /// <summary>
+ /// Gets the description for this command, group, or argument.
+ /// </summary>
+ public string Description { get; }
+
+ /// <summary>
+ /// Gives this command, group, or argument a description, which is used when listing help.
+ /// </summary>
+ /// <param name="description"></param>
+ public DescriptionAttribute(string description)
+ {
+ this.Description = description;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/DontInjectAttribute.cs b/DisCatSharp.CommandsNext/Attributes/DontInjectAttribute.cs
index ad1c4c409..b817b46c5 100644
--- a/DisCatSharp.CommandsNext/Attributes/DontInjectAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/DontInjectAttribute.cs
@@ -1,32 +1,33 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Attributes;
-
-/// <summary>
-/// Prevents this field or property from having its value injected by dependency injection.
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public class DontInjectAttribute : Attribute
-{ }
+namespace DisCatSharp.CommandsNext.Attributes
+{
+ /// <summary>
+ /// Prevents this field or property from having its value injected by dependency injection.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public class DontInjectAttribute : Attribute
+ { }
+}
diff --git a/DisCatSharp.CommandsNext/Attributes/GroupAttribute.cs b/DisCatSharp.CommandsNext/Attributes/GroupAttribute.cs
index 171f3bd12..573dcb087 100644
--- a/DisCatSharp.CommandsNext/Attributes/GroupAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/GroupAttribute.cs
@@ -1,61 +1,62 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Marks this class as a command group.
-/// </summary>
-[AttributeUsage(AttributeTargets.Class)]
-public sealed class GroupAttribute : Attribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Gets the name of this group.
+ /// Marks this class as a command group.
/// </summary>
- public string Name { get; }
-
- /// <summary>
- /// Marks this class as a command group, using the class' name as group name.
- /// </summary>
- public GroupAttribute()
+ [AttributeUsage(AttributeTargets.Class)]
+ public sealed class GroupAttribute : Attribute
{
- this.Name = null;
- }
+ /// <summary>
+ /// Gets the name of this group.
+ /// </summary>
+ public string Name { get; }
- /// <summary>
- /// Marks this class as a command group with specified name.
- /// </summary>
- /// <param name="name">Name of this group.</param>
- public GroupAttribute(string name)
- {
- if (string.IsNullOrWhiteSpace(name))
- throw new ArgumentNullException(nameof(name), "Group names cannot be null, empty, or all-whitespace.");
+ /// <summary>
+ /// Marks this class as a command group, using the class' name as group name.
+ /// </summary>
+ public GroupAttribute()
+ {
+ this.Name = null;
+ }
+
+ /// <summary>
+ /// Marks this class as a command group with specified name.
+ /// </summary>
+ /// <param name="name">Name of this group.</param>
+ public GroupAttribute(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentNullException(nameof(name), "Group names cannot be null, empty, or all-whitespace.");
- if (name.Any(xc => char.IsWhiteSpace(xc)))
- throw new ArgumentException("Group names cannot contain whitespace characters.", nameof(name));
+ if (name.Any(xc => char.IsWhiteSpace(xc)))
+ throw new ArgumentException("Group names cannot contain whitespace characters.", nameof(name));
- this.Name = name;
+ this.Name = name;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/HiddenAttribute.cs b/DisCatSharp.CommandsNext/Attributes/HiddenAttribute.cs
index 7d87125a3..ee13b5781 100644
--- a/DisCatSharp.CommandsNext/Attributes/HiddenAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/HiddenAttribute.cs
@@ -1,32 +1,33 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Attributes;
-
-/// <summary>
-/// Marks this command or group as hidden.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
-public sealed class HiddenAttribute : Attribute
-{ }
+namespace DisCatSharp.CommandsNext.Attributes
+{
+ /// <summary>
+ /// Marks this command or group as hidden.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
+ public sealed class HiddenAttribute : Attribute
+ { }
+}
diff --git a/DisCatSharp.CommandsNext/Attributes/ModuleLifespanAttribute.cs b/DisCatSharp.CommandsNext/Attributes/ModuleLifespanAttribute.cs
index 91479ca26..bdb083f5a 100644
--- a/DisCatSharp.CommandsNext/Attributes/ModuleLifespanAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/ModuleLifespanAttribute.cs
@@ -1,62 +1,63 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Attributes;
-
-/// <summary>
-/// Defines a lifespan for this command module.
-/// </summary>
-[AttributeUsage(AttributeTargets.Class, Inherited = false)]
-public class ModuleLifespanAttribute : Attribute
+namespace DisCatSharp.CommandsNext.Attributes
{
- /// <summary>
- /// Gets the lifespan defined for this module.
- /// </summary>
- public ModuleLifespan Lifespan { get; }
-
/// <summary>
/// Defines a lifespan for this command module.
/// </summary>
- /// <param name="lifespan">Lifespan for this module.</param>
- public ModuleLifespanAttribute(ModuleLifespan lifespan)
+ [AttributeUsage(AttributeTargets.Class, Inherited = false)]
+ public class ModuleLifespanAttribute : Attribute
{
- this.Lifespan = lifespan;
+ /// <summary>
+ /// Gets the lifespan defined for this module.
+ /// </summary>
+ public ModuleLifespan Lifespan { get; }
+
+ /// <summary>
+ /// Defines a lifespan for this command module.
+ /// </summary>
+ /// <param name="lifespan">Lifespan for this module.</param>
+ public ModuleLifespanAttribute(ModuleLifespan lifespan)
+ {
+ this.Lifespan = lifespan;
+ }
}
-}
-/// <summary>
-/// Defines lifespan of a command module.
-/// </summary>
-public enum ModuleLifespan : int
-{
/// <summary>
- /// Defines that this module will be instantiated once.
+ /// Defines lifespan of a command module.
/// </summary>
- Singleton = 0,
+ public enum ModuleLifespan : int
+ {
+ /// <summary>
+ /// Defines that this module will be instantiated once.
+ /// </summary>
+ Singleton = 0,
- /// <summary>
- /// Defines that this module will be instantiated every time a containing command is called.
- /// </summary>
- Transient = 1
+ /// <summary>
+ /// Defines that this module will be instantiated every time a containing command is called.
+ /// </summary>
+ Transient = 1
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/PriorityAttribute.cs b/DisCatSharp.CommandsNext/Attributes/PriorityAttribute.cs
index 79add7b2a..d67439c1c 100644
--- a/DisCatSharp.CommandsNext/Attributes/PriorityAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/PriorityAttribute.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Attributes;
-
-/// <summary>
-/// Defines this command overload's priority. This determines the order in which overloads will be attempted to be called. Commands will be attempted in order of priority, in descending order.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method, Inherited = false)]
-public sealed class PriorityAttribute : Attribute
+namespace DisCatSharp.CommandsNext.Attributes
{
- /// <summary>
- /// Gets the priority of this command overload.
- /// </summary>
- public int Priority { get; }
-
/// <summary>
/// Defines this command overload's priority. This determines the order in which overloads will be attempted to be called. Commands will be attempted in order of priority, in descending order.
/// </summary>
- /// <param name="priority">Priority of this command overload.</param>
- public PriorityAttribute(int priority)
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+ public sealed class PriorityAttribute : Attribute
{
- this.Priority = priority;
+ /// <summary>
+ /// Gets the priority of this command overload.
+ /// </summary>
+ public int Priority { get; }
+
+ /// <summary>
+ /// Defines this command overload's priority. This determines the order in which overloads will be attempted to be called. Commands will be attempted in order of priority, in descending order.
+ /// </summary>
+ /// <param name="priority">Priority of this command overload.</param>
+ public PriorityAttribute(int priority)
+ {
+ this.Priority = priority;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RemainingTextAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RemainingTextAttribute.cs
index 68abc779c..76a2284bc 100644
--- a/DisCatSharp.CommandsNext/Attributes/RemainingTextAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RemainingTextAttribute.cs
@@ -1,32 +1,33 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Attributes;
-
-/// <summary>
-/// Indicates that the command argument takes the rest of the input without parsing.
-/// </summary>
-[AttributeUsage(AttributeTargets.Parameter)]
-public class RemainingTextAttribute : Attribute
-{ }
+namespace DisCatSharp.CommandsNext.Attributes
+{
+ /// <summary>
+ /// Indicates that the command argument takes the rest of the input without parsing.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class RemainingTextAttribute : Attribute
+ { }
+}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireBoostingAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireBoostingAttribute.cs
index b42d9c355..23c36dbbe 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireBoostingAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireBoostingAttribute.cs
@@ -1,83 +1,84 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to boosters.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireBoostingAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Gets the required boost time.
- /// </summary>
- public int Since { get; }
-
- /// <summary>
- /// Gets the required guild.
- /// </summary>
- public ulong GuildId { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RequireBoostingAttribute"/> class.
+ /// Defines that usage of this command is restricted to boosters.
/// </summary>
- /// <param name="days">Boosting since days.</param>
- public RequireBoostingAttribute(int days = 0)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireBoostingAttribute : CheckBaseAttribute
{
- this.GuildId = 0;
- this.Since = days;
- }
+ /// <summary>
+ /// Gets the required boost time.
+ /// </summary>
+ public int Since { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="RequireBoostingAttribute"/> class.
- /// </summary>
- /// <param name="guildId">Target guild id.</param>
- /// <param name="days">Boosting since days.</param>
- public RequireBoostingAttribute(ulong guildId, int days = 0)
- {
- this.GuildId = guildId;
- this.Since = days;
- }
+ /// <summary>
+ /// Gets the required guild.
+ /// </summary>
+ public ulong GuildId { get; }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- {
- if (this.GuildId != 0)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RequireBoostingAttribute"/> class.
+ /// </summary>
+ /// <param name="days">Boosting since days.</param>
+ public RequireBoostingAttribute(int days = 0)
{
- var guild = await ctx.Client.GetGuildAsync(this.GuildId);
- var member = await guild.GetMemberAsync(ctx.User.Id);
- return member != null && member.PremiumSince.HasValue ? await Task.FromResult(member.PremiumSince.Value.UtcDateTime.Date < DateTime.UtcNow.Date.AddDays(-this.Since)) : await Task.FromResult(false);
+ this.GuildId = 0;
+ this.Since = days;
}
- else
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RequireBoostingAttribute"/> class.
+ /// </summary>
+ /// <param name="guildId">Target guild id.</param>
+ /// <param name="days">Boosting since days.</param>
+ public RequireBoostingAttribute(ulong guildId, int days = 0)
+ {
+ this.GuildId = guildId;
+ this.Since = days;
+ }
+
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
{
- return ctx.Member != null && ctx.Member.PremiumSince.HasValue ? await Task.FromResult(ctx.Member.PremiumSince.Value.UtcDateTime.Date < DateTime.UtcNow.Date.AddDays(-this.Since)) : await Task.FromResult(false);
+ if (this.GuildId != 0)
+ {
+ var guild = await ctx.Client.GetGuildAsync(this.GuildId);
+ var member = await guild.GetMemberAsync(ctx.User.Id);
+ return member != null && member.PremiumSince.HasValue ? await Task.FromResult(member.PremiumSince.Value.UtcDateTime.Date < DateTime.UtcNow.Date.AddDays(-this.Since)) : await Task.FromResult(false);
+ }
+ else
+ {
+ return ctx.Member != null && ctx.Member.PremiumSince.HasValue ? await Task.FromResult(ctx.Member.PremiumSince.Value.UtcDateTime.Date < DateTime.UtcNow.Date.AddDays(-this.Since)) : await Task.FromResult(false);
+ }
}
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireBotPermissionsAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireBotPermissionsAttribute.cs
index b50236dee..dd6ce9550 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireBotPermissionsAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireBotPermissionsAttribute.cs
@@ -1,76 +1,77 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is only possible when the bot is granted a specific permission.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireBotPermissionsAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
- /// <summary>
- /// Gets the permissions required by this attribute.
- /// </summary>
- public Permissions Permissions { get; }
-
- /// <summary>
- /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
- /// </summary>
- public bool IgnoreDms { get; } = true;
-
/// <summary>
/// Defines that usage of this command is only possible when the bot is granted a specific permission.
/// </summary>
- /// <param name="permissions">Permissions required to execute this command.</param>
- /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
- public RequireBotPermissionsAttribute(Permissions permissions, bool ignoreDms = true)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireBotPermissionsAttribute : CheckBaseAttribute
{
- this.Permissions = permissions;
- this.IgnoreDms = ignoreDms;
- }
+ /// <summary>
+ /// Gets the permissions required by this attribute.
+ /// </summary>
+ public Permissions Permissions { get; }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- {
- if (ctx.Guild == null)
- return this.IgnoreDms;
+ /// <summary>
+ /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
+ /// </summary>
+ public bool IgnoreDms { get; } = true;
+
+ /// <summary>
+ /// Defines that usage of this command is only possible when the bot is granted a specific permission.
+ /// </summary>
+ /// <param name="permissions">Permissions required to execute this command.</param>
+ /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
+ public RequireBotPermissionsAttribute(Permissions permissions, bool ignoreDms = true)
+ {
+ this.Permissions = permissions;
+ this.IgnoreDms = ignoreDms;
+ }
+
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ {
+ if (ctx.Guild == null)
+ return this.IgnoreDms;
- var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false);
- if (bot == null)
- return false;
+ var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false);
+ if (bot == null)
+ return false;
- if (bot.Id == ctx.Guild.OwnerId)
- return true;
+ if (bot.Id == ctx.Guild.OwnerId)
+ return true;
- var pbot = ctx.Channel.PermissionsFor(bot);
+ var pbot = ctx.Channel.PermissionsFor(bot);
- return (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
+ return (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireCertifiedModeratorAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireCertifiedModeratorAttribute.cs
index fbd6b289e..0c0e82150 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireCertifiedModeratorAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireCertifiedModeratorAttribute.cs
@@ -1,40 +1,41 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to discord certified moderators.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireCertifiedModeratorAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Executes the a check.
+ /// Defines that usage of this command is restricted to discord certified moderators.
/// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => ctx.User.Flags.HasValue ? Task.FromResult(ctx.User.Flags.Value.HasFlag(UserFlags.CertifiedModerator)) : Task.FromResult(false);
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireCertifiedModeratorAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => ctx.User.Flags.HasValue ? Task.FromResult(ctx.User.Flags.Value.HasFlag(UserFlags.CertifiedModerator)) : Task.FromResult(false);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireCommunityAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireCommunityAttribute.cs
index 3255be825..6557d1612 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireCommunityAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireCommunityAttribute.cs
@@ -1,47 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that a command is only usable within a community-enabled guild.
-/// </summary>
-///
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireCommunityAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Defines that this command is only usable within a community-enabled guild.
+ /// Defines that a command is only usable within a community-enabled guild.
/// </summary>
- public RequireCommunityAttribute()
- { }
+ ///
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireCommunityAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Defines that this command is only usable within a community-enabled guild.
+ /// </summary>
+ public RequireCommunityAttribute()
+ { }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => Task.FromResult(ctx.Guild != null && ctx.Guild.IsCommunity);
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => Task.FromResult(ctx.Guild != null && ctx.Guild.IsCommunity);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireDirectMessageAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireDirectMessageAttribute.cs
index c2a328b3b..5c0b5b989 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireDirectMessageAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireDirectMessageAttribute.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that a command is only usable within a direct message channel.
-/// </summary>
-
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireDirectMessageAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Defines that this command is only usable within a direct message channel.
+ /// Defines that a command is only usable within a direct message channel.
/// </summary>
- public RequireDirectMessageAttribute()
- { }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- => Task.FromResult(ctx.Channel is DiscordDmChannel);
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireDirectMessageAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Defines that this command is only usable within a direct message channel.
+ /// </summary>
+ public RequireDirectMessageAttribute()
+ { }
+
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ => Task.FromResult(ctx.Channel is DiscordDmChannel);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireDisCatSharpDeveloperAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireDisCatSharpDeveloperAttribute.cs
index 5f015d6f2..eda6ce7e8 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireDisCatSharpDeveloperAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireDisCatSharpDeveloperAttribute.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to boosters.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireDisCatSharpDeveloperAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Executes the a check.
+ /// Defines that usage of this command is restricted to boosters.
/// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireDisCatSharpDeveloperAttribute : CheckBaseAttribute
{
- var team = (await ctx.Client.GetLibraryDevelopmentTeamAsync()).Developers;
- return team?.Any(x => x.Id == ctx.User.Id) ?? false;
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ {
+ var team = (await ctx.Client.GetLibraryDevelopmentTeamAsync()).Developers;
+ return team?.Any(x => x.Id == ctx.User.Id) ?? false;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireGuildAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireGuildAttribute.cs
index 6ae30f3be..bff1596bb 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireGuildAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireGuildAttribute.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that a command is only usable within a guild.
-/// </summary>
-public sealed class RequireGuildAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Defines that this command is only usable within a guild.
+ /// Defines that a command is only usable within a guild.
/// </summary>
- public RequireGuildAttribute()
- { }
+ public sealed class RequireGuildAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Defines that this command is only usable within a guild.
+ /// </summary>
+ public RequireGuildAttribute()
+ { }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- => Task.FromResult(ctx.Guild != null);
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ => Task.FromResult(ctx.Guild != null);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireGuildOwnerAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireGuildOwnerAttribute.cs
index df316031c..53ca2daac 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireGuildOwnerAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireGuildOwnerAttribute.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to the guild owner.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireGuildOwnerAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Executes the a check.
+ /// Defines that usage of this command is restricted to the guild owner.
/// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireGuildOwnerAttribute : CheckBaseAttribute
{
- var guild = await Task.FromResult(ctx.Guild != null);
- if (guild)
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
{
- var owner = await Task.FromResult(ctx.Member == ctx.Guild.Owner);
+ var guild = await Task.FromResult(ctx.Guild != null);
+ if (guild)
+ {
+ var owner = await Task.FromResult(ctx.Member == ctx.Guild.Owner);
- return owner;
- }
- else
- {
- return false;
+ return owner;
+ }
+ else
+ {
+ return false;
+ }
}
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireMemberVerificationGateAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireMemberVerificationGateAttribute.cs
index 719c37271..98eaa6b9e 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireMemberVerificationGateAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireMemberVerificationGateAttribute.cs
@@ -1,44 +1,45 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that a command is only usable within a guild which has enabled the member verification gate.
-/// </summary>
-public sealed class RequireMemberVerificationGateAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Defines that this command is only usable within guild which has enabled the member verification gate.
+ /// Defines that a command is only usable within a guild which has enabled the member verification gate.
/// </summary>
- public RequireMemberVerificationGateAttribute()
- { }
+ public sealed class RequireMemberVerificationGateAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Defines that this command is only usable within guild which has enabled the member verification gate.
+ /// </summary>
+ public RequireMemberVerificationGateAttribute()
+ { }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => Task.FromResult(ctx.Guild != null && ctx.Guild.HasMemberVerificationGate);
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => Task.FromResult(ctx.Guild != null && ctx.Guild.HasMemberVerificationGate);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireNsfwAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireNsfwAttribute.cs
index e5aea0d2d..cd2f22c17 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireNsfwAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireNsfwAttribute.cs
@@ -1,41 +1,42 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to NSFW channels.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireNsfwAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Executes the a check.
+ /// Defines that usage of this command is restricted to NSFW channels.
/// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- => Task.FromResult(ctx.Channel.Guild == null || ctx.Channel.IsNsfw);
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireNsfwAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ => Task.FromResult(ctx.Channel.Guild == null || ctx.Channel.IsNsfw);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireOwnerAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireOwnerAttribute.cs
index fb111092f..cb448030b 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireOwnerAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireOwnerAttribute.cs
@@ -1,47 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to the owner of the bot.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireOwnerAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Executes the a check.
+ /// Defines that usage of this command is restricted to the owner of the bot.
/// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireOwnerAttribute : CheckBaseAttribute
{
- var app = ctx.Client.CurrentApplication;
- var me = ctx.Client.CurrentUser;
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ {
+ var app = ctx.Client.CurrentApplication;
+ var me = ctx.Client.CurrentUser;
- return app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id);
+ return app != null ? Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : Task.FromResult(ctx.User.Id == me.Id);
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireOwnerOrIdAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireOwnerOrIdAttribute.cs
index 4834edcc7..a6a58fca9 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireOwnerOrIdAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireOwnerOrIdAttribute.cs
@@ -1,68 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Requires ownership of the bot or a whitelisted id to execute this command.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireOwnerOrIdAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Allowed user ids
+ /// Requires ownership of the bot or a whitelisted id to execute this command.
/// </summary>
- public IReadOnlyList<ulong> UserIds { get; }
-
- /// <summary>
- /// Defines that usage of this command is restricted to the owner or whitelisted ids of the bot.
- /// </summary>
- /// <param name="userIds">List of allowed user ids</param>
- public RequireOwnerOrIdAttribute(params ulong[] userIds)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireOwnerOrIdAttribute : CheckBaseAttribute
{
- this.UserIds = new ReadOnlyCollection<ulong>(userIds);
- }
+ /// <summary>
+ /// Allowed user ids
+ /// </summary>
+ public IReadOnlyList<ulong> UserIds { get; }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- {
- var app = ctx.Client.CurrentApplication;
- var me = ctx.Client.CurrentUser;
+ /// <summary>
+ /// Defines that usage of this command is restricted to the owner or whitelisted ids of the bot.
+ /// </summary>
+ /// <param name="userIds">List of allowed user ids</param>
+ public RequireOwnerOrIdAttribute(params ulong[] userIds)
+ {
+ this.UserIds = new ReadOnlyCollection<ulong>(userIds);
+ }
+
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ {
+ var app = ctx.Client.CurrentApplication;
+ var me = ctx.Client.CurrentUser;
- var owner = app != null ? await Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : await Task.FromResult(ctx.User.Id == me.Id);
+ var owner = app != null ? await Task.FromResult(app.Owners.Any(x => x.Id == ctx.User.Id)) : await Task.FromResult(ctx.User.Id == me.Id);
- var allowed = this.UserIds.Contains(ctx.User.Id);
+ var allowed = this.UserIds.Contains(ctx.User.Id);
- return owner || allowed;
+ return owner || allowed;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequirePermissionsAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequirePermissionsAttribute.cs
index 483cabf7a..d2825a0ed 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequirePermissionsAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequirePermissionsAttribute.cs
@@ -1,86 +1,87 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequirePermissionsAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
- /// <summary>
- /// Gets the permissions required by this attribute.
- /// </summary>
- public Permissions Permissions { get; }
-
- /// <summary>
- /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
- /// </summary>
- public bool IgnoreDms { get; } = true;
-
/// <summary>
/// Defines that usage of this command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions.
/// </summary>
- /// <param name="permissions">Permissions required to execute this command.</param>
- /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
- public RequirePermissionsAttribute(Permissions permissions, bool ignoreDms = true)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequirePermissionsAttribute : CheckBaseAttribute
{
- this.Permissions = permissions;
- this.IgnoreDms = ignoreDms;
- }
+ /// <summary>
+ /// Gets the permissions required by this attribute.
+ /// </summary>
+ public Permissions Permissions { get; }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- {
- if (ctx.Guild == null)
- return this.IgnoreDms;
+ /// <summary>
+ /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
+ /// </summary>
+ public bool IgnoreDms { get; } = true;
+
+ /// <summary>
+ /// Defines that usage of this command is restricted to members with specified permissions. This check also verifies that the bot has the same permissions.
+ /// </summary>
+ /// <param name="permissions">Permissions required to execute this command.</param>
+ /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
+ public RequirePermissionsAttribute(Permissions permissions, bool ignoreDms = true)
+ {
+ this.Permissions = permissions;
+ this.IgnoreDms = ignoreDms;
+ }
+
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ {
+ if (ctx.Guild == null)
+ return this.IgnoreDms;
- var usr = ctx.Member;
- if (usr == null)
- return false;
- var pusr = ctx.Channel.PermissionsFor(usr);
+ var usr = ctx.Member;
+ if (usr == null)
+ return false;
+ var pusr = ctx.Channel.PermissionsFor(usr);
- var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false);
- if (bot == null)
- return false;
- var pbot = ctx.Channel.PermissionsFor(bot);
+ var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id).ConfigureAwait(false);
+ if (bot == null)
+ return false;
+ var pbot = ctx.Channel.PermissionsFor(bot);
- var usrok = ctx.Guild.OwnerId == usr.Id;
- var botok = ctx.Guild.OwnerId == bot.Id;
+ var usrok = ctx.Guild.OwnerId == usr.Id;
+ var botok = ctx.Guild.OwnerId == bot.Id;
- if (!usrok)
- usrok = (pusr & Permissions.Administrator) != 0 || (pusr & this.Permissions) == this.Permissions;
+ if (!usrok)
+ usrok = (pusr & Permissions.Administrator) != 0 || (pusr & this.Permissions) == this.Permissions;
- if (!botok)
- botok = (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
+ if (!botok)
+ botok = (pbot & Permissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
- return usrok && botok;
+ return usrok && botok;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequirePrefixesAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequirePrefixesAttribute.cs
index 105bc375d..55c273b47 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequirePrefixesAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequirePrefixesAttribute.cs
@@ -1,65 +1,66 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is only allowed with specific prefixes.
-/// </summary>
-[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false)]
-public sealed class RequirePrefixesAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
- /// <summary>
- /// Gets the array of prefixes with which execution of this command is allowed.
- /// </summary>
- public string[] Prefixes { get; }
-
- /// <summary>
- /// <para>Gets or sets default help behaviour for this check. When this is enabled, invoking help without matching prefix will show the commands.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool ShowInHelp { get; set; } = false;
-
/// <summary>
/// Defines that usage of this command is only allowed with specific prefixes.
/// </summary>
- /// <param name="prefixes">Prefixes with which the execution of this command is allowed.</param>
- public RequirePrefixesAttribute(params string[] prefixes)
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false)]
+ public sealed class RequirePrefixesAttribute : CheckBaseAttribute
{
- if (prefixes?.Any() != true)
- throw new ArgumentNullException(nameof(prefixes), "The allowed prefix collection cannot be null or empty.");
+ /// <summary>
+ /// Gets the array of prefixes with which execution of this command is allowed.
+ /// </summary>
+ public string[] Prefixes { get; }
- this.Prefixes = prefixes;
- }
+ /// <summary>
+ /// <para>Gets or sets default help behaviour for this check. When this is enabled, invoking help without matching prefix will show the commands.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool ShowInHelp { get; set; } = false;
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- => Task.FromResult((help && this.ShowInHelp) || this.Prefixes.Contains(ctx.Prefix, ctx.CommandsNext.GetStringComparer()));
+ /// <summary>
+ /// Defines that usage of this command is only allowed with specific prefixes.
+ /// </summary>
+ /// <param name="prefixes">Prefixes with which the execution of this command is allowed.</param>
+ public RequirePrefixesAttribute(params string[] prefixes)
+ {
+ if (prefixes?.Any() != true)
+ throw new ArgumentNullException(nameof(prefixes), "The allowed prefix collection cannot be null or empty.");
+
+ this.Prefixes = prefixes;
+ }
+
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ => Task.FromResult((help && this.ShowInHelp) || this.Prefixes.Contains(ctx.Prefix, ctx.CommandsNext.GetStringComparer()));
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireReferencedMessageAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireReferencedMessageAttribute.cs
index eb46b0615..095d65dd5 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireReferencedMessageAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireReferencedMessageAttribute.cs
@@ -1,40 +1,41 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that a command is only usable when sent in reply. Command will appear in help regardless of this attribute.
-/// </summary>
-public sealed class RequireReferencedMessageAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
/// Defines that a command is only usable when sent in reply. Command will appear in help regardless of this attribute.
/// </summary>
- public RequireReferencedMessageAttribute()
- { }
+ public sealed class RequireReferencedMessageAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Defines that a command is only usable when sent in reply. Command will appear in help regardless of this attribute.
+ /// </summary>
+ public RequireReferencedMessageAttribute()
+ { }
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- => Task.FromResult(help || ctx.Message.ReferencedMessage != null);
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ => Task.FromResult(help || ctx.Message.ReferencedMessage != null);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireRolesAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireRolesAttribute.cs
index 35b44ebbf..81e1576c2 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireRolesAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireRolesAttribute.cs
@@ -1,107 +1,108 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to members with specified role. Note that it's much preferred to restrict access using <see cref="RequirePermissionsAttribute"/>.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireRolesAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
- /// <summary>
- /// Gets the name of the role required to execute this command.
- /// </summary>
- public IReadOnlyList<string> RoleNames { get; }
-
- /// <summary>
- /// Gets the role checking mode. Refer to <see cref="RoleCheckMode"/> for more information.
- /// </summary>
- public RoleCheckMode CheckMode { get; }
-
/// <summary>
/// Defines that usage of this command is restricted to members with specified role. Note that it's much preferred to restrict access using <see cref="RequirePermissionsAttribute"/>.
/// </summary>
- /// <param name="checkMode">Role checking mode.</param>
- /// <param name="roleNames">Names of the role to be verified by this check.</param>
- public RequireRolesAttribute(RoleCheckMode checkMode, params string[] roleNames)
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireRolesAttribute : CheckBaseAttribute
{
- this.CheckMode = checkMode;
- this.RoleNames = new ReadOnlyCollection<string>(roleNames);
- }
+ /// <summary>
+ /// Gets the name of the role required to execute this command.
+ /// </summary>
+ public IReadOnlyList<string> RoleNames { get; }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- {
- if (ctx.Guild == null || ctx.Member == null)
- return Task.FromResult(false);
+ /// <summary>
+ /// Gets the role checking mode. Refer to <see cref="RoleCheckMode"/> for more information.
+ /// </summary>
+ public RoleCheckMode CheckMode { get; }
- var rns = ctx.Member.Roles.Select(xr => xr.Name);
- var rnc = rns.Count();
- var ins = rns.Intersect(this.RoleNames, ctx.CommandsNext.GetStringComparer());
- var inc = ins.Count();
+ /// <summary>
+ /// Defines that usage of this command is restricted to members with specified role. Note that it's much preferred to restrict access using <see cref="RequirePermissionsAttribute"/>.
+ /// </summary>
+ /// <param name="checkMode">Role checking mode.</param>
+ /// <param name="roleNames">Names of the role to be verified by this check.</param>
+ public RequireRolesAttribute(RoleCheckMode checkMode, params string[] roleNames)
+ {
+ this.CheckMode = checkMode;
+ this.RoleNames = new ReadOnlyCollection<string>(roleNames);
+ }
- return this.CheckMode switch
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
{
- RoleCheckMode.All => Task.FromResult(this.RoleNames.Count == inc),
- RoleCheckMode.SpecifiedOnly => Task.FromResult(rnc == inc),
- RoleCheckMode.None => Task.FromResult(inc == 0),
- _ => Task.FromResult(inc > 0),
- };
+ if (ctx.Guild == null || ctx.Member == null)
+ return Task.FromResult(false);
+
+ var rns = ctx.Member.Roles.Select(xr => xr.Name);
+ var rnc = rns.Count();
+ var ins = rns.Intersect(this.RoleNames, ctx.CommandsNext.GetStringComparer());
+ var inc = ins.Count();
+
+ return this.CheckMode switch
+ {
+ RoleCheckMode.All => Task.FromResult(this.RoleNames.Count == inc),
+ RoleCheckMode.SpecifiedOnly => Task.FromResult(rnc == inc),
+ RoleCheckMode.None => Task.FromResult(inc == 0),
+ _ => Task.FromResult(inc > 0),
+ };
+ }
}
-}
-/// <summary>
-/// Specifies how does <see cref="RequireRolesAttribute"/> check for roles.
-/// </summary>
-public enum RoleCheckMode
-{
/// <summary>
- /// Member is required to have any of the specified roles.
+ /// Specifies how does <see cref="RequireRolesAttribute"/> check for roles.
/// </summary>
- Any,
+ public enum RoleCheckMode
+ {
+ /// <summary>
+ /// Member is required to have any of the specified roles.
+ /// </summary>
+ Any,
- /// <summary>
- /// Member is required to have all of the specified roles.
- /// </summary>
- All,
+ /// <summary>
+ /// Member is required to have all of the specified roles.
+ /// </summary>
+ All,
- /// <summary>
- /// Member is required to have exactly the same roles as specified; no extra roles may be present.
- /// </summary>
- SpecifiedOnly,
+ /// <summary>
+ /// Member is required to have exactly the same roles as specified; no extra roles may be present.
+ /// </summary>
+ SpecifiedOnly,
- /// <summary>
- /// Member is required to have none of the specified roles.
- /// </summary>
- None
+ /// <summary>
+ /// Member is required to have none of the specified roles.
+ /// </summary>
+ None
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireStaffAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireStaffAttribute.cs
index 8d7a52204..f7d383fbb 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireStaffAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireStaffAttribute.cs
@@ -1,40 +1,41 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to discord employees.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireStaffAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Executes the a check.
+ /// Defines that usage of this command is restricted to discord employees.
/// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => ctx.User.Flags.HasValue ? Task.FromResult(ctx.User.Flags.Value.HasFlag(UserFlags.Staff)) : Task.FromResult(false);
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireStaffAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => ctx.User.Flags.HasValue ? Task.FromResult(ctx.User.Flags.Value.HasFlag(UserFlags.Staff)) : Task.FromResult(false);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireUserPermissionsAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireUserPermissionsAttribute.cs
index 3c0cbc620..333327dde 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireUserPermissionsAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireUserPermissionsAttribute.cs
@@ -1,80 +1,81 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that usage of this command is restricted to members with specified permissions.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireUserPermissionsAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Gets the permissions required by this attribute.
+ /// Defines that usage of this command is restricted to members with specified permissions.
/// </summary>
- public Permissions Permissions { get; }
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireUserPermissionsAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Gets the permissions required by this attribute.
+ /// </summary>
+ public Permissions Permissions { get; }
- /// <summary>
- /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
- /// </summary>
- public bool IgnoreDms { get; } = true;
+ /// <summary>
+ /// Gets or sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
+ /// </summary>
+ public bool IgnoreDms { get; } = true;
- /// <summary>
- /// Defines that usage of this command is restricted to members with specified permissions.
- /// </summary>
- /// <param name="permissions">Permissions required to execute this command.</param>
+ /// <summary>
+ /// Defines that usage of this command is restricted to members with specified permissions.
+ /// </summary>
+ /// <param name="permissions">Permissions required to execute this command.</param>
- /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
- public RequireUserPermissionsAttribute(Permissions permissions, bool ignoreDms = true)
- {
- this.Permissions = permissions;
- this.IgnoreDms = ignoreDms;
- }
+ /// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
+ public RequireUserPermissionsAttribute(Permissions permissions, bool ignoreDms = true)
+ {
+ this.Permissions = permissions;
+ this.IgnoreDms = ignoreDms;
+ }
- /// <summary>
- /// Executes the a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
- {
- if (ctx.Guild == null)
- return Task.FromResult(this.IgnoreDms);
+ /// <summary>
+ /// Executes the a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)
+ {
+ if (ctx.Guild == null)
+ return Task.FromResult(this.IgnoreDms);
- var usr = ctx.Member;
- if (usr == null)
- return Task.FromResult(false);
+ var usr = ctx.Member;
+ if (usr == null)
+ return Task.FromResult(false);
- if (usr.Id == ctx.Guild.OwnerId)
- return Task.FromResult(true);
+ if (usr.Id == ctx.Guild.OwnerId)
+ return Task.FromResult(true);
- var pusr = ctx.Channel.PermissionsFor(usr);
+ var pusr = ctx.Channel.PermissionsFor(usr);
- if ((pusr & Permissions.Administrator) != 0)
- return Task.FromResult(true);
+ if ((pusr & Permissions.Administrator) != 0)
+ return Task.FromResult(true);
- return (pusr & this.Permissions) == this.Permissions ? Task.FromResult(true) : Task.FromResult(false);
+ return (pusr & this.Permissions) == this.Permissions ? Task.FromResult(true) : Task.FromResult(false);
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Attributes/RequireWelcomeScreenAttribute.cs b/DisCatSharp.CommandsNext/Attributes/RequireWelcomeScreenAttribute.cs
index 4f8632536..e8c1fea51 100644
--- a/DisCatSharp.CommandsNext/Attributes/RequireWelcomeScreenAttribute.cs
+++ b/DisCatSharp.CommandsNext/Attributes/RequireWelcomeScreenAttribute.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext.Attributes;
-
-/// <summary>
-/// Defines that a command is only usable within a guild which has enabled the welcome screen.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
-public sealed class RequireWelcomeScreenAttribute : CheckBaseAttribute
+namespace DisCatSharp.CommandsNext.Attributes
{
/// <summary>
- /// Defines that this command is only usable within a guild which has enabled the welcome screen.
+ /// Defines that a command is only usable within a guild which has enabled the welcome screen.
/// </summary>
- public RequireWelcomeScreenAttribute()
- { }
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)]
+ public sealed class RequireWelcomeScreenAttribute : CheckBaseAttribute
+ {
+ /// <summary>
+ /// Defines that this command is only usable within a guild which has enabled the welcome screen.
+ /// </summary>
+ public RequireWelcomeScreenAttribute()
+ { }
- /// <summary>
- /// Executes a check.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="help">If true, help - returns true.</param>
- public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => Task.FromResult(ctx.Guild != null && ctx.Guild.HasWelcomeScreen);
+ /// <summary>
+ /// Executes a check.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="help">If true, help - returns true.</param>
+ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help) => Task.FromResult(ctx.Guild != null && ctx.Guild.HasWelcomeScreen);
+ }
}
diff --git a/DisCatSharp.CommandsNext/BaseCommandModule.cs b/DisCatSharp.CommandsNext/BaseCommandModule.cs
index b8edf4662..2bd57bdec 100644
--- a/DisCatSharp.CommandsNext/BaseCommandModule.cs
+++ b/DisCatSharp.CommandsNext/BaseCommandModule.cs
@@ -1,47 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Represents a base class for all command modules.
-/// </summary>
-public abstract class BaseCommandModule
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Called before a command in the implementing module is executed.
+ /// Represents a base class for all command modules.
/// </summary>
- /// <param name="ctx">Context in which the method is being executed.</param>
- /// <returns></returns>
- public virtual Task BeforeExecutionAsync(CommandContext ctx)
- => Task.Delay(0);
+ public abstract class BaseCommandModule
+ {
+ /// <summary>
+ /// Called before a command in the implementing module is executed.
+ /// </summary>
+ /// <param name="ctx">Context in which the method is being executed.</param>
+ /// <returns></returns>
+ public virtual Task BeforeExecutionAsync(CommandContext ctx)
+ => Task.Delay(0);
- /// <summary>
- /// Called after a command in the implementing module is successfully executed.
- /// </summary>
- /// <param name="ctx">Context in which the method is being executed.</param>
- /// <returns></returns>
- public virtual Task AfterExecutionAsync(CommandContext ctx)
- => Task.Delay(0);
+ /// <summary>
+ /// Called after a command in the implementing module is successfully executed.
+ /// </summary>
+ /// <param name="ctx">Context in which the method is being executed.</param>
+ /// <returns></returns>
+ public virtual Task AfterExecutionAsync(CommandContext ctx)
+ => Task.Delay(0);
+ }
}
diff --git a/DisCatSharp.CommandsNext/CommandsNextConfiguration.cs b/DisCatSharp.CommandsNext/CommandsNextConfiguration.cs
index e7cde5584..d6d162e5b 100644
--- a/DisCatSharp.CommandsNext/CommandsNextConfiguration.cs
+++ b/DisCatSharp.CommandsNext/CommandsNextConfiguration.cs
@@ -1,160 +1,161 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
using DisCatSharp.CommandsNext.Attributes;
using DisCatSharp.Entities;
using Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// <para>Represents a delegate for a function that takes a message, and returns the position of the start of command invocation in the message. It has to return -1 if prefix is not present.</para>
-/// <para>
-/// It is recommended that helper methods <see cref="CommandsNextUtilities.GetStringPrefixLength(DiscordMessage, string, StringComparison)"/> and <see cref="CommandsNextUtilities.GetMentionPrefixLength(DiscordMessage, DiscordUser)"/>
-/// be used internally for checking. Their output can be passed through.
-/// </para>
-/// </summary>
-/// <param name="msg">Message to check for prefix.</param>
-/// <returns>Position of the command invocation or -1 if not present.</returns>
-public delegate Task<int> PrefixResolverDelegate(DiscordMessage msg);
-
-/// <summary>
-/// Represents a configuration for <see cref="CommandsNextExtension"/>.
-/// </summary>
-public sealed class CommandsNextConfiguration
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// <para>Sets the string prefixes used for commands.</para>
- /// <para>Defaults to no value (disabled).</para>
- /// </summary>
- public IEnumerable<string> StringPrefixes { internal get; set; }
-
- /// <summary>
- /// <para>Sets the custom prefix resolver used for commands.</para>
- /// <para>Defaults to none (disabled).</para>
- /// </summary>
- public PrefixResolverDelegate PrefixResolver { internal get; set; }
-
- /// <summary>
- /// <para>Sets whether to allow mentioning the bot to be used as command prefix.</para>
- /// <para>Defaults to true.</para>
- /// </summary>
- public bool EnableMentionPrefix { internal get; set; } = true;
-
- /// <summary>
- /// <para>Sets whether strings should be matched in a case-sensitive manner.</para>
- /// <para>This switch affects the behaviour of default prefix resolver, command searching, and argument conversion.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool CaseSensitive { internal get; set; }
-
- /// <summary>
- /// <para>Sets whether to enable default help command.</para>
- /// <para>Disabling this will allow you to make your own help command.</para>
+ /// <para>Represents a delegate for a function that takes a message, and returns the position of the start of command invocation in the message. It has to return -1 if prefix is not present.</para>
/// <para>
- /// Modifying default help can be achieved via custom help formatters (see <see cref="DisCatSharp.CommandsNext.Converters.BaseHelpFormatter"/> and <see cref="CommandsNextExtension.SetHelpFormatter{T}()"/> for more details).
- /// It is recommended to use help formatter instead of disabling help.
+ /// It is recommended that helper methods <see cref="CommandsNextUtilities.GetStringPrefixLength(DiscordMessage, string, StringComparison)"/> and <see cref="CommandsNextUtilities.GetMentionPrefixLength(DiscordMessage, DiscordUser)"/>
+ /// be used internally for checking. Their output can be passed through.
/// </para>
- /// <para>Defaults to true.</para>
- /// </summary>
- public bool EnableDefaultHelp { internal get; set; } = true;
-
- /// <summary>
- /// <para>Controls whether the default help will be sent via DMs or not.</para>
- /// <para>Enabling this will make the bot respond with help via direct messages.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool DmHelp { internal get; set; }
-
- /// <summary>
- /// <para>Sets the default pre-execution checks for the built-in help command.</para>
- /// <para>Only applicable if default help is enabled.</para>
- /// <para>Defaults to null.</para>
- /// </summary>
- public IEnumerable<CheckBaseAttribute> DefaultHelpChecks { internal get; set; }
-
- /// <summary>
- /// <para>Sets whether commands sent via direct messages should be processed.</para>
- /// <para>Defaults to true.</para>
- /// </summary>
- public bool EnableDms { internal get; set; } = true;
-
- /// <summary>
- /// <para>Sets the service provider for this CommandsNext instance.</para>
- /// <para>Objects in this provider are used when instantiating command modules. This allows passing data around without resorting to static members.</para>
- /// <para>Defaults to an empty service provider.</para>
/// </summary>
- public IServiceProvider ServiceProvider { internal get; set; }
-
- /// <summary>
- /// <para>Gets whether any extra arguments passed to commands should be ignored or not. If this is set to false, extra arguments will throw, otherwise they will be ignored.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool IgnoreExtraArguments { internal get; set; }
-
- /// <summary>
- /// <para>Gets or sets whether to automatically enable handling commands.</para>
- /// <para>If this is set to false, you will need to manually handle each incoming message and pass it to CommandsNext.</para>
- /// <para>Defaults to true.</para>
- /// </summary>
- public bool UseDefaultCommandHandler { internal get; set; } = true;
-
- /// <summary>
- /// Creates a new instance of <see cref="CommandsNextConfiguration"/>.
- /// </summary>
- public CommandsNextConfiguration() { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="CommandsNextConfiguration"/> class.
- /// </summary>
- /// <param name="provider">The service provider.</param>
- [ActivatorUtilitiesConstructor]
- public CommandsNextConfiguration(IServiceProvider provider)
- {
- this.ServiceProvider = provider;
- }
+ /// <param name="msg">Message to check for prefix.</param>
+ /// <returns>Position of the command invocation or -1 if not present.</returns>
+ public delegate Task<int> PrefixResolverDelegate(DiscordMessage msg);
/// <summary>
- /// Creates a new instance of <see cref="CommandsNextConfiguration"/>, copying the properties of another configuration.
+ /// Represents a configuration for <see cref="CommandsNextExtension"/>.
/// </summary>
- /// <param name="other">Configuration the properties of which are to be copied.</param>
- public CommandsNextConfiguration(CommandsNextConfiguration other)
+ public sealed class CommandsNextConfiguration
{
- this.CaseSensitive = other.CaseSensitive;
- this.PrefixResolver = other.PrefixResolver;
- this.DefaultHelpChecks = other.DefaultHelpChecks;
- this.EnableDefaultHelp = other.EnableDefaultHelp;
- this.EnableDms = other.EnableDms;
- this.EnableMentionPrefix = other.EnableMentionPrefix;
- this.IgnoreExtraArguments = other.IgnoreExtraArguments;
- this.UseDefaultCommandHandler = other.UseDefaultCommandHandler;
- this.ServiceProvider = other.ServiceProvider;
- this.StringPrefixes = other.StringPrefixes?.ToArray();
- this.DmHelp = other.DmHelp;
+ /// <summary>
+ /// <para>Sets the string prefixes used for commands.</para>
+ /// <para>Defaults to no value (disabled).</para>
+ /// </summary>
+ public IEnumerable<string> StringPrefixes { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets the custom prefix resolver used for commands.</para>
+ /// <para>Defaults to none (disabled).</para>
+ /// </summary>
+ public PrefixResolverDelegate PrefixResolver { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets whether to allow mentioning the bot to be used as command prefix.</para>
+ /// <para>Defaults to true.</para>
+ /// </summary>
+ public bool EnableMentionPrefix { internal get; set; } = true;
+
+ /// <summary>
+ /// <para>Sets whether strings should be matched in a case-sensitive manner.</para>
+ /// <para>This switch affects the behaviour of default prefix resolver, command searching, and argument conversion.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool CaseSensitive { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets whether to enable default help command.</para>
+ /// <para>Disabling this will allow you to make your own help command.</para>
+ /// <para>
+ /// Modifying default help can be achieved via custom help formatters (see <see cref="DisCatSharp.CommandsNext.Converters.BaseHelpFormatter"/> and <see cref="CommandsNextExtension.SetHelpFormatter{T}()"/> for more details).
+ /// It is recommended to use help formatter instead of disabling help.
+ /// </para>
+ /// <para>Defaults to true.</para>
+ /// </summary>
+ public bool EnableDefaultHelp { internal get; set; } = true;
+
+ /// <summary>
+ /// <para>Controls whether the default help will be sent via DMs or not.</para>
+ /// <para>Enabling this will make the bot respond with help via direct messages.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool DmHelp { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets the default pre-execution checks for the built-in help command.</para>
+ /// <para>Only applicable if default help is enabled.</para>
+ /// <para>Defaults to null.</para>
+ /// </summary>
+ public IEnumerable<CheckBaseAttribute> DefaultHelpChecks { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets whether commands sent via direct messages should be processed.</para>
+ /// <para>Defaults to true.</para>
+ /// </summary>
+ public bool EnableDms { internal get; set; } = true;
+
+ /// <summary>
+ /// <para>Sets the service provider for this CommandsNext instance.</para>
+ /// <para>Objects in this provider are used when instantiating command modules. This allows passing data around without resorting to static members.</para>
+ /// <para>Defaults to an empty service provider.</para>
+ /// </summary>
+ public IServiceProvider ServiceProvider { internal get; set; }
+
+ /// <summary>
+ /// <para>Gets whether any extra arguments passed to commands should be ignored or not. If this is set to false, extra arguments will throw, otherwise they will be ignored.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool IgnoreExtraArguments { internal get; set; }
+
+ /// <summary>
+ /// <para>Gets or sets whether to automatically enable handling commands.</para>
+ /// <para>If this is set to false, you will need to manually handle each incoming message and pass it to CommandsNext.</para>
+ /// <para>Defaults to true.</para>
+ /// </summary>
+ public bool UseDefaultCommandHandler { internal get; set; } = true;
+
+ /// <summary>
+ /// Creates a new instance of <see cref="CommandsNextConfiguration"/>.
+ /// </summary>
+ public CommandsNextConfiguration() { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CommandsNextConfiguration"/> class.
+ /// </summary>
+ /// <param name="provider">The service provider.</param>
+ [ActivatorUtilitiesConstructor]
+ public CommandsNextConfiguration(IServiceProvider provider)
+ {
+ this.ServiceProvider = provider;
+ }
+
+ /// <summary>
+ /// Creates a new instance of <see cref="CommandsNextConfiguration"/>, copying the properties of another configuration.
+ /// </summary>
+ /// <param name="other">Configuration the properties of which are to be copied.</param>
+ public CommandsNextConfiguration(CommandsNextConfiguration other)
+ {
+ this.CaseSensitive = other.CaseSensitive;
+ this.PrefixResolver = other.PrefixResolver;
+ this.DefaultHelpChecks = other.DefaultHelpChecks;
+ this.EnableDefaultHelp = other.EnableDefaultHelp;
+ this.EnableDms = other.EnableDms;
+ this.EnableMentionPrefix = other.EnableMentionPrefix;
+ this.IgnoreExtraArguments = other.IgnoreExtraArguments;
+ this.UseDefaultCommandHandler = other.UseDefaultCommandHandler;
+ this.ServiceProvider = other.ServiceProvider;
+ this.StringPrefixes = other.StringPrefixes?.ToArray();
+ this.DmHelp = other.DmHelp;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/CommandsNextEvents.cs b/DisCatSharp.CommandsNext/CommandsNextEvents.cs
index 485c137a2..5ec7f3c72 100644
--- a/DisCatSharp.CommandsNext/CommandsNextEvents.cs
+++ b/DisCatSharp.CommandsNext/CommandsNextEvents.cs
@@ -1,41 +1,42 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Logging;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Contains well-defined event IDs used by CommandsNext.
-/// </summary>
-public static class CommandsNextEvents
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Miscellaneous events, that do not fit in any other category.
+ /// Contains well-defined event IDs used by CommandsNext.
/// </summary>
- internal static EventId Misc { get; } = new(200, "CommandsNext");
+ public static class CommandsNextEvents
+ {
+ /// <summary>
+ /// Miscellaneous events, that do not fit in any other category.
+ /// </summary>
+ internal static EventId Misc { get; } = new(200, "CommandsNext");
- /// <summary>
- /// Events pertaining to Gateway Intents. Typically diagnostic information.
- /// </summary>
- internal static EventId Intents { get; } = new(201, nameof(Intents));
+ /// <summary>
+ /// Events pertaining to Gateway Intents. Typically diagnostic information.
+ /// </summary>
+ internal static EventId Intents { get; } = new(201, nameof(Intents));
+ }
}
diff --git a/DisCatSharp.CommandsNext/CommandsNextExtension.cs b/DisCatSharp.CommandsNext/CommandsNextExtension.cs
index 9115a1503..c1e957735 100644
--- a/DisCatSharp.CommandsNext/CommandsNextExtension.cs
+++ b/DisCatSharp.CommandsNext/CommandsNextExtension.cs
@@ -1,1091 +1,1092 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Reflection;
using System.Threading.Tasks;
using DisCatSharp.CommandsNext.Attributes;
using DisCatSharp.CommandsNext.Builders;
using DisCatSharp.CommandsNext.Converters;
using DisCatSharp.CommandsNext.Entities;
using DisCatSharp.CommandsNext.Exceptions;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// This is the class which handles command registration, management, and execution.
-/// </summary>
-public class CommandsNextExtension : BaseExtension
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets the config.
- /// </summary>
- private readonly CommandsNextConfiguration _config;
-
- /// <summary>
- /// Gets the help formatter.
+ /// This is the class which handles command registration, management, and execution.
/// </summary>
- private readonly HelpFormatterFactory _helpFormatter;
+ public class CommandsNextExtension : BaseExtension
+ {
+ /// <summary>
+ /// Gets the config.
+ /// </summary>
+ private readonly CommandsNextConfiguration _config;
- /// <summary>
- /// Gets the convert generic.
- /// </summary>
- private readonly MethodInfo _convertGeneric;
+ /// <summary>
+ /// Gets the help formatter.
+ /// </summary>
+ private readonly HelpFormatterFactory _helpFormatter;
- /// <summary>
- /// Gets the user friendly type names.
- /// </summary>
- private readonly Dictionary<Type, string> _userFriendlyTypeNames;
- /// <summary>
- /// Gets the argument converters.
- /// </summary>
- internal Dictionary<Type, IArgumentConverter> ArgumentConverters { get; }
+ /// <summary>
+ /// Gets the convert generic.
+ /// </summary>
+ private readonly MethodInfo _convertGeneric;
- /// <summary>
- /// Gets the service provider this CommandsNext module was configured with.
- /// </summary>
- public IServiceProvider Services
- => this._config.ServiceProvider;
+ /// <summary>
+ /// Gets the user friendly type names.
+ /// </summary>
+ private readonly Dictionary<Type, string> _userFriendlyTypeNames;
+ /// <summary>
+ /// Gets the argument converters.
+ /// </summary>
+ internal Dictionary<Type, IArgumentConverter> ArgumentConverters { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="CommandsNextExtension"/> class.
- /// </summary>
- /// <param name="cfg">The cfg.</param>
- internal CommandsNextExtension(CommandsNextConfiguration cfg)
- {
- this._config = new CommandsNextConfiguration(cfg);
- this._topLevelCommands = new Dictionary<string, Command>();
- this._registeredCommandsLazy = new Lazy<IReadOnlyDictionary<string, Command>>(() => new ReadOnlyDictionary<string, Command>(this._topLevelCommands));
- this._helpFormatter = new HelpFormatterFactory();
- this._helpFormatter.SetFormatterType<DefaultHelpFormatter>();
+ /// <summary>
+ /// Gets the service provider this CommandsNext module was configured with.
+ /// </summary>
+ public IServiceProvider Services
+ => this._config.ServiceProvider;
- this.ArgumentConverters = new Dictionary<Type, IArgumentConverter>
- {
- [typeof(string)] = new StringConverter(),
- [typeof(bool)] = new BoolConverter(),
- [typeof(sbyte)] = new Int8Converter(),
- [typeof(byte)] = new Uint8Converter(),
- [typeof(short)] = new Int16Converter(),
- [typeof(ushort)] = new Uint16Converter(),
- [typeof(int)] = new Int32Converter(),
- [typeof(uint)] = new Uint32Converter(),
- [typeof(long)] = new Int64Converter(),
- [typeof(ulong)] = new Uint64Converter(),
- [typeof(float)] = new Float32Converter(),
- [typeof(double)] = new Float64Converter(),
- [typeof(decimal)] = new Float128Converter(),
- [typeof(DateTime)] = new DateTimeConverter(),
- [typeof(DateTimeOffset)] = new DateTimeOffsetConverter(),
- [typeof(TimeSpan)] = new TimeSpanConverter(),
- [typeof(Uri)] = new UriConverter(),
- [typeof(DiscordUser)] = new DiscordUserConverter(),
- [typeof(DiscordMember)] = new DiscordMemberConverter(),
- [typeof(DiscordRole)] = new DiscordRoleConverter(),
- [typeof(DiscordChannel)] = new DiscordChannelConverter(),
- [typeof(DiscordGuild)] = new DiscordGuildConverter(),
- [typeof(DiscordMessage)] = new DiscordMessageConverter(),
- [typeof(DiscordEmoji)] = new DiscordEmojiConverter(),
- [typeof(DiscordThreadChannel)] = new DiscordThreadChannelConverter(),
- [typeof(DiscordInvite)] = new DiscordInviteConverter(),
- [typeof(DiscordColor)] = new DiscordColorConverter(),
- [typeof(DiscordScheduledEvent)] = new DiscordScheduledEventConverter(),
- };
-
- this._userFriendlyTypeNames = new Dictionary<Type, string>()
- {
- [typeof(string)] = "string",
- [typeof(bool)] = "boolean",
- [typeof(sbyte)] = "signed byte",
- [typeof(byte)] = "byte",
- [typeof(short)] = "short",
- [typeof(ushort)] = "unsigned short",
- [typeof(int)] = "int",
- [typeof(uint)] = "unsigned int",
- [typeof(long)] = "long",
- [typeof(ulong)] = "unsigned long",
- [typeof(float)] = "float",
- [typeof(double)] = "double",
- [typeof(decimal)] = "decimal",
- [typeof(DateTime)] = "date and time",
- [typeof(DateTimeOffset)] = "date and time",
- [typeof(TimeSpan)] = "time span",
- [typeof(Uri)] = "URL",
- [typeof(DiscordUser)] = "user",
- [typeof(DiscordMember)] = "member",
- [typeof(DiscordRole)] = "role",
- [typeof(DiscordChannel)] = "channel",
- [typeof(DiscordGuild)] = "guild",
- [typeof(DiscordMessage)] = "message",
- [typeof(DiscordEmoji)] = "emoji",
- [typeof(DiscordThreadChannel)] = "thread",
- [typeof(DiscordInvite)] = "invite",
- [typeof(DiscordColor)] = "color",
- [typeof(DiscordScheduledEvent)] = "event"
- };
-
- var ncvt = typeof(NullableConverter<>);
- var nt = typeof(Nullable<>);
- var cvts = this.ArgumentConverters.Keys.ToArray();
- foreach (var xt in cvts)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CommandsNextExtension"/> class.
+ /// </summary>
+ /// <param name="cfg">The cfg.</param>
+ internal CommandsNextExtension(CommandsNextConfiguration cfg)
{
- var xti = xt.GetTypeInfo();
- if (!xti.IsValueType)
- continue;
-
- var xcvt = ncvt.MakeGenericType(xt);
- var xnt = nt.MakeGenericType(xt);
- if (this.ArgumentConverters.ContainsKey(xcvt))
- continue;
-
- var xcv = Activator.CreateInstance(xcvt) as IArgumentConverter;
- this.ArgumentConverters[xnt] = xcv;
- this._userFriendlyTypeNames[xnt] = this._userFriendlyTypeNames[xt];
- }
+ this._config = new CommandsNextConfiguration(cfg);
+ this._topLevelCommands = new Dictionary<string, Command>();
+ this._registeredCommandsLazy = new Lazy<IReadOnlyDictionary<string, Command>>(() => new ReadOnlyDictionary<string, Command>(this._topLevelCommands));
+ this._helpFormatter = new HelpFormatterFactory();
+ this._helpFormatter.SetFormatterType<DefaultHelpFormatter>();
- var t = this.GetType();
- var ms = t.GetTypeInfo().DeclaredMethods;
- var m = ms.FirstOrDefault(xm => xm.Name == "ConvertArgumentToObj" && xm.ContainsGenericParameters && !xm.IsStatic && xm.IsPrivate);
- this._convertGeneric = m;
- }
+ this.ArgumentConverters = new Dictionary<Type, IArgumentConverter>
+ {
+ [typeof(string)] = new StringConverter(),
+ [typeof(bool)] = new BoolConverter(),
+ [typeof(sbyte)] = new Int8Converter(),
+ [typeof(byte)] = new Uint8Converter(),
+ [typeof(short)] = new Int16Converter(),
+ [typeof(ushort)] = new Uint16Converter(),
+ [typeof(int)] = new Int32Converter(),
+ [typeof(uint)] = new Uint32Converter(),
+ [typeof(long)] = new Int64Converter(),
+ [typeof(ulong)] = new Uint64Converter(),
+ [typeof(float)] = new Float32Converter(),
+ [typeof(double)] = new Float64Converter(),
+ [typeof(decimal)] = new Float128Converter(),
+ [typeof(DateTime)] = new DateTimeConverter(),
+ [typeof(DateTimeOffset)] = new DateTimeOffsetConverter(),
+ [typeof(TimeSpan)] = new TimeSpanConverter(),
+ [typeof(Uri)] = new UriConverter(),
+ [typeof(DiscordUser)] = new DiscordUserConverter(),
+ [typeof(DiscordMember)] = new DiscordMemberConverter(),
+ [typeof(DiscordRole)] = new DiscordRoleConverter(),
+ [typeof(DiscordChannel)] = new DiscordChannelConverter(),
+ [typeof(DiscordGuild)] = new DiscordGuildConverter(),
+ [typeof(DiscordMessage)] = new DiscordMessageConverter(),
+ [typeof(DiscordEmoji)] = new DiscordEmojiConverter(),
+ [typeof(DiscordThreadChannel)] = new DiscordThreadChannelConverter(),
+ [typeof(DiscordInvite)] = new DiscordInviteConverter(),
+ [typeof(DiscordColor)] = new DiscordColorConverter(),
+ [typeof(DiscordScheduledEvent)] = new DiscordScheduledEventConverter(),
+ };
+
+ this._userFriendlyTypeNames = new Dictionary<Type, string>()
+ {
+ [typeof(string)] = "string",
+ [typeof(bool)] = "boolean",
+ [typeof(sbyte)] = "signed byte",
+ [typeof(byte)] = "byte",
+ [typeof(short)] = "short",
+ [typeof(ushort)] = "unsigned short",
+ [typeof(int)] = "int",
+ [typeof(uint)] = "unsigned int",
+ [typeof(long)] = "long",
+ [typeof(ulong)] = "unsigned long",
+ [typeof(float)] = "float",
+ [typeof(double)] = "double",
+ [typeof(decimal)] = "decimal",
+ [typeof(DateTime)] = "date and time",
+ [typeof(DateTimeOffset)] = "date and time",
+ [typeof(TimeSpan)] = "time span",
+ [typeof(Uri)] = "URL",
+ [typeof(DiscordUser)] = "user",
+ [typeof(DiscordMember)] = "member",
+ [typeof(DiscordRole)] = "role",
+ [typeof(DiscordChannel)] = "channel",
+ [typeof(DiscordGuild)] = "guild",
+ [typeof(DiscordMessage)] = "message",
+ [typeof(DiscordEmoji)] = "emoji",
+ [typeof(DiscordThreadChannel)] = "thread",
+ [typeof(DiscordInvite)] = "invite",
+ [typeof(DiscordColor)] = "color",
+ [typeof(DiscordScheduledEvent)] = "event"
+ };
+
+ var ncvt = typeof(NullableConverter<>);
+ var nt = typeof(Nullable<>);
+ var cvts = this.ArgumentConverters.Keys.ToArray();
+ foreach (var xt in cvts)
+ {
+ var xti = xt.GetTypeInfo();
+ if (!xti.IsValueType)
+ continue;
+
+ var xcvt = ncvt.MakeGenericType(xt);
+ var xnt = nt.MakeGenericType(xt);
+ if (this.ArgumentConverters.ContainsKey(xcvt))
+ continue;
+
+ var xcv = Activator.CreateInstance(xcvt) as IArgumentConverter;
+ this.ArgumentConverters[xnt] = xcv;
+ this._userFriendlyTypeNames[xnt] = this._userFriendlyTypeNames[xt];
+ }
- /// <summary>
- /// Sets the help formatter to use with the default help command.
- /// </summary>
- /// <typeparam name="T">Type of the formatter to use.</typeparam>
- public void SetHelpFormatter<T>() where T : BaseHelpFormatter => this._helpFormatter.SetFormatterType<T>();
+ var t = this.GetType();
+ var ms = t.GetTypeInfo().DeclaredMethods;
+ var m = ms.FirstOrDefault(xm => xm.Name == "ConvertArgumentToObj" && xm.ContainsGenericParameters && !xm.IsStatic && xm.IsPrivate);
+ this._convertGeneric = m;
+ }
- #region DiscordClient Registration
- /// <summary>
- /// DO NOT USE THIS MANUALLY.
- /// </summary>
- /// <param name="client">DO NOT USE THIS MANUALLY.</param>
- /// <exception cref="System.InvalidOperationException"/>
- protected internal override void Setup(DiscordClient client)
- {
- if (this.Client != null)
- throw new InvalidOperationException("What did I tell you?");
+ /// <summary>
+ /// Sets the help formatter to use with the default help command.
+ /// </summary>
+ /// <typeparam name="T">Type of the formatter to use.</typeparam>
+ public void SetHelpFormatter<T>() where T : BaseHelpFormatter => this._helpFormatter.SetFormatterType<T>();
- this.Client = client;
+ #region DiscordClient Registration
+ /// <summary>
+ /// DO NOT USE THIS MANUALLY.
+ /// </summary>
+ /// <param name="client">DO NOT USE THIS MANUALLY.</param>
+ /// <exception cref="System.InvalidOperationException"/>
+ protected internal override void Setup(DiscordClient client)
+ {
+ if (this.Client != null)
+ throw new InvalidOperationException("What did I tell you?");
- this._executed = new AsyncEvent<CommandsNextExtension, CommandExecutionEventArgs>("COMMAND_EXECUTED", TimeSpan.Zero, this.Client.EventErrorHandler);
- this._error = new AsyncEvent<CommandsNextExtension, CommandErrorEventArgs>("COMMAND_ERRORED", TimeSpan.Zero, this.Client.EventErrorHandler);
+ this.Client = client;
- if (this._config.UseDefaultCommandHandler)
- this.Client.MessageCreated += this.HandleCommandsAsync;
- else
- this.Client.Logger.LogWarning(CommandsNextEvents.Misc, "Not attaching default command handler - if this is intentional, you can ignore this message");
+ this._executed = new AsyncEvent<CommandsNextExtension, CommandExecutionEventArgs>("COMMAND_EXECUTED", TimeSpan.Zero, this.Client.EventErrorHandler);
+ this._error = new AsyncEvent<CommandsNextExtension, CommandErrorEventArgs>("COMMAND_ERRORED", TimeSpan.Zero, this.Client.EventErrorHandler);
- if (this._config.EnableDefaultHelp)
- {
- this.RegisterCommands(typeof(DefaultHelpModule), null, null, out var tcmds);
+ if (this._config.UseDefaultCommandHandler)
+ this.Client.MessageCreated += this.HandleCommandsAsync;
+ else
+ this.Client.Logger.LogWarning(CommandsNextEvents.Misc, "Not attaching default command handler - if this is intentional, you can ignore this message");
- if (this._config.DefaultHelpChecks != null)
+ if (this._config.EnableDefaultHelp)
{
- var checks = this._config.DefaultHelpChecks.ToArray();
+ this.RegisterCommands(typeof(DefaultHelpModule), null, null, out var tcmds);
+
+ if (this._config.DefaultHelpChecks != null)
+ {
+ var checks = this._config.DefaultHelpChecks.ToArray();
+
+ for (var i = 0; i < tcmds.Count; i++)
+ tcmds[i].WithExecutionChecks(checks);
+ }
- for (var i = 0; i < tcmds.Count; i++)
- tcmds[i].WithExecutionChecks(checks);
+ if (tcmds != null)
+ foreach (var xc in tcmds)
+ this.AddToCommandDictionary(xc.Build(null));
}
- if (tcmds != null)
- foreach (var xc in tcmds)
- this.AddToCommandDictionary(xc.Build(null));
}
+ #endregion
- }
- #endregion
+ #region Command Handling
+ /// <summary>
+ /// Handles the commands async.
+ /// </summary>
+ /// <param name="sender">The sender.</param>
+ /// <param name="e">The e.</param>
+ /// <returns>A Task.</returns>
+ private async Task HandleCommandsAsync(DiscordClient sender, MessageCreateEventArgs e)
+ {
+ if (e.Author.IsBot) // bad bot
+ return;
- #region Command Handling
- /// <summary>
- /// Handles the commands async.
- /// </summary>
- /// <param name="sender">The sender.</param>
- /// <param name="e">The e.</param>
- /// <returns>A Task.</returns>
- private async Task HandleCommandsAsync(DiscordClient sender, MessageCreateEventArgs e)
- {
- if (e.Author.IsBot) // bad bot
- return;
+ if (!this._config.EnableDms && e.Channel.IsPrivate)
+ return;
- if (!this._config.EnableDms && e.Channel.IsPrivate)
- return;
+ var mpos = -1;
+ if (this._config.EnableMentionPrefix)
+ mpos = e.Message.GetMentionPrefixLength(this.Client.CurrentUser);
- var mpos = -1;
- if (this._config.EnableMentionPrefix)
- mpos = e.Message.GetMentionPrefixLength(this.Client.CurrentUser);
+ if (this._config.StringPrefixes?.Any() == true)
+ foreach (var pfix in this._config.StringPrefixes)
+ if (mpos == -1 && !string.IsNullOrWhiteSpace(pfix))
+ mpos = e.Message.GetStringPrefixLength(pfix, this._config.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
- if (this._config.StringPrefixes?.Any() == true)
- foreach (var pfix in this._config.StringPrefixes)
- if (mpos == -1 && !string.IsNullOrWhiteSpace(pfix))
- mpos = e.Message.GetStringPrefixLength(pfix, this._config.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ if (mpos == -1 && this._config.PrefixResolver != null)
+ mpos = await this._config.PrefixResolver(e.Message).ConfigureAwait(false);
- if (mpos == -1 && this._config.PrefixResolver != null)
- mpos = await this._config.PrefixResolver(e.Message).ConfigureAwait(false);
+ if (mpos == -1)
+ return;
- if (mpos == -1)
- return;
+ var pfx = e.Message.Content[..mpos];
+ var cnt = e.Message.Content[mpos..];
- var pfx = e.Message.Content[..mpos];
- var cnt = e.Message.Content[mpos..];
+ var __ = 0;
+ var fname = cnt.ExtractNextArgument(ref __);
- var __ = 0;
- var fname = cnt.ExtractNextArgument(ref __);
+ var cmd = this.FindCommand(cnt, out var args);
+ var ctx = this.CreateContext(e.Message, pfx, cmd, args);
+ if (cmd == null)
+ {
+ await this._error.InvokeAsync(this, new CommandErrorEventArgs(this.Client.ServiceProvider) { Context = ctx, Exception = new CommandNotFoundException(fname) }).ConfigureAwait(false);
+ return;
+ }
- var cmd = this.FindCommand(cnt, out var args);
- var ctx = this.CreateContext(e.Message, pfx, cmd, args);
- if (cmd == null)
- {
- await this._error.InvokeAsync(this, new CommandErrorEventArgs(this.Client.ServiceProvider) { Context = ctx, Exception = new CommandNotFoundException(fname) }).ConfigureAwait(false);
- return;
+ _ = Task.Run(async () => await this.ExecuteCommandAsync(ctx).ConfigureAwait(false));
}
- _ = Task.Run(async () => await this.ExecuteCommandAsync(ctx).ConfigureAwait(false));
- }
+ /// <summary>
+ /// Finds a specified command by its qualified name, then separates arguments.
+ /// </summary>
+ /// <param name="commandString">Qualified name of the command, optionally with arguments.</param>
+ /// <param name="rawArguments">Separated arguments.</param>
+ /// <returns>Found command or null if none was found.</returns>
+ public Command FindCommand(string commandString, out string rawArguments)
+ {
+ rawArguments = null;
- /// <summary>
- /// Finds a specified command by its qualified name, then separates arguments.
- /// </summary>
- /// <param name="commandString">Qualified name of the command, optionally with arguments.</param>
- /// <param name="rawArguments">Separated arguments.</param>
- /// <returns>Found command or null if none was found.</returns>
- public Command FindCommand(string commandString, out string rawArguments)
- {
- rawArguments = null;
+ var ignoreCase = !this._config.CaseSensitive;
+ var pos = 0;
+ var next = commandString.ExtractNextArgument(ref pos);
+ if (next == null)
+ return null;
- var ignoreCase = !this._config.CaseSensitive;
- var pos = 0;
- var next = commandString.ExtractNextArgument(ref pos);
- if (next == null)
- return null;
+ if (!this.RegisteredCommands.TryGetValue(next, out var cmd))
+ {
+ if (!ignoreCase)
+ return null;
- if (!this.RegisteredCommands.TryGetValue(next, out var cmd))
- {
- if (!ignoreCase)
- return null;
+ next = next.ToLowerInvariant();
+ var cmdKvp = this.RegisteredCommands.FirstOrDefault(x => x.Key.ToLowerInvariant() == next);
+ if (cmdKvp.Value == null)
+ return null;
- next = next.ToLowerInvariant();
- var cmdKvp = this.RegisteredCommands.FirstOrDefault(x => x.Key.ToLowerInvariant() == next);
- if (cmdKvp.Value == null)
- return null;
+ cmd = cmdKvp.Value;
+ }
- cmd = cmdKvp.Value;
- }
+ if (cmd is not CommandGroup)
+ {
+ rawArguments = commandString[pos..].Trim();
+ return cmd;
+ }
+
+ while (cmd is CommandGroup)
+ {
+ var cm2 = cmd as CommandGroup;
+ var oldPos = pos;
+ next = commandString.ExtractNextArgument(ref pos);
+ if (next == null)
+ break;
+
+ if (ignoreCase)
+ {
+ next = next.ToLowerInvariant();
+ cmd = cm2.Children.FirstOrDefault(x => x.Name.ToLowerInvariant() == next || x.Aliases?.Any(xx => xx.ToLowerInvariant() == next) == true);
+ }
+ else
+ {
+ cmd = cm2.Children.FirstOrDefault(x => x.Name == next || x.Aliases?.Contains(next) == true);
+ }
+
+ if (cmd == null)
+ {
+ cmd = cm2;
+ pos = oldPos;
+ break;
+ }
+ }
- if (cmd is not CommandGroup)
- {
rawArguments = commandString[pos..].Trim();
return cmd;
}
- while (cmd is CommandGroup)
+ /// <summary>
+ /// Creates a command execution context from specified arguments.
+ /// </summary>
+ /// <param name="msg">Message to use for context.</param>
+ /// <param name="prefix">Command prefix, used to execute commands.</param>
+ /// <param name="cmd">Command to execute.</param>
+ /// <param name="rawArguments">Raw arguments to pass to command.</param>
+ /// <returns>Created command execution context.</returns>
+ public CommandContext CreateContext(DiscordMessage msg, string prefix, Command cmd, string rawArguments = null)
{
- var cm2 = cmd as CommandGroup;
- var oldPos = pos;
- next = commandString.ExtractNextArgument(ref pos);
- if (next == null)
- break;
+ var ctx = new CommandContext
+ {
+ Client = this.Client,
+ Command = cmd,
+ Message = msg,
+ Config = this._config,
+ RawArgumentString = rawArguments ?? "",
+ Prefix = prefix,
+ CommandsNext = this,
+ Services = this.Services
+ };
+
+ if (cmd != null && (cmd.Module is TransientCommandModule || cmd.Module == null))
+ {
+ var scope = ctx.Services.CreateScope();
+ ctx.ServiceScopeContext = new CommandContext.ServiceContext(ctx.Services, scope);
+ ctx.Services = scope.ServiceProvider;
+ }
- if (ignoreCase)
+ return ctx;
+ }
+
+ /// <summary>
+ /// Executes specified command from given context.
+ /// </summary>
+ /// <param name="ctx">Context to execute command from.</param>
+ /// <returns></returns>
+ public async Task ExecuteCommandAsync(CommandContext ctx)
+ {
+ try
{
- next = next.ToLowerInvariant();
- cmd = cm2.Children.FirstOrDefault(x => x.Name.ToLowerInvariant() == next || x.Aliases?.Any(xx => xx.ToLowerInvariant() == next) == true);
+ var cmd = ctx.Command;
+ await this.RunAllChecksAsync(cmd, ctx).ConfigureAwait(false);
+
+ var res = await cmd.ExecuteAsync(ctx).ConfigureAwait(false);
+
+ if (res.IsSuccessful)
+ await this._executed.InvokeAsync(this, new CommandExecutionEventArgs(this.Client.ServiceProvider) { Context = res.Context }).ConfigureAwait(false);
+ else
+ await this._error.InvokeAsync(this, new CommandErrorEventArgs(this.Client.ServiceProvider) { Context = res.Context, Exception = res.Exception }).ConfigureAwait(false);
}
- else
+ catch (Exception ex)
{
- cmd = cm2.Children.FirstOrDefault(x => x.Name == next || x.Aliases?.Contains(next) == true);
+ await this._error.InvokeAsync(this, new CommandErrorEventArgs(this.Client.ServiceProvider) { Context = ctx, Exception = ex }).ConfigureAwait(false);
}
-
- if (cmd == null)
+ finally
{
- cmd = cm2;
- pos = oldPos;
- break;
+ if (ctx.ServiceScopeContext.IsInitialized)
+ ctx.ServiceScopeContext.Dispose();
}
}
- rawArguments = commandString[pos..].Trim();
- return cmd;
- }
-
- /// <summary>
- /// Creates a command execution context from specified arguments.
- /// </summary>
- /// <param name="msg">Message to use for context.</param>
- /// <param name="prefix">Command prefix, used to execute commands.</param>
- /// <param name="cmd">Command to execute.</param>
- /// <param name="rawArguments">Raw arguments to pass to command.</param>
- /// <returns>Created command execution context.</returns>
- public CommandContext CreateContext(DiscordMessage msg, string prefix, Command cmd, string rawArguments = null)
- {
- var ctx = new CommandContext
- {
- Client = this.Client,
- Command = cmd,
- Message = msg,
- Config = this._config,
- RawArgumentString = rawArguments ?? "",
- Prefix = prefix,
- CommandsNext = this,
- Services = this.Services
- };
-
- if (cmd != null && (cmd.Module is TransientCommandModule || cmd.Module == null))
+ /// <summary>
+ /// Runs the all checks async.
+ /// </summary>
+ /// <param name="cmd">The cmd.</param>
+ /// <param name="ctx">The ctx.</param>
+ /// <returns>A Task.</returns>
+ private async Task RunAllChecksAsync(Command cmd, CommandContext ctx)
{
- var scope = ctx.Services.CreateScope();
- ctx.ServiceScopeContext = new CommandContext.ServiceContext(ctx.Services, scope);
- ctx.Services = scope.ServiceProvider;
- }
+ if (cmd.Parent != null)
+ await this.RunAllChecksAsync(cmd.Parent, ctx).ConfigureAwait(false);
- return ctx;
- }
+ var fchecks = await cmd.RunChecksAsync(ctx, false).ConfigureAwait(false);
+ if (fchecks.Any())
+ throw new ChecksFailedException(cmd, ctx, fchecks);
+ }
+ #endregion
- /// <summary>
- /// Executes specified command from given context.
- /// </summary>
- /// <param name="ctx">Context to execute command from.</param>
- /// <returns></returns>
- public async Task ExecuteCommandAsync(CommandContext ctx)
- {
- try
- {
- var cmd = ctx.Command;
- await this.RunAllChecksAsync(cmd, ctx).ConfigureAwait(false);
+ #region Command Registration
+ /// <summary>
+ /// Gets a dictionary of registered top-level commands.
+ /// </summary>
+ public IReadOnlyDictionary<string, Command> RegisteredCommands
+ => this._registeredCommandsLazy.Value;
- var res = await cmd.ExecuteAsync(ctx).ConfigureAwait(false);
+ /// <summary>
+ /// Gets or sets the top level commands.
+ /// </summary>
+ private readonly Dictionary<string, Command> _topLevelCommands;
+ private readonly Lazy<IReadOnlyDictionary<string, Command>> _registeredCommandsLazy;
- if (res.IsSuccessful)
- await this._executed.InvokeAsync(this, new CommandExecutionEventArgs(this.Client.ServiceProvider) { Context = res.Context }).ConfigureAwait(false);
- else
- await this._error.InvokeAsync(this, new CommandErrorEventArgs(this.Client.ServiceProvider) { Context = res.Context, Exception = res.Exception }).ConfigureAwait(false);
- }
- catch (Exception ex)
+ /// <summary>
+ /// Registers all commands from a given assembly. The command classes need to be public to be considered for registration.
+ /// </summary>
+ /// <param name="assembly">Assembly to register commands from.</param>
+ public void RegisterCommands(Assembly assembly)
{
- await this._error.InvokeAsync(this, new CommandErrorEventArgs(this.Client.ServiceProvider) { Context = ctx, Exception = ex }).ConfigureAwait(false);
+ var types = assembly.ExportedTypes.Where(xt =>
+ {
+ var xti = xt.GetTypeInfo();
+ return xti.IsModuleCandidateType() && !xti.IsNested;
+ });
+ foreach (var xt in types)
+ this.RegisterCommands(xt);
}
- finally
+
+ /// <summary>
+ /// Registers all commands from a given command class.
+ /// </summary>
+ /// <typeparam name="T">Class which holds commands to register.</typeparam>
+ public void RegisterCommands<T>() where T : BaseCommandModule
{
- if (ctx.ServiceScopeContext.IsInitialized)
- ctx.ServiceScopeContext.Dispose();
+ var t = typeof(T);
+ this.RegisterCommands(t);
}
- }
- /// <summary>
- /// Runs the all checks async.
- /// </summary>
- /// <param name="cmd">The cmd.</param>
- /// <param name="ctx">The ctx.</param>
- /// <returns>A Task.</returns>
- private async Task RunAllChecksAsync(Command cmd, CommandContext ctx)
- {
- if (cmd.Parent != null)
- await this.RunAllChecksAsync(cmd.Parent, ctx).ConfigureAwait(false);
-
- var fchecks = await cmd.RunChecksAsync(ctx, false).ConfigureAwait(false);
- if (fchecks.Any())
- throw new ChecksFailedException(cmd, ctx, fchecks);
- }
- #endregion
-
- #region Command Registration
- /// <summary>
- /// Gets a dictionary of registered top-level commands.
- /// </summary>
- public IReadOnlyDictionary<string, Command> RegisteredCommands
- => this._registeredCommandsLazy.Value;
-
- /// <summary>
- /// Gets or sets the top level commands.
- /// </summary>
- private readonly Dictionary<string, Command> _topLevelCommands;
- private readonly Lazy<IReadOnlyDictionary<string, Command>> _registeredCommandsLazy;
-
- /// <summary>
- /// Registers all commands from a given assembly. The command classes need to be public to be considered for registration.
- /// </summary>
- /// <param name="assembly">Assembly to register commands from.</param>
- public void RegisterCommands(Assembly assembly)
- {
- var types = assembly.ExportedTypes.Where(xt =>
+ /// <summary>
+ /// Registers all commands from a given command class.
+ /// </summary>
+ /// <param name="t">Type of the class which holds commands to register.</param>
+ public void RegisterCommands(Type t)
{
- var xti = xt.GetTypeInfo();
- return xti.IsModuleCandidateType() && !xti.IsNested;
- });
- foreach (var xt in types)
- this.RegisterCommands(xt);
- }
-
- /// <summary>
- /// Registers all commands from a given command class.
- /// </summary>
- /// <typeparam name="T">Class which holds commands to register.</typeparam>
- public void RegisterCommands<T>() where T : BaseCommandModule
- {
- var t = typeof(T);
- this.RegisterCommands(t);
- }
-
- /// <summary>
- /// Registers all commands from a given command class.
- /// </summary>
- /// <param name="t">Type of the class which holds commands to register.</param>
- public void RegisterCommands(Type t)
- {
- if (t == null)
- throw new ArgumentNullException(nameof(t), "Type cannot be null.");
+ if (t == null)
+ throw new ArgumentNullException(nameof(t), "Type cannot be null.");
- if (!t.IsModuleCandidateType())
- throw new ArgumentNullException(nameof(t), "Type must be a class, which cannot be abstract or static.");
+ if (!t.IsModuleCandidateType())
+ throw new ArgumentNullException(nameof(t), "Type must be a class, which cannot be abstract or static.");
- this.RegisterCommands(t, null, null, out var tempCommands);
+ this.RegisterCommands(t, null, null, out var tempCommands);
- if (tempCommands != null)
- foreach (var command in tempCommands)
- this.AddToCommandDictionary(command.Build(null));
- }
+ if (tempCommands != null)
+ foreach (var command in tempCommands)
+ this.AddToCommandDictionary(command.Build(null));
+ }
- /// <summary>
- /// Registers the commands.
- /// </summary>
- /// <param name="t">The type.</param>
- /// <param name="currentParent">The current parent.</param>
- /// <param name="inheritedChecks">The inherited checks.</param>
- /// <param name="foundCommands">The found commands.</param>
- private void RegisterCommands(Type t, CommandGroupBuilder currentParent, IEnumerable<CheckBaseAttribute> inheritedChecks, out List<CommandBuilder> foundCommands)
- {
- var ti = t.GetTypeInfo();
+ /// <summary>
+ /// Registers the commands.
+ /// </summary>
+ /// <param name="t">The type.</param>
+ /// <param name="currentParent">The current parent.</param>
+ /// <param name="inheritedChecks">The inherited checks.</param>
+ /// <param name="foundCommands">The found commands.</param>
+ private void RegisterCommands(Type t, CommandGroupBuilder currentParent, IEnumerable<CheckBaseAttribute> inheritedChecks, out List<CommandBuilder> foundCommands)
+ {
+ var ti = t.GetTypeInfo();
- var lifespan = ti.GetCustomAttribute<ModuleLifespanAttribute>();
- var moduleLifespan = lifespan != null ? lifespan.Lifespan : ModuleLifespan.Singleton;
+ var lifespan = ti.GetCustomAttribute<ModuleLifespanAttribute>();
+ var moduleLifespan = lifespan != null ? lifespan.Lifespan : ModuleLifespan.Singleton;
- var module = new CommandModuleBuilder()
- .WithType(t)
- .WithLifespan(moduleLifespan)
- .Build(this.Services);
+ var module = new CommandModuleBuilder()
+ .WithType(t)
+ .WithLifespan(moduleLifespan)
+ .Build(this.Services);
- // restrict parent lifespan to more or equally restrictive
- if (currentParent?.Module is TransientCommandModule && moduleLifespan != ModuleLifespan.Transient)
- throw new InvalidOperationException("In a transient module, child modules can only be transient.");
+ // restrict parent lifespan to more or equally restrictive
+ if (currentParent?.Module is TransientCommandModule && moduleLifespan != ModuleLifespan.Transient)
+ throw new InvalidOperationException("In a transient module, child modules can only be transient.");
- // check if we are anything
- var groupBuilder = new CommandGroupBuilder(module);
- var isModule = false;
- var moduleAttributes = ti.GetCustomAttributes();
- var moduleHidden = false;
- var moduleChecks = new List<CheckBaseAttribute>();
+ // check if we are anything
+ var groupBuilder = new CommandGroupBuilder(module);
+ var isModule = false;
+ var moduleAttributes = ti.GetCustomAttributes();
+ var moduleHidden = false;
+ var moduleChecks = new List<CheckBaseAttribute>();
- foreach (var xa in moduleAttributes)
- {
- switch (xa)
+ foreach (var xa in moduleAttributes)
{
- case GroupAttribute g:
- isModule = true;
- var moduleName = g.Name;
- if (moduleName == null)
- {
- moduleName = ti.Name;
-
- if (moduleName.EndsWith("Group") && moduleName != "Group")
- moduleName = moduleName[0..^5];
- else if (moduleName.EndsWith("Module") && moduleName != "Module")
- moduleName = moduleName[0..^6];
- else if (moduleName.EndsWith("Commands") && moduleName != "Commands")
- moduleName = moduleName[0..^8];
- }
+ switch (xa)
+ {
+ case GroupAttribute g:
+ isModule = true;
+ var moduleName = g.Name;
+ if (moduleName == null)
+ {
+ moduleName = ti.Name;
+
+ if (moduleName.EndsWith("Group") && moduleName != "Group")
+ moduleName = moduleName[0..^5];
+ else if (moduleName.EndsWith("Module") && moduleName != "Module")
+ moduleName = moduleName[0..^6];
+ else if (moduleName.EndsWith("Commands") && moduleName != "Commands")
+ moduleName = moduleName[0..^8];
+ }
- if (!this._config.CaseSensitive)
- moduleName = moduleName.ToLowerInvariant();
+ if (!this._config.CaseSensitive)
+ moduleName = moduleName.ToLowerInvariant();
- groupBuilder.WithName(moduleName);
+ groupBuilder.WithName(moduleName);
- if (inheritedChecks != null)
- foreach (var chk in inheritedChecks)
- groupBuilder.WithExecutionCheck(chk);
+ if (inheritedChecks != null)
+ foreach (var chk in inheritedChecks)
+ groupBuilder.WithExecutionCheck(chk);
- foreach (var mi in ti.DeclaredMethods.Where(x => x.IsCommandCandidate(out _) && x.GetCustomAttribute<GroupCommandAttribute>() != null))
- groupBuilder.WithOverload(new CommandOverloadBuilder(mi));
- break;
+ foreach (var mi in ti.DeclaredMethods.Where(x => x.IsCommandCandidate(out _) && x.GetCustomAttribute<GroupCommandAttribute>() != null))
+ groupBuilder.WithOverload(new CommandOverloadBuilder(mi));
+ break;
- case AliasesAttribute a:
- foreach (var xalias in a.Aliases)
- groupBuilder.WithAlias(this._config.CaseSensitive ? xalias : xalias.ToLowerInvariant());
- break;
+ case AliasesAttribute a:
+ foreach (var xalias in a.Aliases)
+ groupBuilder.WithAlias(this._config.CaseSensitive ? xalias : xalias.ToLowerInvariant());
+ break;
- case HiddenAttribute h:
- groupBuilder.WithHiddenStatus(true);
- moduleHidden = true;
- break;
+ case HiddenAttribute h:
+ groupBuilder.WithHiddenStatus(true);
+ moduleHidden = true;
+ break;
- case DescriptionAttribute d:
- groupBuilder.WithDescription(d.Description);
- break;
+ case DescriptionAttribute d:
+ groupBuilder.WithDescription(d.Description);
+ break;
- case CheckBaseAttribute c:
- moduleChecks.Add(c);
- groupBuilder.WithExecutionCheck(c);
- break;
+ case CheckBaseAttribute c:
+ moduleChecks.Add(c);
+ groupBuilder.WithExecutionCheck(c);
+ break;
- default:
- groupBuilder.WithCustomAttribute(xa);
- break;
+ default:
+ groupBuilder.WithCustomAttribute(xa);
+ break;
+ }
}
- }
- if (!isModule)
- {
- groupBuilder = null;
- if (inheritedChecks != null)
- moduleChecks.AddRange(inheritedChecks);
- }
-
- // candidate methods
- var methods = ti.DeclaredMethods;
- var commands = new List<CommandBuilder>();
- var commandBuilders = new Dictionary<string, CommandBuilder>();
- foreach (var m in methods)
- {
- if (!m.IsCommandCandidate(out _))
- continue;
-
- var attrs = m.GetCustomAttributes();
- if (attrs.FirstOrDefault(xa => xa is CommandAttribute) is not CommandAttribute cattr)
- continue;
-
- var commandName = cattr.Name;
- if (commandName == null)
+ if (!isModule)
{
- commandName = m.Name;
- if (commandName.EndsWith("Async") && commandName != "Async")
- commandName = commandName[0..^5];
+ groupBuilder = null;
+ if (inheritedChecks != null)
+ moduleChecks.AddRange(inheritedChecks);
}
- if (!this._config.CaseSensitive)
- commandName = commandName.ToLowerInvariant();
-
- if (!commandBuilders.TryGetValue(commandName, out var commandBuilder))
+ // candidate methods
+ var methods = ti.DeclaredMethods;
+ var commands = new List<CommandBuilder>();
+ var commandBuilders = new Dictionary<string, CommandBuilder>();
+ foreach (var m in methods)
{
- commandBuilders.Add(commandName, commandBuilder = new CommandBuilder(module).WithName(commandName));
+ if (!m.IsCommandCandidate(out _))
+ continue;
- if (!isModule)
- if (currentParent != null)
- currentParent.WithChild(commandBuilder);
- else
- commands.Add(commandBuilder);
- else
- groupBuilder.WithChild(commandBuilder);
- }
+ var attrs = m.GetCustomAttributes();
+ if (attrs.FirstOrDefault(xa => xa is CommandAttribute) is not CommandAttribute cattr)
+ continue;
- commandBuilder.WithOverload(new CommandOverloadBuilder(m));
+ var commandName = cattr.Name;
+ if (commandName == null)
+ {
+ commandName = m.Name;
+ if (commandName.EndsWith("Async") && commandName != "Async")
+ commandName = commandName[0..^5];
+ }
- if (!isModule && moduleChecks.Any())
- foreach (var chk in moduleChecks)
- commandBuilder.WithExecutionCheck(chk);
+ if (!this._config.CaseSensitive)
+ commandName = commandName.ToLowerInvariant();
- foreach (var xa in attrs)
- {
- switch (xa)
+ if (!commandBuilders.TryGetValue(commandName, out var commandBuilder))
{
- case AliasesAttribute a:
- foreach (var xalias in a.Aliases)
- commandBuilder.WithAlias(this._config.CaseSensitive ? xalias : xalias.ToLowerInvariant());
- break;
+ commandBuilders.Add(commandName, commandBuilder = new CommandBuilder(module).WithName(commandName));
- case CheckBaseAttribute p:
- commandBuilder.WithExecutionCheck(p);
- break;
+ if (!isModule)
+ if (currentParent != null)
+ currentParent.WithChild(commandBuilder);
+ else
+ commands.Add(commandBuilder);
+ else
+ groupBuilder.WithChild(commandBuilder);
+ }
- case DescriptionAttribute d:
- commandBuilder.WithDescription(d.Description);
- break;
+ commandBuilder.WithOverload(new CommandOverloadBuilder(m));
- case HiddenAttribute h:
- commandBuilder.WithHiddenStatus(true);
- break;
+ if (!isModule && moduleChecks.Any())
+ foreach (var chk in moduleChecks)
+ commandBuilder.WithExecutionCheck(chk);
- default:
- commandBuilder.WithCustomAttribute(xa);
- break;
+ foreach (var xa in attrs)
+ {
+ switch (xa)
+ {
+ case AliasesAttribute a:
+ foreach (var xalias in a.Aliases)
+ commandBuilder.WithAlias(this._config.CaseSensitive ? xalias : xalias.ToLowerInvariant());
+ break;
+
+ case CheckBaseAttribute p:
+ commandBuilder.WithExecutionCheck(p);
+ break;
+
+ case DescriptionAttribute d:
+ commandBuilder.WithDescription(d.Description);
+ break;
+
+ case HiddenAttribute h:
+ commandBuilder.WithHiddenStatus(true);
+ break;
+
+ default:
+ commandBuilder.WithCustomAttribute(xa);
+ break;
+ }
}
+
+ if (!isModule && moduleHidden)
+ commandBuilder.WithHiddenStatus(true);
}
- if (!isModule && moduleHidden)
- commandBuilder.WithHiddenStatus(true);
+ // candidate types
+ var types = ti.DeclaredNestedTypes
+ .Where(xt => xt.IsModuleCandidateType() && xt.DeclaredConstructors.Any(xc => xc.IsPublic));
+ foreach (var type in types)
+ {
+ this.RegisterCommands(type.AsType(),
+ groupBuilder,
+ !isModule ? moduleChecks : null,
+ out var tempCommands);
+
+ if (isModule)
+ foreach (var chk in moduleChecks)
+ groupBuilder.WithExecutionCheck(chk);
+
+ if (isModule && tempCommands != null)
+ foreach (var xtcmd in tempCommands)
+ groupBuilder.WithChild(xtcmd);
+ else if (tempCommands != null)
+ commands.AddRange(tempCommands);
+ }
+
+ if (isModule && currentParent == null)
+ commands.Add(groupBuilder);
+ else if (isModule)
+ currentParent.WithChild(groupBuilder);
+ foundCommands = commands;
}
- // candidate types
- var types = ti.DeclaredNestedTypes
- .Where(xt => xt.IsModuleCandidateType() && xt.DeclaredConstructors.Any(xc => xc.IsPublic));
- foreach (var type in types)
+ /// <summary>
+ /// Builds and registers all supplied commands.
+ /// </summary>
+ /// <param name="cmds">Commands to build and register.</param>
+ public void RegisterCommands(params CommandBuilder[] cmds)
{
- this.RegisterCommands(type.AsType(),
- groupBuilder,
- !isModule ? moduleChecks : null,
- out var tempCommands);
-
- if (isModule)
- foreach (var chk in moduleChecks)
- groupBuilder.WithExecutionCheck(chk);
-
- if (isModule && tempCommands != null)
- foreach (var xtcmd in tempCommands)
- groupBuilder.WithChild(xtcmd);
- else if (tempCommands != null)
- commands.AddRange(tempCommands);
+ foreach (var cmd in cmds)
+ this.AddToCommandDictionary(cmd.Build(null));
}
- if (isModule && currentParent == null)
- commands.Add(groupBuilder);
- else if (isModule)
- currentParent.WithChild(groupBuilder);
- foundCommands = commands;
- }
-
- /// <summary>
- /// Builds and registers all supplied commands.
- /// </summary>
- /// <param name="cmds">Commands to build and register.</param>
- public void RegisterCommands(params CommandBuilder[] cmds)
- {
- foreach (var cmd in cmds)
- this.AddToCommandDictionary(cmd.Build(null));
- }
-
- /// <summary>
- /// Unregisters specified commands from CommandsNext.
- /// </summary>
- /// <param name="cmds">Commands to unregister.</param>
- public void UnregisterCommands(params Command[] cmds)
- {
- if (cmds.Any(x => x.Parent != null))
- throw new InvalidOperationException("Cannot unregister nested commands.");
+ /// <summary>
+ /// Unregisters specified commands from CommandsNext.
+ /// </summary>
+ /// <param name="cmds">Commands to unregister.</param>
+ public void UnregisterCommands(params Command[] cmds)
+ {
+ if (cmds.Any(x => x.Parent != null))
+ throw new InvalidOperationException("Cannot unregister nested commands.");
- var keys = this.RegisteredCommands.Where(x => cmds.Contains(x.Value)).Select(x => x.Key).ToList();
- foreach (var key in keys)
- this._topLevelCommands.Remove(key);
- }
+ var keys = this.RegisteredCommands.Where(x => cmds.Contains(x.Value)).Select(x => x.Key).ToList();
+ foreach (var key in keys)
+ this._topLevelCommands.Remove(key);
+ }
- /// <summary>
- /// Adds the to command dictionary.
- /// </summary>
- /// <param name="cmd">The cmd.</param>
- private void AddToCommandDictionary(Command cmd)
- {
- if (cmd.Parent != null)
- return;
+ /// <summary>
+ /// Adds the to command dictionary.
+ /// </summary>
+ /// <param name="cmd">The cmd.</param>
+ private void AddToCommandDictionary(Command cmd)
+ {
+ if (cmd.Parent != null)
+ return;
- if (this._topLevelCommands.ContainsKey(cmd.Name) || (cmd.Aliases != null && cmd.Aliases.Any(xs => this._topLevelCommands.ContainsKey(xs))))
- throw new DuplicateCommandException(cmd.QualifiedName);
+ if (this._topLevelCommands.ContainsKey(cmd.Name) || (cmd.Aliases != null && cmd.Aliases.Any(xs => this._topLevelCommands.ContainsKey(xs))))
+ throw new DuplicateCommandException(cmd.QualifiedName);
- this._topLevelCommands[cmd.Name] = cmd;
- if (cmd.Aliases != null)
- foreach (var xs in cmd.Aliases)
- this._topLevelCommands[xs] = cmd;
- }
- #endregion
+ this._topLevelCommands[cmd.Name] = cmd;
+ if (cmd.Aliases != null)
+ foreach (var xs in cmd.Aliases)
+ this._topLevelCommands[xs] = cmd;
+ }
+ #endregion
- #region Default Help
- /// <summary>
- /// Represents the default help module.
- /// </summary>
- [ModuleLifespan(ModuleLifespan.Transient)]
- public class DefaultHelpModule : BaseCommandModule
- {
+ #region Default Help
/// <summary>
- /// Defaults the help async.
+ /// Represents the default help module.
/// </summary>
- /// <param name="ctx">The ctx.</param>
- /// <param name="command">The command.</param>
- /// <returns>A Task.</returns>
- [Command("help"), Description("Displays command help.")]
- public async Task DefaultHelpAsync(CommandContext ctx, [Description("Command to provide help for.")] params string[] command)
+ [ModuleLifespan(ModuleLifespan.Transient)]
+ public class DefaultHelpModule : BaseCommandModule
{
- var topLevel = ctx.CommandsNext._topLevelCommands.Values.Distinct();
- var helpBuilder = ctx.CommandsNext._helpFormatter.Create(ctx);
-
- if (command != null && command.Any())
+ /// <summary>
+ /// Defaults the help async.
+ /// </summary>
+ /// <param name="ctx">The ctx.</param>
+ /// <param name="command">The command.</param>
+ /// <returns>A Task.</returns>
+ [Command("help"), Description("Displays command help.")]
+ public async Task DefaultHelpAsync(CommandContext ctx, [Description("Command to provide help for.")] params string[] command)
{
- Command cmd = null;
- var searchIn = topLevel;
- foreach (var c in command)
+ var topLevel = ctx.CommandsNext._topLevelCommands.Values.Distinct();
+ var helpBuilder = ctx.CommandsNext._helpFormatter.Create(ctx);
+
+ if (command != null && command.Any())
{
- if (searchIn == null)
+ Command cmd = null;
+ var searchIn = topLevel;
+ foreach (var c in command)
{
- cmd = null;
- break;
- }
+ if (searchIn == null)
+ {
+ cmd = null;
+ break;
+ }
- cmd = ctx.Config.CaseSensitive
- ? searchIn.FirstOrDefault(xc => xc.Name == c || (xc.Aliases != null && xc.Aliases.Contains(c)))
- : searchIn.FirstOrDefault(xc => xc.Name.ToLowerInvariant() == c.ToLowerInvariant() || (xc.Aliases != null && xc.Aliases.Select(xs => xs.ToLowerInvariant()).Contains(c.ToLowerInvariant())));
+ cmd = ctx.Config.CaseSensitive
+ ? searchIn.FirstOrDefault(xc => xc.Name == c || (xc.Aliases != null && xc.Aliases.Contains(c)))
+ : searchIn.FirstOrDefault(xc => xc.Name.ToLowerInvariant() == c.ToLowerInvariant() || (xc.Aliases != null && xc.Aliases.Select(xs => xs.ToLowerInvariant()).Contains(c.ToLowerInvariant())));
- if (cmd == null)
- break;
+ if (cmd == null)
+ break;
- var failedChecks = await cmd.RunChecksAsync(ctx, true).ConfigureAwait(false);
- if (failedChecks.Any())
- throw new ChecksFailedException(cmd, ctx, failedChecks);
+ var failedChecks = await cmd.RunChecksAsync(ctx, true).ConfigureAwait(false);
+ if (failedChecks.Any())
+ throw new ChecksFailedException(cmd, ctx, failedChecks);
- searchIn = cmd is CommandGroup ? (cmd as CommandGroup).Children : null;
- }
+ searchIn = cmd is CommandGroup ? (cmd as CommandGroup).Children : null;
+ }
- if (cmd == null)
- throw new CommandNotFoundException(string.Join(" ", command));
+ if (cmd == null)
+ throw new CommandNotFoundException(string.Join(" ", command));
- helpBuilder.WithCommand(cmd);
+ helpBuilder.WithCommand(cmd);
- if (cmd is CommandGroup group)
+ if (cmd is CommandGroup group)
+ {
+ var commandsToSearch = group.Children.Where(xc => !xc.IsHidden);
+ var eligibleCommands = new List<Command>();
+ foreach (var candidateCommand in commandsToSearch)
+ {
+ if (candidateCommand.ExecutionChecks == null || !candidateCommand.ExecutionChecks.Any())
+ {
+ eligibleCommands.Add(candidateCommand);
+ continue;
+ }
+
+ var candidateFailedChecks = await candidateCommand.RunChecksAsync(ctx, true).ConfigureAwait(false);
+ if (!candidateFailedChecks.Any())
+ eligibleCommands.Add(candidateCommand);
+ }
+
+ if (eligibleCommands.Any())
+ helpBuilder.WithSubcommands(eligibleCommands.OrderBy(xc => xc.Name));
+ }
+ }
+ else
{
- var commandsToSearch = group.Children.Where(xc => !xc.IsHidden);
+ var commandsToSearch = topLevel.Where(xc => !xc.IsHidden);
var eligibleCommands = new List<Command>();
- foreach (var candidateCommand in commandsToSearch)
+ foreach (var sc in commandsToSearch)
{
- if (candidateCommand.ExecutionChecks == null || !candidateCommand.ExecutionChecks.Any())
+ if (sc.ExecutionChecks == null || !sc.ExecutionChecks.Any())
{
- eligibleCommands.Add(candidateCommand);
+ eligibleCommands.Add(sc);
continue;
}
- var candidateFailedChecks = await candidateCommand.RunChecksAsync(ctx, true).ConfigureAwait(false);
+ var candidateFailedChecks = await sc.RunChecksAsync(ctx, true).ConfigureAwait(false);
if (!candidateFailedChecks.Any())
- eligibleCommands.Add(candidateCommand);
+ eligibleCommands.Add(sc);
}
if (eligibleCommands.Any())
helpBuilder.WithSubcommands(eligibleCommands.OrderBy(xc => xc.Name));
}
+
+ var helpMessage = helpBuilder.Build();
+
+ var builder = new DiscordMessageBuilder().WithContent(helpMessage.Content).WithEmbed(helpMessage.Embed);
+
+ if (!ctx.Config.DmHelp || ctx.Channel is DiscordDmChannel || ctx.Guild == null)
+ await ctx.RespondAsync(builder).ConfigureAwait(false);
+ else
+ await ctx.Member.SendMessageAsync(builder).ConfigureAwait(false);
+
}
- else
+ }
+ #endregion
+
+ #region Sudo
+ /// <summary>
+ /// Creates a fake command context to execute commands with.
+ /// </summary>
+ /// <param name="actor">The user or member to use as message author.</param>
+ /// <param name="channel">The channel the message is supposed to appear from.</param>
+ /// <param name="messageContents">Contents of the message.</param>
+ /// <param name="prefix">Command prefix, used to execute commands.</param>
+ /// <param name="cmd">Command to execute.</param>
+ /// <param name="rawArguments">Raw arguments to pass to command.</param>
+ /// <returns>Created fake context.</returns>
+ public CommandContext CreateFakeContext(DiscordUser actor, DiscordChannel channel, string messageContents, string prefix, Command cmd, string rawArguments = null)
+ {
+ var epoch = new DateTimeOffset(2015, 1, 1, 0, 0, 0, TimeSpan.Zero);
+ var now = DateTimeOffset.UtcNow;
+ var timeSpan = (ulong)(now - epoch).TotalMilliseconds;
+
+ // create fake message
+ var msg = new DiscordMessage
{
- var commandsToSearch = topLevel.Where(xc => !xc.IsHidden);
- var eligibleCommands = new List<Command>();
- foreach (var sc in commandsToSearch)
+ Discord = this.Client,
+ Author = actor,
+ ChannelId = channel.Id,
+ Content = messageContents,
+ Id = timeSpan << 22,
+ Pinned = false,
+ MentionEveryone = messageContents.Contains("@everyone"),
+ IsTts = false,
+ AttachmentsInternal = new List<DiscordAttachment>(),
+ EmbedsInternal = new List<DiscordEmbed>(),
+ TimestampRaw = now.ToString("yyyy-MM-ddTHH:mm:sszzz"),
+ ReactionsInternal = new List<DiscordReaction>()
+ };
+
+ var mentionedUsers = new List<DiscordUser>();
+ var mentionedRoles = msg.Channel.Guild != null ? new List<DiscordRole>() : null;
+ var mentionedChannels = msg.Channel.Guild != null ? new List<DiscordChannel>() : null;
+
+ if (!string.IsNullOrWhiteSpace(msg.Content))
+ {
+ if (msg.Channel.Guild != null)
{
- if (sc.ExecutionChecks == null || !sc.ExecutionChecks.Any())
- {
- eligibleCommands.Add(sc);
- continue;
- }
-
- var candidateFailedChecks = await sc.RunChecksAsync(ctx, true).ConfigureAwait(false);
- if (!candidateFailedChecks.Any())
- eligibleCommands.Add(sc);
+ mentionedUsers = Utilities.GetUserMentions(msg).Select(xid => msg.Channel.Guild.MembersInternal.TryGetValue(xid, out var member) ? member : null).Cast<DiscordUser>().ToList();
+ mentionedRoles = Utilities.GetRoleMentions(msg).Select(xid => msg.Channel.Guild.GetRole(xid)).ToList();
+ mentionedChannels = Utilities.GetChannelMentions(msg).Select(xid => msg.Channel.Guild.GetChannel(xid)).ToList();
+ }
+ else
+ {
+ mentionedUsers = Utilities.GetUserMentions(msg).Select(this.Client.GetCachedOrEmptyUserInternal).ToList();
}
+ }
- if (eligibleCommands.Any())
- helpBuilder.WithSubcommands(eligibleCommands.OrderBy(xc => xc.Name));
+ msg.MentionedUsersInternal = mentionedUsers;
+ msg.MentionedRolesInternal = mentionedRoles;
+ msg.MentionedChannelsInternal = mentionedChannels;
+
+ var ctx = new CommandContext
+ {
+ Client = this.Client,
+ Command = cmd,
+ Message = msg,
+ Config = this._config,
+ RawArgumentString = rawArguments ?? "",
+ Prefix = prefix,
+ CommandsNext = this,
+ Services = this.Services
+ };
+
+ if (cmd != null && (cmd.Module is TransientCommandModule || cmd.Module == null))
+ {
+ var scope = ctx.Services.CreateScope();
+ ctx.ServiceScopeContext = new CommandContext.ServiceContext(ctx.Services, scope);
+ ctx.Services = scope.ServiceProvider;
}
- var helpMessage = helpBuilder.Build();
+ return ctx;
+ }
+ #endregion
- var builder = new DiscordMessageBuilder().WithContent(helpMessage.Content).WithEmbed(helpMessage.Embed);
+ #region Type Conversion
+ /// <summary>
+ /// Converts a string to specified type.
+ /// </summary>
+ /// <typeparam name="T">Type to convert to.</typeparam>
+ /// <param name="value">Value to convert.</param>
+ /// <param name="ctx">Context in which to convert to.</param>
+ /// <returns>Converted object.</returns>
+ public async Task<T> ConvertArgument<T>(string value, CommandContext ctx)
+ {
+ var t = typeof(T);
+ if (!this.ArgumentConverters.ContainsKey(t))
+ throw new ArgumentException("There is no converter specified for given type.", nameof(T));
- if (!ctx.Config.DmHelp || ctx.Channel is DiscordDmChannel || ctx.Guild == null)
- await ctx.RespondAsync(builder).ConfigureAwait(false);
- else
- await ctx.Member.SendMessageAsync(builder).ConfigureAwait(false);
+ if (this.ArgumentConverters[t] is not IArgumentConverter<T> cv)
+ throw new ArgumentException("Invalid converter registered for this type.", nameof(T));
+ var cvr = await cv.ConvertAsync(value, ctx).ConfigureAwait(false);
+ return !cvr.HasValue ? throw new ArgumentException("Could not convert specified value to given type.", nameof(value)) : cvr.Value;
}
- }
- #endregion
-
- #region Sudo
- /// <summary>
- /// Creates a fake command context to execute commands with.
- /// </summary>
- /// <param name="actor">The user or member to use as message author.</param>
- /// <param name="channel">The channel the message is supposed to appear from.</param>
- /// <param name="messageContents">Contents of the message.</param>
- /// <param name="prefix">Command prefix, used to execute commands.</param>
- /// <param name="cmd">Command to execute.</param>
- /// <param name="rawArguments">Raw arguments to pass to command.</param>
- /// <returns>Created fake context.</returns>
- public CommandContext CreateFakeContext(DiscordUser actor, DiscordChannel channel, string messageContents, string prefix, Command cmd, string rawArguments = null)
- {
- var epoch = new DateTimeOffset(2015, 1, 1, 0, 0, 0, TimeSpan.Zero);
- var now = DateTimeOffset.UtcNow;
- var timeSpan = (ulong)(now - epoch).TotalMilliseconds;
- // create fake message
- var msg = new DiscordMessage
- {
- Discord = this.Client,
- Author = actor,
- ChannelId = channel.Id,
- Content = messageContents,
- Id = timeSpan << 22,
- Pinned = false,
- MentionEveryone = messageContents.Contains("@everyone"),
- IsTts = false,
- AttachmentsInternal = new List<DiscordAttachment>(),
- EmbedsInternal = new List<DiscordEmbed>(),
- TimestampRaw = now.ToString("yyyy-MM-ddTHH:mm:sszzz"),
- ReactionsInternal = new List<DiscordReaction>()
- };
-
- var mentionedUsers = new List<DiscordUser>();
- var mentionedRoles = msg.Channel.Guild != null ? new List<DiscordRole>() : null;
- var mentionedChannels = msg.Channel.Guild != null ? new List<DiscordChannel>() : null;
-
- if (!string.IsNullOrWhiteSpace(msg.Content))
+ /// <summary>
+ /// Converts a string to specified type.
+ /// </summary>
+ /// <param name="value">Value to convert.</param>
+ /// <param name="ctx">Context in which to convert to.</param>
+ /// <param name="type">Type to convert to.</param>
+ /// <returns>Converted object.</returns>
+ public async Task<object> ConvertArgument(string value, CommandContext ctx, Type type)
{
- if (msg.Channel.Guild != null)
+ var m = this._convertGeneric.MakeGenericMethod(type);
+ try
{
- mentionedUsers = Utilities.GetUserMentions(msg).Select(xid => msg.Channel.Guild.MembersInternal.TryGetValue(xid, out var member) ? member : null).Cast<DiscordUser>().ToList();
- mentionedRoles = Utilities.GetRoleMentions(msg).Select(xid => msg.Channel.Guild.GetRole(xid)).ToList();
- mentionedChannels = Utilities.GetChannelMentions(msg).Select(xid => msg.Channel.Guild.GetChannel(xid)).ToList();
+ return await (m.Invoke(this, new object[] { value, ctx }) as Task<object>).ConfigureAwait(false);
}
- else
+ catch (TargetInvocationException ex)
{
- mentionedUsers = Utilities.GetUserMentions(msg).Select(this.Client.GetCachedOrEmptyUserInternal).ToList();
+ throw ex.InnerException;
}
}
- msg.MentionedUsersInternal = mentionedUsers;
- msg.MentionedRolesInternal = mentionedRoles;
- msg.MentionedChannelsInternal = mentionedChannels;
-
- var ctx = new CommandContext
- {
- Client = this.Client,
- Command = cmd,
- Message = msg,
- Config = this._config,
- RawArgumentString = rawArguments ?? "",
- Prefix = prefix,
- CommandsNext = this,
- Services = this.Services
- };
-
- if (cmd != null && (cmd.Module is TransientCommandModule || cmd.Module == null))
+ /// <summary>
+ /// Registers an argument converter for specified type.
+ /// </summary>
+ /// <typeparam name="T">Type for which to register the converter.</typeparam>
+ /// <param name="converter">Converter to register.</param>
+ public void RegisterConverter<T>(IArgumentConverter<T> converter)
{
- var scope = ctx.Services.CreateScope();
- ctx.ServiceScopeContext = new CommandContext.ServiceContext(ctx.Services, scope);
- ctx.Services = scope.ServiceProvider;
- }
-
- return ctx;
- }
- #endregion
+ if (converter == null)
+ throw new ArgumentNullException(nameof(converter), "Converter cannot be null.");
- #region Type Conversion
- /// <summary>
- /// Converts a string to specified type.
- /// </summary>
- /// <typeparam name="T">Type to convert to.</typeparam>
- /// <param name="value">Value to convert.</param>
- /// <param name="ctx">Context in which to convert to.</param>
- /// <returns>Converted object.</returns>
- public async Task<T> ConvertArgument<T>(string value, CommandContext ctx)
- {
- var t = typeof(T);
- if (!this.ArgumentConverters.ContainsKey(t))
- throw new ArgumentException("There is no converter specified for given type.", nameof(T));
+ var t = typeof(T);
+ var ti = t.GetTypeInfo();
+ this.ArgumentConverters[t] = converter;
- if (this.ArgumentConverters[t] is not IArgumentConverter<T> cv)
- throw new ArgumentException("Invalid converter registered for this type.", nameof(T));
+ if (!ti.IsValueType)
+ return;
- var cvr = await cv.ConvertAsync(value, ctx).ConfigureAwait(false);
- return !cvr.HasValue ? throw new ArgumentException("Could not convert specified value to given type.", nameof(value)) : cvr.Value;
- }
+ var nullableConverterType = typeof(NullableConverter<>).MakeGenericType(t);
+ var nullableType = typeof(Nullable<>).MakeGenericType(t);
+ if (this.ArgumentConverters.ContainsKey(nullableType))
+ return;
- /// <summary>
- /// Converts a string to specified type.
- /// </summary>
- /// <param name="value">Value to convert.</param>
- /// <param name="ctx">Context in which to convert to.</param>
- /// <param name="type">Type to convert to.</param>
- /// <returns>Converted object.</returns>
- public async Task<object> ConvertArgument(string value, CommandContext ctx, Type type)
- {
- var m = this._convertGeneric.MakeGenericMethod(type);
- try
- {
- return await (m.Invoke(this, new object[] { value, ctx }) as Task<object>).ConfigureAwait(false);
- }
- catch (TargetInvocationException ex)
- {
- throw ex.InnerException;
+ var nullableConverter = Activator.CreateInstance(nullableConverterType) as IArgumentConverter;
+ this.ArgumentConverters[nullableType] = nullableConverter;
}
- }
-
- /// <summary>
- /// Registers an argument converter for specified type.
- /// </summary>
- /// <typeparam name="T">Type for which to register the converter.</typeparam>
- /// <param name="converter">Converter to register.</param>
- public void RegisterConverter<T>(IArgumentConverter<T> converter)
- {
- if (converter == null)
- throw new ArgumentNullException(nameof(converter), "Converter cannot be null.");
- var t = typeof(T);
- var ti = t.GetTypeInfo();
- this.ArgumentConverters[t] = converter;
+ /// <summary>
+ /// Unregisters an argument converter for specified type.
+ /// </summary>
+ /// <typeparam name="T">Type for which to unregister the converter.</typeparam>
+ public void UnregisterConverter<T>()
+ {
+ var t = typeof(T);
+ var ti = t.GetTypeInfo();
+ if (this.ArgumentConverters.ContainsKey(t))
+ this.ArgumentConverters.Remove(t);
- if (!ti.IsValueType)
- return;
+ if (this._userFriendlyTypeNames.ContainsKey(t))
+ this._userFriendlyTypeNames.Remove(t);
- var nullableConverterType = typeof(NullableConverter<>).MakeGenericType(t);
- var nullableType = typeof(Nullable<>).MakeGenericType(t);
- if (this.ArgumentConverters.ContainsKey(nullableType))
- return;
+ if (!ti.IsValueType)
+ return;
- var nullableConverter = Activator.CreateInstance(nullableConverterType) as IArgumentConverter;
- this.ArgumentConverters[nullableType] = nullableConverter;
- }
+ var nullableType = typeof(Nullable<>).MakeGenericType(t);
+ if (!this.ArgumentConverters.ContainsKey(nullableType))
+ return;
- /// <summary>
- /// Unregisters an argument converter for specified type.
- /// </summary>
- /// <typeparam name="T">Type for which to unregister the converter.</typeparam>
- public void UnregisterConverter<T>()
- {
- var t = typeof(T);
- var ti = t.GetTypeInfo();
- if (this.ArgumentConverters.ContainsKey(t))
- this.ArgumentConverters.Remove(t);
-
- if (this._userFriendlyTypeNames.ContainsKey(t))
- this._userFriendlyTypeNames.Remove(t);
+ this.ArgumentConverters.Remove(nullableType);
+ this._userFriendlyTypeNames.Remove(nullableType);
+ }
- if (!ti.IsValueType)
- return;
+ /// <summary>
+ /// Registers a user-friendly type name.
+ /// </summary>
+ /// <typeparam name="T">Type to register the name for.</typeparam>
+ /// <param name="value">Name to register.</param>
+ public void RegisterUserFriendlyTypeName<T>(string value)
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ throw new ArgumentNullException(nameof(value), "Name cannot be null or empty.");
- var nullableType = typeof(Nullable<>).MakeGenericType(t);
- if (!this.ArgumentConverters.ContainsKey(nullableType))
- return;
+ var t = typeof(T);
+ var ti = t.GetTypeInfo();
+ if (!this.ArgumentConverters.ContainsKey(t))
+ throw new InvalidOperationException("Cannot register a friendly name for a type which has no associated converter.");
- this.ArgumentConverters.Remove(nullableType);
- this._userFriendlyTypeNames.Remove(nullableType);
- }
+ this._userFriendlyTypeNames[t] = value;
- /// <summary>
- /// Registers a user-friendly type name.
- /// </summary>
- /// <typeparam name="T">Type to register the name for.</typeparam>
- /// <param name="value">Name to register.</param>
- public void RegisterUserFriendlyTypeName<T>(string value)
- {
- if (string.IsNullOrWhiteSpace(value))
- throw new ArgumentNullException(nameof(value), "Name cannot be null or empty.");
+ if (!ti.IsValueType)
+ return;
- var t = typeof(T);
- var ti = t.GetTypeInfo();
- if (!this.ArgumentConverters.ContainsKey(t))
- throw new InvalidOperationException("Cannot register a friendly name for a type which has no associated converter.");
+ var nullableType = typeof(Nullable<>).MakeGenericType(t);
+ this._userFriendlyTypeNames[nullableType] = value;
+ }
- this._userFriendlyTypeNames[t] = value;
+ /// <summary>
+ /// Converts a type into user-friendly type name.
+ /// </summary>
+ /// <param name="t">Type to convert.</param>
+ /// <returns>User-friendly type name.</returns>
+ public string GetUserFriendlyTypeName(Type t)
+ {
+ if (this._userFriendlyTypeNames.ContainsKey(t))
+ return this._userFriendlyTypeNames[t];
- if (!ti.IsValueType)
- return;
+ var ti = t.GetTypeInfo();
+ if (ti.IsGenericTypeDefinition && t.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ var tn = ti.GenericTypeArguments[0];
+ return this._userFriendlyTypeNames.ContainsKey(tn) ? this._userFriendlyTypeNames[tn] : tn.Name;
+ }
- var nullableType = typeof(Nullable<>).MakeGenericType(t);
- this._userFriendlyTypeNames[nullableType] = value;
- }
+ return t.Name;
+ }
+ #endregion
- /// <summary>
- /// Converts a type into user-friendly type name.
- /// </summary>
- /// <param name="t">Type to convert.</param>
- /// <returns>User-friendly type name.</returns>
- public string GetUserFriendlyTypeName(Type t)
- {
- if (this._userFriendlyTypeNames.ContainsKey(t))
- return this._userFriendlyTypeNames[t];
+ #region Helpers
+ /// <summary>
+ /// Allows easier interoperability with reflection by turning the <see cref="Task{T}"/> returned by <see cref="ConvertArgument"/>
+ /// into a task containing <see cref="object"/>, using the provided generic type information.
+ /// </summary>
+ private async Task<object> ConvertArgumentToObj<T>(string value, CommandContext ctx)
+ => await this.ConvertArgument<T>(value, ctx).ConfigureAwait(false);
- var ti = t.GetTypeInfo();
- if (ti.IsGenericTypeDefinition && t.GetGenericTypeDefinition() == typeof(Nullable<>))
+ /// <summary>
+ /// Gets the configuration-specific string comparer. This returns <see cref="StringComparer.Ordinal"/> or <see cref="StringComparer.OrdinalIgnoreCase"/>,
+ /// depending on whether <see cref="CommandsNextConfiguration.CaseSensitive"/> is set to <see langword="true"/> or <see langword="false"/>.
+ /// </summary>
+ /// <returns>A string comparer.</returns>
+ internal IEqualityComparer<string> GetStringComparer()
+ => this._config.CaseSensitive
+ ? StringComparer.Ordinal
+ : StringComparer.OrdinalIgnoreCase;
+ #endregion
+
+ #region Events
+ /// <summary>
+ /// Triggered whenever a command executes successfully.
+ /// </summary>
+ public event AsyncEventHandler<CommandsNextExtension, CommandExecutionEventArgs> CommandExecuted
{
- var tn = ti.GenericTypeArguments[0];
- return this._userFriendlyTypeNames.ContainsKey(tn) ? this._userFriendlyTypeNames[tn] : tn.Name;
+ add => this._executed.Register(value);
+ remove => this._executed.Unregister(value);
}
+ private AsyncEvent<CommandsNextExtension, CommandExecutionEventArgs> _executed;
- return t.Name;
- }
- #endregion
-
- #region Helpers
- /// <summary>
- /// Allows easier interoperability with reflection by turning the <see cref="Task{T}"/> returned by <see cref="ConvertArgument"/>
- /// into a task containing <see cref="object"/>, using the provided generic type information.
- /// </summary>
- private async Task<object> ConvertArgumentToObj<T>(string value, CommandContext ctx)
- => await this.ConvertArgument<T>(value, ctx).ConfigureAwait(false);
+ /// <summary>
+ /// Triggered whenever a command throws an exception during execution.
+ /// </summary>
+ public event AsyncEventHandler<CommandsNextExtension, CommandErrorEventArgs> CommandErrored
+ {
+ add => this._error.Register(value);
+ remove => this._error.Unregister(value);
+ }
+ private AsyncEvent<CommandsNextExtension, CommandErrorEventArgs> _error;
- /// <summary>
- /// Gets the configuration-specific string comparer. This returns <see cref="StringComparer.Ordinal"/> or <see cref="StringComparer.OrdinalIgnoreCase"/>,
- /// depending on whether <see cref="CommandsNextConfiguration.CaseSensitive"/> is set to <see langword="true"/> or <see langword="false"/>.
- /// </summary>
- /// <returns>A string comparer.</returns>
- internal IEqualityComparer<string> GetStringComparer()
- => this._config.CaseSensitive
- ? StringComparer.Ordinal
- : StringComparer.OrdinalIgnoreCase;
- #endregion
-
- #region Events
- /// <summary>
- /// Triggered whenever a command executes successfully.
- /// </summary>
- public event AsyncEventHandler<CommandsNextExtension, CommandExecutionEventArgs> CommandExecuted
- {
- add => this._executed.Register(value);
- remove => this._executed.Unregister(value);
- }
- private AsyncEvent<CommandsNextExtension, CommandExecutionEventArgs> _executed;
+ /// <summary>
+ /// Ons the command executed.
+ /// </summary>
+ /// <param name="e">The e.</param>
+ /// <returns>A Task.</returns>
+ private Task OnCommandExecuted(CommandExecutionEventArgs e)
+ => this._executed.InvokeAsync(this, e);
- /// <summary>
- /// Triggered whenever a command throws an exception during execution.
- /// </summary>
- public event AsyncEventHandler<CommandsNextExtension, CommandErrorEventArgs> CommandErrored
- {
- add => this._error.Register(value);
- remove => this._error.Unregister(value);
+ /// <summary>
+ /// Ons the command errored.
+ /// </summary>
+ /// <param name="e">The e.</param>
+ /// <returns>A Task.</returns>
+ private Task OnCommandErrored(CommandErrorEventArgs e)
+ => this._error.InvokeAsync(this, e);
+ #endregion
}
- private AsyncEvent<CommandsNextExtension, CommandErrorEventArgs> _error;
-
- /// <summary>
- /// Ons the command executed.
- /// </summary>
- /// <param name="e">The e.</param>
- /// <returns>A Task.</returns>
- private Task OnCommandExecuted(CommandExecutionEventArgs e)
- => this._executed.InvokeAsync(this, e);
-
- /// <summary>
- /// Ons the command errored.
- /// </summary>
- /// <param name="e">The e.</param>
- /// <returns>A Task.</returns>
- private Task OnCommandErrored(CommandErrorEventArgs e)
- => this._error.InvokeAsync(this, e);
- #endregion
}
diff --git a/DisCatSharp.CommandsNext/CommandsNextUtilities.cs b/DisCatSharp.CommandsNext/CommandsNextUtilities.cs
index 8adb4e29c..db838cfe4 100644
--- a/DisCatSharp.CommandsNext/CommandsNextUtilities.cs
+++ b/DisCatSharp.CommandsNext/CommandsNextUtilities.cs
@@ -1,431 +1,432 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DisCatSharp.CommandsNext.Attributes;
using DisCatSharp.CommandsNext.Converters;
using DisCatSharp.Common.RegularExpressions;
using DisCatSharp.Entities;
using Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Various CommandsNext-related utilities.
-/// </summary>
-public static class CommandsNextUtilities
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets the user regex.
- /// </summary>
- private static Regex s_userRegex { get; } = DiscordRegEx.User;
-
- /// <summary>
- /// Checks whether the message has a specified string prefix.
+ /// Various CommandsNext-related utilities.
/// </summary>
- /// <param name="msg">Message to check.</param>
- /// <param name="str">String to check for.</param>
- /// <param name="comparisonType">Method of string comparison for the purposes of finding prefixes.</param>
- /// <returns>Positive number if the prefix is present, -1 otherwise.</returns>
- public static int GetStringPrefixLength(this DiscordMessage msg, string str, StringComparison comparisonType = StringComparison.Ordinal)
+ public static class CommandsNextUtilities
{
- var content = msg.Content;
- return str.Length >= content.Length ? -1 : !content.StartsWith(str, comparisonType) ? -1 : str.Length;
- }
+ /// <summary>
+ /// Gets the user regex.
+ /// </summary>
+ private static Regex s_userRegex { get; } = DiscordRegEx.User;
+
+ /// <summary>
+ /// Checks whether the message has a specified string prefix.
+ /// </summary>
+ /// <param name="msg">Message to check.</param>
+ /// <param name="str">String to check for.</param>
+ /// <param name="comparisonType">Method of string comparison for the purposes of finding prefixes.</param>
+ /// <returns>Positive number if the prefix is present, -1 otherwise.</returns>
+ public static int GetStringPrefixLength(this DiscordMessage msg, string str, StringComparison comparisonType = StringComparison.Ordinal)
+ {
+ var content = msg.Content;
+ return str.Length >= content.Length ? -1 : !content.StartsWith(str, comparisonType) ? -1 : str.Length;
+ }
- /// <summary>
- /// Checks whether the message contains a specified mention prefix.
- /// </summary>
- /// <param name="msg">Message to check.</param>
- /// <param name="user">User to check for.</param>
- /// <returns>Positive number if the prefix is present, -1 otherwise.</returns>
- public static int GetMentionPrefixLength(this DiscordMessage msg, DiscordUser user)
- {
- var content = msg.Content;
- if (!content.StartsWith("<@"))
- return -1;
+ /// <summary>
+ /// Checks whether the message contains a specified mention prefix.
+ /// </summary>
+ /// <param name="msg">Message to check.</param>
+ /// <param name="user">User to check for.</param>
+ /// <returns>Positive number if the prefix is present, -1 otherwise.</returns>
+ public static int GetMentionPrefixLength(this DiscordMessage msg, DiscordUser user)
+ {
+ var content = msg.Content;
+ if (!content.StartsWith("<@"))
+ return -1;
- var cni = content.IndexOf('>');
- if (cni == -1 || content.Length <= cni + 1)
- return -1;
+ var cni = content.IndexOf('>');
+ if (cni == -1 || content.Length <= cni + 1)
+ return -1;
- var cnp = content[..(cni + 1)];
- var m = s_userRegex.Match(cnp);
- if (!m.Success)
- return -1;
+ var cnp = content[..(cni + 1)];
+ var m = s_userRegex.Match(cnp);
+ if (!m.Success)
+ return -1;
- var userId = ulong.Parse(m.Groups[1].Value, CultureInfo.InvariantCulture);
- return user.Id != userId ? -1 : m.Value.Length;
- }
+ var userId = ulong.Parse(m.Groups[1].Value, CultureInfo.InvariantCulture);
+ return user.Id != userId ? -1 : m.Value.Length;
+ }
- //internal static string ExtractNextArgument(string str, out string remainder)
- /// <summary>
- /// Extracts the next argument.
- /// </summary>
- /// <param name="str">The string.</param>
- /// <param name="startPos">The start position.</param>
- internal static string ExtractNextArgument(this string str, ref int startPos)
- {
- if (string.IsNullOrWhiteSpace(str))
- return null;
-
- var inBacktick = false;
- var inTripleBacktick = false;
- var inQuote = false;
- var inEscape = false;
- var removeIndices = new List<int>(str.Length - startPos);
-
- var i = startPos;
- for (; i < str.Length; i++)
- if (!char.IsWhiteSpace(str[i]))
- break;
- startPos = i;
-
- var endPosition = -1;
- var startPosition = startPos;
- for (i = startPosition; i < str.Length; i++)
+ //internal static string ExtractNextArgument(string str, out string remainder)
+ /// <summary>
+ /// Extracts the next argument.
+ /// </summary>
+ /// <param name="str">The string.</param>
+ /// <param name="startPos">The start position.</param>
+ internal static string ExtractNextArgument(this string str, ref int startPos)
{
- if (char.IsWhiteSpace(str[i]) && !inQuote && !inTripleBacktick && !inBacktick && !inEscape)
- endPosition = i;
+ if (string.IsNullOrWhiteSpace(str))
+ return null;
+
+ var inBacktick = false;
+ var inTripleBacktick = false;
+ var inQuote = false;
+ var inEscape = false;
+ var removeIndices = new List<int>(str.Length - startPos);
+
+ var i = startPos;
+ for (; i < str.Length; i++)
+ if (!char.IsWhiteSpace(str[i]))
+ break;
+ startPos = i;
- if (str[i] == '\\' && str.Length > i + 1)
+ var endPosition = -1;
+ var startPosition = startPos;
+ for (i = startPosition; i < str.Length; i++)
{
- if (!inEscape && !inBacktick && !inTripleBacktick)
+ if (char.IsWhiteSpace(str[i]) && !inQuote && !inTripleBacktick && !inBacktick && !inEscape)
+ endPosition = i;
+
+ if (str[i] == '\\' && str.Length > i + 1)
{
- inEscape = true;
- if (str.IndexOf("\\`", i) == i || str.IndexOf("\\\"", i) == i || str.IndexOf("\\\\", i) == i || (str.Length >= i && char.IsWhiteSpace(str[i + 1])))
+ if (!inEscape && !inBacktick && !inTripleBacktick)
+ {
+ inEscape = true;
+ if (str.IndexOf("\\`", i) == i || str.IndexOf("\\\"", i) == i || str.IndexOf("\\\\", i) == i || (str.Length >= i && char.IsWhiteSpace(str[i + 1])))
+ removeIndices.Add(i - startPosition);
+ i++;
+ }
+ else if ((inBacktick || inTripleBacktick) && str.IndexOf("\\`", i) == i)
+ {
+ inEscape = true;
removeIndices.Add(i - startPosition);
- i++;
- }
- else if ((inBacktick || inTripleBacktick) && str.IndexOf("\\`", i) == i)
- {
- inEscape = true;
- removeIndices.Add(i - startPosition);
- i++;
+ i++;
+ }
}
- }
- if (str[i] == '`' && !inEscape)
- {
- var tripleBacktick = str.IndexOf("```", i) == i;
- if (inTripleBacktick && tripleBacktick)
+ if (str[i] == '`' && !inEscape)
{
- inTripleBacktick = false;
- i += 2;
+ var tripleBacktick = str.IndexOf("```", i) == i;
+ if (inTripleBacktick && tripleBacktick)
+ {
+ inTripleBacktick = false;
+ i += 2;
+ }
+ else if (!inBacktick && tripleBacktick)
+ {
+ inTripleBacktick = true;
+ i += 2;
+ }
+
+ if (inBacktick && !tripleBacktick)
+ inBacktick = false;
+ else if (!inTripleBacktick && tripleBacktick)
+ inBacktick = true;
}
- else if (!inBacktick && tripleBacktick)
+
+ if (str[i] == '"' && !inEscape && !inBacktick && !inTripleBacktick)
{
- inTripleBacktick = true;
- i += 2;
- }
+ removeIndices.Add(i - startPosition);
- if (inBacktick && !tripleBacktick)
- inBacktick = false;
- else if (!inTripleBacktick && tripleBacktick)
- inBacktick = true;
- }
+ inQuote = !inQuote;
+ }
- if (str[i] == '"' && !inEscape && !inBacktick && !inTripleBacktick)
- {
- removeIndices.Add(i - startPosition);
+ if (inEscape)
+ inEscape = false;
- inQuote = !inQuote;
+ if (endPosition != -1)
+ {
+ startPos = endPosition;
+ return startPosition != endPosition ? str[startPosition..endPosition].CleanupString(removeIndices) : null;
+ }
}
- if (inEscape)
- inEscape = false;
-
- if (endPosition != -1)
- {
- startPos = endPosition;
- return startPosition != endPosition ? str[startPosition..endPosition].CleanupString(removeIndices) : null;
- }
+ startPos = str.Length;
+ return startPos != startPosition ? str[startPosition..].CleanupString(removeIndices) : null;
}
- startPos = str.Length;
- return startPos != startPosition ? str[startPosition..].CleanupString(removeIndices) : null;
- }
-
- /// <summary>
- /// Cleanups the string.
- /// </summary>
- /// <param name="s">The string.</param>
- /// <param name="indices">The indices.</param>
- internal static string CleanupString(this string s, IList<int> indices)
- {
- if (!indices.Any())
- return s;
-
- var li = indices.Last();
- var ll = 1;
- for (var x = indices.Count - 2; x >= 0; x--)
+ /// <summary>
+ /// Cleanups the string.
+ /// </summary>
+ /// <param name="s">The string.</param>
+ /// <param name="indices">The indices.</param>
+ internal static string CleanupString(this string s, IList<int> indices)
{
- if (li - indices[x] == ll)
+ if (!indices.Any())
+ return s;
+
+ var li = indices.Last();
+ var ll = 1;
+ for (var x = indices.Count - 2; x >= 0; x--)
{
- ll++;
- continue;
+ if (li - indices[x] == ll)
+ {
+ ll++;
+ continue;
+ }
+
+ s = s.Remove(li - ll + 1, ll);
+ li = indices[x];
+ ll = 1;
}
- s = s.Remove(li - ll + 1, ll);
- li = indices[x];
- ll = 1;
+ return s.Remove(li - ll + 1, ll);
}
- return s.Remove(li - ll + 1, ll);
- }
-
- /// <summary>
- /// Binds the arguments.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- /// <param name="ignoreSurplus">If true, ignore further text in string.</param>
- internal static async Task<ArgumentBindingResult> BindArguments(CommandContext ctx, bool ignoreSurplus)
- {
- var command = ctx.Command;
- var overload = ctx.Overload;
+ /// <summary>
+ /// Binds the arguments.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ /// <param name="ignoreSurplus">If true, ignore further text in string.</param>
+ internal static async Task<ArgumentBindingResult> BindArguments(CommandContext ctx, bool ignoreSurplus)
+ {
+ var command = ctx.Command;
+ var overload = ctx.Overload;
- var args = new object[overload.Arguments.Count + 2];
- args[1] = ctx;
- var rawArgumentList = new List<string>(overload.Arguments.Count);
+ var args = new object[overload.Arguments.Count + 2];
+ args[1] = ctx;
+ var rawArgumentList = new List<string>(overload.Arguments.Count);
- var argString = ctx.RawArgumentString;
- var foundAt = 0;
- var argValue = "";
- for (var i = 0; i < overload.Arguments.Count; i++)
- {
- var arg = overload.Arguments[i];
- if (arg.IsCatchAll)
+ var argString = ctx.RawArgumentString;
+ var foundAt = 0;
+ var argValue = "";
+ for (var i = 0; i < overload.Arguments.Count; i++)
{
- if (arg.IsArray)
+ var arg = overload.Arguments[i];
+ if (arg.IsCatchAll)
{
- while (true)
+ if (arg.IsArray)
+ {
+ while (true)
+ {
+ argValue = ExtractNextArgument(argString, ref foundAt);
+ if (argValue == null)
+ break;
+
+ rawArgumentList.Add(argValue);
+ }
+
+ break;
+ }
+ else
{
- argValue = ExtractNextArgument(argString, ref foundAt);
- if (argValue == null)
+ if (argString == null)
break;
+ argValue = argString[foundAt..].Trim();
+ argValue = argValue == "" ? null : argValue;
+ foundAt = argString.Length;
+
rawArgumentList.Add(argValue);
+ break;
}
-
- break;
}
else
{
- if (argString == null)
- break;
-
- argValue = argString[foundAt..].Trim();
- argValue = argValue == "" ? null : argValue;
- foundAt = argString.Length;
-
+ argValue = ExtractNextArgument(argString, ref foundAt);
rawArgumentList.Add(argValue);
- break;
}
- }
- else
- {
- argValue = ExtractNextArgument(argString, ref foundAt);
- rawArgumentList.Add(argValue);
- }
- if (argValue == null && !arg.IsOptional && !arg.IsCatchAll)
- return new ArgumentBindingResult(new ArgumentException("Not enough arguments supplied to the command."));
- else if (argValue == null)
- rawArgumentList.Add(null);
- }
+ if (argValue == null && !arg.IsOptional && !arg.IsCatchAll)
+ return new ArgumentBindingResult(new ArgumentException("Not enough arguments supplied to the command."));
+ else if (argValue == null)
+ rawArgumentList.Add(null);
+ }
- if (!ignoreSurplus && foundAt < argString.Length)
- return new ArgumentBindingResult(new ArgumentException("Too many arguments were supplied to this command."));
+ if (!ignoreSurplus && foundAt < argString.Length)
+ return new ArgumentBindingResult(new ArgumentException("Too many arguments were supplied to this command."));
- for (var i = 0; i < overload.Arguments.Count; i++)
- {
- var arg = overload.Arguments[i];
- if (arg.IsCatchAll && arg.IsArray)
+ for (var i = 0; i < overload.Arguments.Count; i++)
{
- var array = Array.CreateInstance(arg.Type, rawArgumentList.Count - i);
- var start = i;
- while (i < rawArgumentList.Count)
+ var arg = overload.Arguments[i];
+ if (arg.IsCatchAll && arg.IsArray)
+ {
+ var array = Array.CreateInstance(arg.Type, rawArgumentList.Count - i);
+ var start = i;
+ while (i < rawArgumentList.Count)
+ {
+ try
+ {
+ array.SetValue(await ctx.CommandsNext.ConvertArgument(rawArgumentList[i], ctx, arg.Type).ConfigureAwait(false), i - start);
+ }
+ catch (Exception ex)
+ {
+ return new ArgumentBindingResult(ex);
+ }
+ i++;
+ }
+
+ args[start + 2] = array;
+ break;
+ }
+ else
{
try
{
- array.SetValue(await ctx.CommandsNext.ConvertArgument(rawArgumentList[i], ctx, arg.Type).ConfigureAwait(false), i - start);
+ args[i + 2] = rawArgumentList[i] != null ? await ctx.CommandsNext.ConvertArgument(rawArgumentList[i], ctx, arg.Type).ConfigureAwait(false) : arg.DefaultValue;
}
catch (Exception ex)
{
return new ArgumentBindingResult(ex);
}
- i++;
}
-
- args[start + 2] = array;
- break;
}
- else
- {
- try
- {
- args[i + 2] = rawArgumentList[i] != null ? await ctx.CommandsNext.ConvertArgument(rawArgumentList[i], ctx, arg.Type).ConfigureAwait(false) : arg.DefaultValue;
- }
- catch (Exception ex)
- {
- return new ArgumentBindingResult(ex);
- }
- }
- }
- return new ArgumentBindingResult(args, rawArgumentList);
- }
+ return new ArgumentBindingResult(args, rawArgumentList);
+ }
- /// <summary>
- /// Whether this module is a candidate type.
- /// </summary>
- /// <param name="type">The type.</param>
- internal static bool IsModuleCandidateType(this Type type)
- => type.GetTypeInfo().IsModuleCandidateType();
+ /// <summary>
+ /// Whether this module is a candidate type.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ internal static bool IsModuleCandidateType(this Type type)
+ => type.GetTypeInfo().IsModuleCandidateType();
+
+ /// <summary>
+ /// Whether this module is a candidate type.
+ /// </summary>
+ /// <param name="ti">The type info.</param>
+ internal static bool IsModuleCandidateType(this TypeInfo ti)
+ {
+ // check if compiler-generated
+ if (ti.GetCustomAttribute<CompilerGeneratedAttribute>(false) != null)
+ return false;
+
+ // check if derives from the required base class
+ var tmodule = typeof(BaseCommandModule);
+ var timodule = tmodule.GetTypeInfo();
+ if (!timodule.IsAssignableFrom(ti))
+ return false;
+
+ // check if anonymous
+ if (ti.IsGenericType && ti.Name.Contains("AnonymousType") && (ti.Name.StartsWith("<>") || ti.Name.StartsWith("VB$")) && (ti.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic)
+ return false;
+
+ // check if abstract, static, or not a class
+ if (!ti.IsClass || ti.IsAbstract)
+ return false;
+
+ // check if delegate type
+ var tdelegate = typeof(Delegate).GetTypeInfo();
+ if (tdelegate.IsAssignableFrom(ti))
+ return false;
+
+ // qualifies if any method or type qualifies
+ return ti.DeclaredMethods.Any(xmi => xmi.IsCommandCandidate(out _)) || ti.DeclaredNestedTypes.Any(xti => xti.IsModuleCandidateType());
+ }
- /// <summary>
- /// Whether this module is a candidate type.
- /// </summary>
- /// <param name="ti">The type info.</param>
- internal static bool IsModuleCandidateType(this TypeInfo ti)
- {
- // check if compiler-generated
- if (ti.GetCustomAttribute<CompilerGeneratedAttribute>(false) != null)
- return false;
-
- // check if derives from the required base class
- var tmodule = typeof(BaseCommandModule);
- var timodule = tmodule.GetTypeInfo();
- if (!timodule.IsAssignableFrom(ti))
- return false;
-
- // check if anonymous
- if (ti.IsGenericType && ti.Name.Contains("AnonymousType") && (ti.Name.StartsWith("<>") || ti.Name.StartsWith("VB$")) && (ti.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic)
- return false;
-
- // check if abstract, static, or not a class
- if (!ti.IsClass || ti.IsAbstract)
- return false;
-
- // check if delegate type
- var tdelegate = typeof(Delegate).GetTypeInfo();
- if (tdelegate.IsAssignableFrom(ti))
- return false;
-
- // qualifies if any method or type qualifies
- return ti.DeclaredMethods.Any(xmi => xmi.IsCommandCandidate(out _)) || ti.DeclaredNestedTypes.Any(xti => xti.IsModuleCandidateType());
- }
+ /// <summary>
+ /// Whether this is a command candidate.
+ /// </summary>
+ /// <param name="method">The method.</param>
+ /// <param name="parameters">The parameters.</param>
+ internal static bool IsCommandCandidate(this MethodInfo method, out ParameterInfo[] parameters)
+ {
+ parameters = null;
+ // check if exists
+ if (method == null)
+ return false;
+
+ // check if static, non-public, abstract, a constructor, or a special name
+ if (method.IsStatic || method.IsAbstract || method.IsConstructor || method.IsSpecialName)
+ return false;
+
+ // check if appropriate return and arguments
+ parameters = method.GetParameters();
+ if (!parameters.Any() || parameters.First().ParameterType != typeof(CommandContext) || method.ReturnType != typeof(Task))
+ return false;
+
+ // qualifies
+ return true;
+ }
- /// <summary>
- /// Whether this is a command candidate.
- /// </summary>
- /// <param name="method">The method.</param>
- /// <param name="parameters">The parameters.</param>
- internal static bool IsCommandCandidate(this MethodInfo method, out ParameterInfo[] parameters)
- {
- parameters = null;
- // check if exists
- if (method == null)
- return false;
-
- // check if static, non-public, abstract, a constructor, or a special name
- if (method.IsStatic || method.IsAbstract || method.IsConstructor || method.IsSpecialName)
- return false;
-
- // check if appropriate return and arguments
- parameters = method.GetParameters();
- if (!parameters.Any() || parameters.First().ParameterType != typeof(CommandContext) || method.ReturnType != typeof(Task))
- return false;
-
- // qualifies
- return true;
- }
+ /// <summary>
+ /// Creates the instance.
+ /// </summary>
+ /// <param name="t">The type.</param>
+ /// <param name="services">The services provider.</param>
+ internal static object CreateInstance(this Type t, IServiceProvider services)
+ {
+ var ti = t.GetTypeInfo();
+ var constructors = ti.DeclaredConstructors
+ .Where(xci => xci.IsPublic)
+ .ToArray();
- /// <summary>
- /// Creates the instance.
- /// </summary>
- /// <param name="t">The type.</param>
- /// <param name="services">The services provider.</param>
- internal static object CreateInstance(this Type t, IServiceProvider services)
- {
- var ti = t.GetTypeInfo();
- var constructors = ti.DeclaredConstructors
- .Where(xci => xci.IsPublic)
- .ToArray();
+ if (constructors.Length != 1)
+ throw new ArgumentException("Specified type does not contain a public constructor or contains more than one public constructor.");
- if (constructors.Length != 1)
- throw new ArgumentException("Specified type does not contain a public constructor or contains more than one public constructor.");
+ var constructor = constructors[0];
+ var constructorArgs = constructor.GetParameters();
+ var args = new object[constructorArgs.Length];
- var constructor = constructors[0];
- var constructorArgs = constructor.GetParameters();
- var args = new object[constructorArgs.Length];
+ if (constructorArgs.Length != 0 && services == null)
+ throw new InvalidOperationException("Dependency collection needs to be specified for parameterized constructors.");
- if (constructorArgs.Length != 0 && services == null)
- throw new InvalidOperationException("Dependency collection needs to be specified for parameterized constructors.");
+ // inject via constructor
+ if (constructorArgs.Length != 0)
+ for (var i = 0; i < args.Length; i++)
+ args[i] = services.GetRequiredService(constructorArgs[i].ParameterType);
- // inject via constructor
- if (constructorArgs.Length != 0)
- for (var i = 0; i < args.Length; i++)
- args[i] = services.GetRequiredService(constructorArgs[i].ParameterType);
+ var moduleInstance = Activator.CreateInstance(t, args);
- var moduleInstance = Activator.CreateInstance(t, args);
+ // inject into properties
+ var props = t.GetRuntimeProperties().Where(xp => xp.CanWrite && xp.SetMethod != null && !xp.SetMethod.IsStatic && xp.SetMethod.IsPublic);
+ foreach (var prop in props)
+ {
+ if (prop.GetCustomAttribute<DontInjectAttribute>() != null)
+ continue;
- // inject into properties
- var props = t.GetRuntimeProperties().Where(xp => xp.CanWrite && xp.SetMethod != null && !xp.SetMethod.IsStatic && xp.SetMethod.IsPublic);
- foreach (var prop in props)
- {
- if (prop.GetCustomAttribute<DontInjectAttribute>() != null)
- continue;
+ var service = services.GetService(prop.PropertyType);
+ if (service == null)
+ continue;
- var service = services.GetService(prop.PropertyType);
- if (service == null)
- continue;
+ prop.SetValue(moduleInstance, service);
+ }
- prop.SetValue(moduleInstance, service);
- }
+ // inject into fields
+ var fields = t.GetRuntimeFields().Where(xf => !xf.IsInitOnly && !xf.IsStatic && xf.IsPublic);
+ foreach (var field in fields)
+ {
+ if (field.GetCustomAttribute<DontInjectAttribute>() != null)
+ continue;
- // inject into fields
- var fields = t.GetRuntimeFields().Where(xf => !xf.IsInitOnly && !xf.IsStatic && xf.IsPublic);
- foreach (var field in fields)
- {
- if (field.GetCustomAttribute<DontInjectAttribute>() != null)
- continue;
+ var service = services.GetService(field.FieldType);
+ if (service == null)
+ continue;
- var service = services.GetService(field.FieldType);
- if (service == null)
- continue;
+ field.SetValue(moduleInstance, service);
+ }
- field.SetValue(moduleInstance, service);
+ return moduleInstance;
}
-
- return moduleInstance;
}
}
diff --git a/DisCatSharp.CommandsNext/Converters/ArgumentBindingResult.cs b/DisCatSharp.CommandsNext/Converters/ArgumentBindingResult.cs
index 0f898cfa8..1d21b9c49 100644
--- a/DisCatSharp.CommandsNext/Converters/ArgumentBindingResult.cs
+++ b/DisCatSharp.CommandsNext/Converters/ArgumentBindingResult.cs
@@ -1,74 +1,75 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Represents a argument binding result.
-/// </summary>
-public struct ArgumentBindingResult
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Gets a value indicating whether the binding is successful.
- /// </summary>
- public bool IsSuccessful { get; }
- /// <summary>
- /// Gets the converted.
- /// </summary>
- public object[] Converted { get; }
- /// <summary>
- /// Gets the raw.
- /// </summary>
- public IReadOnlyList<string> Raw { get; }
- /// <summary>
- /// Gets the reason.
+ /// Represents a argument binding result.
/// </summary>
- public Exception Reason { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ArgumentBindingResult"/> class.
- /// </summary>
- /// <param name="converted">The converted.</param>
- /// <param name="raw">The raw.</param>
- public ArgumentBindingResult(object[] converted, IReadOnlyList<string> raw)
+ public struct ArgumentBindingResult
{
- this.IsSuccessful = true;
- this.Reason = null;
- this.Converted = converted;
- this.Raw = raw;
- }
+ /// <summary>
+ /// Gets a value indicating whether the binding is successful.
+ /// </summary>
+ public bool IsSuccessful { get; }
+ /// <summary>
+ /// Gets the converted.
+ /// </summary>
+ public object[] Converted { get; }
+ /// <summary>
+ /// Gets the raw.
+ /// </summary>
+ public IReadOnlyList<string> Raw { get; }
+ /// <summary>
+ /// Gets the reason.
+ /// </summary>
+ public Exception Reason { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ArgumentBindingResult"/> class.
- /// </summary>
- /// <param name="ex">The ex.</param>
- public ArgumentBindingResult(Exception ex)
- {
- this.IsSuccessful = false;
- this.Reason = ex;
- this.Converted = null;
- this.Raw = null;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ArgumentBindingResult"/> class.
+ /// </summary>
+ /// <param name="converted">The converted.</param>
+ /// <param name="raw">The raw.</param>
+ public ArgumentBindingResult(object[] converted, IReadOnlyList<string> raw)
+ {
+ this.IsSuccessful = true;
+ this.Reason = null;
+ this.Converted = converted;
+ this.Raw = raw;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ArgumentBindingResult"/> class.
+ /// </summary>
+ /// <param name="ex">The ex.</param>
+ public ArgumentBindingResult(Exception ex)
+ {
+ this.IsSuccessful = false;
+ this.Reason = ex;
+ this.Converted = null;
+ this.Raw = null;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Converters/BaseHelpFormatter.cs b/DisCatSharp.CommandsNext/Converters/BaseHelpFormatter.cs
index 43406f345..07d39c97b 100644
--- a/DisCatSharp.CommandsNext/Converters/BaseHelpFormatter.cs
+++ b/DisCatSharp.CommandsNext/Converters/BaseHelpFormatter.cs
@@ -1,72 +1,73 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.CommandsNext.Entities;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Represents a base class for all default help formatters.
-/// </summary>
-public abstract class BaseHelpFormatter
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Gets the context in which this formatter is being invoked.
+ /// Represents a base class for all default help formatters.
/// </summary>
- protected CommandContext Context { get; }
+ public abstract class BaseHelpFormatter
+ {
+ /// <summary>
+ /// Gets the context in which this formatter is being invoked.
+ /// </summary>
+ protected CommandContext Context { get; }
- /// <summary>
- /// Gets the CommandsNext extension which constructed this help formatter.
- /// </summary>
- protected CommandsNextExtension CommandsNext => this.Context.CommandsNext;
+ /// <summary>
+ /// Gets the CommandsNext extension which constructed this help formatter.
+ /// </summary>
+ protected CommandsNextExtension CommandsNext => this.Context.CommandsNext;
- /// <summary>
- /// Creates a new help formatter for specified CommandsNext extension instance.
- /// </summary>
- /// <param name="ctx">Context in which this formatter is being invoked.</param>
- public BaseHelpFormatter(CommandContext ctx)
- {
- this.Context = ctx;
- }
+ /// <summary>
+ /// Creates a new help formatter for specified CommandsNext extension instance.
+ /// </summary>
+ /// <param name="ctx">Context in which this formatter is being invoked.</param>
+ public BaseHelpFormatter(CommandContext ctx)
+ {
+ this.Context = ctx;
+ }
- /// <summary>
- /// Sets the command this help message will be for.
- /// </summary>
- /// <param name="command">Command for which the help message is being produced.</param>
- /// <returns>This help formatter.</returns>
- public abstract BaseHelpFormatter WithCommand(Command command);
+ /// <summary>
+ /// Sets the command this help message will be for.
+ /// </summary>
+ /// <param name="command">Command for which the help message is being produced.</param>
+ /// <returns>This help formatter.</returns>
+ public abstract BaseHelpFormatter WithCommand(Command command);
- /// <summary>
- /// Sets the subcommands for this command, if applicable. This method will be called with filtered data.
- /// </summary>
- /// <param name="subcommands">Subcommands for this command group.</param>
- /// <returns>This help formatter.</returns>
- public abstract BaseHelpFormatter WithSubcommands(IEnumerable<Command> subcommands);
+ /// <summary>
+ /// Sets the subcommands for this command, if applicable. This method will be called with filtered data.
+ /// </summary>
+ /// <param name="subcommands">Subcommands for this command group.</param>
+ /// <returns>This help formatter.</returns>
+ public abstract BaseHelpFormatter WithSubcommands(IEnumerable<Command> subcommands);
- /// <summary>
- /// Constructs the help message.
- /// </summary>
- /// <returns>Data for the help message.</returns>
- public abstract CommandHelpMessage Build();
+ /// <summary>
+ /// Constructs the help message.
+ /// </summary>
+ /// <returns>Data for the help message.</returns>
+ public abstract CommandHelpMessage Build();
+ }
}
diff --git a/DisCatSharp.CommandsNext/Converters/DefaultHelpFormatter.cs b/DisCatSharp.CommandsNext/Converters/DefaultHelpFormatter.cs
index a015b6971..fac3006fb 100644
--- a/DisCatSharp.CommandsNext/Converters/DefaultHelpFormatter.cs
+++ b/DisCatSharp.CommandsNext/Converters/DefaultHelpFormatter.cs
@@ -1,124 +1,125 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Linq;
using System.Text;
using DisCatSharp.CommandsNext.Entities;
using DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Default CommandsNext help formatter.
-/// </summary>
-public class DefaultHelpFormatter : BaseHelpFormatter
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Gets the embed builder.
- /// </summary>
- public DiscordEmbedBuilder EmbedBuilder { get; }
-
- /// <summary>
- /// Gets or sets the command.
- /// </summary>
- private Command _command;
-
- /// <summary>
- /// Creates a new default help formatter.
- /// </summary>
- /// <param name="ctx">Context in which this formatter is being invoked.</param>
- public DefaultHelpFormatter(CommandContext ctx)
- : base(ctx)
- {
- this.EmbedBuilder = new DiscordEmbedBuilder()
- .WithTitle("Help")
- .WithColor(0x007FFF);
- }
-
- /// <summary>
- /// Sets the command this help message will be for.
+ /// Default CommandsNext help formatter.
/// </summary>
- /// <param name="command">Command for which the help message is being produced.</param>
- /// <returns>This help formatter.</returns>
- public override BaseHelpFormatter WithCommand(Command command)
+ public class DefaultHelpFormatter : BaseHelpFormatter
{
- this._command = command;
+ /// <summary>
+ /// Gets the embed builder.
+ /// </summary>
+ public DiscordEmbedBuilder EmbedBuilder { get; }
+
+ /// <summary>
+ /// Gets or sets the command.
+ /// </summary>
+ private Command _command;
+
+ /// <summary>
+ /// Creates a new default help formatter.
+ /// </summary>
+ /// <param name="ctx">Context in which this formatter is being invoked.</param>
+ public DefaultHelpFormatter(CommandContext ctx)
+ : base(ctx)
+ {
+ this.EmbedBuilder = new DiscordEmbedBuilder()
+ .WithTitle("Help")
+ .WithColor(0x007FFF);
+ }
- this.EmbedBuilder.WithDescription($"{Formatter.InlineCode(command.Name)}: {command.Description ?? "No description provided."}");
+ /// <summary>
+ /// Sets the command this help message will be for.
+ /// </summary>
+ /// <param name="command">Command for which the help message is being produced.</param>
+ /// <returns>This help formatter.</returns>
+ public override BaseHelpFormatter WithCommand(Command command)
+ {
+ this._command = command;
- if (command is CommandGroup cgroup && cgroup.IsExecutableWithoutSubcommands)
- this.EmbedBuilder.WithDescription($"{this.EmbedBuilder.Description}\n\nThis group can be executed as a standalone command.");
+ this.EmbedBuilder.WithDescription($"{Formatter.InlineCode(command.Name)}: {command.Description ?? "No description provided."}");
- if (command.Aliases?.Any() == true)
- this.EmbedBuilder.AddField(new DiscordEmbedField("Aliases", string.Join(", ", command.Aliases.Select(Formatter.InlineCode))));
+ if (command is CommandGroup cgroup && cgroup.IsExecutableWithoutSubcommands)
+ this.EmbedBuilder.WithDescription($"{this.EmbedBuilder.Description}\n\nThis group can be executed as a standalone command.");
- if (command.Overloads?.Any() == true)
- {
- var sb = new StringBuilder();
+ if (command.Aliases?.Any() == true)
+ this.EmbedBuilder.AddField(new DiscordEmbedField("Aliases", string.Join(", ", command.Aliases.Select(Formatter.InlineCode))));
- foreach (var ovl in command.Overloads.OrderByDescending(x => x.Priority))
+ if (command.Overloads?.Any() == true)
{
- sb.Append('`').Append(command.QualifiedName);
+ var sb = new StringBuilder();
- foreach (var arg in ovl.Arguments)
- sb.Append(arg.IsOptional || arg.IsCatchAll ? " [" : " <").Append(arg.Name).Append(arg.IsCatchAll ? "..." : "").Append(arg.IsOptional || arg.IsCatchAll ? ']' : '>');
+ foreach (var ovl in command.Overloads.OrderByDescending(x => x.Priority))
+ {
+ sb.Append('`').Append(command.QualifiedName);
- sb.Append("`\n");
+ foreach (var arg in ovl.Arguments)
+ sb.Append(arg.IsOptional || arg.IsCatchAll ? " [" : " <").Append(arg.Name).Append(arg.IsCatchAll ? "..." : "").Append(arg.IsOptional || arg.IsCatchAll ? ']' : '>');
- foreach (var arg in ovl.Arguments)
- sb.Append('`').Append(arg.Name).Append(" (").Append(this.CommandsNext.GetUserFriendlyTypeName(arg.Type)).Append(")`: ").Append(arg.Description ?? "No description provided.").Append('\n');
+ sb.Append("`\n");
- sb.Append('\n');
+ foreach (var arg in ovl.Arguments)
+ sb.Append('`').Append(arg.Name).Append(" (").Append(this.CommandsNext.GetUserFriendlyTypeName(arg.Type)).Append(")`: ").Append(arg.Description ?? "No description provided.").Append('\n');
+
+ sb.Append('\n');
+ }
+
+ this.EmbedBuilder.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim()));
}
- this.EmbedBuilder.AddField(new DiscordEmbedField("Arguments", sb.ToString().Trim()));
+ return this;
}
- return this;
- }
-
- /// <summary>
- /// Sets the subcommands for this command, if applicable. This method will be called with filtered data.
- /// </summary>
- /// <param name="subcommands">Subcommands for this command group.</param>
- /// <returns>This help formatter.</returns>
- public override BaseHelpFormatter WithSubcommands(IEnumerable<Command> subcommands)
- {
- this.EmbedBuilder.AddField(new DiscordEmbedField(this._command != null ? "Subcommands" : "Commands", string.Join(", ", subcommands.Select(x => Formatter.InlineCode(x.Name)))));
+ /// <summary>
+ /// Sets the subcommands for this command, if applicable. This method will be called with filtered data.
+ /// </summary>
+ /// <param name="subcommands">Subcommands for this command group.</param>
+ /// <returns>This help formatter.</returns>
+ public override BaseHelpFormatter WithSubcommands(IEnumerable<Command> subcommands)
+ {
+ this.EmbedBuilder.AddField(new DiscordEmbedField(this._command != null ? "Subcommands" : "Commands", string.Join(", ", subcommands.Select(x => Formatter.InlineCode(x.Name)))));
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Construct the help message.
- /// </summary>
- /// <returns>Data for the help message.</returns>
- public override CommandHelpMessage Build()
- {
- if (this._command == null)
- this.EmbedBuilder.WithDescription("Listing all top-level commands and groups. Specify a command to see more information.");
+ /// <summary>
+ /// Construct the help message.
+ /// </summary>
+ /// <returns>Data for the help message.</returns>
+ public override CommandHelpMessage Build()
+ {
+ if (this._command == null)
+ this.EmbedBuilder.WithDescription("Listing all top-level commands and groups. Specify a command to see more information.");
- return new CommandHelpMessage(embed: this.EmbedBuilder.Build());
+ return new CommandHelpMessage(embed: this.EmbedBuilder.Build());
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Converters/EntityConverters.cs b/DisCatSharp.CommandsNext/Converters/EntityConverters.cs
index acc4ea3a4..dd7f6967c 100644
--- a/DisCatSharp.CommandsNext/Converters/EntityConverters.cs
+++ b/DisCatSharp.CommandsNext/Converters/EntityConverters.cs
@@ -1,456 +1,457 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DisCatSharp.Common.RegularExpressions;
using DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Represents a discord user converter.
-/// </summary>
-public class DiscordUserConverter : IArgumentConverter<DiscordUser>
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Converts a string.
+ /// Represents a discord user converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- async Task<Optional<DiscordUser>> IArgumentConverter<DiscordUser>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordUserConverter : IArgumentConverter<DiscordUser>
{
- if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var uid))
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ async Task<Optional<DiscordUser>> IArgumentConverter<DiscordUser>.ConvertAsync(string value, CommandContext ctx)
{
- var result = await ctx.Client.GetUserAsync(uid).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var uid))
+ {
+ var result = await ctx.Client.GetUserAsync(uid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
- var m = DiscordRegEx.User.Match(value);
- if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out uid))
- {
- var result = await ctx.Client.GetUserAsync(uid).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ var m = DiscordRegEx.User.Match(value);
+ if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out uid))
+ {
+ var result = await ctx.Client.GetUserAsync(uid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
- var cs = ctx.Config.CaseSensitive;
- if (!cs)
- value = value.ToLowerInvariant();
+ var cs = ctx.Config.CaseSensitive;
+ if (!cs)
+ value = value.ToLowerInvariant();
- var di = value.IndexOf('#');
- var un = di != -1 ? value[..di] : value;
- var dv = di != -1 ? value[(di + 1)..] : null;
+ var di = value.IndexOf('#');
+ var un = di != -1 ? value[..di] : value;
+ var dv = di != -1 ? value[(di + 1)..] : null;
- var us = ctx.Client.Guilds.Values
- .SelectMany(xkvp => xkvp.Members.Values)
- .Where(xm => (cs ? xm.Username : xm.Username.ToLowerInvariant()) == un && ((dv != null && xm.Discriminator == dv) || dv == null));
+ var us = ctx.Client.Guilds.Values
+ .SelectMany(xkvp => xkvp.Members.Values)
+ .Where(xm => (cs ? xm.Username : xm.Username.ToLowerInvariant()) == un && ((dv != null && xm.Discriminator == dv) || dv == null));
- var usr = us.FirstOrDefault();
- return Optional.FromNullable<DiscordUser>(usr);
+ var usr = us.FirstOrDefault();
+ return Optional.FromNullable<DiscordUser>(usr);
+ }
}
-}
-/// <summary>
-/// Represents a discord member converter.
-/// </summary>
-public class DiscordMemberConverter : IArgumentConverter<DiscordMember>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord member converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- async Task<Optional<DiscordMember>> IArgumentConverter<DiscordMember>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordMemberConverter : IArgumentConverter<DiscordMember>
{
- if (ctx.Guild == null)
- return Optional.None;
-
- if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var uid))
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ async Task<Optional<DiscordMember>> IArgumentConverter<DiscordMember>.ConvertAsync(string value, CommandContext ctx)
{
- var result = await ctx.Guild.GetMemberAsync(uid).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ if (ctx.Guild == null)
+ return Optional.None;
- var m = DiscordRegEx.User.Match(value);
- if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out uid))
- {
- var result = await ctx.Guild.GetMemberAsync(uid).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var uid))
+ {
+ var result = await ctx.Guild.GetMemberAsync(uid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
+
+ var m = DiscordRegEx.User.Match(value);
+ if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out uid))
+ {
+ var result = await ctx.Guild.GetMemberAsync(uid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
- var searchResult = await ctx.Guild.SearchMembersAsync(value).ConfigureAwait(false);
- if (searchResult.Any())
- return Optional.Some(searchResult.First());
+ var searchResult = await ctx.Guild.SearchMembersAsync(value).ConfigureAwait(false);
+ if (searchResult.Any())
+ return Optional.Some(searchResult.First());
- var cs = ctx.Config.CaseSensitive;
- if (!cs)
- value = value.ToLowerInvariant();
+ var cs = ctx.Config.CaseSensitive;
+ if (!cs)
+ value = value.ToLowerInvariant();
- var di = value.IndexOf('#');
- var un = di != -1 ? value[..di] : value;
- var dv = di != -1 ? value[(di + 1)..] : null;
+ var di = value.IndexOf('#');
+ var un = di != -1 ? value[..di] : value;
+ var dv = di != -1 ? value[(di + 1)..] : null;
- var us = ctx.Guild.Members.Values
- .Where(xm => ((cs ? xm.Username : xm.Username.ToLowerInvariant()) == un && ((dv != null && xm.Discriminator == dv) || dv == null))
- || (cs ? xm.Nickname : xm.Nickname?.ToLowerInvariant()) == value);
+ var us = ctx.Guild.Members.Values
+ .Where(xm => ((cs ? xm.Username : xm.Username.ToLowerInvariant()) == un && ((dv != null && xm.Discriminator == dv) || dv == null))
+ || (cs ? xm.Nickname : xm.Nickname?.ToLowerInvariant()) == value);
- return Optional.FromNullable(us.FirstOrDefault());
+ return Optional.FromNullable(us.FirstOrDefault());
+ }
}
-}
-/// <summary>
-/// Represents a discord channel converter.
-/// </summary>
-public class DiscordChannelConverter : IArgumentConverter<DiscordChannel>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord channel converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- async Task<Optional<DiscordChannel>> IArgumentConverter<DiscordChannel>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordChannelConverter : IArgumentConverter<DiscordChannel>
{
- if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var cid))
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ async Task<Optional<DiscordChannel>> IArgumentConverter<DiscordChannel>.ConvertAsync(string value, CommandContext ctx)
{
- var result = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var cid))
+ {
+ var result = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
- var m = DiscordRegEx.Channel.Match(value);
- if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out cid))
- {
- var result = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ var m = DiscordRegEx.Channel.Match(value);
+ if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out cid))
+ {
+ var result = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
- var cs = ctx.Config.CaseSensitive;
- if (!cs)
- value = value.ToLowerInvariant();
+ var cs = ctx.Config.CaseSensitive;
+ if (!cs)
+ value = value.ToLowerInvariant();
- var chn = ctx.Guild?.Channels.Values.FirstOrDefault(xc => (cs ? xc.Name : xc.Name.ToLowerInvariant()) == value);
- return Optional.FromNullable(chn);
+ var chn = ctx.Guild?.Channels.Values.FirstOrDefault(xc => (cs ? xc.Name : xc.Name.ToLowerInvariant()) == value);
+ return Optional.FromNullable(chn);
+ }
}
-}
-/// <summary>
-/// Represents a discord thread channel converter.
-/// </summary>
-public class DiscordThreadChannelConverter : IArgumentConverter<DiscordThreadChannel>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord thread channel converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- async Task<Optional<DiscordThreadChannel>> IArgumentConverter<DiscordThreadChannel>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordThreadChannelConverter : IArgumentConverter<DiscordThreadChannel>
{
- if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var tid))
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ async Task<Optional<DiscordThreadChannel>> IArgumentConverter<DiscordThreadChannel>.ConvertAsync(string value, CommandContext ctx)
{
- var result = await ctx.Client.GetThreadAsync(tid).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var tid))
+ {
+ var result = await ctx.Client.GetThreadAsync(tid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
- var m = DiscordRegEx.Channel.Match(value);
- if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out tid))
- {
- var result = await ctx.Client.GetThreadAsync(tid).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ var m = DiscordRegEx.Channel.Match(value);
+ if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out tid))
+ {
+ var result = await ctx.Client.GetThreadAsync(tid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
- var cs = ctx.Config.CaseSensitive;
- if (!cs)
- value = value.ToLowerInvariant();
+ var cs = ctx.Config.CaseSensitive;
+ if (!cs)
+ value = value.ToLowerInvariant();
- var tchn = ctx.Guild?.Threads.Values.FirstOrDefault(xc => (cs ? xc.Name : xc.Name.ToLowerInvariant()) == value);
- return Optional.FromNullable(tchn);
+ var tchn = ctx.Guild?.Threads.Values.FirstOrDefault(xc => (cs ? xc.Name : xc.Name.ToLowerInvariant()) == value);
+ return Optional.FromNullable(tchn);
+ }
}
-}
-/// <summary>
-/// Represents a discord role converter.
-/// </summary>
-public class DiscordRoleConverter : IArgumentConverter<DiscordRole>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord role converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<DiscordRole>> IArgumentConverter<DiscordRole>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordRoleConverter : IArgumentConverter<DiscordRole>
{
- if (ctx.Guild == null)
- return Task.FromResult(Optional<DiscordRole>.None);
-
- if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var rid))
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<DiscordRole>> IArgumentConverter<DiscordRole>.ConvertAsync(string value, CommandContext ctx)
{
- var result = ctx.Guild.GetRole(rid);
- return Task.FromResult(Optional.FromNullable(result));
- }
+ if (ctx.Guild == null)
+ return Task.FromResult(Optional<DiscordRole>.None);
- var m = DiscordRegEx.Role.Match(value);
- if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out rid))
- {
- var result = ctx.Guild.GetRole(rid);
- return Task.FromResult(Optional.FromNullable(result));
- }
+ if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var rid))
+ {
+ var result = ctx.Guild.GetRole(rid);
+ return Task.FromResult(Optional.FromNullable(result));
+ }
+
+ var m = DiscordRegEx.Role.Match(value);
+ if (m.Success && ulong.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out rid))
+ {
+ var result = ctx.Guild.GetRole(rid);
+ return Task.FromResult(Optional.FromNullable(result));
+ }
- var cs = ctx.Config.CaseSensitive;
- if (!cs)
- value = value.ToLowerInvariant();
+ var cs = ctx.Config.CaseSensitive;
+ if (!cs)
+ value = value.ToLowerInvariant();
- var rol = ctx.Guild.Roles.Values.FirstOrDefault(xr => (cs ? xr.Name : xr.Name.ToLowerInvariant()) == value);
- return Task.FromResult(Optional.FromNullable(rol));
+ var rol = ctx.Guild.Roles.Values.FirstOrDefault(xr => (cs ? xr.Name : xr.Name.ToLowerInvariant()) == value);
+ return Task.FromResult(Optional.FromNullable(rol));
+ }
}
-}
-/// <summary>
-/// Represents a discord guild converter.
-/// </summary>
-public class DiscordGuildConverter : IArgumentConverter<DiscordGuild>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord guild converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<DiscordGuild>> IArgumentConverter<DiscordGuild>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordGuildConverter : IArgumentConverter<DiscordGuild>
{
- if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var gid))
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<DiscordGuild>> IArgumentConverter<DiscordGuild>.ConvertAsync(string value, CommandContext ctx)
{
- return ctx.Client.Guilds.TryGetValue(gid, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<DiscordGuild>.None);
- }
+ if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var gid))
+ {
+ return ctx.Client.Guilds.TryGetValue(gid, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<DiscordGuild>.None);
+ }
- var cs = ctx.Config.CaseSensitive;
- if (!cs)
- value = value?.ToLowerInvariant();
+ var cs = ctx.Config.CaseSensitive;
+ if (!cs)
+ value = value?.ToLowerInvariant();
- var gld = ctx.Client.Guilds.Values.FirstOrDefault(xg => (cs ? xg.Name : xg.Name.ToLowerInvariant()) == value);
- return Task.FromResult(Optional.FromNullable(gld));
+ var gld = ctx.Client.Guilds.Values.FirstOrDefault(xg => (cs ? xg.Name : xg.Name.ToLowerInvariant()) == value);
+ return Task.FromResult(Optional.FromNullable(gld));
+ }
}
-}
-/// <summary>
-/// Represents a discord invite converter.
-/// </summary>
-public class DiscordInviteConverter : IArgumentConverter<DiscordInvite>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord invite converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- async Task<Optional<DiscordInvite>> IArgumentConverter<DiscordInvite>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordInviteConverter : IArgumentConverter<DiscordInvite>
{
- var m = DiscordRegEx.Invite.Match(value);
- if (m.Success)
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ async Task<Optional<DiscordInvite>> IArgumentConverter<DiscordInvite>.ConvertAsync(string value, CommandContext ctx)
{
- ulong? eventId = ulong.TryParse(m.Groups["event"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
- out var eid) ? eid : null;
- var result = await ctx.Client.GetInviteByCodeAsync(m.Groups["code"].Value, scheduledEventId: eventId).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ var m = DiscordRegEx.Invite.Match(value);
+ if (m.Success)
+ {
+ ulong? eventId = ulong.TryParse(m.Groups["event"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
+ out var eid) ? eid : null;
+ var result = await ctx.Client.GetInviteByCodeAsync(m.Groups["code"].Value, scheduledEventId: eventId).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
- var inv = await ctx.Client.GetInviteByCodeAsync(value);
- return Optional.FromNullable(inv);
+ var inv = await ctx.Client.GetInviteByCodeAsync(value);
+ return Optional.FromNullable(inv);
+ }
}
-}
-/// <summary>
-/// Represents a discord message converter.
-/// </summary>
-public class DiscordMessageConverter : IArgumentConverter<DiscordMessage>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord message converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- async Task<Optional<DiscordMessage>> IArgumentConverter<DiscordMessage>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordMessageConverter : IArgumentConverter<DiscordMessage>
{
- if (string.IsNullOrWhiteSpace(value))
- return Optional.None;
-
- var msguri = value.StartsWith("<") && value.EndsWith(">") ? value[1..^1] : value;
- ulong mid;
- if (Uri.TryCreate(msguri, UriKind.Absolute, out var uri))
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ async Task<Optional<DiscordMessage>> IArgumentConverter<DiscordMessage>.ConvertAsync(string value, CommandContext ctx)
{
- var uripath = DiscordRegEx.MessageLink.Match(uri.AbsoluteUri);
- if (!uripath.Success
- || !ulong.TryParse(uripath.Groups["channel"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var cid)
- || !ulong.TryParse(uripath.Groups["message"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out mid))
+ if (string.IsNullOrWhiteSpace(value))
return Optional.None;
- var chn = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
- if (chn == null)
- return Optional.None;
+ var msguri = value.StartsWith("<") && value.EndsWith(">") ? value[1..^1] : value;
+ ulong mid;
+ if (Uri.TryCreate(msguri, UriKind.Absolute, out var uri))
+ {
+ var uripath = DiscordRegEx.MessageLink.Match(uri.AbsoluteUri);
+ if (!uripath.Success
+ || !ulong.TryParse(uripath.Groups["channel"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var cid)
+ || !ulong.TryParse(uripath.Groups["message"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out mid))
+ return Optional.None;
- var msg = await chn.GetMessageAsync(mid).ConfigureAwait(false);
- return Optional.FromNullable(msg);
- }
+ var chn = await ctx.Client.GetChannelAsync(cid).ConfigureAwait(false);
+ if (chn == null)
+ return Optional.None;
- if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out mid))
- {
- var result = await ctx.Channel.GetMessageAsync(mid).ConfigureAwait(false);
- return Optional.FromNullable(result);
- }
+ var msg = await chn.GetMessageAsync(mid).ConfigureAwait(false);
+ return Optional.FromNullable(msg);
+ }
+
+ if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out mid))
+ {
+ var result = await ctx.Channel.GetMessageAsync(mid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
+ }
- return Optional.None;
+ return Optional.None;
+ }
}
-}
-/// <summary>
-/// Represents a discord scheduled event converter.
-/// </summary>
-public class DiscordScheduledEventConverter : IArgumentConverter<DiscordScheduledEvent>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord scheduled event converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- async Task<Optional<DiscordScheduledEvent>> IArgumentConverter<DiscordScheduledEvent>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordScheduledEventConverter : IArgumentConverter<DiscordScheduledEvent>
{
- if (string.IsNullOrWhiteSpace(value))
- return Optional.None;
-
- var msguri = value.StartsWith("<") && value.EndsWith(">") ? value[1..^1] : value;
- ulong seid;
- if (Uri.TryCreate(msguri, UriKind.Absolute, out var uri))
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ async Task<Optional<DiscordScheduledEvent>> IArgumentConverter<DiscordScheduledEvent>.ConvertAsync(string value, CommandContext ctx)
{
- var uripath = DiscordRegEx.Event.Match(uri.AbsoluteUri);
- if (uripath.Success
- && ulong.TryParse(uripath.Groups["guild"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
- out var gid)
- && ulong.TryParse(uripath.Groups["event"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
- out seid))
+ if (string.IsNullOrWhiteSpace(value))
+ return Optional.None;
+
+ var msguri = value.StartsWith("<") && value.EndsWith(">") ? value[1..^1] : value;
+ ulong seid;
+ if (Uri.TryCreate(msguri, UriKind.Absolute, out var uri))
{
- var guild = await ctx.Client.GetGuildAsync(gid).ConfigureAwait(false);
- if (guild == null)
- return Optional.None;
+ var uripath = DiscordRegEx.Event.Match(uri.AbsoluteUri);
+ if (uripath.Success
+ && ulong.TryParse(uripath.Groups["guild"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
+ out var gid)
+ && ulong.TryParse(uripath.Groups["event"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture,
+ out seid))
+ {
+ var guild = await ctx.Client.GetGuildAsync(gid).ConfigureAwait(false);
+ if (guild == null)
+ return Optional.None;
- var ev = await guild.GetScheduledEventAsync(seid).ConfigureAwait(false);
- return Optional.FromNullable(ev);
- }
+ var ev = await guild.GetScheduledEventAsync(seid).ConfigureAwait(false);
+ return Optional.FromNullable(ev);
+ }
- try
- {
- var invite = await ctx.CommandsNext.ConvertArgument<DiscordInvite>(value, ctx).ConfigureAwait(false);
- return Optional.FromNullable(invite.GuildScheduledEvent);
+ try
+ {
+ var invite = await ctx.CommandsNext.ConvertArgument<DiscordInvite>(value, ctx).ConfigureAwait(false);
+ return Optional.FromNullable(invite.GuildScheduledEvent);
+ }
+ catch
+ {
+ return Optional.None;
+ }
}
- catch
+
+ if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out seid))
{
- return Optional.None;
+ var result = await ctx.Guild.GetScheduledEventAsync(seid).ConfigureAwait(false);
+ return Optional.FromNullable(result);
}
- }
- if (ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out seid))
- {
- var result = await ctx.Guild.GetScheduledEventAsync(seid).ConfigureAwait(false);
- return Optional.FromNullable(result);
+ return Optional.None;
}
-
- return Optional.None;
}
-}
-/// <summary>
-/// Represents a discord emoji converter.
-/// </summary>
-public class DiscordEmojiConverter : IArgumentConverter<DiscordEmoji>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord emoji converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<DiscordEmoji>> IArgumentConverter<DiscordEmoji>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordEmojiConverter : IArgumentConverter<DiscordEmoji>
{
- if (DiscordEmoji.TryFromUnicode(ctx.Client, value, out var emoji))
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<DiscordEmoji>> IArgumentConverter<DiscordEmoji>.ConvertAsync(string value, CommandContext ctx)
{
- var result = emoji;
- return Task.FromResult(Optional.Some(result));
- }
+ if (DiscordEmoji.TryFromUnicode(ctx.Client, value, out var emoji))
+ {
+ var result = emoji;
+ return Task.FromResult(Optional.Some(result));
+ }
- var m = DiscordRegEx.Emoji.Match(value);
- if (m.Success)
- {
- var sid = m.Groups["id"].Value;
- var name = m.Groups["name"].Value;
- var anim = m.Groups["animated"].Success;
-
- return !ulong.TryParse(sid, NumberStyles.Integer, CultureInfo.InvariantCulture, out var id)
- ? Task.FromResult(Optional<DiscordEmoji>.None)
- : DiscordEmoji.TryFromGuildEmote(ctx.Client, id, out emoji)
- ? Task.FromResult(Optional.Some(emoji))
- : Task.FromResult(Optional.Some(new DiscordEmoji
- {
- Discord = ctx.Client,
- Id = id,
- Name = name,
- IsAnimated = anim,
- RequiresColons = true,
- IsManaged = false
- }));
- }
+ var m = DiscordRegEx.Emoji.Match(value);
+ if (m.Success)
+ {
+ var sid = m.Groups["id"].Value;
+ var name = m.Groups["name"].Value;
+ var anim = m.Groups["animated"].Success;
+
+ return !ulong.TryParse(sid, NumberStyles.Integer, CultureInfo.InvariantCulture, out var id)
+ ? Task.FromResult(Optional<DiscordEmoji>.None)
+ : DiscordEmoji.TryFromGuildEmote(ctx.Client, id, out emoji)
+ ? Task.FromResult(Optional.Some(emoji))
+ : Task.FromResult(Optional.Some(new DiscordEmoji
+ {
+ Discord = ctx.Client,
+ Id = id,
+ Name = name,
+ IsAnimated = anim,
+ RequiresColons = true,
+ IsManaged = false
+ }));
+ }
- return Task.FromResult(Optional<DiscordEmoji>.None);
+ return Task.FromResult(Optional<DiscordEmoji>.None);
+ }
}
-}
-/// <summary>
-/// Represents a discord color converter.
-/// </summary>
-public class DiscordColorConverter : IArgumentConverter<DiscordColor>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a discord color converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<DiscordColor>> IArgumentConverter<DiscordColor>.ConvertAsync(string value, CommandContext ctx)
+ public class DiscordColorConverter : IArgumentConverter<DiscordColor>
{
- var m = CommonRegEx.HexColorString.Match(value);
- if (m.Success && int.TryParse(m.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var clr))
- return Task.FromResult(Optional.Some<DiscordColor>(clr));
-
- m = CommonRegEx.RgbColorString.Match(value);
- if (m.Success)
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<DiscordColor>> IArgumentConverter<DiscordColor>.ConvertAsync(string value, CommandContext ctx)
{
- var p1 = byte.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var r);
- var p2 = byte.TryParse(m.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var g);
- var p3 = byte.TryParse(m.Groups[3].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var b);
+ var m = CommonRegEx.HexColorString.Match(value);
+ if (m.Success && int.TryParse(m.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var clr))
+ return Task.FromResult(Optional.Some<DiscordColor>(clr));
- return !(p1 && p2 && p3)
- ? Task.FromResult(Optional<DiscordColor>.None)
- : Task.FromResult(Optional.Some(new DiscordColor(r, g, b)));
- }
+ m = CommonRegEx.RgbColorString.Match(value);
+ if (m.Success)
+ {
+ var p1 = byte.TryParse(m.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var r);
+ var p2 = byte.TryParse(m.Groups[2].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var g);
+ var p3 = byte.TryParse(m.Groups[3].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var b);
- return Task.FromResult(Optional<DiscordColor>.None);
+ return !(p1 && p2 && p3)
+ ? Task.FromResult(Optional<DiscordColor>.None)
+ : Task.FromResult(Optional.Some(new DiscordColor(r, g, b)));
+ }
+
+ return Task.FromResult(Optional<DiscordColor>.None);
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Converters/EnumConverter.cs b/DisCatSharp.CommandsNext/Converters/EnumConverter.cs
index b672ff88c..644efc8c5 100644
--- a/DisCatSharp.CommandsNext/Converters/EnumConverter.cs
+++ b/DisCatSharp.CommandsNext/Converters/EnumConverter.cs
@@ -1,51 +1,52 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Reflection;
using System.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Represents a enum converter.
-/// </summary>
-public class EnumConverter<T> : IArgumentConverter<T> where T : struct, IComparable, IConvertible, IFormattable
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Converts a string.
+ /// Represents a enum converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<T>> IArgumentConverter<T>.ConvertAsync(string value, CommandContext ctx)
+ public class EnumConverter<T> : IArgumentConverter<T> where T : struct, IComparable, IConvertible, IFormattable
{
- var t = typeof(T);
- var ti = t.GetTypeInfo();
- return !ti.IsEnum
- ? throw new InvalidOperationException("Cannot convert non-enum value to an enum.")
- : Enum.TryParse(value, !ctx.Config.CaseSensitive, out T ev)
- ? Task.FromResult(Optional.Some(ev))
- : Task.FromResult(Optional<T>.None);
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<T>> IArgumentConverter<T>.ConvertAsync(string value, CommandContext ctx)
+ {
+ var t = typeof(T);
+ var ti = t.GetTypeInfo();
+ return !ti.IsEnum
+ ? throw new InvalidOperationException("Cannot convert non-enum value to an enum.")
+ : Enum.TryParse(value, !ctx.Config.CaseSensitive, out T ev)
+ ? Task.FromResult(Optional.Some(ev))
+ : Task.FromResult(Optional<T>.None);
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Converters/HelpFormatterFactory.cs b/DisCatSharp.CommandsNext/Converters/HelpFormatterFactory.cs
index 543beec5e..ebcc324de 100644
--- a/DisCatSharp.CommandsNext/Converters/HelpFormatterFactory.cs
+++ b/DisCatSharp.CommandsNext/Converters/HelpFormatterFactory.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Represents the help formatter factory.
-/// </summary>
-internal class HelpFormatterFactory
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Gets or sets the factory.
+ /// Represents the help formatter factory.
/// </summary>
- private ObjectFactory _factory;
+ internal class HelpFormatterFactory
+ {
+ /// <summary>
+ /// Gets or sets the factory.
+ /// </summary>
+ private ObjectFactory _factory;
- /// <summary>
- /// Initializes a new instance of the <see cref="HelpFormatterFactory"/> class.
- /// </summary>
- public HelpFormatterFactory() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HelpFormatterFactory"/> class.
+ /// </summary>
+ public HelpFormatterFactory() { }
- /// <summary>
- /// Sets the formatter type.
- /// </summary>
- public void SetFormatterType<T>() where T : BaseHelpFormatter => this._factory = ActivatorUtilities.CreateFactory(typeof(T), new[] { typeof(CommandContext) });
+ /// <summary>
+ /// Sets the formatter type.
+ /// </summary>
+ public void SetFormatterType<T>() where T : BaseHelpFormatter => this._factory = ActivatorUtilities.CreateFactory(typeof(T), new[] { typeof(CommandContext) });
- /// <summary>
- /// Creates the help formatter.
- /// </summary>
- /// <param name="ctx">The command context.</param>
- public BaseHelpFormatter Create(CommandContext ctx) => this._factory(ctx.Services, new object[] { ctx }) as BaseHelpFormatter;
+ /// <summary>
+ /// Creates the help formatter.
+ /// </summary>
+ /// <param name="ctx">The command context.</param>
+ public BaseHelpFormatter Create(CommandContext ctx) => this._factory(ctx.Services, new object[] { ctx }) as BaseHelpFormatter;
+ }
}
diff --git a/DisCatSharp.CommandsNext/Converters/IArgumentConverter.cs b/DisCatSharp.CommandsNext/Converters/IArgumentConverter.cs
index 0c46f2dae..869dad2c8 100644
--- a/DisCatSharp.CommandsNext/Converters/IArgumentConverter.cs
+++ b/DisCatSharp.CommandsNext/Converters/IArgumentConverter.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Argument converter abstract.
-/// </summary>
-public interface IArgumentConverter
-{ }
-
-/// <summary>
-/// Represents a converter for specific argument type.
-/// </summary>
-/// <typeparam name="T">Type for which the converter is to be registered.</typeparam>
-public interface IArgumentConverter<T> : IArgumentConverter
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Converts the raw value into the specified type.
+ /// Argument converter abstract.
+ /// </summary>
+ public interface IArgumentConverter
+ { }
+
+ /// <summary>
+ /// Represents a converter for specific argument type.
/// </summary>
- /// <param name="value">Value to convert.</param>
- /// <param name="ctx">Context in which the value will be converted.</param>
- /// <returns>A structure containing information whether the value was converted, and, if so, the converted value.</returns>
- Task<Optional<T>> ConvertAsync(string value, CommandContext ctx);
+ /// <typeparam name="T">Type for which the converter is to be registered.</typeparam>
+ public interface IArgumentConverter<T> : IArgumentConverter
+ {
+ /// <summary>
+ /// Converts the raw value into the specified type.
+ /// </summary>
+ /// <param name="value">Value to convert.</param>
+ /// <param name="ctx">Context in which the value will be converted.</param>
+ /// <returns>A structure containing information whether the value was converted, and, if so, the converted value.</returns>
+ Task<Optional<T>> ConvertAsync(string value, CommandContext ctx);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Converters/NullableConverter.cs b/DisCatSharp.CommandsNext/Converters/NullableConverter.cs
index 5a73b458d..540adb6d2 100644
--- a/DisCatSharp.CommandsNext/Converters/NullableConverter.cs
+++ b/DisCatSharp.CommandsNext/Converters/NullableConverter.cs
@@ -1,56 +1,57 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Represents a nullable converter.
-/// </summary>
-public class NullableConverter<T> : IArgumentConverter<T?> where T : struct
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Converts a string.
+ /// Represents a nullable converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- async Task<Optional<T?>> IArgumentConverter<T?>.ConvertAsync(string value, CommandContext ctx)
+ public class NullableConverter<T> : IArgumentConverter<T?> where T : struct
{
- if (!ctx.Config.CaseSensitive)
- value = value.ToLowerInvariant();
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ async Task<Optional<T?>> IArgumentConverter<T?>.ConvertAsync(string value, CommandContext ctx)
+ {
+ if (!ctx.Config.CaseSensitive)
+ value = value.ToLowerInvariant();
- if (value == "null")
- return null;
+ if (value == "null")
+ return null;
- if (ctx.CommandsNext.ArgumentConverters.TryGetValue(typeof(T), out var cv))
- {
- var cvx = cv as IArgumentConverter<T>;
- var val = await cvx.ConvertAsync(value, ctx).ConfigureAwait(false);
- return val.Map<T?>(x => x);
- }
+ if (ctx.CommandsNext.ArgumentConverters.TryGetValue(typeof(T), out var cv))
+ {
+ var cvx = cv as IArgumentConverter<T>;
+ var val = await cvx.ConvertAsync(value, ctx).ConfigureAwait(false);
+ return val.Map<T?>(x => x);
+ }
- return Optional.None;
+ return Optional.None;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Converters/NumericConverters.cs b/DisCatSharp.CommandsNext/Converters/NumericConverters.cs
index 01f76d380..91bdbad67 100644
--- a/DisCatSharp.CommandsNext/Converters/NumericConverters.cs
+++ b/DisCatSharp.CommandsNext/Converters/NumericConverters.cs
@@ -1,220 +1,221 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// The bool converter.
-/// </summary>
-public class BoolConverter : IArgumentConverter<bool>
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Converts a string.
+ /// The bool converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<bool>> IArgumentConverter<bool>.ConvertAsync(string value, CommandContext ctx) =>
- bool.TryParse(value, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<bool>.None);
-}
+ public class BoolConverter : IArgumentConverter<bool>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<bool>> IArgumentConverter<bool>.ConvertAsync(string value, CommandContext ctx) =>
+ bool.TryParse(value, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<bool>.None);
+ }
-/// <summary>
-/// The int8 converter.
-/// </summary>
-public class Int8Converter : IArgumentConverter<sbyte>
-{
/// <summary>
- /// Converts a string.
+ /// The int8 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<sbyte>> IArgumentConverter<sbyte>.ConvertAsync(string value, CommandContext ctx) =>
- sbyte.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<sbyte>.None);
-}
+ public class Int8Converter : IArgumentConverter<sbyte>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<sbyte>> IArgumentConverter<sbyte>.ConvertAsync(string value, CommandContext ctx) =>
+ sbyte.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<sbyte>.None);
+ }
-/// <summary>
-/// The uint8 converter.
-/// </summary>
-public class Uint8Converter : IArgumentConverter<byte>
-{
/// <summary>
- /// Converts a string.
+ /// The uint8 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<byte>> IArgumentConverter<byte>.ConvertAsync(string value, CommandContext ctx) =>
- byte.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<byte>.None);
-}
+ public class Uint8Converter : IArgumentConverter<byte>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<byte>> IArgumentConverter<byte>.ConvertAsync(string value, CommandContext ctx) =>
+ byte.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<byte>.None);
+ }
-/// <summary>
-/// The int16 converter.
-/// </summary>
-public class Int16Converter : IArgumentConverter<short>
-{
/// <summary>
- /// Converts a string.
+ /// The int16 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<short>> IArgumentConverter<short>.ConvertAsync(string value, CommandContext ctx) =>
- short.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<short>.None);
-}
+ public class Int16Converter : IArgumentConverter<short>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<short>> IArgumentConverter<short>.ConvertAsync(string value, CommandContext ctx) =>
+ short.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<short>.None);
+ }
-/// <summary>
-/// The uint16 converter.
-/// </summary>
-public class Uint16Converter : IArgumentConverter<ushort>
-{
/// <summary>
- /// Converts a string.
+ /// The uint16 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<ushort>> IArgumentConverter<ushort>.ConvertAsync(string value, CommandContext ctx) =>
- ushort.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<ushort>.None);
-}
+ public class Uint16Converter : IArgumentConverter<ushort>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<ushort>> IArgumentConverter<ushort>.ConvertAsync(string value, CommandContext ctx) =>
+ ushort.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<ushort>.None);
+ }
-/// <summary>
-/// The int32 converter.
-/// </summary>
-public class Int32Converter : IArgumentConverter<int>
-{
/// <summary>
- /// Converts a string.
+ /// The int32 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<int>> IArgumentConverter<int>.ConvertAsync(string value, CommandContext ctx) =>
- int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<int>.None);
-}
+ public class Int32Converter : IArgumentConverter<int>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<int>> IArgumentConverter<int>.ConvertAsync(string value, CommandContext ctx) =>
+ int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<int>.None);
+ }
-/// <summary>
-/// The uint32 converter.
-/// </summary>
-public class Uint32Converter : IArgumentConverter<uint>
-{
/// <summary>
- /// Converts a string.
+ /// The uint32 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<uint>> IArgumentConverter<uint>.ConvertAsync(string value, CommandContext ctx) =>
- uint.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<uint>.None);
-}
+ public class Uint32Converter : IArgumentConverter<uint>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<uint>> IArgumentConverter<uint>.ConvertAsync(string value, CommandContext ctx) =>
+ uint.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<uint>.None);
+ }
-/// <summary>
-/// The int64 converter.
-/// </summary>
-public class Int64Converter : IArgumentConverter<long>
-{
/// <summary>
- /// Converts a string.
+ /// The int64 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<long>> IArgumentConverter<long>.ConvertAsync(string value, CommandContext ctx) =>
- long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<long>.None);
-}
+ public class Int64Converter : IArgumentConverter<long>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<long>> IArgumentConverter<long>.ConvertAsync(string value, CommandContext ctx) =>
+ long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<long>.None);
+ }
-/// <summary>
-/// The uint64 converter.
-/// </summary>
-public class Uint64Converter : IArgumentConverter<ulong>
-{
/// <summary>
- /// Converts a string.
+ /// The uint64 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<ulong>> IArgumentConverter<ulong>.ConvertAsync(string value, CommandContext ctx) =>
- ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<ulong>.None);
-}
+ public class Uint64Converter : IArgumentConverter<ulong>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<ulong>> IArgumentConverter<ulong>.ConvertAsync(string value, CommandContext ctx) =>
+ ulong.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<ulong>.None);
+ }
-/// <summary>
-/// The float32 converter.
-/// </summary>
-public class Float32Converter : IArgumentConverter<float>
-{
/// <summary>
- /// Converts a string.
+ /// The float32 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<float>> IArgumentConverter<float>.ConvertAsync(string value, CommandContext ctx) =>
- float.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<float>.None);
-}
+ public class Float32Converter : IArgumentConverter<float>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<float>> IArgumentConverter<float>.ConvertAsync(string value, CommandContext ctx) =>
+ float.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<float>.None);
+ }
-/// <summary>
-/// The float64 converter.
-/// </summary>
-public class Float64Converter : IArgumentConverter<double>
-{
/// <summary>
- /// Converts a string.
+ /// The float64 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<double>> IArgumentConverter<double>.ConvertAsync(string value, CommandContext ctx) =>
- double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<double>.None);
-}
+ public class Float64Converter : IArgumentConverter<double>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<double>> IArgumentConverter<double>.ConvertAsync(string value, CommandContext ctx) =>
+ double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<double>.None);
+ }
-/// <summary>
-/// The float128 converter.
-/// </summary>
-public class Float128Converter : IArgumentConverter<decimal>
-{
/// <summary>
- /// Converts a string.
+ /// The float128 converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<decimal>> IArgumentConverter<decimal>.ConvertAsync(string value, CommandContext ctx) =>
- decimal.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<decimal>.None);
+ public class Float128Converter : IArgumentConverter<decimal>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<decimal>> IArgumentConverter<decimal>.ConvertAsync(string value, CommandContext ctx) =>
+ decimal.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<decimal>.None);
+ }
}
diff --git a/DisCatSharp.CommandsNext/Converters/StringConverter.cs b/DisCatSharp.CommandsNext/Converters/StringConverter.cs
index 3362cc8bc..ba8984f7b 100644
--- a/DisCatSharp.CommandsNext/Converters/StringConverter.cs
+++ b/DisCatSharp.CommandsNext/Converters/StringConverter.cs
@@ -1,68 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Represents a string converter.
-/// </summary>
-public class StringConverter : IArgumentConverter<string>
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Converts a string.
+ /// Represents a string converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<string>> IArgumentConverter<string>.ConvertAsync(string value, CommandContext ctx)
- => Task.FromResult(Optional.Some(value));
-}
+ public class StringConverter : IArgumentConverter<string>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<string>> IArgumentConverter<string>.ConvertAsync(string value, CommandContext ctx)
+ => Task.FromResult(Optional.Some(value));
+ }
-/// <summary>
-/// Represents a uri converter.
-/// </summary>
-public class UriConverter : IArgumentConverter<Uri>
-{
/// <summary>
- /// Converts a string.
+ /// Represents a uri converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<Uri>> IArgumentConverter<Uri>.ConvertAsync(string value, CommandContext ctx)
+ public class UriConverter : IArgumentConverter<Uri>
{
- try
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<Uri>> IArgumentConverter<Uri>.ConvertAsync(string value, CommandContext ctx)
{
- if (value.StartsWith("<") && value.EndsWith(">"))
- value = value[1..^1];
+ try
+ {
+ if (value.StartsWith("<") && value.EndsWith(">"))
+ value = value[1..^1];
- return Task.FromResult(Optional.Some(new Uri(value)));
- }
- catch
- {
- return Task.FromResult(Optional<Uri>.None);
+ return Task.FromResult(Optional.Some(new Uri(value)));
+ }
+ catch
+ {
+ return Task.FromResult(Optional<Uri>.None);
+ }
}
}
}
diff --git a/DisCatSharp.CommandsNext/Converters/TimeConverters.cs b/DisCatSharp.CommandsNext/Converters/TimeConverters.cs
index eeea636ef..1ed1824a4 100644
--- a/DisCatSharp.CommandsNext/Converters/TimeConverters.cs
+++ b/DisCatSharp.CommandsNext/Converters/TimeConverters.cs
@@ -1,141 +1,142 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DisCatSharp.Common.RegularExpressions;
using DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Converters;
-
-/// <summary>
-/// Represents a date time converter.
-/// </summary>
-public class DateTimeConverter : IArgumentConverter<DateTime>
-{
- /// <summary>
- /// Converts a string.
- /// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<DateTime>> IArgumentConverter<DateTime>.ConvertAsync(string value, CommandContext ctx) =>
- DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<DateTime>.None);
-}
-
-/// <summary>
-/// Represents a date time offset converter.
-/// </summary>
-public class DateTimeOffsetConverter : IArgumentConverter<DateTimeOffset>
-{
- /// <summary>
- /// Converts a string.
- /// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<DateTimeOffset>> IArgumentConverter<DateTimeOffset>.ConvertAsync(string value, CommandContext ctx) =>
- DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)
- ? Task.FromResult(Optional.Some(result))
- : Task.FromResult(Optional<DateTimeOffset>.None);
-}
-
-/// <summary>
-/// Represents a time span converter.
-/// </summary>
-public class TimeSpanConverter : IArgumentConverter<TimeSpan>
+namespace DisCatSharp.CommandsNext.Converters
{
/// <summary>
- /// Gets or sets the time span regex.
+ /// Represents a date time converter.
/// </summary>
- private static Regex s_timeSpanRegex { get; set; }
+ public class DateTimeConverter : IArgumentConverter<DateTime>
+ {
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<DateTime>> IArgumentConverter<DateTime>.ConvertAsync(string value, CommandContext ctx) =>
+ DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<DateTime>.None);
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="TimeSpanConverter"/> class.
+ /// Represents a date time offset converter.
/// </summary>
- static TimeSpanConverter()
+ public class DateTimeOffsetConverter : IArgumentConverter<DateTimeOffset>
{
- s_timeSpanRegex = CommonRegEx.TimeSpan;
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<DateTimeOffset>> IArgumentConverter<DateTimeOffset>.ConvertAsync(string value, CommandContext ctx) =>
+ DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)
+ ? Task.FromResult(Optional.Some(result))
+ : Task.FromResult(Optional<DateTimeOffset>.None);
}
/// <summary>
- /// Converts a string.
+ /// Represents a time span converter.
/// </summary>
- /// <param name="value">The string to convert.</param>
- /// <param name="ctx">The command context.</param>
- Task<Optional<TimeSpan>> IArgumentConverter<TimeSpan>.ConvertAsync(string value, CommandContext ctx)
+ public class TimeSpanConverter : IArgumentConverter<TimeSpan>
{
- if (value == "0")
- return Task.FromResult(Optional.Some(TimeSpan.Zero));
+ /// <summary>
+ /// Gets or sets the time span regex.
+ /// </summary>
+ private static Regex s_timeSpanRegex { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimeSpanConverter"/> class.
+ /// </summary>
+ static TimeSpanConverter()
+ {
+ s_timeSpanRegex = CommonRegEx.TimeSpan;
+ }
- if (int.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out _))
- return Task.FromResult(Optional<TimeSpan>.None);
+ /// <summary>
+ /// Converts a string.
+ /// </summary>
+ /// <param name="value">The string to convert.</param>
+ /// <param name="ctx">The command context.</param>
+ Task<Optional<TimeSpan>> IArgumentConverter<TimeSpan>.ConvertAsync(string value, CommandContext ctx)
+ {
+ if (value == "0")
+ return Task.FromResult(Optional.Some(TimeSpan.Zero));
- if (!ctx.Config.CaseSensitive)
- value = value.ToLowerInvariant();
+ if (int.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out _))
+ return Task.FromResult(Optional<TimeSpan>.None);
- if (TimeSpan.TryParse(value, CultureInfo.InvariantCulture, out var result))
- return Task.FromResult(Optional.Some(result));
+ if (!ctx.Config.CaseSensitive)
+ value = value.ToLowerInvariant();
- var gps = new string[] { "days", "hours", "minutes", "seconds" };
- var mtc = s_timeSpanRegex.Match(value);
- if (!mtc.Success)
- return Task.FromResult(Optional<TimeSpan>.None);
+ if (TimeSpan.TryParse(value, CultureInfo.InvariantCulture, out var result))
+ return Task.FromResult(Optional.Some(result));
- var d = 0;
- var h = 0;
- var m = 0;
- var s = 0;
- foreach (var gp in gps)
- {
- var gpc = mtc.Groups[gp].Value;
- if (string.IsNullOrWhiteSpace(gpc))
- continue;
+ var gps = new string[] { "days", "hours", "minutes", "seconds" };
+ var mtc = s_timeSpanRegex.Match(value);
+ if (!mtc.Success)
+ return Task.FromResult(Optional<TimeSpan>.None);
- var gpt = gpc[^1];
- int.TryParse(gpc[0..^1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var val);
- switch (gpt)
+ var d = 0;
+ var h = 0;
+ var m = 0;
+ var s = 0;
+ foreach (var gp in gps)
{
- case 'd':
- d = val;
- break;
-
- case 'h':
- h = val;
- break;
-
- case 'm':
- m = val;
- break;
-
- case 's':
- s = val;
- break;
+ var gpc = mtc.Groups[gp].Value;
+ if (string.IsNullOrWhiteSpace(gpc))
+ continue;
+
+ var gpt = gpc[^1];
+ int.TryParse(gpc[0..^1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var val);
+ switch (gpt)
+ {
+ case 'd':
+ d = val;
+ break;
+
+ case 'h':
+ h = val;
+ break;
+
+ case 'm':
+ m = val;
+ break;
+
+ case 's':
+ s = val;
+ break;
+ }
}
+ result = new TimeSpan(d, h, m, s);
+ return Task.FromResult(Optional.Some(result));
}
- result = new TimeSpan(d, h, m, s);
- return Task.FromResult(Optional.Some(result));
}
}
diff --git a/DisCatSharp.CommandsNext/Entities/Builders/CommandBuilder.cs b/DisCatSharp.CommandsNext/Entities/Builders/CommandBuilder.cs
index e388bb253..dc30b0444 100644
--- a/DisCatSharp.CommandsNext/Entities/Builders/CommandBuilder.cs
+++ b/DisCatSharp.CommandsNext/Entities/Builders/CommandBuilder.cs
@@ -1,305 +1,306 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using DisCatSharp.CommandsNext.Attributes;
using DisCatSharp.CommandsNext.Entities;
using DisCatSharp.CommandsNext.Exceptions;
-namespace DisCatSharp.CommandsNext.Builders;
-
-/// <summary>
-/// Represents an interface to build a command.
-/// </summary>
-public class CommandBuilder
+namespace DisCatSharp.CommandsNext.Builders
{
/// <summary>
- /// Gets the name set for this command.
- /// </summary>
- public string Name { get; private set; }
-
- /// <summary>
- /// Gets the aliases set for this command.
+ /// Represents an interface to build a command.
/// </summary>
- public IReadOnlyList<string> Aliases { get; }
-
- /// <summary>
- /// Gets the alias list.
- /// </summary>
- private readonly List<string> _aliasList;
-
- /// <summary>
- /// Gets the description set for this command.
- /// </summary>
- public string Description { get; private set; }
-
- /// <summary>
- /// Gets whether this command will be hidden or not.
- /// </summary>
- public bool IsHidden { get; private set; }
-
- /// <summary>
- /// Gets the execution checks defined for this command.
- /// </summary>
- public IReadOnlyList<CheckBaseAttribute> ExecutionChecks { get; }
-
- /// <summary>
- /// Gets the execution check list.
- /// </summary>
- private readonly List<CheckBaseAttribute> _executionCheckList;
-
- /// <summary>
- /// Gets the collection of this command's overloads.
- /// </summary>
- public IReadOnlyList<CommandOverloadBuilder> Overloads { get; }
-
- /// <summary>
- /// Gets the overload list.
- /// </summary>
- private readonly List<CommandOverloadBuilder> _overloadList;
-
- /// <summary>
- /// Gets the overload argument sets.
- /// </summary>
- private readonly HashSet<string> _overloadArgumentSets;
-
- /// <summary>
- /// Gets the module on which this command is to be defined.
- /// </summary>
- public ICommandModule Module { get; }
-
- /// <summary>
- /// Gets custom attributes defined on this command.
- /// </summary>
- public IReadOnlyList<Attribute> CustomAttributes { get; }
-
- /// <summary>
- /// Gets the custom attribute list.
- /// </summary>
- private readonly List<Attribute> _customAttributeList;
-
- /// <summary>
- /// Creates a new module-less command builder.
- /// </summary>
- public CommandBuilder()
- : this(null)
- { }
-
- /// <summary>
- /// Creates a new command builder.
- /// </summary>
- /// <param name="module">Module on which this command is to be defined.</param>
- public CommandBuilder(ICommandModule module)
+ public class CommandBuilder
{
- this._aliasList = new List<string>();
- this.Aliases = new ReadOnlyCollection<string>(this._aliasList);
-
- this._executionCheckList = new List<CheckBaseAttribute>();
- this.ExecutionChecks = new ReadOnlyCollection<CheckBaseAttribute>(this._executionCheckList);
-
- this._overloadArgumentSets = new HashSet<string>();
- this._overloadList = new List<CommandOverloadBuilder>();
- this.Overloads = new ReadOnlyCollection<CommandOverloadBuilder>(this._overloadList);
-
- this.Module = module;
-
- this._customAttributeList = new List<Attribute>();
- this.CustomAttributes = new ReadOnlyCollection<Attribute>(this._customAttributeList);
- }
-
- /// <summary>
- /// Sets the name for this command.
- /// </summary>
- /// <param name="name">Name for this command.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithName(string name)
- {
- if (name == null || name.ToCharArray().Any(xc => char.IsWhiteSpace(xc)))
- throw new ArgumentException("Command name cannot be null or contain any whitespace characters.", nameof(name));
-
- if (this.Name != null)
- throw new InvalidOperationException("This command already has a name.");
-
- if (this._aliasList.Contains(name))
- throw new ArgumentException("Command name cannot be one of its aliases.", nameof(name));
-
- this.Name = name;
- return this;
- }
-
- /// <summary>
- /// Adds aliases to this command.
- /// </summary>
- /// <param name="aliases">Aliases to add to the command.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithAliases(params string[] aliases)
- {
- if (aliases == null || !aliases.Any())
- throw new ArgumentException("You need to pass at least one alias.", nameof(aliases));
-
- foreach (var alias in aliases)
- this.WithAlias(alias);
+ /// <summary>
+ /// Gets the name set for this command.
+ /// </summary>
+ public string Name { get; private set; }
+
+ /// <summary>
+ /// Gets the aliases set for this command.
+ /// </summary>
+ public IReadOnlyList<string> Aliases { get; }
+
+ /// <summary>
+ /// Gets the alias list.
+ /// </summary>
+ private readonly List<string> _aliasList;
+
+ /// <summary>
+ /// Gets the description set for this command.
+ /// </summary>
+ public string Description { get; private set; }
+
+ /// <summary>
+ /// Gets whether this command will be hidden or not.
+ /// </summary>
+ public bool IsHidden { get; private set; }
+
+ /// <summary>
+ /// Gets the execution checks defined for this command.
+ /// </summary>
+ public IReadOnlyList<CheckBaseAttribute> ExecutionChecks { get; }
+
+ /// <summary>
+ /// Gets the execution check list.
+ /// </summary>
+ private readonly List<CheckBaseAttribute> _executionCheckList;
+
+ /// <summary>
+ /// Gets the collection of this command's overloads.
+ /// </summary>
+ public IReadOnlyList<CommandOverloadBuilder> Overloads { get; }
+
+ /// <summary>
+ /// Gets the overload list.
+ /// </summary>
+ private readonly List<CommandOverloadBuilder> _overloadList;
+
+ /// <summary>
+ /// Gets the overload argument sets.
+ /// </summary>
+ private readonly HashSet<string> _overloadArgumentSets;
+
+ /// <summary>
+ /// Gets the module on which this command is to be defined.
+ /// </summary>
+ public ICommandModule Module { get; }
+
+ /// <summary>
+ /// Gets custom attributes defined on this command.
+ /// </summary>
+ public IReadOnlyList<Attribute> CustomAttributes { get; }
+
+ /// <summary>
+ /// Gets the custom attribute list.
+ /// </summary>
+ private readonly List<Attribute> _customAttributeList;
+
+ /// <summary>
+ /// Creates a new module-less command builder.
+ /// </summary>
+ public CommandBuilder()
+ : this(null)
+ { }
+
+ /// <summary>
+ /// Creates a new command builder.
+ /// </summary>
+ /// <param name="module">Module on which this command is to be defined.</param>
+ public CommandBuilder(ICommandModule module)
+ {
+ this._aliasList = new List<string>();
+ this.Aliases = new ReadOnlyCollection<string>(this._aliasList);
- return this;
- }
+ this._executionCheckList = new List<CheckBaseAttribute>();
+ this.ExecutionChecks = new ReadOnlyCollection<CheckBaseAttribute>(this._executionCheckList);
- /// <summary>
- /// Adds an alias to this command.
- /// </summary>
- /// <param name="alias">Alias to add to the command.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithAlias(string alias)
- {
- if (alias.ToCharArray().Any(xc => char.IsWhiteSpace(xc)))
- throw new ArgumentException("Aliases cannot contain whitespace characters or null strings.", nameof(alias));
+ this._overloadArgumentSets = new HashSet<string>();
+ this._overloadList = new List<CommandOverloadBuilder>();
+ this.Overloads = new ReadOnlyCollection<CommandOverloadBuilder>(this._overloadList);
- if (this.Name == alias || this._aliasList.Contains(alias))
- throw new ArgumentException("Aliases cannot contain the command name, and cannot be duplicate.", nameof(alias));
+ this.Module = module;
- this._aliasList.Add(alias);
- return this;
- }
+ this._customAttributeList = new List<Attribute>();
+ this.CustomAttributes = new ReadOnlyCollection<Attribute>(this._customAttributeList);
+ }
- /// <summary>
- /// Sets the description for this command.
- /// </summary>
- /// <param name="description">Description to use for this command.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithDescription(string description)
- {
- this.Description = description;
- return this;
- }
+ /// <summary>
+ /// Sets the name for this command.
+ /// </summary>
+ /// <param name="name">Name for this command.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithName(string name)
+ {
+ if (name == null || name.ToCharArray().Any(xc => char.IsWhiteSpace(xc)))
+ throw new ArgumentException("Command name cannot be null or contain any whitespace characters.", nameof(name));
- /// <summary>
- /// Sets whether this command is to be hidden.
- /// </summary>
- /// <param name="hidden">Whether the command is to be hidden.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithHiddenStatus(bool hidden)
- {
- this.IsHidden = hidden;
- return this;
- }
+ if (this.Name != null)
+ throw new InvalidOperationException("This command already has a name.");
- /// <summary>
- /// Adds pre-execution checks to this command.
- /// </summary>
- /// <param name="checks">Pre-execution checks to add to this command.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithExecutionChecks(params CheckBaseAttribute[] checks)
- {
- this._executionCheckList.AddRange(checks.Except(this._executionCheckList));
- return this;
- }
+ if (this._aliasList.Contains(name))
+ throw new ArgumentException("Command name cannot be one of its aliases.", nameof(name));
- /// <summary>
- /// Adds a pre-execution check to this command.
- /// </summary>
- /// <param name="check">Pre-execution check to add to this command.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithExecutionCheck(CheckBaseAttribute check)
- {
- if (!this._executionCheckList.Contains(check))
- this._executionCheckList.Add(check);
- return this;
- }
+ this.Name = name;
+ return this;
+ }
- /// <summary>
- /// Adds overloads to this command. An executable command needs to have at least one overload.
- /// </summary>
- /// <param name="overloads">Overloads to add to this command.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithOverloads(params CommandOverloadBuilder[] overloads)
- {
- foreach (var overload in overloads)
- this.WithOverload(overload);
+ /// <summary>
+ /// Adds aliases to this command.
+ /// </summary>
+ /// <param name="aliases">Aliases to add to the command.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithAliases(params string[] aliases)
+ {
+ if (aliases == null || !aliases.Any())
+ throw new ArgumentException("You need to pass at least one alias.", nameof(aliases));
- return this;
- }
+ foreach (var alias in aliases)
+ this.WithAlias(alias);
- /// <summary>
- /// Adds an overload to this command. An executable command needs to have at least one overload.
- /// </summary>
- /// <param name="overload">Overload to add to this command.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithOverload(CommandOverloadBuilder overload)
- {
- if (this._overloadArgumentSets.Contains(overload.ArgumentSet))
- throw new DuplicateOverloadException(this.Name, overload.Arguments.Select(x => x.Type).ToList(), overload.ArgumentSet);
+ return this;
+ }
- this._overloadArgumentSets.Add(overload.ArgumentSet);
- this._overloadList.Add(overload);
+ /// <summary>
+ /// Adds an alias to this command.
+ /// </summary>
+ /// <param name="alias">Alias to add to the command.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithAlias(string alias)
+ {
+ if (alias.ToCharArray().Any(xc => char.IsWhiteSpace(xc)))
+ throw new ArgumentException("Aliases cannot contain whitespace characters or null strings.", nameof(alias));
+
+ if (this.Name == alias || this._aliasList.Contains(alias))
+ throw new ArgumentException("Aliases cannot contain the command name, and cannot be duplicate.", nameof(alias));
+
+ this._aliasList.Add(alias);
+ return this;
+ }
+
+ /// <summary>
+ /// Sets the description for this command.
+ /// </summary>
+ /// <param name="description">Description to use for this command.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithDescription(string description)
+ {
+ this.Description = description;
+ return this;
+ }
+
+ /// <summary>
+ /// Sets whether this command is to be hidden.
+ /// </summary>
+ /// <param name="hidden">Whether the command is to be hidden.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithHiddenStatus(bool hidden)
+ {
+ this.IsHidden = hidden;
+ return this;
+ }
+
+ /// <summary>
+ /// Adds pre-execution checks to this command.
+ /// </summary>
+ /// <param name="checks">Pre-execution checks to add to this command.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithExecutionChecks(params CheckBaseAttribute[] checks)
+ {
+ this._executionCheckList.AddRange(checks.Except(this._executionCheckList));
+ return this;
+ }
+
+ /// <summary>
+ /// Adds a pre-execution check to this command.
+ /// </summary>
+ /// <param name="check">Pre-execution check to add to this command.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithExecutionCheck(CheckBaseAttribute check)
+ {
+ if (!this._executionCheckList.Contains(check))
+ this._executionCheckList.Add(check);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds overloads to this command. An executable command needs to have at least one overload.
+ /// </summary>
+ /// <param name="overloads">Overloads to add to this command.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithOverloads(params CommandOverloadBuilder[] overloads)
+ {
+ foreach (var overload in overloads)
+ this.WithOverload(overload);
+
+ return this;
+ }
+
+ /// <summary>
+ /// Adds an overload to this command. An executable command needs to have at least one overload.
+ /// </summary>
+ /// <param name="overload">Overload to add to this command.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithOverload(CommandOverloadBuilder overload)
+ {
+ if (this._overloadArgumentSets.Contains(overload.ArgumentSet))
+ throw new DuplicateOverloadException(this.Name, overload.Arguments.Select(x => x.Type).ToList(), overload.ArgumentSet);
- return this;
- }
+ this._overloadArgumentSets.Add(overload.ArgumentSet);
+ this._overloadList.Add(overload);
- /// <summary>
- /// Adds a custom attribute to this command. This can be used to indicate various custom information about a command.
- /// </summary>
- /// <param name="attribute">Attribute to add.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithCustomAttribute(Attribute attribute)
- {
- this._customAttributeList.Add(attribute);
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Adds multiple custom attributes to this command. This can be used to indicate various custom information about a command.
- /// </summary>
- /// <param name="attributes">Attributes to add.</param>
- /// <returns>This builder.</returns>
- public CommandBuilder WithCustomAttributes(params Attribute[] attributes)
- {
- foreach (var attr in attributes)
- this.WithCustomAttribute(attr);
+ /// <summary>
+ /// Adds a custom attribute to this command. This can be used to indicate various custom information about a command.
+ /// </summary>
+ /// <param name="attribute">Attribute to add.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithCustomAttribute(Attribute attribute)
+ {
+ this._customAttributeList.Add(attribute);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds multiple custom attributes to this command. This can be used to indicate various custom information about a command.
+ /// </summary>
+ /// <param name="attributes">Attributes to add.</param>
+ /// <returns>This builder.</returns>
+ public CommandBuilder WithCustomAttributes(params Attribute[] attributes)
+ {
+ foreach (var attr in attributes)
+ this.WithCustomAttribute(attr);
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Builds the command.
- /// </summary>
- /// <param name="parent">The parent command group.</param>
- internal virtual Command Build(CommandGroup parent)
- {
- var cmd = new Command
+ /// <summary>
+ /// Builds the command.
+ /// </summary>
+ /// <param name="parent">The parent command group.</param>
+ internal virtual Command Build(CommandGroup parent)
{
- Name = this.Name,
- Description = this.Description,
- Aliases = this.Aliases,
- ExecutionChecks = this.ExecutionChecks,
- IsHidden = this.IsHidden,
- Parent = parent,
- Overloads = new ReadOnlyCollection<CommandOverload>(this.Overloads.Select(xo => xo.Build()).ToList()),
- Module = this.Module,
- CustomAttributes = this.CustomAttributes
- };
-
- return cmd;
+ var cmd = new Command
+ {
+ Name = this.Name,
+ Description = this.Description,
+ Aliases = this.Aliases,
+ ExecutionChecks = this.ExecutionChecks,
+ IsHidden = this.IsHidden,
+ Parent = parent,
+ Overloads = new ReadOnlyCollection<CommandOverload>(this.Overloads.Select(xo => xo.Build()).ToList()),
+ Module = this.Module,
+ CustomAttributes = this.CustomAttributes
+ };
+
+ return cmd;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Entities/Builders/CommandGroupBuilder.cs b/DisCatSharp.CommandsNext/Entities/Builders/CommandGroupBuilder.cs
index 0892369d2..a60871069 100644
--- a/DisCatSharp.CommandsNext/Entities/Builders/CommandGroupBuilder.cs
+++ b/DisCatSharp.CommandsNext/Entities/Builders/CommandGroupBuilder.cs
@@ -1,101 +1,102 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using DisCatSharp.CommandsNext.Entities;
-namespace DisCatSharp.CommandsNext.Builders;
-
-/// <summary>
-/// Represents an interface to build a command group.
-/// </summary>
-public sealed class CommandGroupBuilder : CommandBuilder
+namespace DisCatSharp.CommandsNext.Builders
{
/// <summary>
- /// Gets the list of child commands registered for this group.
+ /// Represents an interface to build a command group.
/// </summary>
- public IReadOnlyList<CommandBuilder> Children { get; }
+ public sealed class CommandGroupBuilder : CommandBuilder
+ {
+ /// <summary>
+ /// Gets the list of child commands registered for this group.
+ /// </summary>
+ public IReadOnlyList<CommandBuilder> Children { get; }
- /// <summary>
- /// Gets the children list.
- /// </summary>
- private readonly List<CommandBuilder> _childrenList;
+ /// <summary>
+ /// Gets the children list.
+ /// </summary>
+ private readonly List<CommandBuilder> _childrenList;
- /// <summary>
- /// Creates a new module-less command group builder.
- /// </summary>
- public CommandGroupBuilder()
- : this(null)
- { }
+ /// <summary>
+ /// Creates a new module-less command group builder.
+ /// </summary>
+ public CommandGroupBuilder()
+ : this(null)
+ { }
- /// <summary>
- /// Creates a new command group builder.
- /// </summary>
- /// <param name="module">Module on which this group is to be defined.</param>
- public CommandGroupBuilder(ICommandModule module)
- : base(module)
- {
- this._childrenList = new List<CommandBuilder>();
- this.Children = new ReadOnlyCollection<CommandBuilder>(this._childrenList);
- }
+ /// <summary>
+ /// Creates a new command group builder.
+ /// </summary>
+ /// <param name="module">Module on which this group is to be defined.</param>
+ public CommandGroupBuilder(ICommandModule module)
+ : base(module)
+ {
+ this._childrenList = new List<CommandBuilder>();
+ this.Children = new ReadOnlyCollection<CommandBuilder>(this._childrenList);
+ }
- /// <summary>
- /// Adds a command to the collection of child commands for this group.
- /// </summary>
- /// <param name="child">Command to add to the collection of child commands for this group.</param>
- /// <returns>This builder.</returns>
- public CommandGroupBuilder WithChild(CommandBuilder child)
- {
- this._childrenList.Add(child);
- return this;
- }
+ /// <summary>
+ /// Adds a command to the collection of child commands for this group.
+ /// </summary>
+ /// <param name="child">Command to add to the collection of child commands for this group.</param>
+ /// <returns>This builder.</returns>
+ public CommandGroupBuilder WithChild(CommandBuilder child)
+ {
+ this._childrenList.Add(child);
+ return this;
+ }
- /// <summary>
- /// Builds the command group.
- /// </summary>
- /// <param name="parent">The parent command group.</param>
- internal override Command Build(CommandGroup parent)
- {
- var cmd = new CommandGroup
+ /// <summary>
+ /// Builds the command group.
+ /// </summary>
+ /// <param name="parent">The parent command group.</param>
+ internal override Command Build(CommandGroup parent)
{
- Name = this.Name,
- Description = this.Description,
- Aliases = this.Aliases,
- ExecutionChecks = this.ExecutionChecks,
- IsHidden = this.IsHidden,
- Parent = parent,
- Overloads = new ReadOnlyCollection<CommandOverload>(this.Overloads.Select(xo => xo.Build()).ToList()),
- Module = this.Module,
- CustomAttributes = this.CustomAttributes
- };
+ var cmd = new CommandGroup
+ {
+ Name = this.Name,
+ Description = this.Description,
+ Aliases = this.Aliases,
+ ExecutionChecks = this.ExecutionChecks,
+ IsHidden = this.IsHidden,
+ Parent = parent,
+ Overloads = new ReadOnlyCollection<CommandOverload>(this.Overloads.Select(xo => xo.Build()).ToList()),
+ Module = this.Module,
+ CustomAttributes = this.CustomAttributes
+ };
- var cs = new List<Command>();
- foreach (var xc in this.Children)
- cs.Add(xc.Build(cmd));
+ var cs = new List<Command>();
+ foreach (var xc in this.Children)
+ cs.Add(xc.Build(cmd));
- cmd.Children = new ReadOnlyCollection<Command>(cs);
- return cmd;
+ cmd.Children = new ReadOnlyCollection<Command>(cs);
+ return cmd;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Entities/Builders/CommandModuleBuilder.cs b/DisCatSharp.CommandsNext/Entities/Builders/CommandModuleBuilder.cs
index 47f5210da..e86e581c3 100644
--- a/DisCatSharp.CommandsNext/Entities/Builders/CommandModuleBuilder.cs
+++ b/DisCatSharp.CommandsNext/Entities/Builders/CommandModuleBuilder.cs
@@ -1,87 +1,88 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.CommandsNext.Attributes;
using DisCatSharp.CommandsNext.Entities;
-namespace DisCatSharp.CommandsNext.Builders;
-
-/// <summary>
-/// Represents an interface to build a command module.
-/// </summary>
-public sealed class CommandModuleBuilder
+namespace DisCatSharp.CommandsNext.Builders
{
/// <summary>
- /// Gets the type this build will construct a module out of.
+ /// Represents an interface to build a command module.
/// </summary>
- public Type Type { get; private set; }
-
- /// <summary>
- /// Gets the lifespan for the built module.
- /// </summary>
- public ModuleLifespan Lifespan { get; private set; }
+ public sealed class CommandModuleBuilder
+ {
+ /// <summary>
+ /// Gets the type this build will construct a module out of.
+ /// </summary>
+ public Type Type { get; private set; }
- /// <summary>
- /// Creates a new command module builder.
- /// </summary>
- public CommandModuleBuilder()
- { }
+ /// <summary>
+ /// Gets the lifespan for the built module.
+ /// </summary>
+ public ModuleLifespan Lifespan { get; private set; }
- /// <summary>
- /// Sets the type this builder will construct a module out of.
- /// </summary>
- /// <param name="t">Type to build a module out of. It has to derive from <see cref="BaseCommandModule"/>.</param>
- /// <returns>This builder.</returns>
- public CommandModuleBuilder WithType(Type t)
- {
- if (!t.IsModuleCandidateType())
- throw new ArgumentException("Specified type is not a valid module type.", nameof(t));
+ /// <summary>
+ /// Creates a new command module builder.
+ /// </summary>
+ public CommandModuleBuilder()
+ { }
- this.Type = t;
- return this;
- }
+ /// <summary>
+ /// Sets the type this builder will construct a module out of.
+ /// </summary>
+ /// <param name="t">Type to build a module out of. It has to derive from <see cref="BaseCommandModule"/>.</param>
+ /// <returns>This builder.</returns>
+ public CommandModuleBuilder WithType(Type t)
+ {
+ if (!t.IsModuleCandidateType())
+ throw new ArgumentException("Specified type is not a valid module type.", nameof(t));
- /// <summary>
- /// Lifespan to give this module.
- /// </summary>
- /// <param name="lifespan">Lifespan for this module.</param>
- /// <returns>This builder.</returns>
- public CommandModuleBuilder WithLifespan(ModuleLifespan lifespan)
- {
- this.Lifespan = lifespan;
- return this;
- }
+ this.Type = t;
+ return this;
+ }
- /// <summary>
- /// Builds the command module.
- /// </summary>
- /// <param name="services">The services.</param>
- internal ICommandModule Build(IServiceProvider services) =>
- this.Lifespan switch
+ /// <summary>
+ /// Lifespan to give this module.
+ /// </summary>
+ /// <param name="lifespan">Lifespan for this module.</param>
+ /// <returns>This builder.</returns>
+ public CommandModuleBuilder WithLifespan(ModuleLifespan lifespan)
{
- ModuleLifespan.Singleton => new SingletonCommandModule(this.Type, services),
- ModuleLifespan.Transient => new TransientCommandModule(this.Type),
- _ => throw new NotSupportedException("Module lifespans other than transient and singleton are not supported."),
- };
+ this.Lifespan = lifespan;
+ return this;
+ }
+
+ /// <summary>
+ /// Builds the command module.
+ /// </summary>
+ /// <param name="services">The services.</param>
+ internal ICommandModule Build(IServiceProvider services) =>
+ this.Lifespan switch
+ {
+ ModuleLifespan.Singleton => new SingletonCommandModule(this.Type, services),
+ ModuleLifespan.Transient => new TransientCommandModule(this.Type),
+ _ => throw new NotSupportedException("Module lifespans other than transient and singleton are not supported."),
+ };
+ }
}
diff --git a/DisCatSharp.CommandsNext/Entities/Builders/CommandOverloadBuilder.cs b/DisCatSharp.CommandsNext/Entities/Builders/CommandOverloadBuilder.cs
index 4147a2520..6568682b7 100644
--- a/DisCatSharp.CommandsNext/Entities/Builders/CommandOverloadBuilder.cs
+++ b/DisCatSharp.CommandsNext/Entities/Builders/CommandOverloadBuilder.cs
@@ -1,193 +1,194 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using DisCatSharp.CommandsNext.Attributes;
using DisCatSharp.CommandsNext.Exceptions;
-namespace DisCatSharp.CommandsNext.Builders;
-
-/// <summary>
-/// Represents an interface to build a command overload.
-/// </summary>
-public sealed class CommandOverloadBuilder
+namespace DisCatSharp.CommandsNext.Builders
{
/// <summary>
- /// Gets a value that uniquely identifies an overload.
- /// </summary>
- internal string ArgumentSet { get; }
-
- /// <summary>
- /// Gets the collection of arguments this overload takes.
- /// </summary>
- public IReadOnlyList<CommandArgument> Arguments { get; }
-
- /// <summary>
- /// Gets this overload's priority when picking a suitable one for execution.
- /// </summary>
- public int Priority { get; set; }
-
- /// <summary>
- /// Gets the overload's callable delegate.
+ /// Represents an interface to build a command overload.
/// </summary>
- public Delegate Callable { get; set; }
-
- /// <summary>
- /// Gets the invocation target.
- /// </summary>
- private readonly object _invocationTarget;
-
- /// <summary>
- /// Creates a new command overload builder from specified method.
- /// </summary>
- /// <param name="method">Method to use for this overload.</param>
- public CommandOverloadBuilder(MethodInfo method)
- : this(method, null)
- { }
-
- /// <summary>
- /// Creates a new command overload builder from specified delegate.
- /// </summary>
- /// <param name="method">Delegate to use for this overload.</param>
- public CommandOverloadBuilder(Delegate method)
- : this(method.GetMethodInfo(), method.Target)
- { }
-
- /// <summary>
- /// Prevents a default instance of the <see cref="CommandOverloadBuilder"/> class from being created.
- /// </summary>
- /// <param name="method">The method.</param>
- /// <param name="target">The target.</param>
- private CommandOverloadBuilder(MethodInfo method, object target)
+ public sealed class CommandOverloadBuilder
{
- if (!method.IsCommandCandidate(out var prms))
- throw new ArgumentException("Specified method is not suitable for a command.", nameof(method));
-
- this._invocationTarget = target;
+ /// <summary>
+ /// Gets a value that uniquely identifies an overload.
+ /// </summary>
+ internal string ArgumentSet { get; }
+
+ /// <summary>
+ /// Gets the collection of arguments this overload takes.
+ /// </summary>
+ public IReadOnlyList<CommandArgument> Arguments { get; }
+
+ /// <summary>
+ /// Gets this overload's priority when picking a suitable one for execution.
+ /// </summary>
+ public int Priority { get; set; }
+
+ /// <summary>
+ /// Gets the overload's callable delegate.
+ /// </summary>
+ public Delegate Callable { get; set; }
+
+ /// <summary>
+ /// Gets the invocation target.
+ /// </summary>
+ private readonly object _invocationTarget;
+
+ /// <summary>
+ /// Creates a new command overload builder from specified method.
+ /// </summary>
+ /// <param name="method">Method to use for this overload.</param>
+ public CommandOverloadBuilder(MethodInfo method)
+ : this(method, null)
+ { }
+
+ /// <summary>
+ /// Creates a new command overload builder from specified delegate.
+ /// </summary>
+ /// <param name="method">Delegate to use for this overload.</param>
+ public CommandOverloadBuilder(Delegate method)
+ : this(method.GetMethodInfo(), method.Target)
+ { }
+
+ /// <summary>
+ /// Prevents a default instance of the <see cref="CommandOverloadBuilder"/> class from being created.
+ /// </summary>
+ /// <param name="method">The method.</param>
+ /// <param name="target">The target.</param>
+ private CommandOverloadBuilder(MethodInfo method, object target)
+ {
+ if (!method.IsCommandCandidate(out var prms))
+ throw new ArgumentException("Specified method is not suitable for a command.", nameof(method));
- // create the argument array
- var ea = new ParameterExpression[prms.Length + 1];
- var iep = Expression.Parameter(target?.GetType() ?? method.DeclaringType, "instance");
- ea[0] = iep;
- ea[1] = Expression.Parameter(typeof(CommandContext), "ctx");
+ this._invocationTarget = target;
- var pri = method.GetCustomAttribute<PriorityAttribute>();
- if (pri != null)
- this.Priority = pri.Priority;
+ // create the argument array
+ var ea = new ParameterExpression[prms.Length + 1];
+ var iep = Expression.Parameter(target?.GetType() ?? method.DeclaringType, "instance");
+ ea[0] = iep;
+ ea[1] = Expression.Parameter(typeof(CommandContext), "ctx");
- var i = 2;
- var args = new List<CommandArgument>(prms.Length - 1);
- var setb = new StringBuilder();
- foreach (var arg in prms.Skip(1))
- {
- setb.Append(arg.ParameterType).Append(";");
- var ca = new CommandArgument
- {
- Name = arg.Name,
- Type = arg.ParameterType,
- IsOptional = arg.IsOptional,
- DefaultValue = arg.IsOptional ? arg.DefaultValue : null
- };
+ var pri = method.GetCustomAttribute<PriorityAttribute>();
+ if (pri != null)
+ this.Priority = pri.Priority;
- var attrsCustom = new List<Attribute>();
- var attrs = arg.GetCustomAttributes();
- var isParams = false;
- foreach (var xa in attrs)
+ var i = 2;
+ var args = new List<CommandArgument>(prms.Length - 1);
+ var setb = new StringBuilder();
+ foreach (var arg in prms.Skip(1))
{
- switch (xa)
+ setb.Append(arg.ParameterType).Append(";");
+ var ca = new CommandArgument
+ {
+ Name = arg.Name,
+ Type = arg.ParameterType,
+ IsOptional = arg.IsOptional,
+ DefaultValue = arg.IsOptional ? arg.DefaultValue : null
+ };
+
+ var attrsCustom = new List<Attribute>();
+ var attrs = arg.GetCustomAttributes();
+ var isParams = false;
+ foreach (var xa in attrs)
{
- case DescriptionAttribute d:
- ca.Description = d.Description;
- break;
-
- case RemainingTextAttribute r:
- ca.IsCatchAll = true;
- break;
-
- case ParamArrayAttribute p:
- ca.IsCatchAll = true;
- ca.Type = arg.ParameterType.GetElementType();
- ca.IsArray = true;
- isParams = true;
- break;
-
- default:
- attrsCustom.Add(xa);
- break;
+ switch (xa)
+ {
+ case DescriptionAttribute d:
+ ca.Description = d.Description;
+ break;
+
+ case RemainingTextAttribute r:
+ ca.IsCatchAll = true;
+ break;
+
+ case ParamArrayAttribute p:
+ ca.IsCatchAll = true;
+ ca.Type = arg.ParameterType.GetElementType();
+ ca.IsArray = true;
+ isParams = true;
+ break;
+
+ default:
+ attrsCustom.Add(xa);
+ break;
+ }
}
- }
- if (i > 2 && !ca.IsOptional && !ca.IsCatchAll && args[i - 3].IsOptional)
- throw new InvalidOverloadException("Non-optional argument cannot appear after an optional one", method, arg);
+ if (i > 2 && !ca.IsOptional && !ca.IsCatchAll && args[i - 3].IsOptional)
+ throw new InvalidOverloadException("Non-optional argument cannot appear after an optional one", method, arg);
- if (arg.ParameterType.IsArray && !isParams)
- throw new InvalidOverloadException("Cannot use array arguments without params modifier.", method, arg);
+ if (arg.ParameterType.IsArray && !isParams)
+ throw new InvalidOverloadException("Cannot use array arguments without params modifier.", method, arg);
- ca.CustomAttributes = new ReadOnlyCollection<Attribute>(attrsCustom);
- args.Add(ca);
- ea[i++] = Expression.Parameter(arg.ParameterType, arg.Name);
- }
+ ca.CustomAttributes = new ReadOnlyCollection<Attribute>(attrsCustom);
+ args.Add(ca);
+ ea[i++] = Expression.Parameter(arg.ParameterType, arg.Name);
+ }
- //var ec = Expression.Call(iev, method, ea.Skip(2));
- var ec = Expression.Call(iep, method, ea.Skip(1));
- var el = Expression.Lambda(ec, ea);
+ //var ec = Expression.Call(iev, method, ea.Skip(2));
+ var ec = Expression.Call(iep, method, ea.Skip(1));
+ var el = Expression.Lambda(ec, ea);
- this.ArgumentSet = setb.ToString();
- this.Arguments = new ReadOnlyCollection<CommandArgument>(args);
- this.Callable = el.Compile();
- }
+ this.ArgumentSet = setb.ToString();
+ this.Arguments = new ReadOnlyCollection<CommandArgument>(args);
+ this.Callable = el.Compile();
+ }
- /// <summary>
- /// Sets the priority for this command overload.
- /// </summary>
- /// <param name="priority">Priority for this command overload.</param>
- /// <returns>This builder.</returns>
- public CommandOverloadBuilder WithPriority(int priority)
- {
- this.Priority = priority;
+ /// <summary>
+ /// Sets the priority for this command overload.
+ /// </summary>
+ /// <param name="priority">Priority for this command overload.</param>
+ /// <returns>This builder.</returns>
+ public CommandOverloadBuilder WithPriority(int priority)
+ {
+ this.Priority = priority;
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Builds the command overload.
- /// </summary>
- internal CommandOverload Build()
- {
- var ovl = new CommandOverload()
+ /// <summary>
+ /// Builds the command overload.
+ /// </summary>
+ internal CommandOverload Build()
{
- Arguments = this.Arguments,
- Priority = this.Priority,
- Callable = this.Callable,
- InvocationTarget = this._invocationTarget
- };
+ var ovl = new CommandOverload()
+ {
+ Arguments = this.Arguments,
+ Priority = this.Priority,
+ Callable = this.Callable,
+ InvocationTarget = this._invocationTarget
+ };
- return ovl;
+ return ovl;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Entities/Command.cs b/DisCatSharp.CommandsNext/Entities/Command.cs
index 1018e5744..04b497147 100644
--- a/DisCatSharp.CommandsNext/Entities/Command.cs
+++ b/DisCatSharp.CommandsNext/Entities/Command.cs
@@ -1,232 +1,233 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
using DisCatSharp.CommandsNext.Attributes;
using DisCatSharp.CommandsNext.Entities;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Represents a command.
-/// </summary>
-public class Command
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets this command's name.
- /// </summary>
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets this command's qualified name (i.e. one that includes all module names).
- /// </summary>
- public string QualifiedName
- => this.Parent != null ? string.Concat(this.Parent.QualifiedName, " ", this.Name) : this.Name;
-
- /// <summary>
- /// Gets this command's aliases.
- /// </summary>
- public IReadOnlyList<string> Aliases { get; internal set; }
-
- /// <summary>
- /// Gets this command's parent module, if any.
- /// </summary>
- public CommandGroup Parent { get; internal set; }
-
- /// <summary>
- /// Gets this command's description.
- /// </summary>
- public string Description { get; internal set; }
-
- /// <summary>
- /// Gets whether this command is hidden.
- /// </summary>
- public bool IsHidden { get; internal set; }
-
- /// <summary>
- /// Gets a collection of pre-execution checks for this command.
+ /// Represents a command.
/// </summary>
- public IReadOnlyList<CheckBaseAttribute> ExecutionChecks { get; internal set; }
-
- /// <summary>
- /// Gets a collection of this command's overloads.
- /// </summary>
- public IReadOnlyList<CommandOverload> Overloads { get; internal set; }
-
- /// <summary>
- /// Gets the module in which this command is defined.
- /// </summary>
- public ICommandModule Module { get; internal set; }
-
- /// <summary>
- /// Gets the custom attributes defined on this command.
- /// </summary>
- public IReadOnlyList<Attribute> CustomAttributes { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Command"/> class.
- /// </summary>
- internal Command() { }
-
- /// <summary>
- /// Executes this command with specified context.
- /// </summary>
- /// <param name="ctx">Context to execute the command in.</param>
- /// <returns>Command's execution results.</returns>
- public virtual async Task<CommandResult> ExecuteAsync(CommandContext ctx)
+ public class Command
{
- CommandResult res = default;
- try
+ /// <summary>
+ /// Gets this command's name.
+ /// </summary>
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets this command's qualified name (i.e. one that includes all module names).
+ /// </summary>
+ public string QualifiedName
+ => this.Parent != null ? string.Concat(this.Parent.QualifiedName, " ", this.Name) : this.Name;
+
+ /// <summary>
+ /// Gets this command's aliases.
+ /// </summary>
+ public IReadOnlyList<string> Aliases { get; internal set; }
+
+ /// <summary>
+ /// Gets this command's parent module, if any.
+ /// </summary>
+ public CommandGroup Parent { get; internal set; }
+
+ /// <summary>
+ /// Gets this command's description.
+ /// </summary>
+ public string Description { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this command is hidden.
+ /// </summary>
+ public bool IsHidden { get; internal set; }
+
+ /// <summary>
+ /// Gets a collection of pre-execution checks for this command.
+ /// </summary>
+ public IReadOnlyList<CheckBaseAttribute> ExecutionChecks { get; internal set; }
+
+ /// <summary>
+ /// Gets a collection of this command's overloads.
+ /// </summary>
+ public IReadOnlyList<CommandOverload> Overloads { get; internal set; }
+
+ /// <summary>
+ /// Gets the module in which this command is defined.
+ /// </summary>
+ public ICommandModule Module { get; internal set; }
+
+ /// <summary>
+ /// Gets the custom attributes defined on this command.
+ /// </summary>
+ public IReadOnlyList<Attribute> CustomAttributes { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Command"/> class.
+ /// </summary>
+ internal Command() { }
+
+ /// <summary>
+ /// Executes this command with specified context.
+ /// </summary>
+ /// <param name="ctx">Context to execute the command in.</param>
+ /// <returns>Command's execution results.</returns>
+ public virtual async Task<CommandResult> ExecuteAsync(CommandContext ctx)
{
- var executed = false;
- foreach (var ovl in this.Overloads.OrderByDescending(x => x.Priority))
+ CommandResult res = default;
+ try
+ {
+ var executed = false;
+ foreach (var ovl in this.Overloads.OrderByDescending(x => x.Priority))
+ {
+ ctx.Overload = ovl;
+ var args = await CommandsNextUtilities.BindArguments(ctx, ctx.Config.IgnoreExtraArguments).ConfigureAwait(false);
+
+ if (!args.IsSuccessful)
+ continue;
+
+ ctx.RawArguments = args.Raw;
+
+ var mdl = ovl.InvocationTarget ?? this.Module?.GetInstance(ctx.Services);
+ if (mdl is BaseCommandModule bcmBefore)
+ await bcmBefore.BeforeExecutionAsync(ctx).ConfigureAwait(false);
+
+ args.Converted[0] = mdl;
+ var ret = (Task)ovl.Callable.DynamicInvoke(args.Converted);
+ await ret.ConfigureAwait(false);
+ executed = true;
+ res = new CommandResult
+ {
+ IsSuccessful = true,
+ Context = ctx
+ };
+
+ if (mdl is BaseCommandModule bcmAfter)
+ await bcmAfter.AfterExecutionAsync(ctx).ConfigureAwait(false);
+ break;
+ }
+
+ if (!executed)
+ throw new ArgumentException("Could not find a suitable overload for the command.");
+ }
+ catch (Exception ex)
{
- ctx.Overload = ovl;
- var args = await CommandsNextUtilities.BindArguments(ctx, ctx.Config.IgnoreExtraArguments).ConfigureAwait(false);
-
- if (!args.IsSuccessful)
- continue;
-
- ctx.RawArguments = args.Raw;
-
- var mdl = ovl.InvocationTarget ?? this.Module?.GetInstance(ctx.Services);
- if (mdl is BaseCommandModule bcmBefore)
- await bcmBefore.BeforeExecutionAsync(ctx).ConfigureAwait(false);
-
- args.Converted[0] = mdl;
- var ret = (Task)ovl.Callable.DynamicInvoke(args.Converted);
- await ret.ConfigureAwait(false);
- executed = true;
res = new CommandResult
{
- IsSuccessful = true,
+ IsSuccessful = false,
+ Exception = ex,
Context = ctx
};
-
- if (mdl is BaseCommandModule bcmAfter)
- await bcmAfter.AfterExecutionAsync(ctx).ConfigureAwait(false);
- break;
}
- if (!executed)
- throw new ArgumentException("Could not find a suitable overload for the command.");
+ return res;
}
- catch (Exception ex)
- {
- res = new CommandResult
- {
- IsSuccessful = false,
- Exception = ex,
- Context = ctx
- };
- }
-
- return res;
- }
- /// <summary>
- /// Runs pre-execution checks for this command and returns any that fail for given context.
- /// </summary>
- /// <param name="ctx">Context in which the command is executed.</param>
- /// <param name="help">Whether this check is being executed from help or not. This can be used to probe whether command can be run without setting off certain fail conditions (such as cooldowns).</param>
- /// <returns>Pre-execution checks that fail for given context.</returns>
- public async Task<IEnumerable<CheckBaseAttribute>> RunChecksAsync(CommandContext ctx, bool help)
- {
- var fchecks = new List<CheckBaseAttribute>();
- if (this.ExecutionChecks != null && this.ExecutionChecks.Any())
- foreach (var ec in this.ExecutionChecks)
- if (!await ec.ExecuteCheckAsync(ctx, help).ConfigureAwait(false))
- fchecks.Add(ec);
-
- return fchecks;
- }
+ /// <summary>
+ /// Runs pre-execution checks for this command and returns any that fail for given context.
+ /// </summary>
+ /// <param name="ctx">Context in which the command is executed.</param>
+ /// <param name="help">Whether this check is being executed from help or not. This can be used to probe whether command can be run without setting off certain fail conditions (such as cooldowns).</param>
+ /// <returns>Pre-execution checks that fail for given context.</returns>
+ public async Task<IEnumerable<CheckBaseAttribute>> RunChecksAsync(CommandContext ctx, bool help)
+ {
+ var fchecks = new List<CheckBaseAttribute>();
+ if (this.ExecutionChecks != null && this.ExecutionChecks.Any())
+ foreach (var ec in this.ExecutionChecks)
+ if (!await ec.ExecuteCheckAsync(ctx, help).ConfigureAwait(false))
+ fchecks.Add(ec);
- /// <summary>
- /// Checks whether this command is equal to another one.
- /// </summary>
- /// <param name="cmd1">Command to compare to.</param>
- /// <param name="cmd2">Command to compare.</param>
- /// <returns>Whether the two commands are equal.</returns>
- public static bool operator ==(Command cmd1, Command cmd2)
- {
- var o1 = cmd1 as object;
- var o2 = cmd2 as object;
+ return fchecks;
+ }
- if (o1 == null && o2 != null)
- return false;
- else if (o1 != null && o2 == null)
- return false;
- else if (o1 == null && o2 == null)
- return true;
+ /// <summary>
+ /// Checks whether this command is equal to another one.
+ /// </summary>
+ /// <param name="cmd1">Command to compare to.</param>
+ /// <param name="cmd2">Command to compare.</param>
+ /// <returns>Whether the two commands are equal.</returns>
+ public static bool operator ==(Command cmd1, Command cmd2)
+ {
+ var o1 = cmd1 as object;
+ var o2 = cmd2 as object;
- return cmd1.QualifiedName == cmd2.QualifiedName;
- }
+ if (o1 == null && o2 != null)
+ return false;
+ else if (o1 != null && o2 == null)
+ return false;
+ else if (o1 == null && o2 == null)
+ return true;
- /// <summary>
- /// Checks whether this command is not equal to another one.
- /// </summary>
- /// <param name="cmd1">Command to compare to.</param>
- /// <param name="cmd2">Command to compare.</param>
- /// <returns>Whether the two commands are not equal.</returns>
- public static bool operator !=(Command cmd1, Command cmd2)
- => !(cmd1 == cmd2);
+ return cmd1.QualifiedName == cmd2.QualifiedName;
+ }
- /// <summary>
- /// Checks whether this command equals another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether this command is equal to another object.</returns>
- public override bool Equals(object obj)
- {
- var o1 = obj as object;
- var o2 = this as object;
+ /// <summary>
+ /// Checks whether this command is not equal to another one.
+ /// </summary>
+ /// <param name="cmd1">Command to compare to.</param>
+ /// <param name="cmd2">Command to compare.</param>
+ /// <returns>Whether the two commands are not equal.</returns>
+ public static bool operator !=(Command cmd1, Command cmd2)
+ => !(cmd1 == cmd2);
+
+ /// <summary>
+ /// Checks whether this command equals another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether this command is equal to another object.</returns>
+ public override bool Equals(object obj)
+ {
+ var o1 = obj as object;
+ var o2 = this as object;
- if (o1 == null && o2 != null)
- return false;
- else if (o1 != null && o2 == null)
- return false;
- else if (o1 == null && o2 == null)
- return true;
+ if (o1 == null && o2 != null)
+ return false;
+ else if (o1 != null && o2 == null)
+ return false;
+ else if (o1 == null && o2 == null)
+ return true;
- return obj is Command cmd
+ return obj is Command cmd
&& cmd.QualifiedName == this.QualifiedName;
- }
-
- /// <summary>
- /// Gets this command's hash code.
- /// </summary>
- /// <returns>This command's hash code.</returns>
- public override int GetHashCode() => this.QualifiedName.GetHashCode();
+ }
- /// <summary>
- /// Returns a string representation of this command.
- /// </summary>
- /// <returns>String representation of this command.</returns>
- public override string ToString() =>
- this is CommandGroup g
- ? $"Command Group: {this.QualifiedName}, {g.Children.Count} top-level children"
- : $"Command: {this.QualifiedName}";
+ /// <summary>
+ /// Gets this command's hash code.
+ /// </summary>
+ /// <returns>This command's hash code.</returns>
+ public override int GetHashCode() => this.QualifiedName.GetHashCode();
+
+ /// <summary>
+ /// Returns a string representation of this command.
+ /// </summary>
+ /// <returns>String representation of this command.</returns>
+ public override string ToString() =>
+ this is CommandGroup g
+ ? $"Command Group: {this.QualifiedName}, {g.Children.Count} top-level children"
+ : $"Command: {this.QualifiedName}";
+ }
}
diff --git a/DisCatSharp.CommandsNext/Entities/CommandArgument.cs b/DisCatSharp.CommandsNext/Entities/CommandArgument.cs
index 267d09b38..cd06d369b 100644
--- a/DisCatSharp.CommandsNext/Entities/CommandArgument.cs
+++ b/DisCatSharp.CommandsNext/Entities/CommandArgument.cs
@@ -1,72 +1,73 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Represents a command argument.
-/// </summary>
-public sealed class CommandArgument
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets this argument's name.
+ /// Represents a command argument.
/// </summary>
- public string Name { get; internal set; }
+ public sealed class CommandArgument
+ {
+ /// <summary>
+ /// Gets this argument's name.
+ /// </summary>
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets this argument's type.
- /// </summary>
- public Type Type { get; internal set; }
+ /// <summary>
+ /// Gets this argument's type.
+ /// </summary>
+ public Type Type { get; internal set; }
- /// <summary>
- /// Gets or sets whether this argument is an array argument.
- /// </summary>
- internal bool IsArray { get; set; } = false;
+ /// <summary>
+ /// Gets or sets whether this argument is an array argument.
+ /// </summary>
+ internal bool IsArray { get; set; } = false;
- /// <summary>
- /// Gets whether this argument is optional.
- /// </summary>
- public bool IsOptional { get; internal set; }
+ /// <summary>
+ /// Gets whether this argument is optional.
+ /// </summary>
+ public bool IsOptional { get; internal set; }
- /// <summary>
- /// Gets this argument's default value.
- /// </summary>
- public object DefaultValue { get; internal set; }
+ /// <summary>
+ /// Gets this argument's default value.
+ /// </summary>
+ public object DefaultValue { get; internal set; }
- /// <summary>
- /// Gets whether this argument catches all remaining arguments.
- /// </summary>
- public bool IsCatchAll { get; internal set; }
+ /// <summary>
+ /// Gets whether this argument catches all remaining arguments.
+ /// </summary>
+ public bool IsCatchAll { get; internal set; }
- /// <summary>
- /// Gets this argument's description.
- /// </summary>
- public string Description { get; internal set; }
+ /// <summary>
+ /// Gets this argument's description.
+ /// </summary>
+ public string Description { get; internal set; }
- /// <summary>
- /// Gets the custom attributes attached to this argument.
- /// </summary>
- public IReadOnlyCollection<Attribute> CustomAttributes { get; internal set; }
+ /// <summary>
+ /// Gets the custom attributes attached to this argument.
+ /// </summary>
+ public IReadOnlyCollection<Attribute> CustomAttributes { get; internal set; }
+ }
}
diff --git a/DisCatSharp.CommandsNext/Entities/CommandGroup.cs b/DisCatSharp.CommandsNext/Entities/CommandGroup.cs
index a52164231..de4c806bf 100644
--- a/DisCatSharp.CommandsNext/Entities/CommandGroup.cs
+++ b/DisCatSharp.CommandsNext/Entities/CommandGroup.cs
@@ -1,103 +1,104 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
using DisCatSharp.CommandsNext.Exceptions;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Represents a command group.
-/// </summary>
-public class CommandGroup : Command
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets all the commands that belong to this module.
+ /// Represents a command group.
/// </summary>
- public IReadOnlyList<Command> Children { get; internal set; }
+ public class CommandGroup : Command
+ {
+ /// <summary>
+ /// Gets all the commands that belong to this module.
+ /// </summary>
+ public IReadOnlyList<Command> Children { get; internal set; }
- /// <summary>
- /// Gets whether this command is executable without subcommands.
- /// </summary>
- public bool IsExecutableWithoutSubcommands => this.Overloads?.Any() == true;
+ /// <summary>
+ /// Gets whether this command is executable without subcommands.
+ /// </summary>
+ public bool IsExecutableWithoutSubcommands => this.Overloads?.Any() == true;
- /// <summary>
- /// Initializes a new instance of the <see cref="CommandGroup"/> class.
- /// </summary>
- internal CommandGroup() : base() { }
-
- /// <summary>
- /// Executes this command or its subcommand with specified context.
- /// </summary>
- /// <param name="ctx">Context to execute the command in.</param>
- /// <returns>Command's execution results.</returns>
- public override async Task<CommandResult> ExecuteAsync(CommandContext ctx)
- {
- var findPos = 0;
- var cn = CommandsNextUtilities.ExtractNextArgument(ctx.RawArgumentString, ref findPos);
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CommandGroup"/> class.
+ /// </summary>
+ internal CommandGroup() : base() { }
- if (cn != null)
+ /// <summary>
+ /// Executes this command or its subcommand with specified context.
+ /// </summary>
+ /// <param name="ctx">Context to execute the command in.</param>
+ /// <returns>Command's execution results.</returns>
+ public override async Task<CommandResult> ExecuteAsync(CommandContext ctx)
{
- var cmd = ctx.Config.CaseSensitive
- ? this.Children.FirstOrDefault(xc => xc.Name == cn || (xc.Aliases != null && xc.Aliases.Contains(cn)))
- : this.Children.FirstOrDefault(xc => xc.Name.ToLowerInvariant() == cn.ToLowerInvariant() || (xc.Aliases != null && xc.Aliases.Select(xs => xs.ToLowerInvariant()).Contains(cn.ToLowerInvariant())));
- if (cmd != null)
+ var findPos = 0;
+ var cn = CommandsNextUtilities.ExtractNextArgument(ctx.RawArgumentString, ref findPos);
+
+ if (cn != null)
{
- // pass the execution on
- var xctx = new CommandContext
+ var cmd = ctx.Config.CaseSensitive
+ ? this.Children.FirstOrDefault(xc => xc.Name == cn || (xc.Aliases != null && xc.Aliases.Contains(cn)))
+ : this.Children.FirstOrDefault(xc => xc.Name.ToLowerInvariant() == cn.ToLowerInvariant() || (xc.Aliases != null && xc.Aliases.Select(xs => xs.ToLowerInvariant()).Contains(cn.ToLowerInvariant())));
+ if (cmd != null)
{
- Client = ctx.Client,
- Message = ctx.Message,
- Command = cmd,
- Config = ctx.Config,
- RawArgumentString = ctx.RawArgumentString[findPos..],
- Prefix = ctx.Prefix,
- CommandsNext = ctx.CommandsNext,
- Services = ctx.Services
- };
-
- var fchecks = await cmd.RunChecksAsync(xctx, false).ConfigureAwait(false);
- return fchecks.Any()
- ? new CommandResult
+ // pass the execution on
+ var xctx = new CommandContext
{
- IsSuccessful = false,
- Exception = new ChecksFailedException(cmd, xctx, fchecks),
- Context = xctx
- }
- : await cmd.ExecuteAsync(xctx).ConfigureAwait(false);
- }
- }
+ Client = ctx.Client,
+ Message = ctx.Message,
+ Command = cmd,
+ Config = ctx.Config,
+ RawArgumentString = ctx.RawArgumentString[findPos..],
+ Prefix = ctx.Prefix,
+ CommandsNext = ctx.CommandsNext,
+ Services = ctx.Services
+ };
- return !this.IsExecutableWithoutSubcommands
- ? new CommandResult
- {
- IsSuccessful = false,
- Exception = new InvalidOperationException("No matching subcommands were found, and this group is not executable."),
- Context = ctx
+ var fchecks = await cmd.RunChecksAsync(xctx, false).ConfigureAwait(false);
+ return fchecks.Any()
+ ? new CommandResult
+ {
+ IsSuccessful = false,
+ Exception = new ChecksFailedException(cmd, xctx, fchecks),
+ Context = xctx
+ }
+ : await cmd.ExecuteAsync(xctx).ConfigureAwait(false);
+ }
}
- : await base.ExecuteAsync(ctx).ConfigureAwait(false);
+
+ return !this.IsExecutableWithoutSubcommands
+ ? new CommandResult
+ {
+ IsSuccessful = false,
+ Exception = new InvalidOperationException("No matching subcommands were found, and this group is not executable."),
+ Context = ctx
+ }
+ : await base.ExecuteAsync(ctx).ConfigureAwait(false);
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Entities/CommandHelpMessage.cs b/DisCatSharp.CommandsNext/Entities/CommandHelpMessage.cs
index cd4e26d96..8861527cb 100644
--- a/DisCatSharp.CommandsNext/Entities/CommandHelpMessage.cs
+++ b/DisCatSharp.CommandsNext/Entities/CommandHelpMessage.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
-namespace DisCatSharp.CommandsNext.Entities;
-
-/// <summary>
-/// Represents a formatted help message.
-/// </summary>
-public struct CommandHelpMessage
+namespace DisCatSharp.CommandsNext.Entities
{
/// <summary>
- /// Gets the contents of the help message.
+ /// Represents a formatted help message.
/// </summary>
- public string Content { get; }
+ public struct CommandHelpMessage
+ {
+ /// <summary>
+ /// Gets the contents of the help message.
+ /// </summary>
+ public string Content { get; }
- /// <summary>
- /// Gets the embed attached to the help message.
- /// </summary>
- public DiscordEmbed Embed { get; }
+ /// <summary>
+ /// Gets the embed attached to the help message.
+ /// </summary>
+ public DiscordEmbed Embed { get; }
- /// <summary>
- /// Creates a new instance of a help message.
- /// </summary>
- /// <param name="content">Contents of the message.</param>
- /// <param name="embed">Embed to attach to the message.</param>
- public CommandHelpMessage(string content = null, DiscordEmbed embed = null)
- {
- this.Content = content;
- this.Embed = embed;
+ /// <summary>
+ /// Creates a new instance of a help message.
+ /// </summary>
+ /// <param name="content">Contents of the message.</param>
+ /// <param name="embed">Embed to attach to the message.</param>
+ public CommandHelpMessage(string content = null, DiscordEmbed embed = null)
+ {
+ this.Content = content;
+ this.Embed = embed;
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Entities/CommandModule.cs b/DisCatSharp.CommandsNext/Entities/CommandModule.cs
index 11a35cd5f..147bb4ec8 100644
--- a/DisCatSharp.CommandsNext/Entities/CommandModule.cs
+++ b/DisCatSharp.CommandsNext/Entities/CommandModule.cs
@@ -1,106 +1,107 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Entities;
-
-/// <summary>
-/// Represents a base interface for all types of command modules.
-/// </summary>
-public interface ICommandModule
+namespace DisCatSharp.CommandsNext.Entities
{
/// <summary>
- /// Gets the type of this module.
- /// </summary>
- Type ModuleType { get; }
-
- /// <summary>
- /// Returns an instance of this module.
+ /// Represents a base interface for all types of command modules.
/// </summary>
- /// <param name="services">Services to instantiate the module with.</param>
- /// <returns>A created instance of this module.</returns>
- BaseCommandModule GetInstance(IServiceProvider services);
-}
+ public interface ICommandModule
+ {
+ /// <summary>
+ /// Gets the type of this module.
+ /// </summary>
+ Type ModuleType { get; }
-/// <summary>
-/// Represents a transient command module. This type of module is reinstantiated on every command call.
-/// </summary>
-public class TransientCommandModule : ICommandModule
-{
- /// <summary>
- /// Gets the type of this module.
- /// </summary>
- public Type ModuleType { get; }
+ /// <summary>
+ /// Returns an instance of this module.
+ /// </summary>
+ /// <param name="services">Services to instantiate the module with.</param>
+ /// <returns>A created instance of this module.</returns>
+ BaseCommandModule GetInstance(IServiceProvider services);
+ }
/// <summary>
- /// Creates a new transient module.
+ /// Represents a transient command module. This type of module is reinstantiated on every command call.
/// </summary>
- /// <param name="t">Type of the module to create.</param>
- internal TransientCommandModule(Type t)
+ public class TransientCommandModule : ICommandModule
{
- this.ModuleType = t;
+ /// <summary>
+ /// Gets the type of this module.
+ /// </summary>
+ public Type ModuleType { get; }
+
+ /// <summary>
+ /// Creates a new transient module.
+ /// </summary>
+ /// <param name="t">Type of the module to create.</param>
+ internal TransientCommandModule(Type t)
+ {
+ this.ModuleType = t;
+ }
+
+ /// <summary>
+ /// Creates a new instance of this module.
+ /// </summary>
+ /// <param name="services">Services to instantiate the module with.</param>
+ /// <returns>Created module.</returns>
+ public BaseCommandModule GetInstance(IServiceProvider services)
+ => this.ModuleType.CreateInstance(services) as BaseCommandModule;
}
/// <summary>
- /// Creates a new instance of this module.
+ /// Represents a singleton command module. This type of module is instantiated only when created.
/// </summary>
- /// <param name="services">Services to instantiate the module with.</param>
- /// <returns>Created module.</returns>
- public BaseCommandModule GetInstance(IServiceProvider services)
- => this.ModuleType.CreateInstance(services) as BaseCommandModule;
-}
+ public class SingletonCommandModule : ICommandModule
+ {
+ /// <summary>
+ /// Gets the type of this module.
+ /// </summary>
+ public Type ModuleType { get; }
-/// <summary>
-/// Represents a singleton command module. This type of module is instantiated only when created.
-/// </summary>
-public class SingletonCommandModule : ICommandModule
-{
- /// <summary>
- /// Gets the type of this module.
- /// </summary>
- public Type ModuleType { get; }
+ /// <summary>
+ /// Gets this module's instance.
+ /// </summary>
+ public BaseCommandModule Instance { get; }
- /// <summary>
- /// Gets this module's instance.
- /// </summary>
- public BaseCommandModule Instance { get; }
+ /// <summary>
+ /// Creates a new singleton module, and instantiates it.
+ /// </summary>
+ /// <param name="t">Type of the module to create.</param>
+ /// <param name="services">Services to instantiate the module with.</param>
+ internal SingletonCommandModule(Type t, IServiceProvider services)
+ {
+ this.ModuleType = t;
+ this.Instance = t.CreateInstance(services) as BaseCommandModule;
+ }
- /// <summary>
- /// Creates a new singleton module, and instantiates it.
- /// </summary>
- /// <param name="t">Type of the module to create.</param>
- /// <param name="services">Services to instantiate the module with.</param>
- internal SingletonCommandModule(Type t, IServiceProvider services)
- {
- this.ModuleType = t;
- this.Instance = t.CreateInstance(services) as BaseCommandModule;
+ /// <summary>
+ /// Returns the instance of this module.
+ /// </summary>
+ /// <param name="services">Services to instantiate the module with.</param>
+ /// <returns>This module's instance.</returns>
+ public BaseCommandModule GetInstance(IServiceProvider services)
+ => this.Instance;
}
-
- /// <summary>
- /// Returns the instance of this module.
- /// </summary>
- /// <param name="services">Services to instantiate the module with.</param>
- /// <returns>This module's instance.</returns>
- public BaseCommandModule GetInstance(IServiceProvider services)
- => this.Instance;
}
diff --git a/DisCatSharp.CommandsNext/Entities/CommandOverload.cs b/DisCatSharp.CommandsNext/Entities/CommandOverload.cs
index 2467c7e51..19d691a52 100644
--- a/DisCatSharp.CommandsNext/Entities/CommandOverload.cs
+++ b/DisCatSharp.CommandsNext/Entities/CommandOverload.cs
@@ -1,57 +1,58 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Represents a specific overload of a command.
-/// </summary>
-public sealed class CommandOverload
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets this command overload's arguments.
+ /// Represents a specific overload of a command.
/// </summary>
- public IReadOnlyList<CommandArgument> Arguments { get; internal set; }
+ public sealed class CommandOverload
+ {
+ /// <summary>
+ /// Gets this command overload's arguments.
+ /// </summary>
+ public IReadOnlyList<CommandArgument> Arguments { get; internal set; }
- /// <summary>
- /// Gets this command overload's priority.
- /// </summary>
- public int Priority { get; internal set; }
+ /// <summary>
+ /// Gets this command overload's priority.
+ /// </summary>
+ public int Priority { get; internal set; }
- /// <summary>
- /// Gets this command overload's delegate.
- /// </summary>
- internal Delegate Callable { get; set; }
+ /// <summary>
+ /// Gets this command overload's delegate.
+ /// </summary>
+ internal Delegate Callable { get; set; }
- /// <summary>
- /// Gets or sets the invocation target.
- /// </summary>
- internal object InvocationTarget { get; set; }
+ /// <summary>
+ /// Gets or sets the invocation target.
+ /// </summary>
+ internal object InvocationTarget { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="CommandOverload"/> class.
- /// </summary>
- internal CommandOverload() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CommandOverload"/> class.
+ /// </summary>
+ internal CommandOverload() { }
+ }
}
diff --git a/DisCatSharp.CommandsNext/Entities/CommandResult.cs b/DisCatSharp.CommandsNext/Entities/CommandResult.cs
index 6d6750150..9636b6b3e 100644
--- a/DisCatSharp.CommandsNext/Entities/CommandResult.cs
+++ b/DisCatSharp.CommandsNext/Entities/CommandResult.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents a command's execution result.
-/// </summary>
-public struct CommandResult
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets whether the command execution succeeded.
+ /// Represents a command's execution result.
/// </summary>
- public bool IsSuccessful { get; internal set; }
+ public struct CommandResult
+ {
+ /// <summary>
+ /// Gets whether the command execution succeeded.
+ /// </summary>
+ public bool IsSuccessful { get; internal set; }
- /// <summary>
- /// Gets the exception (if any) that occurred when executing the command.
- /// </summary>
- public Exception Exception { get; internal set; }
+ /// <summary>
+ /// Gets the exception (if any) that occurred when executing the command.
+ /// </summary>
+ public Exception Exception { get; internal set; }
- /// <summary>
- /// Gets the context in which the command was executed.
- /// </summary>
- public CommandContext Context { get; internal set; }
+ /// <summary>
+ /// Gets the context in which the command was executed.
+ /// </summary>
+ public CommandContext Context { get; internal set; }
+ }
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandContext.cs b/DisCatSharp.CommandsNext/EventArgs/CommandContext.cs
index 69cba3138..f9d18366b 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandContext.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandContext.cs
@@ -1,207 +1,208 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Entities;
using Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Represents a context in which a command is executed.
-/// </summary>
-public sealed class CommandContext
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets the client which received the message.
- /// </summary>
- public DiscordClient Client { get; internal set; }
-
- /// <summary>
- /// Gets the message that triggered the execution.
- /// </summary>
- public DiscordMessage Message { get; internal set; }
-
- /// <summary>
- /// Gets the channel in which the execution was triggered,
- /// </summary>
- public DiscordChannel Channel
- => this.Message.Channel;
-
- /// <summary>
- /// Gets the guild in which the execution was triggered. This property is null for commands sent over direct messages.
- /// </summary>
- public DiscordGuild Guild
- => this.Channel.Guild;
-
- /// <summary>
- /// Gets the user who triggered the execution.
- /// </summary>
- public DiscordUser User
- => this.Message.Author;
-
- /// <summary>
- /// Gets the member who triggered the execution. This property is null for commands sent over direct messages.
+ /// Represents a context in which a command is executed.
/// </summary>
- public DiscordMember Member
- => this._lazyMember.Value;
+ public sealed class CommandContext
+ {
+ /// <summary>
+ /// Gets the client which received the message.
+ /// </summary>
+ public DiscordClient Client { get; internal set; }
- private readonly Lazy<DiscordMember> _lazyMember;
+ /// <summary>
+ /// Gets the message that triggered the execution.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
- /// <summary>
- /// Gets the CommandsNext service instance that handled this command.
- /// </summary>
- public CommandsNextExtension CommandsNext { get; internal set; }
+ /// <summary>
+ /// Gets the channel in which the execution was triggered,
+ /// </summary>
+ public DiscordChannel Channel
+ => this.Message.Channel;
- /// <summary>
- /// Gets the service provider for this CNext instance.
- /// </summary>
- public IServiceProvider Services { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the execution was triggered. This property is null for commands sent over direct messages.
+ /// </summary>
+ public DiscordGuild Guild
+ => this.Channel.Guild;
- /// <summary>
- /// Gets the command that is being executed.
- /// </summary>
- public Command Command { get; internal set; }
+ /// <summary>
+ /// Gets the user who triggered the execution.
+ /// </summary>
+ public DiscordUser User
+ => this.Message.Author;
- /// <summary>
- /// Gets the overload of the command that is being executed.
- /// </summary>
- public CommandOverload Overload { get; internal set; }
+ /// <summary>
+ /// Gets the member who triggered the execution. This property is null for commands sent over direct messages.
+ /// </summary>
+ public DiscordMember Member
+ => this._lazyMember.Value;
- /// <summary>
- /// Gets the list of raw arguments passed to the command.
- /// </summary>
- public IReadOnlyList<string> RawArguments { get; internal set; }
+ private readonly Lazy<DiscordMember> _lazyMember;
- /// <summary>
- /// Gets the raw string from which the arguments were extracted.
- /// </summary>
- public string RawArgumentString { get; internal set; }
+ /// <summary>
+ /// Gets the CommandsNext service instance that handled this command.
+ /// </summary>
+ public CommandsNextExtension CommandsNext { get; internal set; }
- /// <summary>
- /// Gets the prefix used to invoke the command.
- /// </summary>
- public string Prefix { get; internal set; }
+ /// <summary>
+ /// Gets the service provider for this CNext instance.
+ /// </summary>
+ public IServiceProvider Services { get; internal set; }
- /// <summary>
- /// Gets or sets the config.
- /// </summary>
- internal CommandsNextConfiguration Config { get; set; }
+ /// <summary>
+ /// Gets the command that is being executed.
+ /// </summary>
+ public Command Command { get; internal set; }
- /// <summary>
- /// Gets or sets the service scope context.
- /// </summary>
- internal ServiceContext ServiceScopeContext { get; set; }
+ /// <summary>
+ /// Gets the overload of the command that is being executed.
+ /// </summary>
+ public CommandOverload Overload { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="CommandContext"/> class.
- /// </summary>
- internal CommandContext()
- {
- this._lazyMember = new Lazy<DiscordMember>(() => this.Guild != null && this.Guild.Members.TryGetValue(this.User.Id, out var member) ? member : this.Guild?.GetMemberAsync(this.User.Id).ConfigureAwait(false).GetAwaiter().GetResult());
- }
+ /// <summary>
+ /// Gets the list of raw arguments passed to the command.
+ /// </summary>
+ public IReadOnlyList<string> RawArguments { get; internal set; }
- /// <summary>
- /// Quickly respond to the message that triggered the command.
- /// </summary>
- /// <param name="content">Message to respond with.</param>
- /// <returns></returns>
- public Task<DiscordMessage> RespondAsync(string content)
- => this.Message.RespondAsync(content);
+ /// <summary>
+ /// Gets the raw string from which the arguments were extracted.
+ /// </summary>
+ public string RawArgumentString { get; internal set; }
- /// <summary>
- /// Quickly respond to the message that triggered the command.
- /// </summary>
- /// <param name="embed">Embed to attach.</param>
- /// <returns></returns>
- public Task<DiscordMessage> RespondAsync(DiscordEmbed embed)
- => this.Message.RespondAsync(embed);
+ /// <summary>
+ /// Gets the prefix used to invoke the command.
+ /// </summary>
+ public string Prefix { get; internal set; }
- /// <summary>
- /// Quickly respond to the message that triggered the command.
- /// </summary>
- /// <param name="content">Message to respond with.</param>
- /// <param name="embed">Embed to attach.</param>
- /// <returns></returns>
- public Task<DiscordMessage> RespondAsync(string content, DiscordEmbed embed)
- => this.Message.RespondAsync(content, embed);
+ /// <summary>
+ /// Gets or sets the config.
+ /// </summary>
+ internal CommandsNextConfiguration Config { get; set; }
- /// <summary>
- /// Quickly respond to the message that triggered the command.
- /// </summary>
- /// <param name="builder">The Discord Message builder.</param>
- /// <returns></returns>
- public Task<DiscordMessage> RespondAsync(DiscordMessageBuilder builder)
- => this.Message.RespondAsync(builder);
+ /// <summary>
+ /// Gets or sets the service scope context.
+ /// </summary>
+ internal ServiceContext ServiceScopeContext { get; set; }
- /// <summary>
- /// Quickly respond to the message that triggered the command.
- /// </summary>
- /// <param name="action">The Discord Message builder.</param>
- /// <returns></returns>
- public Task<DiscordMessage> RespondAsync(Action<DiscordMessageBuilder> action)
- => this.Message.RespondAsync(action);
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CommandContext"/> class.
+ /// </summary>
+ internal CommandContext()
+ {
+ this._lazyMember = new Lazy<DiscordMember>(() => this.Guild != null && this.Guild.Members.TryGetValue(this.User.Id, out var member) ? member : this.Guild?.GetMemberAsync(this.User.Id).ConfigureAwait(false).GetAwaiter().GetResult());
+ }
- /// <summary>
- /// Triggers typing in the channel containing the message that triggered the command.
- /// </summary>
- /// <returns></returns>
- public Task TriggerTypingAsync()
- => this.Channel.TriggerTypingAsync();
+ /// <summary>
+ /// Quickly respond to the message that triggered the command.
+ /// </summary>
+ /// <param name="content">Message to respond with.</param>
+ /// <returns></returns>
+ public Task<DiscordMessage> RespondAsync(string content)
+ => this.Message.RespondAsync(content);
- internal struct ServiceContext : IDisposable
- {
/// <summary>
- /// Gets the provider.
+ /// Quickly respond to the message that triggered the command.
/// </summary>
- public IServiceProvider Provider { get; }
+ /// <param name="embed">Embed to attach.</param>
+ /// <returns></returns>
+ public Task<DiscordMessage> RespondAsync(DiscordEmbed embed)
+ => this.Message.RespondAsync(embed);
+
/// <summary>
- /// Gets the scope.
+ /// Quickly respond to the message that triggered the command.
/// </summary>
- public IServiceScope Scope { get; }
+ /// <param name="content">Message to respond with.</param>
+ /// <param name="embed">Embed to attach.</param>
+ /// <returns></returns>
+ public Task<DiscordMessage> RespondAsync(string content, DiscordEmbed embed)
+ => this.Message.RespondAsync(content, embed);
+
/// <summary>
- /// Gets a value indicating whether is initialized.
+ /// Quickly respond to the message that triggered the command.
/// </summary>
- public bool IsInitialized { get; }
+ /// <param name="builder">The Discord Message builder.</param>
+ /// <returns></returns>
+ public Task<DiscordMessage> RespondAsync(DiscordMessageBuilder builder)
+ => this.Message.RespondAsync(builder);
/// <summary>
- /// Initializes a new instance of the <see cref="ServiceContext"/> class.
+ /// Quickly respond to the message that triggered the command.
/// </summary>
- /// <param name="services">The services.</param>
- /// <param name="scope">The scope.</param>
- public ServiceContext(IServiceProvider services, IServiceScope scope)
- {
- this.Provider = services;
- this.Scope = scope;
- this.IsInitialized = true;
- }
+ /// <param name="action">The Discord Message builder.</param>
+ /// <returns></returns>
+ public Task<DiscordMessage> RespondAsync(Action<DiscordMessageBuilder> action)
+ => this.Message.RespondAsync(action);
/// <summary>
- /// Disposes the command context.
+ /// Triggers typing in the channel containing the message that triggered the command.
/// </summary>
- public void Dispose() => this.Scope?.Dispose();
+ /// <returns></returns>
+ public Task TriggerTypingAsync()
+ => this.Channel.TriggerTypingAsync();
+
+ internal struct ServiceContext : IDisposable
+ {
+ /// <summary>
+ /// Gets the provider.
+ /// </summary>
+ public IServiceProvider Provider { get; }
+ /// <summary>
+ /// Gets the scope.
+ /// </summary>
+ public IServiceScope Scope { get; }
+ /// <summary>
+ /// Gets a value indicating whether is initialized.
+ /// </summary>
+ public bool IsInitialized { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ServiceContext"/> class.
+ /// </summary>
+ /// <param name="services">The services.</param>
+ /// <param name="scope">The scope.</param>
+ public ServiceContext(IServiceProvider services, IServiceScope scope)
+ {
+ this.Provider = services;
+ this.Scope = scope;
+ this.IsInitialized = true;
+ }
+
+ /// <summary>
+ /// Disposes the command context.
+ /// </summary>
+ public void Dispose() => this.Scope?.Dispose();
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs b/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs
index c1ffaad85..9a537c742 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="CommandsNextExtension.CommandErrored"/> event.
-/// </summary>
-public class CommandErrorEventArgs : CommandEventArgs
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets the exception.
+ /// Represents arguments for <see cref="CommandsNextExtension.CommandErrored"/> event.
/// </summary>
- public Exception Exception { get; internal set; }
+ public class CommandErrorEventArgs : CommandEventArgs
+ {
+ /// <summary>
+ /// Gets the exception.
+ /// </summary>
+ public Exception Exception { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="CommandErrorEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- public CommandErrorEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CommandErrorEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public CommandErrorEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs b/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs
index fb422fabc..f281ac26b 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs
@@ -1,51 +1,52 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Base class for all CNext-related events.
-/// </summary>
-public class CommandEventArgs : DiscordEventArgs
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Gets the context in which the command was executed.
+ /// Base class for all CNext-related events.
/// </summary>
- public CommandContext Context { get; internal set; }
+ public class CommandEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the context in which the command was executed.
+ /// </summary>
+ public CommandContext Context { get; internal set; }
- /// <summary>
- /// Gets the command that was executed.
- /// </summary>
- public Command Command
- => this.Context.Command;
+ /// <summary>
+ /// Gets the command that was executed.
+ /// </summary>
+ public Command Command
+ => this.Context.Command;
- /// <summary>
- /// Initializes a new instance of the <see cref="CommandEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- public CommandEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CommandEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public CommandEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs b/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs
index d5125a2a9..afd913b14 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs
@@ -1,38 +1,39 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="CommandsNextExtension.CommandExecuted"/> event.
-/// </summary>
-public class CommandExecutionEventArgs : CommandEventArgs
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Initializes a new instance of the <see cref="CommandExecutionEventArgs"/> class.
+ /// Represents arguments for <see cref="CommandsNextExtension.CommandExecuted"/> event.
/// </summary>
- /// <param name="provider">The provider.</param>
- public CommandExecutionEventArgs(IServiceProvider provider) : base(provider)
- { }
+ public class CommandExecutionEventArgs : CommandEventArgs
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CommandExecutionEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public CommandExecutionEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp.CommandsNext/Exceptions/ChecksFailedException.cs b/DisCatSharp.CommandsNext/Exceptions/ChecksFailedException.cs
index 49518eeaf..e5e2e6906 100644
--- a/DisCatSharp.CommandsNext/Exceptions/ChecksFailedException.cs
+++ b/DisCatSharp.CommandsNext/Exceptions/ChecksFailedException.cs
@@ -1,64 +1,65 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.CommandsNext.Attributes;
-namespace DisCatSharp.CommandsNext.Exceptions;
-
-/// <summary>
-/// Indicates that one or more checks for given command have failed.
-/// </summary>
-public class ChecksFailedException : Exception
+namespace DisCatSharp.CommandsNext.Exceptions
{
/// <summary>
- /// Gets the command that was executed.
+ /// Indicates that one or more checks for given command have failed.
/// </summary>
- public Command Command { get; }
+ public class ChecksFailedException : Exception
+ {
+ /// <summary>
+ /// Gets the command that was executed.
+ /// </summary>
+ public Command Command { get; }
- /// <summary>
- /// Gets the context in which given command was executed.
- /// </summary>
- public CommandContext Context { get; }
+ /// <summary>
+ /// Gets the context in which given command was executed.
+ /// </summary>
+ public CommandContext Context { get; }
- /// <summary>
- /// Gets the checks that failed.
- /// </summary>
- public IReadOnlyList<CheckBaseAttribute> FailedChecks { get; }
+ /// <summary>
+ /// Gets the checks that failed.
+ /// </summary>
+ public IReadOnlyList<CheckBaseAttribute> FailedChecks { get; }
- /// <summary>
- /// Creates a new <see cref="ChecksFailedException"/>.
- /// </summary>
- /// <param name="command">Command that failed to execute.</param>
- /// <param name="ctx">Context in which the command was executed.</param>
- /// <param name="failedChecks">A collection of checks that failed.</param>
- public ChecksFailedException(Command command, CommandContext ctx, IEnumerable<CheckBaseAttribute> failedChecks)
- : base("One or more pre-execution checks failed.")
- {
- this.Command = command;
- this.Context = ctx;
- this.FailedChecks = new ReadOnlyCollection<CheckBaseAttribute>(new List<CheckBaseAttribute>(failedChecks));
+ /// <summary>
+ /// Creates a new <see cref="ChecksFailedException"/>.
+ /// </summary>
+ /// <param name="command">Command that failed to execute.</param>
+ /// <param name="ctx">Context in which the command was executed.</param>
+ /// <param name="failedChecks">A collection of checks that failed.</param>
+ public ChecksFailedException(Command command, CommandContext ctx, IEnumerable<CheckBaseAttribute> failedChecks)
+ : base("One or more pre-execution checks failed.")
+ {
+ this.Command = command;
+ this.Context = ctx;
+ this.FailedChecks = new ReadOnlyCollection<CheckBaseAttribute>(new List<CheckBaseAttribute>(failedChecks));
+ }
}
}
diff --git a/DisCatSharp.CommandsNext/Exceptions/CommandNotFoundException.cs b/DisCatSharp.CommandsNext/Exceptions/CommandNotFoundException.cs
index db0bebb90..a7f358783 100644
--- a/DisCatSharp.CommandsNext/Exceptions/CommandNotFoundException.cs
+++ b/DisCatSharp.CommandsNext/Exceptions/CommandNotFoundException.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Exceptions;
-
-/// <summary>
-/// Thrown when the command service fails to find a command.
-/// </summary>
-public sealed class CommandNotFoundException : Exception
+namespace DisCatSharp.CommandsNext.Exceptions
{
/// <summary>
- /// Gets the name of the command that was not found.
+ /// Thrown when the command service fails to find a command.
/// </summary>
- public string CommandName { get; set; }
-
- /// <summary>
- /// Creates a new <see cref="CommandNotFoundException"/>.
- /// </summary>
- /// <param name="command">Name of the command that was not found.</param>
- public CommandNotFoundException(string command)
- : base("Specified command was not found.")
+ public sealed class CommandNotFoundException : Exception
{
- this.CommandName = command;
- }
+ /// <summary>
+ /// Gets the name of the command that was not found.
+ /// </summary>
+ public string CommandName { get; set; }
- /// <summary>
- /// Returns a string representation of this <see cref="CommandNotFoundException"/>.
- /// </summary>
- /// <returns>A string representation.</returns>
- public override string ToString() => $"{this.GetType()}: {this.Message}\nCommand name: {this.CommandName}"; // much like System.ArgumentNullException works
+ /// <summary>
+ /// Creates a new <see cref="CommandNotFoundException"/>.
+ /// </summary>
+ /// <param name="command">Name of the command that was not found.</param>
+ public CommandNotFoundException(string command)
+ : base("Specified command was not found.")
+ {
+ this.CommandName = command;
+ }
+
+ /// <summary>
+ /// Returns a string representation of this <see cref="CommandNotFoundException"/>.
+ /// </summary>
+ /// <returns>A string representation.</returns>
+ public override string ToString() => $"{this.GetType()}: {this.Message}\nCommand name: {this.CommandName}"; // much like System.ArgumentNullException works
+ }
}
diff --git a/DisCatSharp.CommandsNext/Exceptions/DuplicateCommandException.cs b/DisCatSharp.CommandsNext/Exceptions/DuplicateCommandException.cs
index 597b2176e..f6b7c9a65 100644
--- a/DisCatSharp.CommandsNext/Exceptions/DuplicateCommandException.cs
+++ b/DisCatSharp.CommandsNext/Exceptions/DuplicateCommandException.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Exceptions;
-
-/// <summary>
-/// Indicates that given command name or alias is taken.
-/// </summary>
-public class DuplicateCommandException : Exception
+namespace DisCatSharp.CommandsNext.Exceptions
{
/// <summary>
- /// Gets the name of the command that already exists.
+ /// Indicates that given command name or alias is taken.
/// </summary>
- public string CommandName { get; }
-
- /// <summary>
- /// Creates a new exception indicating that given command name is already taken.
- /// </summary>
- /// <param name="name">Name of the command that was taken.</param>
- internal DuplicateCommandException(string name)
- : base("A command with specified name already exists.")
+ public class DuplicateCommandException : Exception
{
- this.CommandName = name;
- }
+ /// <summary>
+ /// Gets the name of the command that already exists.
+ /// </summary>
+ public string CommandName { get; }
- /// <summary>
- /// Returns a string representation of this <see cref="DuplicateCommandException"/>.
- /// </summary>
- /// <returns>A string representation.</returns>
- public override string ToString() => $"{this.GetType()}: {this.Message}\nCommand name: {this.CommandName}"; // much like System.ArgumentException works
+ /// <summary>
+ /// Creates a new exception indicating that given command name is already taken.
+ /// </summary>
+ /// <param name="name">Name of the command that was taken.</param>
+ internal DuplicateCommandException(string name)
+ : base("A command with specified name already exists.")
+ {
+ this.CommandName = name;
+ }
+
+ /// <summary>
+ /// Returns a string representation of this <see cref="DuplicateCommandException"/>.
+ /// </summary>
+ /// <returns>A string representation.</returns>
+ public override string ToString() => $"{this.GetType()}: {this.Message}\nCommand name: {this.CommandName}"; // much like System.ArgumentException works
+ }
}
diff --git a/DisCatSharp.CommandsNext/Exceptions/DuplicateOverloadException.cs b/DisCatSharp.CommandsNext/Exceptions/DuplicateOverloadException.cs
index f1ad32f48..6b992e157 100644
--- a/DisCatSharp.CommandsNext/Exceptions/DuplicateOverloadException.cs
+++ b/DisCatSharp.CommandsNext/Exceptions/DuplicateOverloadException.cs
@@ -1,68 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.CommandsNext.Exceptions;
-
-/// <summary>
-/// Indicates that given argument set already exists as an overload for specified command.
-/// </summary>
-public class DuplicateOverloadException : Exception
+namespace DisCatSharp.CommandsNext.Exceptions
{
/// <summary>
- /// Gets the name of the command that already has the overload.
+ /// Indicates that given argument set already exists as an overload for specified command.
/// </summary>
- public string CommandName { get; }
+ public class DuplicateOverloadException : Exception
+ {
+ /// <summary>
+ /// Gets the name of the command that already has the overload.
+ /// </summary>
+ public string CommandName { get; }
- /// <summary>
- /// Gets the ordered collection of argument types for the specified overload.
- /// </summary>
- public IReadOnlyList<Type> ArgumentTypes { get; }
+ /// <summary>
+ /// Gets the ordered collection of argument types for the specified overload.
+ /// </summary>
+ public IReadOnlyList<Type> ArgumentTypes { get; }
- /// <summary>
- /// Gets the argument set key.
- /// </summary>
- private readonly string _argumentSetKey;
+ /// <summary>
+ /// Gets the argument set key.
+ /// </summary>
+ private readonly string _argumentSetKey;
- /// <summary>
- /// Creates a new exception indicating given argument set already exists as an overload for specified command.
- /// </summary>
- /// <param name="name">Name of the command with duplicated argument sets.</param>
- /// <param name="argumentTypes">Collection of ordered argument types for the command.</param>
- /// <param name="argumentSetKey">Overload identifier.</param>
- internal DuplicateOverloadException(string name, IList<Type> argumentTypes, string argumentSetKey)
- : base("An overload with specified argument types exists.")
- {
- this.CommandName = name;
- this.ArgumentTypes = new ReadOnlyCollection<Type>(argumentTypes);
- this._argumentSetKey = argumentSetKey;
- }
+ /// <summary>
+ /// Creates a new exception indicating given argument set already exists as an overload for specified command.
+ /// </summary>
+ /// <param name="name">Name of the command with duplicated argument sets.</param>
+ /// <param name="argumentTypes">Collection of ordered argument types for the command.</param>
+ /// <param name="argumentSetKey">Overload identifier.</param>
+ internal DuplicateOverloadException(string name, IList<Type> argumentTypes, string argumentSetKey)
+ : base("An overload with specified argument types exists.")
+ {
+ this.CommandName = name;
+ this.ArgumentTypes = new ReadOnlyCollection<Type>(argumentTypes);
+ this._argumentSetKey = argumentSetKey;
+ }
- /// <summary>
- /// Returns a string representation of this <see cref="DuplicateOverloadException"/>.
- /// </summary>
- /// <returns>A string representation.</returns>
- public override string ToString() => $"{this.GetType()}: {this.Message}\nCommand name: {this.CommandName}\nArgument types: {this._argumentSetKey}"; // much like System.ArgumentException works
+ /// <summary>
+ /// Returns a string representation of this <see cref="DuplicateOverloadException"/>.
+ /// </summary>
+ /// <returns>A string representation.</returns>
+ public override string ToString() => $"{this.GetType()}: {this.Message}\nCommand name: {this.CommandName}\nArgument types: {this._argumentSetKey}"; // much like System.ArgumentException works
+ }
}
diff --git a/DisCatSharp.CommandsNext/Exceptions/InvalidOverloadException.cs b/DisCatSharp.CommandsNext/Exceptions/InvalidOverloadException.cs
index 7626de47b..ed9f6ff06 100644
--- a/DisCatSharp.CommandsNext/Exceptions/InvalidOverloadException.cs
+++ b/DisCatSharp.CommandsNext/Exceptions/InvalidOverloadException.cs
@@ -1,74 +1,75 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Reflection;
-namespace DisCatSharp.CommandsNext.Exceptions;
-
-/// <summary>
-/// Thrown when the command service fails to build a command due to a problem with its overload.
-/// </summary>
-public sealed class InvalidOverloadException : Exception
+namespace DisCatSharp.CommandsNext.Exceptions
{
/// <summary>
- /// Gets the method that caused this exception.
+ /// Thrown when the command service fails to build a command due to a problem with its overload.
/// </summary>
- public MethodInfo Method { get; }
+ public sealed class InvalidOverloadException : Exception
+ {
+ /// <summary>
+ /// Gets the method that caused this exception.
+ /// </summary>
+ public MethodInfo Method { get; }
- /// <summary>
- /// Gets or sets the argument that caused the problem. This can be null.
- /// </summary>
- public ParameterInfo Parameter { get; }
+ /// <summary>
+ /// Gets or sets the argument that caused the problem. This can be null.
+ /// </summary>
+ public ParameterInfo Parameter { get; }
- /// <summary>
- /// Creates a new <see cref="InvalidOverloadException"/>.
- /// </summary>
- /// <param name="message">Exception message.</param>
- /// <param name="method">Method that caused the problem.</param>
- /// <param name="parameter">Method argument that caused the problem.</param>
- public InvalidOverloadException(string message, MethodInfo method, ParameterInfo parameter)
- : base(message)
- {
- this.Method = method;
- this.Parameter = parameter;
- }
+ /// <summary>
+ /// Creates a new <see cref="InvalidOverloadException"/>.
+ /// </summary>
+ /// <param name="message">Exception message.</param>
+ /// <param name="method">Method that caused the problem.</param>
+ /// <param name="parameter">Method argument that caused the problem.</param>
+ public InvalidOverloadException(string message, MethodInfo method, ParameterInfo parameter)
+ : base(message)
+ {
+ this.Method = method;
+ this.Parameter = parameter;
+ }
- /// <summary>
- /// Creates a new <see cref="InvalidOverloadException"/>.
- /// </summary>
- /// <param name="message">Exception message.</param>
- /// <param name="method">Method that caused the problem.</param>
- public InvalidOverloadException(string message, MethodInfo method)
- : this(message, method, null)
- { }
+ /// <summary>
+ /// Creates a new <see cref="InvalidOverloadException"/>.
+ /// </summary>
+ /// <param name="message">Exception message.</param>
+ /// <param name="method">Method that caused the problem.</param>
+ public InvalidOverloadException(string message, MethodInfo method)
+ : this(message, method, null)
+ { }
- /// <summary>
- /// Returns a string representation of this <see cref="InvalidOverloadException"/>.
- /// </summary>
- /// <returns>A string representation.</returns>
- public override string ToString() =>
- // much like System.ArgumentNullException works
- this.Parameter == null
- ? $"{this.GetType()}: {this.Message}\nMethod: {this.Method} (declared in {this.Method.DeclaringType})"
- : $"{this.GetType()}: {this.Message}\nMethod: {this.Method} (declared in {this.Method.DeclaringType})\nArgument: {this.Parameter.ParameterType} {this.Parameter.Name}";
+ /// <summary>
+ /// Returns a string representation of this <see cref="InvalidOverloadException"/>.
+ /// </summary>
+ /// <returns>A string representation.</returns>
+ public override string ToString() =>
+ // much like System.ArgumentNullException works
+ this.Parameter == null
+ ? $"{this.GetType()}: {this.Message}\nMethod: {this.Method} (declared in {this.Method.DeclaringType})"
+ : $"{this.GetType()}: {this.Message}\nMethod: {this.Method} (declared in {this.Method.DeclaringType})\nArgument: {this.Parameter.ParameterType} {this.Parameter.Name}";
+ }
}
diff --git a/DisCatSharp.CommandsNext/ExtensionMethods.cs b/DisCatSharp.CommandsNext/ExtensionMethods.cs
index 731c4b3af..948f6b345 100644
--- a/DisCatSharp.CommandsNext/ExtensionMethods.cs
+++ b/DisCatSharp.CommandsNext/ExtensionMethods.cs
@@ -1,214 +1,215 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Reflection;
using System.Threading.Tasks;
using DisCatSharp.CommandsNext.Builders;
using DisCatSharp.CommandsNext.Converters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.CommandsNext;
-
-/// <summary>
-/// Defines various extensions specific to CommandsNext.
-/// </summary>
-public static class ExtensionMethods
+namespace DisCatSharp.CommandsNext
{
/// <summary>
- /// Enables CommandsNext module on this <see cref="DiscordClient"/>.
+ /// Defines various extensions specific to CommandsNext.
/// </summary>
- /// <param name="client">Client to enable CommandsNext for.</param>
- /// <param name="cfg">CommandsNext configuration to use.</param>
- /// <returns>Created <see cref="CommandsNextExtension"/>.</returns>
- public static CommandsNextExtension UseCommandsNext(this DiscordClient client, CommandsNextConfiguration cfg)
+ public static class ExtensionMethods
{
- if (client.GetExtension<CommandsNextExtension>() != null)
- throw new InvalidOperationException("CommandsNext is already enabled for that client.");
+ /// <summary>
+ /// Enables CommandsNext module on this <see cref="DiscordClient"/>.
+ /// </summary>
+ /// <param name="client">Client to enable CommandsNext for.</param>
+ /// <param name="cfg">CommandsNext configuration to use.</param>
+ /// <returns>Created <see cref="CommandsNextExtension"/>.</returns>
+ public static CommandsNextExtension UseCommandsNext(this DiscordClient client, CommandsNextConfiguration cfg)
+ {
+ if (client.GetExtension<CommandsNextExtension>() != null)
+ throw new InvalidOperationException("CommandsNext is already enabled for that client.");
- if (!Utilities.HasMessageIntents(client.Configuration.Intents))
- client.Logger.LogCritical(CommandsNextEvents.Intents, "The CommandsNext extension is registered but there are no message intents enabled. It is highly recommended to enable them.");
+ if (!Utilities.HasMessageIntents(client.Configuration.Intents))
+ client.Logger.LogCritical(CommandsNextEvents.Intents, "The CommandsNext extension is registered but there are no message intents enabled. It is highly recommended to enable them.");
- if (!client.Configuration.Intents.HasIntent(DiscordIntents.Guilds))
- client.Logger.LogCritical(CommandsNextEvents.Intents, "The CommandsNext extension is registered but the guilds intent is not enabled. It is highly recommended to enable it.");
+ if (!client.Configuration.Intents.HasIntent(DiscordIntents.Guilds))
+ client.Logger.LogCritical(CommandsNextEvents.Intents, "The CommandsNext extension is registered but the guilds intent is not enabled. It is highly recommended to enable it.");
- cfg.ServiceProvider ??= client.ServiceProvider ?? new ServiceCollection().BuildServiceProvider(true);
+ cfg.ServiceProvider ??= client.ServiceProvider ?? new ServiceCollection().BuildServiceProvider(true);
- var cnext = new CommandsNextExtension(cfg);
- client.AddExtension(cnext);
- return cnext;
- }
-
- /// <summary>
- /// Enables CommandsNext module on all shards in this <see cref="DiscordShardedClient"/>.
- /// </summary>
- /// <param name="client">Client to enable CommandsNext for.</param>
- /// <param name="cfg">CommandsNext configuration to use.</param>
- /// <returns>A dictionary of created <see cref="CommandsNextExtension"/>, indexed by shard id.</returns>
- public static async Task<IReadOnlyDictionary<int, CommandsNextExtension>> UseCommandsNextAsync(this DiscordShardedClient client, CommandsNextConfiguration cfg)
- {
- var modules = new Dictionary<int, CommandsNextExtension>();
- await client.InitializeShardsAsync().ConfigureAwait(false);
+ var cnext = new CommandsNextExtension(cfg);
+ client.AddExtension(cnext);
+ return cnext;
+ }
- foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ /// <summary>
+ /// Enables CommandsNext module on all shards in this <see cref="DiscordShardedClient"/>.
+ /// </summary>
+ /// <param name="client">Client to enable CommandsNext for.</param>
+ /// <param name="cfg">CommandsNext configuration to use.</param>
+ /// <returns>A dictionary of created <see cref="CommandsNextExtension"/>, indexed by shard id.</returns>
+ public static async Task<IReadOnlyDictionary<int, CommandsNextExtension>> UseCommandsNextAsync(this DiscordShardedClient client, CommandsNextConfiguration cfg)
{
- var cnext = shard.GetExtension<CommandsNextExtension>();
- if (cnext == null)
- cnext = shard.UseCommandsNext(cfg);
+ var modules = new Dictionary<int, CommandsNextExtension>();
+ await client.InitializeShardsAsync().ConfigureAwait(false);
- modules[shard.ShardId] = cnext;
- }
+ foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ {
+ var cnext = shard.GetExtension<CommandsNextExtension>();
+ if (cnext == null)
+ cnext = shard.UseCommandsNext(cfg);
- return new ReadOnlyDictionary<int, CommandsNextExtension>(modules);
- }
+ modules[shard.ShardId] = cnext;
+ }
- /// <summary>
- /// Gets the active CommandsNext module for this client.
- /// </summary>
- /// <param name="client">Client to get CommandsNext module from.</param>
- /// <returns>The module, or null if not activated.</returns>
- public static CommandsNextExtension GetCommandsNext(this DiscordClient client)
- => client.GetExtension<CommandsNextExtension>();
+ return new ReadOnlyDictionary<int, CommandsNextExtension>(modules);
+ }
+ /// <summary>
+ /// Gets the active CommandsNext module for this client.
+ /// </summary>
+ /// <param name="client">Client to get CommandsNext module from.</param>
+ /// <returns>The module, or null if not activated.</returns>
+ public static CommandsNextExtension GetCommandsNext(this DiscordClient client)
+ => client.GetExtension<CommandsNextExtension>();
+
+
+ /// <summary>
+ /// Gets the active CommandsNext modules for all shards in this client.
+ /// </summary>
+ /// <param name="client">Client to get CommandsNext instances from.</param>
+ /// <returns>A dictionary of the modules, indexed by shard id.</returns>
+ public static async Task<IReadOnlyDictionary<int, CommandsNextExtension>> GetCommandsNextAsync(this DiscordShardedClient client)
+ {
+ await client.InitializeShardsAsync().ConfigureAwait(false);
+ var extensions = new Dictionary<int, CommandsNextExtension>();
- /// <summary>
- /// Gets the active CommandsNext modules for all shards in this client.
- /// </summary>
- /// <param name="client">Client to get CommandsNext instances from.</param>
- /// <returns>A dictionary of the modules, indexed by shard id.</returns>
- public static async Task<IReadOnlyDictionary<int, CommandsNextExtension>> GetCommandsNextAsync(this DiscordShardedClient client)
- {
- await client.InitializeShardsAsync().ConfigureAwait(false);
- var extensions = new Dictionary<int, CommandsNextExtension>();
+ foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ {
+ extensions.Add(shard.ShardId, shard.GetExtension<CommandsNextExtension>());
+ }
- foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
- {
- extensions.Add(shard.ShardId, shard.GetExtension<CommandsNextExtension>());
+ return new ReadOnlyDictionary<int, CommandsNextExtension>(extensions);
}
- return new ReadOnlyDictionary<int, CommandsNextExtension>(extensions);
- }
-
- /// <summary>
- /// Registers all commands from a given assembly. The command classes need to be public to be considered for registration.
- /// </summary>
- /// <param name="extensions">Extensions to register commands on.</param>
- /// <param name="assembly">Assembly to register commands from.</param>
- public static void RegisterCommands(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, Assembly assembly)
- {
- foreach (var extension in extensions.Values)
- extension.RegisterCommands(assembly);
- }
- /// <summary>
- /// Registers all commands from a given command class.
- /// </summary>
- /// <typeparam name="T">Class which holds commands to register.</typeparam>
- /// <param name="extensions">Extensions to register commands on.</param>
- public static void RegisterCommands<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions) where T : BaseCommandModule
- {
- foreach (var extension in extensions.Values)
- extension.RegisterCommands<T>();
- }
- /// <summary>
- /// Registers all commands from a given command class.
- /// </summary>
- /// <param name="extensions">Extensions to register commands on.</param>
- /// <param name="t">Type of the class which holds commands to register.</param>
- public static void RegisterCommands(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, Type t)
- {
- foreach (var extension in extensions.Values)
- extension.RegisterCommands(t);
- }
- /// <summary>
- /// Builds and registers all supplied commands.
- /// </summary>
- /// <param name="extensions">Extensions to register commands on.</param>
- /// <param name="cmds">Commands to build and register.</param>
- public static void RegisterCommands(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, params CommandBuilder[] cmds)
- {
- foreach (var extension in extensions.Values)
- extension.RegisterCommands(cmds);
- }
+ /// <summary>
+ /// Registers all commands from a given assembly. The command classes need to be public to be considered for registration.
+ /// </summary>
+ /// <param name="extensions">Extensions to register commands on.</param>
+ /// <param name="assembly">Assembly to register commands from.</param>
+ public static void RegisterCommands(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, Assembly assembly)
+ {
+ foreach (var extension in extensions.Values)
+ extension.RegisterCommands(assembly);
+ }
+ /// <summary>
+ /// Registers all commands from a given command class.
+ /// </summary>
+ /// <typeparam name="T">Class which holds commands to register.</typeparam>
+ /// <param name="extensions">Extensions to register commands on.</param>
+ public static void RegisterCommands<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions) where T : BaseCommandModule
+ {
+ foreach (var extension in extensions.Values)
+ extension.RegisterCommands<T>();
+ }
+ /// <summary>
+ /// Registers all commands from a given command class.
+ /// </summary>
+ /// <param name="extensions">Extensions to register commands on.</param>
+ /// <param name="t">Type of the class which holds commands to register.</param>
+ public static void RegisterCommands(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, Type t)
+ {
+ foreach (var extension in extensions.Values)
+ extension.RegisterCommands(t);
+ }
+ /// <summary>
+ /// Builds and registers all supplied commands.
+ /// </summary>
+ /// <param name="extensions">Extensions to register commands on.</param>
+ /// <param name="cmds">Commands to build and register.</param>
+ public static void RegisterCommands(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, params CommandBuilder[] cmds)
+ {
+ foreach (var extension in extensions.Values)
+ extension.RegisterCommands(cmds);
+ }
- /// <summary>
- /// Unregisters specified commands from CommandsNext.
- /// </summary>
- /// <param name="extensions">Extensions to unregister commands on.</param>
- /// <param name="cmds">Commands to unregister.</param>
- public static void UnregisterCommands(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, params Command[] cmds)
- {
- foreach (var extension in extensions.Values)
- extension.UnregisterCommands(cmds);
- }
+ /// <summary>
+ /// Unregisters specified commands from CommandsNext.
+ /// </summary>
+ /// <param name="extensions">Extensions to unregister commands on.</param>
+ /// <param name="cmds">Commands to unregister.</param>
+ public static void UnregisterCommands(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, params Command[] cmds)
+ {
+ foreach (var extension in extensions.Values)
+ extension.UnregisterCommands(cmds);
+ }
- /// <summary>
- /// Registers an argument converter for specified type.
- /// </summary>
- /// <typeparam name="T">Type for which to register the converter.</typeparam>
- /// <param name="extensions">Extensions to register the converter on.</param>
- /// <param name="converter">Converter to register.</param>
- public static void RegisterConverter<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, IArgumentConverter<T> converter)
- {
- foreach (var extension in extensions.Values)
- extension.RegisterConverter(converter);
- }
+ /// <summary>
+ /// Registers an argument converter for specified type.
+ /// </summary>
+ /// <typeparam name="T">Type for which to register the converter.</typeparam>
+ /// <param name="extensions">Extensions to register the converter on.</param>
+ /// <param name="converter">Converter to register.</param>
+ public static void RegisterConverter<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, IArgumentConverter<T> converter)
+ {
+ foreach (var extension in extensions.Values)
+ extension.RegisterConverter(converter);
+ }
- /// <summary>
- /// Unregisters an argument converter for specified type.
- /// </summary>
- /// <typeparam name="T">Type for which to unregister the converter.</typeparam>
- /// <param name="extensions">Extensions to unregister the converter on.</param>
- public static void UnregisterConverter<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions)
- {
- foreach (var extension in extensions.Values)
- extension.UnregisterConverter<T>();
- }
+ /// <summary>
+ /// Unregisters an argument converter for specified type.
+ /// </summary>
+ /// <typeparam name="T">Type for which to unregister the converter.</typeparam>
+ /// <param name="extensions">Extensions to unregister the converter on.</param>
+ public static void UnregisterConverter<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions)
+ {
+ foreach (var extension in extensions.Values)
+ extension.UnregisterConverter<T>();
+ }
- /// <summary>
- /// Registers a user-friendly type name.
- /// </summary>
- /// <typeparam name="T">Type to register the name for.</typeparam>
- /// <param name="extensions">Extensions to register the name on.</param>
- /// <param name="value">Name to register.</param>
- public static void RegisterUserFriendlyTypeName<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, string value)
- {
- foreach (var extension in extensions.Values)
- extension.RegisterUserFriendlyTypeName<T>(value);
- }
+ /// <summary>
+ /// Registers a user-friendly type name.
+ /// </summary>
+ /// <typeparam name="T">Type to register the name for.</typeparam>
+ /// <param name="extensions">Extensions to register the name on.</param>
+ /// <param name="value">Name to register.</param>
+ public static void RegisterUserFriendlyTypeName<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions, string value)
+ {
+ foreach (var extension in extensions.Values)
+ extension.RegisterUserFriendlyTypeName<T>(value);
+ }
- /// <summary>
- /// Sets the help formatter to use with the default help command.
- /// </summary>
- /// <typeparam name="T">Type of the formatter to use.</typeparam>
- /// <param name="extensions">Extensions to set the help formatter on.</param>
- public static void SetHelpFormatter<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions) where T : BaseHelpFormatter
- {
- foreach (var extension in extensions.Values)
- extension.SetHelpFormatter<T>();
+ /// <summary>
+ /// Sets the help formatter to use with the default help command.
+ /// </summary>
+ /// <typeparam name="T">Type of the formatter to use.</typeparam>
+ /// <param name="extensions">Extensions to set the help formatter on.</param>
+ public static void SetHelpFormatter<T>(this IReadOnlyDictionary<int, CommandsNextExtension> extensions) where T : BaseHelpFormatter
+ {
+ foreach (var extension in extensions.Values)
+ extension.SetHelpFormatter<T>();
+ }
}
}
diff --git a/DisCatSharp.Common/Attributes/DateTimeFormatAttribute.cs b/DisCatSharp.Common/Attributes/DateTimeFormatAttribute.cs
index c2c2232d6..e1a645f57 100644
--- a/DisCatSharp.Common/Attributes/DateTimeFormatAttribute.cs
+++ b/DisCatSharp.Common/Attributes/DateTimeFormatAttribute.cs
@@ -1,132 +1,133 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
// ReSharper disable InconsistentNaming
-namespace DisCatSharp.Common.Serialization;
-
-/// <summary>
-/// Defines the format for string-serialized <see cref="System.DateTime"/> and <see cref="System.DateTimeOffset"/> objects.
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class DateTimeFormatAttribute : SerializationAttribute
+namespace DisCatSharp.Common.Serialization
{
/// <summary>
- /// Gets the ISO 8601 format string of "yyyy-MM-ddTHH:mm:ss.fffzzz".
- /// </summary>
- public const string FORMAT_ISO_8601 = "yyyy-MM-ddTHH:mm:ss.fffzzz";
-
- /// <summary>
- /// Gets the RFC 1123 format string of "R".
- /// </summary>
- public const string FORMAT_RFC_1123 = "R";
-
- /// <summary>
- /// Gets the general long format.
- /// </summary>
- public const string FORMAT_LONG = "G";
-
- /// <summary>
- /// Gets the general short format.
- /// </summary>
- public const string FORMAT_SHORT = "g";
-
- /// <summary>
- /// Gets the custom datetime format string to use.
- /// </summary>
- public string Format { get; }
-
- /// <summary>
- /// Gets the predefined datetime format kind.
+ /// Defines the format for string-serialized <see cref="System.DateTime"/> and <see cref="System.DateTimeOffset"/> objects.
/// </summary>
- public DateTimeFormatKind Kind { get; }
-
- /// <summary>
- /// Specifies a predefined format to use.
- /// </summary>
- /// <param name="kind">Predefined format kind to use.</param>
- public DateTimeFormatAttribute(DateTimeFormatKind kind)
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class DateTimeFormatAttribute : SerializationAttribute
{
- if (kind < 0 || kind > DateTimeFormatKind.InvariantLocaleShort)
- throw new ArgumentOutOfRangeException(nameof(kind), "Specified format kind is not legal or supported.");
-
- this.Kind = kind;
- this.Format = null;
+ /// <summary>
+ /// Gets the ISO 8601 format string of "yyyy-MM-ddTHH:mm:ss.fffzzz".
+ /// </summary>
+ public const string FORMAT_ISO_8601 = "yyyy-MM-ddTHH:mm:ss.fffzzz";
+
+ /// <summary>
+ /// Gets the RFC 1123 format string of "R".
+ /// </summary>
+ public const string FORMAT_RFC_1123 = "R";
+
+ /// <summary>
+ /// Gets the general long format.
+ /// </summary>
+ public const string FORMAT_LONG = "G";
+
+ /// <summary>
+ /// Gets the general short format.
+ /// </summary>
+ public const string FORMAT_SHORT = "g";
+
+ /// <summary>
+ /// Gets the custom datetime format string to use.
+ /// </summary>
+ public string Format { get; }
+
+ /// <summary>
+ /// Gets the predefined datetime format kind.
+ /// </summary>
+ public DateTimeFormatKind Kind { get; }
+
+ /// <summary>
+ /// Specifies a predefined format to use.
+ /// </summary>
+ /// <param name="kind">Predefined format kind to use.</param>
+ public DateTimeFormatAttribute(DateTimeFormatKind kind)
+ {
+ if (kind < 0 || kind > DateTimeFormatKind.InvariantLocaleShort)
+ throw new ArgumentOutOfRangeException(nameof(kind), "Specified format kind is not legal or supported.");
+
+ this.Kind = kind;
+ this.Format = null;
+ }
+
+ /// <summary>
+ /// <para>Specifies a custom format to use.</para>
+ /// <para>See https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings for more details.</para>
+ /// </summary>
+ /// <param name="format">Custom format string to use.</param>
+ public DateTimeFormatAttribute(string format)
+ {
+ if (string.IsNullOrWhiteSpace(format))
+ throw new ArgumentNullException(nameof(format), "Specified format cannot be null or empty.");
+
+ this.Kind = DateTimeFormatKind.Custom;
+ this.Format = format;
+ }
}
/// <summary>
- /// <para>Specifies a custom format to use.</para>
- /// <para>See https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings for more details.</para>
+ /// <para>Defines which built-in format to use for for <see cref="System.DateTime"/> and <see cref="System.DateTimeOffset"/> serialization.</para>
+ /// <para>See https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings and https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings for more details.</para>
/// </summary>
- /// <param name="format">Custom format string to use.</param>
- public DateTimeFormatAttribute(string format)
+ public enum DateTimeFormatKind : int
{
- if (string.IsNullOrWhiteSpace(format))
- throw new ArgumentNullException(nameof(format), "Specified format cannot be null or empty.");
-
- this.Kind = DateTimeFormatKind.Custom;
- this.Format = format;
+ /// <summary>
+ /// Specifies ISO 8601 format, which is equivalent to .NET format string of "yyyy-MM-ddTHH:mm:ss.fffzzz".
+ /// </summary>
+ ISO8601 = 0,
+
+ /// <summary>
+ /// Specifies RFC 1123 format, which is equivalent to .NET format string of "R".
+ /// </summary>
+ RFC1123 = 1,
+
+ /// <summary>
+ /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.CurrentCulture"/>, with a format string of "G". This format is not recommended for portability reasons.
+ /// </summary>
+ CurrentLocaleLong = 2,
+
+ /// <summary>
+ /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.CurrentCulture"/>, with a format string of "g". This format is not recommended for portability reasons.
+ /// </summary>
+ CurrentLocaleShort = 3,
+
+ /// <summary>
+ /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "G".
+ /// </summary>
+ InvariantLocaleLong = 4,
+
+ /// <summary>
+ /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "g".
+ /// </summary>
+ InvariantLocaleShort = 5,
+
+ /// <summary>
+ /// Specifies a custom format. This value is not usable directly.
+ /// </summary>
+ Custom = int.MaxValue
}
}
-
-/// <summary>
-/// <para>Defines which built-in format to use for for <see cref="System.DateTime"/> and <see cref="System.DateTimeOffset"/> serialization.</para>
-/// <para>See https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings and https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings for more details.</para>
-/// </summary>
-public enum DateTimeFormatKind : int
-{
- /// <summary>
- /// Specifies ISO 8601 format, which is equivalent to .NET format string of "yyyy-MM-ddTHH:mm:ss.fffzzz".
- /// </summary>
- ISO8601 = 0,
-
- /// <summary>
- /// Specifies RFC 1123 format, which is equivalent to .NET format string of "R".
- /// </summary>
- RFC1123 = 1,
-
- /// <summary>
- /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.CurrentCulture"/>, with a format string of "G". This format is not recommended for portability reasons.
- /// </summary>
- CurrentLocaleLong = 2,
-
- /// <summary>
- /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.CurrentCulture"/>, with a format string of "g". This format is not recommended for portability reasons.
- /// </summary>
- CurrentLocaleShort = 3,
-
- /// <summary>
- /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "G".
- /// </summary>
- InvariantLocaleLong = 4,
-
- /// <summary>
- /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "g".
- /// </summary>
- InvariantLocaleShort = 5,
-
- /// <summary>
- /// Specifies a custom format. This value is not usable directly.
- /// </summary>
- Custom = int.MaxValue
-}
diff --git a/DisCatSharp.Common/Attributes/DecomposerAttribute.cs b/DisCatSharp.Common/Attributes/DecomposerAttribute.cs
index 49503b638..b6ba47f89 100644
--- a/DisCatSharp.Common/Attributes/DecomposerAttribute.cs
+++ b/DisCatSharp.Common/Attributes/DecomposerAttribute.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Serialization;
-
-/// <summary>
-/// Specifies a decomposer for a given type or property.
-/// </summary>
-[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class DecomposerAttribute : SerializationAttribute
+namespace DisCatSharp.Common.Serialization
{
/// <summary>
- /// Gets the type of the decomposer.
+ /// Specifies a decomposer for a given type or property.
/// </summary>
- public Type DecomposerType { get; }
-
- /// <summary>
- /// Specifies a decomposer for given type or property.
- /// </summary>
- /// <param name="type">Type of decomposer to use.</param>
- public DecomposerAttribute(Type type)
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class DecomposerAttribute : SerializationAttribute
{
- if (!typeof(IDecomposer).IsAssignableFrom(type) || !type.IsClass || type.IsAbstract) // abstract covers static - static = abstract + sealed
- throw new ArgumentException("Invalid type specified. Must be a non-abstract class which implements DisCatSharp.Common.Serialization.IDecomposer interface.", nameof(type));
+ /// <summary>
+ /// Gets the type of the decomposer.
+ /// </summary>
+ public Type DecomposerType { get; }
+
+ /// <summary>
+ /// Specifies a decomposer for given type or property.
+ /// </summary>
+ /// <param name="type">Type of decomposer to use.</param>
+ public DecomposerAttribute(Type type)
+ {
+ if (!typeof(IDecomposer).IsAssignableFrom(type) || !type.IsClass || type.IsAbstract) // abstract covers static - static = abstract + sealed
+ throw new ArgumentException("Invalid type specified. Must be a non-abstract class which implements DisCatSharp.Common.Serialization.IDecomposer interface.", nameof(type));
- this.DecomposerType = type;
+ this.DecomposerType = type;
+ }
}
}
diff --git a/DisCatSharp.Common/Attributes/EnumAttributes.cs b/DisCatSharp.Common/Attributes/EnumAttributes.cs
index b52b2cad2..7c2485cc2 100644
--- a/DisCatSharp.Common/Attributes/EnumAttributes.cs
+++ b/DisCatSharp.Common/Attributes/EnumAttributes.cs
@@ -1,41 +1,42 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Serialization;
+namespace DisCatSharp.Common.Serialization
+{
+ /// <summary>
+ /// <para>Specifies that this enum should be serialized and deserialized as its underlying numeric type.</para>
+ /// <para>This is used to change the behaviour of enum serialization.</para>
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class NumericEnumAttribute : SerializationAttribute
+ { }
-/// <summary>
-/// <para>Specifies that this enum should be serialized and deserialized as its underlying numeric type.</para>
-/// <para>This is used to change the behaviour of enum serialization.</para>
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class NumericEnumAttribute : SerializationAttribute
-{ }
-
-/// <summary>
-/// <para>Specifies that this enum should be serialized and deserialized as its string representation.</para>
-/// <para>This is used to change the behaviour of enum serialization.</para>
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class StringEnumAttribute : SerializationAttribute
-{ }
+ /// <summary>
+ /// <para>Specifies that this enum should be serialized and deserialized as its string representation.</para>
+ /// <para>This is used to change the behaviour of enum serialization.</para>
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class StringEnumAttribute : SerializationAttribute
+ { }
+}
diff --git a/DisCatSharp.Common/Attributes/IncludeNullAttribute.cs b/DisCatSharp.Common/Attributes/IncludeNullAttribute.cs
index 3738e8af3..afb4cc8ac 100644
--- a/DisCatSharp.Common/Attributes/IncludeNullAttribute.cs
+++ b/DisCatSharp.Common/Attributes/IncludeNullAttribute.cs
@@ -1,34 +1,35 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Serialization;
-
-/// <summary>
-/// <para>Specifies that if the value of the field or property is null, it should be included in the serialized data.</para>
-/// <para>This alters the default behaviour of ignoring nulls.</para>
-/// </summary>
-[Obsolete("Use [DataMember] with EmitDefaultValue = true.")]
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class IncludeNullAttribute : SerializationAttribute
-{ }
+namespace DisCatSharp.Common.Serialization
+{
+ /// <summary>
+ /// <para>Specifies that if the value of the field or property is null, it should be included in the serialized data.</para>
+ /// <para>This alters the default behaviour of ignoring nulls.</para>
+ /// </summary>
+ [Obsolete("Use [DataMember] with EmitDefaultValue = true.")]
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class IncludeNullAttribute : SerializationAttribute
+ { }
+}
diff --git a/DisCatSharp.Common/Attributes/Int53Attribute.cs b/DisCatSharp.Common/Attributes/Int53Attribute.cs
index e3f440c28..b8bad4b91 100644
--- a/DisCatSharp.Common/Attributes/Int53Attribute.cs
+++ b/DisCatSharp.Common/Attributes/Int53Attribute.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Serialization;
-
-/// <summary>
-/// <para>Specifies that this 64-bit integer uses no more than 53 bits to represent its value.</para>
-/// <para>This is used to indicate that large numbers are safe for direct serialization into formats which do support 64-bit integers natively (such as JSON).</para>
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class Int53Attribute : SerializationAttribute
+namespace DisCatSharp.Common.Serialization
{
/// <summary>
- /// <para>Gets the maximum safe value representable as an integer by a IEEE754 64-bit binary floating point value.</para>
- /// <para>This value equals to 9007199254740991.</para>
+ /// <para>Specifies that this 64-bit integer uses no more than 53 bits to represent its value.</para>
+ /// <para>This is used to indicate that large numbers are safe for direct serialization into formats which do support 64-bit integers natively (such as JSON).</para>
/// </summary>
- public const long MAX_VALUE = (1L << 53) - 1;
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class Int53Attribute : SerializationAttribute
+ {
+ /// <summary>
+ /// <para>Gets the maximum safe value representable as an integer by a IEEE754 64-bit binary floating point value.</para>
+ /// <para>This value equals to 9007199254740991.</para>
+ /// </summary>
+ public const long MAX_VALUE = (1L << 53) - 1;
- /// <summary>
- /// <para>Gets the minimum safe value representable as an integer by a IEEE754 64-bit binary floating point value.</para>
- /// <para>This value equals to -9007199254740991.</para>
- /// </summary>
- public const long MIN_VALUE = -MAX_VALUE;
+ /// <summary>
+ /// <para>Gets the minimum safe value representable as an integer by a IEEE754 64-bit binary floating point value.</para>
+ /// <para>This value equals to -9007199254740991.</para>
+ /// </summary>
+ public const long MIN_VALUE = -MAX_VALUE;
+ }
}
diff --git a/DisCatSharp.Common/Attributes/SerializationAttribute.cs b/DisCatSharp.Common/Attributes/SerializationAttribute.cs
index 86e1f7c6f..950605130 100644
--- a/DisCatSharp.Common/Attributes/SerializationAttribute.cs
+++ b/DisCatSharp.Common/Attributes/SerializationAttribute.cs
@@ -1,31 +1,32 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Serialization;
-
-/// <summary>
-/// ABC for serialization attributes.
-/// </summary>
-public abstract class SerializationAttribute : Attribute
-{ }
+namespace DisCatSharp.Common.Serialization
+{
+ /// <summary>
+ /// ABC for serialization attributes.
+ /// </summary>
+ public abstract class SerializationAttribute : Attribute
+ { }
+}
diff --git a/DisCatSharp.Common/Attributes/SerializedNameAttribute.cs b/DisCatSharp.Common/Attributes/SerializedNameAttribute.cs
index 4411d8be2..914d706c3 100644
--- a/DisCatSharp.Common/Attributes/SerializedNameAttribute.cs
+++ b/DisCatSharp.Common/Attributes/SerializedNameAttribute.cs
@@ -1,47 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Serialization;
-
-/// <summary>
-/// Declares name of a property in serialized data. This is used for mapping serialized data to object properties and fields.
-/// </summary>
-[Obsolete("Use [DataMember] with set Name instead.")]
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class SerializedNameAttribute : SerializationAttribute
+namespace DisCatSharp.Common.Serialization
{
- /// <summary>
- /// Gets the serialized name of the field or property.
- /// </summary>
- public string Name { get; }
-
/// <summary>
/// Declares name of a property in serialized data. This is used for mapping serialized data to object properties and fields.
/// </summary>
- /// <param name="name">Name of the field or property in serialized data.</param>
- public SerializedNameAttribute(string name)
+ [Obsolete("Use [DataMember] with set Name instead.")]
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class SerializedNameAttribute : SerializationAttribute
{
- this.Name = name;
+ /// <summary>
+ /// Gets the serialized name of the field or property.
+ /// </summary>
+ public string Name { get; }
+
+ /// <summary>
+ /// Declares name of a property in serialized data. This is used for mapping serialized data to object properties and fields.
+ /// </summary>
+ /// <param name="name">Name of the field or property in serialized data.</param>
+ public SerializedNameAttribute(string name)
+ {
+ this.Name = name;
+ }
}
}
diff --git a/DisCatSharp.Common/Attributes/TimeSpanAttributes.cs b/DisCatSharp.Common/Attributes/TimeSpanAttributes.cs
index af1e257ed..b88643fb5 100644
--- a/DisCatSharp.Common/Attributes/TimeSpanAttributes.cs
+++ b/DisCatSharp.Common/Attributes/TimeSpanAttributes.cs
@@ -1,41 +1,42 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Serialization;
+namespace DisCatSharp.Common.Serialization
+{
+ /// <summary>
+ /// <para>Specifies that this <see cref="System.TimeSpan"/> will be serialized as a number of whole seconds.</para>
+ /// <para>This value will always be serialized as a number.</para>
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class TimeSpanSecondsAttribute : SerializationAttribute
+ { }
-/// <summary>
-/// <para>Specifies that this <see cref="System.TimeSpan"/> will be serialized as a number of whole seconds.</para>
-/// <para>This value will always be serialized as a number.</para>
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class TimeSpanSecondsAttribute : SerializationAttribute
-{ }
-
-/// <summary>
-/// <para>Specifies that this <see cref="System.TimeSpan"/> will be serialized as a number of whole milliseconds.</para>
-/// <para>This value will always be serialized as a number.</para>
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class TimeSpanMillisecondsAttribute : SerializationAttribute
-{ }
+ /// <summary>
+ /// <para>Specifies that this <see cref="System.TimeSpan"/> will be serialized as a number of whole milliseconds.</para>
+ /// <para>This value will always be serialized as a number.</para>
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class TimeSpanMillisecondsAttribute : SerializationAttribute
+ { }
+}
diff --git a/DisCatSharp.Common/Attributes/TimeSpanFormatAttribute.cs b/DisCatSharp.Common/Attributes/TimeSpanFormatAttribute.cs
index af570ce41..8cfd15cf4 100644
--- a/DisCatSharp.Common/Attributes/TimeSpanFormatAttribute.cs
+++ b/DisCatSharp.Common/Attributes/TimeSpanFormatAttribute.cs
@@ -1,132 +1,133 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
// ReSharper disable InconsistentNaming
-namespace DisCatSharp.Common.Serialization;
-
-/// <summary>
-/// Defines the format for string-serialized <see cref="System.TimeSpan"/> objects.
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class TimeSpanFormatAttribute : SerializationAttribute
+namespace DisCatSharp.Common.Serialization
{
/// <summary>
- /// Gets the ISO 8601 format string of @"ddThh\:mm\:ss\.fff".
- /// </summary>
- public const string FORMAT_ISO_8601 = @"ddThh\:mm\:ss\.fff";
-
- /// <summary>
- /// Gets the constant format.
- /// </summary>
- public const string FORMAT_CONSTANT = "c";
-
- /// <summary>
- /// Gets the general long format.
- /// </summary>
- public const string FORMAT_LONG = "G";
-
- /// <summary>
- /// Gets the general short format.
- /// </summary>
- public const string FORMAT_SHORT = "g";
-
- /// <summary>
- /// Gets the custom datetime format string to use.
- /// </summary>
- public string Format { get; }
-
- /// <summary>
- /// Gets the predefined datetime format kind.
+ /// Defines the format for string-serialized <see cref="System.TimeSpan"/> objects.
/// </summary>
- public TimeSpanFormatKind Kind { get; }
-
- /// <summary>
- /// Specifies a predefined format to use.
- /// </summary>
- /// <param name="kind">Predefined format kind to use.</param>
- public TimeSpanFormatAttribute(TimeSpanFormatKind kind)
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class TimeSpanFormatAttribute : SerializationAttribute
{
- if (kind < 0 || kind > TimeSpanFormatKind.InvariantLocaleShort)
- throw new ArgumentOutOfRangeException(nameof(kind), "Specified format kind is not legal or supported.");
-
- this.Kind = kind;
- this.Format = null;
+ /// <summary>
+ /// Gets the ISO 8601 format string of @"ddThh\:mm\:ss\.fff".
+ /// </summary>
+ public const string FORMAT_ISO_8601 = @"ddThh\:mm\:ss\.fff";
+
+ /// <summary>
+ /// Gets the constant format.
+ /// </summary>
+ public const string FORMAT_CONSTANT = "c";
+
+ /// <summary>
+ /// Gets the general long format.
+ /// </summary>
+ public const string FORMAT_LONG = "G";
+
+ /// <summary>
+ /// Gets the general short format.
+ /// </summary>
+ public const string FORMAT_SHORT = "g";
+
+ /// <summary>
+ /// Gets the custom datetime format string to use.
+ /// </summary>
+ public string Format { get; }
+
+ /// <summary>
+ /// Gets the predefined datetime format kind.
+ /// </summary>
+ public TimeSpanFormatKind Kind { get; }
+
+ /// <summary>
+ /// Specifies a predefined format to use.
+ /// </summary>
+ /// <param name="kind">Predefined format kind to use.</param>
+ public TimeSpanFormatAttribute(TimeSpanFormatKind kind)
+ {
+ if (kind < 0 || kind > TimeSpanFormatKind.InvariantLocaleShort)
+ throw new ArgumentOutOfRangeException(nameof(kind), "Specified format kind is not legal or supported.");
+
+ this.Kind = kind;
+ this.Format = null;
+ }
+
+ /// <summary>
+ /// <para>Specifies a custom format to use.</para>
+ /// <para>See https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-timespan-format-strings for more details.</para>
+ /// </summary>
+ /// <param name="format">Custom format string to use.</param>
+ public TimeSpanFormatAttribute(string format)
+ {
+ if (string.IsNullOrWhiteSpace(format))
+ throw new ArgumentNullException(nameof(format), "Specified format cannot be null or empty.");
+
+ this.Kind = TimeSpanFormatKind.Custom;
+ this.Format = format;
+ }
}
/// <summary>
- /// <para>Specifies a custom format to use.</para>
- /// <para>See https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-timespan-format-strings for more details.</para>
+ /// <para>Defines which built-in format to use for <see cref="System.TimeSpan"/> serialization.</para>
+ /// <para>See https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings and https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-timespan-format-strings for more details.</para>
/// </summary>
- /// <param name="format">Custom format string to use.</param>
- public TimeSpanFormatAttribute(string format)
+ public enum TimeSpanFormatKind : int
{
- if (string.IsNullOrWhiteSpace(format))
- throw new ArgumentNullException(nameof(format), "Specified format cannot be null or empty.");
-
- this.Kind = TimeSpanFormatKind.Custom;
- this.Format = format;
+ /// <summary>
+ /// Specifies ISO 8601-like time format, which is equivalent to .NET format string of @"ddThh\:mm\:ss\.fff".
+ /// </summary>
+ ISO8601 = 0,
+
+ /// <summary>
+ /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "c".
+ /// </summary>
+ InvariantConstant = 1,
+
+ /// <summary>
+ /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.CurrentCulture"/>, with a format string of "G". This format is not recommended for portability reasons.
+ /// </summary>
+ CurrentLocaleLong = 2,
+
+ /// <summary>
+ /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.CurrentCulture"/>, with a format string of "g". This format is not recommended for portability reasons.
+ /// </summary>
+ CurrentLocaleShort = 3,
+
+ /// <summary>
+ /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "G". This format is not recommended for portability reasons.
+ /// </summary>
+ InvariantLocaleLong = 4,
+
+ /// <summary>
+ /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "g". This format is not recommended for portability reasons.
+ /// </summary>
+ InvariantLocaleShort = 5,
+
+ /// <summary>
+ /// Specifies a custom format. This value is not usable directly.
+ /// </summary>
+ Custom = int.MaxValue
}
}
-
-/// <summary>
-/// <para>Defines which built-in format to use for <see cref="System.TimeSpan"/> serialization.</para>
-/// <para>See https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings and https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-timespan-format-strings for more details.</para>
-/// </summary>
-public enum TimeSpanFormatKind : int
-{
- /// <summary>
- /// Specifies ISO 8601-like time format, which is equivalent to .NET format string of @"ddThh\:mm\:ss\.fff".
- /// </summary>
- ISO8601 = 0,
-
- /// <summary>
- /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "c".
- /// </summary>
- InvariantConstant = 1,
-
- /// <summary>
- /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.CurrentCulture"/>, with a format string of "G". This format is not recommended for portability reasons.
- /// </summary>
- CurrentLocaleLong = 2,
-
- /// <summary>
- /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.CurrentCulture"/>, with a format string of "g". This format is not recommended for portability reasons.
- /// </summary>
- CurrentLocaleShort = 3,
-
- /// <summary>
- /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "G". This format is not recommended for portability reasons.
- /// </summary>
- InvariantLocaleLong = 4,
-
- /// <summary>
- /// Specifies a format defined by <see cref="System.Globalization.CultureInfo.InvariantCulture"/>, with a format string of "g". This format is not recommended for portability reasons.
- /// </summary>
- InvariantLocaleShort = 5,
-
- /// <summary>
- /// Specifies a custom format. This value is not usable directly.
- /// </summary>
- Custom = int.MaxValue
-}
diff --git a/DisCatSharp.Common/Attributes/UnixTimestampAttributes.cs b/DisCatSharp.Common/Attributes/UnixTimestampAttributes.cs
index cf3b9aed8..2bf2ba457 100644
--- a/DisCatSharp.Common/Attributes/UnixTimestampAttributes.cs
+++ b/DisCatSharp.Common/Attributes/UnixTimestampAttributes.cs
@@ -1,41 +1,42 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Serialization;
+namespace DisCatSharp.Common.Serialization
+{
+ /// <summary>
+ /// <para>Specifies that this <see cref="System.DateTime"/> or <see cref="System.DateTimeOffset"/> will be serialized as Unix timestamp seconds.</para>
+ /// <para>This value will always be serialized as a number.</para>
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class UnixSecondsAttribute : SerializationAttribute
+ { }
-/// <summary>
-/// <para>Specifies that this <see cref="System.DateTime"/> or <see cref="System.DateTimeOffset"/> will be serialized as Unix timestamp seconds.</para>
-/// <para>This value will always be serialized as a number.</para>
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class UnixSecondsAttribute : SerializationAttribute
-{ }
-
-/// <summary>
-/// <para>Specifies that this <see cref="System.DateTime"/> or <see cref="System.DateTimeOffset"/> will be serialized as Unix timestamp milliseconds.</para>
-/// <para>This value will always be serialized as a number.</para>
-/// </summary>
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
-public sealed class UnixMillisecondsAttribute : SerializationAttribute
-{ }
+ /// <summary>
+ /// <para>Specifies that this <see cref="System.DateTime"/> or <see cref="System.DateTimeOffset"/> will be serialized as Unix timestamp milliseconds.</para>
+ /// <para>This value will always be serialized as a number.</para>
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
+ public sealed class UnixMillisecondsAttribute : SerializationAttribute
+ { }
+}
diff --git a/DisCatSharp.Common/RegularExpressions/CommonRegEx.cs b/DisCatSharp.Common/RegularExpressions/CommonRegEx.cs
index 4ae0c76c2..d436083d6 100644
--- a/DisCatSharp.Common/RegularExpressions/CommonRegEx.cs
+++ b/DisCatSharp.Common/RegularExpressions/CommonRegEx.cs
@@ -1,74 +1,75 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Text.RegularExpressions;
-namespace DisCatSharp.Common.RegularExpressions;
-
-/// <summary>
-/// Provides common regex.
-/// </summary>
-public static class CommonRegEx
+namespace DisCatSharp.Common.RegularExpressions
{
/// <summary>
- /// Represents a hex color string.
+ /// Provides common regex.
/// </summary>
- public static Regex HexColorString
- => new(@"^#?([a-fA-F0-9]{6})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ public static class CommonRegEx
+ {
+ /// <summary>
+ /// Represents a hex color string.
+ /// </summary>
+ public static Regex HexColorString
+ => new(@"^#?([a-fA-F0-9]{6})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
- /// <summary>
- /// Represents a rgb color string.
- /// </summary>
- public static Regex RgbColorString
- => new(@"^(\d{1,3})\s*?,\s*?(\d{1,3}),\s*?(\d{1,3})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ /// <summary>
+ /// Represents a rgb color string.
+ /// </summary>
+ public static Regex RgbColorString
+ => new(@"^(\d{1,3})\s*?,\s*?(\d{1,3}),\s*?(\d{1,3})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
- /// <summary>
- /// Represents a timespan.
- /// </summary>
- public static Regex TimeSpan
- => new(@"^(?<days>\d+d\s*)?(?<hours>\d{1,2}h\s*)?(?<minutes>\d{1,2}m\s*)?(?<seconds>\d{1,2}s\s*)?$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ /// <summary>
+ /// Represents a timespan.
+ /// </summary>
+ public static Regex TimeSpan
+ => new(@"^(?<days>\d+d\s*)?(?<hours>\d{1,2}h\s*)?(?<minutes>\d{1,2}m\s*)?(?<seconds>\d{1,2}s\s*)?$", RegexOptions.ECMAScript | RegexOptions.Compiled);
- /// <summary>
- /// Represents a advanced youtube regex.
- /// Named groups:
- /// <list type="table">
- /// <listheader>
- /// <term>group</term>
- /// <description>description</description>
- /// </listheader>
- /// <item>
- /// <term>id</term>
- /// <description>Video ID</description>
- /// </item>
- /// <item>
- /// <term>list</term>
- /// <description>List ID</description>
- /// </item>
- /// <item>
- /// <term>index</term>
- /// <description>List index</description>
- /// </item>
- /// </list>
- /// </summary>
- public static Regex AdvancedYoutubeRegex
- => new(@"http(s)?:\/\/(www\.)?youtu(\.be|be\.com)\/(watch\?v=|playlist)?(?<id>\w{1,})?((\?|\&)list=(?<list>\w{1,}))(&index=(?<index>\d{1,}))?", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ /// <summary>
+ /// Represents a advanced youtube regex.
+ /// Named groups:
+ /// <list type="table">
+ /// <listheader>
+ /// <term>group</term>
+ /// <description>description</description>
+ /// </listheader>
+ /// <item>
+ /// <term>id</term>
+ /// <description>Video ID</description>
+ /// </item>
+ /// <item>
+ /// <term>list</term>
+ /// <description>List ID</description>
+ /// </item>
+ /// <item>
+ /// <term>index</term>
+ /// <description>List index</description>
+ /// </item>
+ /// </list>
+ /// </summary>
+ public static Regex AdvancedYoutubeRegex
+ => new(@"http(s)?:\/\/(www\.)?youtu(\.be|be\.com)\/(watch\?v=|playlist)?(?<id>\w{1,})?((\?|\&)list=(?<list>\w{1,}))(&index=(?<index>\d{1,}))?", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ }
}
diff --git a/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs b/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs
index ca38fbad0..4aa3d8ddf 100644
--- a/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs
+++ b/DisCatSharp.Common/RegularExpressions/DiscordRegEx.cs
@@ -1,124 +1,125 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Text.RegularExpressions;
-namespace DisCatSharp.Common.RegularExpressions;
-
-/// <summary>
-/// Provides common regex for discord related things.
-/// </summary>
-public static class DiscordRegEx
+namespace DisCatSharp.Common.RegularExpressions
{
- // language=regex
- private const string WEBSITE = @"(https?:\/\/)?(www\.|canary\.|ptb\.)?(discord|discordapp)\.com\/";
-
- /// <summary>
- /// Represents a invite.
- /// </summary>
- public static readonly Regex Invite
- = new($@"^((https?:\/\/)?(www\.)?discord\.gg(\/.*)*|{WEBSITE}invite)\/(?<code>[a-zA-Z0-9]*)(\?event=(?<event>\d+))?$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a message link.
- /// </summary>
- public static readonly Regex MessageLink
- = new($@"^{WEBSITE}channels\/(?<guild>(?:\d+|@me))\/(?<channel>\d+)\/(?<message>\d+)\/?", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a emoji.
- /// </summary>
- public static readonly Regex Emoji
- = new(@"^<(?<animated>a)?:(?<name>[a-zA-Z0-9_]+?):(?<id>\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a animated emoji.
- /// </summary>
- public static readonly Regex AnimatedEmoji
- = new(@"^<(?<animated>a):(?<name>\w{2,32}):(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a non-animated emoji.
- /// </summary>
- public static readonly Regex StaticEmoji
- = new(@"^<:(?<name>\w{2,32}):(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a timestamp.
- /// </summary>
- public static readonly Regex Timestamp
- = new(@"^<t:(?<timestamp>-?\d{1,13})(:(?<style>[tTdDfFR]))?>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a default styled timestamp.
- /// </summary>
- public static readonly Regex DefaultStyledTimestamp
- = new(@"^<t:(?<timestamp>-?\d{1,13})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a styled timestamp.
- /// </summary>
- public static readonly Regex StyledTimestamp
- = new(@"^<t:(?<timestamp>-?\d{1,13}):(?<style>[tTdDfFR])>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a role.
- /// </summary>
- public static readonly Regex Role
- = new(@"^<@&(\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a channel.
- /// </summary>
- public static readonly Regex Channel
- = new(@"^<#(\d+)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a user.
- /// </summary>
- public static readonly Regex User
- = new(@"^<@\!?(\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a user with nickname.
- /// </summary>
- public static readonly Regex UserWithNickname
- = new(@"^<@\!(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a user with optional nickname.
- /// </summary>
- public static readonly Regex UserWithOptionalNickname
- = new(@"^<@\!?(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
- /// <summary>
- /// Represents a user without nickname.
- /// </summary>
- public static readonly Regex UserWithoutNickname
- = new(@"^<@(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
-
/// <summary>
- /// Represents a event.
+ /// Provides common regex for discord related things.
/// </summary>
- public static readonly Regex Event
- = new($@"^{WEBSITE}events\/(?<guild>\d+)\/(?<event>\d+)$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ public static class DiscordRegEx
+ {
+ // language=regex
+ private const string WEBSITE = @"(https?:\/\/)?(www\.|canary\.|ptb\.)?(discord|discordapp)\.com\/";
+
+ /// <summary>
+ /// Represents a invite.
+ /// </summary>
+ public static readonly Regex Invite
+ = new($@"^((https?:\/\/)?(www\.)?discord\.gg(\/.*)*|{WEBSITE}invite)\/(?<code>[a-zA-Z0-9]*)(\?event=(?<event>\d+))?$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a message link.
+ /// </summary>
+ public static readonly Regex MessageLink
+ = new($@"^{WEBSITE}channels\/(?<guild>(?:\d+|@me))\/(?<channel>\d+)\/(?<message>\d+)\/?", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a emoji.
+ /// </summary>
+ public static readonly Regex Emoji
+ = new(@"^<(?<animated>a)?:(?<name>[a-zA-Z0-9_]+?):(?<id>\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a animated emoji.
+ /// </summary>
+ public static readonly Regex AnimatedEmoji
+ = new(@"^<(?<animated>a):(?<name>\w{2,32}):(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a non-animated emoji.
+ /// </summary>
+ public static readonly Regex StaticEmoji
+ = new(@"^<:(?<name>\w{2,32}):(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a timestamp.
+ /// </summary>
+ public static readonly Regex Timestamp
+ = new(@"^<t:(?<timestamp>-?\d{1,13})(:(?<style>[tTdDfFR]))?>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a default styled timestamp.
+ /// </summary>
+ public static readonly Regex DefaultStyledTimestamp
+ = new(@"^<t:(?<timestamp>-?\d{1,13})$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a styled timestamp.
+ /// </summary>
+ public static readonly Regex StyledTimestamp
+ = new(@"^<t:(?<timestamp>-?\d{1,13}):(?<style>[tTdDfFR])>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a role.
+ /// </summary>
+ public static readonly Regex Role
+ = new(@"^<@&(\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a channel.
+ /// </summary>
+ public static readonly Regex Channel
+ = new(@"^<#(\d+)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a user.
+ /// </summary>
+ public static readonly Regex User
+ = new(@"^<@\!?(\d+?)>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a user with nickname.
+ /// </summary>
+ public static readonly Regex UserWithNickname
+ = new(@"^<@\!(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a user with optional nickname.
+ /// </summary>
+ public static readonly Regex UserWithOptionalNickname
+ = new(@"^<@\!?(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a user without nickname.
+ /// </summary>
+ public static readonly Regex UserWithoutNickname
+ = new(@"^<@(?<id>\d{17,20})>$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+
+ /// <summary>
+ /// Represents a event.
+ /// </summary>
+ public static readonly Regex Event
+ = new($@"^{WEBSITE}events\/(?<guild>\d+)\/(?<event>\d+)$", RegexOptions.ECMAScript | RegexOptions.Compiled);
+ }
}
diff --git a/DisCatSharp.Common/Types/CharSpanLookupDictionary.cs b/DisCatSharp.Common/Types/CharSpanLookupDictionary.cs
index 3ace497bc..11806b9d0 100644
--- a/DisCatSharp.Common/Types/CharSpanLookupDictionary.cs
+++ b/DisCatSharp.Common/Types/CharSpanLookupDictionary.cs
@@ -1,813 +1,814 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using System.Collections.Generic;
using System.Collections.Immutable;
-namespace DisCatSharp.Common;
-
-/// <summary>
-/// Represents collection of string keys and <typeparamref name="TValue"/> values, allowing the use of <see cref="System.ReadOnlySpan{T}"/> for dictionary operations.
-/// </summary>
-/// <typeparam name="TValue">Type of items in this dictionary.</typeparam>
-public sealed class CharSpanLookupDictionary<TValue> :
- IDictionary<string, TValue>,
- IReadOnlyDictionary<string, TValue>,
- IDictionary
+namespace DisCatSharp.Common
{
/// <summary>
- /// Gets the collection of all keys present in this dictionary.
+ /// Represents collection of string keys and <typeparamref name="TValue"/> values, allowing the use of <see cref="System.ReadOnlySpan{T}"/> for dictionary operations.
/// </summary>
- public IEnumerable<string> Keys => this.GetKeysInternal();
- /// <summary>
- /// Gets the keys.
- /// </summary>
- ICollection<string> IDictionary<string, TValue>.Keys => this.GetKeysInternal();
- /// <summary>
- /// Gets the keys.
- /// </summary>
- ICollection IDictionary.Keys => this.GetKeysInternal();
+ /// <typeparam name="TValue">Type of items in this dictionary.</typeparam>
+ public sealed class CharSpanLookupDictionary<TValue> :
+ IDictionary<string, TValue>,
+ IReadOnlyDictionary<string, TValue>,
+ IDictionary
+ {
+ /// <summary>
+ /// Gets the collection of all keys present in this dictionary.
+ /// </summary>
+ public IEnumerable<string> Keys => this.GetKeysInternal();
+ /// <summary>
+ /// Gets the keys.
+ /// </summary>
+ ICollection<string> IDictionary<string, TValue>.Keys => this.GetKeysInternal();
+ /// <summary>
+ /// Gets the keys.
+ /// </summary>
+ ICollection IDictionary.Keys => this.GetKeysInternal();
- /// <summary>
- /// Gets the collection of all values present in this dictionary.
- /// </summary>
- public IEnumerable<TValue> Values => this.GetValuesInternal();
- /// <summary>
- /// Gets the values.
- /// </summary>
- ICollection<TValue> IDictionary<string, TValue>.Values => this.GetValuesInternal();
- /// <summary>
- /// Gets the values.
- /// </summary>
- ICollection IDictionary.Values => this.GetValuesInternal();
+ /// <summary>
+ /// Gets the collection of all values present in this dictionary.
+ /// </summary>
+ public IEnumerable<TValue> Values => this.GetValuesInternal();
+ /// <summary>
+ /// Gets the values.
+ /// </summary>
+ ICollection<TValue> IDictionary<string, TValue>.Values => this.GetValuesInternal();
+ /// <summary>
+ /// Gets the values.
+ /// </summary>
+ ICollection IDictionary.Values => this.GetValuesInternal();
- /// <summary>
- /// Gets the total number of items in this dictionary.
- /// </summary>
- public int Count { get; private set; }
+ /// <summary>
+ /// Gets the total number of items in this dictionary.
+ /// </summary>
+ public int Count { get; private set; }
- /// <summary>
- /// Gets whether this dictionary is read-only.
- /// </summary>
- public bool IsReadOnly => false;
+ /// <summary>
+ /// Gets whether this dictionary is read-only.
+ /// </summary>
+ public bool IsReadOnly => false;
- /// <summary>
- /// Gets whether this dictionary has a fixed size.
- /// </summary>
- public bool IsFixedSize => false;
+ /// <summary>
+ /// Gets whether this dictionary has a fixed size.
+ /// </summary>
+ public bool IsFixedSize => false;
- /// <summary>
- /// Gets whether this dictionary is considered thread-safe.
- /// </summary>
- public bool IsSynchronized => false;
+ /// <summary>
+ /// Gets whether this dictionary is considered thread-safe.
+ /// </summary>
+ public bool IsSynchronized => false;
- /// <summary>
- /// Gets the object which allows synchronizing access to this dictionary.
- /// </summary>
- public object SyncRoot { get; } = new();
+ /// <summary>
+ /// Gets the object which allows synchronizing access to this dictionary.
+ /// </summary>
+ public object SyncRoot { get; } = new();
- /// <summary>
- /// Gets or sets a value corresponding to given key in this dictionary.
- /// </summary>
- /// <param name="key">Key to get or set the value for.</param>
- /// <returns>Value matching the supplied key, if applicable.</returns>
- public TValue this[string key]
- {
- get
+ /// <summary>
+ /// Gets or sets a value corresponding to given key in this dictionary.
+ /// </summary>
+ /// <param name="key">Key to get or set the value for.</param>
+ /// <returns>Value matching the supplied key, if applicable.</returns>
+ public TValue this[string key]
{
- if (key == null)
- throw new ArgumentNullException(nameof(key));
+ get
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
- if (!this.TryRetrieveInternal(key.AsSpan(), out var value))
- throw new KeyNotFoundException($"The given key '{key}' was not present in the dictionary.");
+ if (!this.TryRetrieveInternal(key.AsSpan(), out var value))
+ throw new KeyNotFoundException($"The given key '{key}' was not present in the dictionary.");
- return value;
- }
+ return value;
+ }
- set
- {
- if (key == null)
- throw new ArgumentNullException(nameof(key));
+ set
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
- this.TryInsertInternal(key, value, true);
+ this.TryInsertInternal(key, value, true);
+ }
}
- }
- /// <summary>
- /// Gets or sets a value corresponding to given key in this dictionary.
- /// </summary>
- /// <param name="key">Key to get or set the value for.</param>
- /// <returns>Value matching the supplied key, if applicable.</returns>
- public TValue this[ReadOnlySpan<char> key]
- {
- get
+ /// <summary>
+ /// Gets or sets a value corresponding to given key in this dictionary.
+ /// </summary>
+ /// <param name="key">Key to get or set the value for.</param>
+ /// <returns>Value matching the supplied key, if applicable.</returns>
+ public TValue this[ReadOnlySpan<char> key]
{
- if (!this.TryRetrieveInternal(key, out var value))
- throw new KeyNotFoundException($"The given key was not present in the dictionary.");
+ get
+ {
+ if (!this.TryRetrieveInternal(key, out var value))
+ throw new KeyNotFoundException($"The given key was not present in the dictionary.");
- return value;
- }
+ return value;
+ }
- set
- {
- unsafe
+ set
{
- fixed (char* chars = &key.GetPinnableReference())
- this.TryInsertInternal(new string(chars, 0, key.Length), value, true);
+ unsafe
+ {
+ fixed (char* chars = &key.GetPinnableReference())
+ this.TryInsertInternal(new string(chars, 0, key.Length), value, true);
+ }
}
}
- }
- object IDictionary.this[object key]
- {
- get
+ object IDictionary.this[object key]
{
- if (!(key is string tkey))
- throw new ArgumentException("Key needs to be an instance of a string.");
-
- if (!this.TryRetrieveInternal(tkey.AsSpan(), out var value))
- throw new KeyNotFoundException($"The given key '{tkey}' was not present in the dictionary.");
+ get
+ {
+ if (!(key is string tkey))
+ throw new ArgumentException("Key needs to be an instance of a string.");
- return value;
- }
+ if (!this.TryRetrieveInternal(tkey.AsSpan(), out var value))
+ throw new KeyNotFoundException($"The given key '{tkey}' was not present in the dictionary.");
- set
- {
- if (!(key is string tkey))
- throw new ArgumentException("Key needs to be an instance of a string.");
+ return value;
+ }
- if (!(value is TValue tvalue))
+ set
{
- tvalue = default;
- if (tvalue != null)
- throw new ArgumentException($"Value needs to be an instance of {typeof(TValue)}.");
- }
+ if (!(key is string tkey))
+ throw new ArgumentException("Key needs to be an instance of a string.");
- this.TryInsertInternal(tkey, tvalue, true);
+ if (!(value is TValue tvalue))
+ {
+ tvalue = default;
+ if (tvalue != null)
+ throw new ArgumentException($"Value needs to be an instance of {typeof(TValue)}.");
+ }
+
+ this.TryInsertInternal(tkey, tvalue, true);
+ }
}
- }
- /// <summary>
- /// Gets the internal buckets.
- /// </summary>
- private readonly Dictionary<ulong, KeyedValue> _internalBuckets;
+ /// <summary>
+ /// Gets the internal buckets.
+ /// </summary>
+ private readonly Dictionary<ulong, KeyedValue> _internalBuckets;
- /// <summary>
- /// Creates a new, empty <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/>.
- /// </summary>
- public CharSpanLookupDictionary()
- {
- this._internalBuckets = new Dictionary<ulong, KeyedValue>();
- }
+ /// <summary>
+ /// Creates a new, empty <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/>.
+ /// </summary>
+ public CharSpanLookupDictionary()
+ {
+ this._internalBuckets = new Dictionary<ulong, KeyedValue>();
+ }
- /// <summary>
- /// Creates a new, empty <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and sets its initial capacity to specified value.
- /// </summary>
- /// <param name="initialCapacity">Initial capacity of the dictionary.</param>
- public CharSpanLookupDictionary(int initialCapacity)
- {
- this._internalBuckets = new Dictionary<ulong, KeyedValue>(initialCapacity);
- }
+ /// <summary>
+ /// Creates a new, empty <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and sets its initial capacity to specified value.
+ /// </summary>
+ /// <param name="initialCapacity">Initial capacity of the dictionary.</param>
+ public CharSpanLookupDictionary(int initialCapacity)
+ {
+ this._internalBuckets = new Dictionary<ulong, KeyedValue>(initialCapacity);
+ }
- /// <summary>
- /// Creates a new <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied dictionary.
- /// </summary>
- /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
- public CharSpanLookupDictionary(IDictionary<string, TValue> values)
- : this(values.Count)
- {
- foreach (var (k, v) in values)
- this.Add(k, v);
- }
+ /// <summary>
+ /// Creates a new <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied dictionary.
+ /// </summary>
+ /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
+ public CharSpanLookupDictionary(IDictionary<string, TValue> values)
+ : this(values.Count)
+ {
+ foreach (var (k, v) in values)
+ this.Add(k, v);
+ }
- /// <summary>
- /// Creates a new <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied dictionary.
- /// </summary>
- /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
- public CharSpanLookupDictionary(IReadOnlyDictionary<string, TValue> values)
- : this(values.Count)
- {
- foreach (var (k, v) in values)
- this.Add(k, v);
- }
+ /// <summary>
+ /// Creates a new <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied dictionary.
+ /// </summary>
+ /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
+ public CharSpanLookupDictionary(IReadOnlyDictionary<string, TValue> values)
+ : this(values.Count)
+ {
+ foreach (var (k, v) in values)
+ this.Add(k, v);
+ }
- /// <summary>
- /// Creates a new <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied key-value collection.
- /// </summary>
- /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
- public CharSpanLookupDictionary(IEnumerable<KeyValuePair<string, TValue>> values)
- : this()
- {
- foreach (var (k, v) in values)
- this.Add(k, v);
- }
+ /// <summary>
+ /// Creates a new <see cref="CharSpanLookupDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied key-value collection.
+ /// </summary>
+ /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
+ public CharSpanLookupDictionary(IEnumerable<KeyValuePair<string, TValue>> values)
+ : this()
+ {
+ foreach (var (k, v) in values)
+ this.Add(k, v);
+ }
- /// <summary>
- /// Inserts a specific key and corresponding value into this dictionary.
- /// </summary>
- /// <param name="key">Key to insert.</param>
- /// <param name="value">Value corresponding to this key.</param>
- public void Add(string key, TValue value)
- {
- if (!this.TryInsertInternal(key, value, false))
- throw new ArgumentException("Given key is already present in the dictionary.", nameof(key));
- }
+ /// <summary>
+ /// Inserts a specific key and corresponding value into this dictionary.
+ /// </summary>
+ /// <param name="key">Key to insert.</param>
+ /// <param name="value">Value corresponding to this key.</param>
+ public void Add(string key, TValue value)
+ {
+ if (!this.TryInsertInternal(key, value, false))
+ throw new ArgumentException("Given key is already present in the dictionary.", nameof(key));
+ }
- /// <summary>
- /// Inserts a specific key and corresponding value into this dictionary.
- /// </summary>
- /// <param name="key">Key to insert.</param>
- /// <param name="value">Value corresponding to this key.</param>
- public void Add(ReadOnlySpan<char> key, TValue value)
- {
- unsafe
+ /// <summary>
+ /// Inserts a specific key and corresponding value into this dictionary.
+ /// </summary>
+ /// <param name="key">Key to insert.</param>
+ /// <param name="value">Value corresponding to this key.</param>
+ public void Add(ReadOnlySpan<char> key, TValue value)
{
- fixed (char* chars = &key.GetPinnableReference())
- if (!this.TryInsertInternal(new string(chars, 0, key.Length), value, false))
- throw new ArgumentException("Given key is already present in the dictionary.", nameof(key));
+ unsafe
+ {
+ fixed (char* chars = &key.GetPinnableReference())
+ if (!this.TryInsertInternal(new string(chars, 0, key.Length), value, false))
+ throw new ArgumentException("Given key is already present in the dictionary.", nameof(key));
+ }
}
- }
- /// <summary>
- /// Attempts to insert a specific key and corresponding value into this dictionary.
- /// </summary>
- /// <param name="key">Key to insert.</param>
- /// <param name="value">Value corresponding to this key.</param>
- /// <returns>Whether the operation was successful.</returns>
- public bool TryAdd(string key, TValue value)
- => this.TryInsertInternal(key, value, false);
+ /// <summary>
+ /// Attempts to insert a specific key and corresponding value into this dictionary.
+ /// </summary>
+ /// <param name="key">Key to insert.</param>
+ /// <param name="value">Value corresponding to this key.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public bool TryAdd(string key, TValue value)
+ => this.TryInsertInternal(key, value, false);
- /// <summary>
- /// Attempts to insert a specific key and corresponding value into this dictionary.
- /// </summary>
- /// <param name="key">Key to insert.</param>
- /// <param name="value">Value corresponding to this key.</param>
- /// <returns>Whether the operation was successful.</returns>
- public bool TryAdd(ReadOnlySpan<char> key, TValue value)
- {
- unsafe
+ /// <summary>
+ /// Attempts to insert a specific key and corresponding value into this dictionary.
+ /// </summary>
+ /// <param name="key">Key to insert.</param>
+ /// <param name="value">Value corresponding to this key.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public bool TryAdd(ReadOnlySpan<char> key, TValue value)
{
- fixed (char* chars = &key.GetPinnableReference())
- return this.TryInsertInternal(new string(chars, 0, key.Length), value, false);
+ unsafe
+ {
+ fixed (char* chars = &key.GetPinnableReference())
+ return this.TryInsertInternal(new string(chars, 0, key.Length), value, false);
+ }
}
- }
- /// <summary>
- /// Attempts to retrieve a value corresponding to the supplied key from this dictionary.
- /// </summary>
- /// <param name="key">Key to retrieve the value for.</param>
- /// <param name="value">Retrieved value.</param>
- /// <returns>Whether the operation was successful.</returns>
- public bool TryGetValue(string key, out TValue value)
- {
- if (key == null)
- throw new ArgumentNullException(nameof(key));
-
- return this.TryRetrieveInternal(key.AsSpan(), out value);
- }
+ /// <summary>
+ /// Attempts to retrieve a value corresponding to the supplied key from this dictionary.
+ /// </summary>
+ /// <param name="key">Key to retrieve the value for.</param>
+ /// <param name="value">Retrieved value.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public bool TryGetValue(string key, out TValue value)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
- /// <summary>
- /// Attempts to retrieve a value corresponding to the supplied key from this dictionary.
- /// </summary>
- /// <param name="key">Key to retrieve the value for.</param>
- /// <param name="value">Retrieved value.</param>
- /// <returns>Whether the operation was successful.</returns>
- public bool TryGetValue(ReadOnlySpan<char> key, out TValue value)
- => this.TryRetrieveInternal(key, out value);
+ return this.TryRetrieveInternal(key.AsSpan(), out value);
+ }
- /// <summary>
- /// Attempts to remove a value corresponding to the supplied key from this dictionary.
- /// </summary>
- /// <param name="key">Key to remove the value for.</param>
- /// <param name="value">Removed value.</param>
- /// <returns>Whether the operation was successful.</returns>
- public bool TryRemove(string key, out TValue value)
- {
- if (key == null)
- throw new ArgumentNullException(nameof(key));
+ /// <summary>
+ /// Attempts to retrieve a value corresponding to the supplied key from this dictionary.
+ /// </summary>
+ /// <param name="key">Key to retrieve the value for.</param>
+ /// <param name="value">Retrieved value.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public bool TryGetValue(ReadOnlySpan<char> key, out TValue value)
+ => this.TryRetrieveInternal(key, out value);
- return this.TryRemoveInternal(key.AsSpan(), out value);
- }
+ /// <summary>
+ /// Attempts to remove a value corresponding to the supplied key from this dictionary.
+ /// </summary>
+ /// <param name="key">Key to remove the value for.</param>
+ /// <param name="value">Removed value.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public bool TryRemove(string key, out TValue value)
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
- /// <summary>
- /// Attempts to remove a value corresponding to the supplied key from this dictionary.
- /// </summary>
- /// <param name="key">Key to remove the value for.</param>
- /// <param name="value">Removed value.</param>
- /// <returns>Whether the operation was successful.</returns>
- public bool TryRemove(ReadOnlySpan<char> key, out TValue value)
- => this.TryRemoveInternal(key, out value);
+ return this.TryRemoveInternal(key.AsSpan(), out value);
+ }
- /// <summary>
- /// Checks whether this dictionary contains the specified key.
- /// </summary>
- /// <param name="key">Key to check for in this dictionary.</param>
- /// <returns>Whether the key was present in the dictionary.</returns>
- public bool ContainsKey(string key)
- => this.ContainsKeyInternal(key.AsSpan());
+ /// <summary>
+ /// Attempts to remove a value corresponding to the supplied key from this dictionary.
+ /// </summary>
+ /// <param name="key">Key to remove the value for.</param>
+ /// <param name="value">Removed value.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public bool TryRemove(ReadOnlySpan<char> key, out TValue value)
+ => this.TryRemoveInternal(key, out value);
- /// <summary>
- /// Checks whether this dictionary contains the specified key.
- /// </summary>
- /// <param name="key">Key to check for in this dictionary.</param>
- /// <returns>Whether the key was present in the dictionary.</returns>
- public bool ContainsKey(ReadOnlySpan<char> key)
- => this.ContainsKeyInternal(key);
+ /// <summary>
+ /// Checks whether this dictionary contains the specified key.
+ /// </summary>
+ /// <param name="key">Key to check for in this dictionary.</param>
+ /// <returns>Whether the key was present in the dictionary.</returns>
+ public bool ContainsKey(string key)
+ => this.ContainsKeyInternal(key.AsSpan());
- /// <summary>
- /// Removes all items from this dictionary.
- /// </summary>
- public void Clear()
- {
- this._internalBuckets.Clear();
- this.Count = 0;
- }
+ /// <summary>
+ /// Checks whether this dictionary contains the specified key.
+ /// </summary>
+ /// <param name="key">Key to check for in this dictionary.</param>
+ /// <returns>Whether the key was present in the dictionary.</returns>
+ public bool ContainsKey(ReadOnlySpan<char> key)
+ => this.ContainsKeyInternal(key);
- /// <summary>
- /// Gets an enumerator over key-value pairs in this dictionary.
- /// </summary>
- /// <returns></returns>
- public IEnumerator<KeyValuePair<string, TValue>> GetEnumerator()
- => new Enumerator(this);
+ /// <summary>
+ /// Removes all items from this dictionary.
+ /// </summary>
+ public void Clear()
+ {
+ this._internalBuckets.Clear();
+ this.Count = 0;
+ }
- /// <summary>
- /// Removes the.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <returns>A bool.</returns>
- bool IDictionary<string, TValue>.Remove(string key)
- => this.TryRemove(key.AsSpan(), out _);
+ /// <summary>
+ /// Gets an enumerator over key-value pairs in this dictionary.
+ /// </summary>
+ /// <returns></returns>
+ public IEnumerator<KeyValuePair<string, TValue>> GetEnumerator()
+ => new Enumerator(this);
- /// <summary>
- /// Adds the.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <param name="value">The value.</param>
- void IDictionary.Add(object key, object value)
- {
- if (!(key is string tkey))
- throw new ArgumentException("Key needs to be an instance of a string.");
+ /// <summary>
+ /// Removes the.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <returns>A bool.</returns>
+ bool IDictionary<string, TValue>.Remove(string key)
+ => this.TryRemove(key.AsSpan(), out _);
- if (!(value is TValue tvalue))
+ /// <summary>
+ /// Adds the.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="value">The value.</param>
+ void IDictionary.Add(object key, object value)
{
- tvalue = default;
- if (tvalue != null)
- throw new ArgumentException($"Value needs to be an instance of {typeof(TValue)}.");
- }
+ if (!(key is string tkey))
+ throw new ArgumentException("Key needs to be an instance of a string.");
- this.Add(tkey, tvalue);
- }
+ if (!(value is TValue tvalue))
+ {
+ tvalue = default;
+ if (tvalue != null)
+ throw new ArgumentException($"Value needs to be an instance of {typeof(TValue)}.");
+ }
- /// <summary>
- /// Removes the.
- /// </summary>
- /// <param name="key">The key.</param>
- void IDictionary.Remove(object key)
- {
- if (!(key is string tkey))
- throw new ArgumentException("Key needs to be an instance of a string.");
+ this.Add(tkey, tvalue);
+ }
- this.TryRemove(tkey, out _);
- }
+ /// <summary>
+ /// Removes the.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ void IDictionary.Remove(object key)
+ {
+ if (!(key is string tkey))
+ throw new ArgumentException("Key needs to be an instance of a string.");
- /// <summary>
- /// Contains the.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <returns>A bool.</returns>
- bool IDictionary.Contains(object key)
- {
- if (!(key is string tkey))
- throw new ArgumentException("Key needs to be an instance of a string.");
+ this.TryRemove(tkey, out _);
+ }
- return this.ContainsKey(tkey);
- }
+ /// <summary>
+ /// Contains the.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <returns>A bool.</returns>
+ bool IDictionary.Contains(object key)
+ {
+ if (!(key is string tkey))
+ throw new ArgumentException("Key needs to be an instance of a string.");
- /// <summary>
- /// Gets the enumerator.
- /// </summary>
- /// <returns>An IDictionaryEnumerator.</returns>
- IDictionaryEnumerator IDictionary.GetEnumerator()
- => new Enumerator(this);
+ return this.ContainsKey(tkey);
+ }
- /// <summary>
- /// Adds the.
- /// </summary>
- /// <param name="item">The item.</param>
- void ICollection<KeyValuePair<string, TValue>>.Add(KeyValuePair<string, TValue> item)
- => this.Add(item.Key, item.Value);
+ /// <summary>
+ /// Gets the enumerator.
+ /// </summary>
+ /// <returns>An IDictionaryEnumerator.</returns>
+ IDictionaryEnumerator IDictionary.GetEnumerator()
+ => new Enumerator(this);
- /// <summary>
- /// Removes the.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>A bool.</returns>
- bool ICollection<KeyValuePair<string, TValue>>.Remove(KeyValuePair<string, TValue> item)
- => this.TryRemove(item.Key, out _);
+ /// <summary>
+ /// Adds the.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ void ICollection<KeyValuePair<string, TValue>>.Add(KeyValuePair<string, TValue> item)
+ => this.Add(item.Key, item.Value);
- /// <summary>
- /// Contains the.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <returns>A bool.</returns>
- bool ICollection<KeyValuePair<string, TValue>>.Contains(KeyValuePair<string, TValue> item)
- => this.TryGetValue(item.Key, out var value) && EqualityComparer<TValue>.Default.Equals(value, item.Value);
+ /// <summary>
+ /// Removes the.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns>A bool.</returns>
+ bool ICollection<KeyValuePair<string, TValue>>.Remove(KeyValuePair<string, TValue> item)
+ => this.TryRemove(item.Key, out _);
- /// <summary>
- /// Copies the to.
- /// </summary>
- /// <param name="array">The array.</param>
- /// <param name="arrayIndex">The array index.</param>
- void ICollection<KeyValuePair<string, TValue>>.CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
- {
- if (array.Length - arrayIndex < this.Count)
- throw new ArgumentException("Target array is too small.", nameof(array));
+ /// <summary>
+ /// Contains the.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns>A bool.</returns>
+ bool ICollection<KeyValuePair<string, TValue>>.Contains(KeyValuePair<string, TValue> item)
+ => this.TryGetValue(item.Key, out var value) && EqualityComparer<TValue>.Default.Equals(value, item.Value);
- var i = arrayIndex;
- foreach (var (k, v) in this._internalBuckets)
+ /// <summary>
+ /// Copies the to.
+ /// </summary>
+ /// <param name="array">The array.</param>
+ /// <param name="arrayIndex">The array index.</param>
+ void ICollection<KeyValuePair<string, TValue>>.CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)
{
- var kdv = v;
- while (kdv != null)
+ if (array.Length - arrayIndex < this.Count)
+ throw new ArgumentException("Target array is too small.", nameof(array));
+
+ var i = arrayIndex;
+ foreach (var (k, v) in this._internalBuckets)
{
- array[i++] = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
- kdv = kdv.Next;
+ var kdv = v;
+ while (kdv != null)
+ {
+ array[i++] = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
+ kdv = kdv.Next;
+ }
}
}
- }
- /// <summary>
- /// Copies the to.
- /// </summary>
- /// <param name="array">The array.</param>
- /// <param name="arrayIndex">The array index.</param>
- void ICollection.CopyTo(Array array, int arrayIndex)
- {
- if (array is KeyValuePair<string, TValue>[] tarray)
+ /// <summary>
+ /// Copies the to.
+ /// </summary>
+ /// <param name="array">The array.</param>
+ /// <param name="arrayIndex">The array index.</param>
+ void ICollection.CopyTo(Array array, int arrayIndex)
{
- (this as ICollection<KeyValuePair<string, TValue>>).CopyTo(tarray, arrayIndex);
- return;
- }
+ if (array is KeyValuePair<string, TValue>[] tarray)
+ {
+ (this as ICollection<KeyValuePair<string, TValue>>).CopyTo(tarray, arrayIndex);
+ return;
+ }
- if (array is not object[])
- throw new ArgumentException($"Array needs to be an instance of {typeof(TValue[])} or object[].");
+ if (array is not object[])
+ throw new ArgumentException($"Array needs to be an instance of {typeof(TValue[])} or object[].");
- var i = arrayIndex;
- foreach (var (k, v) in this._internalBuckets)
- {
- var kdv = v;
- while (kdv != null)
+ var i = arrayIndex;
+ foreach (var (k, v) in this._internalBuckets)
{
- array.SetValue(new KeyValuePair<string, TValue>(kdv.Key, kdv.Value), i++);
- kdv = kdv.Next;
+ var kdv = v;
+ while (kdv != null)
+ {
+ array.SetValue(new KeyValuePair<string, TValue>(kdv.Key, kdv.Value), i++);
+ kdv = kdv.Next;
+ }
}
}
- }
- /// <summary>
- /// Gets the enumerator.
- /// </summary>
- /// <returns>An IEnumerator.</returns>
- IEnumerator IEnumerable.GetEnumerator()
- => this.GetEnumerator();
-
- /// <summary>
- /// Tries the insert internal.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <param name="value">The value.</param>
- /// <param name="replace">If true, replace.</param>
- /// <returns>A bool.</returns>
- private bool TryInsertInternal(string key, TValue value, bool replace)
- {
- if (key == null)
- throw new ArgumentNullException(nameof(key), "Key cannot be null.");
+ /// <summary>
+ /// Gets the enumerator.
+ /// </summary>
+ /// <returns>An IEnumerator.</returns>
+ IEnumerator IEnumerable.GetEnumerator()
+ => this.GetEnumerator();
- var hash = key.CalculateKnuthHash();
- if (!this._internalBuckets.ContainsKey(hash))
+ /// <summary>
+ /// Tries the insert internal.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="value">The value.</param>
+ /// <param name="replace">If true, replace.</param>
+ /// <returns>A bool.</returns>
+ private bool TryInsertInternal(string key, TValue value, bool replace)
{
- this._internalBuckets.Add(hash, new KeyedValue(key, hash, value));
- this.Count++;
- return true;
- }
+ if (key == null)
+ throw new ArgumentNullException(nameof(key), "Key cannot be null.");
- var kdv = this._internalBuckets[hash];
- var kdvLast = kdv;
- while (kdv != null)
- {
- if (kdv.Key == key)
+ var hash = key.CalculateKnuthHash();
+ if (!this._internalBuckets.ContainsKey(hash))
{
- if (!replace)
- return false;
-
- kdv.Value = value;
+ this._internalBuckets.Add(hash, new KeyedValue(key, hash, value));
+ this.Count++;
return true;
}
- kdvLast = kdv;
- kdv = kdv.Next;
- }
+ var kdv = this._internalBuckets[hash];
+ var kdvLast = kdv;
+ while (kdv != null)
+ {
+ if (kdv.Key == key)
+ {
+ if (!replace)
+ return false;
- kdvLast.Next = new KeyedValue(key, hash, value);
- this.Count++;
- return true;
- }
+ kdv.Value = value;
+ return true;
+ }
- /// <summary>
- /// Tries the retrieve internal.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <param name="value">The value.</param>
- /// <returns>A bool.</returns>
- private bool TryRetrieveInternal(ReadOnlySpan<char> key, out TValue value)
- {
- value = default;
+ kdvLast = kdv;
+ kdv = kdv.Next;
+ }
- var hash = key.CalculateKnuthHash();
- if (!this._internalBuckets.TryGetValue(hash, out var kdv))
- return false;
+ kdvLast.Next = new KeyedValue(key, hash, value);
+ this.Count++;
+ return true;
+ }
- while (kdv != null)
+ /// <summary>
+ /// Tries the retrieve internal.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>A bool.</returns>
+ private bool TryRetrieveInternal(ReadOnlySpan<char> key, out TValue value)
{
- if (key.SequenceEqual(kdv.Key.AsSpan()))
- {
- value = kdv.Value;
- return true;
- }
- }
+ value = default;
- return false;
- }
+ var hash = key.CalculateKnuthHash();
+ if (!this._internalBuckets.TryGetValue(hash, out var kdv))
+ return false;
- /// <summary>
- /// Tries the remove internal.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <param name="value">The value.</param>
- /// <returns>A bool.</returns>
- private bool TryRemoveInternal(ReadOnlySpan<char> key, out TValue value)
- {
- value = default;
+ while (kdv != null)
+ {
+ if (key.SequenceEqual(kdv.Key.AsSpan()))
+ {
+ value = kdv.Value;
+ return true;
+ }
+ }
- var hash = key.CalculateKnuthHash();
- if (!this._internalBuckets.TryGetValue(hash, out var kdv))
return false;
-
- if (kdv.Next == null && key.SequenceEqual(kdv.Key.AsSpan()))
- {
- // Only bucket under this hash and key matches, pop the entire bucket
-
- value = kdv.Value;
- this._internalBuckets.Remove(hash);
- this.Count--;
- return true;
}
- else if (kdv.Next == null)
- {
- // Only bucket under this hash and key does not match, cannot remove
- return false;
- }
- else if (key.SequenceEqual(kdv.Key.AsSpan()))
+ /// <summary>
+ /// Tries the remove internal.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>A bool.</returns>
+ private bool TryRemoveInternal(ReadOnlySpan<char> key, out TValue value)
{
- // First key in the bucket matches, pop it and set its child as current bucket
+ value = default;
- value = kdv.Value;
- this._internalBuckets[hash] = kdv.Next;
- this.Count--;
- return true;
- }
+ var hash = key.CalculateKnuthHash();
+ if (!this._internalBuckets.TryGetValue(hash, out var kdv))
+ return false;
- var kdvLast = kdv;
- kdv = kdv.Next;
- while (kdv != null)
- {
- if (key.SequenceEqual(kdv.Key.AsSpan()))
+ if (kdv.Next == null && key.SequenceEqual(kdv.Key.AsSpan()))
{
- // Key matched, remove this bucket from the chain
+ // Only bucket under this hash and key matches, pop the entire bucket
value = kdv.Value;
- kdvLast.Next = kdv.Next;
+ this._internalBuckets.Remove(hash);
this.Count--;
return true;
}
+ else if (kdv.Next == null)
+ {
+ // Only bucket under this hash and key does not match, cannot remove
- kdvLast = kdv;
- kdv = kdv.Next;
- }
-
- return false;
- }
-
- /// <summary>
- /// Contains the key internal.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <returns>A bool.</returns>
- private bool ContainsKeyInternal(ReadOnlySpan<char> key)
- {
- var hash = key.CalculateKnuthHash();
- if (!this._internalBuckets.TryGetValue(hash, out var kdv))
- return false;
+ return false;
+ }
+ else if (key.SequenceEqual(kdv.Key.AsSpan()))
+ {
+ // First key in the bucket matches, pop it and set its child as current bucket
- while (kdv != null)
- {
- if (key.SequenceEqual(kdv.Key.AsSpan()))
+ value = kdv.Value;
+ this._internalBuckets[hash] = kdv.Next;
+ this.Count--;
return true;
+ }
+ var kdvLast = kdv;
kdv = kdv.Next;
- }
-
- return false;
- }
-
- /// <summary>
- /// Gets the keys internal.
- /// </summary>
- /// <returns>An ImmutableArray.</returns>
- private ImmutableArray<string> GetKeysInternal()
- {
- var builder = ImmutableArray.CreateBuilder<string>(this.Count);
- foreach (var value in this._internalBuckets.Values)
- {
- var kdv = value;
while (kdv != null)
{
- builder.Add(kdv.Key);
- kdv = kdv.Next;
- }
- }
+ if (key.SequenceEqual(kdv.Key.AsSpan()))
+ {
+ // Key matched, remove this bucket from the chain
- return builder.MoveToImmutable();
- }
+ value = kdv.Value;
+ kdvLast.Next = kdv.Next;
+ this.Count--;
+ return true;
+ }
- /// <summary>
- /// Gets the values internal.
- /// </summary>
- /// <returns>An ImmutableArray.</returns>
- private ImmutableArray<TValue> GetValuesInternal()
- {
- var builder = ImmutableArray.CreateBuilder<TValue>(this.Count);
- foreach (var value in this._internalBuckets.Values)
- {
- var kdv = value;
- while (kdv != null)
- {
- builder.Add(kdv.Value);
+ kdvLast = kdv;
kdv = kdv.Next;
}
- }
- return builder.MoveToImmutable();
- }
-
- /// <summary>
- /// The keyed value.
- /// </summary>
- private class KeyedValue
- {
- /// <summary>
- /// Gets the key hash.
- /// </summary>
- public ulong KeyHash { get; }
- /// <summary>
- /// Gets the key.
- /// </summary>
- public string Key { get; }
- /// <summary>
- /// Gets or sets the value.
- /// </summary>
- public TValue Value { get; set; }
-
- /// <summary>
- /// Gets or sets the next.
- /// </summary>
- public KeyedValue Next { get; set; }
+ return false;
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="KeyedValue"/> class.
+ /// Contains the key internal.
/// </summary>
/// <param name="key">The key.</param>
- /// <param name="keyHash">The key hash.</param>
- /// <param name="value">The value.</param>
- public KeyedValue(string key, ulong keyHash, TValue value)
+ /// <returns>A bool.</returns>
+ private bool ContainsKeyInternal(ReadOnlySpan<char> key)
{
- this.KeyHash = keyHash;
- this.Key = key;
- this.Value = value;
+ var hash = key.CalculateKnuthHash();
+ if (!this._internalBuckets.TryGetValue(hash, out var kdv))
+ return false;
+
+ while (kdv != null)
+ {
+ if (key.SequenceEqual(kdv.Key.AsSpan()))
+ return true;
+
+ kdv = kdv.Next;
+ }
+
+ return false;
}
- }
- /// <summary>
- /// The enumerator.
- /// </summary>
- private class Enumerator :
- IEnumerator<KeyValuePair<string, TValue>>,
- IDictionaryEnumerator
- {
- /// <summary>
- /// Gets the current.
- /// </summary>
- public KeyValuePair<string, TValue> Current { get; private set; }
- /// <summary>
- /// Gets the current.
- /// </summary>
- object IEnumerator.Current => this.Current;
- /// <summary>
- /// Gets the key.
- /// </summary>
- object IDictionaryEnumerator.Key => this.Current.Key;
/// <summary>
- /// Gets the value.
+ /// Gets the keys internal.
/// </summary>
- object IDictionaryEnumerator.Value => this.Current.Value;
- /// <summary>
- /// Gets the entry.
- /// </summary>
- DictionaryEntry IDictionaryEnumerator.Entry => new(this.Current.Key, this.Current.Value);
+ /// <returns>An ImmutableArray.</returns>
+ private ImmutableArray<string> GetKeysInternal()
+ {
+ var builder = ImmutableArray.CreateBuilder<string>(this.Count);
+ foreach (var value in this._internalBuckets.Values)
+ {
+ var kdv = value;
+ while (kdv != null)
+ {
+ builder.Add(kdv.Key);
+ kdv = kdv.Next;
+ }
+ }
- /// <summary>
- /// Gets the internal dictionary.
- /// </summary>
- private readonly CharSpanLookupDictionary<TValue> _internalDictionary;
+ return builder.MoveToImmutable();
+ }
/// <summary>
- /// Gets the internal enumerator.
+ /// Gets the values internal.
/// </summary>
- private readonly IEnumerator<KeyValuePair<ulong, KeyedValue>> _internalEnumerator;
+ /// <returns>An ImmutableArray.</returns>
+ private ImmutableArray<TValue> GetValuesInternal()
+ {
+ var builder = ImmutableArray.CreateBuilder<TValue>(this.Count);
+ foreach (var value in this._internalBuckets.Values)
+ {
+ var kdv = value;
+ while (kdv != null)
+ {
+ builder.Add(kdv.Value);
+ kdv = kdv.Next;
+ }
+ }
- /// <summary>
- /// Gets or sets the current value.
- /// </summary>
- private KeyedValue _currentValue;
+ return builder.MoveToImmutable();
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="Enumerator"/> class.
+ /// The keyed value.
/// </summary>
- /// <param name="spDict">The sp dict.</param>
- public Enumerator(CharSpanLookupDictionary<TValue> spDict)
+ private class KeyedValue
{
- this._internalDictionary = spDict;
- this._internalEnumerator = this._internalDictionary._internalBuckets.GetEnumerator();
+ /// <summary>
+ /// Gets the key hash.
+ /// </summary>
+ public ulong KeyHash { get; }
+ /// <summary>
+ /// Gets the key.
+ /// </summary>
+ public string Key { get; }
+ /// <summary>
+ /// Gets or sets the value.
+ /// </summary>
+ public TValue Value { get; set; }
+
+ /// <summary>
+ /// Gets or sets the next.
+ /// </summary>
+ public KeyedValue Next { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="KeyedValue"/> class.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="keyHash">The key hash.</param>
+ /// <param name="value">The value.</param>
+ public KeyedValue(string key, ulong keyHash, TValue value)
+ {
+ this.KeyHash = keyHash;
+ this.Key = key;
+ this.Value = value;
+ }
}
/// <summary>
- /// Moves the next.
+ /// The enumerator.
/// </summary>
- /// <returns>A bool.</returns>
- public bool MoveNext()
+ private class Enumerator :
+ IEnumerator<KeyValuePair<string, TValue>>,
+ IDictionaryEnumerator
{
- var kdv = this._currentValue;
- if (kdv == null)
+ /// <summary>
+ /// Gets the current.
+ /// </summary>
+ public KeyValuePair<string, TValue> Current { get; private set; }
+ /// <summary>
+ /// Gets the current.
+ /// </summary>
+ object IEnumerator.Current => this.Current;
+ /// <summary>
+ /// Gets the key.
+ /// </summary>
+ object IDictionaryEnumerator.Key => this.Current.Key;
+ /// <summary>
+ /// Gets the value.
+ /// </summary>
+ object IDictionaryEnumerator.Value => this.Current.Value;
+ /// <summary>
+ /// Gets the entry.
+ /// </summary>
+ DictionaryEntry IDictionaryEnumerator.Entry => new(this.Current.Key, this.Current.Value);
+
+ /// <summary>
+ /// Gets the internal dictionary.
+ /// </summary>
+ private readonly CharSpanLookupDictionary<TValue> _internalDictionary;
+
+ /// <summary>
+ /// Gets the internal enumerator.
+ /// </summary>
+ private readonly IEnumerator<KeyValuePair<ulong, KeyedValue>> _internalEnumerator;
+
+ /// <summary>
+ /// Gets or sets the current value.
+ /// </summary>
+ private KeyedValue _currentValue;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Enumerator"/> class.
+ /// </summary>
+ /// <param name="spDict">The sp dict.</param>
+ public Enumerator(CharSpanLookupDictionary<TValue> spDict)
{
- if (!this._internalEnumerator.MoveNext())
- return false;
+ this._internalDictionary = spDict;
+ this._internalEnumerator = this._internalDictionary._internalBuckets.GetEnumerator();
+ }
- kdv = this._internalEnumerator.Current.Value;
- this.Current = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
+ /// <summary>
+ /// Moves the next.
+ /// </summary>
+ /// <returns>A bool.</returns>
+ public bool MoveNext()
+ {
+ var kdv = this._currentValue;
+ if (kdv == null)
+ {
+ if (!this._internalEnumerator.MoveNext())
+ return false;
+
+ kdv = this._internalEnumerator.Current.Value;
+ this.Current = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
+ this._currentValue = kdv.Next;
+ return true;
+ }
+
+ this.Current = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
this._currentValue = kdv.Next;
return true;
}
- this.Current = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
- this._currentValue = kdv.Next;
- return true;
- }
+ /// <summary>
+ /// Resets the.
+ /// </summary>
+ public void Reset()
+ {
+ this._internalEnumerator.Reset();
+ this.Current = default;
+ this._currentValue = null;
+ }
- /// <summary>
- /// Resets the.
- /// </summary>
- public void Reset()
- {
- this._internalEnumerator.Reset();
- this.Current = default;
- this._currentValue = null;
+ /// <summary>
+ /// Disposes the.
+ /// </summary>
+ public void Dispose() => this.Reset();
}
-
- /// <summary>
- /// Disposes the.
- /// </summary>
- public void Dispose() => this.Reset();
}
}
diff --git a/DisCatSharp.Common/Types/CharSpanLookupReadOnlyDictionary.cs b/DisCatSharp.Common/Types/CharSpanLookupReadOnlyDictionary.cs
index b8c3b2a1d..7a5a73a9f 100644
--- a/DisCatSharp.Common/Types/CharSpanLookupReadOnlyDictionary.cs
+++ b/DisCatSharp.Common/Types/CharSpanLookupReadOnlyDictionary.cs
@@ -1,415 +1,416 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
-namespace DisCatSharp.Common;
-
-/// <summary>
-/// Represents collection of string keys and <typeparamref name="TValue"/> values, allowing the use of <see cref="System.ReadOnlySpan{T}"/> for dictionary operations.
-/// </summary>
-/// <typeparam name="TValue">Type of items in this dictionary.</typeparam>
-public sealed class CharSpanLookupReadOnlyDictionary<TValue> : IReadOnlyDictionary<string, TValue>
+namespace DisCatSharp.Common
{
/// <summary>
- /// Gets the collection of all keys present in this dictionary.
+ /// Represents collection of string keys and <typeparamref name="TValue"/> values, allowing the use of <see cref="System.ReadOnlySpan{T}"/> for dictionary operations.
/// </summary>
- public IEnumerable<string> Keys => this.GetKeysInternal();
+ /// <typeparam name="TValue">Type of items in this dictionary.</typeparam>
+ public sealed class CharSpanLookupReadOnlyDictionary<TValue> : IReadOnlyDictionary<string, TValue>
+ {
+ /// <summary>
+ /// Gets the collection of all keys present in this dictionary.
+ /// </summary>
+ public IEnumerable<string> Keys => this.GetKeysInternal();
- /// <summary>
- /// Gets the collection of all values present in this dictionary.
- /// </summary>
- public IEnumerable<TValue> Values => this.GetValuesInternal();
+ /// <summary>
+ /// Gets the collection of all values present in this dictionary.
+ /// </summary>
+ public IEnumerable<TValue> Values => this.GetValuesInternal();
- /// <summary>
- /// Gets the total number of items in this dictionary.
- /// </summary>
- public int Count { get; }
+ /// <summary>
+ /// Gets the total number of items in this dictionary.
+ /// </summary>
+ public int Count { get; }
- /// <summary>
- /// Gets a value corresponding to given key in this dictionary.
- /// </summary>
- /// <param name="key">Key to get or set the value for.</param>
- /// <returns>Value matching the supplied key, if applicable.</returns>
- public TValue this[string key]
- {
- get
+ /// <summary>
+ /// Gets a value corresponding to given key in this dictionary.
+ /// </summary>
+ /// <param name="key">Key to get or set the value for.</param>
+ /// <returns>Value matching the supplied key, if applicable.</returns>
+ public TValue this[string key]
{
- if (key == null)
- throw new ArgumentNullException(nameof(key));
+ get
+ {
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
- if (!this.TryRetrieveInternal(key.AsSpan(), out var value))
- throw new KeyNotFoundException($"The given key '{key}' was not present in the dictionary.");
+ if (!this.TryRetrieveInternal(key.AsSpan(), out var value))
+ throw new KeyNotFoundException($"The given key '{key}' was not present in the dictionary.");
- return value;
+ return value;
+ }
}
- }
- /// <summary>
- /// Gets a value corresponding to given key in this dictionary.
- /// </summary>
- /// <param name="key">Key to get or set the value for.</param>
- /// <returns>Value matching the supplied key, if applicable.</returns>
- public TValue this[ReadOnlySpan<char> key]
- {
- get
+ /// <summary>
+ /// Gets a value corresponding to given key in this dictionary.
+ /// </summary>
+ /// <param name="key">Key to get or set the value for.</param>
+ /// <returns>Value matching the supplied key, if applicable.</returns>
+ public TValue this[ReadOnlySpan<char> key]
{
- if (!this.TryRetrieveInternal(key, out var value))
- throw new KeyNotFoundException($"The given key was not present in the dictionary.");
+ get
+ {
+ if (!this.TryRetrieveInternal(key, out var value))
+ throw new KeyNotFoundException($"The given key was not present in the dictionary.");
- return value;
+ return value;
+ }
}
- }
-
- /// <summary>
- /// Gets the internal buckets.
- /// </summary>
- private readonly IReadOnlyDictionary<ulong, KeyedValue> _internalBuckets;
-
- /// <summary>
- /// Creates a new <see cref="CharSpanLookupReadOnlyDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied dictionary.
- /// </summary>
- /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
- public CharSpanLookupReadOnlyDictionary(IDictionary<string, TValue> values)
- : this(values as IEnumerable<KeyValuePair<string, TValue>>)
- { }
-
- /// <summary>
- /// Creates a new <see cref="CharSpanLookupReadOnlyDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied dictionary.
- /// </summary>
- /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
- public CharSpanLookupReadOnlyDictionary(IReadOnlyDictionary<string, TValue> values)
- : this(values as IEnumerable<KeyValuePair<string, TValue>>)
- { }
-
- /// <summary>
- /// Creates a new <see cref="CharSpanLookupReadOnlyDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied key-value collection.
- /// </summary>
- /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
- public CharSpanLookupReadOnlyDictionary(IEnumerable<KeyValuePair<string, TValue>> values)
- {
- this._internalBuckets = PrepareItems(values, out var count);
- this.Count = count;
- }
-
- /// <summary>
- /// Attempts to retrieve a value corresponding to the supplied key from this dictionary.
- /// </summary>
- /// <param name="key">Key to retrieve the value for.</param>
- /// <param name="value">Retrieved value.</param>
- /// <returns>Whether the operation was successful.</returns>
- public bool TryGetValue(string key, out TValue value)
- {
- if (key == null)
- throw new ArgumentNullException(nameof(key));
-
- return this.TryRetrieveInternal(key.AsSpan(), out value);
- }
-
- /// <summary>
- /// Attempts to retrieve a value corresponding to the supplied key from this dictionary.
- /// </summary>
- /// <param name="key">Key to retrieve the value for.</param>
- /// <param name="value">Retrieved value.</param>
- /// <returns>Whether the operation was successful.</returns>
- public bool TryGetValue(ReadOnlySpan<char> key, out TValue value)
- => this.TryRetrieveInternal(key, out value);
-
- /// <summary>
- /// Checks whether this dictionary contains the specified key.
- /// </summary>
- /// <param name="key">Key to check for in this dictionary.</param>
- /// <returns>Whether the key was present in the dictionary.</returns>
- public bool ContainsKey(string key)
- => this.ContainsKeyInternal(key.AsSpan());
- /// <summary>
- /// Checks whether this dictionary contains the specified key.
- /// </summary>
- /// <param name="key">Key to check for in this dictionary.</param>
- /// <returns>Whether the key was present in the dictionary.</returns>
- public bool ContainsKey(ReadOnlySpan<char> key)
- => this.ContainsKeyInternal(key);
-
- /// <summary>
- /// Gets an enumerator over key-value pairs in this dictionary.
- /// </summary>
- /// <returns></returns>
- public IEnumerator<KeyValuePair<string, TValue>> GetEnumerator()
- => new Enumerator(this);
+ /// <summary>
+ /// Gets the internal buckets.
+ /// </summary>
+ private readonly IReadOnlyDictionary<ulong, KeyedValue> _internalBuckets;
- /// <summary>
- /// Gets the enumerator.
- /// </summary>
- /// <returns>An IEnumerator.</returns>
- IEnumerator IEnumerable.GetEnumerator()
- => this.GetEnumerator();
+ /// <summary>
+ /// Creates a new <see cref="CharSpanLookupReadOnlyDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied dictionary.
+ /// </summary>
+ /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
+ public CharSpanLookupReadOnlyDictionary(IDictionary<string, TValue> values)
+ : this(values as IEnumerable<KeyValuePair<string, TValue>>)
+ { }
- /// <summary>
- /// Tries the retrieve internal.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <param name="value">The value.</param>
- /// <returns>A bool.</returns>
- private bool TryRetrieveInternal(ReadOnlySpan<char> key, out TValue value)
- {
- value = default;
+ /// <summary>
+ /// Creates a new <see cref="CharSpanLookupReadOnlyDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied dictionary.
+ /// </summary>
+ /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
+ public CharSpanLookupReadOnlyDictionary(IReadOnlyDictionary<string, TValue> values)
+ : this(values as IEnumerable<KeyValuePair<string, TValue>>)
+ { }
- var hash = key.CalculateKnuthHash();
- if (!this._internalBuckets.TryGetValue(hash, out var kdv))
- return false;
+ /// <summary>
+ /// Creates a new <see cref="CharSpanLookupReadOnlyDictionary{TValue}"/> with string keys and items of type <typeparamref name="TValue"/> and populates it with key-value pairs from supplied key-value collection.
+ /// </summary>
+ /// <param name="values">Dictionary containing items to populate this dictionary with.</param>
+ public CharSpanLookupReadOnlyDictionary(IEnumerable<KeyValuePair<string, TValue>> values)
+ {
+ this._internalBuckets = PrepareItems(values, out var count);
+ this.Count = count;
+ }
- while (kdv != null)
+ /// <summary>
+ /// Attempts to retrieve a value corresponding to the supplied key from this dictionary.
+ /// </summary>
+ /// <param name="key">Key to retrieve the value for.</param>
+ /// <param name="value">Retrieved value.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public bool TryGetValue(string key, out TValue value)
{
- if (key.SequenceEqual(kdv.Key.AsSpan()))
- {
- value = kdv.Value;
- return true;
- }
+ if (key == null)
+ throw new ArgumentNullException(nameof(key));
+
+ return this.TryRetrieveInternal(key.AsSpan(), out value);
}
- return false;
- }
+ /// <summary>
+ /// Attempts to retrieve a value corresponding to the supplied key from this dictionary.
+ /// </summary>
+ /// <param name="key">Key to retrieve the value for.</param>
+ /// <param name="value">Retrieved value.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public bool TryGetValue(ReadOnlySpan<char> key, out TValue value)
+ => this.TryRetrieveInternal(key, out value);
- /// <summary>
- /// Contains the key internal.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <returns>A bool.</returns>
- private bool ContainsKeyInternal(ReadOnlySpan<char> key)
- {
- var hash = key.CalculateKnuthHash();
- if (!this._internalBuckets.TryGetValue(hash, out var kdv))
- return false;
+ /// <summary>
+ /// Checks whether this dictionary contains the specified key.
+ /// </summary>
+ /// <param name="key">Key to check for in this dictionary.</param>
+ /// <returns>Whether the key was present in the dictionary.</returns>
+ public bool ContainsKey(string key)
+ => this.ContainsKeyInternal(key.AsSpan());
- while (kdv != null)
- {
- if (key.SequenceEqual(kdv.Key.AsSpan()))
- return true;
+ /// <summary>
+ /// Checks whether this dictionary contains the specified key.
+ /// </summary>
+ /// <param name="key">Key to check for in this dictionary.</param>
+ /// <returns>Whether the key was present in the dictionary.</returns>
+ public bool ContainsKey(ReadOnlySpan<char> key)
+ => this.ContainsKeyInternal(key);
- kdv = kdv.Next;
- }
+ /// <summary>
+ /// Gets an enumerator over key-value pairs in this dictionary.
+ /// </summary>
+ /// <returns></returns>
+ public IEnumerator<KeyValuePair<string, TValue>> GetEnumerator()
+ => new Enumerator(this);
- return false;
- }
+ /// <summary>
+ /// Gets the enumerator.
+ /// </summary>
+ /// <returns>An IEnumerator.</returns>
+ IEnumerator IEnumerable.GetEnumerator()
+ => this.GetEnumerator();
- /// <summary>
- /// Gets the keys internal.
- /// </summary>
- /// <returns>An ImmutableArray.</returns>
- private ImmutableArray<string> GetKeysInternal()
- {
- var builder = ImmutableArray.CreateBuilder<string>(this.Count);
- foreach (var value in this._internalBuckets.Values)
+ /// <summary>
+ /// Tries the retrieve internal.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>A bool.</returns>
+ private bool TryRetrieveInternal(ReadOnlySpan<char> key, out TValue value)
{
- var kdv = value;
- while (kdv != null)
- {
- builder.Add(kdv.Key);
- kdv = kdv.Next;
- }
- }
+ value = default;
- return builder.MoveToImmutable();
- }
+ var hash = key.CalculateKnuthHash();
+ if (!this._internalBuckets.TryGetValue(hash, out var kdv))
+ return false;
- /// <summary>
- /// Gets the values internal.
- /// </summary>
- /// <returns>An ImmutableArray.</returns>
- private ImmutableArray<TValue> GetValuesInternal()
- {
- var builder = ImmutableArray.CreateBuilder<TValue>(this.Count);
- foreach (var value in this._internalBuckets.Values)
- {
- var kdv = value;
while (kdv != null)
{
- builder.Add(kdv.Value);
- kdv = kdv.Next;
+ if (key.SequenceEqual(kdv.Key.AsSpan()))
+ {
+ value = kdv.Value;
+ return true;
+ }
}
- }
- return builder.MoveToImmutable();
- }
+ return false;
+ }
- /// <summary>
- /// Prepares the items.
- /// </summary>
- /// <param name="items">The items.</param>
- /// <param name="count">The count.</param>
- /// <returns>An IReadOnlyDictionary.</returns>
- private static IReadOnlyDictionary<ulong, KeyedValue> PrepareItems(IEnumerable<KeyValuePair<string, TValue>> items, out int count)
- {
- count = 0;
- var dict = new Dictionary<ulong, KeyedValue>();
- foreach (var (k, v) in items)
+ /// <summary>
+ /// Contains the key internal.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <returns>A bool.</returns>
+ private bool ContainsKeyInternal(ReadOnlySpan<char> key)
{
- if (k == null)
- throw new ArgumentException("Keys cannot be null.", nameof(items));
-
- var hash = k.CalculateKnuthHash();
- if (!dict.ContainsKey(hash))
- {
- dict.Add(hash, new KeyedValue(k, hash, v));
- count++;
- continue;
- }
+ var hash = key.CalculateKnuthHash();
+ if (!this._internalBuckets.TryGetValue(hash, out var kdv))
+ return false;
- var kdv = dict[hash];
- var kdvLast = kdv;
while (kdv != null)
{
- if (kdv.Key == k)
- throw new ArgumentException("Given key is already present in the dictionary.", nameof(items));
+ if (key.SequenceEqual(kdv.Key.AsSpan()))
+ return true;
- kdvLast = kdv;
kdv = kdv.Next;
}
- kdvLast.Next = new KeyedValue(k, hash, v);
- count++;
+ return false;
}
- return new ReadOnlyDictionary<ulong, KeyedValue>(dict);
- }
-
- /// <summary>
- /// The keyed value.
- /// </summary>
- private class KeyedValue
- {
- /// <summary>
- /// Gets the key hash.
- /// </summary>
- public ulong KeyHash { get; }
- /// <summary>
- /// Gets the key.
- /// </summary>
- public string Key { get; }
- /// <summary>
- /// Gets or sets the value.
- /// </summary>
- public TValue Value { get; set; }
-
- /// <summary>
- /// Gets or sets the next.
- /// </summary>
- public KeyedValue Next { get; set; }
-
/// <summary>
- /// Initializes a new instance of the <see cref="KeyedValue"/> class.
+ /// Gets the keys internal.
/// </summary>
- /// <param name="key">The key.</param>
- /// <param name="keyHash">The key hash.</param>
- /// <param name="value">The value.</param>
- public KeyedValue(string key, ulong keyHash, TValue value)
+ /// <returns>An ImmutableArray.</returns>
+ private ImmutableArray<string> GetKeysInternal()
{
- this.KeyHash = keyHash;
- this.Key = key;
- this.Value = value;
+ var builder = ImmutableArray.CreateBuilder<string>(this.Count);
+ foreach (var value in this._internalBuckets.Values)
+ {
+ var kdv = value;
+ while (kdv != null)
+ {
+ builder.Add(kdv.Key);
+ kdv = kdv.Next;
+ }
+ }
+
+ return builder.MoveToImmutable();
}
- }
- /// <summary>
- /// The enumerator.
- /// </summary>
- private class Enumerator : IEnumerator<KeyValuePair<string, TValue>>
- {
/// <summary>
- /// Gets the current.
+ /// Gets the values internal.
/// </summary>
- public KeyValuePair<string, TValue> Current { get; private set; }
- /// <summary>
- /// Gets the current.
- /// </summary>
- object IEnumerator.Current => this.Current;
+ /// <returns>An ImmutableArray.</returns>
+ private ImmutableArray<TValue> GetValuesInternal()
+ {
+ var builder = ImmutableArray.CreateBuilder<TValue>(this.Count);
+ foreach (var value in this._internalBuckets.Values)
+ {
+ var kdv = value;
+ while (kdv != null)
+ {
+ builder.Add(kdv.Value);
+ kdv = kdv.Next;
+ }
+ }
- /// <summary>
- /// Gets the internal dictionary.
- /// </summary>
- private readonly CharSpanLookupReadOnlyDictionary<TValue> _internalDictionary;
+ return builder.MoveToImmutable();
+ }
/// <summary>
- /// Gets the internal enumerator.
+ /// Prepares the items.
/// </summary>
- private readonly IEnumerator<KeyValuePair<ulong, KeyedValue>> _internalEnumerator;
+ /// <param name="items">The items.</param>
+ /// <param name="count">The count.</param>
+ /// <returns>An IReadOnlyDictionary.</returns>
+ private static IReadOnlyDictionary<ulong, KeyedValue> PrepareItems(IEnumerable<KeyValuePair<string, TValue>> items, out int count)
+ {
+ count = 0;
+ var dict = new Dictionary<ulong, KeyedValue>();
+ foreach (var (k, v) in items)
+ {
+ if (k == null)
+ throw new ArgumentException("Keys cannot be null.", nameof(items));
+
+ var hash = k.CalculateKnuthHash();
+ if (!dict.ContainsKey(hash))
+ {
+ dict.Add(hash, new KeyedValue(k, hash, v));
+ count++;
+ continue;
+ }
+
+ var kdv = dict[hash];
+ var kdvLast = kdv;
+ while (kdv != null)
+ {
+ if (kdv.Key == k)
+ throw new ArgumentException("Given key is already present in the dictionary.", nameof(items));
+
+ kdvLast = kdv;
+ kdv = kdv.Next;
+ }
+
+ kdvLast.Next = new KeyedValue(k, hash, v);
+ count++;
+ }
- /// <summary>
- /// Gets or sets the current value.
- /// </summary>
- private KeyedValue _currentValue;
+ return new ReadOnlyDictionary<ulong, KeyedValue>(dict);
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="Enumerator"/> class.
+ /// The keyed value.
/// </summary>
- /// <param name="spDict">The sp dict.</param>
- public Enumerator(CharSpanLookupReadOnlyDictionary<TValue> spDict)
+ private class KeyedValue
{
- this._internalDictionary = spDict;
- this._internalEnumerator = this._internalDictionary._internalBuckets.GetEnumerator();
+ /// <summary>
+ /// Gets the key hash.
+ /// </summary>
+ public ulong KeyHash { get; }
+ /// <summary>
+ /// Gets the key.
+ /// </summary>
+ public string Key { get; }
+ /// <summary>
+ /// Gets or sets the value.
+ /// </summary>
+ public TValue Value { get; set; }
+
+ /// <summary>
+ /// Gets or sets the next.
+ /// </summary>
+ public KeyedValue Next { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="KeyedValue"/> class.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="keyHash">The key hash.</param>
+ /// <param name="value">The value.</param>
+ public KeyedValue(string key, ulong keyHash, TValue value)
+ {
+ this.KeyHash = keyHash;
+ this.Key = key;
+ this.Value = value;
+ }
}
/// <summary>
- /// Moves the next.
+ /// The enumerator.
/// </summary>
- /// <returns>A bool.</returns>
- public bool MoveNext()
+ private class Enumerator : IEnumerator<KeyValuePair<string, TValue>>
{
- var kdv = this._currentValue;
- if (kdv == null)
+ /// <summary>
+ /// Gets the current.
+ /// </summary>
+ public KeyValuePair<string, TValue> Current { get; private set; }
+ /// <summary>
+ /// Gets the current.
+ /// </summary>
+ object IEnumerator.Current => this.Current;
+
+ /// <summary>
+ /// Gets the internal dictionary.
+ /// </summary>
+ private readonly CharSpanLookupReadOnlyDictionary<TValue> _internalDictionary;
+
+ /// <summary>
+ /// Gets the internal enumerator.
+ /// </summary>
+ private readonly IEnumerator<KeyValuePair<ulong, KeyedValue>> _internalEnumerator;
+
+ /// <summary>
+ /// Gets or sets the current value.
+ /// </summary>
+ private KeyedValue _currentValue;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Enumerator"/> class.
+ /// </summary>
+ /// <param name="spDict">The sp dict.</param>
+ public Enumerator(CharSpanLookupReadOnlyDictionary<TValue> spDict)
{
- if (!this._internalEnumerator.MoveNext())
- return false;
+ this._internalDictionary = spDict;
+ this._internalEnumerator = this._internalDictionary._internalBuckets.GetEnumerator();
+ }
- kdv = this._internalEnumerator.Current.Value;
- this.Current = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
+ /// <summary>
+ /// Moves the next.
+ /// </summary>
+ /// <returns>A bool.</returns>
+ public bool MoveNext()
+ {
+ var kdv = this._currentValue;
+ if (kdv == null)
+ {
+ if (!this._internalEnumerator.MoveNext())
+ return false;
+ kdv = this._internalEnumerator.Current.Value;
+ this.Current = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
+
+ this._currentValue = kdv.Next;
+ return true;
+ }
+
+ this.Current = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
this._currentValue = kdv.Next;
return true;
}
- this.Current = new KeyValuePair<string, TValue>(kdv.Key, kdv.Value);
- this._currentValue = kdv.Next;
- return true;
- }
+ /// <summary>
+ /// Resets the.
+ /// </summary>
+ public void Reset()
+ {
+ this._internalEnumerator.Reset();
+ this.Current = default;
+ this._currentValue = null;
+ }
- /// <summary>
- /// Resets the.
- /// </summary>
- public void Reset()
- {
- this._internalEnumerator.Reset();
- this.Current = default;
- this._currentValue = null;
+ /// <summary>
+ /// Disposes the.
+ /// </summary>
+ public void Dispose() => this.Reset();
}
-
- /// <summary>
- /// Disposes the.
- /// </summary>
- public void Dispose() => this.Reset();
}
}
diff --git a/DisCatSharp.Common/Types/ContinuousMemoryBuffer.cs b/DisCatSharp.Common/Types/ContinuousMemoryBuffer.cs
index 0368bb08d..d6163205b 100644
--- a/DisCatSharp.Common/Types/ContinuousMemoryBuffer.cs
+++ b/DisCatSharp.Common/Types/ContinuousMemoryBuffer.cs
@@ -1,254 +1,255 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-namespace DisCatSharp.Common.Types;
-
-/// <summary>
-/// Provides a resizable memory buffer analogous to <see cref="MemoryBuffer{T}"/>, using a single continuous memory region instead.
-/// </summary>
-/// <typeparam name="T">Type of item to hold in the buffer.</typeparam>
-public sealed class ContinuousMemoryBuffer<T> : IMemoryBuffer<T> where T : unmanaged
+namespace DisCatSharp.Common.Types
{
- /// <inheritdoc />
- public ulong Capacity => (ulong)this._buff.Length;
-
- /// <inheritdoc />
- public ulong Length => (ulong)this._pos;
-
- /// <inheritdoc />
- public ulong Count => (ulong)(this._pos / this._itemSize);
-
- private readonly MemoryPool<byte> _pool;
- private IMemoryOwner<byte> _buffOwner;
- private Memory<byte> _buff;
- private readonly bool _clear;
- private int _pos;
- private readonly int _itemSize;
- private bool _isDisposed;
-
/// <summary>
- /// Creates a new buffer with a specified segment size, specified number of initially-allocated segments, and supplied memory pool.
+ /// Provides a resizable memory buffer analogous to <see cref="MemoryBuffer{T}"/>, using a single continuous memory region instead.
/// </summary>
- /// <param name="initialSize">Initial size of the buffer in bytes. Defaults to 64KiB.</param>
- /// <param name="memPool">Memory pool to use for renting buffers. Defaults to <see cref="System.Buffers.MemoryPool{T}.Shared"/>.</param>
- /// <param name="clearOnDispose">Determines whether the underlying buffers should be cleared on exit. If dealing with sensitive data, it might be a good idea to set this option to true.</param>
- public ContinuousMemoryBuffer(int initialSize = 65536, MemoryPool<byte> memPool = default, bool clearOnDispose = false)
+ /// <typeparam name="T">Type of item to hold in the buffer.</typeparam>
+ public sealed class ContinuousMemoryBuffer<T> : IMemoryBuffer<T> where T : unmanaged
{
- this._itemSize = Unsafe.SizeOf<T>();
- this._pool = memPool ?? MemoryPool<byte>.Shared;
- this._clear = clearOnDispose;
-
- this._buffOwner = this._pool.Rent(initialSize);
- this._buff = this._buffOwner.Memory;
+ /// <inheritdoc />
+ public ulong Capacity => (ulong)this._buff.Length;
+
+ /// <inheritdoc />
+ public ulong Length => (ulong)this._pos;
+
+ /// <inheritdoc />
+ public ulong Count => (ulong)(this._pos / this._itemSize);
+
+ private readonly MemoryPool<byte> _pool;
+ private IMemoryOwner<byte> _buffOwner;
+ private Memory<byte> _buff;
+ private readonly bool _clear;
+ private int _pos;
+ private readonly int _itemSize;
+ private bool _isDisposed;
+
+ /// <summary>
+ /// Creates a new buffer with a specified segment size, specified number of initially-allocated segments, and supplied memory pool.
+ /// </summary>
+ /// <param name="initialSize">Initial size of the buffer in bytes. Defaults to 64KiB.</param>
+ /// <param name="memPool">Memory pool to use for renting buffers. Defaults to <see cref="System.Buffers.MemoryPool{T}.Shared"/>.</param>
+ /// <param name="clearOnDispose">Determines whether the underlying buffers should be cleared on exit. If dealing with sensitive data, it might be a good idea to set this option to true.</param>
+ public ContinuousMemoryBuffer(int initialSize = 65536, MemoryPool<byte> memPool = default, bool clearOnDispose = false)
+ {
+ this._itemSize = Unsafe.SizeOf<T>();
+ this._pool = memPool ?? MemoryPool<byte>.Shared;
+ this._clear = clearOnDispose;
- this._isDisposed = false;
- }
+ this._buffOwner = this._pool.Rent(initialSize);
+ this._buff = this._buffOwner.Memory;
- /// <inheritdoc />
- public void Write(ReadOnlySpan<T> data)
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
-
- var bytes = MemoryMarshal.AsBytes(data);
- this.EnsureSize(this._pos + bytes.Length);
+ this._isDisposed = false;
+ }
- bytes.CopyTo(this._buff[this._pos..].Span);
- this._pos += bytes.Length;
- }
+ /// <inheritdoc />
+ public void Write(ReadOnlySpan<T> data)
+ {
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- /// <inheritdoc />
- public void Write(T[] data, int start, int count)
- => this.Write(data.AsSpan(start, count));
+ var bytes = MemoryMarshal.AsBytes(data);
+ this.EnsureSize(this._pos + bytes.Length);
- /// <inheritdoc />
- public void Write(ArraySegment<T> data)
- => this.Write(data.AsSpan());
+ bytes.CopyTo(this._buff[this._pos..].Span);
+ this._pos += bytes.Length;
+ }
- /// <inheritdoc />
- public void Write(Stream stream)
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
+ /// <inheritdoc />
+ public void Write(T[] data, int start, int count)
+ => this.Write(data.AsSpan(start, count));
- if (stream.CanSeek)
- this.WriteStreamSeekable(stream);
- else
- this.WriteStreamUnseekable(stream);
- }
+ /// <inheritdoc />
+ public void Write(ArraySegment<T> data)
+ => this.Write(data.AsSpan());
- /// <summary>
- /// Writes the stream seekable.
- /// </summary>
- /// <param name="stream">The stream.</param>
- private void WriteStreamSeekable(Stream stream)
- {
- if (stream.Length > int.MaxValue)
- throw new ArgumentException("Stream is too long.", nameof(stream));
-
- this.EnsureSize(this._pos + (int)stream.Length);
- var memo = ArrayPool<byte>.Shared.Rent((int)stream.Length);
- try
- {
- var br = stream.Read(memo, 0, memo.Length);
- memo.AsSpan(0, br).CopyTo(this._buff[this._pos..].Span);
- }
- finally
+ /// <inheritdoc />
+ public void Write(Stream stream)
{
- ArrayPool<byte>.Shared.Return(memo);
- }
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- this._pos += (int)stream.Length;
- }
+ if (stream.CanSeek)
+ this.WriteStreamSeekable(stream);
+ else
+ this.WriteStreamUnseekable(stream);
+ }
- /// <summary>
- /// Writes the stream unseekable.
- /// </summary>
- /// <param name="stream">The stream.</param>
- private void WriteStreamUnseekable(Stream stream)
- {
- var memo = ArrayPool<byte>.Shared.Rent(4096);
- try
+ /// <summary>
+ /// Writes the stream seekable.
+ /// </summary>
+ /// <param name="stream">The stream.</param>
+ private void WriteStreamSeekable(Stream stream)
{
- var br = 0;
- while ((br = stream.Read(memo, 0, memo.Length)) != 0)
+ if (stream.Length > int.MaxValue)
+ throw new ArgumentException("Stream is too long.", nameof(stream));
+
+ this.EnsureSize(this._pos + (int)stream.Length);
+ var memo = ArrayPool<byte>.Shared.Rent((int)stream.Length);
+ try
{
- this.EnsureSize(this._pos + br);
+ var br = stream.Read(memo, 0, memo.Length);
memo.AsSpan(0, br).CopyTo(this._buff[this._pos..].Span);
- this._pos += br;
}
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(memo);
+ }
+
+ this._pos += (int)stream.Length;
}
- finally
+
+ /// <summary>
+ /// Writes the stream unseekable.
+ /// </summary>
+ /// <param name="stream">The stream.</param>
+ private void WriteStreamUnseekable(Stream stream)
{
- ArrayPool<byte>.Shared.Return(memo);
+ var memo = ArrayPool<byte>.Shared.Rent(4096);
+ try
+ {
+ var br = 0;
+ while ((br = stream.Read(memo, 0, memo.Length)) != 0)
+ {
+ this.EnsureSize(this._pos + br);
+ memo.AsSpan(0, br).CopyTo(this._buff[this._pos..].Span);
+ this._pos += br;
+ }
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(memo);
+ }
}
- }
- /// <inheritdoc />
- public bool Read(Span<T> destination, ulong source, out int itemsWritten)
- {
- itemsWritten = 0;
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
+ /// <inheritdoc />
+ public bool Read(Span<T> destination, ulong source, out int itemsWritten)
+ {
+ itemsWritten = 0;
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- source *= (ulong)this._itemSize;
- if (source > this.Count)
- throw new ArgumentOutOfRangeException(nameof(source), "Cannot copy data from beyond the buffer.");
+ source *= (ulong)this._itemSize;
+ if (source > this.Count)
+ throw new ArgumentOutOfRangeException(nameof(source), "Cannot copy data from beyond the buffer.");
- var start = (int)source;
- var sbuff = this._buff[start..this._pos ].Span;
- var dbuff = MemoryMarshal.AsBytes(destination);
- if (sbuff.Length > dbuff.Length)
- sbuff = sbuff[..dbuff.Length];
+ var start = (int)source;
+ var sbuff = this._buff[start..this._pos ].Span;
+ var dbuff = MemoryMarshal.AsBytes(destination);
+ if (sbuff.Length > dbuff.Length)
+ sbuff = sbuff[..dbuff.Length];
- itemsWritten = sbuff.Length / this._itemSize;
- sbuff.CopyTo(dbuff);
+ itemsWritten = sbuff.Length / this._itemSize;
+ sbuff.CopyTo(dbuff);
- return this.Length - source != (ulong)itemsWritten;
- }
+ return this.Length - source != (ulong)itemsWritten;
+ }
- /// <inheritdoc />
- public bool Read(T[] data, int start, int count, ulong source, out int itemsWritten)
- => this.Read(data.AsSpan(start, count), source, out itemsWritten);
+ /// <inheritdoc />
+ public bool Read(T[] data, int start, int count, ulong source, out int itemsWritten)
+ => this.Read(data.AsSpan(start, count), source, out itemsWritten);
- /// <inheritdoc />
- public bool Read(ArraySegment<T> data, ulong source, out int itemsWritten)
- => this.Read(data.AsSpan(), source, out itemsWritten);
+ /// <inheritdoc />
+ public bool Read(ArraySegment<T> data, ulong source, out int itemsWritten)
+ => this.Read(data.AsSpan(), source, out itemsWritten);
- /// <inheritdoc />
- public T[] ToArray()
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
+ /// <inheritdoc />
+ public T[] ToArray()
+ {
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- return MemoryMarshal.Cast<byte, T>(this._buff[..this._pos].Span).ToArray();
- }
+ return MemoryMarshal.Cast<byte, T>(this._buff[..this._pos].Span).ToArray();
+ }
- /// <inheritdoc />
- public void CopyTo(Stream destination)
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
+ /// <inheritdoc />
+ public void CopyTo(Stream destination)
+ {
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- var buff = this._buff[..this._pos].ToArray();
- destination.Write(buff, 0, buff.Length);
- }
+ var buff = this._buff[..this._pos].ToArray();
+ destination.Write(buff, 0, buff.Length);
+ }
- /// <inheritdoc />
- public void Clear()
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
+ /// <inheritdoc />
+ public void Clear()
+ {
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- this._pos = 0;
- }
+ this._pos = 0;
+ }
- /// <summary>
- /// Disposes of any resources claimed by this buffer.
- /// </summary>
- public void Dispose()
- {
- if (this._isDisposed)
- return;
+ /// <summary>
+ /// Disposes of any resources claimed by this buffer.
+ /// </summary>
+ public void Dispose()
+ {
+ if (this._isDisposed)
+ return;
- this._isDisposed = true;
- if (this._clear)
- this._buff.Span.Clear();
+ this._isDisposed = true;
+ if (this._clear)
+ this._buff.Span.Clear();
- this._buffOwner.Dispose();
- this._buff = default;
- }
+ this._buffOwner.Dispose();
+ this._buff = default;
+ }
- /// <summary>
- /// Ensures the size.
- /// </summary>
- /// <param name="newCapacity">The new capacity.</param>
- private void EnsureSize(int newCapacity)
- {
- var cap = this._buff.Length;
- if (cap >= newCapacity)
- return;
+ /// <summary>
+ /// Ensures the size.
+ /// </summary>
+ /// <param name="newCapacity">The new capacity.</param>
+ private void EnsureSize(int newCapacity)
+ {
+ var cap = this._buff.Length;
+ if (cap >= newCapacity)
+ return;
- var factor = newCapacity / cap;
- if (newCapacity % cap != 0)
- ++factor;
+ var factor = newCapacity / cap;
+ if (newCapacity % cap != 0)
+ ++factor;
- var newActualCapacity = cap * factor;
+ var newActualCapacity = cap * factor;
- var newBuffOwner = this._pool.Rent(newActualCapacity);
- var newBuff = newBuffOwner.Memory;
+ var newBuffOwner = this._pool.Rent(newActualCapacity);
+ var newBuff = newBuffOwner.Memory;
- this._buff.Span.CopyTo(newBuff.Span);
- if (this._clear)
- this._buff.Span.Clear();
+ this._buff.Span.CopyTo(newBuff.Span);
+ if (this._clear)
+ this._buff.Span.Clear();
- this._buffOwner.Dispose();
- this._buffOwner = newBuffOwner;
- this._buff = newBuff;
+ this._buffOwner.Dispose();
+ this._buffOwner = newBuffOwner;
+ this._buff = newBuff;
+ }
}
}
diff --git a/DisCatSharp.Common/Types/IMemoryBuffer.cs b/DisCatSharp.Common/Types/IMemoryBuffer.cs
index 7896a4bd5..d498b4816 100644
--- a/DisCatSharp.Common/Types/IMemoryBuffer.cs
+++ b/DisCatSharp.Common/Types/IMemoryBuffer.cs
@@ -1,126 +1,127 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
-namespace DisCatSharp.Common.Types;
-
-/// <summary>
-/// An interface describing the API of resizable memory buffers, such as <see cref="MemoryBuffer{T}"/> and <see cref="ContinuousMemoryBuffer{T}"/>.
-/// </summary>
-/// <typeparam name="T">Type of item to hold in the buffer.</typeparam>
-public interface IMemoryBuffer<T> : IDisposable where T : unmanaged
+namespace DisCatSharp.Common.Types
{
/// <summary>
- /// Gets the total capacity of this buffer. The capacity is the number of segments allocated, multiplied by size of individual segment.
+ /// An interface describing the API of resizable memory buffers, such as <see cref="MemoryBuffer{T}"/> and <see cref="ContinuousMemoryBuffer{T}"/>.
/// </summary>
- ulong Capacity { get; }
+ /// <typeparam name="T">Type of item to hold in the buffer.</typeparam>
+ public interface IMemoryBuffer<T> : IDisposable where T : unmanaged
+ {
+ /// <summary>
+ /// Gets the total capacity of this buffer. The capacity is the number of segments allocated, multiplied by size of individual segment.
+ /// </summary>
+ ulong Capacity { get; }
- /// <summary>
- /// Gets the amount of bytes currently written to the buffer. This number is never greater than <see cref="Capacity"/>.
- /// </summary>
- ulong Length { get; }
+ /// <summary>
+ /// Gets the amount of bytes currently written to the buffer. This number is never greater than <see cref="Capacity"/>.
+ /// </summary>
+ ulong Length { get; }
- /// <summary>
- /// Gets the number of items currently written to the buffer. This number is equal to <see cref="Count"/> divided by size of <typeparamref name="T"/>.
- /// </summary>
- ulong Count { get; }
+ /// <summary>
+ /// Gets the number of items currently written to the buffer. This number is equal to <see cref="Count"/> divided by size of <typeparamref name="T"/>.
+ /// </summary>
+ ulong Count { get; }
- /// <summary>
- /// Appends data from a supplied buffer to this buffer, growing it if necessary.
- /// </summary>
- /// <param name="data">Buffer containing data to write.</param>
- void Write(ReadOnlySpan<T> data);
+ /// <summary>
+ /// Appends data from a supplied buffer to this buffer, growing it if necessary.
+ /// </summary>
+ /// <param name="data">Buffer containing data to write.</param>
+ void Write(ReadOnlySpan<T> data);
- /// <summary>
- /// Appends data from a supplied array to this buffer, growing it if necessary.
- /// </summary>
- /// <param name="data">Array containing data to write.</param>
- /// <param name="start">Index from which to start reading the data.</param>
- /// <param name="count">Number of bytes to read from the source.</param>
- void Write(T[] data, int start, int count);
+ /// <summary>
+ /// Appends data from a supplied array to this buffer, growing it if necessary.
+ /// </summary>
+ /// <param name="data">Array containing data to write.</param>
+ /// <param name="start">Index from which to start reading the data.</param>
+ /// <param name="count">Number of bytes to read from the source.</param>
+ void Write(T[] data, int start, int count);
- /// <summary>
- /// Appends data from a supplied array slice to this buffer, growing it if necessary.
- /// </summary>
- /// <param name="data">Array slice containing data to write.</param>
- void Write(ArraySegment<T> data);
+ /// <summary>
+ /// Appends data from a supplied array slice to this buffer, growing it if necessary.
+ /// </summary>
+ /// <param name="data">Array slice containing data to write.</param>
+ void Write(ArraySegment<T> data);
- /// <summary>
- /// Appends data from a supplied stream to this buffer, growing it if necessary.
- /// </summary>
- /// <param name="stream">Stream to copy data from.</param>
- void Write(Stream stream);
+ /// <summary>
+ /// Appends data from a supplied stream to this buffer, growing it if necessary.
+ /// </summary>
+ /// <param name="stream">Stream to copy data from.</param>
+ void Write(Stream stream);
- /// <summary>
- /// Reads data from this buffer to the specified destination buffer. This method will write either as many
- /// bytes as there are in the destination buffer, or however many bytes are available in this buffer,
- /// whichever is less.
- /// </summary>
- /// <param name="destination">Buffer to read the data from this buffer into.</param>
- /// <param name="source">Starting position in this buffer to read from.</param>
- /// <param name="itemsWritten">Number of items written to the destination buffer.</param>
- /// <returns>Whether more data is available in this buffer.</returns>
- bool Read(Span<T> destination, ulong source, out int itemsWritten);
+ /// <summary>
+ /// Reads data from this buffer to the specified destination buffer. This method will write either as many
+ /// bytes as there are in the destination buffer, or however many bytes are available in this buffer,
+ /// whichever is less.
+ /// </summary>
+ /// <param name="destination">Buffer to read the data from this buffer into.</param>
+ /// <param name="source">Starting position in this buffer to read from.</param>
+ /// <param name="itemsWritten">Number of items written to the destination buffer.</param>
+ /// <returns>Whether more data is available in this buffer.</returns>
+ bool Read(Span<T> destination, ulong source, out int itemsWritten);
- /// <summary>
- /// Reads data from this buffer to specified destination array. This method will write either as many bytes
- /// as specified for the destination array, or however many bytes are available in this buffer, whichever is
- /// less.
- /// </summary>
- /// <param name="data">Array to read the data from this buffer into.</param>
- /// <param name="start">Starting position in the target array to write to.</param>
- /// <param name="count">Maximum number of bytes to write to target array.</param>
- /// <param name="source">Starting position in this buffer to read from.</param>
- /// <param name="itemsWritten">Number of items written to the destination buffer.</param>
- /// <returns>Whether more data is available in this buffer.</returns>
- bool Read(T[] data, int start, int count, ulong source, out int itemsWritten);
+ /// <summary>
+ /// Reads data from this buffer to specified destination array. This method will write either as many bytes
+ /// as specified for the destination array, or however many bytes are available in this buffer, whichever is
+ /// less.
+ /// </summary>
+ /// <param name="data">Array to read the data from this buffer into.</param>
+ /// <param name="start">Starting position in the target array to write to.</param>
+ /// <param name="count">Maximum number of bytes to write to target array.</param>
+ /// <param name="source">Starting position in this buffer to read from.</param>
+ /// <param name="itemsWritten">Number of items written to the destination buffer.</param>
+ /// <returns>Whether more data is available in this buffer.</returns>
+ bool Read(T[] data, int start, int count, ulong source, out int itemsWritten);
- /// <summary>
- /// Reads data from this buffer to specified destination array slice. This method will write either as many
- /// bytes as specified in the target slice, or however many bytes are available in this buffer, whichever is
- /// less.
- /// </summary>
- /// <param name="data"></param>
- /// <param name="source"></param>
- /// <param name="itemsWritten">Number of items written to the destination buffer.</param>
- /// <returns>Whether more data is available in this buffer.</returns>
- bool Read(ArraySegment<T> data, ulong source, out int itemsWritten);
+ /// <summary>
+ /// Reads data from this buffer to specified destination array slice. This method will write either as many
+ /// bytes as specified in the target slice, or however many bytes are available in this buffer, whichever is
+ /// less.
+ /// </summary>
+ /// <param name="data"></param>
+ /// <param name="source"></param>
+ /// <param name="itemsWritten">Number of items written to the destination buffer.</param>
+ /// <returns>Whether more data is available in this buffer.</returns>
+ bool Read(ArraySegment<T> data, ulong source, out int itemsWritten);
- /// <summary>
- /// Converts this buffer into a single continuous byte array.
- /// </summary>
- /// <returns>Converted byte array.</returns>
- T[] ToArray();
+ /// <summary>
+ /// Converts this buffer into a single continuous byte array.
+ /// </summary>
+ /// <returns>Converted byte array.</returns>
+ T[] ToArray();
- /// <summary>
- /// Copies all the data from this buffer to a stream.
- /// </summary>
- /// <param name="destination">Stream to copy this buffer's data to.</param>
- void CopyTo(Stream destination);
+ /// <summary>
+ /// Copies all the data from this buffer to a stream.
+ /// </summary>
+ /// <param name="destination">Stream to copy this buffer's data to.</param>
+ void CopyTo(Stream destination);
- /// <summary>
- /// Resets the buffer's pointer to the beginning, allowing for reuse.
- /// </summary>
- void Clear();
+ /// <summary>
+ /// Resets the buffer's pointer to the beginning, allowing for reuse.
+ /// </summary>
+ void Clear();
+ }
}
diff --git a/DisCatSharp.Common/Types/LinqMethods.cs b/DisCatSharp.Common/Types/LinqMethods.cs
index 43c0e2069..576afe2cc 100644
--- a/DisCatSharp.Common/Types/LinqMethods.cs
+++ b/DisCatSharp.Common/Types/LinqMethods.cs
@@ -1,78 +1,79 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
-namespace DisCatSharp.Common;
-
-/// <summary>
-/// Various Methods for Linq
-/// </summary>
-public static class LinqMethods
+namespace DisCatSharp.Common
{
/// <summary>
- /// Safely tries to get the first match out of a list.
+ /// Various Methods for Linq
/// </summary>
- /// <typeparam name="TSource">Value type of list.</typeparam>
- /// <param name="list">The list to use.</param>
- /// <param name="predicate">The predicate.</param>
- /// <param name="value">The value to get if succeeded</param>
- /// <returns>Whether a value was found.</returns>
-#nullable enable
- public static bool GetFirstValueWhere<TSource>(this List<TSource?>? list, Func<TSource?, bool> predicate, out TSource? value)
+ public static class LinqMethods
{
- if (list == null || !list.Any())
+ /// <summary>
+ /// Safely tries to get the first match out of a list.
+ /// </summary>
+ /// <typeparam name="TSource">Value type of list.</typeparam>
+ /// <param name="list">The list to use.</param>
+ /// <param name="predicate">The predicate.</param>
+ /// <param name="value">The value to get if succeeded</param>
+ /// <returns>Whether a value was found.</returns>
+#nullable enable
+ public static bool GetFirstValueWhere<TSource>(this List<TSource?>? list, Func<TSource?, bool> predicate, out TSource? value)
{
- value = default;
- return false;
- }
+ if (list == null || !list.Any())
+ {
+ value = default;
+ return false;
+ }
- value = list.Where(predicate).FirstOrDefault();
+ value = list.Where(predicate).FirstOrDefault();
- return value is not null;
- }
+ return value is not null;
+ }
#nullable disable
- /// <summary>
- /// Safely tries to extract the value of the first match where target key is found, otherwise null.
- /// </summary>
- /// <typeparam name="TKey">Key type of dictionary.</typeparam>
- /// <typeparam name="TValue">Value type of dictionary.</typeparam>
- /// <param name="dict">The dictionary to use.</param>
- /// <param name="key">The key to search for.</param>
- /// <param name="value">The value to get if succeeded.</param>
- /// <returns>Whether a value was found through the key.</returns>
+ /// <summary>
+ /// Safely tries to extract the value of the first match where target key is found, otherwise null.
+ /// </summary>
+ /// <typeparam name="TKey">Key type of dictionary.</typeparam>
+ /// <typeparam name="TValue">Value type of dictionary.</typeparam>
+ /// <param name="dict">The dictionary to use.</param>
+ /// <param name="key">The key to search for.</param>
+ /// <param name="value">The value to get if succeeded.</param>
+ /// <returns>Whether a value was found through the key.</returns>
#nullable enable
- public static bool GetFirstValueByKey<TKey, TValue>(this Dictionary<TKey?, TValue?>? dict, TKey? key, out TValue? value)
- {
- if (dict == null)
+ public static bool GetFirstValueByKey<TKey, TValue>(this Dictionary<TKey?, TValue?>? dict, TKey? key, out TValue? value)
{
- value = default;
- return false;
- }
+ if (dict == null)
+ {
+ value = default;
+ return false;
+ }
- return dict.TryGetValue(key, out value);
- }
+ return dict.TryGetValue(key, out value);
+ }
#nullable disable
+ }
}
diff --git a/DisCatSharp.Common/Types/MemoryBuffer.cs b/DisCatSharp.Common/Types/MemoryBuffer.cs
index da63266f0..aca6d3151 100644
--- a/DisCatSharp.Common/Types/MemoryBuffer.cs
+++ b/DisCatSharp.Common/Types/MemoryBuffer.cs
@@ -1,339 +1,340 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-namespace DisCatSharp.Common.Types;
-
-/// <summary>
-/// Provides a resizable memory buffer, which can be read from and written to. It will automatically resize whenever required.
-/// </summary>
-/// <typeparam name="T">Type of item to hold in the buffer.</typeparam>
-public sealed class MemoryBuffer<T> : IMemoryBuffer<T> where T : unmanaged
+namespace DisCatSharp.Common.Types
{
- /// <inheritdoc />
- public ulong Capacity => this._segments.Aggregate(0UL, (a, x) => a + (ulong)x.Memory.Length); // .Sum() does only int
-
- /// <inheritdoc />
- public ulong Length { get; private set; }
-
- /// <inheritdoc />
- public ulong Count => this.Length / (ulong)this._itemSize;
-
- private readonly MemoryPool<byte> _pool;
- private readonly int _segmentSize;
- private int _lastSegmentLength;
- private int _segNo;
- private readonly bool _clear;
- private readonly List<IMemoryOwner<byte>> _segments;
- private readonly int _itemSize;
- private bool _isDisposed;
-
/// <summary>
- /// Creates a new buffer with a specified segment size, specified number of initially-allocated segments, and supplied memory pool.
+ /// Provides a resizable memory buffer, which can be read from and written to. It will automatically resize whenever required.
/// </summary>
- /// <param name="segmentSize">Byte size of an individual segment. Defaults to 64KiB.</param>
- /// <param name="initialSegmentCount">Number of segments to allocate. Defaults to 0.</param>
- /// <param name="memPool">Memory pool to use for renting buffers. Defaults to <see cref="System.Buffers.MemoryPool{T}.Shared"/>.</param>
- /// <param name="clearOnDispose">Determines whether the underlying buffers should be cleared on exit. If dealing with sensitive data, it might be a good idea to set this option to true.</param>
- public MemoryBuffer(int segmentSize = 65536, int initialSegmentCount = 0, MemoryPool<byte> memPool = default, bool clearOnDispose = false)
+ /// <typeparam name="T">Type of item to hold in the buffer.</typeparam>
+ public sealed class MemoryBuffer<T> : IMemoryBuffer<T> where T : unmanaged
{
- this._itemSize = Unsafe.SizeOf<T>();
- if (segmentSize % this._itemSize != 0)
- throw new ArgumentException("Segment size must match size of individual item.");
-
- this._pool = memPool ?? MemoryPool<byte>.Shared;
+ /// <inheritdoc />
+ public ulong Capacity => this._segments.Aggregate(0UL, (a, x) => a + (ulong)x.Memory.Length); // .Sum() does only int
+
+ /// <inheritdoc />
+ public ulong Length { get; private set; }
+
+ /// <inheritdoc />
+ public ulong Count => this.Length / (ulong)this._itemSize;
+
+ private readonly MemoryPool<byte> _pool;
+ private readonly int _segmentSize;
+ private int _lastSegmentLength;
+ private int _segNo;
+ private readonly bool _clear;
+ private readonly List<IMemoryOwner<byte>> _segments;
+ private readonly int _itemSize;
+ private bool _isDisposed;
+
+ /// <summary>
+ /// Creates a new buffer with a specified segment size, specified number of initially-allocated segments, and supplied memory pool.
+ /// </summary>
+ /// <param name="segmentSize">Byte size of an individual segment. Defaults to 64KiB.</param>
+ /// <param name="initialSegmentCount">Number of segments to allocate. Defaults to 0.</param>
+ /// <param name="memPool">Memory pool to use for renting buffers. Defaults to <see cref="System.Buffers.MemoryPool{T}.Shared"/>.</param>
+ /// <param name="clearOnDispose">Determines whether the underlying buffers should be cleared on exit. If dealing with sensitive data, it might be a good idea to set this option to true.</param>
+ public MemoryBuffer(int segmentSize = 65536, int initialSegmentCount = 0, MemoryPool<byte> memPool = default, bool clearOnDispose = false)
+ {
+ this._itemSize = Unsafe.SizeOf<T>();
+ if (segmentSize % this._itemSize != 0)
+ throw new ArgumentException("Segment size must match size of individual item.");
- this._segmentSize = segmentSize;
- this._segNo = 0;
- this._lastSegmentLength = 0;
- this._clear = clearOnDispose;
+ this._pool = memPool ?? MemoryPool<byte>.Shared;
- this._segments = new List<IMemoryOwner<byte>>(initialSegmentCount + 1);
- for (var i = 0; i < initialSegmentCount; i++)
- this._segments.Add(this._pool.Rent(this._segmentSize));
+ this._segmentSize = segmentSize;
+ this._segNo = 0;
+ this._lastSegmentLength = 0;
+ this._clear = clearOnDispose;
- this.Length = 0;
+ this._segments = new List<IMemoryOwner<byte>>(initialSegmentCount + 1);
+ for (var i = 0; i < initialSegmentCount; i++)
+ this._segments.Add(this._pool.Rent(this._segmentSize));
- this._isDisposed = false;
- }
+ this.Length = 0;
- /// <inheritdoc />
- public void Write(ReadOnlySpan<T> data)
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
-
- var src = MemoryMarshal.AsBytes(data);
- this.Grow(src.Length);
+ this._isDisposed = false;
+ }
- while (this._segNo < this._segments.Count && src.Length > 0)
+ /// <inheritdoc />
+ public void Write(ReadOnlySpan<T> data)
{
- var seg = this._segments[this._segNo];
- var mem = seg.Memory;
- var avs = mem.Length - this._lastSegmentLength;
- avs = avs > src.Length
- ? src.Length
- : avs;
- var dmem = mem[this._lastSegmentLength..];
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- src[..avs].CopyTo(dmem.Span);
- src = src[avs..];
+ var src = MemoryMarshal.AsBytes(data);
+ this.Grow(src.Length);
- this.Length += (ulong)avs;
- this._lastSegmentLength += avs;
-
- if (this._lastSegmentLength == mem.Length)
+ while (this._segNo < this._segments.Count && src.Length > 0)
{
- this._segNo++;
- this._lastSegmentLength = 0;
+ var seg = this._segments[this._segNo];
+ var mem = seg.Memory;
+ var avs = mem.Length - this._lastSegmentLength;
+ avs = avs > src.Length
+ ? src.Length
+ : avs;
+ var dmem = mem[this._lastSegmentLength..];
+
+ src[..avs].CopyTo(dmem.Span);
+ src = src[avs..];
+
+ this.Length += (ulong)avs;
+ this._lastSegmentLength += avs;
+
+ if (this._lastSegmentLength == mem.Length)
+ {
+ this._segNo++;
+ this._lastSegmentLength = 0;
+ }
}
}
- }
- /// <inheritdoc />
- public void Write(T[] data, int start, int count)
- => this.Write(data.AsSpan(start, count));
+ /// <inheritdoc />
+ public void Write(T[] data, int start, int count)
+ => this.Write(data.AsSpan(start, count));
- /// <inheritdoc />
- public void Write(ArraySegment<T> data)
- => this.Write(data.AsSpan());
+ /// <inheritdoc />
+ public void Write(ArraySegment<T> data)
+ => this.Write(data.AsSpan());
- /// <inheritdoc />
- public void Write(Stream stream)
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
+ /// <inheritdoc />
+ public void Write(Stream stream)
+ {
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- if (stream.CanSeek)
- this.WriteStreamSeekable(stream);
- else
- this.WriteStreamUnseekable(stream);
- }
+ if (stream.CanSeek)
+ this.WriteStreamSeekable(stream);
+ else
+ this.WriteStreamUnseekable(stream);
+ }
- /// <summary>
- /// Writes the stream seekable.
- /// </summary>
- /// <param name="stream">The stream.</param>
- private void WriteStreamSeekable(Stream stream)
- {
- var len = (int)(stream.Length - stream.Position);
- this.Grow(len);
+ /// <summary>
+ /// Writes the stream seekable.
+ /// </summary>
+ /// <param name="stream">The stream.</param>
+ private void WriteStreamSeekable(Stream stream)
+ {
+ var len = (int)(stream.Length - stream.Position);
+ this.Grow(len);
- var buff = new byte[this._segmentSize];
+ var buff = new byte[this._segmentSize];
- while (this._segNo < this._segments.Count && len > 0)
- {
- var seg = this._segments[this._segNo];
- var mem = seg.Memory;
- var avs = mem.Length - this._lastSegmentLength;
- avs = avs > len
- ? len
- : avs;
- var dmem = mem[this._lastSegmentLength..];
-
- var lsl = this._lastSegmentLength;
- var slen = dmem.Span.Length - lsl;
- stream.Read(buff, 0, slen);
- buff.AsSpan(0, slen).CopyTo(dmem.Span);
- len -= dmem.Span.Length;
-
- this.Length += (ulong)avs;
- this._lastSegmentLength += avs;
-
- if (this._lastSegmentLength == mem.Length)
+ while (this._segNo < this._segments.Count && len > 0)
{
- this._segNo++;
- this._lastSegmentLength = 0;
+ var seg = this._segments[this._segNo];
+ var mem = seg.Memory;
+ var avs = mem.Length - this._lastSegmentLength;
+ avs = avs > len
+ ? len
+ : avs;
+ var dmem = mem[this._lastSegmentLength..];
+
+ var lsl = this._lastSegmentLength;
+ var slen = dmem.Span.Length - lsl;
+ stream.Read(buff, 0, slen);
+ buff.AsSpan(0, slen).CopyTo(dmem.Span);
+ len -= dmem.Span.Length;
+
+ this.Length += (ulong)avs;
+ this._lastSegmentLength += avs;
+
+ if (this._lastSegmentLength == mem.Length)
+ {
+ this._segNo++;
+ this._lastSegmentLength = 0;
+ }
}
}
- }
-
- /// <summary>
- /// Writes the stream unseekable.
- /// </summary>
- /// <param name="stream">The stream.</param>
- private void WriteStreamUnseekable(Stream stream)
- {
- var read = 0;
- var buff = new byte[this._segmentSize];
- var buffs = buff.AsSpan();
- while ((read = stream.Read(buff, 0, buff.Length - this._lastSegmentLength)) != 0)
- this.Write(MemoryMarshal.Cast<byte, T>(buffs[..read]));
- }
-
- /// <inheritdoc />
- public bool Read(Span<T> destination, ulong source, out int itemsWritten)
- {
- itemsWritten = 0;
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
-
- source *= (ulong)this._itemSize;
- if (source > this.Count)
- throw new ArgumentOutOfRangeException(nameof(source), "Cannot copy data from beyond the buffer.");
- // Find where to begin
- var i = 0;
- for (; i < this._segments.Count; i++)
+ /// <summary>
+ /// Writes the stream unseekable.
+ /// </summary>
+ /// <param name="stream">The stream.</param>
+ private void WriteStreamUnseekable(Stream stream)
{
- var seg = this._segments[i];
- var mem = seg.Memory;
- if ((ulong)mem.Length > source)
- break;
-
- source -= (ulong)mem.Length;
+ var read = 0;
+ var buff = new byte[this._segmentSize];
+ var buffs = buff.AsSpan();
+ while ((read = stream.Read(buff, 0, buff.Length - this._lastSegmentLength)) != 0)
+ this.Write(MemoryMarshal.Cast<byte, T>(buffs[..read]));
}
- // Do actual copy
- var dl = (int)(this.Length - source);
- var sri = (int)source;
- var dst = MemoryMarshal.AsBytes(destination);
- for (; i < this._segments.Count && dst.Length > 0; i++)
+ /// <inheritdoc />
+ public bool Read(Span<T> destination, ulong source, out int itemsWritten)
{
- var seg = this._segments[i];
- var mem = seg.Memory;
- var src = mem.Span;
+ itemsWritten = 0;
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
+
+ source *= (ulong)this._itemSize;
+ if (source > this.Count)
+ throw new ArgumentOutOfRangeException(nameof(source), "Cannot copy data from beyond the buffer.");
- if (sri != 0)
+ // Find where to begin
+ var i = 0;
+ for (; i < this._segments.Count; i++)
{
- src = src[sri..];
- sri = 0;
+ var seg = this._segments[i];
+ var mem = seg.Memory;
+ if ((ulong)mem.Length > source)
+ break;
+
+ source -= (ulong)mem.Length;
}
- if (itemsWritten + src.Length > dl)
- src = src[..(dl - itemsWritten)];
+ // Do actual copy
+ var dl = (int)(this.Length - source);
+ var sri = (int)source;
+ var dst = MemoryMarshal.AsBytes(destination);
+ for (; i < this._segments.Count && dst.Length > 0; i++)
+ {
+ var seg = this._segments[i];
+ var mem = seg.Memory;
+ var src = mem.Span;
- if (src.Length > dst.Length)
- src = src[..dst.Length];
+ if (sri != 0)
+ {
+ src = src[sri..];
+ sri = 0;
+ }
- src.CopyTo(dst);
- dst = dst[src.Length..];
- itemsWritten += src.Length;
- }
+ if (itemsWritten + src.Length > dl)
+ src = src[..(dl - itemsWritten)];
- itemsWritten /= this._itemSize;
- return this.Length - source != (ulong)itemsWritten;
- }
+ if (src.Length > dst.Length)
+ src = src[..dst.Length];
- /// <inheritdoc />
- public bool Read(T[] data, int start, int count, ulong source, out int itemsWritten)
- => this.Read(data.AsSpan(start, count), source, out itemsWritten);
+ src.CopyTo(dst);
+ dst = dst[src.Length..];
+ itemsWritten += src.Length;
+ }
- /// <inheritdoc />
- public bool Read(ArraySegment<T> data, ulong source, out int itemsWritten)
- => this.Read(data.AsSpan(), source, out itemsWritten);
+ itemsWritten /= this._itemSize;
+ return this.Length - source != (ulong)itemsWritten;
+ }
- /// <inheritdoc />
- public T[] ToArray()
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
+ /// <inheritdoc />
+ public bool Read(T[] data, int start, int count, ulong source, out int itemsWritten)
+ => this.Read(data.AsSpan(start, count), source, out itemsWritten);
- var bytes = new T[this.Count];
- this.Read(bytes, 0, out _);
- return bytes;
- }
+ /// <inheritdoc />
+ public bool Read(ArraySegment<T> data, ulong source, out int itemsWritten)
+ => this.Read(data.AsSpan(), source, out itemsWritten);
- /// <inheritdoc />
- public void CopyTo(Stream destination)
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
+ /// <inheritdoc />
+ public T[] ToArray()
+ {
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- var longest = this._segments.Max(x => x.Memory.Length);
- var buff = new byte[longest];
+ var bytes = new T[this.Count];
+ this.Read(bytes, 0, out _);
+ return bytes;
+ }
- foreach (var seg in this._segments)
+ /// <inheritdoc />
+ public void CopyTo(Stream destination)
{
- var mem = seg.Memory.Span;
- var spn = buff.AsSpan(0, mem.Length);
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
- mem.CopyTo(spn);
- destination.Write(buff, 0, spn.Length);
- }
- }
+ var longest = this._segments.Max(x => x.Memory.Length);
+ var buff = new byte[longest];
- /// <inheritdoc />
- public void Clear()
- {
- if (this._isDisposed)
- throw new ObjectDisposedException("This buffer is disposed.");
+ foreach (var seg in this._segments)
+ {
+ var mem = seg.Memory.Span;
+ var spn = buff.AsSpan(0, mem.Length);
- this._segNo = 0;
- this._lastSegmentLength = 0;
- this.Length = 0;
- }
+ mem.CopyTo(spn);
+ destination.Write(buff, 0, spn.Length);
+ }
+ }
- /// <summary>
- /// Disposes of any resources claimed by this buffer.
- /// </summary>
- public void Dispose()
- {
- if (this._isDisposed)
- return;
+ /// <inheritdoc />
+ public void Clear()
+ {
+ if (this._isDisposed)
+ throw new ObjectDisposedException("This buffer is disposed.");
+
+ this._segNo = 0;
+ this._lastSegmentLength = 0;
+ this.Length = 0;
+ }
- this._isDisposed = true;
- foreach (var segment in this._segments)
+ /// <summary>
+ /// Disposes of any resources claimed by this buffer.
+ /// </summary>
+ public void Dispose()
{
- if (this._clear)
- segment.Memory.Span.Clear();
+ if (this._isDisposed)
+ return;
+
+ this._isDisposed = true;
+ foreach (var segment in this._segments)
+ {
+ if (this._clear)
+ segment.Memory.Span.Clear();
- segment.Dispose();
+ segment.Dispose();
+ }
}
- }
- /// <summary>
- /// Grows the.
- /// </summary>
- /// <param name="minAmount">The min amount.</param>
- private void Grow(int minAmount)
- {
- var capacity = this.Capacity;
- var length = this.Length;
- var totalAmt = length + (ulong)minAmount;
- if (capacity >= totalAmt)
- return; // we're good
-
- var amt = (int)(totalAmt - capacity);
- var segCount = amt / this._segmentSize;
- if (amt % this._segmentSize != 0)
- segCount++;
-
- // Basically List<T>.EnsureCapacity
- // Default grow behaviour is minimum current*2
- var segCap = this._segments.Count + segCount;
- if (segCap > this._segments.Capacity)
- this._segments.Capacity = segCap < this._segments.Capacity * 2
- ? this._segments.Capacity * 2
- : segCap;
-
- for (var i = 0; i < segCount; i++)
- this._segments.Add(this._pool.Rent(this._segmentSize));
+ /// <summary>
+ /// Grows the.
+ /// </summary>
+ /// <param name="minAmount">The min amount.</param>
+ private void Grow(int minAmount)
+ {
+ var capacity = this.Capacity;
+ var length = this.Length;
+ var totalAmt = length + (ulong)minAmount;
+ if (capacity >= totalAmt)
+ return; // we're good
+
+ var amt = (int)(totalAmt - capacity);
+ var segCount = amt / this._segmentSize;
+ if (amt % this._segmentSize != 0)
+ segCount++;
+
+ // Basically List<T>.EnsureCapacity
+ // Default grow behaviour is minimum current*2
+ var segCap = this._segments.Count + segCount;
+ if (segCap > this._segments.Capacity)
+ this._segments.Capacity = segCap < this._segments.Capacity * 2
+ ? this._segments.Capacity * 2
+ : segCap;
+
+ for (var i = 0; i < segCount; i++)
+ this._segments.Add(this._pool.Rent(this._segmentSize));
+ }
}
}
diff --git a/DisCatSharp.Common/Types/SecureRandom.cs b/DisCatSharp.Common/Types/SecureRandom.cs
index 36b85b3ee..6daf056b9 100644
--- a/DisCatSharp.Common/Types/SecureRandom.cs
+++ b/DisCatSharp.Common/Types/SecureRandom.cs
@@ -1,330 +1,331 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
-namespace DisCatSharp.Common;
-
-/// <summary>
-/// Provides a cryptographically-secure pseudorandom number generator (CSPRNG) implementation compatible with <see cref="System.Random"/>.
-/// </summary>
-public sealed class SecureRandom : Random, IDisposable
+namespace DisCatSharp.Common
{
/// <summary>
- /// Gets the r n g.
- /// </summary>
- private readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
-
- private volatile bool _isDisposed;
-
- /// <summary>
- /// Creates a new instance of <see cref="SecureRandom"/>.
- /// </summary>
- public SecureRandom()
- { }
-
- /// <summary>
- /// Finalizes this <see cref="SecureRandom"/> instance by disposing it.
- /// </summary>
- ~SecureRandom()
- {
- this.Dispose();
- }
-
- /// <summary>
- /// Fills a supplied buffer with random bytes.
- /// </summary>
- /// <param name="buffer">Buffer to fill with random bytes.</param>
- public void GetBytes(byte[] buffer) => this._rng.GetBytes(buffer);
-
- /// <summary>
- /// Fills a supplied buffer with random nonzero bytes.
- /// </summary>
- /// <param name="buffer">Buffer to fill with random nonzero bytes.</param>
- public void GetNonZeroBytes(byte[] buffer) => this._rng.GetNonZeroBytes(buffer);
-
- /// <summary>
- /// Fills a supplied memory region with random bytes.
+ /// Provides a cryptographically-secure pseudorandom number generator (CSPRNG) implementation compatible with <see cref="System.Random"/>.
/// </summary>
- /// <param name="buffer">Memory region to fill with random bytes.</param>
- public void GetBytes(Span<byte> buffer)
+ public sealed class SecureRandom : Random, IDisposable
{
- var buff = ArrayPool<byte>.Shared.Rent(buffer.Length);
- try
+ /// <summary>
+ /// Gets the r n g.
+ /// </summary>
+ private readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
+
+ private volatile bool _isDisposed;
+
+ /// <summary>
+ /// Creates a new instance of <see cref="SecureRandom"/>.
+ /// </summary>
+ public SecureRandom()
+ { }
+
+ /// <summary>
+ /// Finalizes this <see cref="SecureRandom"/> instance by disposing it.
+ /// </summary>
+ ~SecureRandom()
{
- var buffSpan = buff.AsSpan(0, buffer.Length);
- this._rng.GetBytes(buff);
- buffSpan.CopyTo(buffer);
+ this.Dispose();
}
- finally
- {
- ArrayPool<byte>.Shared.Return(buff);
- }
- }
- /// <summary>
- /// Fills a supplied memory region with random nonzero bytes.
- /// </summary>
- /// <param name="buffer">Memory region to fill with random nonzero bytes.</param>
- public void GetNonZeroBytes(Span<byte> buffer)
- {
- var buff = ArrayPool<byte>.Shared.Rent(buffer.Length);
- try
+ /// <summary>
+ /// Fills a supplied buffer with random bytes.
+ /// </summary>
+ /// <param name="buffer">Buffer to fill with random bytes.</param>
+ public void GetBytes(byte[] buffer) => this._rng.GetBytes(buffer);
+
+ /// <summary>
+ /// Fills a supplied buffer with random nonzero bytes.
+ /// </summary>
+ /// <param name="buffer">Buffer to fill with random nonzero bytes.</param>
+ public void GetNonZeroBytes(byte[] buffer) => this._rng.GetNonZeroBytes(buffer);
+
+ /// <summary>
+ /// Fills a supplied memory region with random bytes.
+ /// </summary>
+ /// <param name="buffer">Memory region to fill with random bytes.</param>
+ public void GetBytes(Span<byte> buffer)
{
- var buffSpan = buff.AsSpan(0, buffer.Length);
- this._rng.GetNonZeroBytes(buff);
- buffSpan.CopyTo(buffer);
+ var buff = ArrayPool<byte>.Shared.Rent(buffer.Length);
+ try
+ {
+ var buffSpan = buff.AsSpan(0, buffer.Length);
+ this._rng.GetBytes(buff);
+ buffSpan.CopyTo(buffer);
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buff);
+ }
}
- finally
+
+ /// <summary>
+ /// Fills a supplied memory region with random nonzero bytes.
+ /// </summary>
+ /// <param name="buffer">Memory region to fill with random nonzero bytes.</param>
+ public void GetNonZeroBytes(Span<byte> buffer)
{
- ArrayPool<byte>.Shared.Return(buff);
+ var buff = ArrayPool<byte>.Shared.Rent(buffer.Length);
+ try
+ {
+ var buffSpan = buff.AsSpan(0, buffer.Length);
+ this._rng.GetNonZeroBytes(buff);
+ buffSpan.CopyTo(buffer);
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buff);
+ }
}
- }
-
- /// <summary>
- /// Generates a signed 8-bit integer within specified range.
- /// </summary>
- /// <param name="min">Minimum value to generate. Defaults to 0.</param>
- /// <param name="max">Maximum value to generate. Defaults to <see cref="sbyte.MaxValue"/>.</param>
- /// <returns>Generated random value.</returns>
- public sbyte GetInt8(sbyte min = 0, sbyte max = sbyte.MaxValue)
- {
- if (max <= min)
- throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
-
- var offset = (sbyte)(min < 0 ? -min : 0);
- min += offset;
- max += offset;
-
- return (sbyte)(Math.Abs(this.Generate<sbyte>()) % (max - min) + min - offset);
- }
-
- /// <summary>
- /// Generates a unsigned 8-bit integer within specified range.
- /// </summary>
- /// <param name="min">Minimum value to generate. Defaults to 0.</param>
- /// <param name="max">Maximum value to generate. Defaults to <see cref="byte.MaxValue"/>.</param>
- /// <returns>Generated random value.</returns>
- public byte GetUInt8(byte min = 0, byte max = byte.MaxValue)
- {
- if (max <= min)
- throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
- return (byte)(this.Generate<byte>() % (max - min) + min);
- }
-
- /// <summary>
- /// Generates a signed 16-bit integer within specified range.
- /// </summary>
- /// <param name="min">Minimum value to generate. Defaults to 0.</param>
- /// <param name="max">Maximum value to generate. Defaults to <see cref="short.MaxValue"/>.</param>
- /// <returns>Generated random value.</returns>
- public short GetInt16(short min = 0, short max = short.MaxValue)
- {
- if (max <= min)
- throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
-
- var offset = (short)(min < 0 ? -min : 0);
- min += offset;
- max += offset;
+ /// <summary>
+ /// Generates a signed 8-bit integer within specified range.
+ /// </summary>
+ /// <param name="min">Minimum value to generate. Defaults to 0.</param>
+ /// <param name="max">Maximum value to generate. Defaults to <see cref="sbyte.MaxValue"/>.</param>
+ /// <returns>Generated random value.</returns>
+ public sbyte GetInt8(sbyte min = 0, sbyte max = sbyte.MaxValue)
+ {
+ if (max <= min)
+ throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
- return (short)(Math.Abs(this.Generate<short>()) % (max - min) + min - offset);
- }
+ var offset = (sbyte)(min < 0 ? -min : 0);
+ min += offset;
+ max += offset;
- /// <summary>
- /// Generates a unsigned 16-bit integer within specified range.
- /// </summary>
- /// <param name="min">Minimum value to generate. Defaults to 0.</param>
- /// <param name="max">Maximum value to generate. Defaults to <see cref="ushort.MaxValue"/>.</param>
- /// <returns>Generated random value.</returns>
- public ushort GetUInt16(ushort min = 0, ushort max = ushort.MaxValue)
- {
- if (max <= min)
- throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
+ return (sbyte)(Math.Abs(this.Generate<sbyte>()) % (max - min) + min - offset);
+ }
- return (ushort)(this.Generate<ushort>() % (max - min) + min);
- }
+ /// <summary>
+ /// Generates a unsigned 8-bit integer within specified range.
+ /// </summary>
+ /// <param name="min">Minimum value to generate. Defaults to 0.</param>
+ /// <param name="max">Maximum value to generate. Defaults to <see cref="byte.MaxValue"/>.</param>
+ /// <returns>Generated random value.</returns>
+ public byte GetUInt8(byte min = 0, byte max = byte.MaxValue)
+ {
+ if (max <= min)
+ throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
- /// <summary>
- /// Generates a signed 32-bit integer within specified range.
- /// </summary>
- /// <param name="min">Minimum value to generate. Defaults to 0.</param>
- /// <param name="max">Maximum value to generate. Defaults to <see cref="int.MaxValue"/>.</param>
- /// <returns>Generated random value.</returns>
- public int GetInt32(int min = 0, int max = int.MaxValue)
- {
- if (max <= min)
- throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
+ return (byte)(this.Generate<byte>() % (max - min) + min);
+ }
- var offset = min < 0 ? -min : 0;
- min += offset;
- max += offset;
+ /// <summary>
+ /// Generates a signed 16-bit integer within specified range.
+ /// </summary>
+ /// <param name="min">Minimum value to generate. Defaults to 0.</param>
+ /// <param name="max">Maximum value to generate. Defaults to <see cref="short.MaxValue"/>.</param>
+ /// <returns>Generated random value.</returns>
+ public short GetInt16(short min = 0, short max = short.MaxValue)
+ {
+ if (max <= min)
+ throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
- return Math.Abs(this.Generate<int>()) % (max - min) + min - offset;
- }
+ var offset = (short)(min < 0 ? -min : 0);
+ min += offset;
+ max += offset;
- /// <summary>
- /// Generates a unsigned 32-bit integer within specified range.
- /// </summary>
- /// <param name="min">Minimum value to generate. Defaults to 0.</param>
- /// <param name="max">Maximum value to generate. Defaults to <see cref="uint.MaxValue"/>.</param>
- /// <returns>Generated random value.</returns>
- public uint GetUInt32(uint min = 0, uint max = uint.MaxValue)
- {
- if (max <= min)
- throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
+ return (short)(Math.Abs(this.Generate<short>()) % (max - min) + min - offset);
+ }
- return this.Generate<uint>() % (max - min) + min;
- }
+ /// <summary>
+ /// Generates a unsigned 16-bit integer within specified range.
+ /// </summary>
+ /// <param name="min">Minimum value to generate. Defaults to 0.</param>
+ /// <param name="max">Maximum value to generate. Defaults to <see cref="ushort.MaxValue"/>.</param>
+ /// <returns>Generated random value.</returns>
+ public ushort GetUInt16(ushort min = 0, ushort max = ushort.MaxValue)
+ {
+ if (max <= min)
+ throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
- /// <summary>
- /// Generates a signed 64-bit integer within specified range.
- /// </summary>
- /// <param name="min">Minimum value to generate. Defaults to 0.</param>
- /// <param name="max">Maximum value to generate. Defaults to <see cref="long.MaxValue"/>.</param>
- /// <returns>Generated random value.</returns>
- public long GetInt64(long min = 0, long max = long.MaxValue)
- {
- if (max <= min)
- throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
+ return (ushort)(this.Generate<ushort>() % (max - min) + min);
+ }
- var offset = min < 0 ? -min : 0;
- min += offset;
- max += offset;
+ /// <summary>
+ /// Generates a signed 32-bit integer within specified range.
+ /// </summary>
+ /// <param name="min">Minimum value to generate. Defaults to 0.</param>
+ /// <param name="max">Maximum value to generate. Defaults to <see cref="int.MaxValue"/>.</param>
+ /// <returns>Generated random value.</returns>
+ public int GetInt32(int min = 0, int max = int.MaxValue)
+ {
+ if (max <= min)
+ throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
- return Math.Abs(this.Generate<long>()) % (max - min) + min - offset;
- }
+ var offset = min < 0 ? -min : 0;
+ min += offset;
+ max += offset;
- /// <summary>
- /// Generates a unsigned 64-bit integer within specified range.
- /// </summary>
- /// <param name="min">Minimum value to generate. Defaults to 0.</param>
- /// <param name="max">Maximum value to generate. Defaults to <see cref="ulong.MaxValue"/>.</param>
- /// <returns>Generated random value.</returns>
- public ulong GetUInt64(ulong min = 0, ulong max = ulong.MaxValue)
- {
- if (max <= min)
- throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
+ return Math.Abs(this.Generate<int>()) % (max - min) + min - offset;
+ }
- return this.Generate<ulong>() % (max - min) + min;
- }
+ /// <summary>
+ /// Generates a unsigned 32-bit integer within specified range.
+ /// </summary>
+ /// <param name="min">Minimum value to generate. Defaults to 0.</param>
+ /// <param name="max">Maximum value to generate. Defaults to <see cref="uint.MaxValue"/>.</param>
+ /// <returns>Generated random value.</returns>
+ public uint GetUInt32(uint min = 0, uint max = uint.MaxValue)
+ {
+ if (max <= min)
+ throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
- /// <summary>
- /// Generates a 32-bit floating-point number between 0.0 and 1.0.
- /// </summary>
- /// <returns>Generated 32-bit floating-point number.</returns>
- public float GetSingle()
- {
- var (i1, i2) = ((float)this.GetInt32(), (float)this.GetInt32());
- return i1 / i2 % 1.0F;
- }
+ return this.Generate<uint>() % (max - min) + min;
+ }
- /// <summary>
- /// Generates a 64-bit floating-point number between 0.0 and 1.0.
- /// </summary>
- /// <returns>Generated 64-bit floating-point number.</returns>
- public double GetDouble()
- {
- var (i1, i2) = ((double)this.GetInt64(), (double)this.GetInt64());
- return i1 / i2 % 1.0;
- }
+ /// <summary>
+ /// Generates a signed 64-bit integer within specified range.
+ /// </summary>
+ /// <param name="min">Minimum value to generate. Defaults to 0.</param>
+ /// <param name="max">Maximum value to generate. Defaults to <see cref="long.MaxValue"/>.</param>
+ /// <returns>Generated random value.</returns>
+ public long GetInt64(long min = 0, long max = long.MaxValue)
+ {
+ if (max <= min)
+ throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
- /// <summary>
- /// Generates a 32-bit integer between 0 and <see cref="int.MaxValue"/>. Upper end exclusive.
- /// </summary>
- /// <returns>Generated 32-bit integer.</returns>
- public override int Next()
- => this.GetInt32();
+ var offset = min < 0 ? -min : 0;
+ min += offset;
+ max += offset;
- /// <summary>
- /// Generates a 32-bit integer between 0 and <paramref name="maxValue"/>. Upper end exclusive.
- /// </summary>
- /// <param name="maxValue">Maximum value of the generated integer.</param>
- /// <returns>Generated 32-bit integer.</returns>
- public override int Next(int maxValue)
- => this.GetInt32(0, maxValue);
-
- /// <summary>
- /// Generates a 32-bit integer between <paramref name="minValue"/> and <paramref name="maxValue"/>. Upper end exclusive.
- /// </summary>
- /// <param name="minValue">Minimum value of the generate integer.</param>
- /// <param name="maxValue">Maximum value of the generated integer.</param>
- /// <returns>Generated 32-bit integer.</returns>
- public override int Next(int minValue, int maxValue)
- => this.GetInt32(minValue, maxValue);
+ return Math.Abs(this.Generate<long>()) % (max - min) + min - offset;
+ }
- /// <summary>
- /// Generates a 64-bit floating-point number between 0.0 and 1.0. Upper end exclusive.
- /// </summary>
- /// <returns>Generated 64-bit floating-point number.</returns>
- public override double NextDouble()
- => this.GetDouble();
+ /// <summary>
+ /// Generates a unsigned 64-bit integer within specified range.
+ /// </summary>
+ /// <param name="min">Minimum value to generate. Defaults to 0.</param>
+ /// <param name="max">Maximum value to generate. Defaults to <see cref="ulong.MaxValue"/>.</param>
+ /// <returns>Generated random value.</returns>
+ public ulong GetUInt64(ulong min = 0, ulong max = ulong.MaxValue)
+ {
+ if (max <= min)
+ throw new ArgumentException("Maximum needs to be greater than minimum.", nameof(max));
- /// <summary>
- /// Fills specified buffer with random bytes.
- /// </summary>
- /// <param name="buffer">Buffer to fill with bytes.</param>
- public override void NextBytes(byte[] buffer)
- => this.GetBytes(buffer);
+ return this.Generate<ulong>() % (max - min) + min;
+ }
- /// <summary>
- /// Fills specified memory region with random bytes.
- /// </summary>
- /// <param name="buffer">Memory region to fill with bytes.</param>
- public new void NextBytes(Span<byte> buffer)
- => this.GetBytes(buffer);
+ /// <summary>
+ /// Generates a 32-bit floating-point number between 0.0 and 1.0.
+ /// </summary>
+ /// <returns>Generated 32-bit floating-point number.</returns>
+ public float GetSingle()
+ {
+ var (i1, i2) = ((float)this.GetInt32(), (float)this.GetInt32());
+ return i1 / i2 % 1.0F;
+ }
- /// <summary>
- /// Disposes this <see cref="SecureRandom"/> instance and its resources.
- /// </summary>
- public void Dispose()
- {
- if (this._isDisposed)
- return;
+ /// <summary>
+ /// Generates a 64-bit floating-point number between 0.0 and 1.0.
+ /// </summary>
+ /// <returns>Generated 64-bit floating-point number.</returns>
+ public double GetDouble()
+ {
+ var (i1, i2) = ((double)this.GetInt64(), (double)this.GetInt64());
+ return i1 / i2 % 1.0;
+ }
- this._isDisposed = true;
- this._rng.Dispose();
- }
+ /// <summary>
+ /// Generates a 32-bit integer between 0 and <see cref="int.MaxValue"/>. Upper end exclusive.
+ /// </summary>
+ /// <returns>Generated 32-bit integer.</returns>
+ public override int Next()
+ => this.GetInt32();
+
+ /// <summary>
+ /// Generates a 32-bit integer between 0 and <paramref name="maxValue"/>. Upper end exclusive.
+ /// </summary>
+ /// <param name="maxValue">Maximum value of the generated integer.</param>
+ /// <returns>Generated 32-bit integer.</returns>
+ public override int Next(int maxValue)
+ => this.GetInt32(0, maxValue);
+
+ /// <summary>
+ /// Generates a 32-bit integer between <paramref name="minValue"/> and <paramref name="maxValue"/>. Upper end exclusive.
+ /// </summary>
+ /// <param name="minValue">Minimum value of the generate integer.</param>
+ /// <param name="maxValue">Maximum value of the generated integer.</param>
+ /// <returns>Generated 32-bit integer.</returns>
+ public override int Next(int minValue, int maxValue)
+ => this.GetInt32(minValue, maxValue);
+
+ /// <summary>
+ /// Generates a 64-bit floating-point number between 0.0 and 1.0. Upper end exclusive.
+ /// </summary>
+ /// <returns>Generated 64-bit floating-point number.</returns>
+ public override double NextDouble()
+ => this.GetDouble();
+
+ /// <summary>
+ /// Fills specified buffer with random bytes.
+ /// </summary>
+ /// <param name="buffer">Buffer to fill with bytes.</param>
+ public override void NextBytes(byte[] buffer)
+ => this.GetBytes(buffer);
+
+ /// <summary>
+ /// Fills specified memory region with random bytes.
+ /// </summary>
+ /// <param name="buffer">Memory region to fill with bytes.</param>
+ public new void NextBytes(Span<byte> buffer)
+ => this.GetBytes(buffer);
+
+ /// <summary>
+ /// Disposes this <see cref="SecureRandom"/> instance and its resources.
+ /// </summary>
+ public void Dispose()
+ {
+ if (this._isDisposed)
+ return;
- /// <summary>
- /// Generates a random 64-bit floating-point number between 0.0 and 1.0. Upper end exclusive.
- /// </summary>
- /// <returns>Generated 64-bit floating-point number.</returns>
- protected override double Sample()
- => this.GetDouble();
+ this._isDisposed = true;
+ this._rng.Dispose();
+ }
- /// <summary>
- /// Generates the.
- /// </summary>
- /// <returns>A T.</returns>
- private T Generate<T>() where T : struct
- {
- var size = Unsafe.SizeOf<T>();
- Span<byte> buff = stackalloc byte[size];
- this.GetBytes(buff);
- return MemoryMarshal.Read<T>(buff);
+ /// <summary>
+ /// Generates a random 64-bit floating-point number between 0.0 and 1.0. Upper end exclusive.
+ /// </summary>
+ /// <returns>Generated 64-bit floating-point number.</returns>
+ protected override double Sample()
+ => this.GetDouble();
+
+ /// <summary>
+ /// Generates the.
+ /// </summary>
+ /// <returns>A T.</returns>
+ private T Generate<T>() where T : struct
+ {
+ var size = Unsafe.SizeOf<T>();
+ Span<byte> buff = stackalloc byte[size];
+ this.GetBytes(buff);
+ return MemoryMarshal.Read<T>(buff);
+ }
}
}
diff --git a/DisCatSharp.Common/Types/Serialization/ComplexDecomposer.cs b/DisCatSharp.Common/Types/Serialization/ComplexDecomposer.cs
index 16764cb8e..0878380cd 100644
--- a/DisCatSharp.Common/Types/Serialization/ComplexDecomposer.cs
+++ b/DisCatSharp.Common/Types/Serialization/ComplexDecomposer.cs
@@ -1,115 +1,116 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Numerics;
-namespace DisCatSharp.Common.Serialization;
-
-/// <summary>
-/// Decomposes <see cref="System.Numerics.Complex"/> numbers into tuples (arrays of 2).
-/// </summary>
-public sealed class ComplexDecomposer : IDecomposer
+namespace DisCatSharp.Common.Serialization
{
/// <summary>
- /// Gets the t complex.
- /// </summary>
- private static Type s_complex { get; } = typeof(Complex);
- /// <summary>
- /// Gets the t double array.
- /// </summary>
- private static Type s_doubleArray { get; } = typeof(double[]);
- /// <summary>
- /// Gets the t double enumerable.
- /// </summary>
- private static Type s_doubleEnumerable { get; } = typeof(IEnumerable<double>);
- /// <summary>
- /// Gets the t object array.
- /// </summary>
- private static Type s_objectArray { get; } = typeof(object[]);
- /// <summary>
- /// Gets the t object enumerable.
+ /// Decomposes <see cref="System.Numerics.Complex"/> numbers into tuples (arrays of 2).
/// </summary>
- private static Type s_objectEnumerable { get; } = typeof(IEnumerable<object>);
-
- /// <inheritdoc />
- public bool CanDecompose(Type t)
- => t == s_complex;
-
- /// <inheritdoc />
- public bool CanRecompose(Type t)
- => t == s_doubleArray
- || t == s_objectArray
- || s_doubleEnumerable.IsAssignableFrom(t)
- || s_objectEnumerable.IsAssignableFrom(t);
-
- /// <inheritdoc />
- public bool TryDecompose(object obj, Type tobj, out object decomposed, out Type tdecomposed)
+ public sealed class ComplexDecomposer : IDecomposer
{
- decomposed = null;
- tdecomposed = s_doubleArray;
-
- if (tobj != s_complex || obj is not Complex c)
- return false;
-
- decomposed = new[] { c.Real, c.Imaginary };
- return true;
- }
-
- /// <inheritdoc />
- public bool TryRecompose(object obj, Type tobj, Type trecomposed, out object recomposed)
- {
- recomposed = null;
-
- if (trecomposed != s_complex)
- return false;
-
- // ie<double>
- if (s_doubleEnumerable.IsAssignableFrom(tobj) && obj is IEnumerable<double> ied)
+ /// <summary>
+ /// Gets the t complex.
+ /// </summary>
+ private static Type s_complex { get; } = typeof(Complex);
+ /// <summary>
+ /// Gets the t double array.
+ /// </summary>
+ private static Type s_doubleArray { get; } = typeof(double[]);
+ /// <summary>
+ /// Gets the t double enumerable.
+ /// </summary>
+ private static Type s_doubleEnumerable { get; } = typeof(IEnumerable<double>);
+ /// <summary>
+ /// Gets the t object array.
+ /// </summary>
+ private static Type s_objectArray { get; } = typeof(object[]);
+ /// <summary>
+ /// Gets the t object enumerable.
+ /// </summary>
+ private static Type s_objectEnumerable { get; } = typeof(IEnumerable<object>);
+
+ /// <inheritdoc />
+ public bool CanDecompose(Type t)
+ => t == s_complex;
+
+ /// <inheritdoc />
+ public bool CanRecompose(Type t)
+ => t == s_doubleArray
+ || t == s_objectArray
+ || s_doubleEnumerable.IsAssignableFrom(t)
+ || s_objectEnumerable.IsAssignableFrom(t);
+
+ /// <inheritdoc />
+ public bool TryDecompose(object obj, Type tobj, out object decomposed, out Type tdecomposed)
{
- if (!ied.TryFirstTwo(out var values))
+ decomposed = null;
+ tdecomposed = s_doubleArray;
+
+ if (tobj != s_complex || obj is not Complex c)
return false;
- var (real, imag) = values;
- recomposed = new Complex(real, imag);
+ decomposed = new[] { c.Real, c.Imaginary };
return true;
}
- // ie<obj>
- if (s_objectEnumerable.IsAssignableFrom(tobj) && obj is IEnumerable<object> ieo)
+ /// <inheritdoc />
+ public bool TryRecompose(object obj, Type tobj, Type trecomposed, out object recomposed)
{
- if (!ieo.TryFirstTwo(out var values))
- return false;
+ recomposed = null;
- var (real, imag) = values;
- if (real is not double dreal || imag is not double dimag)
+ if (trecomposed != s_complex)
return false;
- recomposed = new Complex(dreal, dimag);
- return true;
- }
+ // ie<double>
+ if (s_doubleEnumerable.IsAssignableFrom(tobj) && obj is IEnumerable<double> ied)
+ {
+ if (!ied.TryFirstTwo(out var values))
+ return false;
+
+ var (real, imag) = values;
+ recomposed = new Complex(real, imag);
+ return true;
+ }
+
+ // ie<obj>
+ if (s_objectEnumerable.IsAssignableFrom(tobj) && obj is IEnumerable<object> ieo)
+ {
+ if (!ieo.TryFirstTwo(out var values))
+ return false;
+
+ var (real, imag) = values;
+ if (real is not double dreal || imag is not double dimag)
+ return false;
- return false;
+ recomposed = new Complex(dreal, dimag);
+ return true;
+ }
+
+ return false;
+ }
}
}
diff --git a/DisCatSharp.Common/Types/Serialization/IDecomposer.cs b/DisCatSharp.Common/Types/Serialization/IDecomposer.cs
index 7c4519421..e419f9d06 100644
--- a/DisCatSharp.Common/Types/Serialization/IDecomposer.cs
+++ b/DisCatSharp.Common/Types/Serialization/IDecomposer.cs
@@ -1,66 +1,67 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Serialization;
-
-/// <summary>
-/// Provides an interface to decompose an object into another object or combination of objects.
-/// </summary>
-public interface IDecomposer
+namespace DisCatSharp.Common.Serialization
{
/// <summary>
- /// Checks whether the decomposer can decompose a specific type.
+ /// Provides an interface to decompose an object into another object or combination of objects.
/// </summary>
- /// <param name="t">Type to check.</param>
- /// <returns>Whether the decomposer can decompose a given type.</returns>
- bool CanDecompose(Type t);
+ public interface IDecomposer
+ {
+ /// <summary>
+ /// Checks whether the decomposer can decompose a specific type.
+ /// </summary>
+ /// <param name="t">Type to check.</param>
+ /// <returns>Whether the decomposer can decompose a given type.</returns>
+ bool CanDecompose(Type t);
- /// <summary>
- /// <para>Checks whether the decomposer can recompose a specific decomposed type.</para>
- /// <para>Note that while a type might be considered recomposable, other factors might prevent recomposing operation from being successful.</para>
- /// </summary>
- /// <param name="t">Decomposed type to check.</param>
- /// <returns>Whether the decomposer can decompose a given type.</returns>
- bool CanRecompose(Type t);
+ /// <summary>
+ /// <para>Checks whether the decomposer can recompose a specific decomposed type.</para>
+ /// <para>Note that while a type might be considered recomposable, other factors might prevent recomposing operation from being successful.</para>
+ /// </summary>
+ /// <param name="t">Decomposed type to check.</param>
+ /// <returns>Whether the decomposer can decompose a given type.</returns>
+ bool CanRecompose(Type t);
- /// <summary>
- /// Attempts to decompose a given object of specified source type. The operation produces the decomposed object and the type it got decomposed into.
- /// </summary>
- /// <param name="obj">Object to decompose.</param>
- /// <param name="tobj">Type to decompose.</param>
- /// <param name="decomposed">Decomposition result.</param>
- /// <param name="tdecomposed">Type of the result.</param>
- /// <returns>Whether the operation was successful.</returns>
- bool TryDecompose(object obj, Type tobj, out object decomposed, out Type tdecomposed);
+ /// <summary>
+ /// Attempts to decompose a given object of specified source type. The operation produces the decomposed object and the type it got decomposed into.
+ /// </summary>
+ /// <param name="obj">Object to decompose.</param>
+ /// <param name="tobj">Type to decompose.</param>
+ /// <param name="decomposed">Decomposition result.</param>
+ /// <param name="tdecomposed">Type of the result.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ bool TryDecompose(object obj, Type tobj, out object decomposed, out Type tdecomposed);
- /// <summary>
- /// Attempts to recompose given object of specified source type, into specified target type. The operation produces the recomposed object.
- /// </summary>
- /// <param name="obj">Object to recompose from.</param>
- /// <param name="tobj">Type of data to recompose.</param>
- /// <param name="trecomposed">Type to recompose into.</param>
- /// <param name="recomposed">Recomposition result.</param>
- /// <returns>Whether the operation was successful.</returns>
- bool TryRecompose(object obj, Type tobj, Type trecomposed, out object recomposed);
+ /// <summary>
+ /// Attempts to recompose given object of specified source type, into specified target type. The operation produces the recomposed object.
+ /// </summary>
+ /// <param name="obj">Object to recompose from.</param>
+ /// <param name="tobj">Type of data to recompose.</param>
+ /// <param name="trecomposed">Type to recompose into.</param>
+ /// <param name="recomposed">Recomposition result.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ bool TryRecompose(object obj, Type tobj, Type trecomposed, out object recomposed);
+ }
}
diff --git a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEvent.cs b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEvent.cs
index b4dedd58a..f8790f4a4 100644
--- a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEvent.cs
+++ b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEvent.cs
@@ -1,200 +1,201 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Immutable;
using System.Threading.Tasks;
-namespace DisCatSharp.Common.Utilities;
-
-/// <summary>
-/// ABC for <see cref="AsyncEvent{TSender, TArgs}"/>, allowing for using instances thereof without knowing the underlying instance's type parameters.
-/// </summary>
-public abstract class AsyncEvent
+namespace DisCatSharp.Common.Utilities
{
/// <summary>
- /// Gets the name of this event.
- /// </summary>
- public string Name { get; }
-
- /// <summary>
- /// Prevents a default instance of the <see cref="AsyncEvent"/> class from being created.
+ /// ABC for <see cref="AsyncEvent{TSender, TArgs}"/>, allowing for using instances thereof without knowing the underlying instance's type parameters.
/// </summary>
- /// <param name="name">The name.</param>
- private protected AsyncEvent(string name)
+ public abstract class AsyncEvent
{
- this.Name = name;
+ /// <summary>
+ /// Gets the name of this event.
+ /// </summary>
+ public string Name { get; }
+
+ /// <summary>
+ /// Prevents a default instance of the <see cref="AsyncEvent"/> class from being created.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ private protected AsyncEvent(string name)
+ {
+ this.Name = name;
+ }
}
-}
-
-/// <summary>
-/// Implementation of asynchronous event. The handlers of such events are executed asynchronously, but sequentially.
-/// </summary>
-/// <typeparam name="TSender">Type of the object that dispatches this event.</typeparam>
-/// <typeparam name="TArgs">Type of event argument object passed to this event's handlers.</typeparam>
-public sealed class AsyncEvent<TSender, TArgs> : AsyncEvent
- where TArgs : AsyncEventArgs
-{
- /// <summary>
- /// Gets the maximum allotted execution time for all handlers. Any event which causes the handler to time out
- /// will raise a non-fatal <see cref="AsyncEventTimeoutException{TSender, TArgs}"/>.
- /// </summary>
- public TimeSpan MaximumExecutionTime { get; }
-
- private readonly object _lock = new();
- private ImmutableArray<AsyncEventHandler<TSender, TArgs>> _handlers;
- private readonly AsyncEventExceptionHandler<TSender, TArgs> _exceptionHandler;
/// <summary>
- /// Creates a new asynchronous event with specified name and exception handler.
+ /// Implementation of asynchronous event. The handlers of such events are executed asynchronously, but sequentially.
/// </summary>
- /// <param name="name">Name of this event.</param>
- /// <param name="maxExecutionTime">Maximum handler execution time. A value of <see cref="System.TimeSpan.Zero"/> means infinite.</param>
- /// <param name="exceptionHandler">Delegate which handles exceptions caused by this event.</param>
- public AsyncEvent(string name, TimeSpan maxExecutionTime, AsyncEventExceptionHandler<TSender, TArgs> exceptionHandler)
- : base(name)
+ /// <typeparam name="TSender">Type of the object that dispatches this event.</typeparam>
+ /// <typeparam name="TArgs">Type of event argument object passed to this event's handlers.</typeparam>
+ public sealed class AsyncEvent<TSender, TArgs> : AsyncEvent
+ where TArgs : AsyncEventArgs
{
- this._handlers = ImmutableArray<AsyncEventHandler<TSender, TArgs>>.Empty;
- this._exceptionHandler = exceptionHandler;
-
- this.MaximumExecutionTime = maxExecutionTime;
- }
+ /// <summary>
+ /// Gets the maximum allotted execution time for all handlers. Any event which causes the handler to time out
+ /// will raise a non-fatal <see cref="AsyncEventTimeoutException{TSender, TArgs}"/>.
+ /// </summary>
+ public TimeSpan MaximumExecutionTime { get; }
+
+ private readonly object _lock = new();
+ private ImmutableArray<AsyncEventHandler<TSender, TArgs>> _handlers;
+ private readonly AsyncEventExceptionHandler<TSender, TArgs> _exceptionHandler;
+
+ /// <summary>
+ /// Creates a new asynchronous event with specified name and exception handler.
+ /// </summary>
+ /// <param name="name">Name of this event.</param>
+ /// <param name="maxExecutionTime">Maximum handler execution time. A value of <see cref="System.TimeSpan.Zero"/> means infinite.</param>
+ /// <param name="exceptionHandler">Delegate which handles exceptions caused by this event.</param>
+ public AsyncEvent(string name, TimeSpan maxExecutionTime, AsyncEventExceptionHandler<TSender, TArgs> exceptionHandler)
+ : base(name)
+ {
+ this._handlers = ImmutableArray<AsyncEventHandler<TSender, TArgs>>.Empty;
+ this._exceptionHandler = exceptionHandler;
- /// <summary>
- /// Registers a new handler for this event.
- /// </summary>
- /// <param name="handler">Handler to register for this event.</param>
- public void Register(AsyncEventHandler<TSender, TArgs> handler)
- {
- if (handler == null)
- throw new ArgumentNullException(nameof(handler));
+ this.MaximumExecutionTime = maxExecutionTime;
+ }
- lock (this._lock)
- this._handlers = this._handlers.Add(handler);
- }
+ /// <summary>
+ /// Registers a new handler for this event.
+ /// </summary>
+ /// <param name="handler">Handler to register for this event.</param>
+ public void Register(AsyncEventHandler<TSender, TArgs> handler)
+ {
+ if (handler == null)
+ throw new ArgumentNullException(nameof(handler));
- /// <summary>
- /// Unregisters an existing handler from this event.
- /// </summary>
- /// <param name="handler">Handler to unregister from the event.</param>
- public void Unregister(AsyncEventHandler<TSender, TArgs> handler)
- {
- if (handler == null)
- throw new ArgumentNullException(nameof(handler));
+ lock (this._lock)
+ this._handlers = this._handlers.Add(handler);
+ }
- lock (this._lock)
- this._handlers = this._handlers.Remove(handler);
- }
+ /// <summary>
+ /// Unregisters an existing handler from this event.
+ /// </summary>
+ /// <param name="handler">Handler to unregister from the event.</param>
+ public void Unregister(AsyncEventHandler<TSender, TArgs> handler)
+ {
+ if (handler == null)
+ throw new ArgumentNullException(nameof(handler));
- /// <summary>
- /// Unregisters all existing handlers from this event.
- /// </summary>
- public void UnregisterAll() => this._handlers = ImmutableArray<AsyncEventHandler<TSender, TArgs>>.Empty;
+ lock (this._lock)
+ this._handlers = this._handlers.Remove(handler);
+ }
- /// <summary>
- /// <para>Raises this event by invoking all of its registered handlers, in order of registration.</para>
- /// <para>All exceptions throw during invocation will be handled by the event's registered exception handler.</para>
- /// </summary>
- /// <param name="sender">Object which raised this event.</param>
- /// <param name="e">Arguments for this event.</param>
- /// <param name="exceptionMode">Defines what to do with exceptions caught from handlers.</param>
- /// <returns></returns>
- public async Task InvokeAsync(TSender sender, TArgs e, AsyncEventExceptionMode exceptionMode = AsyncEventExceptionMode.Default)
- {
- var handlers = this._handlers;
- if (handlers.Length == 0)
- return;
-
- // Collect exceptions
- List<Exception> exceptions = null;
- if ((exceptionMode & AsyncEventExceptionMode.ThrowAll) != 0)
- exceptions = new List<Exception>(handlers.Length * 2 /* timeout + regular */);
-
- // If we have a timeout configured, start the timeout task
- var timeout = this.MaximumExecutionTime > TimeSpan.Zero ? Task.Delay(this.MaximumExecutionTime) : null;
- for (var i = 0; i < handlers.Length; i++)
+ /// <summary>
+ /// Unregisters all existing handlers from this event.
+ /// </summary>
+ public void UnregisterAll() => this._handlers = ImmutableArray<AsyncEventHandler<TSender, TArgs>>.Empty;
+
+ /// <summary>
+ /// <para>Raises this event by invoking all of its registered handlers, in order of registration.</para>
+ /// <para>All exceptions throw during invocation will be handled by the event's registered exception handler.</para>
+ /// </summary>
+ /// <param name="sender">Object which raised this event.</param>
+ /// <param name="e">Arguments for this event.</param>
+ /// <param name="exceptionMode">Defines what to do with exceptions caught from handlers.</param>
+ /// <returns></returns>
+ public async Task InvokeAsync(TSender sender, TArgs e, AsyncEventExceptionMode exceptionMode = AsyncEventExceptionMode.Default)
{
- var handler = handlers[i];
- try
+ var handlers = this._handlers;
+ if (handlers.Length == 0)
+ return;
+
+ // Collect exceptions
+ List<Exception> exceptions = null;
+ if ((exceptionMode & AsyncEventExceptionMode.ThrowAll) != 0)
+ exceptions = new List<Exception>(handlers.Length * 2 /* timeout + regular */);
+
+ // If we have a timeout configured, start the timeout task
+ var timeout = this.MaximumExecutionTime > TimeSpan.Zero ? Task.Delay(this.MaximumExecutionTime) : null;
+ for (var i = 0; i < handlers.Length; i++)
{
- // Start the handler execution
- var handlerTask = handler(sender, e);
- if (handlerTask != null && timeout != null)
+ var handler = handlers[i];
+ try
{
- // If timeout is configured, wait for any task to finish
- // If the timeout task finishes first, the handler is causing a timeout
-
- var result = await Task.WhenAny(timeout, handlerTask).ConfigureAwait(false);
- if (result == timeout)
+ // Start the handler execution
+ var handlerTask = handler(sender, e);
+ if (handlerTask != null && timeout != null)
{
- timeout = null;
- var timeoutEx = new AsyncEventTimeoutException<TSender, TArgs>(this, handler);
+ // If timeout is configured, wait for any task to finish
+ // If the timeout task finishes first, the handler is causing a timeout
+
+ var result = await Task.WhenAny(timeout, handlerTask).ConfigureAwait(false);
+ if (result == timeout)
+ {
+ timeout = null;
+ var timeoutEx = new AsyncEventTimeoutException<TSender, TArgs>(this, handler);
- // Notify about the timeout and complete execution
- if ((exceptionMode & AsyncEventExceptionMode.HandleNonFatal) == AsyncEventExceptionMode.HandleNonFatal)
- this.HandleException(timeoutEx, handler, sender, e);
+ // Notify about the timeout and complete execution
+ if ((exceptionMode & AsyncEventExceptionMode.HandleNonFatal) == AsyncEventExceptionMode.HandleNonFatal)
+ this.HandleException(timeoutEx, handler, sender, e);
- if ((exceptionMode & AsyncEventExceptionMode.ThrowNonFatal) == AsyncEventExceptionMode.ThrowNonFatal)
- exceptions.Add(timeoutEx);
+ if ((exceptionMode & AsyncEventExceptionMode.ThrowNonFatal) == AsyncEventExceptionMode.ThrowNonFatal)
+ exceptions.Add(timeoutEx);
+ await handlerTask.ConfigureAwait(false);
+ }
+ }
+ else if (handlerTask != null)
+ {
+ // No timeout is configured, or timeout already expired, proceed as usual
await handlerTask.ConfigureAwait(false);
}
+
+ if (e.Handled)
+ break;
}
- else if (handlerTask != null)
+ catch (Exception ex)
{
- // No timeout is configured, or timeout already expired, proceed as usual
- await handlerTask.ConfigureAwait(false);
- }
+ e.Handled = false;
- if (e.Handled)
- break;
- }
- catch (Exception ex)
- {
- e.Handled = false;
-
- if ((exceptionMode & AsyncEventExceptionMode.HandleFatal) == AsyncEventExceptionMode.HandleFatal)
- this.HandleException(ex, handler, sender, e);
+ if ((exceptionMode & AsyncEventExceptionMode.HandleFatal) == AsyncEventExceptionMode.HandleFatal)
+ this.HandleException(ex, handler, sender, e);
- if ((exceptionMode & AsyncEventExceptionMode.ThrowFatal) == AsyncEventExceptionMode.ThrowFatal)
- exceptions.Add(ex);
+ if ((exceptionMode & AsyncEventExceptionMode.ThrowFatal) == AsyncEventExceptionMode.ThrowFatal)
+ exceptions.Add(ex);
+ }
}
- }
- if ((exceptionMode & AsyncEventExceptionMode.ThrowAll) != 0 && exceptions.Count > 0)
- throw new AggregateException("Exceptions were thrown during execution of the event's handlers.", exceptions);
- }
+ if ((exceptionMode & AsyncEventExceptionMode.ThrowAll) != 0 && exceptions.Count > 0)
+ throw new AggregateException("Exceptions were thrown during execution of the event's handlers.", exceptions);
+ }
- /// <summary>
- /// Handles the exception.
- /// </summary>
- /// <param name="ex">The ex.</param>
- /// <param name="handler">The handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="args">The args.</param>
- private void HandleException(Exception ex, AsyncEventHandler<TSender, TArgs> handler, TSender sender, TArgs args)
- {
- if (this._exceptionHandler != null)
- this._exceptionHandler(this, ex, handler, sender, args);
+ /// <summary>
+ /// Handles the exception.
+ /// </summary>
+ /// <param name="ex">The ex.</param>
+ /// <param name="handler">The handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="args">The args.</param>
+ private void HandleException(Exception ex, AsyncEventHandler<TSender, TArgs> handler, TSender sender, TArgs args)
+ {
+ if (this._exceptionHandler != null)
+ this._exceptionHandler(this, ex, handler, sender, args);
+ }
}
}
diff --git a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventArgs.cs b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventArgs.cs
index 3e0d6551f..dbb486ead 100644
--- a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventArgs.cs
+++ b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventArgs.cs
@@ -1,37 +1,38 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Utilities;
-
-/// <summary>
-/// Contains arguments passed to an asynchronous event.
-/// </summary>
-public class AsyncEventArgs : EventArgs
+namespace DisCatSharp.Common.Utilities
{
/// <summary>
- /// <para>Gets or sets whether this event was handled.</para>
- /// <para>Setting this to true will prevent other handlers from running.</para>
+ /// Contains arguments passed to an asynchronous event.
/// </summary>
- public bool Handled { get; set; } = false;
+ public class AsyncEventArgs : EventArgs
+ {
+ /// <summary>
+ /// <para>Gets or sets whether this event was handled.</para>
+ /// <para>Setting this to true will prevent other handlers from running.</para>
+ /// </summary>
+ public bool Handled { get; set; } = false;
+ }
}
diff --git a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventExceptionHandler.cs b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventExceptionHandler.cs
index 71b767152..913568515 100644
--- a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventExceptionHandler.cs
+++ b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventExceptionHandler.cs
@@ -1,38 +1,39 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Utilities;
-
-/// <summary>
-/// Handles any exception raised by an <see cref="AsyncEvent{TSender, TArgs}"/> or its handlers.
-/// </summary>
-/// <typeparam name="TSender">Type of the object that dispatches this event.</typeparam>
-/// <typeparam name="TArgs">Type of the object which holds arguments for this event.</typeparam>
-/// <param name="asyncEvent">Asynchronous event which threw the exception.</param>
-/// <param name="exception">Exception that was thrown</param>
-/// <param name="handler">Handler which threw the exception.</param>
-/// <param name="sender">Object which dispatched the event.</param>
-/// <param name="eventArgs">Arguments with which the event was dispatched.</param>
-public delegate void AsyncEventExceptionHandler<TSender, TArgs>(AsyncEvent<TSender, TArgs> asyncEvent, Exception exception, AsyncEventHandler<TSender, TArgs> handler, TSender sender, TArgs eventArgs)
- where TArgs : AsyncEventArgs;
+namespace DisCatSharp.Common.Utilities
+{
+ /// <summary>
+ /// Handles any exception raised by an <see cref="AsyncEvent{TSender, TArgs}"/> or its handlers.
+ /// </summary>
+ /// <typeparam name="TSender">Type of the object that dispatches this event.</typeparam>
+ /// <typeparam name="TArgs">Type of the object which holds arguments for this event.</typeparam>
+ /// <param name="asyncEvent">Asynchronous event which threw the exception.</param>
+ /// <param name="exception">Exception that was thrown</param>
+ /// <param name="handler">Handler which threw the exception.</param>
+ /// <param name="sender">Object which dispatched the event.</param>
+ /// <param name="eventArgs">Arguments with which the event was dispatched.</param>
+ public delegate void AsyncEventExceptionHandler<TSender, TArgs>(AsyncEvent<TSender, TArgs> asyncEvent, Exception exception, AsyncEventHandler<TSender, TArgs> handler, TSender sender, TArgs eventArgs)
+ where TArgs : AsyncEventArgs;
+}
diff --git a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventExceptionMode.cs b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventExceptionMode.cs
index 5857024ff..7aa386047 100644
--- a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventExceptionMode.cs
+++ b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventExceptionMode.cs
@@ -1,81 +1,82 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Common.Utilities;
-
-/// <summary>
-/// Defines the behaviour for throwing exceptions from <see cref="AsyncEvent{TSender, TArgs}.InvokeAsync(TSender, TArgs, AsyncEventExceptionMode)"/>.
-/// </summary>
-public enum AsyncEventExceptionMode : int
+namespace DisCatSharp.Common.Utilities
{
/// <summary>
- /// Defines that no exceptions should be thrown. Only exception handlers will be used.
+ /// Defines the behaviour for throwing exceptions from <see cref="AsyncEvent{TSender, TArgs}.InvokeAsync(TSender, TArgs, AsyncEventExceptionMode)"/>.
/// </summary>
- IgnoreAll = 0,
+ public enum AsyncEventExceptionMode : int
+ {
+ /// <summary>
+ /// Defines that no exceptions should be thrown. Only exception handlers will be used.
+ /// </summary>
+ IgnoreAll = 0,
- /// <summary>
- /// Defines that only fatal (i.e. non-<see cref="AsyncEventTimeoutException{TSender, TArgs}"/>) exceptions
- /// should be thrown.
- /// </summary>
- ThrowFatal = 1,
+ /// <summary>
+ /// Defines that only fatal (i.e. non-<see cref="AsyncEventTimeoutException{TSender, TArgs}"/>) exceptions
+ /// should be thrown.
+ /// </summary>
+ ThrowFatal = 1,
- /// <summary>
- /// Defines that only non-fatal (i.e. <see cref="AsyncEventTimeoutException{TSender, TArgs}"/>) exceptions
- /// should be thrown.
- /// </summary>
- ThrowNonFatal = 2,
+ /// <summary>
+ /// Defines that only non-fatal (i.e. <see cref="AsyncEventTimeoutException{TSender, TArgs}"/>) exceptions
+ /// should be thrown.
+ /// </summary>
+ ThrowNonFatal = 2,
- /// <summary>
- /// Defines that all exceptions should be thrown. This is equivalent to combining <see cref="ThrowFatal"/> and
- /// <see cref="ThrowNonFatal"/> flags.
- /// </summary>
- ThrowAll = ThrowFatal | ThrowNonFatal,
+ /// <summary>
+ /// Defines that all exceptions should be thrown. This is equivalent to combining <see cref="ThrowFatal"/> and
+ /// <see cref="ThrowNonFatal"/> flags.
+ /// </summary>
+ ThrowAll = ThrowFatal | ThrowNonFatal,
- /// <summary>
- /// Defines that only fatal (i.e. non-<see cref="AsyncEventTimeoutException{TSender, TArgs}"/>) exceptions
- /// should be handled by the specified exception handler.
- /// </summary>
- HandleFatal = 4,
+ /// <summary>
+ /// Defines that only fatal (i.e. non-<see cref="AsyncEventTimeoutException{TSender, TArgs}"/>) exceptions
+ /// should be handled by the specified exception handler.
+ /// </summary>
+ HandleFatal = 4,
- /// <summary>
- /// Defines that only non-fatal (i.e. <see cref="AsyncEventTimeoutException{TSender, TArgs}"/>) exceptions
- /// should be handled by the specified exception handler.
- /// </summary>
- HandleNonFatal = 8,
+ /// <summary>
+ /// Defines that only non-fatal (i.e. <see cref="AsyncEventTimeoutException{TSender, TArgs}"/>) exceptions
+ /// should be handled by the specified exception handler.
+ /// </summary>
+ HandleNonFatal = 8,
- /// <summary>
- /// Defines that all exceptions should be handled by the specified exception handler. This is equivalent to
- /// combining <see cref="HandleFatal"/> and <see cref="HandleNonFatal"/> flags.
- /// </summary>
- HandleAll = HandleFatal | HandleNonFatal,
+ /// <summary>
+ /// Defines that all exceptions should be handled by the specified exception handler. This is equivalent to
+ /// combining <see cref="HandleFatal"/> and <see cref="HandleNonFatal"/> flags.
+ /// </summary>
+ HandleAll = HandleFatal | HandleNonFatal,
- /// <summary>
- /// Defines that all exceptions should be thrown and handled by the specified exception handler. This is
- /// equivalent to combining <see cref="HandleAll"/> and <see cref="ThrowAll"/> flags.
- /// </summary>
- ThrowAllHandleAll = ThrowAll | HandleAll,
+ /// <summary>
+ /// Defines that all exceptions should be thrown and handled by the specified exception handler. This is
+ /// equivalent to combining <see cref="HandleAll"/> and <see cref="ThrowAll"/> flags.
+ /// </summary>
+ ThrowAllHandleAll = ThrowAll | HandleAll,
- /// <summary>
- /// Default mode, equivalent to <see cref="HandleAll"/>.
- /// </summary>
- Default = HandleAll
+ /// <summary>
+ /// Default mode, equivalent to <see cref="HandleAll"/>.
+ /// </summary>
+ Default = HandleAll
+ }
}
diff --git a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventHandler.cs b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventHandler.cs
index 2cd967bc7..738421215 100644
--- a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventHandler.cs
+++ b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventHandler.cs
@@ -1,35 +1,36 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.Common.Utilities;
-
-/// <summary>
-/// Handles an asynchronous event of type <see cref="AsyncEvent{TSender, TArgs}"/>. The handler will take an instance of <typeparamref name="TArgs"/> as its arguments.
-/// </summary>
-/// <typeparam name="TSender">Type of the object that dispatches this event.</typeparam>
-/// <typeparam name="TArgs">Type of the object which holds arguments for this event.</typeparam>
-/// <param name="sender">Object which raised this event.</param>
-/// <param name="e">Arguments for this event.</param>
-/// <returns></returns>
-public delegate Task AsyncEventHandler<in TSender, in TArgs>(TSender sender, TArgs e) where TArgs : AsyncEventArgs;
+namespace DisCatSharp.Common.Utilities
+{
+ /// <summary>
+ /// Handles an asynchronous event of type <see cref="AsyncEvent{TSender, TArgs}"/>. The handler will take an instance of <typeparamref name="TArgs"/> as its arguments.
+ /// </summary>
+ /// <typeparam name="TSender">Type of the object that dispatches this event.</typeparam>
+ /// <typeparam name="TArgs">Type of the object which holds arguments for this event.</typeparam>
+ /// <param name="sender">Object which raised this event.</param>
+ /// <param name="e">Arguments for this event.</param>
+ /// <returns></returns>
+ public delegate Task AsyncEventHandler<in TSender, in TArgs>(TSender sender, TArgs e) where TArgs : AsyncEventArgs;
+}
diff --git a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventTimeoutException.cs b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventTimeoutException.cs
index 0c5c2e021..7aa6e83d9 100644
--- a/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventTimeoutException.cs
+++ b/DisCatSharp.Common/Utilities/AsyncEvent/AsyncEventTimeoutException.cs
@@ -1,83 +1,84 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Utilities;
-
-/// <summary>
-/// ABC for <see cref="AsyncEventHandler{TSender, TArgs}"/>, allowing for using instances thereof without knowing the underlying instance's type parameters.
-/// </summary>
-public abstract class AsyncEventTimeoutException : Exception
+namespace DisCatSharp.Common.Utilities
{
/// <summary>
- /// Gets the event the invocation of which caused the timeout.
+ /// ABC for <see cref="AsyncEventHandler{TSender, TArgs}"/>, allowing for using instances thereof without knowing the underlying instance's type parameters.
/// </summary>
- public AsyncEvent Event { get; }
+ public abstract class AsyncEventTimeoutException : Exception
+ {
+ /// <summary>
+ /// Gets the event the invocation of which caused the timeout.
+ /// </summary>
+ public AsyncEvent Event { get; }
- /// <summary>
- /// Gets the handler which caused the timeout.
- /// </summary>
- public AsyncEventHandler<object, AsyncEventArgs> Handler { get; }
+ /// <summary>
+ /// Gets the handler which caused the timeout.
+ /// </summary>
+ public AsyncEventHandler<object, AsyncEventArgs> Handler { get; }
- /// <summary>
- /// Prevents a default instance of the <see cref="AsyncEventTimeoutException"/> class from being created.
- /// </summary>
- /// <param name="asyncEvent">The async event.</param>
- /// <param name="eventHandler">The event handler.</param>
- /// <param name="message">The message.</param>
- private protected AsyncEventTimeoutException(AsyncEvent asyncEvent, AsyncEventHandler<object, AsyncEventArgs> eventHandler, string message)
- : base(message)
- {
- this.Event = asyncEvent;
- this.Handler = eventHandler;
+ /// <summary>
+ /// Prevents a default instance of the <see cref="AsyncEventTimeoutException"/> class from being created.
+ /// </summary>
+ /// <param name="asyncEvent">The async event.</param>
+ /// <param name="eventHandler">The event handler.</param>
+ /// <param name="message">The message.</param>
+ private protected AsyncEventTimeoutException(AsyncEvent asyncEvent, AsyncEventHandler<object, AsyncEventArgs> eventHandler, string message)
+ : base(message)
+ {
+ this.Event = asyncEvent;
+ this.Handler = eventHandler;
+ }
}
-}
-/// <summary>
-/// <para>Thrown whenever execution of an <see cref="AsyncEventHandler{TSender, TArgs}"/> exceeds maximum time allowed.</para>
-/// <para>This is a non-fatal exception, used primarily to inform users that their code is taking too long to execute.</para>
-/// </summary>
-/// <typeparam name="TSender">Type of sender that dispatched this asynchronous event.</typeparam>
-/// <typeparam name="TArgs">Type of event arguments for the asynchronous event.</typeparam>
-public class AsyncEventTimeoutException<TSender, TArgs> : AsyncEventTimeoutException
- where TArgs : AsyncEventArgs
-{
/// <summary>
- /// Gets the event the invocation of which caused the timeout.
+ /// <para>Thrown whenever execution of an <see cref="AsyncEventHandler{TSender, TArgs}"/> exceeds maximum time allowed.</para>
+ /// <para>This is a non-fatal exception, used primarily to inform users that their code is taking too long to execute.</para>
/// </summary>
- public new AsyncEvent<TSender, TArgs> Event => base.Event as AsyncEvent<TSender, TArgs>;
+ /// <typeparam name="TSender">Type of sender that dispatched this asynchronous event.</typeparam>
+ /// <typeparam name="TArgs">Type of event arguments for the asynchronous event.</typeparam>
+ public class AsyncEventTimeoutException<TSender, TArgs> : AsyncEventTimeoutException
+ where TArgs : AsyncEventArgs
+ {
+ /// <summary>
+ /// Gets the event the invocation of which caused the timeout.
+ /// </summary>
+ public new AsyncEvent<TSender, TArgs> Event => base.Event as AsyncEvent<TSender, TArgs>;
- /// <summary>
- /// Gets the handler which caused the timeout.
- /// </summary>
- public new AsyncEventHandler<TSender, TArgs> Handler => base.Handler as AsyncEventHandler<TSender, TArgs>;
+ /// <summary>
+ /// Gets the handler which caused the timeout.
+ /// </summary>
+ public new AsyncEventHandler<TSender, TArgs> Handler => base.Handler as AsyncEventHandler<TSender, TArgs>;
- /// <summary>
- /// Creates a new timeout exception for specified event and handler.
- /// </summary>
- /// <param name="asyncEvent">Event the execution of which timed out.</param>
- /// <param name="eventHandler">Handler which timed out.</param>
- public AsyncEventTimeoutException(AsyncEvent<TSender, TArgs> asyncEvent, AsyncEventHandler<TSender, TArgs> eventHandler)
- : base(asyncEvent, eventHandler as AsyncEventHandler<object, AsyncEventArgs>, "An event handler caused the invocation of an asynchronous event to time out.")
- { }
+ /// <summary>
+ /// Creates a new timeout exception for specified event and handler.
+ /// </summary>
+ /// <param name="asyncEvent">Event the execution of which timed out.</param>
+ /// <param name="eventHandler">Handler which timed out.</param>
+ public AsyncEventTimeoutException(AsyncEvent<TSender, TArgs> asyncEvent, AsyncEventHandler<TSender, TArgs> eventHandler)
+ : base(asyncEvent, eventHandler as AsyncEventHandler<object, AsyncEventArgs>, "An event handler caused the invocation of an asynchronous event to time out.")
+ { }
+ }
}
diff --git a/DisCatSharp.Common/Utilities/AsyncExecutor.cs b/DisCatSharp.Common/Utilities/AsyncExecutor.cs
index 671ee3c00..6f9101f4b 100644
--- a/DisCatSharp.Common/Utilities/AsyncExecutor.cs
+++ b/DisCatSharp.Common/Utilities/AsyncExecutor.cs
@@ -1,172 +1,173 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
-namespace DisCatSharp.Common.Utilities;
-
-/// <summary>
-/// Provides a simplified way of executing asynchronous code synchronously.
-/// </summary>
-public class AsyncExecutor
+namespace DisCatSharp.Common.Utilities
{
/// <summary>
- /// Creates a new instance of asynchronous executor.
- /// </summary>
- public AsyncExecutor()
- { }
-
- /// <summary>
- /// Executes a specified task in an asynchronous manner, waiting for its completion.
+ /// Provides a simplified way of executing asynchronous code synchronously.
/// </summary>
- /// <param name="task">Task to execute.</param>
- public void Execute(Task task)
+ public class AsyncExecutor
{
- // create state object
- var taskState = new StateRef<object>(new AutoResetEvent(false));
+ /// <summary>
+ /// Creates a new instance of asynchronous executor.
+ /// </summary>
+ public AsyncExecutor()
+ { }
- // queue a task and wait for it to finish executing
- task.ContinueWith(TaskCompletionHandler, taskState);
- taskState.Lock.WaitOne();
+ /// <summary>
+ /// Executes a specified task in an asynchronous manner, waiting for its completion.
+ /// </summary>
+ /// <param name="task">Task to execute.</param>
+ public void Execute(Task task)
+ {
+ // create state object
+ var taskState = new StateRef<object>(new AutoResetEvent(false));
- // check for and rethrow any exceptions
- if (taskState.Exception != null)
- throw taskState.Exception;
+ // queue a task and wait for it to finish executing
+ task.ContinueWith(TaskCompletionHandler, taskState);
+ taskState.Lock.WaitOne();
- // completion method
- void TaskCompletionHandler(Task t, object state)
- {
- // retrieve state data
- var stateRef = state as StateRef<object>;
+ // check for and rethrow any exceptions
+ if (taskState.Exception != null)
+ throw taskState.Exception;
- // retrieve any exceptions or cancellation status
- if (t.IsFaulted)
- {
- if (t.Exception.InnerExceptions.Count == 1) // unwrap if 1
- stateRef.Exception = t.Exception.InnerException;
- else
- stateRef.Exception = t.Exception;
- }
- else if (t.IsCanceled)
+ // completion method
+ void TaskCompletionHandler(Task t, object state)
{
- stateRef.Exception = new TaskCanceledException(t);
+ // retrieve state data
+ var stateRef = state as StateRef<object>;
+
+ // retrieve any exceptions or cancellation status
+ if (t.IsFaulted)
+ {
+ if (t.Exception.InnerExceptions.Count == 1) // unwrap if 1
+ stateRef.Exception = t.Exception.InnerException;
+ else
+ stateRef.Exception = t.Exception;
+ }
+ else if (t.IsCanceled)
+ {
+ stateRef.Exception = new TaskCanceledException(t);
+ }
+
+ // signal that the execution is done
+ stateRef.Lock.Set();
}
-
- // signal that the execution is done
- stateRef.Lock.Set();
}
- }
-
- /// <summary>
- /// Executes a specified task in an asynchronous manner, waiting for its completion, and returning the result.
- /// </summary>
- /// <typeparam name="T">Type of the Task's return value.</typeparam>
- /// <param name="task">Task to execute.</param>
- /// <returns>Task's result.</returns>
- public T Execute<T>(Task<T> task)
- {
- // create state object
- var taskState = new StateRef<T>(new AutoResetEvent(false));
- // queue a task and wait for it to finish executing
- task.ContinueWith(TaskCompletionHandler, taskState);
- taskState.Lock.WaitOne();
-
- // check for and rethrow any exceptions
- if (taskState.Exception != null)
- throw taskState.Exception;
+ /// <summary>
+ /// Executes a specified task in an asynchronous manner, waiting for its completion, and returning the result.
+ /// </summary>
+ /// <typeparam name="T">Type of the Task's return value.</typeparam>
+ /// <param name="task">Task to execute.</param>
+ /// <returns>Task's result.</returns>
+ public T Execute<T>(Task<T> task)
+ {
+ // create state object
+ var taskState = new StateRef<T>(new AutoResetEvent(false));
- // return the result, if any
- if (taskState.HasResult)
- return taskState.Result;
+ // queue a task and wait for it to finish executing
+ task.ContinueWith(TaskCompletionHandler, taskState);
+ taskState.Lock.WaitOne();
- // throw exception if no result
- throw new Exception("Task returned no result.");
+ // check for and rethrow any exceptions
+ if (taskState.Exception != null)
+ throw taskState.Exception;
- // completion method
- void TaskCompletionHandler(Task<T> t, object state)
- {
- // retrieve state data
- var stateRef = state as StateRef<T>;
+ // return the result, if any
+ if (taskState.HasResult)
+ return taskState.Result;
- // retrieve any exceptions or cancellation status
- if (t.IsFaulted)
- {
- if (t.Exception.InnerExceptions.Count == 1) // unwrap if 1
- stateRef.Exception = t.Exception.InnerException;
- else
- stateRef.Exception = t.Exception;
- }
- else if (t.IsCanceled)
- {
- stateRef.Exception = new TaskCanceledException(t);
- }
+ // throw exception if no result
+ throw new Exception("Task returned no result.");
- // return the result from the task, if any
- if (t.IsCompleted && !t.IsFaulted)
+ // completion method
+ void TaskCompletionHandler(Task<T> t, object state)
{
- stateRef.HasResult = true;
- stateRef.Result = t.Result;
+ // retrieve state data
+ var stateRef = state as StateRef<T>;
+
+ // retrieve any exceptions or cancellation status
+ if (t.IsFaulted)
+ {
+ if (t.Exception.InnerExceptions.Count == 1) // unwrap if 1
+ stateRef.Exception = t.Exception.InnerException;
+ else
+ stateRef.Exception = t.Exception;
+ }
+ else if (t.IsCanceled)
+ {
+ stateRef.Exception = new TaskCanceledException(t);
+ }
+
+ // return the result from the task, if any
+ if (t.IsCompleted && !t.IsFaulted)
+ {
+ stateRef.HasResult = true;
+ stateRef.Result = t.Result;
+ }
+
+ // signal that the execution is done
+ stateRef.Lock.Set();
}
-
- // signal that the execution is done
- stateRef.Lock.Set();
}
- }
-
- /// <summary>
- /// The state ref.
- /// </summary>
- private sealed class StateRef<T>
- {
- /// <summary>
- /// Gets the lock used to wait for task's completion.
- /// </summary>
- public AutoResetEvent Lock { get; }
/// <summary>
- /// Gets the exception that occurred during task's execution, if any.
+ /// The state ref.
/// </summary>
- public Exception Exception { get; set; }
-
- /// <summary>
- /// Gets the result returned by the task.
- /// </summary>
- public T Result { get; set; }
-
- /// <summary>
- /// Gets whether the task returned a result.
- /// </summary>
- public bool HasResult { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StateRef{T}"/> class.
- /// </summary>
- /// <param name="lock">The lock.</param>
- public StateRef(AutoResetEvent @lock)
+ private sealed class StateRef<T>
{
- this.Lock = @lock;
+ /// <summary>
+ /// Gets the lock used to wait for task's completion.
+ /// </summary>
+ public AutoResetEvent Lock { get; }
+
+ /// <summary>
+ /// Gets the exception that occurred during task's execution, if any.
+ /// </summary>
+ public Exception Exception { get; set; }
+
+ /// <summary>
+ /// Gets the result returned by the task.
+ /// </summary>
+ public T Result { get; set; }
+
+ /// <summary>
+ /// Gets whether the task returned a result.
+ /// </summary>
+ public bool HasResult { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StateRef{T}"/> class.
+ /// </summary>
+ /// <param name="lock">The lock.</param>
+ public StateRef(AutoResetEvent @lock)
+ {
+ this.Lock = @lock;
+ }
}
}
}
diff --git a/DisCatSharp.Common/Utilities/AsyncManualResetEvent.cs b/DisCatSharp.Common/Utilities/AsyncManualResetEvent.cs
index 1dd49f83d..c15e09956 100644
--- a/DisCatSharp.Common/Utilities/AsyncManualResetEvent.cs
+++ b/DisCatSharp.Common/Utilities/AsyncManualResetEvent.cs
@@ -1,80 +1,81 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
-namespace DisCatSharp.Common.Utilities;
-
-/// <summary>
-/// Represents a thread synchronization event that, when signaled, must be reset manually. Unlike <see cref="System.Threading.ManualResetEventSlim"/>, this event is asynchronous.
-/// </summary>
-public sealed class AsyncManualResetEvent
+namespace DisCatSharp.Common.Utilities
{
/// <summary>
- /// Gets whether this event has been signaled.
+ /// Represents a thread synchronization event that, when signaled, must be reset manually. Unlike <see cref="System.Threading.ManualResetEventSlim"/>, this event is asynchronous.
/// </summary>
- public bool IsSet => this._resetTcs?.Task?.IsCompleted == true;
+ public sealed class AsyncManualResetEvent
+ {
+ /// <summary>
+ /// Gets whether this event has been signaled.
+ /// </summary>
+ public bool IsSet => this._resetTcs?.Task?.IsCompleted == true;
- private volatile TaskCompletionSource<bool> _resetTcs;
+ private volatile TaskCompletionSource<bool> _resetTcs;
- /// <summary>
- /// Creates a new asynchronous synchronization event with initial state.
- /// </summary>
- /// <param name="initialState">Initial state of this event.</param>
- public AsyncManualResetEvent(bool initialState)
- {
- this._resetTcs = new TaskCompletionSource<bool>();
- if (initialState)
- this._resetTcs.TrySetResult(initialState);
- }
+ /// <summary>
+ /// Creates a new asynchronous synchronization event with initial state.
+ /// </summary>
+ /// <param name="initialState">Initial state of this event.</param>
+ public AsyncManualResetEvent(bool initialState)
+ {
+ this._resetTcs = new TaskCompletionSource<bool>();
+ if (initialState)
+ this._resetTcs.TrySetResult(initialState);
+ }
- // Spawn a threadpool thread instead of making a task
- // Maybe overkill, but I am less unsure of this than awaits and
- // potentially cross-scheduler interactions
- /// <summary>
- /// Asynchronously signal this event.
- /// </summary>
- /// <returns></returns>
- public Task SetAsync()
- => Task.Run(() => this._resetTcs.TrySetResult(true));
+ // Spawn a threadpool thread instead of making a task
+ // Maybe overkill, but I am less unsure of this than awaits and
+ // potentially cross-scheduler interactions
+ /// <summary>
+ /// Asynchronously signal this event.
+ /// </summary>
+ /// <returns></returns>
+ public Task SetAsync()
+ => Task.Run(() => this._resetTcs.TrySetResult(true));
- /// <summary>
- /// Asynchronously wait for this event to be signaled.
- /// </summary>
- /// <returns></returns>
- public Task WaitAsync()
- => this._resetTcs.Task;
+ /// <summary>
+ /// Asynchronously wait for this event to be signaled.
+ /// </summary>
+ /// <returns></returns>
+ public Task WaitAsync()
+ => this._resetTcs.Task;
- /// <summary>
- /// Reset this event's signal state to unsignaled.
- /// </summary>
- public void Reset()
- {
- while (true)
+ /// <summary>
+ /// Reset this event's signal state to unsignaled.
+ /// </summary>
+ public void Reset()
{
- var tcs = this._resetTcs;
- if (!tcs.Task.IsCompleted || Interlocked.CompareExchange(ref this._resetTcs, new TaskCompletionSource<bool>(), tcs) == tcs)
- return;
+ while (true)
+ {
+ var tcs = this._resetTcs;
+ if (!tcs.Task.IsCompleted || Interlocked.CompareExchange(ref this._resetTcs, new TaskCompletionSource<bool>(), tcs) == tcs)
+ return;
+ }
}
}
}
diff --git a/DisCatSharp.Common/Utilities/EnsureObjectStates.cs b/DisCatSharp.Common/Utilities/EnsureObjectStates.cs
index 4573c9441..d781fdb92 100644
--- a/DisCatSharp.Common/Utilities/EnsureObjectStates.cs
+++ b/DisCatSharp.Common/Utilities/EnsureObjectStates.cs
@@ -1,78 +1,79 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Linq;
-namespace DisCatSharp.Common;
-
-/// <summary>
-/// Ensures that certain objects have the target state.
-/// </summary>
-public static class EnsureObjectStates
+namespace DisCatSharp.Common
{
/// <summary>
- /// Checks whether the dictionary is null or empty.
+ /// Ensures that certain objects have the target state.
/// </summary>
- /// <typeparam name="T1">Any key type.</typeparam>
- /// <typeparam name="T2">Any value type.</typeparam>
- /// <param name="dictionary">The dictionary to check on.</param>
- /// <returns>True if satisfied, false otherwise.</returns>
+ public static class EnsureObjectStates
+ {
+ /// <summary>
+ /// Checks whether the dictionary is null or empty.
+ /// </summary>
+ /// <typeparam name="T1">Any key type.</typeparam>
+ /// <typeparam name="T2">Any value type.</typeparam>
+ /// <param name="dictionary">The dictionary to check on.</param>
+ /// <returns>True if satisfied, false otherwise.</returns>
#nullable enable
- public static bool EmptyOrNull<T1, T2>(this Dictionary<T1?, T2?>? dictionary)
- => dictionary == null || !dictionary.Any() || dictionary.Keys == null || !dictionary.Keys.Any();
+ public static bool EmptyOrNull<T1, T2>(this Dictionary<T1?, T2?>? dictionary)
+ => dictionary == null || !dictionary.Any() || dictionary.Keys == null || !dictionary.Keys.Any();
#nullable disable
- /// <summary>
- /// Checks whether the dictionary is not null and not empty.
- /// </summary>
- /// <typeparam name="T1">Any key type.</typeparam>
- /// <typeparam name="T2">Any value type.</typeparam>
- /// <param name="dictionary">The dictionary to check on.</param>
- /// <returns>True if satisfied, false otherwise.</returns>
+ /// <summary>
+ /// Checks whether the dictionary is not null and not empty.
+ /// </summary>
+ /// <typeparam name="T1">Any key type.</typeparam>
+ /// <typeparam name="T2">Any value type.</typeparam>
+ /// <param name="dictionary">The dictionary to check on.</param>
+ /// <returns>True if satisfied, false otherwise.</returns>
#nullable enable
- public static bool NotEmptyAndNotNull<T1, T2>(this Dictionary<T1?, T2?>? dictionary)
- => dictionary != null && dictionary.Any() && dictionary.Keys != null && dictionary.Keys.Any();
+ public static bool NotEmptyAndNotNull<T1, T2>(this Dictionary<T1?, T2?>? dictionary)
+ => dictionary != null && dictionary.Any() && dictionary.Keys != null && dictionary.Keys.Any();
#nullable disable
- /// <summary>
- /// Checks whether the list is null or empty.
- /// </summary>
- /// <typeparam name="T">Any value type.</typeparam>
- /// <param name="list">The list to check on.</param>
- /// <returns>True if satisfied, false otherwise.</returns>
+ /// <summary>
+ /// Checks whether the list is null or empty.
+ /// </summary>
+ /// <typeparam name="T">Any value type.</typeparam>
+ /// <param name="list">The list to check on.</param>
+ /// <returns>True if satisfied, false otherwise.</returns>
#nullable enable
- public static bool EmptyOrNull<T>(this List<T?>? list)
- => list == null || !list.Any();
+ public static bool EmptyOrNull<T>(this List<T?>? list)
+ => list == null || !list.Any();
#nullable disable
- /// <summary>
- /// Checks whether the list is not null and not empty.
- /// </summary>
- /// <typeparam name="T">Any value type.</typeparam>
- /// <param name="list">The list to check on.</param>
- /// <returns>True if satisfied, false otherwise.</returns>
+ /// <summary>
+ /// Checks whether the list is not null and not empty.
+ /// </summary>
+ /// <typeparam name="T">Any value type.</typeparam>
+ /// <param name="list">The list to check on.</param>
+ /// <returns>True if satisfied, false otherwise.</returns>
#nullable enable
- public static bool NotEmptyAndNotNull<T>(this List<T?>? list)
- => list != null && list.Any();
+ public static bool NotEmptyAndNotNull<T>(this List<T?>? list)
+ => list != null && list.Any();
#nullable disable
+ }
}
diff --git a/DisCatSharp.Common/Utilities/Extensions.cs b/DisCatSharp.Common/Utilities/Extensions.cs
index e72cf9001..b7c97298c 100644
--- a/DisCatSharp.Common/Utilities/Extensions.cs
+++ b/DisCatSharp.Common/Utilities/Extensions.cs
@@ -1,494 +1,495 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Runtime.CompilerServices;
-namespace DisCatSharp.Common;
-
-/// <summary>
-/// Assortment of various extension and utility methods, designed to make working with various types a little easier.
-/// </summary>
-public static class Extensions
+namespace DisCatSharp.Common
{
/// <summary>
- /// <para>Deconstructs a <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/> key-value pair item (<see cref="System.Collections.Generic.KeyValuePair{TKey, TValue}"/>) into 2 separate variables.</para>
- /// <para>This allows for enumerating over dictionaries in foreach blocks by using a (k, v) tuple as the enumerator variable, instead of having to use a <see cref="System.Collections.Generic.KeyValuePair{TKey, TValue}"/> directly.</para>
+ /// Assortment of various extension and utility methods, designed to make working with various types a little easier.
/// </summary>
- /// <typeparam name="TKey">Type of dictionary item key.</typeparam>
- /// <typeparam name="TValue">Type of dictionary item value.</typeparam>
- /// <param name="kvp">Key-value pair to deconstruct.</param>
- /// <param name="key">Deconstructed key.</param>
- /// <param name="value">Deconstructed value.</param>
- public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
+ public static class Extensions
{
- key = kvp.Key;
- value = kvp.Value;
- }
-
- /// <summary>
- /// Calculates the length of string representation of given number in base 10 (including sign, if present).
- /// </summary>
- /// <param name="num">Number to calculate the length of.</param>
- /// <returns>Calculated number length.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateLength(this sbyte num)
- => num == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(num == sbyte.MinValue ? num + 1 : num))) + (num < 0 ? 2 /* include sign */ : 1);
-
- /// <summary>
- /// Calculates the length of string representation of given number in base 10 (including sign, if present).
- /// </summary>
- /// <param name="num">Number to calculate the length of.</param>
- /// <returns>Calculated number length.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateLength(this byte num)
- => num == 0 ? 1 : (int)Math.Floor(Math.Log10(num)) + 1;
-
- /// <summary>
- /// Calculates the length of string representation of given number in base 10 (including sign, if present).
- /// </summary>
- /// <param name="num">Number to calculate the length of.</param>
- /// <returns>Calculated number length.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateLength(this short num)
- => num == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(num == short.MinValue ? num + 1 : num))) + (num < 0 ? 2 /* include sign */ : 1);
-
- /// <summary>
- /// Calculates the length of string representation of given number in base 10 (including sign, if present).
- /// </summary>
- /// <param name="num">Number to calculate the length of.</param>
- /// <returns>Calculated number length.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateLength(this ushort num)
- => num == 0 ? 1 : (int)Math.Floor(Math.Log10(num)) + 1;
-
- /// <summary>
- /// Calculates the length of string representation of given number in base 10 (including sign, if present).
- /// </summary>
- /// <param name="num">Number to calculate the length of.</param>
- /// <returns>Calculated number length.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateLength(this int num)
- => num == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(num == int.MinValue ? num + 1 : num))) + (num < 0 ? 2 /* include sign */ : 1);
-
- /// <summary>
- /// Calculates the length of string representation of given number in base 10 (including sign, if present).
- /// </summary>
- /// <param name="num">Number to calculate the length of.</param>
- /// <returns>Calculated number length.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateLength(this uint num)
- => num == 0 ? 1 : (int)Math.Floor(Math.Log10(num)) + 1;
-
- /// <summary>
- /// Calculates the length of string representation of given number in base 10 (including sign, if present).
- /// </summary>
- /// <param name="num">Number to calculate the length of.</param>
- /// <returns>Calculated number length.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateLength(this long num)
- => num == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(num == long.MinValue ? num + 1 : num))) + (num < 0 ? 2 /* include sign */ : 1);
-
- /// <summary>
- /// Calculates the length of string representation of given number in base 10 (including sign, if present).
- /// </summary>
- /// <param name="num">Number to calculate the length of.</param>
- /// <returns>Calculated number length.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateLength(this ulong num)
- => num == 0 ? 1 : (int)Math.Floor(Math.Log10(num)) + 1;
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this sbyte num, sbyte min, sbyte max, bool inclusive = true)
- {
- if (min > max)
+ /// <summary>
+ /// <para>Deconstructs a <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/> key-value pair item (<see cref="System.Collections.Generic.KeyValuePair{TKey, TValue}"/>) into 2 separate variables.</para>
+ /// <para>This allows for enumerating over dictionaries in foreach blocks by using a (k, v) tuple as the enumerator variable, instead of having to use a <see cref="System.Collections.Generic.KeyValuePair{TKey, TValue}"/> directly.</para>
+ /// </summary>
+ /// <typeparam name="TKey">Type of dictionary item key.</typeparam>
+ /// <typeparam name="TValue">Type of dictionary item value.</typeparam>
+ /// <param name="kvp">Key-value pair to deconstruct.</param>
+ /// <param name="key">Deconstructed key.</param>
+ /// <param name="value">Deconstructed value.</param>
+ public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
{
- min ^= max;
- max ^= min;
- min ^= max;
+ key = kvp.Key;
+ value = kvp.Value;
}
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this byte num, byte min, byte max, bool inclusive = true)
- {
- if (min > max)
+ /// <summary>
+ /// Calculates the length of string representation of given number in base 10 (including sign, if present).
+ /// </summary>
+ /// <param name="num">Number to calculate the length of.</param>
+ /// <returns>Calculated number length.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateLength(this sbyte num)
+ => num == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(num == sbyte.MinValue ? num + 1 : num))) + (num < 0 ? 2 /* include sign */ : 1);
+
+ /// <summary>
+ /// Calculates the length of string representation of given number in base 10 (including sign, if present).
+ /// </summary>
+ /// <param name="num">Number to calculate the length of.</param>
+ /// <returns>Calculated number length.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateLength(this byte num)
+ => num == 0 ? 1 : (int)Math.Floor(Math.Log10(num)) + 1;
+
+ /// <summary>
+ /// Calculates the length of string representation of given number in base 10 (including sign, if present).
+ /// </summary>
+ /// <param name="num">Number to calculate the length of.</param>
+ /// <returns>Calculated number length.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateLength(this short num)
+ => num == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(num == short.MinValue ? num + 1 : num))) + (num < 0 ? 2 /* include sign */ : 1);
+
+ /// <summary>
+ /// Calculates the length of string representation of given number in base 10 (including sign, if present).
+ /// </summary>
+ /// <param name="num">Number to calculate the length of.</param>
+ /// <returns>Calculated number length.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateLength(this ushort num)
+ => num == 0 ? 1 : (int)Math.Floor(Math.Log10(num)) + 1;
+
+ /// <summary>
+ /// Calculates the length of string representation of given number in base 10 (including sign, if present).
+ /// </summary>
+ /// <param name="num">Number to calculate the length of.</param>
+ /// <returns>Calculated number length.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateLength(this int num)
+ => num == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(num == int.MinValue ? num + 1 : num))) + (num < 0 ? 2 /* include sign */ : 1);
+
+ /// <summary>
+ /// Calculates the length of string representation of given number in base 10 (including sign, if present).
+ /// </summary>
+ /// <param name="num">Number to calculate the length of.</param>
+ /// <returns>Calculated number length.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateLength(this uint num)
+ => num == 0 ? 1 : (int)Math.Floor(Math.Log10(num)) + 1;
+
+ /// <summary>
+ /// Calculates the length of string representation of given number in base 10 (including sign, if present).
+ /// </summary>
+ /// <param name="num">Number to calculate the length of.</param>
+ /// <returns>Calculated number length.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateLength(this long num)
+ => num == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(num == long.MinValue ? num + 1 : num))) + (num < 0 ? 2 /* include sign */ : 1);
+
+ /// <summary>
+ /// Calculates the length of string representation of given number in base 10 (including sign, if present).
+ /// </summary>
+ /// <param name="num">Number to calculate the length of.</param>
+ /// <returns>Calculated number length.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateLength(this ulong num)
+ => num == 0 ? 1 : (int)Math.Floor(Math.Log10(num)) + 1;
+
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this sbyte num, sbyte min, sbyte max, bool inclusive = true)
{
- min ^= max;
- max ^= min;
- min ^= max;
+ if (min > max)
+ {
+ min ^= max;
+ max ^= min;
+ min ^= max;
+ }
+
+ return inclusive ? num >= min && num <= max : num > min && num < max;
}
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this short num, short min, short max, bool inclusive = true)
- {
- if (min > max)
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this byte num, byte min, byte max, bool inclusive = true)
{
- min ^= max;
- max ^= min;
- min ^= max;
+ if (min > max)
+ {
+ min ^= max;
+ max ^= min;
+ min ^= max;
+ }
+
+ return inclusive ? num >= min && num <= max : num > min && num < max;
}
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this ushort num, ushort min, ushort max, bool inclusive = true)
- {
- if (min > max)
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this short num, short min, short max, bool inclusive = true)
{
- min ^= max;
- max ^= min;
- min ^= max;
+ if (min > max)
+ {
+ min ^= max;
+ max ^= min;
+ min ^= max;
+ }
+
+ return inclusive ? num >= min && num <= max : num > min && num < max;
}
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this int num, int min, int max, bool inclusive = true)
- {
- if (min > max)
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this ushort num, ushort min, ushort max, bool inclusive = true)
{
- min ^= max;
- max ^= min;
- min ^= max;
+ if (min > max)
+ {
+ min ^= max;
+ max ^= min;
+ min ^= max;
+ }
+
+ return inclusive ? num >= min && num <= max : num > min && num < max;
}
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this uint num, uint min, uint max, bool inclusive = true)
- {
- if (min > max)
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this int num, int min, int max, bool inclusive = true)
{
- min ^= max;
- max ^= min;
- min ^= max;
+ if (min > max)
+ {
+ min ^= max;
+ max ^= min;
+ min ^= max;
+ }
+
+ return inclusive ? num >= min && num <= max : num > min && num < max;
}
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this long num, long min, long max, bool inclusive = true)
- {
- if (min > max)
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this uint num, uint min, uint max, bool inclusive = true)
{
- min ^= max;
- max ^= min;
- min ^= max;
+ if (min > max)
+ {
+ min ^= max;
+ max ^= min;
+ min ^= max;
+ }
+
+ return inclusive ? num >= min && num <= max : num > min && num < max;
}
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this ulong num, ulong min, ulong max, bool inclusive = true)
- {
- if (min > max)
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this long num, long min, long max, bool inclusive = true)
{
- min ^= max;
- max ^= min;
- min ^= max;
+ if (min > max)
+ {
+ min ^= max;
+ max ^= min;
+ min ^= max;
+ }
+
+ return inclusive ? num >= min && num <= max : num > min && num < max;
}
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this float num, float min, float max, bool inclusive = true)
- {
- if (min > max)
- return false;
-
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
- /// </summary>
- /// <param name="num">Number to test.</param>
- /// <param name="min">Lower bound of the range.</param>
- /// <param name="max">Upper bound of the range.</param>
- /// <param name="inclusive">Whether the check is to be inclusive.</param>
- /// <returns>Whether the value is in range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRange(this double num, double min, double max, bool inclusive = true)
- {
- if (min > max)
- return false;
-
- return inclusive ? num >= min && num <= max : num > min && num < max;
- }
-
- /// <summary>
- /// Returns whether supplied character is in any of the following ranges: a-z, A-Z, 0-9.
- /// </summary>
- /// <param name="c">Character to test.</param>
- /// <returns>Whether the character is in basic alphanumeric character range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsBasicAlphanumeric(this char c)
- => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
-
- /// <summary>
- /// Returns whether supplied character is in the 0-9 range.
- /// </summary>
- /// <param name="c">Character to test.</param>
- /// <returns>Whether the character is in basic numeric digit character range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsBasicDigit(this char c)
- => c >= '0' && c <= '9';
-
- /// <summary>
- /// Returns whether supplied character is in the a-z or A-Z range.
- /// </summary>
- /// <param name="c">Character to test.</param>
- /// <returns>Whether the character is in basic letter character range.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsBasicLetter(this char c)
- => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
-
- /// <summary>
- /// Tests whether given string ends with given character.
- /// </summary>
- /// <param name="s">String to test.</param>
- /// <param name="c">Character to test for.</param>
- /// <returns>Whether the supplied string ends with supplied character.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool EndsWithCharacter(this string s, char c)
- => s.Length >= 1 && s[^1] == c;
-
- /// <summary>
- /// Tests whether given string starts with given character.
- /// </summary>
- /// <param name="s">String to test.</param>
- /// <param name="c">Character to test for.</param>
- /// <returns>Whether the supplied string starts with supplied character.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool StartsWithCharacter(this string s, char c)
- => s.Length >= 1 && s[0] == c;
-
- // https://stackoverflow.com/questions/9545619/a-fast-hash-function-for-string-in-c-sharp
- // Calls are inlined to call the underlying method directly
- /// <summary>
- /// Computes a 64-bit Knuth hash from supplied characters.
- /// </summary>
- /// <param name="chars">Characters to compute the hash value from.</param>
- /// <returns>Computer 64-bit Knuth hash.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong CalculateKnuthHash(this ReadOnlySpan<char> chars)
- => Knuth(chars);
-
- /// <summary>
- /// Computes a 64-bit Knuth hash from supplied characters.
- /// </summary>
- /// <param name="chars">Characters to compute the hash value from.</param>
- /// <returns>Computer 64-bit Knuth hash.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong CalculateKnuthHash(this Span<char> chars)
- => Knuth(chars);
-
- /// <summary>
- /// Computes a 64-bit Knuth hash from supplied characters.
- /// </summary>
- /// <param name="chars">Characters to compute the hash value from.</param>
- /// <returns>Computer 64-bit Knuth hash.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong CalculateKnuthHash(this ReadOnlyMemory<char> chars)
- => Knuth(chars.Span);
-
- /// <summary>
- /// Computes a 64-bit Knuth hash from supplied characters.
- /// </summary>
- /// <param name="chars">Characters to compute the hash value from.</param>
- /// <returns>Computer 64-bit Knuth hash.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong CalculateKnuthHash(this Memory<char> chars)
- => Knuth(chars.Span);
-
- /// <summary>
- /// Computes a 64-bit Knuth hash from supplied characters.
- /// </summary>
- /// <param name="chars">Characters to compute the hash value from.</param>
- /// <returns>Computer 64-bit Knuth hash.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong CalculateKnuthHash(this ArraySegment<char> chars)
- => Knuth(chars.AsSpan());
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this ulong num, ulong min, ulong max, bool inclusive = true)
+ {
+ if (min > max)
+ {
+ min ^= max;
+ max ^= min;
+ min ^= max;
+ }
+
+ return inclusive ? num >= min && num <= max : num > min && num < max;
+ }
- /// <summary>
- /// Computes a 64-bit Knuth hash from supplied characters.
- /// </summary>
- /// <param name="chars">Characters to compute the hash value from.</param>
- /// <returns>Computer 64-bit Knuth hash.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong CalculateKnuthHash(this char[] chars)
- => Knuth(chars.AsSpan());
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this float num, float min, float max, bool inclusive = true)
+ {
+ if (min > max)
+ return false;
- /// <summary>
- /// Computes a 64-bit Knuth hash from supplied characters.
- /// </summary>
- /// <param name="chars">Characters to compute the hash value from.</param>
- /// <param name="start">Offset in the array to start calculating from.</param>
- /// <param name="count">Number of characters to compute the hash from.</param>
- /// <returns>Computer 64-bit Knuth hash.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong CalculateKnuthHash(this char[] chars, int start, int count)
- => Knuth(chars.AsSpan(start, count));
+ return inclusive ? num >= min && num <= max : num > min && num < max;
+ }
- /// <summary>
- /// Computes a 64-bit Knuth hash from supplied characters.
- /// </summary>
- /// <param name="chars">Characters to compute the hash value from.</param>
- /// <returns>Computer 64-bit Knuth hash.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong CalculateKnuthHash(this string chars)
- => Knuth(chars.AsSpan());
+ /// <summary>
+ /// Tests whether given value is in supplied range, optionally allowing it to be an exclusive check.
+ /// </summary>
+ /// <param name="num">Number to test.</param>
+ /// <param name="min">Lower bound of the range.</param>
+ /// <param name="max">Upper bound of the range.</param>
+ /// <param name="inclusive">Whether the check is to be inclusive.</param>
+ /// <returns>Whether the value is in range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRange(this double num, double min, double max, bool inclusive = true)
+ {
+ if (min > max)
+ return false;
- /// <summary>
- /// Computes a 64-bit Knuth hash from supplied characters.
- /// </summary>
- /// <param name="chars">Characters to compute the hash value from.</param>
- /// <param name="start">Offset in the array to start calculating from.</param>
- /// <param name="count">Number of characters to compute the hash from.</param>
- /// <returns>Computer 64-bit Knuth hash.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ulong CalculateKnuthHash(this string chars, int start, int count)
- => Knuth(chars.AsSpan(start, count));
+ return inclusive ? num >= min && num <= max : num > min && num < max;
+ }
- /// <summary>
- /// Gets the two first elements of the <see cref="IEnumerable{T}"/>, if they exist.
- /// </summary>
- /// <param name="enumerable">The enumerable.</param>
- /// <param name="values">The output values. Undefined if <code>false</code> is returned.</param>
- /// <returns>Whether the <see cref="IEnumerable{T}"/> contained enough elements.</returns>
- internal static bool TryFirstTwo<T>(this IEnumerable<T> enumerable, out (T first, T second) values)
- {
- values = default;
+ /// <summary>
+ /// Returns whether supplied character is in any of the following ranges: a-z, A-Z, 0-9.
+ /// </summary>
+ /// <param name="c">Character to test.</param>
+ /// <returns>Whether the character is in basic alphanumeric character range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsBasicAlphanumeric(this char c)
+ => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
+
+ /// <summary>
+ /// Returns whether supplied character is in the 0-9 range.
+ /// </summary>
+ /// <param name="c">Character to test.</param>
+ /// <returns>Whether the character is in basic numeric digit character range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsBasicDigit(this char c)
+ => c >= '0' && c <= '9';
+
+ /// <summary>
+ /// Returns whether supplied character is in the a-z or A-Z range.
+ /// </summary>
+ /// <param name="c">Character to test.</param>
+ /// <returns>Whether the character is in basic letter character range.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsBasicLetter(this char c)
+ => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+
+ /// <summary>
+ /// Tests whether given string ends with given character.
+ /// </summary>
+ /// <param name="s">String to test.</param>
+ /// <param name="c">Character to test for.</param>
+ /// <returns>Whether the supplied string ends with supplied character.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool EndsWithCharacter(this string s, char c)
+ => s.Length >= 1 && s[^1] == c;
+
+ /// <summary>
+ /// Tests whether given string starts with given character.
+ /// </summary>
+ /// <param name="s">String to test.</param>
+ /// <param name="c">Character to test for.</param>
+ /// <returns>Whether the supplied string starts with supplied character.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool StartsWithCharacter(this string s, char c)
+ => s.Length >= 1 && s[0] == c;
+
+ // https://stackoverflow.com/questions/9545619/a-fast-hash-function-for-string-in-c-sharp
+ // Calls are inlined to call the underlying method directly
+ /// <summary>
+ /// Computes a 64-bit Knuth hash from supplied characters.
+ /// </summary>
+ /// <param name="chars">Characters to compute the hash value from.</param>
+ /// <returns>Computer 64-bit Knuth hash.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong CalculateKnuthHash(this ReadOnlySpan<char> chars)
+ => Knuth(chars);
+
+ /// <summary>
+ /// Computes a 64-bit Knuth hash from supplied characters.
+ /// </summary>
+ /// <param name="chars">Characters to compute the hash value from.</param>
+ /// <returns>Computer 64-bit Knuth hash.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong CalculateKnuthHash(this Span<char> chars)
+ => Knuth(chars);
+
+ /// <summary>
+ /// Computes a 64-bit Knuth hash from supplied characters.
+ /// </summary>
+ /// <param name="chars">Characters to compute the hash value from.</param>
+ /// <returns>Computer 64-bit Knuth hash.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong CalculateKnuthHash(this ReadOnlyMemory<char> chars)
+ => Knuth(chars.Span);
+
+ /// <summary>
+ /// Computes a 64-bit Knuth hash from supplied characters.
+ /// </summary>
+ /// <param name="chars">Characters to compute the hash value from.</param>
+ /// <returns>Computer 64-bit Knuth hash.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong CalculateKnuthHash(this Memory<char> chars)
+ => Knuth(chars.Span);
+
+ /// <summary>
+ /// Computes a 64-bit Knuth hash from supplied characters.
+ /// </summary>
+ /// <param name="chars">Characters to compute the hash value from.</param>
+ /// <returns>Computer 64-bit Knuth hash.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong CalculateKnuthHash(this ArraySegment<char> chars)
+ => Knuth(chars.AsSpan());
+
+ /// <summary>
+ /// Computes a 64-bit Knuth hash from supplied characters.
+ /// </summary>
+ /// <param name="chars">Characters to compute the hash value from.</param>
+ /// <returns>Computer 64-bit Knuth hash.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong CalculateKnuthHash(this char[] chars)
+ => Knuth(chars.AsSpan());
+
+ /// <summary>
+ /// Computes a 64-bit Knuth hash from supplied characters.
+ /// </summary>
+ /// <param name="chars">Characters to compute the hash value from.</param>
+ /// <param name="start">Offset in the array to start calculating from.</param>
+ /// <param name="count">Number of characters to compute the hash from.</param>
+ /// <returns>Computer 64-bit Knuth hash.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong CalculateKnuthHash(this char[] chars, int start, int count)
+ => Knuth(chars.AsSpan(start, count));
+
+ /// <summary>
+ /// Computes a 64-bit Knuth hash from supplied characters.
+ /// </summary>
+ /// <param name="chars">Characters to compute the hash value from.</param>
+ /// <returns>Computer 64-bit Knuth hash.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong CalculateKnuthHash(this string chars)
+ => Knuth(chars.AsSpan());
+
+ /// <summary>
+ /// Computes a 64-bit Knuth hash from supplied characters.
+ /// </summary>
+ /// <param name="chars">Characters to compute the hash value from.</param>
+ /// <param name="start">Offset in the array to start calculating from.</param>
+ /// <param name="count">Number of characters to compute the hash from.</param>
+ /// <returns>Computer 64-bit Knuth hash.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong CalculateKnuthHash(this string chars, int start, int count)
+ => Knuth(chars.AsSpan(start, count));
+
+ /// <summary>
+ /// Gets the two first elements of the <see cref="IEnumerable{T}"/>, if they exist.
+ /// </summary>
+ /// <param name="enumerable">The enumerable.</param>
+ /// <param name="values">The output values. Undefined if <code>false</code> is returned.</param>
+ /// <returns>Whether the <see cref="IEnumerable{T}"/> contained enough elements.</returns>
+ internal static bool TryFirstTwo<T>(this IEnumerable<T> enumerable, out (T first, T second) values)
+ {
+ values = default;
- using var enumerator = enumerable.GetEnumerator();
+ using var enumerator = enumerable.GetEnumerator();
- if (!enumerator.MoveNext())
- return false;
+ if (!enumerator.MoveNext())
+ return false;
- var first = enumerator.Current;
+ var first = enumerator.Current;
- if (!enumerator.MoveNext())
- return false;
+ if (!enumerator.MoveNext())
+ return false;
- values = (first, enumerator.Current);
- return true;
- }
+ values = (first, enumerator.Current);
+ return true;
+ }
- /// <summary>
- /// Knuths the.
- /// </summary>
- /// <param name="chars">The chars.</param>
- /// <returns>An ulong.</returns>
- private static ulong Knuth(ReadOnlySpan<char> chars)
- {
- var hash = 3074457345618258791ul;
- for (var i = 0; i < chars.Length; i++)
- hash = (hash + chars[i]) * 3074457345618258799ul;
- return hash;
+ /// <summary>
+ /// Knuths the.
+ /// </summary>
+ /// <param name="chars">The chars.</param>
+ /// <returns>An ulong.</returns>
+ private static ulong Knuth(ReadOnlySpan<char> chars)
+ {
+ var hash = 3074457345618258791ul;
+ for (var i = 0; i < chars.Length; i++)
+ hash = (hash + chars[i]) * 3074457345618258799ul;
+ return hash;
+ }
}
}
diff --git a/DisCatSharp.Common/Utilities/ReflectionUtilities.cs b/DisCatSharp.Common/Utilities/ReflectionUtilities.cs
index b2729ca03..8208228bd 100644
--- a/DisCatSharp.Common/Utilities/ReflectionUtilities.cs
+++ b/DisCatSharp.Common/Utilities/ReflectionUtilities.cs
@@ -1,74 +1,75 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Reflection;
using System.Runtime.Serialization;
-namespace DisCatSharp.Common.Utilities;
-
-/// <summary>
-/// Contains various utilities for use with .NET's reflection.
-/// </summary>
-public static class ReflectionUtilities
+namespace DisCatSharp.Common.Utilities
{
/// <summary>
- /// <para>Creates an empty, uninitialized instance of specified type.</para>
- /// <para>This method will not call the constructor for the specified type. As such, the object might not be properly initialized.</para>
+ /// Contains various utilities for use with .NET's reflection.
/// </summary>
- /// <remarks>
- /// This method is intended for reflection use only.
- /// </remarks>
- /// <param name="t">Type of the object to instantiate.</param>
- /// <returns>Empty, uninitialized object of specified type.</returns>
- public static object CreateEmpty(this Type t)
- => FormatterServices.GetUninitializedObject(t);
+ public static class ReflectionUtilities
+ {
+ /// <summary>
+ /// <para>Creates an empty, uninitialized instance of specified type.</para>
+ /// <para>This method will not call the constructor for the specified type. As such, the object might not be properly initialized.</para>
+ /// </summary>
+ /// <remarks>
+ /// This method is intended for reflection use only.
+ /// </remarks>
+ /// <param name="t">Type of the object to instantiate.</param>
+ /// <returns>Empty, uninitialized object of specified type.</returns>
+ public static object CreateEmpty(this Type t)
+ => FormatterServices.GetUninitializedObject(t);
- /// <summary>
- /// <para>Creates an empty, uninitialized instance of type <typeparamref name="T"/>.</para>
- /// <para>This method will not call the constructor for type <typeparamref name="T"/>. As such, the object might not be properly initialized.</para>
- /// </summary>
- /// <remarks>
- /// This method is intended for reflection use only.
- /// </remarks>
- /// <typeparam name="T">Type of the object to instantiate.</typeparam>
- /// <returns>Empty, uninitialized object of specified type.</returns>
- public static T CreateEmpty<T>()
- => (T)FormatterServices.GetUninitializedObject(typeof(T));
+ /// <summary>
+ /// <para>Creates an empty, uninitialized instance of type <typeparamref name="T"/>.</para>
+ /// <para>This method will not call the constructor for type <typeparamref name="T"/>. As such, the object might not be properly initialized.</para>
+ /// </summary>
+ /// <remarks>
+ /// This method is intended for reflection use only.
+ /// </remarks>
+ /// <typeparam name="T">Type of the object to instantiate.</typeparam>
+ /// <returns>Empty, uninitialized object of specified type.</returns>
+ public static T CreateEmpty<T>()
+ => (T)FormatterServices.GetUninitializedObject(typeof(T));
- /// <summary>
- /// Converts a given object into a dictionary of property name to property value mappings.
- /// </summary>
- /// <typeparam name="T">Type of object to convert.</typeparam>
- /// <param name="obj">Object to convert.</param>
- /// <returns>Converted dictionary.</returns>
- public static IReadOnlyDictionary<string, object> ToDictionary<T>(this T obj)
- {
- if (obj == null)
- throw new NullReferenceException();
+ /// <summary>
+ /// Converts a given object into a dictionary of property name to property value mappings.
+ /// </summary>
+ /// <typeparam name="T">Type of object to convert.</typeparam>
+ /// <param name="obj">Object to convert.</param>
+ /// <returns>Converted dictionary.</returns>
+ public static IReadOnlyDictionary<string, object> ToDictionary<T>(this T obj)
+ {
+ if (obj == null)
+ throw new NullReferenceException();
- return new CharSpanLookupReadOnlyDictionary<object>(typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
- .Select(x => new KeyValuePair<string, object>(x.Name, x.GetValue(obj))));
+ return new CharSpanLookupReadOnlyDictionary<object>(typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Select(x => new KeyValuePair<string, object>(x.Name, x.GetValue(obj))));
+ }
}
}
diff --git a/DisCatSharp.Common/Utilities/RuntimeInformation.cs b/DisCatSharp.Common/Utilities/RuntimeInformation.cs
index e1de8c2c5..3aeebe8ff 100644
--- a/DisCatSharp.Common/Utilities/RuntimeInformation.cs
+++ b/DisCatSharp.Common/Utilities/RuntimeInformation.cs
@@ -1,77 +1,78 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using System.Linq;
using System.Reflection;
using System.Text;
-namespace DisCatSharp.Common.Utilities;
-
-/// <summary>
-/// Gets information about current runtime.
-/// </summary>
-public static class RuntimeInformation
+namespace DisCatSharp.Common.Utilities
{
/// <summary>
- /// Gets the current runtime's version.
+ /// Gets information about current runtime.
/// </summary>
- public static string Version { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RuntimeInformation"/> class.
- /// </summary>
- static RuntimeInformation()
+ public static class RuntimeInformation
{
- var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
- var mscorlib = loadedAssemblies.Select(x => new { Assembly = x, AssemblyName = x.GetName() })
- .FirstOrDefault(x => x.AssemblyName.Name == "mscorlib" || x.AssemblyName.Name == "System.Private.CoreLib");
+ /// <summary>
+ /// Gets the current runtime's version.
+ /// </summary>
+ public static string Version { get; }
- var location = mscorlib.Assembly.Location;
- var assemblyFile = new FileInfo(location);
- var versionFile = new FileInfo(Path.Combine(assemblyFile.Directory.FullName, ".version"));
- if (versionFile.Exists)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RuntimeInformation"/> class.
+ /// </summary>
+ static RuntimeInformation()
{
- var lines = File.ReadAllLines(versionFile.FullName, new UTF8Encoding(false));
+ var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
+ var mscorlib = loadedAssemblies.Select(x => new { Assembly = x, AssemblyName = x.GetName() })
+ .FirstOrDefault(x => x.AssemblyName.Name == "mscorlib" || x.AssemblyName.Name == "System.Private.CoreLib");
- if (lines.Length >= 2)
+ var location = mscorlib.Assembly.Location;
+ var assemblyFile = new FileInfo(location);
+ var versionFile = new FileInfo(Path.Combine(assemblyFile.Directory.FullName, ".version"));
+ if (versionFile.Exists)
{
- Version = lines[1];
- return;
+ var lines = File.ReadAllLines(versionFile.FullName, new UTF8Encoding(false));
+
+ if (lines.Length >= 2)
+ {
+ Version = lines[1];
+ return;
+ }
}
- }
- var infVersion = mscorlib.Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
- if (infVersion != null)
- {
- var infVersionString = infVersion.InformationalVersion;
- if (!string.IsNullOrWhiteSpace(infVersionString))
+ var infVersion = mscorlib.Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
+ if (infVersion != null)
{
- Version = infVersionString.Split(' ').First();
- return;
+ var infVersionString = infVersion.InformationalVersion;
+ if (!string.IsNullOrWhiteSpace(infVersionString))
+ {
+ Version = infVersionString.Split(' ').First();
+ return;
+ }
}
- }
- Version = mscorlib.AssemblyName.Version.ToString();
+ Version = mscorlib.AssemblyName.Version.ToString();
+ }
}
}
diff --git a/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs b/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs
index bfdc19779..627906079 100644
--- a/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs
+++ b/DisCatSharp.Configuration.Tests/ConfigurationExtensionTests.cs
@@ -1,336 +1,337 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using DisCatSharp.Configuration.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Xunit;
-namespace DisCatSharp.Configuration.Tests;
-
-public class ConfigurationExtensionTests
+namespace DisCatSharp.Configuration.Tests
{
- #region Test Classes
- class SampleClass
- {
- public int Amount { get; set; }
- public string? Email { get; set; }
- }
-
- class ClassWithArray
+ public class ConfigurationExtensionTests
{
- public int[] Values { get; set; } = { 1, 2, 3, 4, 5 };
- public string[] Strings { get; set; } = { "1", "2", "3", "4", "5" };
- }
+ #region Test Classes
+ class SampleClass
+ {
+ public int Amount { get; set; }
+ public string? Email { get; set; }
+ }
- class ClassWithEnumerable
- {
- public IEnumerable<int> Values { get; set; } = new[] { 1, 2, 3, 4, 5 };
- public IEnumerable<string> Strings { get; set; } = new[] { "1", "2", "3", "4", "5" };
- }
+ class ClassWithArray
+ {
+ public int[] Values { get; set; } = { 1, 2, 3, 4, 5 };
+ public string[] Strings { get; set; } = { "1", "2", "3", "4", "5" };
+ }
- class ClassWithList
- {
- public List<string> Strings { get; set; } = new()
+ class ClassWithEnumerable
{
- "1",
- "2",
- "3",
- "4",
- "5"
- };
-
- public List<int> Values { get; set; } = new()
+ public IEnumerable<int> Values { get; set; } = new[] { 1, 2, 3, 4, 5 };
+ public IEnumerable<string> Strings { get; set; } = new[] { "1", "2", "3", "4", "5" };
+ }
+
+ class ClassWithList
{
- 1,
- 2,
- 3,
- 4,
- 5
- };
- }
+ public List<string> Strings { get; set; } = new()
+ {
+ "1",
+ "2",
+ "3",
+ "4",
+ "5"
+ };
+
+ public List<int> Values { get; set; } = new()
+ {
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ };
+ }
- class SampleClass2
- {
- public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(7);
- public string Name { get; set; } = "Sample";
- public string ConstructorValue { get; }
- public SampleClass2(string value)
+ class SampleClass2
{
- this.ConstructorValue = value;
+ public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(7);
+ public string Name { get; set; } = "Sample";
+ public string ConstructorValue { get; }
+ public SampleClass2(string value)
+ {
+ this.ConstructorValue = value;
+ }
}
- }
- #endregion
+ #endregion
- private IConfiguration EnumerableTestConfiguration() =>
- new ConfigurationBuilder()
- .AddJsonFile("enumerable-test.json")
- .Build();
+ private IConfiguration EnumerableTestConfiguration() =>
+ new ConfigurationBuilder()
+ .AddJsonFile("enumerable-test.json")
+ .Build();
- private IConfiguration HasSectionWithSuffixConfiguration() =>
- new ConfigurationBuilder()
- .AddJsonFile("section-with-suffix.json")
- .Build();
+ private IConfiguration HasSectionWithSuffixConfiguration() =>
+ new ConfigurationBuilder()
+ .AddJsonFile("section-with-suffix.json")
+ .Build();
+
+ private IConfiguration HasSectionNoSuffixConfiguration() =>
+ new ConfigurationBuilder()
+ .AddJsonFile("section-no-suffix.json")
+ .Build();
- private IConfiguration HasSectionNoSuffixConfiguration() =>
- new ConfigurationBuilder()
- .AddJsonFile("section-no-suffix.json")
+ private IConfiguration BasicDiscordConfiguration() => new ConfigurationBuilder()
+ .AddJsonFile("default-discord.json")
.Build();
- private IConfiguration BasicDiscordConfiguration() => new ConfigurationBuilder()
- .AddJsonFile("default-discord.json")
- .Build();
+ private IConfiguration DiscordIntentsConfig() => new ConfigurationBuilder()
+ .AddJsonFile("intents-discord.json")
+ .Build();
- private IConfiguration DiscordIntentsConfig() => new ConfigurationBuilder()
- .AddJsonFile("intents-discord.json")
+ private IConfiguration DiscordHaphazardConfig() => new ConfigurationBuilder()
+ .AddJsonFile("haphazard-discord.json")
.Build();
- private IConfiguration DiscordHaphazardConfig() => new ConfigurationBuilder()
- .AddJsonFile("haphazard-discord.json")
- .Build();
+ private IConfiguration SampleConfig() => new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary<string, string>
+ {
+ { "Sample:Amount", "200" },
+ { "Sample:Email", "[email protected]" }
+ })
+ .Build();
- private IConfiguration SampleConfig() => new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary<string, string>
- {
- { "Sample:Amount", "200" },
- { "Sample:Email", "[email protected]" }
- })
- .Build();
+ private IConfiguration SampleClass2Configuration_Default() => new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary<string, string>
+ {
+ {"Random:Stuff", "Meow"},
+ {"SampleClass2:Name", "Purfection"}
+ })
+ .Build();
- private IConfiguration SampleClass2Configuration_Default() => new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary<string, string>
- {
- {"Random:Stuff", "Meow"},
- {"SampleClass2:Name", "Purfection"}
- })
- .Build();
+ private IConfiguration SampleClass2Configuration_Change() => new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary<string, string>
+ {
+ { "SampleClass:Timeout", "01:30:00" }, { "SampleClass:NotValid", "Something" }
+ })
+ .Build();
- private IConfiguration SampleClass2Configuration_Change() => new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary<string, string>
- {
- { "SampleClass:Timeout", "01:30:00" }, { "SampleClass:NotValid", "Something" }
- })
- .Build();
+ private IConfiguration SampleClass2EnumerableTest() => new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary<string, string>
+ {
+ { "SampleClass:EnumerableTest", "[\"10\",\"20\",\"30\"]" }
+ })
+ .Build();
- private IConfiguration SampleClass2EnumerableTest() => new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary<string, string>
- {
- { "SampleClass:EnumerableTest", "[\"10\",\"20\",\"30\"]" }
- })
- .Build();
+ private IConfiguration SampleClass2ArrayTest() => new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary<string, string>
+ {
+ { "SampleClass:ArrayTest", "[\"10\",\"20\",\"30\"]" }
+ })
+ .Build();
- private IConfiguration SampleClass2ArrayTest() => new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary<string, string>
- {
- { "SampleClass:ArrayTest", "[\"10\",\"20\",\"30\"]" }
- })
- .Build();
+ private IConfiguration SampleClass2ListTest() => new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary<string, string>
+ {
+ { "SampleClass:ListTest", "[\"10\",\"20\",\"30\"]" }
+ })
+ .Build();
- private IConfiguration SampleClass2ListTest() => new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary<string, string>
+ [Fact]
+ public void TestExtractDiscordConfig_Intents()
{
- { "SampleClass:ListTest", "[\"10\",\"20\",\"30\"]" }
- })
- .Build();
+ var source = this.DiscordIntentsConfig();
- [Fact]
- public void TestExtractDiscordConfig_Intents()
- {
- var source = this.DiscordIntentsConfig();
-
- var config = source.ExtractConfig<DiscordConfiguration>("Discord");
+ var config = source.ExtractConfig<DiscordConfiguration>("Discord");
- var expected = DiscordIntents.GuildEmojisAndStickers | DiscordIntents.GuildMembers |
- DiscordIntents.GuildInvites | DiscordIntents.GuildMessageReactions;
+ var expected = DiscordIntents.GuildEmojisAndStickers | DiscordIntents.GuildMembers |
+ DiscordIntents.GuildInvites | DiscordIntents.GuildMessageReactions;
- Assert.Equal(expected, config.Intents);
- }
+ Assert.Equal(expected, config.Intents);
+ }
- [Fact]
- public void TestExtractDiscordConfig_Haphazard()
- {
- var source = this.DiscordHaphazardConfig();
+ [Fact]
+ public void TestExtractDiscordConfig_Haphazard()
+ {
+ var source = this.DiscordHaphazardConfig();
- var config = source.ExtractConfig<DiscordConfiguration>("Discord");
- var expectedIntents = DiscordIntents.GuildEmojisAndStickers | DiscordIntents.GuildMembers |
- DiscordIntents.Guilds;
+ var config = source.ExtractConfig<DiscordConfiguration>("Discord");
+ var expectedIntents = DiscordIntents.GuildEmojisAndStickers | DiscordIntents.GuildMembers |
+ DiscordIntents.Guilds;
- Assert.Equal(expectedIntents, config.Intents);
- Assert.True(config.MobileStatus);
- Assert.Equal(1000, config.LargeThreshold);
- Assert.Equal(TimeSpan.FromHours(10), config.HttpTimeout);
- }
+ Assert.Equal(expectedIntents, config.Intents);
+ Assert.True(config.MobileStatus);
+ Assert.Equal(1000, config.LargeThreshold);
+ Assert.Equal(TimeSpan.FromHours(10), config.HttpTimeout);
+ }
- [Fact]
- public void TestExtractDiscordConfig_Default()
- {
- var source = this.BasicDiscordConfiguration();
- var config = source.ExtractConfig<DiscordConfiguration>("Discord");
-
- Assert.Equal("1234567890", config.Token);
- Assert.Equal(TokenType.Bot, config.TokenType);
- Assert.Equal(LogLevel.Information, config.MinimumLogLevel);
- Assert.True(config.UseRelativeRatelimit);
- Assert.Equal("yyyy-MM-dd HH:mm:ss zzz", config.LogTimestampFormat);
- Assert.Equal(250, config.LargeThreshold);
- Assert.True(config.AutoReconnect);
- Assert.Equal(123123, config.ShardId);
- Assert.Equal(GatewayCompressionLevel.Stream, config.GatewayCompressionLevel);
- Assert.Equal(1024, config.MessageCacheSize);
- Assert.Equal(TimeSpan.FromSeconds(20), config.HttpTimeout);
- Assert.False(config.ReconnectIndefinitely);
- Assert.True(config.AlwaysCacheMembers);
- Assert.Equal(DiscordIntents.AllUnprivileged, config.Intents);
- Assert.False(config.MobileStatus);
- Assert.False(config.UseCanary);
- Assert.False(config.AutoRefreshChannelCache);
- }
+ [Fact]
+ public void TestExtractDiscordConfig_Default()
+ {
+ var source = this.BasicDiscordConfiguration();
+ var config = source.ExtractConfig<DiscordConfiguration>("Discord");
+
+ Assert.Equal("1234567890", config.Token);
+ Assert.Equal(TokenType.Bot, config.TokenType);
+ Assert.Equal(LogLevel.Information, config.MinimumLogLevel);
+ Assert.True(config.UseRelativeRatelimit);
+ Assert.Equal("yyyy-MM-dd HH:mm:ss zzz", config.LogTimestampFormat);
+ Assert.Equal(250, config.LargeThreshold);
+ Assert.True(config.AutoReconnect);
+ Assert.Equal(123123, config.ShardId);
+ Assert.Equal(GatewayCompressionLevel.Stream, config.GatewayCompressionLevel);
+ Assert.Equal(1024, config.MessageCacheSize);
+ Assert.Equal(TimeSpan.FromSeconds(20), config.HttpTimeout);
+ Assert.False(config.ReconnectIndefinitely);
+ Assert.True(config.AlwaysCacheMembers);
+ Assert.Equal(DiscordIntents.AllUnprivileged, config.Intents);
+ Assert.False(config.MobileStatus);
+ Assert.False(config.UseCanary);
+ Assert.False(config.AutoRefreshChannelCache);
+ }
- [Fact]
- public void TestSection()
- {
- var source = this.SampleConfig();
- var config = source.ExtractConfig<SampleClass>("Sample", null);
+ [Fact]
+ public void TestSection()
+ {
+ var source = this.SampleConfig();
+ var config = source.ExtractConfig<SampleClass>("Sample", null);
- Assert.Equal(200, config.Amount);
- Assert.Equal("[email protected]", config.Email);
- }
+ Assert.Equal(200, config.Amount);
+ Assert.Equal("[email protected]", config.Email);
+ }
- [Fact]
- public void TestExtractConfig_V2_Default()
- {
- var source = this.SampleClass2Configuration_Default();
- var config = (SampleClass2) source.ExtractConfig("SampleClass", () => new SampleClass2("Test"), null);
- Assert.Equal(TimeSpan.FromMinutes(7), config.Timeout);
- Assert.Equal("Test", config.ConstructorValue);
- Assert.Equal("Sample", config.Name);
- }
+ [Fact]
+ public void TestExtractConfig_V2_Default()
+ {
+ var source = this.SampleClass2Configuration_Default();
+ var config = (SampleClass2) source.ExtractConfig("SampleClass", () => new SampleClass2("Test"), null);
+ Assert.Equal(TimeSpan.FromMinutes(7), config.Timeout);
+ Assert.Equal("Test", config.ConstructorValue);
+ Assert.Equal("Sample", config.Name);
+ }
- [Fact]
- public void TestExtractConfig_V2_Change()
- {
- var source = this.SampleClass2Configuration_Change();
- var config = (SampleClass2) source.ExtractConfig("SampleClass", () => new SampleClass2("Test123"), null);
- var span = new TimeSpan(0, 1, 30, 0);
- Assert.Equal(span, config.Timeout);
- Assert.Equal("Test123", config.ConstructorValue);
- Assert.Equal("Sample", config.Name);
- }
+ [Fact]
+ public void TestExtractConfig_V2_Change()
+ {
+ var source = this.SampleClass2Configuration_Change();
+ var config = (SampleClass2) source.ExtractConfig("SampleClass", () => new SampleClass2("Test123"), null);
+ var span = new TimeSpan(0, 1, 30, 0);
+ Assert.Equal(span, config.Timeout);
+ Assert.Equal("Test123", config.ConstructorValue);
+ Assert.Equal("Sample", config.Name);
+ }
- [Fact]
- public void TestExtractConfig_V3_Default()
- {
- var source = this.SampleClass2Configuration_Default();
- var config =
- (SampleClass2)new ConfigSection(ref source, "SampleClass", null).ExtractConfig(() =>
- new SampleClass2("Meow"));
-
- Assert.Equal("Meow", config.ConstructorValue);
- Assert.Equal(TimeSpan.FromMinutes(7), config.Timeout);
- Assert.Equal("Sample", config.Name);
- }
+ [Fact]
+ public void TestExtractConfig_V3_Default()
+ {
+ var source = this.SampleClass2Configuration_Default();
+ var config =
+ (SampleClass2)new ConfigSection(ref source, "SampleClass", null).ExtractConfig(() =>
+ new SampleClass2("Meow"));
+
+ Assert.Equal("Meow", config.ConstructorValue);
+ Assert.Equal(TimeSpan.FromMinutes(7), config.Timeout);
+ Assert.Equal("Sample", config.Name);
+ }
- [Fact]
- public void TestExtractConfig_V3_Change()
- {
- var source = this.SampleClass2Configuration_Change();
- var config =
- (SampleClass2)new ConfigSection(ref source, "SampleClass", null).ExtractConfig(() =>
- new SampleClass2("Meow"));
-
- Assert.Equal("Meow", config.ConstructorValue);
- var span = new TimeSpan(0, 1, 30, 0);
- Assert.Equal(span, config.Timeout);
- Assert.Equal("Sample", config.Name);
- }
+ [Fact]
+ public void TestExtractConfig_V3_Change()
+ {
+ var source = this.SampleClass2Configuration_Change();
+ var config =
+ (SampleClass2)new ConfigSection(ref source, "SampleClass", null).ExtractConfig(() =>
+ new SampleClass2("Meow"));
+
+ Assert.Equal("Meow", config.ConstructorValue);
+ var span = new TimeSpan(0, 1, 30, 0);
+ Assert.Equal(span, config.Timeout);
+ Assert.Equal("Sample", config.Name);
+ }
- [Fact]
- public void TestExtractConfig_Enumerable()
- {
- var source = this.EnumerableTestConfiguration();
- var config =
- (ClassWithEnumerable)new ConfigSection(ref source, "ClassWithEnumerable", null).ExtractConfig(() =>
- new ClassWithEnumerable());
-
- Assert.NotNull(config.Values);
- Assert.Equal(3, config.Values.Count());
- Assert.NotNull(config.Strings);
- Assert.Equal(3, config.Values.Count());
- }
+ [Fact]
+ public void TestExtractConfig_Enumerable()
+ {
+ var source = this.EnumerableTestConfiguration();
+ var config =
+ (ClassWithEnumerable)new ConfigSection(ref source, "ClassWithEnumerable", null).ExtractConfig(() =>
+ new ClassWithEnumerable());
+
+ Assert.NotNull(config.Values);
+ Assert.Equal(3, config.Values.Count());
+ Assert.NotNull(config.Strings);
+ Assert.Equal(3, config.Values.Count());
+ }
- [Fact]
- public void TestExtractConfig_Array()
- {
- var source = this.EnumerableTestConfiguration();
- var config =
- (ClassWithArray)new ConfigSection(ref source, "ClassWithArray", null).ExtractConfig(() =>
- new ClassWithArray());
- Assert.NotNull(config.Values);
- Assert.Equal(3, config.Values.Length);
- Assert.NotNull(config.Strings);
- Assert.Equal(3, config.Values.Length);
- }
+ [Fact]
+ public void TestExtractConfig_Array()
+ {
+ var source = this.EnumerableTestConfiguration();
+ var config =
+ (ClassWithArray)new ConfigSection(ref source, "ClassWithArray", null).ExtractConfig(() =>
+ new ClassWithArray());
+ Assert.NotNull(config.Values);
+ Assert.Equal(3, config.Values.Length);
+ Assert.NotNull(config.Strings);
+ Assert.Equal(3, config.Values.Length);
+ }
- [Fact]
- public void TestExtractConfig_List()
- {
- var source = this.EnumerableTestConfiguration();
- var config =
- (ClassWithList)new ConfigSection(ref source, "ClassWithList", null).ExtractConfig(() =>
- new ClassWithList());
- Assert.NotNull(config.Values);
- Assert.Equal(3, config.Values.Count);
- Assert.NotNull(config.Strings);
- Assert.Equal(3, config.Values.Count);
- }
+ [Fact]
+ public void TestExtractConfig_List()
+ {
+ var source = this.EnumerableTestConfiguration();
+ var config =
+ (ClassWithList)new ConfigSection(ref source, "ClassWithList", null).ExtractConfig(() =>
+ new ClassWithList());
+ Assert.NotNull(config.Values);
+ Assert.Equal(3, config.Values.Count);
+ Assert.NotNull(config.Strings);
+ Assert.Equal(3, config.Values.Count);
+ }
- [Fact]
- public void TestHasSectionWithSuffix()
- {
- var source = this.HasSectionWithSuffixConfiguration();
+ [Fact]
+ public void TestHasSectionWithSuffix()
+ {
+ var source = this.HasSectionWithSuffixConfiguration();
- Assert.True(source.HasSection("DiscordConfiguration"));
- Assert.False(source.HasSection("Discord"));
+ Assert.True(source.HasSection("DiscordConfiguration"));
+ Assert.False(source.HasSection("Discord"));
#pragma warning disable 8625
- Assert.False(source.HasSection("DiscordConfiguration", null));
+ Assert.False(source.HasSection("DiscordConfiguration", null));
#pragma warning restore 8625
- }
+ }
- [Fact]
- public void TestHasSectionNoSuffix()
- {
- var source = this.HasSectionNoSuffixConfiguration();
+ [Fact]
+ public void TestHasSectionNoSuffix()
+ {
+ var source = this.HasSectionNoSuffixConfiguration();
- Assert.True(source.HasSection("Discord"));
- Assert.False(source.HasSection("DiscordConfiguration"));
+ Assert.True(source.HasSection("Discord"));
+ Assert.False(source.HasSection("DiscordConfiguration"));
#pragma warning disable 8625
- Assert.False(source.HasSection("Discord", null));
+ Assert.False(source.HasSection("Discord", null));
#pragma warning restore 8625
+ }
}
}
diff --git a/DisCatSharp.Configuration/ConfigurationExtensions.cs b/DisCatSharp.Configuration/ConfigurationExtensions.cs
index 3e08d68db..0f7729d29 100644
--- a/DisCatSharp.Configuration/ConfigurationExtensions.cs
+++ b/DisCatSharp.Configuration/ConfigurationExtensions.cs
@@ -1,312 +1,313 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using System.Linq;
using DisCatSharp.Configuration.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.Configuration;
-
-/// <summary>
-/// The configuration extensions.
-/// </summary>
-internal static class ConfigurationExtensions
+namespace DisCatSharp.Configuration
{
/// <summary>
- /// The factory error message.
- /// </summary>
- private const string FACTORY_ERROR_MESSAGE = "Require a function which provides a default entity to work with";
- /// <summary>
- /// The default root lib.
- /// </summary>
- public const string DEFAULT_ROOT_LIB = "DisCatSharp";
- /// <summary>
- /// The config suffix.
+ /// The configuration extensions.
/// </summary>
- private const string CONFIG_SUFFIX = "Configuration";
-
-
- /// <summary>
- /// Easily piece together paths that will work within <see cref="IConfiguration"/>
- /// </summary>
- /// <param name="config">(not used - only for adding context based functionality)</param>
- /// <param name="values">The strings to piece together</param>
- /// <returns>Strings joined together via ':'</returns>
- public static string ConfigPath(this IConfiguration config, params string[] values) => string.Join(":", values);
-
- /// <summary>
- /// Skims over the configuration section and only overrides values that are explicitly defined within the config
- /// </summary>
- /// <param name="config">Instance of config</param>
- /// <param name="section">Section which contains values for <paramref name="config"/></param>
- private static void HydrateInstance(ref object config, ConfigSection section)
+ internal static class ConfigurationExtensions
{
- var props = config.GetType().GetProperties();
-
- foreach (var prop in props)
+ /// <summary>
+ /// The factory error message.
+ /// </summary>
+ private const string FACTORY_ERROR_MESSAGE = "Require a function which provides a default entity to work with";
+ /// <summary>
+ /// The default root lib.
+ /// </summary>
+ public const string DEFAULT_ROOT_LIB = "DisCatSharp";
+ /// <summary>
+ /// The config suffix.
+ /// </summary>
+ private const string CONFIG_SUFFIX = "Configuration";
+
+
+ /// <summary>
+ /// Easily piece together paths that will work within <see cref="IConfiguration"/>
+ /// </summary>
+ /// <param name="config">(not used - only for adding context based functionality)</param>
+ /// <param name="values">The strings to piece together</param>
+ /// <returns>Strings joined together via ':'</returns>
+ public static string ConfigPath(this IConfiguration config, params string[] values) => string.Join(":", values);
+
+ /// <summary>
+ /// Skims over the configuration section and only overrides values that are explicitly defined within the config
+ /// </summary>
+ /// <param name="config">Instance of config</param>
+ /// <param name="section">Section which contains values for <paramref name="config"/></param>
+ private static void HydrateInstance(ref object config, ConfigSection section)
{
- // Must have a set method for this to work, otherwise continue on
- if (prop.SetMethod == null)
- continue;
+ var props = config.GetType().GetProperties();
- var entry = section.GetValue(prop.Name);
- object? value = null;
-
- if (typeof(string) == prop.PropertyType)
+ foreach (var prop in props)
{
- // We do NOT want to override value if nothing was provided
- if (!string.IsNullOrEmpty(entry))
- prop.SetValue(config, entry);
+ // Must have a set method for this to work, otherwise continue on
+ if (prop.SetMethod == null)
+ continue;
- continue;
- }
+ var entry = section.GetValue(prop.Name);
+ object? value = null;
- // We need to address collections a bit differently
- // They can come in the form of "root:section:name" with a string representation OR
- // "root:section:name:0" <--- this is not detectable when checking the above path
- if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
- {
- value = string.IsNullOrEmpty(section.GetValue(prop.Name))
- ? section.Config
- .GetSection(section.GetPath(prop.Name)).Get(prop.PropertyType)
- : Newtonsoft.Json.JsonConvert.DeserializeObject(entry, prop.PropertyType);
+ if (typeof(string) == prop.PropertyType)
+ {
+ // We do NOT want to override value if nothing was provided
+ if (!string.IsNullOrEmpty(entry))
+ prop.SetValue(config, entry);
- if (value == null)
continue;
+ }
- prop.SetValue(config, value);
- }
+ // We need to address collections a bit differently
+ // They can come in the form of "root:section:name" with a string representation OR
+ // "root:section:name:0" <--- this is not detectable when checking the above path
+ if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
+ {
+ value = string.IsNullOrEmpty(section.GetValue(prop.Name))
+ ? section.Config
+ .GetSection(section.GetPath(prop.Name)).Get(prop.PropertyType)
+ : Newtonsoft.Json.JsonConvert.DeserializeObject(entry, prop.PropertyType);
- // From this point onward we require the 'entry' value to have something useful
- if (string.IsNullOrEmpty(entry))
- continue;
+ if (value == null)
+ continue;
- try
- {
- // Primitive types are simple to convert
- if (prop.PropertyType.IsPrimitive)
- value = Convert.ChangeType(entry, prop.PropertyType);
- else
- {
- // The following types require a different approach
- if (prop.PropertyType.IsEnum)
- value = Enum.Parse(prop.PropertyType, entry);
- else if (typeof(TimeSpan) == prop.PropertyType)
- value = TimeSpan.Parse(entry);
- else if (typeof(DateTime) == prop.PropertyType)
- value = DateTime.Parse(entry);
- else if (typeof(DateTimeOffset) == prop.PropertyType)
- value = DateTimeOffset.Parse(entry);
+ prop.SetValue(config, value);
}
- // Update value within our config instance
- prop.SetValue(config, value);
- }
- catch (Exception ex)
- {
- Console.Error.WriteLine(
- $"Unable to convert value of '{entry}' to type '{prop.PropertyType.Name}' for prop '{prop.Name}' in config '{config.GetType().Name}'\n\t\t{ex.Message}");
+ // From this point onward we require the 'entry' value to have something useful
+ if (string.IsNullOrEmpty(entry))
+ continue;
+
+ try
+ {
+ // Primitive types are simple to convert
+ if (prop.PropertyType.IsPrimitive)
+ value = Convert.ChangeType(entry, prop.PropertyType);
+ else
+ {
+ // The following types require a different approach
+ if (prop.PropertyType.IsEnum)
+ value = Enum.Parse(prop.PropertyType, entry);
+ else if (typeof(TimeSpan) == prop.PropertyType)
+ value = TimeSpan.Parse(entry);
+ else if (typeof(DateTime) == prop.PropertyType)
+ value = DateTime.Parse(entry);
+ else if (typeof(DateTimeOffset) == prop.PropertyType)
+ value = DateTimeOffset.Parse(entry);
+ }
+
+ // Update value within our config instance
+ prop.SetValue(config, value);
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine(
+ $"Unable to convert value of '{entry}' to type '{prop.PropertyType.Name}' for prop '{prop.Name}' in config '{config.GetType().Name}'\n\t\t{ex.Message}");
+ }
}
}
- }
- /// <summary>
- /// Instantiate an entity using <paramref name="factory"/> then walk through the specified <paramref name="section"/>
- /// and translate user-defined config values to the instantiated instance from <paramref name="factory"/>
- /// </summary>
- /// <param name="section">Section containing values for targeted config</param>
- /// <param name="factory">Function which generates a default entity</param>
- /// <returns>Hydrated instance of an entity which contains user-defined values (if any)</returns>
- /// <exception cref="ArgumentNullException">When <paramref name="factory"/> is null</exception>
- public static object ExtractConfig(this ConfigSection section, Func<object> factory)
- {
- if (factory == null)
- throw new ArgumentNullException(nameof(factory), FACTORY_ERROR_MESSAGE);
+ /// <summary>
+ /// Instantiate an entity using <paramref name="factory"/> then walk through the specified <paramref name="section"/>
+ /// and translate user-defined config values to the instantiated instance from <paramref name="factory"/>
+ /// </summary>
+ /// <param name="section">Section containing values for targeted config</param>
+ /// <param name="factory">Function which generates a default entity</param>
+ /// <returns>Hydrated instance of an entity which contains user-defined values (if any)</returns>
+ /// <exception cref="ArgumentNullException">When <paramref name="factory"/> is null</exception>
+ public static object ExtractConfig(this ConfigSection section, Func<object> factory)
+ {
+ if (factory == null)
+ throw new ArgumentNullException(nameof(factory), FACTORY_ERROR_MESSAGE);
- // Create default instance
- var config = factory();
+ // Create default instance
+ var config = factory();
- HydrateInstance(ref config, section);
+ HydrateInstance(ref config, section);
- return config;
- }
+ return config;
+ }
- /// <summary>
- /// Instantiate an entity using <paramref name="factory"/> then walk through the specified <paramref name="sectionName"/>
- /// in <paramref name="config"/>. Translate user-defined config values to the instantiated instance from <paramref name="factory"/>
- /// </summary>
- /// <param name="config">Loaded App Configuration</param>
- /// <param name="sectionName">Name of section to load</param>
- /// <param name="factory">Function which creates a default entity to work with</param>
- /// <param name="rootSectionName">(Optional) Used when section is nested within another. Default value is <see cref="DEFAULT_ROOT_LIB"/></param>
- /// <returns>Hydrated instance of an entity which contains user-defined values (if any)</returns>
- /// <exception cref="ArgumentNullException">When <paramref name="factory"/> is null</exception>
- public static object ExtractConfig(this IConfiguration config, string sectionName, Func<object> factory,
- string? rootSectionName = DEFAULT_ROOT_LIB)
- {
- if (factory == null)
- throw new ArgumentNullException(nameof(factory), FACTORY_ERROR_MESSAGE);
+ /// <summary>
+ /// Instantiate an entity using <paramref name="factory"/> then walk through the specified <paramref name="sectionName"/>
+ /// in <paramref name="config"/>. Translate user-defined config values to the instantiated instance from <paramref name="factory"/>
+ /// </summary>
+ /// <param name="config">Loaded App Configuration</param>
+ /// <param name="sectionName">Name of section to load</param>
+ /// <param name="factory">Function which creates a default entity to work with</param>
+ /// <param name="rootSectionName">(Optional) Used when section is nested within another. Default value is <see cref="DEFAULT_ROOT_LIB"/></param>
+ /// <returns>Hydrated instance of an entity which contains user-defined values (if any)</returns>
+ /// <exception cref="ArgumentNullException">When <paramref name="factory"/> is null</exception>
+ public static object ExtractConfig(this IConfiguration config, string sectionName, Func<object> factory,
+ string? rootSectionName = DEFAULT_ROOT_LIB)
+ {
+ if (factory == null)
+ throw new ArgumentNullException(nameof(factory), FACTORY_ERROR_MESSAGE);
- // create default instance
- var instance = factory();
+ // create default instance
+ var instance = factory();
- HydrateInstance(ref instance, new ConfigSection(ref config, sectionName, rootSectionName));
+ HydrateInstance(ref instance, new ConfigSection(ref config, sectionName, rootSectionName));
- return instance;
- }
+ return instance;
+ }
- /// <summary>
- /// Instantiate a new instance of <typeparamref name="TConfig"/>, then walk through the specified <paramref name="sectionName"/>
- /// in <paramref name="config"/>. Translate user-defined config values to the <typeparamref name="TConfig"/> instance.
- /// </summary>
- /// <param name="config">Loaded App Configuration</param>
- /// <param name="serviceProvider"></param>
- /// <param name="sectionName">Name of section to load</param>
- /// <param name="rootSectionName">(Optional) Used when section is nested with another. Default value is <see cref="DEFAULT_ROOT_LIB"/></param>
- /// <typeparam name="TConfig">Type of instance that <paramref name="sectionName"/> represents</typeparam>
- /// <returns>Hydrated instance of <typeparamref name="TConfig"/> which contains the user-defined values (if any).</returns>
- public static TConfig ExtractConfig<TConfig>(this IConfiguration config, IServiceProvider serviceProvider, string sectionName, string? rootSectionName = DEFAULT_ROOT_LIB)
- where TConfig : new()
- {
- // Default values should hopefully be provided from the constructor
- var configInstance = ActivatorUtilities.CreateInstance(serviceProvider, typeof(TConfig));
+ /// <summary>
+ /// Instantiate a new instance of <typeparamref name="TConfig"/>, then walk through the specified <paramref name="sectionName"/>
+ /// in <paramref name="config"/>. Translate user-defined config values to the <typeparamref name="TConfig"/> instance.
+ /// </summary>
+ /// <param name="config">Loaded App Configuration</param>
+ /// <param name="serviceProvider"></param>
+ /// <param name="sectionName">Name of section to load</param>
+ /// <param name="rootSectionName">(Optional) Used when section is nested with another. Default value is <see cref="DEFAULT_ROOT_LIB"/></param>
+ /// <typeparam name="TConfig">Type of instance that <paramref name="sectionName"/> represents</typeparam>
+ /// <returns>Hydrated instance of <typeparamref name="TConfig"/> which contains the user-defined values (if any).</returns>
+ public static TConfig ExtractConfig<TConfig>(this IConfiguration config, IServiceProvider serviceProvider, string sectionName, string? rootSectionName = DEFAULT_ROOT_LIB)
+ where TConfig : new()
+ {
+ // Default values should hopefully be provided from the constructor
+ var configInstance = ActivatorUtilities.CreateInstance(serviceProvider, typeof(TConfig));
- HydrateInstance(ref configInstance, new ConfigSection(ref config, sectionName, rootSectionName));
+ HydrateInstance(ref configInstance, new ConfigSection(ref config, sectionName, rootSectionName));
- return (TConfig)configInstance;
- }
+ return (TConfig)configInstance;
+ }
- /// <summary>
- /// Instantiate a new instance of <typeparamref name="TConfig"/>, then walk through the specified <paramref name="sectionName"/>
- /// in <paramref name="config"/>. Translate user-defined config values to the <typeparamref name="TConfig"/> instance.
- /// </summary>
- /// <param name="config">Loaded App Configuration</param>
- /// <param name="sectionName">Name of section to load</param>
- /// <param name="rootSectionName">(Optional) Used when section is nested with another. Default value is <see cref="DEFAULT_ROOT_LIB"/></param>
- /// <typeparam name="TConfig">Type of instance that <paramref name="sectionName"/> represents</typeparam>
- /// <returns>Hydrated instance of <typeparamref name="TConfig"/> which contains the user-defined values (if any).</returns>
- public static TConfig ExtractConfig<TConfig>(this IConfiguration config, string sectionName, string? rootSectionName = DEFAULT_ROOT_LIB)
- where TConfig : new()
- {
- // Default values should hopefully be provided from the constructor
- object configInstance = new TConfig();
+ /// <summary>
+ /// Instantiate a new instance of <typeparamref name="TConfig"/>, then walk through the specified <paramref name="sectionName"/>
+ /// in <paramref name="config"/>. Translate user-defined config values to the <typeparamref name="TConfig"/> instance.
+ /// </summary>
+ /// <param name="config">Loaded App Configuration</param>
+ /// <param name="sectionName">Name of section to load</param>
+ /// <param name="rootSectionName">(Optional) Used when section is nested with another. Default value is <see cref="DEFAULT_ROOT_LIB"/></param>
+ /// <typeparam name="TConfig">Type of instance that <paramref name="sectionName"/> represents</typeparam>
+ /// <returns>Hydrated instance of <typeparamref name="TConfig"/> which contains the user-defined values (if any).</returns>
+ public static TConfig ExtractConfig<TConfig>(this IConfiguration config, string sectionName, string? rootSectionName = DEFAULT_ROOT_LIB)
+ where TConfig : new()
+ {
+ // Default values should hopefully be provided from the constructor
+ object configInstance = new TConfig();
- HydrateInstance(ref configInstance, new ConfigSection(ref config, sectionName, rootSectionName));
+ HydrateInstance(ref configInstance, new ConfigSection(ref config, sectionName, rootSectionName));
- return (TConfig)configInstance;
- }
+ return (TConfig)configInstance;
+ }
- /// <summary>
- /// Determines if <paramref name="config"/> contains a particular section/object (not value)
- /// </summary>
- /// <remarks>
- /// <code>
- /// {
- /// "Discord": { // this is a section/object
- ///
- /// },
- /// "Value": "something" // this is not a section/object
- /// }
- /// </code>
- /// </remarks>
- /// <param name="config"></param>
- /// <param name="values"></param>
- /// <returns>True if section exists, otherwise false</returns>
- public static bool HasSection(this IConfiguration config, params string[] values)
- {
- if (!values.Any())
- return false;
+ /// <summary>
+ /// Determines if <paramref name="config"/> contains a particular section/object (not value)
+ /// </summary>
+ /// <remarks>
+ /// <code>
+ /// {
+ /// "Discord": { // this is a section/object
+ ///
+ /// },
+ /// "Value": "something" // this is not a section/object
+ /// }
+ /// </code>
+ /// </remarks>
+ /// <param name="config"></param>
+ /// <param name="values"></param>
+ /// <returns>True if section exists, otherwise false</returns>
+ public static bool HasSection(this IConfiguration config, params string[] values)
+ {
+ if (!values.Any())
+ return false;
- if (values.Length == 1)
- return config.GetChildren().Any(x => x.Key == values[0]);
+ if (values.Length == 1)
+ return config.GetChildren().Any(x => x.Key == values[0]);
- if (config.GetChildren().All(x => x.Key != values[0]))
- return false;
+ if (config.GetChildren().All(x => x.Key != values[0]))
+ return false;
- var current = config.GetSection(values[0]);
+ var current = config.GetSection(values[0]);
- for (var i = 1; i < values.Length - 1; i++)
- {
- if (current.GetChildren().All(x => x.Key != values[i]))
- return false;
+ for (var i = 1; i < values.Length - 1; i++)
+ {
+ if (current.GetChildren().All(x => x.Key != values[i]))
+ return false;
- current = current.GetSection(values[i]);
- }
+ current = current.GetSection(values[i]);
+ }
- return current.GetChildren().Any(x => x.Key == values[^1]);
- }
+ return current.GetChildren().Any(x => x.Key == values[^1]);
+ }
- /// <summary>
- /// Instantiates an instance of <see cref="DiscordClient"/>, then consumes any custom
- /// configuration from user/developer from <paramref name="config"/>. <br/>
- /// View remarks for more info
- /// </summary>
- /// <remarks>
- /// This is an example of how your JSON structure should look if you wish
- /// to override one or more of the default values from <see cref="DiscordConfiguration"/>
- /// <code>
- /// {
- /// "DisCatSharp": {
- /// "Discord": { }
- /// }
- /// }
- /// </code>
- /// <br/>
- /// Alternatively, you can use the type name itself
- /// <code>
- /// {
- /// "DisCatSharp": {
- /// "DiscordConfiguration": { }
- /// }
- /// }
- /// </code>
- /// <code>
- /// {
- /// "botSectionName": {
- /// "DiscordConfiguration": { }
- /// }
- /// }
- /// </code>
- /// </remarks>
- /// <param name="config"></param>
- /// <param name="serviceProvider"></param>
- /// <param name="botSectionName"></param>
- /// <returns>Instance of <see cref="DiscordClient"/></returns>
- public static DiscordClient BuildClient(this IConfiguration config, IServiceProvider serviceProvider,
- string botSectionName = DEFAULT_ROOT_LIB)
- {
- var section = config.HasSection(botSectionName, "Discord")
- ? "Discord"
- : config.HasSection(botSectionName, $"Discord{CONFIG_SUFFIX}")
- ? $"Discord:{CONFIG_SUFFIX}"
- : null;
-
- return string.IsNullOrEmpty(section)
- ? new DiscordClient(new DiscordConfiguration(serviceProvider))
- : new DiscordClient(config.ExtractConfig<DiscordConfiguration>(serviceProvider, section, botSectionName));
+ /// <summary>
+ /// Instantiates an instance of <see cref="DiscordClient"/>, then consumes any custom
+ /// configuration from user/developer from <paramref name="config"/>. <br/>
+ /// View remarks for more info
+ /// </summary>
+ /// <remarks>
+ /// This is an example of how your JSON structure should look if you wish
+ /// to override one or more of the default values from <see cref="DiscordConfiguration"/>
+ /// <code>
+ /// {
+ /// "DisCatSharp": {
+ /// "Discord": { }
+ /// }
+ /// }
+ /// </code>
+ /// <br/>
+ /// Alternatively, you can use the type name itself
+ /// <code>
+ /// {
+ /// "DisCatSharp": {
+ /// "DiscordConfiguration": { }
+ /// }
+ /// }
+ /// </code>
+ /// <code>
+ /// {
+ /// "botSectionName": {
+ /// "DiscordConfiguration": { }
+ /// }
+ /// }
+ /// </code>
+ /// </remarks>
+ /// <param name="config"></param>
+ /// <param name="serviceProvider"></param>
+ /// <param name="botSectionName"></param>
+ /// <returns>Instance of <see cref="DiscordClient"/></returns>
+ public static DiscordClient BuildClient(this IConfiguration config, IServiceProvider serviceProvider,
+ string botSectionName = DEFAULT_ROOT_LIB)
+ {
+ var section = config.HasSection(botSectionName, "Discord")
+ ? "Discord"
+ : config.HasSection(botSectionName, $"Discord{CONFIG_SUFFIX}")
+ ? $"Discord:{CONFIG_SUFFIX}"
+ : null;
+
+ return string.IsNullOrEmpty(section)
+ ? new DiscordClient(new DiscordConfiguration(serviceProvider))
+ : new DiscordClient(config.ExtractConfig<DiscordConfiguration>(serviceProvider, section, botSectionName));
+ }
}
}
diff --git a/DisCatSharp.Configuration/Models/ConfigSection.cs b/DisCatSharp.Configuration/Models/ConfigSection.cs
index 68c11f4e6..b37bf3b50 100644
--- a/DisCatSharp.Configuration/Models/ConfigSection.cs
+++ b/DisCatSharp.Configuration/Models/ConfigSection.cs
@@ -1,91 +1,92 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Configuration;
-namespace DisCatSharp.Configuration.Models;
-
-/// <summary>
-/// Represents an object in <see cref="IConfiguration"/>
-/// </summary>
-internal readonly struct ConfigSection
+namespace DisCatSharp.Configuration.Models
{
/// <summary>
- /// Key within <see cref="Config"/> which represents an object containing multiple values
+ /// Represents an object in <see cref="IConfiguration"/>
/// </summary>
- public string SectionName { get; }
+ internal readonly struct ConfigSection
+ {
+ /// <summary>
+ /// Key within <see cref="Config"/> which represents an object containing multiple values
+ /// </summary>
+ public string SectionName { get; }
- /// <summary>
- /// Optional used to indicate this section is nested within another
- /// </summary>
- public string? Root { get; }
+ /// <summary>
+ /// Optional used to indicate this section is nested within another
+ /// </summary>
+ public string? Root { get; }
- /// <summary>
- /// Reference to <see cref="IConfiguration"/> used within application
- /// </summary>
- public IConfiguration Config { get; }
+ /// <summary>
+ /// Reference to <see cref="IConfiguration"/> used within application
+ /// </summary>
+ public IConfiguration Config { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ConfigSection"/> class.
- /// </summary>
- /// <param name="config">Reference to config</param>
- /// <param name="sectionName">Section of interest</param>
- /// <param name="rootName">(Optional) Indicates <paramref name="sectionName"/> is nested within this name. Default value is DisCatSharp</param>
- public ConfigSection(ref IConfiguration config, string sectionName, string? rootName = "DisCatSharp")
- {
- this.Config = config;
- this.SectionName = sectionName;
- this.Root = rootName;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConfigSection"/> class.
+ /// </summary>
+ /// <param name="config">Reference to config</param>
+ /// <param name="sectionName">Section of interest</param>
+ /// <param name="rootName">(Optional) Indicates <paramref name="sectionName"/> is nested within this name. Default value is DisCatSharp</param>
+ public ConfigSection(ref IConfiguration config, string sectionName, string? rootName = "DisCatSharp")
+ {
+ this.Config = config;
+ this.SectionName = sectionName;
+ this.Root = rootName;
+ }
- /// <summary>
- /// Checks if key exists in <see cref="Config"/>
- /// </summary>
- /// <param name="name">Property / Key to search for in section</param>
- /// <returns>True if key exists, otherwise false. Outputs path to config regardless</returns>
- public bool ContainsKey(string name)
- {
- var path = string.IsNullOrEmpty(this.Root)
- ? this.Config.ConfigPath(this.SectionName, name)
- : this.Config.ConfigPath(this.Root, this.SectionName, name);
+ /// <summary>
+ /// Checks if key exists in <see cref="Config"/>
+ /// </summary>
+ /// <param name="name">Property / Key to search for in section</param>
+ /// <returns>True if key exists, otherwise false. Outputs path to config regardless</returns>
+ public bool ContainsKey(string name)
+ {
+ var path = string.IsNullOrEmpty(this.Root)
+ ? this.Config.ConfigPath(this.SectionName, name)
+ : this.Config.ConfigPath(this.Root, this.SectionName, name);
- return !string.IsNullOrEmpty(this.Config[path]);
- }
+ return !string.IsNullOrEmpty(this.Config[path]);
+ }
- /// <summary>
- /// Attempts to get value associated to the config path. <br/> Should be used in unison with <see cref="ContainsKey"/>
- /// </summary>
- /// <param name="propName">Config path to value</param>
- /// <returns>Value found at <paramref name="propName"/></returns>
- public string GetValue(string propName)
- => this.Config[this.GetPath(propName)];
+ /// <summary>
+ /// Attempts to get value associated to the config path. <br/> Should be used in unison with <see cref="ContainsKey"/>
+ /// </summary>
+ /// <param name="propName">Config path to value</param>
+ /// <returns>Value found at <paramref name="propName"/></returns>
+ public string GetValue(string propName)
+ => this.Config[this.GetPath(propName)];
- /// <summary>
- /// Gets the path.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <returns>A string.</returns>
- public string GetPath(string value) =>
- string.IsNullOrEmpty(this.Root)
- ? this.Config.ConfigPath(this.SectionName, value)
- : this.Config.ConfigPath(this.Root, this.SectionName, value);
+ /// <summary>
+ /// Gets the path.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <returns>A string.</returns>
+ public string GetPath(string value) =>
+ string.IsNullOrEmpty(this.Root)
+ ? this.Config.ConfigPath(this.SectionName, value)
+ : this.Config.ConfigPath(this.Root, this.SectionName, value);
+ }
}
diff --git a/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs b/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs
index 060e9dc8b..cc50f2205 100644
--- a/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs
+++ b/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs
@@ -1,109 +1,110 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.Hosting.DependencyInjection;
-
-/// <summary>
-/// The service collection extensions.
-/// </summary>
-public static class ServiceCollectionExtensions
+namespace DisCatSharp.Hosting.DependencyInjection
{
/// <summary>
- /// Adds your bot as a BackgroundService, registered in Dependency Injection as <typeparamref name="TService"/>
+ /// The service collection extensions.
/// </summary>
- /// <remarks>
- /// <see cref="IDiscordHostedService"/> is scoped to ServiceLifetime.Singleton. <br/>
- /// Maps to Implementation of <typeparamref name="TService"/>
- /// </remarks>
- /// <param name="services"></param>
- /// <typeparam name="TService"></typeparam>
- /// <returns>Reference to <paramref name="services"/> for chaining purposes</returns>
- public static IServiceCollection AddDiscordHostedService<TService>(this IServiceCollection services)
- where TService : class, IDiscordHostedService
+ public static class ServiceCollectionExtensions
{
- services.AddSingleton<TService>();
- services.AddHostedService(provider => provider.GetRequiredService<TService>());
- return services;
- }
+ /// <summary>
+ /// Adds your bot as a BackgroundService, registered in Dependency Injection as <typeparamref name="TService"/>
+ /// </summary>
+ /// <remarks>
+ /// <see cref="IDiscordHostedService"/> is scoped to ServiceLifetime.Singleton. <br/>
+ /// Maps to Implementation of <typeparamref name="TService"/>
+ /// </remarks>
+ /// <param name="services"></param>
+ /// <typeparam name="TService"></typeparam>
+ /// <returns>Reference to <paramref name="services"/> for chaining purposes</returns>
+ public static IServiceCollection AddDiscordHostedService<TService>(this IServiceCollection services)
+ where TService : class, IDiscordHostedService
+ {
+ services.AddSingleton<TService>();
+ services.AddHostedService(provider => provider.GetRequiredService<TService>());
+ return services;
+ }
- /// <summary>
- /// Adds your bot as a BackgroundService, registered in Dependency Injection as <typeparamref name="TService"/>
- /// </summary>
- /// <remarks>
- /// <see cref="IDiscordHostedShardService"/> is scoped to ServiceLifetime.Singleton. <br/>
- /// Maps to Implementation of <typeparamref name="TService"/>
- /// </remarks>
- /// <param name="services"></param>
- /// <typeparam name="TService"></typeparam>
- /// <returns>Reference to <paramref name="services"/> for chaining purposes</returns>
- public static IServiceCollection AddDiscordHostedShardService<TService>(this IServiceCollection services)
- where TService : class, IDiscordHostedShardService
- {
- services.AddSingleton<TService>();
- services.AddHostedService(provider => provider.GetRequiredService<TService>());
- return services;
- }
+ /// <summary>
+ /// Adds your bot as a BackgroundService, registered in Dependency Injection as <typeparamref name="TService"/>
+ /// </summary>
+ /// <remarks>
+ /// <see cref="IDiscordHostedShardService"/> is scoped to ServiceLifetime.Singleton. <br/>
+ /// Maps to Implementation of <typeparamref name="TService"/>
+ /// </remarks>
+ /// <param name="services"></param>
+ /// <typeparam name="TService"></typeparam>
+ /// <returns>Reference to <paramref name="services"/> for chaining purposes</returns>
+ public static IServiceCollection AddDiscordHostedShardService<TService>(this IServiceCollection services)
+ where TService : class, IDiscordHostedShardService
+ {
+ services.AddSingleton<TService>();
+ services.AddHostedService(provider => provider.GetRequiredService<TService>());
+ return services;
+ }
- /// <summary>
- /// Add <typeparamref name="TService"/> as a background service which derives from
- /// <typeparamref name="TInterface"/> and <see cref="IDiscordHostedService"/>
- /// </summary>
- /// <remarks>
- /// To retrieve your bot via Dependency Injection you can reference it via <typeparamref name="TInterface"/>
- /// </remarks>
- /// <param name="services"></param>
- /// <typeparam name="TInterface">Interface which <typeparamref name="TService"/> inherits from</typeparam>
- /// <typeparam name="TService">Your custom bot</typeparam>
- /// <returns>Reference to <paramref name="services"/> for chaining purposes</returns>
- public static IServiceCollection AddDiscordHostedService<TInterface, TService>(this IServiceCollection services)
- where TInterface : class, IDiscordHostedService
- where TService : class, TInterface, IDiscordHostedService
- {
- services.AddSingleton<TInterface, TService>();
- services.AddHostedService(provider => provider.GetRequiredService<TInterface>());
+ /// <summary>
+ /// Add <typeparamref name="TService"/> as a background service which derives from
+ /// <typeparamref name="TInterface"/> and <see cref="IDiscordHostedService"/>
+ /// </summary>
+ /// <remarks>
+ /// To retrieve your bot via Dependency Injection you can reference it via <typeparamref name="TInterface"/>
+ /// </remarks>
+ /// <param name="services"></param>
+ /// <typeparam name="TInterface">Interface which <typeparamref name="TService"/> inherits from</typeparam>
+ /// <typeparam name="TService">Your custom bot</typeparam>
+ /// <returns>Reference to <paramref name="services"/> for chaining purposes</returns>
+ public static IServiceCollection AddDiscordHostedService<TInterface, TService>(this IServiceCollection services)
+ where TInterface : class, IDiscordHostedService
+ where TService : class, TInterface, IDiscordHostedService
+ {
+ services.AddSingleton<TInterface, TService>();
+ services.AddHostedService(provider => provider.GetRequiredService<TInterface>());
- return services;
- }
+ return services;
+ }
- /// <summary>
- /// Add <typeparamref name="TService"/> as a background service which derives from
- /// <typeparamref name="TInterface"/> and <see cref="IDiscordHostedShardService"/>
- /// </summary>
- /// <remarks>
- /// To retrieve your bot via Dependency Injection you can reference it via <typeparamref name="TInterface"/>
- /// </remarks>
- /// <param name="services"></param>
- /// <typeparam name="TInterface">Interface which <typeparamref name="TService"/> inherits from</typeparam>
- /// <typeparam name="TService">Your custom bot</typeparam>
- /// <returns>Reference to <paramref name="services"/> for chaining purposes</returns>
- public static IServiceCollection AddDiscordHostedShardService<TInterface, TService>(
- this IServiceCollection services)
- where TInterface : class, IDiscordHostedShardService
- where TService : class, TInterface, IDiscordHostedShardService
- {
- services.AddSingleton<TInterface, TService>();
- services.AddHostedService(provider => provider.GetRequiredService<TInterface>());
- return services;
+ /// <summary>
+ /// Add <typeparamref name="TService"/> as a background service which derives from
+ /// <typeparamref name="TInterface"/> and <see cref="IDiscordHostedShardService"/>
+ /// </summary>
+ /// <remarks>
+ /// To retrieve your bot via Dependency Injection you can reference it via <typeparamref name="TInterface"/>
+ /// </remarks>
+ /// <param name="services"></param>
+ /// <typeparam name="TInterface">Interface which <typeparamref name="TService"/> inherits from</typeparam>
+ /// <typeparam name="TService">Your custom bot</typeparam>
+ /// <returns>Reference to <paramref name="services"/> for chaining purposes</returns>
+ public static IServiceCollection AddDiscordHostedShardService<TInterface, TService>(
+ this IServiceCollection services)
+ where TInterface : class, IDiscordHostedShardService
+ where TService : class, TInterface, IDiscordHostedShardService
+ {
+ services.AddSingleton<TInterface, TService>();
+ services.AddHostedService(provider => provider.GetRequiredService<TInterface>());
+ return services;
+ }
}
}
diff --git a/DisCatSharp.Hosting.Tests/ExtensionTests.cs b/DisCatSharp.Hosting.Tests/ExtensionTests.cs
index 6ab7c7e8e..aaabf6f06 100644
--- a/DisCatSharp.Hosting.Tests/ExtensionTests.cs
+++ b/DisCatSharp.Hosting.Tests/ExtensionTests.cs
@@ -1,121 +1,122 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using DisCatSharp.Interactivity;
using DisCatSharp.Lavalink;
using Microsoft.Extensions.Configuration;
using Xunit;
-namespace DisCatSharp.Hosting.Tests;
-
-public class HostExtensionTests
+namespace DisCatSharp.Hosting.Tests
{
- #region Reference to external assemblies - required to ensure they're loaded
+ public class HostExtensionTests
+ {
+ #region Reference to external assemblies - required to ensure they're loaded
#pragma warning disable 414
- private InteractivityConfiguration? _interactivityConfig = null;
- private LavalinkConfiguration? _lavalinkConfig = null;
- private DiscordConfiguration? _discordConfig = null;
+ private InteractivityConfiguration? _interactivityConfig = null;
+ private LavalinkConfiguration? _lavalinkConfig = null;
+ private DiscordConfiguration? _discordConfig = null;
#pragma warning restore 414
- #endregion
-
- private Dictionary<string, string> DefaultDiscord() =>
- new()
- {
- { "DisCatSharp:Discord:Token", "1234567890" },
- { "DisCatSharp:Discord:TokenType", "Bot" },
- { "DisCatSharp:Discord:MinimumLogLevel", "Information" },
- { "DisCatSharp:Discord:UseRelativeRateLimit", "true" },
- { "DisCatSharp:Discord:LogTimestampFormat", "yyyy-MM-dd HH:mm:ss zzz" },
- { "DisCatSharp:Discord:LargeThreshold", "250" },
- { "DisCatSharp:Discord:AutoReconnect", "true" },
- { "DisCatSharp:Discord:ShardId", "123123" },
- { "DisCatSharp:Discord:GatewayCompressionLevel", "Stream" },
- { "DisCatSharp:Discord:MessageCacheSize", "1024" },
- { "DisCatSharp:Discord:HttpTimeout", "00:00:20" },
- { "DisCatSharp:Discord:ReconnectIndefinitely", "false" },
- { "DisCatSharp:Discord:AlwaysCacheMembers", "true" },
- { "DisCatSharp:Discord:DiscordIntents", "AllUnprivileged" },
- { "DisCatSharp:Discord:MobileStatus", "false" },
- { "DisCatSharp:Discord:UseCanary", "false" },
- { "DisCatSharp:Discord:AutoRefreshChannelCache", "false" },
- { "DisCatSharp:Discord:Intents", "AllUnprivileged" }
- };
-
- public IConfiguration DiscordInteractivityConfiguration() => new ConfigurationBuilder()
- .AddInMemoryCollection(new Dictionary<string, string>(this.DefaultDiscord())
- {
- {"DisCatSharp:Using", "[\"DisCatSharp.Interactivity\"]"} // this should be enough to automatically add the extension
+ #endregion
+
+ private Dictionary<string, string> DefaultDiscord() =>
+ new()
+ {
+ { "DisCatSharp:Discord:Token", "1234567890" },
+ { "DisCatSharp:Discord:TokenType", "Bot" },
+ { "DisCatSharp:Discord:MinimumLogLevel", "Information" },
+ { "DisCatSharp:Discord:UseRelativeRateLimit", "true" },
+ { "DisCatSharp:Discord:LogTimestampFormat", "yyyy-MM-dd HH:mm:ss zzz" },
+ { "DisCatSharp:Discord:LargeThreshold", "250" },
+ { "DisCatSharp:Discord:AutoReconnect", "true" },
+ { "DisCatSharp:Discord:ShardId", "123123" },
+ { "DisCatSharp:Discord:GatewayCompressionLevel", "Stream" },
+ { "DisCatSharp:Discord:MessageCacheSize", "1024" },
+ { "DisCatSharp:Discord:HttpTimeout", "00:00:20" },
+ { "DisCatSharp:Discord:ReconnectIndefinitely", "false" },
+ { "DisCatSharp:Discord:AlwaysCacheMembers", "true" },
+ { "DisCatSharp:Discord:DiscordIntents", "AllUnprivileged" },
+ { "DisCatSharp:Discord:MobileStatus", "false" },
+ { "DisCatSharp:Discord:UseCanary", "false" },
+ { "DisCatSharp:Discord:AutoRefreshChannelCache", "false" },
+ { "DisCatSharp:Discord:Intents", "AllUnprivileged" }
+ };
+
+ public IConfiguration DiscordInteractivityConfiguration() => new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary<string, string>(this.DefaultDiscord())
+ {
+ {"DisCatSharp:Using", "[\"DisCatSharp.Interactivity\"]"} // this should be enough to automatically add the extension
})
- .Build();
+ .Build();
- public IConfiguration DiscordOnlyConfiguration() => new ConfigurationBuilder()
- .AddInMemoryCollection(this.DefaultDiscord())
- .Build();
+ public IConfiguration DiscordOnlyConfiguration() => new ConfigurationBuilder()
+ .AddInMemoryCollection(this.DefaultDiscord())
+ .Build();
- public IConfiguration DiscordInteractivityAndLavaLinkConfiguration() => new ConfigurationBuilder()
- .AddJsonFile("interactivity-lavalink.json")
- .Build();
+ public IConfiguration DiscordInteractivityAndLavaLinkConfiguration() => new ConfigurationBuilder()
+ .AddJsonFile("interactivity-lavalink.json")
+ .Build();
- [Fact]
- public void DiscoverExtensions_Interactivity()
- {
- var source = this.DiscordInteractivityConfiguration();
- var discovered = source.FindImplementedExtensions();
+ [Fact]
+ public void DiscoverExtensions_Interactivity()
+ {
+ var source = this.DiscordInteractivityConfiguration();
+ var discovered = source.FindImplementedExtensions();
- // Remember that DiscordConfiguration does not have an implementation type which is assignable to BaseExtension
- Assert.Single(discovered);
- var item = discovered.First();
+ // Remember that DiscordConfiguration does not have an implementation type which is assignable to BaseExtension
+ Assert.Single(discovered);
+ var item = discovered.First();
- Assert.Equal(typeof(InteractivityConfiguration), item.Value.ConfigType);
- Assert.Equal(typeof(InteractivityExtension), item.Value.ImplementationType);
- Assert.Equal("InteractivityExtension", item.Key);
- }
+ Assert.Equal(typeof(InteractivityConfiguration), item.Value.ConfigType);
+ Assert.Equal(typeof(InteractivityExtension), item.Value.ImplementationType);
+ Assert.Equal("InteractivityExtension", item.Key);
+ }
- [Fact]
- public void DiscoverExtensions_InteractivityAndLavaLink()
- {
- var source = this.DiscordInteractivityAndLavaLinkConfiguration();
- var discovered = source.FindImplementedExtensions();
+ [Fact]
+ public void DiscoverExtensions_InteractivityAndLavaLink()
+ {
+ var source = this.DiscordInteractivityAndLavaLinkConfiguration();
+ var discovered = source.FindImplementedExtensions();
- Assert.Equal(2, discovered.Count);
- var first = discovered.First();
- var last = discovered.Last();
+ Assert.Equal(2, discovered.Count);
+ var first = discovered.First();
+ var last = discovered.Last();
- Assert.Equal(typeof(InteractivityConfiguration), first.Value.ConfigType);
- Assert.Equal(typeof(InteractivityExtension), first.Value.ImplementationType);
- Assert.True("InteractivityExtension".Equals(first.Key, StringComparison.OrdinalIgnoreCase));
+ Assert.Equal(typeof(InteractivityConfiguration), first.Value.ConfigType);
+ Assert.Equal(typeof(InteractivityExtension), first.Value.ImplementationType);
+ Assert.True("InteractivityExtension".Equals(first.Key, StringComparison.OrdinalIgnoreCase));
- Assert.Equal(typeof(LavalinkConfiguration), last.Value.ConfigType);
- Assert.Equal(typeof(LavalinkExtension), last.Value.ImplementationType);
- Assert.True("LavalinkExtension".Equals(last.Key, StringComparison.OrdinalIgnoreCase));
+ Assert.Equal(typeof(LavalinkConfiguration), last.Value.ConfigType);
+ Assert.Equal(typeof(LavalinkExtension), last.Value.ImplementationType);
+ Assert.True("LavalinkExtension".Equals(last.Key, StringComparison.OrdinalIgnoreCase));
+ }
}
}
diff --git a/DisCatSharp.Hosting.Tests/HostTests.cs b/DisCatSharp.Hosting.Tests/HostTests.cs
index 68f7ff134..0e9b818f3 100644
--- a/DisCatSharp.Hosting.Tests/HostTests.cs
+++ b/DisCatSharp.Hosting.Tests/HostTests.cs
@@ -1,266 +1,267 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Interactivity;
using DisCatSharp.Lavalink;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Xunit;
-namespace DisCatSharp.Hosting.Tests;
-
-public sealed class Bot : DiscordHostedService
+namespace DisCatSharp.Hosting.Tests
{
- public Bot(IConfiguration config, ILogger<Bot> logger, IServiceProvider provider, IHostApplicationLifetime lifetime) : base(config, logger, provider, lifetime)
+ public sealed class Bot : DiscordHostedService
{
- this.ConfigureAsync().GetAwaiter().GetResult();
- this.ConfigureExtensionsAsync().GetAwaiter().GetResult();
+ public Bot(IConfiguration config, ILogger<Bot> logger, IServiceProvider provider, IHostApplicationLifetime lifetime) : base(config, logger, provider, lifetime)
+ {
+ this.ConfigureAsync().GetAwaiter().GetResult();
+ this.ConfigureExtensionsAsync().GetAwaiter().GetResult();
+ }
}
-}
-public sealed class MyCustomBot : DiscordHostedService
-{
- public MyCustomBot(IConfiguration config, ILogger<MyCustomBot> logger, IServiceProvider provider, IHostApplicationLifetime lifetime) : base(config, logger, provider, lifetime, "MyCustomBot")
+ public sealed class MyCustomBot : DiscordHostedService
{
- this.ConfigureAsync().GetAwaiter().GetResult();
- this.ConfigureExtensionsAsync().GetAwaiter().GetResult();
+ public MyCustomBot(IConfiguration config, ILogger<MyCustomBot> logger, IServiceProvider provider, IHostApplicationLifetime lifetime) : base(config, logger, provider, lifetime, "MyCustomBot")
+ {
+ this.ConfigureAsync().GetAwaiter().GetResult();
+ this.ConfigureExtensionsAsync().GetAwaiter().GetResult();
+ }
}
-}
-public interface IBotTwoService : IDiscordHostedService
-{
- string GiveMeAResponse();
-}
-
-
-public sealed class BotTwoService : DiscordHostedService, IBotTwoService
-{
- public BotTwoService(IConfiguration config, ILogger<BotTwoService> logger, IServiceProvider provider, IHostApplicationLifetime lifetime) : base(config, logger, provider, lifetime, "BotTwo")
+ public interface IBotTwoService : IDiscordHostedService
{
- this.ConfigureAsync().GetAwaiter().GetResult();
- this.ConfigureExtensionsAsync().GetAwaiter().GetResult();
+ string GiveMeAResponse();
}
- public string GiveMeAResponse() => "I'm working";
-}
-
-public class HostTests
-{
- private Dictionary<string, string> DefaultDiscord() =>
- new()
- {
- { "DisCatSharp:Discord:Token", "1234567890" },
- { "DisCatSharp:Discord:TokenType", "Bot" },
- { "DisCatSharp:Discord:MinimumLogLevel", "Information" },
- { "DisCatSharp:Discord:UseRelativeRateLimit", "true" },
- { "DisCatSharp:Discord:LogTimestampFormat", "yyyy-MM-dd HH:mm:ss zzz" },
- { "DisCatSharp:Discord:LargeThreshold", "250" },
- { "DisCatSharp:Discord:AutoReconnect", "true" },
- { "DisCatSharp:Discord:ShardId", "123123" },
- { "DisCatSharp:Discord:GatewayCompressionLevel", "Stream" },
- { "DisCatSharp:Discord:MessageCacheSize", "1024" },
- { "DisCatSharp:Discord:HttpTimeout", "00:00:20" },
- { "DisCatSharp:Discord:ReconnectIndefinitely", "false" },
- { "DisCatSharp:Discord:AlwaysCacheMembers", "true" },
- { "DisCatSharp:Discord:DiscordIntents", "AllUnprivileged" },
- { "DisCatSharp:Discord:MobileStatus", "false" },
- { "DisCatSharp:Discord:UseCanary", "false" },
- { "DisCatSharp:Discord:AutoRefreshChannelCache", "false" },
- { "DisCatSharp:Discord:Intents", "AllUnprivileged" }
- };
-
- public Dictionary<string, string> DiscordInteractivity() => new(this.DefaultDiscord())
- {
- { "DisCatSharp:Using", "[\"DisCatSharp.Interactivity\"]" },
- };
- public Dictionary<string, string> DiscordInteractivityAndLavalink() => new(this.DefaultDiscord())
+ public sealed class BotTwoService : DiscordHostedService, IBotTwoService
{
- { "DisCatSharp:Using", "[\"DisCatSharp.Interactivity\", \"DisCatSharp.Lavalink\"]" },
- };
-
- IHostBuilder Create(Dictionary<string, string> configValues) =>
- Host.CreateDefaultBuilder()
- .ConfigureServices(services => services.AddSingleton<IDiscordHostedService, Bot>())
- .ConfigureHostConfiguration(builder => builder.AddInMemoryCollection(configValues));
-
- IHostBuilder Create(string filename) =>
- Host.CreateDefaultBuilder()
- .ConfigureServices(services => services.AddSingleton<IDiscordHostedService, MyCustomBot>())
- .ConfigureHostConfiguration(builder => builder.AddJsonFile(filename));
-
- IHostBuilder Create<TInterface, TBot>(string filename)
- where TInterface : class, IDiscordHostedService
- where TBot : class, TInterface, IDiscordHostedService =>
- Host.CreateDefaultBuilder()
- .ConfigureServices(services => services.AddSingleton<TInterface, TBot>())
- .ConfigureHostConfiguration(builder => builder.AddJsonFile(filename));
-
-
- [Fact]
- public void TestBotCustomInterface()
- {
- IHost? host = null;
-
- try
+ public BotTwoService(IConfiguration config, ILogger<BotTwoService> logger, IServiceProvider provider, IHostApplicationLifetime lifetime) : base(config, logger, provider, lifetime, "BotTwo")
{
- host = this.Create<IBotTwoService, BotTwoService>("BotTwo.json").Build();
- var service = host.Services.GetRequiredService<IBotTwoService>();
-
- Assert.NotNull(service);
-
- var response = service.GiveMeAResponse();
- Assert.Equal("I'm working", response);
- }
- finally
- {
- host?.Dispose();
+ this.ConfigureAsync().GetAwaiter().GetResult();
+ this.ConfigureExtensionsAsync().GetAwaiter().GetResult();
}
+
+ public string GiveMeAResponse() => "I'm working";
}
- [Fact]
- public void TestDifferentSection_InteractivityOnly()
+ public class HostTests
{
- IHost? host = null;
+ private Dictionary<string, string> DefaultDiscord() =>
+ new()
+ {
+ { "DisCatSharp:Discord:Token", "1234567890" },
+ { "DisCatSharp:Discord:TokenType", "Bot" },
+ { "DisCatSharp:Discord:MinimumLogLevel", "Information" },
+ { "DisCatSharp:Discord:UseRelativeRateLimit", "true" },
+ { "DisCatSharp:Discord:LogTimestampFormat", "yyyy-MM-dd HH:mm:ss zzz" },
+ { "DisCatSharp:Discord:LargeThreshold", "250" },
+ { "DisCatSharp:Discord:AutoReconnect", "true" },
+ { "DisCatSharp:Discord:ShardId", "123123" },
+ { "DisCatSharp:Discord:GatewayCompressionLevel", "Stream" },
+ { "DisCatSharp:Discord:MessageCacheSize", "1024" },
+ { "DisCatSharp:Discord:HttpTimeout", "00:00:20" },
+ { "DisCatSharp:Discord:ReconnectIndefinitely", "false" },
+ { "DisCatSharp:Discord:AlwaysCacheMembers", "true" },
+ { "DisCatSharp:Discord:DiscordIntents", "AllUnprivileged" },
+ { "DisCatSharp:Discord:MobileStatus", "false" },
+ { "DisCatSharp:Discord:UseCanary", "false" },
+ { "DisCatSharp:Discord:AutoRefreshChannelCache", "false" },
+ { "DisCatSharp:Discord:Intents", "AllUnprivileged" }
+ };
+
+ public Dictionary<string, string> DiscordInteractivity() => new(this.DefaultDiscord())
+ {
+ { "DisCatSharp:Using", "[\"DisCatSharp.Interactivity\"]" },
+ };
- try
+ public Dictionary<string, string> DiscordInteractivityAndLavalink() => new(this.DefaultDiscord())
{
- host = this.Create("interactivity-different-section.json").Build();
- var service = host.Services.GetRequiredService<IDiscordHostedService>();
+ { "DisCatSharp:Using", "[\"DisCatSharp.Interactivity\", \"DisCatSharp.Lavalink\"]" },
+ };
- Assert.NotNull(service);
- Assert.NotNull(service.Client);
- Assert.Null(service.Client.GetExtension<LavalinkExtension>());
+ IHostBuilder Create(Dictionary<string, string> configValues) =>
+ Host.CreateDefaultBuilder()
+ .ConfigureServices(services => services.AddSingleton<IDiscordHostedService, Bot>())
+ .ConfigureHostConfiguration(builder => builder.AddInMemoryCollection(configValues));
- var intents = DiscordIntents.GuildEmojisAndStickers | DiscordIntents.GuildMembers |
- DiscordIntents.Guilds;
- Assert.Equal(intents, service.Client.Intents);
+ IHostBuilder Create(string filename) =>
+ Host.CreateDefaultBuilder()
+ .ConfigureServices(services => services.AddSingleton<IDiscordHostedService, MyCustomBot>())
+ .ConfigureHostConfiguration(builder => builder.AddJsonFile(filename));
+ IHostBuilder Create<TInterface, TBot>(string filename)
+ where TInterface : class, IDiscordHostedService
+ where TBot : class, TInterface, IDiscordHostedService =>
+ Host.CreateDefaultBuilder()
+ .ConfigureServices(services => services.AddSingleton<TInterface, TBot>())
+ .ConfigureHostConfiguration(builder => builder.AddJsonFile(filename));
- var interactivity = service.Client.GetExtension<InteractivityExtension>();
- Assert.NotNull(interactivity);
- Assert.NotNull(host.Services);
- Assert.NotNull(service.Client.ServiceProvider);
- }
- finally
+ [Fact]
+ public void TestBotCustomInterface()
{
- host?.Dispose();
+ IHost? host = null;
+
+ try
+ {
+ host = this.Create<IBotTwoService, BotTwoService>("BotTwo.json").Build();
+ var service = host.Services.GetRequiredService<IBotTwoService>();
+
+ Assert.NotNull(service);
+
+ var response = service.GiveMeAResponse();
+ Assert.Equal("I'm working", response);
+ }
+ finally
+ {
+ host?.Dispose();
+ }
}
- }
- [Fact]
- public void TestDifferentSection_LavalinkOnly()
- {
- IHost? host = null;
-
- try
+ [Fact]
+ public void TestDifferentSection_InteractivityOnly()
{
- host = this.Create("lavalink-different-section.json").Build();
- var service = host.Services.GetRequiredService<IDiscordHostedService>();
+ IHost? host = null;
- Assert.NotNull(service);
- Assert.NotNull(service.Client);
- Assert.NotNull(service.Client.GetExtension<LavalinkExtension>());
- Assert.Null(service.Client.GetExtension<InteractivityExtension>());
+ try
+ {
+ host = this.Create("interactivity-different-section.json").Build();
+ var service = host.Services.GetRequiredService<IDiscordHostedService>();
- var intents = DiscordIntents.Guilds;
- Assert.Equal(intents, service.Client.Intents);
- Assert.NotNull(service.Client.ServiceProvider);
- }
- finally
- {
- host?.Dispose();
- }
- }
+ Assert.NotNull(service);
+ Assert.NotNull(service.Client);
+ Assert.Null(service.Client.GetExtension<LavalinkExtension>());
- [Fact]
- public void TestNoExtensions()
- {
- IHost? host = null;
+ var intents = DiscordIntents.GuildEmojisAndStickers | DiscordIntents.GuildMembers |
+ DiscordIntents.Guilds;
+ Assert.Equal(intents, service.Client.Intents);
- try
- {
- host = this.Create(this.DefaultDiscord()).Build();
- var service = host.Services.GetRequiredService<IDiscordHostedService>();
- Assert.NotNull(service);
- Assert.NotNull(service.Client);
- Assert.NotNull(service.Client.ServiceProvider);
- }
- finally
- {
- host?.Dispose();
- }
- }
+ var interactivity = service.Client.GetExtension<InteractivityExtension>();
+ Assert.NotNull(interactivity);
- [Fact]
- public void TestInteractivityExtension()
- {
- IHost? host = null;
+ Assert.NotNull(host.Services);
+ Assert.NotNull(service.Client.ServiceProvider);
+ }
+ finally
+ {
+ host?.Dispose();
+ }
+ }
- try
+ [Fact]
+ public void TestDifferentSection_LavalinkOnly()
{
- host = this.Create(this.DiscordInteractivity()).Build();
-
- var service = host.Services.GetRequiredService<IDiscordHostedService>();
- Assert.NotNull(service);
- Assert.NotNull(service.Client);
- Assert.NotNull(service.Client.GetExtension<InteractivityExtension>());
- Assert.NotNull(service.Client.ServiceProvider);
+ IHost? host = null;
+
+ try
+ {
+ host = this.Create("lavalink-different-section.json").Build();
+ var service = host.Services.GetRequiredService<IDiscordHostedService>();
+
+ Assert.NotNull(service);
+ Assert.NotNull(service.Client);
+ Assert.NotNull(service.Client.GetExtension<LavalinkExtension>());
+ Assert.Null(service.Client.GetExtension<InteractivityExtension>());
+
+ var intents = DiscordIntents.Guilds;
+ Assert.Equal(intents, service.Client.Intents);
+ Assert.NotNull(service.Client.ServiceProvider);
+ }
+ finally
+ {
+ host?.Dispose();
+ }
}
- finally
+
+ [Fact]
+ public void TestNoExtensions()
{
- host?.Dispose();
+ IHost? host = null;
+
+ try
+ {
+ host = this.Create(this.DefaultDiscord()).Build();
+
+ var service = host.Services.GetRequiredService<IDiscordHostedService>();
+ Assert.NotNull(service);
+ Assert.NotNull(service.Client);
+ Assert.NotNull(service.Client.ServiceProvider);
+ }
+ finally
+ {
+ host?.Dispose();
+ }
}
- }
-
- [Fact]
- public void TestInteractivityLavalinkExtensions()
- {
- IHost? host = null;
- try
+ [Fact]
+ public void TestInteractivityExtension()
{
- host = this.Create(this.DiscordInteractivityAndLavalink()).Build();
-
- var service = host.Services.GetRequiredService<IDiscordHostedService>();
-
- Assert.NotNull(service);
- Assert.NotNull(service.Client);
- Assert.NotNull(service.Client.GetExtension<InteractivityExtension>());
- Assert.NotNull(service.Client.GetExtension<LavalinkExtension>());
- Assert.NotNull(service.Client.ServiceProvider);
+ IHost? host = null;
+
+ try
+ {
+ host = this.Create(this.DiscordInteractivity()).Build();
+
+ var service = host.Services.GetRequiredService<IDiscordHostedService>();
+ Assert.NotNull(service);
+ Assert.NotNull(service.Client);
+ Assert.NotNull(service.Client.GetExtension<InteractivityExtension>());
+ Assert.NotNull(service.Client.ServiceProvider);
+ }
+ finally
+ {
+ host?.Dispose();
+ }
}
- finally
+
+ [Fact]
+ public void TestInteractivityLavalinkExtensions()
{
- host?.Dispose();
+ IHost? host = null;
+
+ try
+ {
+ host = this.Create(this.DiscordInteractivityAndLavalink()).Build();
+
+ var service = host.Services.GetRequiredService<IDiscordHostedService>();
+
+ Assert.NotNull(service);
+ Assert.NotNull(service.Client);
+ Assert.NotNull(service.Client.GetExtension<InteractivityExtension>());
+ Assert.NotNull(service.Client.GetExtension<LavalinkExtension>());
+ Assert.NotNull(service.Client.ServiceProvider);
+ }
+ finally
+ {
+ host?.Dispose();
+ }
}
}
}
diff --git a/DisCatSharp.Hosting/BaseHostedService.cs b/DisCatSharp.Hosting/BaseHostedService.cs
index f176e68f7..a5f0720ca 100644
--- a/DisCatSharp.Hosting/BaseHostedService.cs
+++ b/DisCatSharp.Hosting/BaseHostedService.cs
@@ -1,205 +1,206 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Contains the common logic between having a <see cref="DiscordClient"/> or
-/// <see cref="DiscordShardedClient"/> as a Hosted Service
-/// </summary>
-public abstract class BaseHostedService : BackgroundService
+namespace DisCatSharp.Hosting
{
- protected readonly ILogger<BaseHostedService> Logger;
- protected readonly IHostApplicationLifetime ApplicationLifetime;
- protected readonly IConfiguration Configuration;
- protected readonly IServiceProvider ServiceProvider;
- protected readonly string BotSection;
-
/// <summary>
- /// Initializes a new instance of the <see cref="BaseHostedService"/> class.
+ /// Contains the common logic between having a <see cref="DiscordClient"/> or
+ /// <see cref="DiscordShardedClient"/> as a Hosted Service
/// </summary>
- /// <param name="config">The config.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="serviceProvider">The service provider.</param>
- /// <param name="applicationLifetime">The application lifetime.</param>
- /// <param name="configBotSection">The config bot section.</param>
- internal BaseHostedService(IConfiguration config,
- ILogger<BaseHostedService> logger,
- IServiceProvider serviceProvider,
- IHostApplicationLifetime applicationLifetime,
- string configBotSection = DisCatSharp.Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB)
+ public abstract class BaseHostedService : BackgroundService
{
- this.Configuration = config;
- this.Logger = logger;
- this.ApplicationLifetime = applicationLifetime;
- this.ServiceProvider = serviceProvider;
- this.BotSection = configBotSection;
- }
-
- /// <summary>
- /// When the bot(s) fail to start, this method will be invoked. (Default behavior is to shutdown)
- /// </summary>
- /// <param name="ex">The exception/reason for not starting</param>
- protected virtual void OnInitializationError(Exception ex) => this.ApplicationLifetime.StopApplication();
-
- /// <summary>
- /// Connect your client(s) to Discord
- /// </summary>
- /// <returns>Task</returns>
- protected abstract Task ConnectAsync();
+ protected readonly ILogger<BaseHostedService> Logger;
+ protected readonly IHostApplicationLifetime ApplicationLifetime;
+ protected readonly IConfiguration Configuration;
+ protected readonly IServiceProvider ServiceProvider;
+ protected readonly string BotSection;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BaseHostedService"/> class.
+ /// </summary>
+ /// <param name="config">The config.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="serviceProvider">The service provider.</param>
+ /// <param name="applicationLifetime">The application lifetime.</param>
+ /// <param name="configBotSection">The config bot section.</param>
+ internal BaseHostedService(IConfiguration config,
+ ILogger<BaseHostedService> logger,
+ IServiceProvider serviceProvider,
+ IHostApplicationLifetime applicationLifetime,
+ string configBotSection = DisCatSharp.Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB)
+ {
+ this.Configuration = config;
+ this.Logger = logger;
+ this.ApplicationLifetime = applicationLifetime;
+ this.ServiceProvider = serviceProvider;
+ this.BotSection = configBotSection;
+ }
- /// <summary>
- /// Dynamically load extensions by using <see cref="Configuration"/> and
- /// <see cref="ServiceProvider"/>
- /// </summary>
- /// <param name="client">Client to add extension method(s) to</param>
- /// <returns>Task</returns>
- protected Task InitializeExtensions(DiscordClient client)
- {
- var typeMap = this.Configuration.FindImplementedExtensions(this.BotSection);
+ /// <summary>
+ /// When the bot(s) fail to start, this method will be invoked. (Default behavior is to shutdown)
+ /// </summary>
+ /// <param name="ex">The exception/reason for not starting</param>
+ protected virtual void OnInitializationError(Exception ex) => this.ApplicationLifetime.StopApplication();
+
+ /// <summary>
+ /// Connect your client(s) to Discord
+ /// </summary>
+ /// <returns>Task</returns>
+ protected abstract Task ConnectAsync();
+
+ /// <summary>
+ /// Dynamically load extensions by using <see cref="Configuration"/> and
+ /// <see cref="ServiceProvider"/>
+ /// </summary>
+ /// <param name="client">Client to add extension method(s) to</param>
+ /// <returns>Task</returns>
+ protected Task InitializeExtensions(DiscordClient client)
+ {
+ var typeMap = this.Configuration.FindImplementedExtensions(this.BotSection);
- this.Logger.LogDebug($"Found the following config types: {string.Join("\n\t", typeMap.Keys)}");
+ this.Logger.LogDebug($"Found the following config types: {string.Join("\n\t", typeMap.Keys)}");
- foreach (var typePair in typeMap)
- try
- {
- /*
+ 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);
+ 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 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);
+ 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)
+ if (instance == null)
+ {
+ this.Logger.LogError($"Unable to instantiate '{typePair.Value.ImplementationType.Name}'");
+ continue;
+ }
+
+ // Add an easy reference to our extensions for later use
+ client.AddExtension((BaseExtension)instance);
+ }
+ catch (Exception ex)
{
- this.Logger.LogError($"Unable to instantiate '{typePair.Value.ImplementationType.Name}'");
- continue;
+ this.Logger.LogError($"Unable to register '{typePair.Value.ImplementationType.Name}': \n\t{ex.Message}");
+ this.OnInitializationError(ex);
}
- // Add an easy reference to our extensions for later use
- client.AddExtension((BaseExtension)instance);
+ return Task.CompletedTask;
+ }
+
+ /// <summary>
+ /// Configure / Initialize the <see cref="DiscordClient"/> or
+ /// <see cref="DiscordShardedClient"/>
+ /// </summary>
+ /// <returns></returns>
+ protected abstract Task ConfigureAsync();
+
+ /// <summary>
+ /// Configure the extensions for your <see cref="DiscordShardedClient"/> or
+ /// <see cref="DiscordClient"/>
+ /// </summary>
+ /// <returns></returns>
+ protected abstract Task ConfigureExtensionsAsync();
+
+ /// <summary>
+ /// Runs just prior to <see cref="ConnectAsync"/>.
+ /// </summary>
+ /// <returns></returns>
+ protected virtual Task PreConnectAsync() => Task.CompletedTask;
+
+ /// <summary>
+ /// Runs immediately after <see cref="ConnectAsync"/>.
+ /// </summary>
+ /// <returns>Task</returns>
+ protected virtual Task PostConnectAsync() => Task.CompletedTask;
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ try
+ {
+ await this.ConfigureAsync();
+ await this.PreConnectAsync();
+ await this.ConnectAsync();
+ await this.ConfigureExtensionsAsync();
+ await this.PostConnectAsync();
}
catch (Exception ex)
{
- this.Logger.LogError($"Unable to register '{typePair.Value.ImplementationType.Name}': \n\t{ex.Message}");
- this.OnInitializationError(ex);
- }
-
- return Task.CompletedTask;
- }
-
- /// <summary>
- /// Configure / Initialize the <see cref="DiscordClient"/> or
- /// <see cref="DiscordShardedClient"/>
- /// </summary>
- /// <returns></returns>
- protected abstract Task ConfigureAsync();
-
- /// <summary>
- /// Configure the extensions for your <see cref="DiscordShardedClient"/> or
- /// <see cref="DiscordClient"/>
- /// </summary>
- /// <returns></returns>
- protected abstract Task ConfigureExtensionsAsync();
-
- /// <summary>
- /// Runs just prior to <see cref="ConnectAsync"/>.
- /// </summary>
- /// <returns></returns>
- protected virtual Task PreConnectAsync() => Task.CompletedTask;
-
- /// <summary>
- /// Runs immediately after <see cref="ConnectAsync"/>.
- /// </summary>
- /// <returns>Task</returns>
- protected virtual Task PostConnectAsync() => Task.CompletedTask;
-
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- try
- {
- await this.ConfigureAsync();
- await this.PreConnectAsync();
- await this.ConnectAsync();
- await this.ConfigureExtensionsAsync();
- await this.PostConnectAsync();
- }
- 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 happens and
* manually exit
*/
- this.Logger.LogError($"Was unable to start {this.GetType().Name} Bot as a Hosted Service");
+ this.Logger.LogError($"Was unable to start {this.GetType().Name} Bot as a Hosted Service");
- // Power given to developer for handling exception
- this.OnInitializationError(ex);
- }
+ // Power given to developer for handling exception
+ this.OnInitializationError(ex);
+ }
- // Wait indefinitely -- but use stopping token so we can properly cancel if needed
- await Task.Delay(-1, stoppingToken);
+ // Wait indefinitely -- but use stopping token so we can properly cancel if needed
+ await Task.Delay(-1, stoppingToken);
+ }
}
}
diff --git a/DisCatSharp.Hosting/ConfigurationExtensions.cs b/DisCatSharp.Hosting/ConfigurationExtensions.cs
index 5af974580..43159a267 100644
--- a/DisCatSharp.Hosting/ConfigurationExtensions.cs
+++ b/DisCatSharp.Hosting/ConfigurationExtensions.cs
@@ -1,202 +1,203 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Reflection;
using DisCatSharp.Configuration;
using DisCatSharp.Configuration.Models;
using Microsoft.Extensions.Configuration;
-namespace DisCatSharp.Hosting;
-
-internal struct ExtensionConfigResult
+namespace DisCatSharp.Hosting
{
- /// <summary>
- /// Gets or sets the section.
- /// </summary>
- public ConfigSection? Section { get; set; }
- /// <summary>
- /// Gets or sets the config type.
- /// </summary>
- public Type ConfigType { get; set; }
- /// <summary>
- /// Gets or sets the implementation type.
- /// </summary>
- public Type ImplementationType { get; set; }
-}
+ internal struct ExtensionConfigResult
+ {
+ /// <summary>
+ /// Gets or sets the section.
+ /// </summary>
+ public ConfigSection? Section { get; set; }
+ /// <summary>
+ /// Gets or sets the config type.
+ /// </summary>
+ public Type ConfigType { get; set; }
+ /// <summary>
+ /// Gets or sets the implementation type.
+ /// </summary>
+ public Type ImplementationType { get; set; }
+ }
-/// <summary>
-/// The configuration extensions.
-/// </summary>
-internal static class ConfigurationExtensions
-{
/// <summary>
- /// Find assemblies that match the names provided via <paramref name="names"/>.
+ /// The configuration extensions.
/// </summary>
- /// <remarks>
- /// In some cases the assembly the user is after could be used in the application but
- /// not appear within the <see cref="AppDomain"/>. <br/>
- /// The workaround for this is to check the assemblies in the <see cref="AppDomain"/>, as well as referenced
- /// assemblies. If the targeted assembly is a reference, we need to load it into our workspace to get more info.
- /// </remarks>
- /// <param name="names">Names of assemblies to look for</param>
- /// <returns>Assemblies which meet the given names. No duplicates</returns>
- public static Assembly[] FindAssemblies(string[]? names)
+ internal static class ConfigurationExtensions
{
+ /// <summary>
+ /// Find assemblies that match the names provided via <paramref name="names"/>.
+ /// </summary>
+ /// <remarks>
+ /// In some cases the assembly the user is after could be used in the application but
+ /// not appear within the <see cref="AppDomain"/>. <br/>
+ /// The workaround for this is to check the assemblies in the <see cref="AppDomain"/>, as well as referenced
+ /// assemblies. If the targeted assembly is a reference, we need to load it into our workspace to get more info.
+ /// </remarks>
+ /// <param name="names">Names of assemblies to look for</param>
+ /// <returns>Assemblies which meet the given names. No duplicates</returns>
+ public static Assembly[] FindAssemblies(string[]? names)
+ {
- /*
+ /*
There is a possibility that an assembly can be referenced in multiple assemblies.
To alleviate duplicates we need to shrink our queue as we find things
*/
- List<Assembly> results = new();
+ List<Assembly> results = new();
- if (names is null)
- return results.ToArray();
+ if (names is null)
+ return results.ToArray();
- List<string> queue = new(names);
- foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
- {
- if (!queue.Any())
- break;
+ List<string> queue = new(names);
+ foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ if (!queue.Any())
+ break;
- var loadedAssemblyName = assembly.GetName().Name;
+ var loadedAssemblyName = assembly.GetName().Name;
- // Kinda need the name to do our thing
- if (loadedAssemblyName == null)
- continue;
+ // Kinda need the name to do our thing
+ if (loadedAssemblyName == null)
+ continue;
- // Is this something we're looking for?
- if (queue.Contains(loadedAssemblyName))
- {
- results.Add(assembly);
+ // Is this something we're looking for?
+ if (queue.Contains(loadedAssemblyName))
+ {
+ results.Add(assembly);
- // Shrink queue so we don't accidentally add the same assembly > 1 times
- queue.Remove(loadedAssemblyName);
- }
+ // Shrink queue so we don't accidentally add the same assembly > 1 times
+ queue.Remove(loadedAssemblyName);
+ }
- // Time to check if one of the referenced assemblies is something we're looking for
- foreach (var referencedAssembly in assembly.GetReferencedAssemblies()
- .Where(x => x.Name != null && queue.Contains(x.Name)))
- try
- {
- // Must load the assembly into our workspace so we can do stuff with it later
- results.Add(Assembly.Load(referencedAssembly));
+ // Time to check if one of the referenced assemblies is something we're looking for
+ foreach (var referencedAssembly in assembly.GetReferencedAssemblies()
+ .Where(x => x.Name != null && queue.Contains(x.Name)))
+ try
+ {
+ // Must load the assembly into our workspace so we can do stuff with it later
+ results.Add(Assembly.Load(referencedAssembly));
#pragma warning disable 8604
- queue.Remove(referencedAssembly.Name);
+ queue.Remove(referencedAssembly.Name);
#pragma warning restore 8604
- }
- catch (Exception ex)
- {
- Console.Error.WriteLine($"Unable to load referenced assembly: '{referencedAssembly.Name}' \n\t{ex.Message}");
- }
- }
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine($"Unable to load referenced assembly: '{referencedAssembly.Name}' \n\t{ex.Message}");
+ }
+ }
- return results.ToArray();
- }
+ return results.ToArray();
+ }
- /// <summary>
- /// Easily identify which configuration types have been added to the <paramref name="configuration"/> <br/>
- /// This way we can dynamically load extensions without explicitly doing so
- /// </summary>
- /// <param name="configuration"></param>
- /// <param name="rootName"></param>
- /// <returns>Dictionary where Key -> Name of implemented type<br/>Value -> <see cref="ExtensionConfigResult"/></returns>
- public static Dictionary<string, ExtensionConfigResult> FindImplementedExtensions(this IConfiguration configuration,
- string rootName = Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB)
- {
- if (string.IsNullOrEmpty(rootName))
- throw new ArgumentNullException(nameof(rootName), "Root name must be provided");
+ /// <summary>
+ /// Easily identify which configuration types have been added to the <paramref name="configuration"/> <br/>
+ /// This way we can dynamically load extensions without explicitly doing so
+ /// </summary>
+ /// <param name="configuration"></param>
+ /// <param name="rootName"></param>
+ /// <returns>Dictionary where Key -> Name of implemented type<br/>Value -> <see cref="ExtensionConfigResult"/></returns>
+ public static Dictionary<string, ExtensionConfigResult> FindImplementedExtensions(this IConfiguration configuration,
+ string rootName = Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB)
+ {
+ if (string.IsNullOrEmpty(rootName))
+ throw new ArgumentNullException(nameof(rootName), "Root name must be provided");
- Dictionary<string, ExtensionConfigResult> results = new();
- string[]? assemblyNames;
+ Dictionary<string, ExtensionConfigResult> results = new();
+ string[]? assemblyNames;
- // Has the user defined a using section within the root name?
- if (!configuration.HasSection(rootName, "Using"))
- return results;
+ // Has the user defined a using section within the root name?
+ if (!configuration.HasSection(rootName, "Using"))
+ return results;
- /*
+ /*
There are 2 ways a user could list which assemblies are used
"Using": "[\"Assembly.Name\"]"
"Using": ["Assembly.Name"]
JSON or as Text.
*/
- assemblyNames = string.IsNullOrEmpty(configuration[configuration.ConfigPath(rootName, "Using")])
- ? configuration.GetSection(configuration.ConfigPath(rootName, "Using")).Get<string[]>()
- : Newtonsoft.Json.JsonConvert.DeserializeObject<string[]>(
- configuration[configuration.ConfigPath(rootName, "Using")]);
+ assemblyNames = string.IsNullOrEmpty(configuration[configuration.ConfigPath(rootName, "Using")])
+ ? configuration.GetSection(configuration.ConfigPath(rootName, "Using")).Get<string[]>()
+ : Newtonsoft.Json.JsonConvert.DeserializeObject<string[]>(
+ configuration[configuration.ConfigPath(rootName, "Using")]);
#pragma warning disable 8604
- foreach (var assembly in FindAssemblies(assemblyNames.Select(x => x.StartsWith(Constants.LibName) ? x : $"{Constants.LibName}.{x}").ToArray()))
- {
- ExtensionConfigResult result = new();
-
- foreach (var type in assembly.ExportedTypes
- .Where(x => x.Name.EndsWith(Constants.ConfigSuffix) && !x.IsAbstract && !x.IsInterface))
+ foreach (var assembly in FindAssemblies(assemblyNames.Select(x => x.StartsWith(Constants.LibName) ? x : $"{Constants.LibName}.{x}").ToArray()))
{
- string sectionName = type.Name;
- string prefix = type.Name.Replace(Constants.ConfigSuffix, "");
+ ExtensionConfigResult result = new();
- result.ConfigType = type;
+ foreach (var type in assembly.ExportedTypes
+ .Where(x => x.Name.EndsWith(Constants.ConfigSuffix) && !x.IsAbstract && !x.IsInterface))
+ {
+ string sectionName = type.Name;
+ string prefix = type.Name.Replace(Constants.ConfigSuffix, "");
- // Does a section exist with the classname? (DiscordConfiguration - for instance)
- if (configuration.HasSection(rootName, sectionName))
- result.Section = new ConfigSection(ref configuration, type.Name, rootName);
+ result.ConfigType = type;
- // Does a section exist with the classname minus Configuration? (Discord - for Instance)
- else if (configuration.HasSection(rootName, prefix))
- result.Section = new ConfigSection(ref configuration, prefix, rootName);
+ // Does a section exist with the classname? (DiscordConfiguration - for instance)
+ if (configuration.HasSection(rootName, sectionName))
+ result.Section = new ConfigSection(ref configuration, type.Name, rootName);
- // IF THE SECTION IS NOT PROVIDED --> WE WILL USE DEFAULT CONFIG IMPLEMENTATION
+ // Does a section exist with the classname minus Configuration? (Discord - for Instance)
+ else if (configuration.HasSection(rootName, prefix))
+ result.Section = new ConfigSection(ref configuration, prefix, rootName);
- /*
+ // IF THE SECTION IS NOT PROVIDED --> WE WILL USE DEFAULT CONFIG IMPLEMENTATION
+
+ /*
Now we need to find the type which should consume our config
In the event a user has some "fluff" between prefix and suffix we'll
just check for beginning and ending values.
Type should not be an interface or abstract, should also be assignable to BaseExtension
*/
- var implementationType = assembly.ExportedTypes.FirstOrDefault(x =>
- !x.IsAbstract && !x.IsInterface && x.Name.StartsWith(prefix) &&
- x.Name.EndsWith(Constants.ExtensionSuffix) && x.IsAssignableTo(typeof(BaseExtension)));
+ var implementationType = assembly.ExportedTypes.FirstOrDefault(x =>
+ !x.IsAbstract && !x.IsInterface && x.Name.StartsWith(prefix) &&
+ x.Name.EndsWith(Constants.ExtensionSuffix) && x.IsAssignableTo(typeof(BaseExtension)));
- // If the implementation type was found we can add it to our result set
- if (implementationType != null)
- {
- result.ImplementationType = implementationType;
- results.Add(implementationType.Name, result);
+ // If the implementation type was found we can add it to our result set
+ if (implementationType != null)
+ {
+ result.ImplementationType = implementationType;
+ results.Add(implementationType.Name, result);
+ }
}
}
- }
#pragma warning restore 8604
- return results;
- }
+ return results;
+ }
+ }
}
diff --git a/DisCatSharp.Hosting/Constants.cs b/DisCatSharp.Hosting/Constants.cs
index 8a7ce0133..ae8ec403b 100644
--- a/DisCatSharp.Hosting/Constants.cs
+++ b/DisCatSharp.Hosting/Constants.cs
@@ -1,42 +1,43 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Hosting;
-
-/// <summary>
-/// The constants.
-/// </summary>
-internal static class Constants
+namespace DisCatSharp.Hosting
{
/// <summary>
- /// Gets the lib name.
- /// </summary>
- public static string LibName => Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB;
- /// <summary>
- /// Gets the config suffix.
- /// </summary>
- public static string ConfigSuffix => "Configuration";
- /// <summary>
- /// Gets the extension suffix.
+ /// The constants.
/// </summary>
- public static string ExtensionSuffix => "Extension";
+ internal static class Constants
+ {
+ /// <summary>
+ /// Gets the lib name.
+ /// </summary>
+ public static string LibName => Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB;
+ /// <summary>
+ /// Gets the config suffix.
+ /// </summary>
+ public static string ConfigSuffix => "Configuration";
+ /// <summary>
+ /// Gets the extension suffix.
+ /// </summary>
+ public static string ExtensionSuffix => "Extension";
+ }
}
diff --git a/DisCatSharp.Hosting/DiscordHostedService.cs b/DisCatSharp.Hosting/DiscordHostedService.cs
index c9397851d..c55b53a8e 100644
--- a/DisCatSharp.Hosting/DiscordHostedService.cs
+++ b/DisCatSharp.Hosting/DiscordHostedService.cs
@@ -1,83 +1,84 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Hosting;
-
-/// <summary>
-/// Simple implementation for <see cref="DiscordClient"/> to work as a <see cref="Microsoft.Extensions.Hosting.BackgroundService"/>
-/// </summary>
-public abstract class DiscordHostedService : BaseHostedService, IDiscordHostedService
+namespace DisCatSharp.Hosting
{
- /// <inheritdoc/>
- public DiscordClient Client { get; protected set; }
-
-#pragma warning disable 8618
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordHostedService"/> class.
+ /// Simple implementation for <see cref="DiscordClient"/> to work as a <see cref="Microsoft.Extensions.Hosting.BackgroundService"/>
/// </summary>
- /// <param name="config">IConfiguration provided via Dependency Injection. Aggregate method to access configuration files </param>
- /// <param name="logger">An ILogger to work with, provided via Dependency Injection</param>
- /// <param name="serviceProvider">ServiceProvider reference which contains all items currently registered for Dependency Injection</param>
- /// <param name="applicationLifetime">Contains the appropriate methods for disposing / stopping BackgroundServices during runtime</param>
- /// <param name="configBotSection">The name of the JSON/Config Key which contains the configuration for this Discord Service</param>
- protected DiscordHostedService(IConfiguration config,
- ILogger<DiscordHostedService> logger,
- IServiceProvider serviceProvider,
- IHostApplicationLifetime applicationLifetime,
- string configBotSection = DisCatSharp.Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB)
- : base(config, logger, serviceProvider, applicationLifetime, configBotSection)
+ public abstract class DiscordHostedService : BaseHostedService, IDiscordHostedService
{
+ /// <inheritdoc/>
+ public DiscordClient Client { get; protected set; }
- }
+#pragma warning disable 8618
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordHostedService"/> class.
+ /// </summary>
+ /// <param name="config">IConfiguration provided via Dependency Injection. Aggregate method to access configuration files </param>
+ /// <param name="logger">An ILogger to work with, provided via Dependency Injection</param>
+ /// <param name="serviceProvider">ServiceProvider reference which contains all items currently registered for Dependency Injection</param>
+ /// <param name="applicationLifetime">Contains the appropriate methods for disposing / stopping BackgroundServices during runtime</param>
+ /// <param name="configBotSection">The name of the JSON/Config Key which contains the configuration for this Discord Service</param>
+ protected DiscordHostedService(IConfiguration config,
+ ILogger<DiscordHostedService> logger,
+ IServiceProvider serviceProvider,
+ IHostApplicationLifetime applicationLifetime,
+ string configBotSection = DisCatSharp.Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB)
+ : base(config, logger, serviceProvider, applicationLifetime, configBotSection)
+ {
+
+ }
#pragma warning restore 8618
- protected override Task ConfigureAsync()
- {
- try
+ protected override Task ConfigureAsync()
{
- this.Client = this.Configuration.BuildClient(this.ServiceProvider, this.BotSection);
+ try
+ {
+ this.Client = this.Configuration.BuildClient(this.ServiceProvider, this.BotSection);
+ }
+ catch (Exception ex)
+ {
+ this.Logger.LogError($"Was unable to build {nameof(DiscordClient)} for {this.GetType().Name}");
+ this.OnInitializationError(ex);
+ }
+
+ return Task.CompletedTask;
}
- catch (Exception ex)
+ protected sealed override async Task ConnectAsync() => await this.Client.ConnectAsync();
+
+ protected override Task ConfigureExtensionsAsync()
{
- this.Logger.LogError($"Was unable to build {nameof(DiscordClient)} for {this.GetType().Name}");
- this.OnInitializationError(ex);
+ this.InitializeExtensions(this.Client);
+ return Task.CompletedTask;
}
-
- return Task.CompletedTask;
- }
- protected sealed override async Task ConnectAsync() => await this.Client.ConnectAsync();
-
- protected override Task ConfigureExtensionsAsync()
- {
- this.InitializeExtensions(this.Client);
- return Task.CompletedTask;
}
}
diff --git a/DisCatSharp.Hosting/DiscordSharedHostedService.cs b/DisCatSharp.Hosting/DiscordSharedHostedService.cs
index ef4ec2708..d4a15b019 100644
--- a/DisCatSharp.Hosting/DiscordSharedHostedService.cs
+++ b/DisCatSharp.Hosting/DiscordSharedHostedService.cs
@@ -1,88 +1,89 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Hosting;
-
-/// <summary>
-/// Simple Implementation for <see cref="DiscordShardedClient"/> to work as a <see cref="Microsoft.Extensions.Hosting.BackgroundService"/>
-/// </summary>
-public abstract class DiscordShardedHostedService : BaseHostedService, IDiscordHostedShardService
+namespace DisCatSharp.Hosting
{
- public DiscordShardedClient ShardedClient { get; protected set; }
-
-#pragma warning disable 8618
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordShardedHostedService"/> class.
+ /// Simple Implementation for <see cref="DiscordShardedClient"/> to work as a <see cref="Microsoft.Extensions.Hosting.BackgroundService"/>
/// </summary>
- /// <param name="config">The config.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="serviceProvider">The service provider.</param>
- /// <param name="applicationLifetime">The application lifetime.</param>
- /// <param name="configBotSection">The config bot section.</param>
- protected DiscordShardedHostedService(IConfiguration config,
- ILogger<DiscordShardedHostedService> logger,
- IServiceProvider serviceProvider,
- IHostApplicationLifetime applicationLifetime,
- string configBotSection = DisCatSharp.Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB)
- : base(config, logger, serviceProvider, applicationLifetime, configBotSection)
+ public abstract class DiscordShardedHostedService : BaseHostedService, IDiscordHostedShardService
{
+ public DiscordShardedClient ShardedClient { get; protected set; }
- }
-#pragma warning restore 8618
-
- protected override Task ConfigureAsync()
- {
- try
+#pragma warning disable 8618
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordShardedHostedService"/> class.
+ /// </summary>
+ /// <param name="config">The config.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="serviceProvider">The service provider.</param>
+ /// <param name="applicationLifetime">The application lifetime.</param>
+ /// <param name="configBotSection">The config bot section.</param>
+ protected DiscordShardedHostedService(IConfiguration config,
+ ILogger<DiscordShardedHostedService> logger,
+ IServiceProvider serviceProvider,
+ IHostApplicationLifetime applicationLifetime,
+ string configBotSection = DisCatSharp.Configuration.ConfigurationExtensions.DEFAULT_ROOT_LIB)
+ : base(config, logger, serviceProvider, applicationLifetime, configBotSection)
{
- var config = this.Configuration.ExtractConfig<DiscordConfiguration>(this.ServiceProvider, "Discord", this.BotSection);
- this.ShardedClient = new DiscordShardedClient(config);
+
}
- catch (Exception ex)
+#pragma warning restore 8618
+
+ protected override Task ConfigureAsync()
{
- this.Logger.LogError($"Was unable to build {nameof(DiscordShardedClient)} for {this.GetType().Name}");
- this.OnInitializationError(ex);
- }
+ try
+ {
+ var config = this.Configuration.ExtractConfig<DiscordConfiguration>(this.ServiceProvider, "Discord", this.BotSection);
+ this.ShardedClient = new DiscordShardedClient(config);
+ }
+ catch (Exception ex)
+ {
+ this.Logger.LogError($"Was unable to build {nameof(DiscordShardedClient)} for {this.GetType().Name}");
+ this.OnInitializationError(ex);
+ }
- return Task.CompletedTask;
- }
+ return Task.CompletedTask;
+ }
- protected sealed override async Task ConnectAsync() => await this.ShardedClient.StartAsync();
+ protected sealed override async Task ConnectAsync() => await this.ShardedClient.StartAsync();
- protected override Task ConfigureExtensionsAsync()
- {
- foreach (var client in this.ShardedClient.ShardClients.Values)
+ protected override Task ConfigureExtensionsAsync()
{
- this.InitializeExtensions(client);
- }
+ foreach (var client in this.ShardedClient.ShardClients.Values)
+ {
+ this.InitializeExtensions(client);
+ }
- return Task.CompletedTask;
+ return Task.CompletedTask;
+ }
}
}
diff --git a/DisCatSharp.Hosting/IDiscordHostedService.cs b/DisCatSharp.Hosting/IDiscordHostedService.cs
index e4e9f735f..52466c640 100644
--- a/DisCatSharp.Hosting/IDiscordHostedService.cs
+++ b/DisCatSharp.Hosting/IDiscordHostedService.cs
@@ -1,42 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Hosting;
-
-/// <summary>
-/// Contract required for <see cref="DiscordClient"/> to work in a web hosting environment
-/// </summary>
-public interface IDiscordHostedService : Microsoft.Extensions.Hosting.IHostedService
+namespace DisCatSharp.Hosting
{
+
/// <summary>
- /// Reference to connected client
+ /// Contract required for <see cref="DiscordClient"/> to work in a web hosting environment
/// </summary>
- DiscordClient Client { get; }
-}
+ public interface IDiscordHostedService : Microsoft.Extensions.Hosting.IHostedService
+ {
+ /// <summary>
+ /// Reference to connected client
+ /// </summary>
+ DiscordClient Client { get; }
+ }
-/// <summary>
-/// Contract required for <see cref="DiscordShardedClient"/> to work in a web hosting environment
-/// </summary>
-public interface IDiscordHostedShardService : Microsoft.Extensions.Hosting.IHostedService
-{
- DiscordShardedClient ShardedClient { get; }
+ /// <summary>
+ /// Contract required for <see cref="DiscordShardedClient"/> to work in a web hosting environment
+ /// </summary>
+ public interface IDiscordHostedShardService : Microsoft.Extensions.Hosting.IHostedService
+ {
+ DiscordShardedClient ShardedClient { get; }
+ }
}
diff --git a/DisCatSharp.Interactivity/Enums/ButtonPaginationBehavior.cs b/DisCatSharp.Interactivity/Enums/ButtonPaginationBehavior.cs
index ad4e9108d..04593d056 100644
--- a/DisCatSharp.Interactivity/Enums/ButtonPaginationBehavior.cs
+++ b/DisCatSharp.Interactivity/Enums/ButtonPaginationBehavior.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Interactivity.Enums;
-
-/// <summary>
-/// Represents options of how to handle pagination timing out.
-/// </summary>
-public enum ButtonPaginationBehavior
+namespace DisCatSharp.Interactivity.Enums
{
/// <summary>
- /// The buttons should be disabled when pagination times out.
- /// </summary>
- Disable,
- /// <summary>
- /// The buttons should be left as is when pagination times out.
- /// </summary>
- Ignore,
- /// <summary>
- /// The entire message should be deleted when pagination times out.
- /// </summary>
- DeleteMessage,
- /// <summary>
- /// The buttons should be removed entirely when pagination times out.
+ /// Represents options of how to handle pagination timing out.
/// </summary>
- DeleteButtons
+ public enum ButtonPaginationBehavior
+ {
+ /// <summary>
+ /// The buttons should be disabled when pagination times out.
+ /// </summary>
+ Disable,
+ /// <summary>
+ /// The buttons should be left as is when pagination times out.
+ /// </summary>
+ Ignore,
+ /// <summary>
+ /// The entire message should be deleted when pagination times out.
+ /// </summary>
+ DeleteMessage,
+ /// <summary>
+ /// The buttons should be removed entirely when pagination times out.
+ /// </summary>
+ DeleteButtons
+ }
}
diff --git a/DisCatSharp.Interactivity/Enums/InteractionResponseBehavior.cs b/DisCatSharp.Interactivity/Enums/InteractionResponseBehavior.cs
index fee53e7ab..dedff34af 100644
--- a/DisCatSharp.Interactivity/Enums/InteractionResponseBehavior.cs
+++ b/DisCatSharp.Interactivity/Enums/InteractionResponseBehavior.cs
@@ -1,42 +1,43 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Interactivity.Enums;
-
-/// <summary>
-/// The interaction response behavior.
-/// </summary>
-public enum InteractionResponseBehavior
+namespace DisCatSharp.Interactivity.Enums
{
/// <summary>
- /// Indicates that invalid input should be ignored when waiting for interactions. This will cause the interaction to fail.
- /// </summary>
- Ignore,
- /// <summary>
- /// Indicates that invalid input should be ACK'd. The interaction will succeed, but nothing will happen.
- /// </summary>
- Ack,
- /// <summary>
- /// Indicates that invalid input should warrant an ephemeral error message.
+ /// The interaction response behavior.
/// </summary>
- Respond
+ public enum InteractionResponseBehavior
+ {
+ /// <summary>
+ /// Indicates that invalid input should be ignored when waiting for interactions. This will cause the interaction to fail.
+ /// </summary>
+ Ignore,
+ /// <summary>
+ /// Indicates that invalid input should be ACK'd. The interaction will succeed, but nothing will happen.
+ /// </summary>
+ Ack,
+ /// <summary>
+ /// Indicates that invalid input should warrant an ephemeral error message.
+ /// </summary>
+ Respond
+ }
}
diff --git a/DisCatSharp.Interactivity/Enums/Modal/ModalPage.cs b/DisCatSharp.Interactivity/Enums/Modal/ModalPage.cs
index 78b2ad23c..c724a8446 100644
--- a/DisCatSharp.Interactivity/Enums/Modal/ModalPage.cs
+++ b/DisCatSharp.Interactivity/Enums/Modal/ModalPage.cs
@@ -1,60 +1,61 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
using DisCatSharp.Enums;
-namespace DisCatSharp.Interactivity.Enums;
-
-/// <summary>
-/// A modal page.
-/// </summary>
-public class ModalPage
+namespace DisCatSharp.Interactivity.Enums
{
/// <summary>
- /// Creates a new modal page for the paginated modal builder.
+ /// A modal page.
/// </summary>
- /// <param name="modal">The modal to display.</param>
- /// <param name="openButton">The button to display to open the current page. This is skipped if possible.</param>
- /// <param name="openText">The text to display to open the current page. This is skipped if possible.</param>
- public ModalPage(DiscordInteractionModalBuilder modal, DiscordButtonComponent openButton = null, string openText = null)
+ public class ModalPage
{
- this.Modal = modal;
- this.OpenButton = openButton ?? new DiscordButtonComponent(ButtonStyle.Primary, null, "Open next page", false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("📄")));
- this.OpenMessage = new DiscordInteractionResponseBuilder().WithContent(openText ?? "`Click the button below to continue to the next page.`").AsEphemeral();
- }
+ /// <summary>
+ /// Creates a new modal page for the paginated modal builder.
+ /// </summary>
+ /// <param name="modal">The modal to display.</param>
+ /// <param name="openButton">The button to display to open the current page. This is skipped if possible.</param>
+ /// <param name="openText">The text to display to open the current page. This is skipped if possible.</param>
+ public ModalPage(DiscordInteractionModalBuilder modal, DiscordButtonComponent openButton = null, string openText = null)
+ {
+ this.Modal = modal;
+ this.OpenButton = openButton ?? new DiscordButtonComponent(ButtonStyle.Primary, null, "Open next page", false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("📄")));
+ this.OpenMessage = new DiscordInteractionResponseBuilder().WithContent(openText ?? "`Click the button below to continue to the next page.`").AsEphemeral();
+ }
- /// <summary>
- /// The modal that will be displayed.
- /// </summary>
- public DiscordInteractionModalBuilder Modal { get; set; }
+ /// <summary>
+ /// The modal that will be displayed.
+ /// </summary>
+ public DiscordInteractionModalBuilder Modal { get; set; }
- /// <summary>
- /// The button that will be displayed on the ephemeral message.
- /// </summary>
- public DiscordButtonComponent OpenButton { get; set; }
+ /// <summary>
+ /// The button that will be displayed on the ephemeral message.
+ /// </summary>
+ public DiscordButtonComponent OpenButton { get; set; }
- /// <summary>
- /// The ephemeral message to display for this page.
- /// </summary>
- public DiscordInteractionResponseBuilder OpenMessage { get; set; }
+ /// <summary>
+ /// The ephemeral message to display for this page.
+ /// </summary>
+ public DiscordInteractionResponseBuilder OpenMessage { get; set; }
+ }
}
diff --git a/DisCatSharp.Interactivity/Enums/Modal/PaginatedModalResponse.cs b/DisCatSharp.Interactivity/Enums/Modal/PaginatedModalResponse.cs
index 847a72990..2eaee9237 100644
--- a/DisCatSharp.Interactivity/Enums/Modal/PaginatedModalResponse.cs
+++ b/DisCatSharp.Interactivity/Enums/Modal/PaginatedModalResponse.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
-namespace DisCatSharp.Interactivity.Enums;
-
-/// <summary>
-/// A response from the paginated modal response
-/// </summary>
-public class PaginatedModalResponse
+namespace DisCatSharp.Interactivity.Enums
{
/// <summary>
- /// The responses. The key is the customid of each component.
+ /// A response from the paginated modal response
/// </summary>
- public IReadOnlyDictionary<string, string> Responses { get; internal set; }
+ public class PaginatedModalResponse
+ {
+ /// <summary>
+ /// The responses. The key is the customid of each component.
+ /// </summary>
+ public IReadOnlyDictionary<string, string> Responses { get; internal set; }
- /// <summary>
- /// The last interaction. This is automatically replied to with a ephemeral "thinking" state. Use EditOriginalResponseAsync to modify this.
- /// </summary>
- public DiscordInteraction Interaction { get; internal set; }
+ /// <summary>
+ /// The last interaction. This is automatically replied to with a ephemeral "thinking" state. Use EditOriginalResponseAsync to modify this.
+ /// </summary>
+ public DiscordInteraction Interaction { get; internal set; }
- /// <summary>
- /// Whether the interaction timed out.
- /// </summary>
- public bool TimedOut { get; internal set; }
+ /// <summary>
+ /// Whether the interaction timed out.
+ /// </summary>
+ public bool TimedOut { get; internal set; }
+ }
}
diff --git a/DisCatSharp.Interactivity/Enums/PaginationBehaviour.cs b/DisCatSharp.Interactivity/Enums/PaginationBehaviour.cs
index fffeeebab..29e4c9d65 100644
--- a/DisCatSharp.Interactivity/Enums/PaginationBehaviour.cs
+++ b/DisCatSharp.Interactivity/Enums/PaginationBehaviour.cs
@@ -1,40 +1,41 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Interactivity.Enums;
-
-/// <summary>
-/// Specifies how pagination will handle advancing past the first and last pages.
-/// </summary>
-public enum PaginationBehaviour
+namespace DisCatSharp.Interactivity.Enums
{
/// <summary>
- /// Going forward beyond the last page will loop back to the first page.
- /// Likewise, going back from the first page will loop around to the last page.
+ /// Specifies how pagination will handle advancing past the first and last pages.
/// </summary>
- WrapAround = 0,
+ public enum PaginationBehaviour
+ {
+ /// <summary>
+ /// Going forward beyond the last page will loop back to the first page.
+ /// Likewise, going back from the first page will loop around to the last page.
+ /// </summary>
+ WrapAround = 0,
- /// <summary>
- /// Attempting to go beyond the first or last page will be ignored.
- /// </summary>
- Ignore = 1
+ /// <summary>
+ /// Attempting to go beyond the first or last page will be ignored.
+ /// </summary>
+ Ignore = 1
+ }
}
diff --git a/DisCatSharp.Interactivity/Enums/PaginationDeletion.cs b/DisCatSharp.Interactivity/Enums/PaginationDeletion.cs
index 4dbec498e..47c610765 100644
--- a/DisCatSharp.Interactivity/Enums/PaginationDeletion.cs
+++ b/DisCatSharp.Interactivity/Enums/PaginationDeletion.cs
@@ -1,44 +1,45 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Interactivity.Enums;
-
-/// <summary>
-/// Specifies what should be done once pagination times out.
-/// </summary>
-public enum PaginationDeletion
+namespace DisCatSharp.Interactivity.Enums
{
/// <summary>
- /// Reaction emojis will be deleted on timeout.
+ /// Specifies what should be done once pagination times out.
/// </summary>
- DeleteEmojis = 0,
+ public enum PaginationDeletion
+ {
+ /// <summary>
+ /// Reaction emojis will be deleted on timeout.
+ /// </summary>
+ DeleteEmojis = 0,
- /// <summary>
- /// Reaction emojis will not be deleted on timeout.
- /// </summary>
- KeepEmojis = 1,
+ /// <summary>
+ /// Reaction emojis will not be deleted on timeout.
+ /// </summary>
+ KeepEmojis = 1,
- /// <summary>
- /// The message will be completely deleted on timeout.
- /// </summary>
- DeleteMessage = 2
+ /// <summary>
+ /// The message will be completely deleted on timeout.
+ /// </summary>
+ DeleteMessage = 2
+ }
}
diff --git a/DisCatSharp.Interactivity/Enums/PollBehaviour.cs b/DisCatSharp.Interactivity/Enums/PollBehaviour.cs
index 564223781..9589e068c 100644
--- a/DisCatSharp.Interactivity/Enums/PollBehaviour.cs
+++ b/DisCatSharp.Interactivity/Enums/PollBehaviour.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Interactivity.Enums;
-
-/// <summary>
-/// Specifies what should be done when a poll times out.
-/// </summary>
-public enum PollBehaviour
+namespace DisCatSharp.Interactivity.Enums
{
/// <summary>
- /// Reaction emojis will not be deleted.
+ /// Specifies what should be done when a poll times out.
/// </summary>
- KeepEmojis = 0,
+ public enum PollBehaviour
+ {
+ /// <summary>
+ /// Reaction emojis will not be deleted.
+ /// </summary>
+ KeepEmojis = 0,
- /// <summary>
- /// Reaction emojis will be deleted.
- /// </summary>
- DeleteEmojis = 1
+ /// <summary>
+ /// Reaction emojis will be deleted.
+ /// </summary>
+ DeleteEmojis = 1
+ }
}
diff --git a/DisCatSharp.Interactivity/Enums/SplitType.cs b/DisCatSharp.Interactivity/Enums/SplitType.cs
index 586fbf6bc..6e3c3e9c2 100644
--- a/DisCatSharp.Interactivity/Enums/SplitType.cs
+++ b/DisCatSharp.Interactivity/Enums/SplitType.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Interactivity.Enums;
-
-/// <summary>
-/// Specifies how to split a string.
-/// </summary>
-public enum SplitType
+namespace DisCatSharp.Interactivity.Enums
{
/// <summary>
- /// Splits string per 500 characters.
+ /// Specifies how to split a string.
/// </summary>
- Character,
+ public enum SplitType
+ {
+ /// <summary>
+ /// Splits string per 500 characters.
+ /// </summary>
+ Character,
- /// <summary>
- /// Splits string per 15 lines.
- /// </summary>
- Line
+ /// <summary>
+ /// Splits string per 15 lines.
+ /// </summary>
+ Line
+ }
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Components/ComponentEventWaiter.cs b/DisCatSharp.Interactivity/EventHandling/Components/ComponentEventWaiter.cs
index e3b8860a4..f8d6b5f80 100644
--- a/DisCatSharp.Interactivity/EventHandling/Components/ComponentEventWaiter.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Components/ComponentEventWaiter.cs
@@ -1,152 +1,153 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
using ConcurrentCollections;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Interactivity.Enums;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// A component-based version of <see cref="EventWaiter{T}"/>
-/// </summary>
-internal class ComponentEventWaiter : IDisposable
+namespace DisCatSharp.Interactivity.EventHandling
{
- private readonly DiscordClient _client;
- private readonly ConcurrentHashSet<ComponentMatchRequest> _matchRequests = new();
- private readonly ConcurrentHashSet<ComponentCollectRequest> _collectRequests = new();
-
- private readonly DiscordFollowupMessageBuilder _message;
- private readonly InteractivityConfiguration _config;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ComponentEventWaiter"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="config">The config.</param>
- public ComponentEventWaiter(DiscordClient client, InteractivityConfiguration config)
- {
- this._client = client;
- this._client.ComponentInteractionCreated += this.Handle;
- this._config = config;
-
- this._message = new DiscordFollowupMessageBuilder { Content = config.ResponseMessage ?? "This message was not meant for you.", IsEphemeral = true };
- }
-
/// <summary>
- /// Waits for a specified <see cref="ComponentMatchRequest"/>'s predicate to be fulfilled.
+ /// A component-based version of <see cref="EventWaiter{T}"/>
/// </summary>
- /// <param name="request">The request to wait for.</param>
- /// <returns>The returned args, or null if it timed out.</returns>
- public async Task<ComponentInteractionCreateEventArgs> WaitForMatchAsync(ComponentMatchRequest request)
+ internal class ComponentEventWaiter : IDisposable
{
- this._matchRequests.Add(request);
-
- try
- {
- return await request.Tcs.Task.ConfigureAwait(false);
- }
- catch (Exception e)
- {
- this._client.Logger.LogError(InteractivityEvents.InteractivityWaitError, e, "An exception was thrown while waiting for components.");
- return null;
- }
- finally
+ private readonly DiscordClient _client;
+ private readonly ConcurrentHashSet<ComponentMatchRequest> _matchRequests = new();
+ private readonly ConcurrentHashSet<ComponentCollectRequest> _collectRequests = new();
+
+ private readonly DiscordFollowupMessageBuilder _message;
+ private readonly InteractivityConfiguration _config;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ComponentEventWaiter"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="config">The config.</param>
+ public ComponentEventWaiter(DiscordClient client, InteractivityConfiguration config)
{
- this._matchRequests.TryRemove(request);
- }
- }
+ this._client = client;
+ this._client.ComponentInteractionCreated += this.Handle;
+ this._config = config;
- /// <summary>
- /// Collects reactions and returns the result when the <see cref="ComponentMatchRequest"/>'s cancellation token is canceled.
- /// </summary>
- /// <param name="request">The request to wait on.</param>
- /// <returns>The result from request's predicate over the period of time leading up to the token's cancellation.</returns>
- public async Task<IReadOnlyList<ComponentInteractionCreateEventArgs>> CollectMatchesAsync(ComponentCollectRequest request)
- {
- this._collectRequests.Add(request);
- try
- {
- await request.Tcs.Task.ConfigureAwait(false);
- }
- catch (Exception e)
- {
- this._client.Logger.LogError(InteractivityEvents.InteractivityCollectorError, e, "There was an error while collecting component event args.");
+ this._message = new DiscordFollowupMessageBuilder { Content = config.ResponseMessage ?? "This message was not meant for you.", IsEphemeral = true };
}
- finally
+
+ /// <summary>
+ /// Waits for a specified <see cref="ComponentMatchRequest"/>'s predicate to be fulfilled.
+ /// </summary>
+ /// <param name="request">The request to wait for.</param>
+ /// <returns>The returned args, or null if it timed out.</returns>
+ public async Task<ComponentInteractionCreateEventArgs> WaitForMatchAsync(ComponentMatchRequest request)
{
- this._collectRequests.TryRemove(request);
- }
+ this._matchRequests.Add(request);
- return request.Collected.ToArray();
- }
+ try
+ {
+ return await request.Tcs.Task.ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ this._client.Logger.LogError(InteractivityEvents.InteractivityWaitError, e, "An exception was thrown while waiting for components.");
+ return null;
+ }
+ finally
+ {
+ this._matchRequests.TryRemove(request);
+ }
+ }
- /// <summary>
- /// Handles the waiter.
- /// </summary>
- /// <param name="_">The client.</param>
- /// <param name="args">The args.</param>
- private async Task Handle(DiscordClient _, ComponentInteractionCreateEventArgs args)
- {
- foreach (var mreq in this._matchRequests.ToArray())
+ /// <summary>
+ /// Collects reactions and returns the result when the <see cref="ComponentMatchRequest"/>'s cancellation token is canceled.
+ /// </summary>
+ /// <param name="request">The request to wait on.</param>
+ /// <returns>The result from request's predicate over the period of time leading up to the token's cancellation.</returns>
+ public async Task<IReadOnlyList<ComponentInteractionCreateEventArgs>> CollectMatchesAsync(ComponentCollectRequest request)
{
- if (mreq.Message == args.Message && mreq.IsMatch(args))
- mreq.Tcs.TrySetResult(args);
+ this._collectRequests.Add(request);
+ try
+ {
+ await request.Tcs.Task.ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ this._client.Logger.LogError(InteractivityEvents.InteractivityCollectorError, e, "There was an error while collecting component event args.");
+ }
+ finally
+ {
+ this._collectRequests.TryRemove(request);
+ }
- else if (this._config.ResponseBehavior is InteractionResponseBehavior.Respond)
- await args.Interaction.CreateFollowupMessageAsync(this._message).ConfigureAwait(false);
+ return request.Collected.ToArray();
}
-
- foreach (var creq in this._collectRequests.ToArray())
+ /// <summary>
+ /// Handles the waiter.
+ /// </summary>
+ /// <param name="_">The client.</param>
+ /// <param name="args">The args.</param>
+ private async Task Handle(DiscordClient _, ComponentInteractionCreateEventArgs args)
{
- if (creq.Message == args.Message && creq.IsMatch(args))
+ foreach (var mreq in this._matchRequests.ToArray())
{
- await args.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate).ConfigureAwait(false);
-
- if (creq.IsMatch(args))
- creq.Collected.Add(args);
+ if (mreq.Message == args.Message && mreq.IsMatch(args))
+ mreq.Tcs.TrySetResult(args);
else if (this._config.ResponseBehavior is InteractionResponseBehavior.Respond)
await args.Interaction.CreateFollowupMessageAsync(this._message).ConfigureAwait(false);
}
+
+
+ foreach (var creq in this._collectRequests.ToArray())
+ {
+ if (creq.Message == args.Message && creq.IsMatch(args))
+ {
+ await args.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate).ConfigureAwait(false);
+
+ if (creq.IsMatch(args))
+ creq.Collected.Add(args);
+
+ else if (this._config.ResponseBehavior is InteractionResponseBehavior.Respond)
+ await args.Interaction.CreateFollowupMessageAsync(this._message).ConfigureAwait(false);
+ }
+ }
+ }
+ /// <summary>
+ /// Disposes the waiter.
+ /// </summary>
+ public void Dispose()
+ {
+ this._matchRequests.Clear();
+ this._collectRequests.Clear();
+ this._client.ComponentInteractionCreated -= this.Handle;
}
- }
- /// <summary>
- /// Disposes the waiter.
- /// </summary>
- public void Dispose()
- {
- this._matchRequests.Clear();
- this._collectRequests.Clear();
- this._client.ComponentInteractionCreated -= this.Handle;
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Components/ComponentPaginator.cs b/DisCatSharp.Interactivity/EventHandling/Components/ComponentPaginator.cs
index 50915b8b1..93b1ae922 100644
--- a/DisCatSharp.Interactivity/EventHandling/Components/ComponentPaginator.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Components/ComponentPaginator.cs
@@ -1,182 +1,183 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Interactivity.Enums;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// The component paginator.
-/// </summary>
-internal class ComponentPaginator : IPaginator
+namespace DisCatSharp.Interactivity.EventHandling
{
- private readonly DiscordClient _client;
- private readonly InteractivityConfiguration _config;
- private readonly DiscordMessageBuilder _builder = new();
- private readonly Dictionary<ulong, IPaginationRequest> _requests = new();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ComponentPaginator"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="config">The config.</param>
- public ComponentPaginator(DiscordClient client, InteractivityConfiguration config)
- {
- this._client = client;
- this._client.ComponentInteractionCreated += this.Handle;
- this._config = config;
- }
-
/// <summary>
- /// Does the pagination async.
+ /// The component paginator.
/// </summary>
- /// <param name="request">The request.</param>
- public async Task DoPaginationAsync(IPaginationRequest request)
+ internal class ComponentPaginator : IPaginator
{
- var id = (await request.GetMessageAsync().ConfigureAwait(false)).Id;
- this._requests.Add(id, request);
-
- try
- {
- var tcs = await request.GetTaskCompletionSourceAsync().ConfigureAwait(false);
- await tcs.Task.ConfigureAwait(false);
- }
- catch (Exception ex)
+ private readonly DiscordClient _client;
+ private readonly InteractivityConfiguration _config;
+ private readonly DiscordMessageBuilder _builder = new();
+ private readonly Dictionary<ulong, IPaginationRequest> _requests = new();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ComponentPaginator"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="config">The config.</param>
+ public ComponentPaginator(DiscordClient client, InteractivityConfiguration config)
{
- this._client.Logger.LogError(InteractivityEvents.InteractivityPaginationError, ex, "There was an exception while paginating.");
+ this._client = client;
+ this._client.ComponentInteractionCreated += this.Handle;
+ this._config = config;
}
- finally
+
+ /// <summary>
+ /// Does the pagination async.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public async Task DoPaginationAsync(IPaginationRequest request)
{
- this._requests.Remove(id);
+ var id = (await request.GetMessageAsync().ConfigureAwait(false)).Id;
+ this._requests.Add(id, request);
+
try
{
- await request.DoCleanupAsync().ConfigureAwait(false);
+ var tcs = await request.GetTaskCompletionSourceAsync().ConfigureAwait(false);
+ await tcs.Task.ConfigureAwait(false);
}
catch (Exception ex)
{
- this._client.Logger.LogError(InteractivityEvents.InteractivityPaginationError, ex, "There was an exception while cleaning up pagination.");
+ this._client.Logger.LogError(InteractivityEvents.InteractivityPaginationError, ex, "There was an exception while paginating.");
+ }
+ finally
+ {
+ this._requests.Remove(id);
+ try
+ {
+ await request.DoCleanupAsync().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ this._client.Logger.LogError(InteractivityEvents.InteractivityPaginationError, ex, "There was an exception while cleaning up pagination.");
+ }
}
}
- }
- /// <summary>
- /// Disposes the paginator.
- /// </summary>
- public void Dispose() => this._client.ComponentInteractionCreated -= this.Handle;
+ /// <summary>
+ /// Disposes the paginator.
+ /// </summary>
+ public void Dispose() => this._client.ComponentInteractionCreated -= this.Handle;
- /// <summary>
- /// Handles the pagination event.
- /// </summary>
- /// <param name="_">The client.</param>
- /// <param name="e">The event arguments.</param>
- private async Task Handle(DiscordClient _, ComponentInteractionCreateEventArgs e)
- {
- if (e.Interaction.Type == InteractionType.ModalSubmit)
- return;
+ /// <summary>
+ /// Handles the pagination event.
+ /// </summary>
+ /// <param name="_">The client.</param>
+ /// <param name="e">The event arguments.</param>
+ private async Task Handle(DiscordClient _, ComponentInteractionCreateEventArgs e)
+ {
+ if (e.Interaction.Type == InteractionType.ModalSubmit)
+ return;
- if (!this._requests.TryGetValue(e.Message.Id, out var req))
- return;
+ if (!this._requests.TryGetValue(e.Message.Id, out var req))
+ return;
- if (this._config.AckPaginationButtons)
- {
- e.Handled = true;
- await e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate).ConfigureAwait(false);
- }
+ if (this._config.AckPaginationButtons)
+ {
+ e.Handled = true;
+ await e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate).ConfigureAwait(false);
+ }
- if (await req.GetUserAsync().ConfigureAwait(false) != e.User)
- {
- if (this._config.ResponseBehavior is InteractionResponseBehavior.Respond)
- await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder { Content = this._config.ResponseMessage, IsEphemeral = true }).ConfigureAwait(false);
+ if (await req.GetUserAsync().ConfigureAwait(false) != e.User)
+ {
+ if (this._config.ResponseBehavior is InteractionResponseBehavior.Respond)
+ await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder { Content = this._config.ResponseMessage, IsEphemeral = true }).ConfigureAwait(false);
- return;
- }
+ return;
+ }
- if (req is InteractionPaginationRequest ipr)
- ipr.RegenerateCts(e.Interaction); // Necessary to ensure we don't prematurely yeet the CTS //
+ if (req is InteractionPaginationRequest ipr)
+ ipr.RegenerateCts(e.Interaction); // Necessary to ensure we don't prematurely yeet the CTS //
- await this.HandlePaginationAsync(req, e).ConfigureAwait(false);
- }
+ await this.HandlePaginationAsync(req, e).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the pagination async.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="args">The arguments.</param>
- private async Task HandlePaginationAsync(IPaginationRequest request, ComponentInteractionCreateEventArgs args)
- {
- var buttons = this._config.PaginationButtons;
- var msg = await request.GetMessageAsync().ConfigureAwait(false);
- var id = args.Id;
- var tcs = await request.GetTaskCompletionSourceAsync().ConfigureAwait(false);
+ /// <summary>
+ /// Handles the pagination async.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="args">The arguments.</param>
+ private async Task HandlePaginationAsync(IPaginationRequest request, ComponentInteractionCreateEventArgs args)
+ {
+ var buttons = this._config.PaginationButtons;
+ var msg = await request.GetMessageAsync().ConfigureAwait(false);
+ var id = args.Id;
+ var tcs = await request.GetTaskCompletionSourceAsync().ConfigureAwait(false);
#pragma warning disable CS8846 // The switch expression does not handle all possible values of its input type (it is not exhaustive).
- var paginationTask = id switch
+ var paginationTask = id switch
#pragma warning restore CS8846 // The switch expression does not handle all possible values of its input type (it is not exhaustive).
{
- _ when id == buttons.SkipLeft.CustomId => request.SkipLeftAsync(),
- _ when id == buttons.SkipRight.CustomId => request.SkipRightAsync(),
- _ when id == buttons.Stop.CustomId => Task.FromResult(tcs.TrySetResult(true)),
- _ when id == buttons.Left.CustomId => request.PreviousPageAsync(),
- _ when id == buttons.Right.CustomId => request.NextPageAsync(),
- };
+ _ when id == buttons.SkipLeft.CustomId => request.SkipLeftAsync(),
+ _ when id == buttons.SkipRight.CustomId => request.SkipRightAsync(),
+ _ when id == buttons.Stop.CustomId => Task.FromResult(tcs.TrySetResult(true)),
+ _ when id == buttons.Left.CustomId => request.PreviousPageAsync(),
+ _ when id == buttons.Right.CustomId => request.NextPageAsync(),
+ };
- await paginationTask.ConfigureAwait(false);
+ await paginationTask.ConfigureAwait(false);
- if (id == buttons.Stop.CustomId)
- return;
+ if (id == buttons.Stop.CustomId)
+ return;
- var page = await request.GetPageAsync().ConfigureAwait(false);
+ var page = await request.GetPageAsync().ConfigureAwait(false);
- var bts = await request.GetButtonsAsync().ConfigureAwait(false);
+ var bts = await request.GetButtonsAsync().ConfigureAwait(false);
- if (request is InteractionPaginationRequest ipr)
- {
- var builder = new DiscordWebhookBuilder()
- .WithContent(page.Content)
- .AddEmbed(page.Embed)
- .AddComponents(bts);
+ if (request is InteractionPaginationRequest ipr)
+ {
+ var builder = new DiscordWebhookBuilder()
+ .WithContent(page.Content)
+ .AddEmbed(page.Embed)
+ .AddComponents(bts);
- await args.Interaction.EditOriginalResponseAsync(builder).ConfigureAwait(false);
- return;
- }
+ await args.Interaction.EditOriginalResponseAsync(builder).ConfigureAwait(false);
+ return;
+ }
- this._builder.Clear();
+ this._builder.Clear();
- this._builder
- .WithContent(page.Content)
- .AddEmbed(page.Embed)
- .AddComponents(bts);
+ this._builder
+ .WithContent(page.Content)
+ .AddEmbed(page.Embed)
+ .AddComponents(bts);
- await this._builder.ModifyAsync(msg).ConfigureAwait(false);
+ await this._builder.ModifyAsync(msg).ConfigureAwait(false);
+ }
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Components/ModalEventWaiter.cs b/DisCatSharp.Interactivity/EventHandling/Components/ModalEventWaiter.cs
index de376d3c2..83b6dd22e 100644
--- a/DisCatSharp.Interactivity/EventHandling/Components/ModalEventWaiter.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Components/ModalEventWaiter.cs
@@ -1,111 +1,112 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using ConcurrentCollections;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Interactivity.Enums;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// A modal-based version of <see cref="EventWaiter{T}"/>
-/// </summary>
-internal class ModalEventWaiter : IDisposable
+namespace DisCatSharp.Interactivity.EventHandling
{
- private readonly DiscordClient _client;
- private readonly ConcurrentHashSet<ModalMatchRequest> _modalMatchRequests = new();
-
- private readonly DiscordFollowupMessageBuilder _message;
- private readonly InteractivityConfiguration _config;
-
/// <summary>
- /// Initializes a new instance of the <see cref="ComponentEventWaiter"/> class.
+ /// A modal-based version of <see cref="EventWaiter{T}"/>
/// </summary>
- /// <param name="client">The client.</param>
- /// <param name="config">The config.</param>
- public ModalEventWaiter(DiscordClient client, InteractivityConfiguration config)
+ internal class ModalEventWaiter : IDisposable
{
- this._client = client;
- this._client.ComponentInteractionCreated += this.Handle;
- this._config = config;
+ private readonly DiscordClient _client;
+ private readonly ConcurrentHashSet<ModalMatchRequest> _modalMatchRequests = new();
- this._message = new DiscordFollowupMessageBuilder { Content = config.ResponseMessage ?? "This modal was not meant for you.", IsEphemeral = true };
- }
-
- /// <summary>
- /// Waits for a specified <see cref="ModalMatchRequest"/>'s predicate to be fulfilled.
- /// </summary>
- /// <param name="request">The request to wait for.</param>
- /// <returns>The returned args, or null if it timed out.</returns>
- public async Task<ComponentInteractionCreateEventArgs> WaitForModalMatchAsync(ModalMatchRequest request)
- {
- this._modalMatchRequests.Add(request);
+ private readonly DiscordFollowupMessageBuilder _message;
+ private readonly InteractivityConfiguration _config;
- try
- {
- return await request.Tcs.Task.ConfigureAwait(false);
- }
- catch (Exception e)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ComponentEventWaiter"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="config">The config.</param>
+ public ModalEventWaiter(DiscordClient client, InteractivityConfiguration config)
{
- this._client.Logger.LogError(InteractivityEvents.InteractivityWaitError, e, "An exception was thrown while waiting for modals.");
- return null;
+ this._client = client;
+ this._client.ComponentInteractionCreated += this.Handle;
+ this._config = config;
+
+ this._message = new DiscordFollowupMessageBuilder { Content = config.ResponseMessage ?? "This modal was not meant for you.", IsEphemeral = true };
}
- finally
+
+ /// <summary>
+ /// Waits for a specified <see cref="ModalMatchRequest"/>'s predicate to be fulfilled.
+ /// </summary>
+ /// <param name="request">The request to wait for.</param>
+ /// <returns>The returned args, or null if it timed out.</returns>
+ public async Task<ComponentInteractionCreateEventArgs> WaitForModalMatchAsync(ModalMatchRequest request)
{
- this._modalMatchRequests.TryRemove(request);
+ this._modalMatchRequests.Add(request);
+
+ try
+ {
+ return await request.Tcs.Task.ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ this._client.Logger.LogError(InteractivityEvents.InteractivityWaitError, e, "An exception was thrown while waiting for modals.");
+ return null;
+ }
+ finally
+ {
+ this._modalMatchRequests.TryRemove(request);
+ }
}
- }
- /// <summary>
- /// Handles the waiter.
- /// </summary>
- /// <param name="_">The client.</param>
- /// <param name="args">The args.</param>
- private async Task Handle(DiscordClient _, ComponentInteractionCreateEventArgs args)
- {
- foreach (var mreq in this._modalMatchRequests.ToArray())
+ /// <summary>
+ /// Handles the waiter.
+ /// </summary>
+ /// <param name="_">The client.</param>
+ /// <param name="args">The args.</param>
+ private async Task Handle(DiscordClient _, ComponentInteractionCreateEventArgs args)
{
- if (mreq.CustomId == args.Interaction.Data.CustomId && mreq.IsMatch(args))
- mreq.Tcs.TrySetResult(args);
+ foreach (var mreq in this._modalMatchRequests.ToArray())
+ {
+ if (mreq.CustomId == args.Interaction.Data.CustomId && mreq.IsMatch(args))
+ mreq.Tcs.TrySetResult(args);
- else if (this._config.ResponseBehavior is InteractionResponseBehavior.Respond)
- await args.Interaction.CreateFollowupMessageAsync(this._message).ConfigureAwait(false);
+ else if (this._config.ResponseBehavior is InteractionResponseBehavior.Respond)
+ await args.Interaction.CreateFollowupMessageAsync(this._message).ConfigureAwait(false);
+ }
}
- }
- /// <summary>
- /// Disposes the waiter.
- /// </summary>
- public void Dispose()
- {
- this._modalMatchRequests.Clear();
- this._client.ComponentInteractionCreated -= this.Handle;
+ /// <summary>
+ /// Disposes the waiter.
+ /// </summary>
+ public void Dispose()
+ {
+ this._modalMatchRequests.Clear();
+ this._client.ComponentInteractionCreated -= this.Handle;
+ }
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Components/PaginationButtons.cs b/DisCatSharp.Interactivity/EventHandling/Components/PaginationButtons.cs
index f1e113974..06d29a5f2 100644
--- a/DisCatSharp.Interactivity/EventHandling/Components/PaginationButtons.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Components/PaginationButtons.cs
@@ -1,94 +1,95 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
using DisCatSharp.Enums;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// The pagination buttons.
-/// </summary>
-public class PaginationButtons
+namespace DisCatSharp.Interactivity.EventHandling
{
/// <summary>
- /// Gets or sets the skip left button.
+ /// The pagination buttons.
/// </summary>
- public DiscordButtonComponent SkipLeft { internal get; set; }
+ public class PaginationButtons
+ {
+ /// <summary>
+ /// Gets or sets the skip left button.
+ /// </summary>
+ public DiscordButtonComponent SkipLeft { internal get; set; }
- /// <summary>
- /// Gets or sets the left button.
- /// </summary>
- public DiscordButtonComponent Left { internal get; set; }
+ /// <summary>
+ /// Gets or sets the left button.
+ /// </summary>
+ public DiscordButtonComponent Left { internal get; set; }
- /// <summary>
- /// Gets or sets the stop button.
- /// </summary>
- public DiscordButtonComponent Stop { internal get; set; }
+ /// <summary>
+ /// Gets or sets the stop button.
+ /// </summary>
+ public DiscordButtonComponent Stop { internal get; set; }
- /// <summary>
- /// Gets or sets the right button.
- /// </summary>
- public DiscordButtonComponent Right { internal get; set; }
+ /// <summary>
+ /// Gets or sets the right button.
+ /// </summary>
+ public DiscordButtonComponent Right { internal get; set; }
- /// <summary>
- /// Gets or sets the skip right button.
- /// </summary>
- public DiscordButtonComponent SkipRight { internal get; set; }
+ /// <summary>
+ /// Gets or sets the skip right button.
+ /// </summary>
+ public DiscordButtonComponent SkipRight { internal get; set; }
- /// <summary>
- /// Gets the button array.
- /// </summary>
- internal DiscordButtonComponent[] ButtonArray => new[]
- {
- this.SkipLeft,
- this.Left,
- this.Stop,
- this.Right,
- this.SkipRight
- };
+ /// <summary>
+ /// Gets the button array.
+ /// </summary>
+ internal DiscordButtonComponent[] ButtonArray => new[]
+ {
+ this.SkipLeft,
+ this.Left,
+ this.Stop,
+ this.Right,
+ this.SkipRight
+ };
- /// <summary>
- /// Initializes a new instance of the <see cref="PaginationButtons"/> class.
- /// </summary>
- public PaginationButtons()
- {
- this.SkipLeft = new DiscordButtonComponent(ButtonStyle.Secondary, "leftskip", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("⏮")));
- this.Left = new DiscordButtonComponent(ButtonStyle.Secondary, "left", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("◀")));
- this.Stop = new DiscordButtonComponent(ButtonStyle.Secondary, "stop", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("⏹")));
- this.Right = new DiscordButtonComponent(ButtonStyle.Secondary, "right", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("▶")));
- this.SkipRight = new DiscordButtonComponent(ButtonStyle.Secondary, "rightskip", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("⏭")));
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PaginationButtons"/> class.
+ /// </summary>
+ public PaginationButtons()
+ {
+ this.SkipLeft = new DiscordButtonComponent(ButtonStyle.Secondary, "leftskip", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("⏮")));
+ this.Left = new DiscordButtonComponent(ButtonStyle.Secondary, "left", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("◀")));
+ this.Stop = new DiscordButtonComponent(ButtonStyle.Secondary, "stop", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("⏹")));
+ this.Right = new DiscordButtonComponent(ButtonStyle.Secondary, "right", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("▶")));
+ this.SkipRight = new DiscordButtonComponent(ButtonStyle.Secondary, "rightskip", null, false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("⏭")));
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="PaginationButtons"/> class.
- /// </summary>
- /// <param name="other">The other <see cref="PaginationButtons"/>.</param>
- public PaginationButtons(PaginationButtons other)
- {
- this.Stop = new DiscordButtonComponent(other.Stop);
- this.Left = new DiscordButtonComponent(other.Left);
- this.Right = new DiscordButtonComponent(other.Right);
- this.SkipLeft = new DiscordButtonComponent(other.SkipLeft);
- this.SkipRight = new DiscordButtonComponent(other.SkipRight);
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PaginationButtons"/> class.
+ /// </summary>
+ /// <param name="other">The other <see cref="PaginationButtons"/>.</param>
+ public PaginationButtons(PaginationButtons other)
+ {
+ this.Stop = new DiscordButtonComponent(other.Stop);
+ this.Left = new DiscordButtonComponent(other.Left);
+ this.Right = new DiscordButtonComponent(other.Right);
+ this.SkipLeft = new DiscordButtonComponent(other.SkipLeft);
+ this.SkipRight = new DiscordButtonComponent(other.SkipRight);
+ }
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Components/Requests/ButtonPaginationRequest.cs b/DisCatSharp.Interactivity/EventHandling/Components/Requests/ButtonPaginationRequest.cs
index e2d4e5ef4..4cbbfc605 100644
--- a/DisCatSharp.Interactivity/EventHandling/Components/Requests/ButtonPaginationRequest.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Components/Requests/ButtonPaginationRequest.cs
@@ -1,244 +1,245 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Interactivity.Enums;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// The button pagination request.
-/// </summary>
-internal class ButtonPaginationRequest : IPaginationRequest
+namespace DisCatSharp.Interactivity.EventHandling
{
- private int _index;
- private readonly List<Page> _pages = new();
-
- private readonly TaskCompletionSource<bool> _tcs = new();
-
- private readonly CancellationToken _token;
- private readonly DiscordUser _user;
- private readonly DiscordMessage _message;
- private readonly PaginationButtons _buttons;
- private readonly PaginationBehaviour _wrapBehavior;
- private readonly ButtonPaginationBehavior _behaviorBehavior;
-
/// <summary>
- /// Initializes a new instance of the <see cref="ButtonPaginationRequest"/> class.
+ /// The button pagination request.
/// </summary>
- /// <param name="message">The message.</param>
- /// <param name="user">The user.</param>
- /// <param name="behavior">The behavior.</param>
- /// <param name="buttonBehavior">The button behavior.</param>
- /// <param name="buttons">The buttons.</param>
- /// <param name="pages">The pages.</param>
- /// <param name="token">The token.</param>
- public ButtonPaginationRequest(DiscordMessage message, DiscordUser user,
- PaginationBehaviour behavior, ButtonPaginationBehavior buttonBehavior,
- PaginationButtons buttons, IEnumerable<Page> pages, CancellationToken token)
+ internal class ButtonPaginationRequest : IPaginationRequest
{
- this._user = user;
- this._token = token;
- this._buttons = new PaginationButtons(buttons);
- this._message = message;
- this._wrapBehavior = behavior;
- this._behaviorBehavior = buttonBehavior;
- this._pages.AddRange(pages);
-
- this._token.Register(() => this._tcs.TrySetResult(false));
- }
-
- /// <summary>
- /// Gets the page count.
- /// </summary>
- public int PageCount => this._pages.Count;
+ private int _index;
+ private readonly List<Page> _pages = new();
+
+ private readonly TaskCompletionSource<bool> _tcs = new();
+
+ private readonly CancellationToken _token;
+ private readonly DiscordUser _user;
+ private readonly DiscordMessage _message;
+ private readonly PaginationButtons _buttons;
+ private readonly PaginationBehaviour _wrapBehavior;
+ private readonly ButtonPaginationBehavior _behaviorBehavior;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ButtonPaginationRequest"/> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <param name="user">The user.</param>
+ /// <param name="behavior">The behavior.</param>
+ /// <param name="buttonBehavior">The button behavior.</param>
+ /// <param name="buttons">The buttons.</param>
+ /// <param name="pages">The pages.</param>
+ /// <param name="token">The token.</param>
+ public ButtonPaginationRequest(DiscordMessage message, DiscordUser user,
+ PaginationBehaviour behavior, ButtonPaginationBehavior buttonBehavior,
+ PaginationButtons buttons, IEnumerable<Page> pages, CancellationToken token)
+ {
+ this._user = user;
+ this._token = token;
+ this._buttons = new PaginationButtons(buttons);
+ this._message = message;
+ this._wrapBehavior = behavior;
+ this._behaviorBehavior = buttonBehavior;
+ this._pages.AddRange(pages);
+
+ this._token.Register(() => this._tcs.TrySetResult(false));
+ }
- /// <summary>
- /// Gets the page.
- /// </summary>
- public Task<Page> GetPageAsync()
- {
- var page = Task.FromResult(this._pages[this._index]);
+ /// <summary>
+ /// Gets the page count.
+ /// </summary>
+ public int PageCount => this._pages.Count;
- if (this.PageCount is 1)
+ /// <summary>
+ /// Gets the page.
+ /// </summary>
+ public Task<Page> GetPageAsync()
{
- this._buttons.SkipLeft.Disable();
- this._buttons.Left.Disable();
- this._buttons.Right.Disable();
- this._buttons.SkipRight.Disable();
- this._buttons.Stop.Enable();
- return page;
- }
+ var page = Task.FromResult(this._pages[this._index]);
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
- return page;
+ if (this.PageCount is 1)
+ {
+ this._buttons.SkipLeft.Disable();
+ this._buttons.Left.Disable();
+ this._buttons.Right.Disable();
+ this._buttons.SkipRight.Disable();
+ this._buttons.Stop.Enable();
+ return page;
+ }
- this._buttons.SkipLeft.Disabled = this._index < 2;
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ return page;
- this._buttons.Left.Disabled = this._index < 1;
+ this._buttons.SkipLeft.Disabled = this._index < 2;
- this._buttons.Right.Disabled = this._index >= this.PageCount - 1;
+ this._buttons.Left.Disabled = this._index < 1;
- this._buttons.SkipRight.Disabled = this._index >= this.PageCount - 2;
+ this._buttons.Right.Disabled = this._index >= this.PageCount - 1;
- return page;
- }
+ this._buttons.SkipRight.Disabled = this._index >= this.PageCount - 2;
- /// <summary>
- /// Skips the left.
- /// </summary>
- public Task SkipLeftAsync()
- {
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
- {
- this._index = this._index is 0 ? this._pages.Count - 1 : 0;
- return Task.CompletedTask;
+ return page;
}
- this._index = 0;
+ /// <summary>
+ /// Skips the left.
+ /// </summary>
+ public Task SkipLeftAsync()
+ {
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ {
+ this._index = this._index is 0 ? this._pages.Count - 1 : 0;
+ return Task.CompletedTask;
+ }
- return Task.CompletedTask;
- }
+ this._index = 0;
- /// <summary>
- /// Skips the right.
- /// </summary>
- public Task SkipRightAsync()
- {
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
- {
- this._index = this._index == this.PageCount - 1 ? 0 : this.PageCount - 1;
return Task.CompletedTask;
}
- this._index = this._pages.Count - 1;
-
- return Task.CompletedTask;
- }
-
- /// <summary>
- /// Gets the next page.
- /// </summary>
- public Task NextPageAsync()
- {
- this._index++;
-
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ /// <summary>
+ /// Skips the right.
+ /// </summary>
+ public Task SkipRightAsync()
{
- if (this._index >= this.PageCount)
- this._index = 0;
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ {
+ this._index = this._index == this.PageCount - 1 ? 0 : this.PageCount - 1;
+ return Task.CompletedTask;
+ }
+
+ this._index = this._pages.Count - 1;
return Task.CompletedTask;
}
- this._index = Math.Min(this._index, this.PageCount - 1);
+ /// <summary>
+ /// Gets the next page.
+ /// </summary>
+ public Task NextPageAsync()
+ {
+ this._index++;
- return Task.CompletedTask;
- }
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ {
+ if (this._index >= this.PageCount)
+ this._index = 0;
- /// <summary>
- /// Gets the previous page.
- /// </summary>
- public Task PreviousPageAsync()
- {
- this._index--;
+ return Task.CompletedTask;
+ }
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
- {
- if (this._index is -1)
- this._index = this._pages.Count - 1;
+ this._index = Math.Min(this._index, this.PageCount - 1);
return Task.CompletedTask;
}
- this._index = Math.Max(this._index, 0);
-
- return Task.CompletedTask;
- }
-
- /// <summary>
- /// Gets the emojis.
- /// </summary>
- public Task<PaginationEmojis> GetEmojisAsync() => Task.FromException<PaginationEmojis>(new NotSupportedException("Emojis aren't supported for this request."));
-
- /// <summary>
- /// Gets the buttons.
- /// </summary>
- public Task<IEnumerable<DiscordButtonComponent>> GetButtonsAsync()
- => Task.FromResult((IEnumerable<DiscordButtonComponent>)this._buttons.ButtonArray);
-
- /// <summary>
- /// Gets the message.
- /// </summary>
- public Task<DiscordMessage> GetMessageAsync() => Task.FromResult(this._message);
-
- /// <summary>
- /// Gets the user.
- /// </summary>
- public Task<DiscordUser> GetUserAsync() => Task.FromResult(this._user);
-
- /// <summary>
- /// Gets the task completion source.
- /// </summary>
- public Task<TaskCompletionSource<bool>> GetTaskCompletionSourceAsync() => Task.FromResult(this._tcs);
-
- /// <summary>
- /// Does the cleanup.
- /// </summary>
- public async Task DoCleanupAsync()
- {
- switch (this._behaviorBehavior)
+ /// <summary>
+ /// Gets the previous page.
+ /// </summary>
+ public Task PreviousPageAsync()
{
- case ButtonPaginationBehavior.Disable:
- var buttons = this._buttons.ButtonArray.Select(b => b.Disable());
+ this._index--;
- var builder = new DiscordMessageBuilder()
- .WithContent(this._pages[this._index].Content)
- .AddEmbed(this._pages[this._index].Embed)
- .AddComponents(buttons);
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ {
+ if (this._index is -1)
+ this._index = this._pages.Count - 1;
- await builder.ModifyAsync(this._message).ConfigureAwait(false);
- break;
+ return Task.CompletedTask;
+ }
- case ButtonPaginationBehavior.DeleteButtons:
- builder = new DiscordMessageBuilder()
- .WithContent(this._pages[this._index].Content)
- .AddEmbed(this._pages[this._index].Embed);
+ this._index = Math.Max(this._index, 0);
- await builder.ModifyAsync(this._message).ConfigureAwait(false);
- break;
-
- case ButtonPaginationBehavior.DeleteMessage:
- await this._message.DeleteAsync().ConfigureAwait(false);
- break;
+ return Task.CompletedTask;
+ }
- case ButtonPaginationBehavior.Ignore:
- break;
+ /// <summary>
+ /// Gets the emojis.
+ /// </summary>
+ public Task<PaginationEmojis> GetEmojisAsync() => Task.FromException<PaginationEmojis>(new NotSupportedException("Emojis aren't supported for this request."));
+
+ /// <summary>
+ /// Gets the buttons.
+ /// </summary>
+ public Task<IEnumerable<DiscordButtonComponent>> GetButtonsAsync()
+ => Task.FromResult((IEnumerable<DiscordButtonComponent>)this._buttons.ButtonArray);
+
+ /// <summary>
+ /// Gets the message.
+ /// </summary>
+ public Task<DiscordMessage> GetMessageAsync() => Task.FromResult(this._message);
+
+ /// <summary>
+ /// Gets the user.
+ /// </summary>
+ public Task<DiscordUser> GetUserAsync() => Task.FromResult(this._user);
+
+ /// <summary>
+ /// Gets the task completion source.
+ /// </summary>
+ public Task<TaskCompletionSource<bool>> GetTaskCompletionSourceAsync() => Task.FromResult(this._tcs);
+
+ /// <summary>
+ /// Does the cleanup.
+ /// </summary>
+ public async Task DoCleanupAsync()
+ {
+ switch (this._behaviorBehavior)
+ {
+ case ButtonPaginationBehavior.Disable:
+ var buttons = this._buttons.ButtonArray.Select(b => b.Disable());
+
+ var builder = new DiscordMessageBuilder()
+ .WithContent(this._pages[this._index].Content)
+ .AddEmbed(this._pages[this._index].Embed)
+ .AddComponents(buttons);
+
+ await builder.ModifyAsync(this._message).ConfigureAwait(false);
+ break;
+
+ case ButtonPaginationBehavior.DeleteButtons:
+ builder = new DiscordMessageBuilder()
+ .WithContent(this._pages[this._index].Content)
+ .AddEmbed(this._pages[this._index].Embed);
+
+ await builder.ModifyAsync(this._message).ConfigureAwait(false);
+ break;
+
+ case ButtonPaginationBehavior.DeleteMessage:
+ await this._message.DeleteAsync().ConfigureAwait(false);
+ break;
+
+ case ButtonPaginationBehavior.Ignore:
+ break;
+ }
}
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Components/Requests/ComponentCollectRequest.cs b/DisCatSharp.Interactivity/EventHandling/Components/Requests/ComponentCollectRequest.cs
index 6ee2d5b59..a2e03aa10 100644
--- a/DisCatSharp.Interactivity/EventHandling/Components/Requests/ComponentCollectRequest.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Components/Requests/ComponentCollectRequest.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Threading;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// Represents a component event that is being waited for.
-/// </summary>
-internal sealed class ComponentCollectRequest : ComponentMatchRequest
+namespace DisCatSharp.Interactivity.EventHandling
{
/// <summary>
- /// Gets the collected.
+ /// Represents a component event that is being waited for.
/// </summary>
- public ConcurrentBag<ComponentInteractionCreateEventArgs> Collected { get; private set; }
+ internal sealed class ComponentCollectRequest : ComponentMatchRequest
+ {
+ /// <summary>
+ /// Gets the collected.
+ /// </summary>
+ public ConcurrentBag<ComponentInteractionCreateEventArgs> Collected { get; private set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ComponentCollectRequest"/> class.
- /// </summary>
- /// <param name="message"></param>
- /// <param name="predicate">The predicate.</param>
- /// <param name="cancellation">The cancellation token.</param>
- public ComponentCollectRequest(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken cancellation) : base(message, predicate, cancellation) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ComponentCollectRequest"/> class.
+ /// </summary>
+ /// <param name="message"></param>
+ /// <param name="predicate">The predicate.</param>
+ /// <param name="cancellation">The cancellation token.</param>
+ public ComponentCollectRequest(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken cancellation) : base(message, predicate, cancellation) { }
+ }
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Components/Requests/ComponentMatchRequest.cs b/DisCatSharp.Interactivity/EventHandling/Components/Requests/ComponentMatchRequest.cs
index 11a9cfd5e..bbb84acc6 100644
--- a/DisCatSharp.Interactivity/EventHandling/Components/Requests/ComponentMatchRequest.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Components/Requests/ComponentMatchRequest.cs
@@ -1,69 +1,70 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// Represents a match that is being waited for.
-/// </summary>
-internal class ComponentMatchRequest
+namespace DisCatSharp.Interactivity.EventHandling
{
/// <summary>
- /// The id to wait on. This should be uniquely formatted to avoid collisions.
+ /// Represents a match that is being waited for.
/// </summary>
- public DiscordMessage Message { get; private set; }
+ internal class ComponentMatchRequest
+ {
+ /// <summary>
+ /// The id to wait on. This should be uniquely formatted to avoid collisions.
+ /// </summary>
+ public DiscordMessage Message { get; private set; }
- /// <summary>
- /// The completion source that represents the result of the match.
- /// </summary>
- public TaskCompletionSource<ComponentInteractionCreateEventArgs> Tcs { get; private set; } = new();
+ /// <summary>
+ /// The completion source that represents the result of the match.
+ /// </summary>
+ public TaskCompletionSource<ComponentInteractionCreateEventArgs> Tcs { get; private set; } = new();
- protected readonly CancellationToken Cancellation;
- protected readonly Func<ComponentInteractionCreateEventArgs, bool> Predicate;
+ protected readonly CancellationToken Cancellation;
+ protected readonly Func<ComponentInteractionCreateEventArgs, bool> Predicate;
- /// <summary>
- /// Initializes a new instance of the <see cref="ComponentMatchRequest"/> class.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="predicate">The predicate.</param>
- /// <param name="cancellation">The cancellation token.</param>
- public ComponentMatchRequest(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken cancellation)
- {
- this.Message = message;
- this.Predicate = predicate;
- this.Cancellation = cancellation;
- this.Cancellation.Register(() => this.Tcs.TrySetResult(null)); // TrySetCancelled would probably be better but I digress ~Velvet //
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ComponentMatchRequest"/> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <param name="predicate">The predicate.</param>
+ /// <param name="cancellation">The cancellation token.</param>
+ public ComponentMatchRequest(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken cancellation)
+ {
+ this.Message = message;
+ this.Predicate = predicate;
+ this.Cancellation = cancellation;
+ this.Cancellation.Register(() => this.Tcs.TrySetResult(null)); // TrySetCancelled would probably be better but I digress ~Velvet //
+ }
- /// <summary>
- /// Whether it is a match.
- /// </summary>
- /// <param name="args">The arguments.</param>
- public bool IsMatch(ComponentInteractionCreateEventArgs args) => this.Predicate(args);
+ /// <summary>
+ /// Whether it is a match.
+ /// </summary>
+ /// <param name="args">The arguments.</param>
+ public bool IsMatch(ComponentInteractionCreateEventArgs args) => this.Predicate(args);
+ }
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Components/Requests/InteractionPaginationRequest.cs b/DisCatSharp.Interactivity/EventHandling/Components/Requests/InteractionPaginationRequest.cs
index e31e6c329..f040e5933 100644
--- a/DisCatSharp.Interactivity/EventHandling/Components/Requests/InteractionPaginationRequest.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Components/Requests/InteractionPaginationRequest.cs
@@ -1,264 +1,265 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Interactivity.Enums;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// The interaction pagination request.
-/// </summary>
-internal class InteractionPaginationRequest : IPaginationRequest
+namespace DisCatSharp.Interactivity.EventHandling
{
- private int _index;
- private readonly List<Page> _pages = new();
-
- private readonly TaskCompletionSource<bool> _tcs = new();
-
-
- private DiscordInteraction _lastInteraction;
- private CancellationTokenSource _interactionCts;
-
- private readonly CancellationToken _token;
- private readonly DiscordUser _user;
- private readonly DiscordMessage _message;
- private readonly PaginationButtons _buttons;
- private readonly PaginationBehaviour _wrapBehavior;
- private readonly ButtonPaginationBehavior _behaviorBehavior;
-
-
/// <summary>
- /// Initializes a new instance of the <see cref="InteractionPaginationRequest"/> class.
+ /// The interaction pagination request.
/// </summary>
- /// <param name="interaction">The interaction.</param>
- /// <param name="message">The message.</param>
- /// <param name="user">The user.</param>
- /// <param name="behavior">The behavior.</param>
- /// <param name="behaviorBehavior">The behavior behavior.</param>
- /// <param name="buttons">The buttons.</param>
- /// <param name="pages">The pages.</param>
- /// <param name="token">The token.</param>
- public InteractionPaginationRequest(DiscordInteraction interaction, DiscordMessage message, DiscordUser user,
- PaginationBehaviour behavior, ButtonPaginationBehavior behaviorBehavior,
- PaginationButtons buttons, IEnumerable<Page> pages, CancellationToken token)
+ internal class InteractionPaginationRequest : IPaginationRequest
{
- this._user = user;
- this._token = token;
- this._buttons = new PaginationButtons(buttons);
- this._message = message;
- this._wrapBehavior = behavior;
- this._behaviorBehavior = behaviorBehavior;
- this._pages.AddRange(pages);
-
- this.RegenerateCts(interaction);
- this._token.Register(() => this._tcs.TrySetResult(false));
- }
-
- /// <summary>
- /// Gets the page count.
- /// </summary>
- public int PageCount => this._pages.Count;
+ private int _index;
+ private readonly List<Page> _pages = new();
+
+ private readonly TaskCompletionSource<bool> _tcs = new();
+
+
+ private DiscordInteraction _lastInteraction;
+ private CancellationTokenSource _interactionCts;
+
+ private readonly CancellationToken _token;
+ private readonly DiscordUser _user;
+ private readonly DiscordMessage _message;
+ private readonly PaginationButtons _buttons;
+ private readonly PaginationBehaviour _wrapBehavior;
+ private readonly ButtonPaginationBehavior _behaviorBehavior;
+
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InteractionPaginationRequest"/> class.
+ /// </summary>
+ /// <param name="interaction">The interaction.</param>
+ /// <param name="message">The message.</param>
+ /// <param name="user">The user.</param>
+ /// <param name="behavior">The behavior.</param>
+ /// <param name="behaviorBehavior">The behavior behavior.</param>
+ /// <param name="buttons">The buttons.</param>
+ /// <param name="pages">The pages.</param>
+ /// <param name="token">The token.</param>
+ public InteractionPaginationRequest(DiscordInteraction interaction, DiscordMessage message, DiscordUser user,
+ PaginationBehaviour behavior, ButtonPaginationBehavior behaviorBehavior,
+ PaginationButtons buttons, IEnumerable<Page> pages, CancellationToken token)
+ {
+ this._user = user;
+ this._token = token;
+ this._buttons = new PaginationButtons(buttons);
+ this._message = message;
+ this._wrapBehavior = behavior;
+ this._behaviorBehavior = behaviorBehavior;
+ this._pages.AddRange(pages);
+
+ this.RegenerateCts(interaction);
+ this._token.Register(() => this._tcs.TrySetResult(false));
+ }
- /// <summary>
- /// Regenerates the cts.
- /// </summary>
- /// <param name="interaction">The interaction.</param>
- internal void RegenerateCts(DiscordInteraction interaction)
- {
- this._interactionCts?.Dispose();
- this._lastInteraction = interaction;
- this._interactionCts = new CancellationTokenSource(TimeSpan.FromSeconds((60 * 15) - 5));
- this._interactionCts.Token.Register(() => this._tcs.TrySetResult(false));
- }
+ /// <summary>
+ /// Gets the page count.
+ /// </summary>
+ public int PageCount => this._pages.Count;
- /// <summary>
- /// Gets the page.
- /// </summary>
- public Task<Page> GetPageAsync()
- {
- var page = Task.FromResult(this._pages[this._index]);
-
- if (this.PageCount is 1)
+ /// <summary>
+ /// Regenerates the cts.
+ /// </summary>
+ /// <param name="interaction">The interaction.</param>
+ internal void RegenerateCts(DiscordInteraction interaction)
{
- this._buttons.ButtonArray.Select(b => b.Disable());
- this._buttons.Stop.Enable();
- return page;
+ this._interactionCts?.Dispose();
+ this._lastInteraction = interaction;
+ this._interactionCts = new CancellationTokenSource(TimeSpan.FromSeconds((60 * 15) - 5));
+ this._interactionCts.Token.Register(() => this._tcs.TrySetResult(false));
}
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
- return page;
+ /// <summary>
+ /// Gets the page.
+ /// </summary>
+ public Task<Page> GetPageAsync()
+ {
+ var page = Task.FromResult(this._pages[this._index]);
- this._buttons.SkipLeft.Disabled = this._index < 2;
+ if (this.PageCount is 1)
+ {
+ this._buttons.ButtonArray.Select(b => b.Disable());
+ this._buttons.Stop.Enable();
+ return page;
+ }
- this._buttons.Left.Disabled = this._index < 1;
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ return page;
- this._buttons.Right.Disabled = this._index == this.PageCount - 1;
+ this._buttons.SkipLeft.Disabled = this._index < 2;
- this._buttons.SkipRight.Disabled = this._index >= this.PageCount - 2;
+ this._buttons.Left.Disabled = this._index < 1;
- return page;
- }
+ this._buttons.Right.Disabled = this._index == this.PageCount - 1;
- /// <summary>
- /// Skips the left page.
- /// </summary>
- public Task SkipLeftAsync()
- {
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
- {
- this._index = this._index is 0 ? this._pages.Count - 1 : 0;
- return Task.CompletedTask;
+ this._buttons.SkipRight.Disabled = this._index >= this.PageCount - 2;
+
+ return page;
}
- this._index = 0;
+ /// <summary>
+ /// Skips the left page.
+ /// </summary>
+ public Task SkipLeftAsync()
+ {
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ {
+ this._index = this._index is 0 ? this._pages.Count - 1 : 0;
+ return Task.CompletedTask;
+ }
- return Task.CompletedTask;
- }
+ this._index = 0;
- /// <summary>
- /// Skips the right page.
- /// </summary>
- public Task SkipRightAsync()
- {
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
- {
- this._index = this._index == this.PageCount - 1 ? 0 : this.PageCount - 1;
return Task.CompletedTask;
}
- this._index = this._pages.Count - 1;
-
- return Task.CompletedTask;
- }
-
- /// <summary>
- /// Gets the next page.
- /// </summary>
- /// <returns>A Task.</returns>
- public Task NextPageAsync()
- {
- this._index++;
-
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ /// <summary>
+ /// Skips the right page.
+ /// </summary>
+ public Task SkipRightAsync()
{
- if (this._index >= this.PageCount)
- this._index = 0;
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ {
+ this._index = this._index == this.PageCount - 1 ? 0 : this.PageCount - 1;
+ return Task.CompletedTask;
+ }
+
+ this._index = this._pages.Count - 1;
return Task.CompletedTask;
}
- this._index = Math.Min(this._index, this.PageCount - 1);
+ /// <summary>
+ /// Gets the next page.
+ /// </summary>
+ /// <returns>A Task.</returns>
+ public Task NextPageAsync()
+ {
+ this._index++;
- return Task.CompletedTask;
- }
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ {
+ if (this._index >= this.PageCount)
+ this._index = 0;
- /// <summary>
- /// Gets the previous page.
- /// </summary>
- public Task PreviousPageAsync()
- {
- this._index--;
+ return Task.CompletedTask;
+ }
- if (this._wrapBehavior is PaginationBehaviour.WrapAround)
- {
- if (this._index is -1)
- this._index = this._pages.Count - 1;
+ this._index = Math.Min(this._index, this.PageCount - 1);
return Task.CompletedTask;
}
- this._index = Math.Max(this._index, 0);
-
- return Task.CompletedTask;
- }
-
- /// <summary>
- /// Gets the emojis.
- /// </summary>
- public Task<PaginationEmojis> GetEmojisAsync()
- => Task.FromException<PaginationEmojis>(new NotSupportedException("Emojis aren't supported for this request."));
-
- /// <summary>
- /// Gets the buttons.
- /// </summary>
- public Task<IEnumerable<DiscordButtonComponent>> GetButtonsAsync()
- => Task.FromResult((IEnumerable<DiscordButtonComponent>)this._buttons.ButtonArray);
-
- /// <summary>
- /// Gets the message.
- /// </summary>
- public Task<DiscordMessage> GetMessageAsync() => Task.FromResult(this._message);
-
- /// <summary>
- /// Gets the user.
- /// </summary>
- public Task<DiscordUser> GetUserAsync() => Task.FromResult(this._user);
-
- /// <summary>
- /// Gets the task completion source.
- /// </summary>
- public Task<TaskCompletionSource<bool>> GetTaskCompletionSourceAsync() => Task.FromResult(this._tcs);
-
- /// <summary>
- /// Cleanup.
- /// </summary>
- public async Task DoCleanupAsync()
- {
- switch (this._behaviorBehavior)
+ /// <summary>
+ /// Gets the previous page.
+ /// </summary>
+ public Task PreviousPageAsync()
{
- case ButtonPaginationBehavior.Disable:
- var buttons = this._buttons.ButtonArray
- .Select(b => new DiscordButtonComponent(b))
- .Select(b => b.Disable());
+ this._index--;
- var builder = new DiscordWebhookBuilder()
- .WithContent(this._pages[this._index].Content)
- .AddEmbed(this._pages[this._index].Embed)
- .AddComponents(buttons);
+ if (this._wrapBehavior is PaginationBehaviour.WrapAround)
+ {
+ if (this._index is -1)
+ this._index = this._pages.Count - 1;
- await this._lastInteraction.EditOriginalResponseAsync(builder).ConfigureAwait(false);
- break;
+ return Task.CompletedTask;
+ }
- case ButtonPaginationBehavior.DeleteButtons:
- builder = new DiscordWebhookBuilder()
- .WithContent(this._pages[this._index].Content)
- .AddEmbed(this._pages[this._index].Embed);
+ this._index = Math.Max(this._index, 0);
- await this._lastInteraction.EditOriginalResponseAsync(builder).ConfigureAwait(false);
- break;
-
- case ButtonPaginationBehavior.DeleteMessage:
- await this._lastInteraction.DeleteOriginalResponseAsync().ConfigureAwait(false);
- break;
+ return Task.CompletedTask;
+ }
- case ButtonPaginationBehavior.Ignore:
- break;
+ /// <summary>
+ /// Gets the emojis.
+ /// </summary>
+ public Task<PaginationEmojis> GetEmojisAsync()
+ => Task.FromException<PaginationEmojis>(new NotSupportedException("Emojis aren't supported for this request."));
+
+ /// <summary>
+ /// Gets the buttons.
+ /// </summary>
+ public Task<IEnumerable<DiscordButtonComponent>> GetButtonsAsync()
+ => Task.FromResult((IEnumerable<DiscordButtonComponent>)this._buttons.ButtonArray);
+
+ /// <summary>
+ /// Gets the message.
+ /// </summary>
+ public Task<DiscordMessage> GetMessageAsync() => Task.FromResult(this._message);
+
+ /// <summary>
+ /// Gets the user.
+ /// </summary>
+ public Task<DiscordUser> GetUserAsync() => Task.FromResult(this._user);
+
+ /// <summary>
+ /// Gets the task completion source.
+ /// </summary>
+ public Task<TaskCompletionSource<bool>> GetTaskCompletionSourceAsync() => Task.FromResult(this._tcs);
+
+ /// <summary>
+ /// Cleanup.
+ /// </summary>
+ public async Task DoCleanupAsync()
+ {
+ switch (this._behaviorBehavior)
+ {
+ case ButtonPaginationBehavior.Disable:
+ var buttons = this._buttons.ButtonArray
+ .Select(b => new DiscordButtonComponent(b))
+ .Select(b => b.Disable());
+
+ var builder = new DiscordWebhookBuilder()
+ .WithContent(this._pages[this._index].Content)
+ .AddEmbed(this._pages[this._index].Embed)
+ .AddComponents(buttons);
+
+ await this._lastInteraction.EditOriginalResponseAsync(builder).ConfigureAwait(false);
+ break;
+
+ case ButtonPaginationBehavior.DeleteButtons:
+ builder = new DiscordWebhookBuilder()
+ .WithContent(this._pages[this._index].Content)
+ .AddEmbed(this._pages[this._index].Embed);
+
+ await this._lastInteraction.EditOriginalResponseAsync(builder).ConfigureAwait(false);
+ break;
+
+ case ButtonPaginationBehavior.DeleteMessage:
+ await this._lastInteraction.DeleteOriginalResponseAsync().ConfigureAwait(false);
+ break;
+
+ case ButtonPaginationBehavior.Ignore:
+ break;
+ }
}
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Components/Requests/ModalMatchRequest.cs b/DisCatSharp.Interactivity/EventHandling/Components/Requests/ModalMatchRequest.cs
index e447f48a8..ea51e196c 100644
--- a/DisCatSharp.Interactivity/EventHandling/Components/Requests/ModalMatchRequest.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Components/Requests/ModalMatchRequest.cs
@@ -1,68 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
using DisCatSharp.EventArgs;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// Represents a match that is being waited for.
-/// </summary>
-internal class ModalMatchRequest
+namespace DisCatSharp.Interactivity.EventHandling
{
/// <summary>
- /// The id to wait on. This should be uniquely formatted to avoid collisions.
+ /// Represents a match that is being waited for.
/// </summary>
- public string CustomId { get; private set; }
+ internal class ModalMatchRequest
+ {
+ /// <summary>
+ /// The id to wait on. This should be uniquely formatted to avoid collisions.
+ /// </summary>
+ public string CustomId { get; private set; }
- /// <summary>
- /// The completion source that represents the result of the match.
- /// </summary>
- public TaskCompletionSource<ComponentInteractionCreateEventArgs> Tcs { get; private set; } = new();
+ /// <summary>
+ /// The completion source that represents the result of the match.
+ /// </summary>
+ public TaskCompletionSource<ComponentInteractionCreateEventArgs> Tcs { get; private set; } = new();
- protected readonly CancellationToken Cancellation;
- protected readonly Func<ComponentInteractionCreateEventArgs, bool> Predicate;
+ protected readonly CancellationToken Cancellation;
+ protected readonly Func<ComponentInteractionCreateEventArgs, bool> Predicate;
- /// <summary>
- /// Initializes a new instance of the <see cref="ModalMatchRequest"/> class.
- /// </summary>
- /// <param name="customId">The custom id.</param>
- /// <param name="predicate">The predicate.</param>
- /// <param name="cancellation">The cancellation token.</param>
- public ModalMatchRequest(string customId, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken cancellation)
- {
- this.CustomId = customId;
- this.Predicate = predicate;
- this.Cancellation = cancellation;
- this.Cancellation.Register(() => this.Tcs.TrySetResult(null)); // TrySetCancelled would probably be better but I digress ~Velvet //
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ModalMatchRequest"/> class.
+ /// </summary>
+ /// <param name="customId">The custom id.</param>
+ /// <param name="predicate">The predicate.</param>
+ /// <param name="cancellation">The cancellation token.</param>
+ public ModalMatchRequest(string customId, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken cancellation)
+ {
+ this.CustomId = customId;
+ this.Predicate = predicate;
+ this.Cancellation = cancellation;
+ this.Cancellation.Register(() => this.Tcs.TrySetResult(null)); // TrySetCancelled would probably be better but I digress ~Velvet //
+ }
- /// <summary>
- /// Whether it is a match.
- /// </summary>
- /// <param name="args">The arguments.</param>
- public bool IsMatch(ComponentInteractionCreateEventArgs args) => this.Predicate(args);
+ /// <summary>
+ /// Whether it is a match.
+ /// </summary>
+ /// <param name="args">The arguments.</param>
+ public bool IsMatch(ComponentInteractionCreateEventArgs args) => this.Predicate(args);
+ }
}
diff --git a/DisCatSharp.Interactivity/EventHandling/EventWaiter.cs b/DisCatSharp.Interactivity/EventHandling/EventWaiter.cs
index c9279bda8..8a9ba3d7c 100644
--- a/DisCatSharp.Interactivity/EventHandling/EventWaiter.cs
+++ b/DisCatSharp.Interactivity/EventHandling/EventWaiter.cs
@@ -1,173 +1,174 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Reflection;
using System.Threading.Tasks;
using ConcurrentCollections;
using DisCatSharp.Common.Utilities;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// EventWaiter is a class that serves as a layer between the InteractivityExtension
-/// and the DiscordClient to listen to an event and check for matches to a predicate.
-/// </summary>
-/// <typeparam name="T"></typeparam>
-internal class EventWaiter<T> : IDisposable where T : AsyncEventArgs
+namespace DisCatSharp.Interactivity.EventHandling
{
- DiscordClient _client;
- AsyncEvent<DiscordClient, T> _event;
- AsyncEventHandler<DiscordClient, T> _handler;
- ConcurrentHashSet<MatchRequest<T>> _matchRequests;
- ConcurrentHashSet<CollectRequest<T>> _collectRequests;
- bool _disposed;
-
- /// <summary>
- /// Creates a new EventWaiter object.
- /// </summary>
- /// <param name="client">Your DiscordClient</param>
- public EventWaiter(DiscordClient client)
- {
- this._client = client;
- var tinfo = this._client.GetType().GetTypeInfo();
- var handler = tinfo.DeclaredFields.First(x => x.FieldType == typeof(AsyncEvent<DiscordClient, T>));
- this._matchRequests = new ConcurrentHashSet<MatchRequest<T>>();
- this._collectRequests = new ConcurrentHashSet<CollectRequest<T>>();
- this._event = (AsyncEvent<DiscordClient, T>)handler.GetValue(this._client);
- this._handler = new AsyncEventHandler<DiscordClient, T>(this.HandleEvent);
- this._event.Register(this._handler);
- }
-
/// <summary>
- /// Waits for a match to a specific request, else returns null.
+ /// EventWaiter is a class that serves as a layer between the InteractivityExtension
+ /// and the DiscordClient to listen to an event and check for matches to a predicate.
/// </summary>
- /// <param name="request">Request to match</param>
- /// <returns></returns>
- public async Task<T> WaitForMatchAsync(MatchRequest<T> request)
+ /// <typeparam name="T"></typeparam>
+ internal class EventWaiter<T> : IDisposable where T : AsyncEventArgs
{
- T result = null;
- this._matchRequests.Add(request);
- try
+ DiscordClient _client;
+ AsyncEvent<DiscordClient, T> _event;
+ AsyncEventHandler<DiscordClient, T> _handler;
+ ConcurrentHashSet<MatchRequest<T>> _matchRequests;
+ ConcurrentHashSet<CollectRequest<T>> _collectRequests;
+ bool _disposed;
+
+ /// <summary>
+ /// Creates a new EventWaiter object.
+ /// </summary>
+ /// <param name="client">Your DiscordClient</param>
+ public EventWaiter(DiscordClient client)
{
- result = await request.Tcs.Task.ConfigureAwait(false);
+ this._client = client;
+ var tinfo = this._client.GetType().GetTypeInfo();
+ var handler = tinfo.DeclaredFields.First(x => x.FieldType == typeof(AsyncEvent<DiscordClient, T>));
+ this._matchRequests = new ConcurrentHashSet<MatchRequest<T>>();
+ this._collectRequests = new ConcurrentHashSet<CollectRequest<T>>();
+ this._event = (AsyncEvent<DiscordClient, T>)handler.GetValue(this._client);
+ this._handler = new AsyncEventHandler<DiscordClient, T>(this.HandleEvent);
+ this._event.Register(this._handler);
}
- catch (Exception ex)
- {
- this._client.Logger.LogError(InteractivityEvents.InteractivityWaitError, ex, "An exception occurred while waiting for {0}", typeof(T).Name);
- }
- finally
- {
- request.Dispose();
- this._matchRequests.TryRemove(request);
- }
- return result;
- }
- /// <summary>
- /// Collects the matches async.
- /// </summary>
- /// <param name="request">The request.</param>
- public async Task<ReadOnlyCollection<T>> CollectMatchesAsync(CollectRequest<T> request)
- {
- ReadOnlyCollection<T> result = null;
- this._collectRequests.Add(request);
- try
- {
- await request.Tcs.Task.ConfigureAwait(false);
- }
- catch (Exception ex)
+ /// <summary>
+ /// Waits for a match to a specific request, else returns null.
+ /// </summary>
+ /// <param name="request">Request to match</param>
+ /// <returns></returns>
+ public async Task<T> WaitForMatchAsync(MatchRequest<T> request)
{
- this._client.Logger.LogError(InteractivityEvents.InteractivityWaitError, ex, "An exception occurred while collecting from {0}", typeof(T).Name);
+ T result = null;
+ this._matchRequests.Add(request);
+ try
+ {
+ result = await request.Tcs.Task.ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ this._client.Logger.LogError(InteractivityEvents.InteractivityWaitError, ex, "An exception occurred while waiting for {0}", typeof(T).Name);
+ }
+ finally
+ {
+ request.Dispose();
+ this._matchRequests.TryRemove(request);
+ }
+ return result;
}
- finally
+
+ /// <summary>
+ /// Collects the matches async.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public async Task<ReadOnlyCollection<T>> CollectMatchesAsync(CollectRequest<T> request)
{
- result = new ReadOnlyCollection<T>(new HashSet<T>(request.Collected).ToList());
- request.Dispose();
- this._collectRequests.TryRemove(request);
+ ReadOnlyCollection<T> result = null;
+ this._collectRequests.Add(request);
+ try
+ {
+ await request.Tcs.Task.ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ this._client.Logger.LogError(InteractivityEvents.InteractivityWaitError, ex, "An exception occurred while collecting from {0}", typeof(T).Name);
+ }
+ finally
+ {
+ result = new ReadOnlyCollection<T>(new HashSet<T>(request.Collected).ToList());
+ request.Dispose();
+ this._collectRequests.TryRemove(request);
+ }
+ return result;
}
- return result;
- }
- /// <summary>
- /// Handles the event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The event's arguments.</param>
- private Task HandleEvent(DiscordClient client, T eventArgs)
- {
- if (!this._disposed)
+ /// <summary>
+ /// Handles the event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The event's arguments.</param>
+ private Task HandleEvent(DiscordClient client, T eventArgs)
{
- foreach (var req in this._matchRequests)
+ if (!this._disposed)
{
- if (req.Predicate(eventArgs))
+ foreach (var req in this._matchRequests)
{
- req.Tcs.TrySetResult(eventArgs);
+ if (req.Predicate(eventArgs))
+ {
+ req.Tcs.TrySetResult(eventArgs);
+ }
}
- }
- foreach (var req in this._collectRequests)
- {
- if (req.Predicate(eventArgs))
+ foreach (var req in this._collectRequests)
{
- req.Collected.Add(eventArgs);
+ if (req.Predicate(eventArgs))
+ {
+ req.Collected.Add(eventArgs);
+ }
}
}
- }
- return Task.CompletedTask;
- }
+ return Task.CompletedTask;
+ }
- ~EventWaiter()
- {
- this.Dispose();
- }
+ ~EventWaiter()
+ {
+ this.Dispose();
+ }
- /// <summary>
- /// Disposes this EventWaiter
- /// </summary>
- public void Dispose()
- {
- this._disposed = true;
- if (this._event != null)
- this._event.Unregister(this._handler);
+ /// <summary>
+ /// Disposes this EventWaiter
+ /// </summary>
+ public void Dispose()
+ {
+ this._disposed = true;
+ if (this._event != null)
+ this._event.Unregister(this._handler);
- this._event = null;
- this._handler = null;
- this._client = null;
+ this._event = null;
+ this._handler = null;
+ this._client = null;
- if (this._matchRequests != null)
- this._matchRequests.Clear();
- if (this._collectRequests != null)
- this._collectRequests.Clear();
+ if (this._matchRequests != null)
+ this._matchRequests.Clear();
+ if (this._collectRequests != null)
+ this._collectRequests.Clear();
- this._matchRequests = null;
- this._collectRequests = null;
+ this._matchRequests = null;
+ this._collectRequests = null;
+ }
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/IPaginator.cs b/DisCatSharp.Interactivity/EventHandling/IPaginator.cs
index f9f0902b1..c763931fa 100644
--- a/DisCatSharp.Interactivity/EventHandling/IPaginator.cs
+++ b/DisCatSharp.Interactivity/EventHandling/IPaginator.cs
@@ -1,43 +1,45 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// The paginator.
-/// </summary>
-internal interface IPaginator
+namespace DisCatSharp.Interactivity.EventHandling
{
- /// <summary>
- /// Paginates.
- /// </summary>
- /// <param name="request">The request to paginate.</param>
- /// <returns>A task that completes when the pagination finishes or times out.</returns>
- Task DoPaginationAsync(IPaginationRequest request);
/// <summary>
- /// Disposes this EventWaiter
+ /// The paginator.
/// </summary>
- void Dispose();
+ internal interface IPaginator
+ {
+ /// <summary>
+ /// Paginates.
+ /// </summary>
+ /// <param name="request">The request to paginate.</param>
+ /// <returns>A task that completes when the pagination finishes or times out.</returns>
+ Task DoPaginationAsync(IPaginationRequest request);
+
+ /// <summary>
+ /// Disposes this EventWaiter
+ /// </summary>
+ void Dispose();
+ }
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Paginator.cs b/DisCatSharp.Interactivity/EventHandling/Paginator.cs
index 26b775bbf..8711e1df4 100644
--- a/DisCatSharp.Interactivity/EventHandling/Paginator.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Paginator.cs
@@ -1,313 +1,314 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using ConcurrentCollections;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Interactivity.Enums;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// The paginator.
-/// </summary>
-internal class Paginator : IPaginator
+namespace DisCatSharp.Interactivity.EventHandling
{
- DiscordClient _client;
- ConcurrentHashSet<IPaginationRequest> _requests;
-
/// <summary>
- /// Creates a new EventWaiter object.
+ /// The paginator.
/// </summary>
- /// <param name="client">Discord client</param>
- public Paginator(DiscordClient client)
+ internal class Paginator : IPaginator
{
- this._client = client;
- this._requests = new ConcurrentHashSet<IPaginationRequest>();
-
- this._client.MessageReactionAdded += this.HandleReactionAdd;
- this._client.MessageReactionRemoved += this.HandleReactionRemove;
- this._client.MessageReactionsCleared += this.HandleReactionClear;
- }
+ DiscordClient _client;
+ ConcurrentHashSet<IPaginationRequest> _requests;
- /// <summary>
- /// Dos the pagination async.
- /// </summary>
- /// <param name="request">The request.</param>
- public async Task DoPaginationAsync(IPaginationRequest request)
- {
- await this.ResetReactionsAsync(request).ConfigureAwait(false);
- this._requests.Add(request);
- try
- {
- var tcs = await request.GetTaskCompletionSourceAsync().ConfigureAwait(false);
- await tcs.Task.ConfigureAwait(false);
- }
- catch (Exception ex)
+ /// <summary>
+ /// Creates a new EventWaiter object.
+ /// </summary>
+ /// <param name="client">Discord client</param>
+ public Paginator(DiscordClient client)
{
- this._client.Logger.LogError(InteractivityEvents.InteractivityPaginationError, ex, "Exception occurred while paginating");
+ this._client = client;
+ this._requests = new ConcurrentHashSet<IPaginationRequest>();
+
+ this._client.MessageReactionAdded += this.HandleReactionAdd;
+ this._client.MessageReactionRemoved += this.HandleReactionRemove;
+ this._client.MessageReactionsCleared += this.HandleReactionClear;
}
- finally
+
+ /// <summary>
+ /// Dos the pagination async.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ public async Task DoPaginationAsync(IPaginationRequest request)
{
- this._requests.TryRemove(request);
+ await this.ResetReactionsAsync(request).ConfigureAwait(false);
+ this._requests.Add(request);
try
{
- await request.DoCleanupAsync().ConfigureAwait(false);
+ var tcs = await request.GetTaskCompletionSourceAsync().ConfigureAwait(false);
+ await tcs.Task.ConfigureAwait(false);
}
catch (Exception ex)
{
this._client.Logger.LogError(InteractivityEvents.InteractivityPaginationError, ex, "Exception occurred while paginating");
}
+ finally
+ {
+ this._requests.TryRemove(request);
+ try
+ {
+ await request.DoCleanupAsync().ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ this._client.Logger.LogError(InteractivityEvents.InteractivityPaginationError, ex, "Exception occurred while paginating");
+ }
+ }
}
- }
-
- /// <summary>
- /// Handles the reaction add.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The event's arguments.</param>
- private Task HandleReactionAdd(DiscordClient client, MessageReactionAddEventArgs eventArgs)
- {
- if (this._requests.Count == 0)
- return Task.CompletedTask;
- _ = Task.Run(async () =>
+ /// <summary>
+ /// Handles the reaction add.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The event's arguments.</param>
+ private Task HandleReactionAdd(DiscordClient client, MessageReactionAddEventArgs eventArgs)
{
- foreach (var req in this._requests)
- {
- var emojis = await req.GetEmojisAsync().ConfigureAwait(false);
- var msg = await req.GetMessageAsync().ConfigureAwait(false);
- var usr = await req.GetUserAsync().ConfigureAwait(false);
+ if (this._requests.Count == 0)
+ return Task.CompletedTask;
- if (msg.Id == eventArgs.Message.Id)
+ _ = Task.Run(async () =>
+ {
+ foreach (var req in this._requests)
{
- if (eventArgs.User.Id == usr.Id)
+ var emojis = await req.GetEmojisAsync().ConfigureAwait(false);
+ var msg = await req.GetMessageAsync().ConfigureAwait(false);
+ var usr = await req.GetUserAsync().ConfigureAwait(false);
+
+ if (msg.Id == eventArgs.Message.Id)
{
- if (req.PageCount > 1 &&
- (eventArgs.Emoji == emojis.Left ||
- eventArgs.Emoji == emojis.SkipLeft ||
- eventArgs.Emoji == emojis.Right ||
- eventArgs.Emoji == emojis.SkipRight ||
- eventArgs.Emoji == emojis.Stop))
- {
- await this.PaginateAsync(req, eventArgs.Emoji).ConfigureAwait(false);
- }
- else if (eventArgs.Emoji == emojis.Stop &&
- req is PaginationRequest paginationRequest &&
- paginationRequest.PaginationDeletion == PaginationDeletion.DeleteMessage)
- {
- await this.PaginateAsync(req, eventArgs.Emoji).ConfigureAwait(false);
- }
- else
+ if (eventArgs.User.Id == usr.Id)
{
- await msg.DeleteReactionAsync(eventArgs.Emoji, eventArgs.User).ConfigureAwait(false);
+ if (req.PageCount > 1 &&
+ (eventArgs.Emoji == emojis.Left ||
+ eventArgs.Emoji == emojis.SkipLeft ||
+ eventArgs.Emoji == emojis.Right ||
+ eventArgs.Emoji == emojis.SkipRight ||
+ eventArgs.Emoji == emojis.Stop))
+ {
+ await this.PaginateAsync(req, eventArgs.Emoji).ConfigureAwait(false);
+ }
+ else if (eventArgs.Emoji == emojis.Stop &&
+ req is PaginationRequest paginationRequest &&
+ paginationRequest.PaginationDeletion == PaginationDeletion.DeleteMessage)
+ {
+ await this.PaginateAsync(req, eventArgs.Emoji).ConfigureAwait(false);
+ }
+ else
+ {
+ await msg.DeleteReactionAsync(eventArgs.Emoji, eventArgs.User).ConfigureAwait(false);
+ }
}
- }
- else if (eventArgs.User.Id != this._client.CurrentUser.Id)
- {
- if (eventArgs.Emoji != emojis.Left &&
- eventArgs.Emoji != emojis.SkipLeft &&
- eventArgs.Emoji != emojis.Right &&
- eventArgs.Emoji != emojis.SkipRight &&
- eventArgs.Emoji != emojis.Stop)
+ else if (eventArgs.User.Id != this._client.CurrentUser.Id)
{
- await msg.DeleteReactionAsync(eventArgs.Emoji, eventArgs.User).ConfigureAwait(false);
+ if (eventArgs.Emoji != emojis.Left &&
+ eventArgs.Emoji != emojis.SkipLeft &&
+ eventArgs.Emoji != emojis.Right &&
+ eventArgs.Emoji != emojis.SkipRight &&
+ eventArgs.Emoji != emojis.Stop)
+ {
+ await msg.DeleteReactionAsync(eventArgs.Emoji, eventArgs.User).ConfigureAwait(false);
+ }
}
}
}
- }
- });
- return Task.CompletedTask;
- }
-
- /// <summary>
- /// Handles the reaction remove.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The event's arguments.</param>
- private Task HandleReactionRemove(DiscordClient client, MessageReactionRemoveEventArgs eventArgs)
- {
- if (this._requests.Count == 0)
+ });
return Task.CompletedTask;
+ }
- _ = Task.Run(async () =>
+ /// <summary>
+ /// Handles the reaction remove.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The event's arguments.</param>
+ private Task HandleReactionRemove(DiscordClient client, MessageReactionRemoveEventArgs eventArgs)
{
- foreach (var req in this._requests)
- {
- var emojis = await req.GetEmojisAsync().ConfigureAwait(false);
- var msg = await req.GetMessageAsync().ConfigureAwait(false);
- var usr = await req.GetUserAsync().ConfigureAwait(false);
+ if (this._requests.Count == 0)
+ return Task.CompletedTask;
- if (msg.Id == eventArgs.Message.Id)
+ _ = Task.Run(async () =>
+ {
+ foreach (var req in this._requests)
{
- if (eventArgs.User.Id == usr.Id)
+ var emojis = await req.GetEmojisAsync().ConfigureAwait(false);
+ var msg = await req.GetMessageAsync().ConfigureAwait(false);
+ var usr = await req.GetUserAsync().ConfigureAwait(false);
+
+ if (msg.Id == eventArgs.Message.Id)
{
- if (req.PageCount > 1 &&
- (eventArgs.Emoji == emojis.Left ||
- eventArgs.Emoji == emojis.SkipLeft ||
- eventArgs.Emoji == emojis.Right ||
- eventArgs.Emoji == emojis.SkipRight ||
- eventArgs.Emoji == emojis.Stop))
- {
- await this.PaginateAsync(req, eventArgs.Emoji).ConfigureAwait(false);
- }
- else if (eventArgs.Emoji == emojis.Stop &&
- req is PaginationRequest paginationRequest &&
- paginationRequest.PaginationDeletion == PaginationDeletion.DeleteMessage)
+ if (eventArgs.User.Id == usr.Id)
{
- await this.PaginateAsync(req, eventArgs.Emoji).ConfigureAwait(false);
+ if (req.PageCount > 1 &&
+ (eventArgs.Emoji == emojis.Left ||
+ eventArgs.Emoji == emojis.SkipLeft ||
+ eventArgs.Emoji == emojis.Right ||
+ eventArgs.Emoji == emojis.SkipRight ||
+ eventArgs.Emoji == emojis.Stop))
+ {
+ await this.PaginateAsync(req, eventArgs.Emoji).ConfigureAwait(false);
+ }
+ else if (eventArgs.Emoji == emojis.Stop &&
+ req is PaginationRequest paginationRequest &&
+ paginationRequest.PaginationDeletion == PaginationDeletion.DeleteMessage)
+ {
+ await this.PaginateAsync(req, eventArgs.Emoji).ConfigureAwait(false);
+ }
}
}
}
- }
- });
+ });
- return Task.CompletedTask;
- }
-
- /// <summary>
- /// Handles the reaction clear.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The eventArgs.</param>
- private Task HandleReactionClear(DiscordClient client, MessageReactionsClearEventArgs eventArgs)
- {
- if (this._requests.Count == 0)
return Task.CompletedTask;
+ }
- _ = Task.Run(async () =>
+ /// <summary>
+ /// Handles the reaction clear.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The eventArgs.</param>
+ private Task HandleReactionClear(DiscordClient client, MessageReactionsClearEventArgs eventArgs)
{
- foreach (var req in this._requests)
- {
- var msg = await req.GetMessageAsync().ConfigureAwait(false);
+ if (this._requests.Count == 0)
+ return Task.CompletedTask;
- if (msg.Id == eventArgs.Message.Id)
+ _ = Task.Run(async () =>
+ {
+ foreach (var req in this._requests)
{
- await this.ResetReactionsAsync(req).ConfigureAwait(false);
+ var msg = await req.GetMessageAsync().ConfigureAwait(false);
+
+ if (msg.Id == eventArgs.Message.Id)
+ {
+ await this.ResetReactionsAsync(req).ConfigureAwait(false);
+ }
}
- }
- });
+ });
- return Task.CompletedTask;
- }
+ return Task.CompletedTask;
+ }
- /// <summary>
- /// Resets the reactions async.
- /// </summary>
- /// <param name="p">The p.</param>
- private async Task ResetReactionsAsync(IPaginationRequest p)
- {
- var msg = await p.GetMessageAsync().ConfigureAwait(false);
- var emojis = await p.GetEmojisAsync().ConfigureAwait(false);
+ /// <summary>
+ /// Resets the reactions async.
+ /// </summary>
+ /// <param name="p">The p.</param>
+ private async Task ResetReactionsAsync(IPaginationRequest p)
+ {
+ var msg = await p.GetMessageAsync().ConfigureAwait(false);
+ var emojis = await p.GetEmojisAsync().ConfigureAwait(false);
- // Test permissions to avoid a 403:
- // https://totally-not.a-sketchy.site/3pXpRLK.png
- // Yes, this is an issue
- // No, we should not require people to guarantee MANAGE_MESSAGES
- // Need to check following:
- // - In guild?
- // - If yes, check if have permission
- // - If all above fail (DM || guild && no permission), skip this
- var chn = msg.Channel;
- var gld = chn?.Guild;
- var mbr = gld?.CurrentMember;
+ // Test permissions to avoid a 403:
+ // https://totally-not.a-sketchy.site/3pXpRLK.png
+ // Yes, this is an issue
+ // No, we should not require people to guarantee MANAGE_MESSAGES
+ // Need to check following:
+ // - In guild?
+ // - If yes, check if have permission
+ // - If all above fail (DM || guild && no permission), skip this
+ var chn = msg.Channel;
+ var gld = chn?.Guild;
+ var mbr = gld?.CurrentMember;
- if (mbr != null /* == is guild and cache is valid */ && (chn.PermissionsFor(mbr) & Permissions.ManageChannels) != 0) /* == has permissions */
- await msg.DeleteAllReactionsAsync("Pagination").ConfigureAwait(false);
- // ENDOF: 403 fix
+ if (mbr != null /* == is guild and cache is valid */ && (chn.PermissionsFor(mbr) & Permissions.ManageChannels) != 0) /* == has permissions */
+ await msg.DeleteAllReactionsAsync("Pagination").ConfigureAwait(false);
+ // ENDOF: 403 fix
- if (p.PageCount > 1)
- {
- if (emojis.SkipLeft != null)
- await msg.CreateReactionAsync(emojis.SkipLeft).ConfigureAwait(false);
- if (emojis.Left != null)
- await msg.CreateReactionAsync(emojis.Left).ConfigureAwait(false);
- if (emojis.Right != null)
- await msg.CreateReactionAsync(emojis.Right).ConfigureAwait(false);
- if (emojis.SkipRight != null)
- await msg.CreateReactionAsync(emojis.SkipRight).ConfigureAwait(false);
- if (emojis.Stop != null)
+ if (p.PageCount > 1)
+ {
+ if (emojis.SkipLeft != null)
+ await msg.CreateReactionAsync(emojis.SkipLeft).ConfigureAwait(false);
+ if (emojis.Left != null)
+ await msg.CreateReactionAsync(emojis.Left).ConfigureAwait(false);
+ if (emojis.Right != null)
+ await msg.CreateReactionAsync(emojis.Right).ConfigureAwait(false);
+ if (emojis.SkipRight != null)
+ await msg.CreateReactionAsync(emojis.SkipRight).ConfigureAwait(false);
+ if (emojis.Stop != null)
+ await msg.CreateReactionAsync(emojis.Stop).ConfigureAwait(false);
+ }
+ else if (emojis.Stop != null && p is PaginationRequest paginationRequest && paginationRequest.PaginationDeletion == PaginationDeletion.DeleteMessage)
+ {
await msg.CreateReactionAsync(emojis.Stop).ConfigureAwait(false);
+ }
}
- else if (emojis.Stop != null && p is PaginationRequest paginationRequest && paginationRequest.PaginationDeletion == PaginationDeletion.DeleteMessage)
- {
- await msg.CreateReactionAsync(emojis.Stop).ConfigureAwait(false);
- }
- }
- /// <summary>
- /// Paginates the async.
- /// </summary>
- /// <param name="p">The p.</param>
- /// <param name="emoji">The emoji.</param>
- private async Task PaginateAsync(IPaginationRequest p, DiscordEmoji emoji)
- {
- var emojis = await p.GetEmojisAsync().ConfigureAwait(false);
- var msg = await p.GetMessageAsync().ConfigureAwait(false);
-
- if (emoji == emojis.SkipLeft)
- await p.SkipLeftAsync().ConfigureAwait(false);
- else if (emoji == emojis.Left)
- await p.PreviousPageAsync().ConfigureAwait(false);
- else if (emoji == emojis.Right)
- await p.NextPageAsync().ConfigureAwait(false);
- else if (emoji == emojis.SkipRight)
- await p.SkipRightAsync().ConfigureAwait(false);
- else if (emoji == emojis.Stop)
+ /// <summary>
+ /// Paginates the async.
+ /// </summary>
+ /// <param name="p">The p.</param>
+ /// <param name="emoji">The emoji.</param>
+ private async Task PaginateAsync(IPaginationRequest p, DiscordEmoji emoji)
{
- var tcs = await p.GetTaskCompletionSourceAsync().ConfigureAwait(false);
- tcs.TrySetResult(true);
- return;
- }
+ var emojis = await p.GetEmojisAsync().ConfigureAwait(false);
+ var msg = await p.GetMessageAsync().ConfigureAwait(false);
- var page = await p.GetPageAsync().ConfigureAwait(false);
- var builder = new DiscordMessageBuilder()
- .WithContent(page.Content)
- .WithEmbed(page.Embed);
+ if (emoji == emojis.SkipLeft)
+ await p.SkipLeftAsync().ConfigureAwait(false);
+ else if (emoji == emojis.Left)
+ await p.PreviousPageAsync().ConfigureAwait(false);
+ else if (emoji == emojis.Right)
+ await p.NextPageAsync().ConfigureAwait(false);
+ else if (emoji == emojis.SkipRight)
+ await p.SkipRightAsync().ConfigureAwait(false);
+ else if (emoji == emojis.Stop)
+ {
+ var tcs = await p.GetTaskCompletionSourceAsync().ConfigureAwait(false);
+ tcs.TrySetResult(true);
+ return;
+ }
- await builder.ModifyAsync(msg).ConfigureAwait(false);
- }
+ var page = await p.GetPageAsync().ConfigureAwait(false);
+ var builder = new DiscordMessageBuilder()
+ .WithContent(page.Content)
+ .WithEmbed(page.Embed);
- ~Paginator()
- {
- this.Dispose();
- }
+ await builder.ModifyAsync(msg).ConfigureAwait(false);
+ }
- /// <summary>
- /// Disposes this EventWaiter
- /// </summary>
- public void Dispose()
- {
- this._client.MessageReactionAdded -= this.HandleReactionAdd;
- this._client.MessageReactionRemoved -= this.HandleReactionRemove;
- this._client.MessageReactionsCleared -= this.HandleReactionClear;
- this._client = null;
- this._requests.Clear();
- this._requests = null;
+ ~Paginator()
+ {
+ this.Dispose();
+ }
+
+ /// <summary>
+ /// Disposes this EventWaiter
+ /// </summary>
+ public void Dispose()
+ {
+ this._client.MessageReactionAdded -= this.HandleReactionAdd;
+ this._client.MessageReactionRemoved -= this.HandleReactionRemove;
+ this._client.MessageReactionsCleared -= this.HandleReactionClear;
+ this._client = null;
+ this._requests.Clear();
+ this._requests = null;
+ }
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Poller.cs b/DisCatSharp.Interactivity/EventHandling/Poller.cs
index 976508d4d..932fad295 100644
--- a/DisCatSharp.Interactivity/EventHandling/Poller.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Poller.cs
@@ -1,176 +1,177 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
using ConcurrentCollections;
using DisCatSharp.EventArgs;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// The poller.
-/// </summary>
-internal class Poller
+namespace DisCatSharp.Interactivity.EventHandling
{
- DiscordClient _client;
- ConcurrentHashSet<PollRequest> _requests;
-
/// <summary>
- /// Creates a new EventWaiter object.
+ /// The poller.
/// </summary>
- /// <param name="client">Your DiscordClient</param>
- public Poller(DiscordClient client)
+ internal class Poller
{
- this._client = client;
- this._requests = new ConcurrentHashSet<PollRequest>();
-
- this._client.MessageReactionAdded += this.HandleReactionAdd;
- this._client.MessageReactionRemoved += this.HandleReactionRemove;
- this._client.MessageReactionsCleared += this.HandleReactionClear;
- }
+ DiscordClient _client;
+ ConcurrentHashSet<PollRequest> _requests;
- /// <summary>
- /// Dos the poll async.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>A Task.</returns>
- public async Task<ReadOnlyCollection<PollEmoji>> DoPollAsync(PollRequest request)
- {
- ReadOnlyCollection<PollEmoji> result = null;
- this._requests.Add(request);
- try
+ /// <summary>
+ /// Creates a new EventWaiter object.
+ /// </summary>
+ /// <param name="client">Your DiscordClient</param>
+ public Poller(DiscordClient client)
{
- await request.Tcs.Task.ConfigureAwait(false);
+ this._client = client;
+ this._requests = new ConcurrentHashSet<PollRequest>();
+
+ this._client.MessageReactionAdded += this.HandleReactionAdd;
+ this._client.MessageReactionRemoved += this.HandleReactionRemove;
+ this._client.MessageReactionsCleared += this.HandleReactionClear;
}
- catch (Exception ex)
+
+ /// <summary>
+ /// Dos the poll async.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>A Task.</returns>
+ public async Task<ReadOnlyCollection<PollEmoji>> DoPollAsync(PollRequest request)
{
- this._client.Logger.LogError(InteractivityEvents.InteractivityPollError, ex, "Exception occurred while polling");
+ ReadOnlyCollection<PollEmoji> result = null;
+ this._requests.Add(request);
+ try
+ {
+ await request.Tcs.Task.ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ this._client.Logger.LogError(InteractivityEvents.InteractivityPollError, ex, "Exception occurred while polling");
+ }
+ finally
+ {
+ result = new ReadOnlyCollection<PollEmoji>(new HashSet<PollEmoji>(request.Collected).ToList());
+ request.Dispose();
+ this._requests.TryRemove(request);
+ }
+ return result;
}
- finally
+
+ /// <summary>
+ /// Handles the reaction add.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The event's arguments.</param>
+ /// <returns>A Task.</returns>
+ private Task HandleReactionAdd(DiscordClient client, MessageReactionAddEventArgs eventArgs)
{
- result = new ReadOnlyCollection<PollEmoji>(new HashSet<PollEmoji>(request.Collected).ToList());
- request.Dispose();
- this._requests.TryRemove(request);
- }
- return result;
- }
+ if (this._requests.Count == 0)
+ return Task.CompletedTask;
- /// <summary>
- /// Handles the reaction add.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The event's arguments.</param>
- /// <returns>A Task.</returns>
- private Task HandleReactionAdd(DiscordClient client, MessageReactionAddEventArgs eventArgs)
- {
- if (this._requests.Count == 0)
+ _ = Task.Run(async () =>
+ {
+ foreach (var req in this._requests)
+ {
+ // match message
+ if (req.Message.Id == eventArgs.Message.Id && req.Message.ChannelId == eventArgs.Channel.Id)
+ {
+ if (req.Emojis.Contains(eventArgs.Emoji) && !req.Collected.Any(x => x.Voted.Contains(eventArgs.User)))
+ {
+ if (eventArgs.User.Id != this._client.CurrentUser.Id)
+ req.AddReaction(eventArgs.Emoji, eventArgs.User);
+ }
+ else
+ {
+ var member = await eventArgs.Channel.Guild.GetMemberAsync(client.CurrentUser.Id).ConfigureAwait(false);
+ if (eventArgs.Channel.PermissionsFor(member).HasPermission(Permissions.ManageMessages))
+ await eventArgs.Message.DeleteReactionAsync(eventArgs.Emoji, eventArgs.User).ConfigureAwait(false);
+ }
+ }
+ }
+ });
return Task.CompletedTask;
+ }
- _ = Task.Run(async () =>
+ /// <summary>
+ /// Handles the reaction remove.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The event's arguments.</param>
+ /// <returns>A Task.</returns>
+ private Task HandleReactionRemove(DiscordClient client, MessageReactionRemoveEventArgs eventArgs)
{
foreach (var req in this._requests)
{
// match message
if (req.Message.Id == eventArgs.Message.Id && req.Message.ChannelId == eventArgs.Channel.Id)
{
- if (req.Emojis.Contains(eventArgs.Emoji) && !req.Collected.Any(x => x.Voted.Contains(eventArgs.User)))
- {
- if (eventArgs.User.Id != this._client.CurrentUser.Id)
- req.AddReaction(eventArgs.Emoji, eventArgs.User);
- }
- else
- {
- var member = await eventArgs.Channel.Guild.GetMemberAsync(client.CurrentUser.Id).ConfigureAwait(false);
- if (eventArgs.Channel.PermissionsFor(member).HasPermission(Permissions.ManageMessages))
- await eventArgs.Message.DeleteReactionAsync(eventArgs.Emoji, eventArgs.User).ConfigureAwait(false);
- }
+ if (eventArgs.User.Id != this._client.CurrentUser.Id)
+ req.RemoveReaction(eventArgs.Emoji, eventArgs.User);
}
}
- });
- return Task.CompletedTask;
- }
+ return Task.CompletedTask;
+ }
- /// <summary>
- /// Handles the reaction remove.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The event's arguments.</param>
- /// <returns>A Task.</returns>
- private Task HandleReactionRemove(DiscordClient client, MessageReactionRemoveEventArgs eventArgs)
- {
- foreach (var req in this._requests)
+ /// <summary>
+ /// Handles the reaction clear.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The event's arguments.</param>
+ /// <returns>A Task.</returns>
+ private Task HandleReactionClear(DiscordClient client, MessageReactionsClearEventArgs eventArgs)
{
- // match message
- if (req.Message.Id == eventArgs.Message.Id && req.Message.ChannelId == eventArgs.Channel.Id)
+ foreach (var req in this._requests)
{
- if (eventArgs.User.Id != this._client.CurrentUser.Id)
- req.RemoveReaction(eventArgs.Emoji, eventArgs.User);
+ // match message
+ if (req.Message.Id == eventArgs.Message.Id && req.Message.ChannelId == eventArgs.Channel.Id)
+ {
+ req.ClearCollected();
+ }
}
+ return Task.CompletedTask;
}
- return Task.CompletedTask;
- }
- /// <summary>
- /// Handles the reaction clear.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The event's arguments.</param>
- /// <returns>A Task.</returns>
- private Task HandleReactionClear(DiscordClient client, MessageReactionsClearEventArgs eventArgs)
- {
- foreach (var req in this._requests)
+ ~Poller()
{
- // match message
- if (req.Message.Id == eventArgs.Message.Id && req.Message.ChannelId == eventArgs.Channel.Id)
- {
- req.ClearCollected();
- }
+ this.Dispose();
}
- return Task.CompletedTask;
- }
-
- ~Poller()
- {
- this.Dispose();
- }
- /// <summary>
- /// Disposes this EventWaiter
- /// </summary>
- public void Dispose()
- {
- this._client.MessageReactionAdded -= this.HandleReactionAdd;
- this._client.MessageReactionRemoved -= this.HandleReactionRemove;
- this._client.MessageReactionsCleared -= this.HandleReactionClear;
- this._client = null;
- this._requests.Clear();
- this._requests = null;
+ /// <summary>
+ /// Disposes this EventWaiter
+ /// </summary>
+ public void Dispose()
+ {
+ this._client.MessageReactionAdded -= this.HandleReactionAdd;
+ this._client.MessageReactionRemoved -= this.HandleReactionRemove;
+ this._client.MessageReactionsCleared -= this.HandleReactionClear;
+ this._client = null;
+ this._requests.Clear();
+ this._requests = null;
+ }
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/ReactionCollector.cs b/DisCatSharp.Interactivity/EventHandling/ReactionCollector.cs
index 0fbc0cc5b..1cbe9cbf5 100644
--- a/DisCatSharp.Interactivity/EventHandling/ReactionCollector.cs
+++ b/DisCatSharp.Interactivity/EventHandling/ReactionCollector.cs
@@ -1,286 +1,287 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using ConcurrentCollections;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// EventWaiter is a class that serves as a layer between the InteractivityExtension
-/// and the DiscordClient to listen to an event and check for matches to a predicate.
-/// </summary>
-internal class ReactionCollector : IDisposable
+namespace DisCatSharp.Interactivity.EventHandling
{
- DiscordClient _client;
-
- AsyncEvent<DiscordClient, MessageReactionAddEventArgs> _reactionAddEvent;
- AsyncEventHandler<DiscordClient, MessageReactionAddEventArgs> _reactionAddHandler;
-
- AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs> _reactionRemoveEvent;
- AsyncEventHandler<DiscordClient, MessageReactionRemoveEventArgs> _reactionRemoveHandler;
-
- AsyncEvent<DiscordClient, MessageReactionsClearEventArgs> _reactionClearEvent;
- AsyncEventHandler<DiscordClient, MessageReactionsClearEventArgs> _reactionClearHandler;
-
- ConcurrentHashSet<ReactionCollectRequest> _requests;
-
/// <summary>
- /// Creates a new EventWaiter object.
+ /// EventWaiter is a class that serves as a layer between the InteractivityExtension
+ /// and the DiscordClient to listen to an event and check for matches to a predicate.
/// </summary>
- /// <param name="client">Your DiscordClient</param>
- public ReactionCollector(DiscordClient client)
+ internal class ReactionCollector : IDisposable
{
- this._client = client;
- var tinfo = this._client.GetType().GetTypeInfo();
+ DiscordClient _client;
- this._requests = new ConcurrentHashSet<ReactionCollectRequest>();
+ AsyncEvent<DiscordClient, MessageReactionAddEventArgs> _reactionAddEvent;
+ AsyncEventHandler<DiscordClient, MessageReactionAddEventArgs> _reactionAddHandler;
- // Grabbing all three events from client
- var handler = tinfo.DeclaredFields.First(x => x.FieldType == typeof(AsyncEvent<DiscordClient, MessageReactionAddEventArgs>));
+ AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs> _reactionRemoveEvent;
+ AsyncEventHandler<DiscordClient, MessageReactionRemoveEventArgs> _reactionRemoveHandler;
- this._reactionAddEvent = (AsyncEvent<DiscordClient, MessageReactionAddEventArgs>)handler.GetValue(this._client);
- this._reactionAddHandler = new AsyncEventHandler<DiscordClient, MessageReactionAddEventArgs>(this.HandleReactionAdd);
- this._reactionAddEvent.Register(this._reactionAddHandler);
+ AsyncEvent<DiscordClient, MessageReactionsClearEventArgs> _reactionClearEvent;
+ AsyncEventHandler<DiscordClient, MessageReactionsClearEventArgs> _reactionClearHandler;
- handler = tinfo.DeclaredFields.First(x => x.FieldType == typeof(AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs>));
+ ConcurrentHashSet<ReactionCollectRequest> _requests;
- this._reactionRemoveEvent = (AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs>)handler.GetValue(this._client);
- this._reactionRemoveHandler = new AsyncEventHandler<DiscordClient, MessageReactionRemoveEventArgs>(this.HandleReactionRemove);
- this._reactionRemoveEvent.Register(this._reactionRemoveHandler);
+ /// <summary>
+ /// Creates a new EventWaiter object.
+ /// </summary>
+ /// <param name="client">Your DiscordClient</param>
+ public ReactionCollector(DiscordClient client)
+ {
+ this._client = client;
+ var tinfo = this._client.GetType().GetTypeInfo();
- handler = tinfo.DeclaredFields.First(x => x.FieldType == typeof(AsyncEvent<DiscordClient, MessageReactionsClearEventArgs>));
+ this._requests = new ConcurrentHashSet<ReactionCollectRequest>();
- this._reactionClearEvent = (AsyncEvent<DiscordClient, MessageReactionsClearEventArgs>)handler.GetValue(this._client);
- this._reactionClearHandler = new AsyncEventHandler<DiscordClient, MessageReactionsClearEventArgs>(this.HandleReactionClear);
- this._reactionClearEvent.Register(this._reactionClearHandler);
- }
+ // Grabbing all three events from client
+ var handler = tinfo.DeclaredFields.First(x => x.FieldType == typeof(AsyncEvent<DiscordClient, MessageReactionAddEventArgs>));
- /// <summary>
- /// Collects the async.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>A Task.</returns>
- public async Task<ReadOnlyCollection<Reaction>> CollectAsync(ReactionCollectRequest request)
- {
- this._requests.Add(request);
- var result = (ReadOnlyCollection<Reaction>)null;
+ this._reactionAddEvent = (AsyncEvent<DiscordClient, MessageReactionAddEventArgs>)handler.GetValue(this._client);
+ this._reactionAddHandler = new AsyncEventHandler<DiscordClient, MessageReactionAddEventArgs>(this.HandleReactionAdd);
+ this._reactionAddEvent.Register(this._reactionAddHandler);
- try
- {
- await request.Tcs.Task.ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- this._client.Logger.LogError(InteractivityEvents.InteractivityCollectorError, ex, "Exception occurred while collecting reactions");
+ handler = tinfo.DeclaredFields.First(x => x.FieldType == typeof(AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs>));
+
+ this._reactionRemoveEvent = (AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs>)handler.GetValue(this._client);
+ this._reactionRemoveHandler = new AsyncEventHandler<DiscordClient, MessageReactionRemoveEventArgs>(this.HandleReactionRemove);
+ this._reactionRemoveEvent.Register(this._reactionRemoveHandler);
+
+ handler = tinfo.DeclaredFields.First(x => x.FieldType == typeof(AsyncEvent<DiscordClient, MessageReactionsClearEventArgs>));
+
+ this._reactionClearEvent = (AsyncEvent<DiscordClient, MessageReactionsClearEventArgs>)handler.GetValue(this._client);
+ this._reactionClearHandler = new AsyncEventHandler<DiscordClient, MessageReactionsClearEventArgs>(this.HandleReactionClear);
+ this._reactionClearEvent.Register(this._reactionClearHandler);
}
- finally
+
+ /// <summary>
+ /// Collects the async.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>A Task.</returns>
+ public async Task<ReadOnlyCollection<Reaction>> CollectAsync(ReactionCollectRequest request)
{
- result = new ReadOnlyCollection<Reaction>(new HashSet<Reaction>(request.Collected).ToList());
- request.Dispose();
- this._requests.TryRemove(request);
+ this._requests.Add(request);
+ var result = (ReadOnlyCollection<Reaction>)null;
+
+ try
+ {
+ await request.Tcs.Task.ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ this._client.Logger.LogError(InteractivityEvents.InteractivityCollectorError, ex, "Exception occurred while collecting reactions");
+ }
+ finally
+ {
+ result = new ReadOnlyCollection<Reaction>(new HashSet<Reaction>(request.Collected).ToList());
+ request.Dispose();
+ this._requests.TryRemove(request);
+ }
+ return result;
}
- return result;
- }
- /// <summary>
- /// Handles the reaction add.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The event's arguments.</param>
- /// <returns>A Task.</returns>
- private Task HandleReactionAdd(DiscordClient client, MessageReactionAddEventArgs eventArgs)
- {
- // foreach request add
- foreach (var req in this._requests)
+ /// <summary>
+ /// Handles the reaction add.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The event's arguments.</param>
+ /// <returns>A Task.</returns>
+ private Task HandleReactionAdd(DiscordClient client, MessageReactionAddEventArgs eventArgs)
{
- if (req.Message.Id == eventArgs.Message.Id)
+ // foreach request add
+ foreach (var req in this._requests)
{
- if (req.Collected.Any(x => x.Emoji == eventArgs.Emoji && x.Users.Any(y => y.Id == eventArgs.User.Id)))
+ if (req.Message.Id == eventArgs.Message.Id)
{
- var reaction = req.Collected.First(x => x.Emoji == eventArgs.Emoji && x.Users.Any(y => y.Id == eventArgs.User.Id));
- req.Collected.TryRemove(reaction);
- reaction.Users.Add(eventArgs.User);
- req.Collected.Add(reaction);
- }
- else
- {
- req.Collected.Add(new Reaction()
+ if (req.Collected.Any(x => x.Emoji == eventArgs.Emoji && x.Users.Any(y => y.Id == eventArgs.User.Id)))
{
- Emoji = eventArgs.Emoji,
- Users = new ConcurrentHashSet<DiscordUser>() { eventArgs.User }
- });
+ var reaction = req.Collected.First(x => x.Emoji == eventArgs.Emoji && x.Users.Any(y => y.Id == eventArgs.User.Id));
+ req.Collected.TryRemove(reaction);
+ reaction.Users.Add(eventArgs.User);
+ req.Collected.Add(reaction);
+ }
+ else
+ {
+ req.Collected.Add(new Reaction()
+ {
+ Emoji = eventArgs.Emoji,
+ Users = new ConcurrentHashSet<DiscordUser>() { eventArgs.User }
+ });
+ }
}
}
+ return Task.CompletedTask;
}
- return Task.CompletedTask;
- }
- /// <summary>
- /// Handles the reaction remove.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The event's arguments.</param>
- /// <returns>A Task.</returns>
- private Task HandleReactionRemove(DiscordClient client, MessageReactionRemoveEventArgs eventArgs)
- {
- // foreach request remove
- foreach (var req in this._requests)
+ /// <summary>
+ /// Handles the reaction remove.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The event's arguments.</param>
+ /// <returns>A Task.</returns>
+ private Task HandleReactionRemove(DiscordClient client, MessageReactionRemoveEventArgs eventArgs)
{
- if (req.Message.Id == eventArgs.Message.Id)
+ // foreach request remove
+ foreach (var req in this._requests)
{
- if (req.Collected.Any(x => x.Emoji == eventArgs.Emoji && x.Users.Any(y => y.Id == eventArgs.User.Id)))
+ if (req.Message.Id == eventArgs.Message.Id)
{
- var reaction = req.Collected.First(x => x.Emoji == eventArgs.Emoji && x.Users.Any(y => y.Id == eventArgs.User.Id));
- req.Collected.TryRemove(reaction);
- reaction.Users.TryRemove(eventArgs.User);
- if (reaction.Users.Count > 0)
- req.Collected.Add(reaction);
+ if (req.Collected.Any(x => x.Emoji == eventArgs.Emoji && x.Users.Any(y => y.Id == eventArgs.User.Id)))
+ {
+ var reaction = req.Collected.First(x => x.Emoji == eventArgs.Emoji && x.Users.Any(y => y.Id == eventArgs.User.Id));
+ req.Collected.TryRemove(reaction);
+ reaction.Users.TryRemove(eventArgs.User);
+ if (reaction.Users.Count > 0)
+ req.Collected.Add(reaction);
+ }
}
}
+ return Task.CompletedTask;
}
- return Task.CompletedTask;
- }
- /// <summary>
- /// Handles the reaction clear.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="eventArgs">The event's arguments.</param>
- /// <returns>A Task.</returns>
- private Task HandleReactionClear(DiscordClient client, MessageReactionsClearEventArgs eventArgs)
- {
- // foreach request add
- foreach (var req in this._requests)
+ /// <summary>
+ /// Handles the reaction clear.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="eventArgs">The event's arguments.</param>
+ /// <returns>A Task.</returns>
+ private Task HandleReactionClear(DiscordClient client, MessageReactionsClearEventArgs eventArgs)
{
- if (req.Message.Id == eventArgs.Message.Id)
+ // foreach request add
+ foreach (var req in this._requests)
{
- req.Collected.Clear();
+ if (req.Message.Id == eventArgs.Message.Id)
+ {
+ req.Collected.Clear();
+ }
}
+ return Task.CompletedTask;
}
- return Task.CompletedTask;
- }
- ~ReactionCollector()
- {
- this.Dispose();
- }
+ ~ReactionCollector()
+ {
+ this.Dispose();
+ }
- /// <summary>
- /// Disposes this EventWaiter
- /// </summary>
- public void Dispose()
- {
- this._client = null;
+ /// <summary>
+ /// Disposes this EventWaiter
+ /// </summary>
+ public void Dispose()
+ {
+ this._client = null;
- this._reactionAddEvent.Unregister(this._reactionAddHandler);
- this._reactionRemoveEvent.Unregister(this._reactionRemoveHandler);
- this._reactionClearEvent.Unregister(this._reactionClearHandler);
+ this._reactionAddEvent.Unregister(this._reactionAddHandler);
+ this._reactionRemoveEvent.Unregister(this._reactionRemoveHandler);
+ this._reactionClearEvent.Unregister(this._reactionClearHandler);
- this._reactionAddEvent = null;
- this._reactionAddHandler = null;
- this._reactionRemoveEvent = null;
- this._reactionRemoveHandler = null;
- this._reactionClearEvent = null;
- this._reactionClearHandler = null;
+ this._reactionAddEvent = null;
+ this._reactionAddHandler = null;
+ this._reactionRemoveEvent = null;
+ this._reactionRemoveHandler = null;
+ this._reactionClearEvent = null;
+ this._reactionClearHandler = null;
- this._requests.Clear();
- this._requests = null;
+ this._requests.Clear();
+ this._requests = null;
+ }
}
-}
-
-/// <summary>
-/// The reaction collect request.
-/// </summary>
-public class ReactionCollectRequest : IDisposable
-{
- internal TaskCompletionSource<Reaction> Tcs;
- internal CancellationTokenSource Ct;
- internal TimeSpan Timeout;
- internal DiscordMessage Message;
- internal ConcurrentHashSet<Reaction> Collected;
/// <summary>
- /// Initializes a new instance of the <see cref="ReactionCollectRequest"/> class.
+ /// The reaction collect request.
/// </summary>
- /// <param name="msg">The msg.</param>
- /// <param name="timeout">The timeout.</param>
- public ReactionCollectRequest(DiscordMessage msg, TimeSpan timeout)
+ public class ReactionCollectRequest : IDisposable
{
- this.Message = msg;
- this.Collected = new ConcurrentHashSet<Reaction>();
- this.Timeout = timeout;
- this.Tcs = new TaskCompletionSource<Reaction>();
- this.Ct = new CancellationTokenSource(this.Timeout);
- this.Ct.Token.Register(() => this.Tcs.TrySetResult(null));
- }
+ internal TaskCompletionSource<Reaction> Tcs;
+ internal CancellationTokenSource Ct;
+ internal TimeSpan Timeout;
+ internal DiscordMessage Message;
+ internal ConcurrentHashSet<Reaction> Collected;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ReactionCollectRequest"/> class.
+ /// </summary>
+ /// <param name="msg">The msg.</param>
+ /// <param name="timeout">The timeout.</param>
+ public ReactionCollectRequest(DiscordMessage msg, TimeSpan timeout)
+ {
+ this.Message = msg;
+ this.Collected = new ConcurrentHashSet<Reaction>();
+ this.Timeout = timeout;
+ this.Tcs = new TaskCompletionSource<Reaction>();
+ this.Ct = new CancellationTokenSource(this.Timeout);
+ this.Ct.Token.Register(() => this.Tcs.TrySetResult(null));
+ }
- ~ReactionCollectRequest()
- {
- this.Dispose();
+ ~ReactionCollectRequest()
+ {
+ this.Dispose();
+ }
+
+ /// <summary>
+ /// Disposes the.
+ /// </summary>
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ this.Ct.Dispose();
+ this.Tcs = null;
+ this.Message = null;
+ this.Collected?.Clear();
+ this.Collected = null;
+ }
}
/// <summary>
- /// Disposes the.
+ /// The reaction.
/// </summary>
- public void Dispose()
+ public class Reaction
{
- GC.SuppressFinalize(this);
- this.Ct.Dispose();
- this.Tcs = null;
- this.Message = null;
- this.Collected?.Clear();
- this.Collected = null;
+ /// <summary>
+ /// Gets the emoji.
+ /// </summary>
+ public DiscordEmoji Emoji { get; internal set; }
+ /// <summary>
+ /// Gets the users.
+ /// </summary>
+ public ConcurrentHashSet<DiscordUser> Users { get; internal set; }
+ /// <summary>
+ /// Gets the total.
+ /// </summary>
+ public int Total => this.Users.Count;
}
}
-
-/// <summary>
-/// The reaction.
-/// </summary>
-public class Reaction
-{
- /// <summary>
- /// Gets the emoji.
- /// </summary>
- public DiscordEmoji Emoji { get; internal set; }
- /// <summary>
- /// Gets the users.
- /// </summary>
- public ConcurrentHashSet<DiscordUser> Users { get; internal set; }
- /// <summary>
- /// Gets the total.
- /// </summary>
- public int Total => this.Users.Count;
-}
diff --git a/DisCatSharp.Interactivity/EventHandling/Requests/CollectRequest.cs b/DisCatSharp.Interactivity/EventHandling/Requests/CollectRequest.cs
index 3d312e5f9..52edbbb67 100644
--- a/DisCatSharp.Interactivity/EventHandling/Requests/CollectRequest.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Requests/CollectRequest.cs
@@ -1,92 +1,93 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
using ConcurrentCollections;
using DisCatSharp.Common.Utilities;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// CollectRequest is a class that serves as a representation of
-/// EventArgs that are being collected within a specific time frame.
-/// </summary>
-/// <typeparam name="T"></typeparam>
-internal class CollectRequest<T> : IDisposable where T : AsyncEventArgs
+namespace DisCatSharp.Interactivity.EventHandling
{
- internal TaskCompletionSource<bool> Tcs;
- internal CancellationTokenSource Ct;
- internal Func<T, bool> Predicate;
- internal TimeSpan Timeout;
- internal ConcurrentHashSet<T> Collected;
-
/// <summary>
- /// Creates a new CollectRequest object.
+ /// CollectRequest is a class that serves as a representation of
+ /// EventArgs that are being collected within a specific time frame.
/// </summary>
- /// <param name="predicate">Predicate to match</param>
- /// <param name="timeout">Timeout time</param>
- public CollectRequest(Func<T, bool> predicate, TimeSpan timeout)
+ /// <typeparam name="T"></typeparam>
+ internal class CollectRequest<T> : IDisposable where T : AsyncEventArgs
{
- this.Tcs = new TaskCompletionSource<bool>();
- this.Ct = new CancellationTokenSource(timeout);
- this.Predicate = predicate;
- this.Ct.Token.Register(() => this.Tcs.TrySetResult(true));
- this.Timeout = timeout;
- this.Collected = new ConcurrentHashSet<T>();
- }
+ internal TaskCompletionSource<bool> Tcs;
+ internal CancellationTokenSource Ct;
+ internal Func<T, bool> Predicate;
+ internal TimeSpan Timeout;
+ internal ConcurrentHashSet<T> Collected;
- ~CollectRequest()
- {
- this.Dispose();
- }
+ /// <summary>
+ /// Creates a new CollectRequest object.
+ /// </summary>
+ /// <param name="predicate">Predicate to match</param>
+ /// <param name="timeout">Timeout time</param>
+ public CollectRequest(Func<T, bool> predicate, TimeSpan timeout)
+ {
+ this.Tcs = new TaskCompletionSource<bool>();
+ this.Ct = new CancellationTokenSource(timeout);
+ this.Predicate = predicate;
+ this.Ct.Token.Register(() => this.Tcs.TrySetResult(true));
+ this.Timeout = timeout;
+ this.Collected = new ConcurrentHashSet<T>();
+ }
- /// <summary>
- /// Disposes this CollectRequest.
- /// </summary>
- public void Dispose()
- {
- this.Ct.Dispose();
- this.Tcs = null;
- this.Predicate = null;
+ ~CollectRequest()
+ {
+ this.Dispose();
+ }
- if (this.Collected != null)
+ /// <summary>
+ /// Disposes this CollectRequest.
+ /// </summary>
+ public void Dispose()
{
- this.Collected.Clear();
- this.Collected = null;
+ this.Ct.Dispose();
+ this.Tcs = null;
+ this.Predicate = null;
+
+ if (this.Collected != null)
+ {
+ this.Collected.Clear();
+ this.Collected = null;
+ }
}
}
}
/*
^ ^
( Quack! )> (ミචᆽචミ)
(somewhere on twitter I read amazon had a duck
that said meow so I had to add a cat that says quack)
*/
diff --git a/DisCatSharp.Interactivity/EventHandling/Requests/IPaginationRequest.cs b/DisCatSharp.Interactivity/EventHandling/Requests/IPaginationRequest.cs
index ca1f8102a..40c4a0470 100644
--- a/DisCatSharp.Interactivity/EventHandling/Requests/IPaginationRequest.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Requests/IPaginationRequest.cs
@@ -1,106 +1,107 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// The pagination request.
-/// </summary>
-public interface IPaginationRequest
+namespace DisCatSharp.Interactivity.EventHandling
{
/// <summary>
- /// Returns the number of pages.
+ /// The pagination request.
/// </summary>
- /// <returns></returns>
- int PageCount { get; }
+ public interface IPaginationRequest
+ {
+ /// <summary>
+ /// Returns the number of pages.
+ /// </summary>
+ /// <returns></returns>
+ int PageCount { get; }
- /// <summary>
- /// Returns the current page.
- /// </summary>
- /// <returns></returns>
- Task<Page> GetPageAsync();
+ /// <summary>
+ /// Returns the current page.
+ /// </summary>
+ /// <returns></returns>
+ Task<Page> GetPageAsync();
- /// <summary>
- /// Tells the request to set its index to the first page.
- /// </summary>
- /// <returns></returns>
- Task SkipLeftAsync();
+ /// <summary>
+ /// Tells the request to set its index to the first page.
+ /// </summary>
+ /// <returns></returns>
+ Task SkipLeftAsync();
- /// <summary>
- /// Tells the request to set its index to the last page.
- /// </summary>
- /// <returns></returns>
- Task SkipRightAsync();
+ /// <summary>
+ /// Tells the request to set its index to the last page.
+ /// </summary>
+ /// <returns></returns>
+ Task SkipRightAsync();
- /// <summary>
- /// Tells the request to increase its index by one.
- /// </summary>
- /// <returns></returns>
- Task NextPageAsync();
+ /// <summary>
+ /// Tells the request to increase its index by one.
+ /// </summary>
+ /// <returns></returns>
+ Task NextPageAsync();
- /// <summary>
- /// Tells the request to decrease its index by one.
- /// </summary>
- /// <returns></returns>
- Task PreviousPageAsync();
+ /// <summary>
+ /// Tells the request to decrease its index by one.
+ /// </summary>
+ /// <returns></returns>
+ Task PreviousPageAsync();
- /// <summary>
- /// Requests the message buttons from the pagination request.
- /// </summary>
- /// <returns>The buttons.</returns>
- Task<IEnumerable<DiscordButtonComponent>> GetButtonsAsync();
+ /// <summary>
+ /// Requests the message buttons from the pagination request.
+ /// </summary>
+ /// <returns>The buttons.</returns>
+ Task<IEnumerable<DiscordButtonComponent>> GetButtonsAsync();
- /// <summary>
- /// Requests message emojis from pagination request.
- /// </summary>
- /// <returns></returns>
- Task<PaginationEmojis> GetEmojisAsync();
+ /// <summary>
+ /// Requests message emojis from pagination request.
+ /// </summary>
+ /// <returns></returns>
+ Task<PaginationEmojis> GetEmojisAsync();
- /// <summary>
- /// Gets pagination message from this request.
- /// </summary>
- /// <returns></returns>
- Task<DiscordMessage> GetMessageAsync();
+ /// <summary>
+ /// Gets pagination message from this request.
+ /// </summary>
+ /// <returns></returns>
+ Task<DiscordMessage> GetMessageAsync();
- /// <summary>
- /// Gets the user this pagination applies to.
- /// </summary>
- /// <returns></returns>
- Task<DiscordUser> GetUserAsync();
+ /// <summary>
+ /// Gets the user this pagination applies to.
+ /// </summary>
+ /// <returns></returns>
+ Task<DiscordUser> GetUserAsync();
- /// <summary>
- /// Get this request's Task Completion Source.
- /// </summary>
- /// <returns></returns>
- Task<TaskCompletionSource<bool>> GetTaskCompletionSourceAsync();
+ /// <summary>
+ /// Get this request's Task Completion Source.
+ /// </summary>
+ /// <returns></returns>
+ Task<TaskCompletionSource<bool>> GetTaskCompletionSourceAsync();
- /// <summary>
- /// Tells the request to perform cleanup.
- /// </summary>
- /// <returns></returns>
- Task DoCleanupAsync();
+ /// <summary>
+ /// Tells the request to perform cleanup.
+ /// </summary>
+ /// <returns></returns>
+ Task DoCleanupAsync();
+ }
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Requests/MatchRequest.cs b/DisCatSharp.Interactivity/EventHandling/Requests/MatchRequest.cs
index 0ed290e16..f90b0a14c 100644
--- a/DisCatSharp.Interactivity/EventHandling/Requests/MatchRequest.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Requests/MatchRequest.cs
@@ -1,71 +1,72 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// MatchRequest is a class that serves as a representation of a
-/// match that is being waited for.
-/// </summary>
-/// <typeparam name="T"></typeparam>
-internal class MatchRequest<T> : IDisposable where T : AsyncEventArgs
+namespace DisCatSharp.Interactivity.EventHandling
{
- internal TaskCompletionSource<T> Tcs;
- internal CancellationTokenSource Ct;
- internal Func<T, bool> Predicate;
- internal TimeSpan Timeout;
-
/// <summary>
- /// Creates a new MatchRequest object.
+ /// MatchRequest is a class that serves as a representation of a
+ /// match that is being waited for.
/// </summary>
- /// <param name="predicate">Predicate to match</param>
- /// <param name="timeout">Timeout time</param>
- public MatchRequest(Func<T, bool> predicate, TimeSpan timeout)
+ /// <typeparam name="T"></typeparam>
+ internal class MatchRequest<T> : IDisposable where T : AsyncEventArgs
{
- this.Tcs = new TaskCompletionSource<T>();
- this.Ct = new CancellationTokenSource(timeout);
- this.Predicate = predicate;
- this.Ct.Token.Register(() => this.Tcs.TrySetResult(null));
- this.Timeout = timeout;
- }
+ internal TaskCompletionSource<T> Tcs;
+ internal CancellationTokenSource Ct;
+ internal Func<T, bool> Predicate;
+ internal TimeSpan Timeout;
- ~MatchRequest()
- {
- this.Dispose();
- }
+ /// <summary>
+ /// Creates a new MatchRequest object.
+ /// </summary>
+ /// <param name="predicate">Predicate to match</param>
+ /// <param name="timeout">Timeout time</param>
+ public MatchRequest(Func<T, bool> predicate, TimeSpan timeout)
+ {
+ this.Tcs = new TaskCompletionSource<T>();
+ this.Ct = new CancellationTokenSource(timeout);
+ this.Predicate = predicate;
+ this.Ct.Token.Register(() => this.Tcs.TrySetResult(null));
+ this.Timeout = timeout;
+ }
- /// <summary>
- /// Disposes this MatchRequest.
- /// </summary>
- public void Dispose()
- {
- this.Ct.Dispose();
- this.Tcs = null;
- this.Predicate = null;
+ ~MatchRequest()
+ {
+ this.Dispose();
+ }
+
+ /// <summary>
+ /// Disposes this MatchRequest.
+ /// </summary>
+ public void Dispose()
+ {
+ this.Ct.Dispose();
+ this.Tcs = null;
+ this.Predicate = null;
+ }
}
}
diff --git a/DisCatSharp.Interactivity/EventHandling/Requests/PollRequest.cs b/DisCatSharp.Interactivity/EventHandling/Requests/PollRequest.cs
index b389404b1..879ea2271 100644
--- a/DisCatSharp.Interactivity/EventHandling/Requests/PollRequest.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Requests/PollRequest.cs
@@ -1,155 +1,156 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using ConcurrentCollections;
using DisCatSharp.Entities;
-namespace DisCatSharp.Interactivity.EventHandling;
-
-/// <summary>
-/// The poll request.
-/// </summary>
-public class PollRequest
+namespace DisCatSharp.Interactivity.EventHandling
{
- internal TaskCompletionSource<bool> Tcs;
- internal CancellationTokenSource Ct;
- internal TimeSpan Timeout;
- internal ConcurrentHashSet<PollEmoji> Collected;
- internal DiscordMessage Message;
- internal IEnumerable<DiscordEmoji> Emojis;
-
/// <summary>
- ///
+ /// The poll request.
/// </summary>
- /// <param name="message"></param>
- /// <param name="timeout"></param>
- /// <param name="emojis"></param>
- public PollRequest(DiscordMessage message, TimeSpan timeout, IEnumerable<DiscordEmoji> emojis)
+ public class PollRequest
{
- this.Tcs = new TaskCompletionSource<bool>();
- this.Ct = new CancellationTokenSource(timeout);
- this.Ct.Token.Register(() => this.Tcs.TrySetResult(true));
- this.Timeout = timeout;
- this.Emojis = emojis;
- this.Collected = new ConcurrentHashSet<PollEmoji>();
- this.Message = message;
+ internal TaskCompletionSource<bool> Tcs;
+ internal CancellationTokenSource Ct;
+ internal TimeSpan Timeout;
+ internal ConcurrentHashSet<PollEmoji> Collected;
+ internal DiscordMessage Message;
+ internal IEnumerable<DiscordEmoji> Emojis;
- foreach (var e in emojis)
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="message"></param>
+ /// <param name="timeout"></param>
+ /// <param name="emojis"></param>
+ public PollRequest(DiscordMessage message, TimeSpan timeout, IEnumerable<DiscordEmoji> emojis)
{
- this.Collected.Add(new PollEmoji(e));
+ this.Tcs = new TaskCompletionSource<bool>();
+ this.Ct = new CancellationTokenSource(timeout);
+ this.Ct.Token.Register(() => this.Tcs.TrySetResult(true));
+ this.Timeout = timeout;
+ this.Emojis = emojis;
+ this.Collected = new ConcurrentHashSet<PollEmoji>();
+ this.Message = message;
+
+ foreach (var e in emojis)
+ {
+ this.Collected.Add(new PollEmoji(e));
+ }
}
- }
- /// <summary>
- /// Clears the collected.
- /// </summary>
- internal void ClearCollected()
- {
- this.Collected.Clear();
- foreach (var e in this.Emojis)
+ /// <summary>
+ /// Clears the collected.
+ /// </summary>
+ internal void ClearCollected()
{
- this.Collected.Add(new PollEmoji(e));
+ this.Collected.Clear();
+ foreach (var e in this.Emojis)
+ {
+ this.Collected.Add(new PollEmoji(e));
+ }
}
- }
- /// <summary>
- /// Removes the reaction.
- /// </summary>
- /// <param name="emoji">The emoji.</param>
- /// <param name="member">The member.</param>
- internal void RemoveReaction(DiscordEmoji emoji, DiscordUser member)
- {
- if (this.Collected.Any(x => x.Emoji == emoji))
+ /// <summary>
+ /// Removes the reaction.
+ /// </summary>
+ /// <param name="emoji">The emoji.</param>
+ /// <param name="member">The member.</param>
+ internal void RemoveReaction(DiscordEmoji emoji, DiscordUser member)
{
- if (this.Collected.Any(x => x.Voted.Contains(member)))
+ if (this.Collected.Any(x => x.Emoji == emoji))
{
- var e = this.Collected.First(x => x.Emoji == emoji);
- this.Collected.TryRemove(e);
- e.Voted.TryRemove(member);
- this.Collected.Add(e);
+ if (this.Collected.Any(x => x.Voted.Contains(member)))
+ {
+ var e = this.Collected.First(x => x.Emoji == emoji);
+ this.Collected.TryRemove(e);
+ e.Voted.TryRemove(member);
+ this.Collected.Add(e);
+ }
}
}
- }
- /// <summary>
- /// Adds the reaction.
- /// </summary>
- /// <param name="emoji">The emoji.</param>
- /// <param name="member">The member.</param>
- internal void AddReaction(DiscordEmoji emoji, DiscordUser member)
- {
- if (this.Collected.Any(x => x.Emoji == emoji))
+ /// <summary>
+ /// Adds the reaction.
+ /// </summary>
+ /// <param name="emoji">The emoji.</param>
+ /// <param name="member">The member.</param>
+ internal void AddReaction(DiscordEmoji emoji, DiscordUser member)
{
- if (!this.Collected.Any(x => x.Voted.Contains(member)))
+ if (this.Collected.Any(x => x.Emoji == emoji))
{
- var e = this.Collected.First(x => x.Emoji == emoji);
- this.Collected.TryRemove(e);
- e.Voted.Add(member);
- this.Collected.Add(e);
+ if (!this.Collected.Any(x => x.Voted.Contains(member)))
+ {
+ var e = this.Collected.First(x => x.Emoji == emoji);
+ this.Collected.TryRemove(e);
+ e.Voted.Add(member);
+ this.Collected.Add(e);
+ }
}
}
- }
- ~PollRequest()
- {
- this.Dispose();
- }
+ ~PollRequest()
+ {
+ this.Dispose();
+ }
- /// <summary>
- /// Disposes this PollRequest.
- /// </summary>
- public void Dispose()
- {
- this.Ct.Dispose();
- this.Tcs = null;
+ /// <summary>
+ /// Disposes this PollRequest.
+ /// </summary>
+ public void Dispose()
+ {
+ this.Ct.Dispose();
+ this.Tcs = null;
+ }
}
-}
-/// <summary>
-/// The poll emoji.
-/// </summary>
-public class PollEmoji
-{
/// <summary>
- /// Initializes a new instance of the <see cref="PollEmoji"/> class.
+ /// The poll emoji.
/// </summary>
- /// <param name="emoji">The emoji.</param>
- internal PollEmoji(DiscordEmoji emoji)
+ public class PollEmoji
{
- this.Emoji = emoji;
- this.Voted = new ConcurrentHashSet<DiscordUser>();
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PollEmoji"/> class.
+ /// </summary>
+ /// <param name="emoji">The emoji.</param>
+ internal PollEmoji(DiscordEmoji emoji)
+ {
+ this.Emoji = emoji;
+ this.Voted = new ConcurrentHashSet<DiscordUser>();
+ }
- public DiscordEmoji Emoji;
- public ConcurrentHashSet<DiscordUser> Voted;
- /// <summary>
- /// Gets the total.
- /// </summary>
- public int Total => this.Voted.Count;
+ public DiscordEmoji Emoji;
+ public ConcurrentHashSet<DiscordUser> Voted;
+ /// <summary>
+ /// Gets the total.
+ /// </summary>
+ public int Total => this.Voted.Count;
+ }
}
diff --git a/DisCatSharp.Interactivity/Extensions/ChannelExtensions.cs b/DisCatSharp.Interactivity/Extensions/ChannelExtensions.cs
index 2f9e96daa..ca8b490ef 100644
--- a/DisCatSharp.Interactivity/Extensions/ChannelExtensions.cs
+++ b/DisCatSharp.Interactivity/Extensions/ChannelExtensions.cs
@@ -1,150 +1,151 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Interactivity.Enums;
using DisCatSharp.Interactivity.EventHandling;
-namespace DisCatSharp.Interactivity.Extensions;
-
-/// <summary>
-/// Interactivity extension methods for <see cref="DisCatSharp.Entities.DiscordChannel"/>.
-/// </summary>
-public static class ChannelExtensions
+namespace DisCatSharp.Interactivity.Extensions
{
/// <summary>
- /// Waits for the next message sent in this channel that satisfies the predicate.
- /// </summary>
- /// <param name="channel">The channel to monitor.</param>
- /// <param name="predicate">A predicate that should return <see langword="true"/> if a message matches.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
- public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordChannel channel, Func<DiscordMessage, bool> predicate, TimeSpan? timeoutOverride = null)
- => GetInteractivity(channel).WaitForMessageAsync(msg => msg.ChannelId == channel.Id && predicate(msg), timeoutOverride);
-
- /// <summary>
- /// Waits for the next message sent in this channel.
+ /// Interactivity extension methods for <see cref="DisCatSharp.Entities.DiscordChannel"/>.
/// </summary>
- /// <param name="channel">The channel to monitor.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
- public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordChannel channel, TimeSpan? timeoutOverride = null)
- => channel.GetNextMessageAsync(msg => true, timeoutOverride);
-
- /// <summary>
- /// Waits for the next message sent in this channel from a specific user.
- /// </summary>
- /// <param name="channel">The channel to monitor.</param>
- /// <param name="user">The target user.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
- public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordChannel channel, DiscordUser user, TimeSpan? timeoutOverride = null)
- => channel.GetNextMessageAsync(msg => msg.Author.Id == user.Id, timeoutOverride);
-
- /// <summary>
- /// Waits for a specific user to start typing in this channel.
- /// </summary>
- /// <param name="channel">The target channel.</param>
- /// <param name="user">The target user.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
- public static Task<InteractivityResult<TypingStartEventArgs>> WaitForUserTypingAsync(this DiscordChannel channel, DiscordUser user, TimeSpan? timeoutOverride = null)
- => GetInteractivity(channel).WaitForUserTypingAsync(user, channel, timeoutOverride);
-
-
- /// <summary>
- /// Sends a new paginated message.
- /// </summary>
- /// <param name="channel">Target channel.</param>
- /// <param name="user">The user that will be able to control the pages.</param>
- /// <param name="pages">A collection of <see cref="Page"/> to display.</param>
- /// <param name="emojis">Pagination emojis.</param>
- /// <param name="behaviour">Pagination behaviour (when hitting max and min indices).</param>
- /// <param name="deletion">Deletion behaviour.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
- public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationEmojis emojis, PaginationBehaviour? behaviour = default, PaginationDeletion? deletion = default, TimeSpan? timeoutOverride = null)
- => GetInteractivity(channel).SendPaginatedMessageAsync(channel, user, pages, emojis, behaviour, deletion, timeoutOverride);
-
- /// <summary>
- /// Sends a new paginated message with buttons.
- /// </summary>
- /// <param name="channel">Target channel.</param>
- /// <param name="user">The user that will be able to control the pages.</param>
- /// <param name="pages">A collection of <see cref="Page"/> to display.</param>
- /// <param name="buttons">Pagination buttons (leave null to default to ones on configuration).</param>
- /// <param name="behaviour">Pagination behaviour.</param>
- /// <param name="deletion">Deletion behaviour</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
- public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
- => GetInteractivity(channel).SendPaginatedMessageAsync(channel, user, pages, buttons, behaviour, deletion, token);
-
- /// <inheritdoc cref="SendPaginatedMessageAsync(DiscordChannel, DiscordUser, IEnumerable{Page}, PaginationButtons, PaginationBehaviour?, ButtonPaginationBehavior?, CancellationToken)"/>
- public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
- => channel.SendPaginatedMessageAsync(user, pages, default, behaviour, deletion, token);
-
- /// <summary>
- /// Sends a new paginated message with buttons.
- /// </summary>
- /// <param name="channel">Target channel.</param>
- /// <param name="user">The user that will be able to control the pages.</param>
- /// <param name="pages">A collection of <see cref="Page"/> to display.</param>
- /// <param name="buttons">Pagination buttons (leave null to default to ones on configuration).</param>
- /// <param name="behaviour">Pagination behaviour.</param>
- /// <param name="deletion">Deletion behaviour.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
- public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons, TimeSpan? timeoutOverride, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default)
- => GetInteractivity(channel).SendPaginatedMessageAsync(channel, user, pages, buttons, timeoutOverride, behaviour, deletion);
-
-
- /// <summary>
- /// Sends the paginated message async.
- /// </summary>
- /// <param name="channel">The channel.</param>
- /// <param name="user">The user.</param>
- /// <param name="pages">The pages.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- /// <param name="behaviour">The behaviour.</param>
- /// <param name="deletion">The deletion.</param>
- /// <returns>A Task.</returns>
- public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, TimeSpan? timeoutOverride, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default)
- => channel.SendPaginatedMessageAsync(user, pages, default, timeoutOverride, behaviour, deletion);
-
- /// <summary>
- /// Retrieves an interactivity instance from a channel instance.
- /// </summary>
- private static InteractivityExtension GetInteractivity(DiscordChannel channel)
+ public static class ChannelExtensions
{
- var client = (DiscordClient)channel.Discord;
- var interactivity = client.GetInteractivity();
-
- return interactivity ?? throw new InvalidOperationException($"Interactivity is not enabled for this {(client.IsShard ? "shard" : "client")}.");
+ /// <summary>
+ /// Waits for the next message sent in this channel that satisfies the predicate.
+ /// </summary>
+ /// <param name="channel">The channel to monitor.</param>
+ /// <param name="predicate">A predicate that should return <see langword="true"/> if a message matches.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
+ public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordChannel channel, Func<DiscordMessage, bool> predicate, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(channel).WaitForMessageAsync(msg => msg.ChannelId == channel.Id && predicate(msg), timeoutOverride);
+
+ /// <summary>
+ /// Waits for the next message sent in this channel.
+ /// </summary>
+ /// <param name="channel">The channel to monitor.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
+ public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordChannel channel, TimeSpan? timeoutOverride = null)
+ => channel.GetNextMessageAsync(msg => true, timeoutOverride);
+
+ /// <summary>
+ /// Waits for the next message sent in this channel from a specific user.
+ /// </summary>
+ /// <param name="channel">The channel to monitor.</param>
+ /// <param name="user">The target user.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
+ public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordChannel channel, DiscordUser user, TimeSpan? timeoutOverride = null)
+ => channel.GetNextMessageAsync(msg => msg.Author.Id == user.Id, timeoutOverride);
+
+ /// <summary>
+ /// Waits for a specific user to start typing in this channel.
+ /// </summary>
+ /// <param name="channel">The target channel.</param>
+ /// <param name="user">The target user.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
+ public static Task<InteractivityResult<TypingStartEventArgs>> WaitForUserTypingAsync(this DiscordChannel channel, DiscordUser user, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(channel).WaitForUserTypingAsync(user, channel, timeoutOverride);
+
+
+ /// <summary>
+ /// Sends a new paginated message.
+ /// </summary>
+ /// <param name="channel">Target channel.</param>
+ /// <param name="user">The user that will be able to control the pages.</param>
+ /// <param name="pages">A collection of <see cref="Page"/> to display.</param>
+ /// <param name="emojis">Pagination emojis.</param>
+ /// <param name="behaviour">Pagination behaviour (when hitting max and min indices).</param>
+ /// <param name="deletion">Deletion behaviour.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
+ public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationEmojis emojis, PaginationBehaviour? behaviour = default, PaginationDeletion? deletion = default, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(channel).SendPaginatedMessageAsync(channel, user, pages, emojis, behaviour, deletion, timeoutOverride);
+
+ /// <summary>
+ /// Sends a new paginated message with buttons.
+ /// </summary>
+ /// <param name="channel">Target channel.</param>
+ /// <param name="user">The user that will be able to control the pages.</param>
+ /// <param name="pages">A collection of <see cref="Page"/> to display.</param>
+ /// <param name="buttons">Pagination buttons (leave null to default to ones on configuration).</param>
+ /// <param name="behaviour">Pagination behaviour.</param>
+ /// <param name="deletion">Deletion behaviour</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
+ public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
+ => GetInteractivity(channel).SendPaginatedMessageAsync(channel, user, pages, buttons, behaviour, deletion, token);
+
+ /// <inheritdoc cref="SendPaginatedMessageAsync(DiscordChannel, DiscordUser, IEnumerable{Page}, PaginationButtons, PaginationBehaviour?, ButtonPaginationBehavior?, CancellationToken)"/>
+ public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
+ => channel.SendPaginatedMessageAsync(user, pages, default, behaviour, deletion, token);
+
+ /// <summary>
+ /// Sends a new paginated message with buttons.
+ /// </summary>
+ /// <param name="channel">Target channel.</param>
+ /// <param name="user">The user that will be able to control the pages.</param>
+ /// <param name="pages">A collection of <see cref="Page"/> to display.</param>
+ /// <param name="buttons">Pagination buttons (leave null to default to ones on configuration).</param>
+ /// <param name="behaviour">Pagination behaviour.</param>
+ /// <param name="deletion">Deletion behaviour.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the channel.</exception>
+ public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons, TimeSpan? timeoutOverride, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default)
+ => GetInteractivity(channel).SendPaginatedMessageAsync(channel, user, pages, buttons, timeoutOverride, behaviour, deletion);
+
+
+ /// <summary>
+ /// Sends the paginated message async.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ /// <param name="user">The user.</param>
+ /// <param name="pages">The pages.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ /// <param name="behaviour">The behaviour.</param>
+ /// <param name="deletion">The deletion.</param>
+ /// <returns>A Task.</returns>
+ public static Task SendPaginatedMessageAsync(this DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, TimeSpan? timeoutOverride, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default)
+ => channel.SendPaginatedMessageAsync(user, pages, default, timeoutOverride, behaviour, deletion);
+
+ /// <summary>
+ /// Retrieves an interactivity instance from a channel instance.
+ /// </summary>
+ private static InteractivityExtension GetInteractivity(DiscordChannel channel)
+ {
+ var client = (DiscordClient)channel.Discord;
+ var interactivity = client.GetInteractivity();
+
+ return interactivity ?? throw new InvalidOperationException($"Interactivity is not enabled for this {(client.IsShard ? "shard" : "client")}.");
+ }
}
}
diff --git a/DisCatSharp.Interactivity/Extensions/ClientExtensions.cs b/DisCatSharp.Interactivity/Extensions/ClientExtensions.cs
index 036fe3757..632c10247 100644
--- a/DisCatSharp.Interactivity/Extensions/ClientExtensions.cs
+++ b/DisCatSharp.Interactivity/Extensions/ClientExtensions.cs
@@ -1,99 +1,100 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
-namespace DisCatSharp.Interactivity.Extensions;
-
-/// <summary>
-/// Interactivity extension methods for <see cref="DiscordClient"/> and <see cref="DiscordShardedClient"/>.
-/// </summary>
-public static class ClientExtensions
+namespace DisCatSharp.Interactivity.Extensions
{
/// <summary>
- /// Enables interactivity for this <see cref="DiscordClient"/> instance.
+ /// Interactivity extension methods for <see cref="DiscordClient"/> and <see cref="DiscordShardedClient"/>.
/// </summary>
- /// <param name="client">The client to enable interactivity for.</param>
- /// <param name="configuration">A configuration instance. Default configuration values will be used if none is provided.</param>
- /// <returns>A brand new <see cref="InteractivityExtension"/> instance.</returns>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity has already been enabled for the client instance.</exception>
- public static InteractivityExtension UseInteractivity(this DiscordClient client, InteractivityConfiguration configuration = null)
+ public static class ClientExtensions
{
- if (client.GetExtension<InteractivityExtension>() != null) throw new InvalidOperationException($"Interactivity is already enabled for this {(client.IsShard ? "shard" : "client")}.");
+ /// <summary>
+ /// Enables interactivity for this <see cref="DiscordClient"/> instance.
+ /// </summary>
+ /// <param name="client">The client to enable interactivity for.</param>
+ /// <param name="configuration">A configuration instance. Default configuration values will be used if none is provided.</param>
+ /// <returns>A brand new <see cref="InteractivityExtension"/> instance.</returns>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity has already been enabled for the client instance.</exception>
+ public static InteractivityExtension UseInteractivity(this DiscordClient client, InteractivityConfiguration configuration = null)
+ {
+ if (client.GetExtension<InteractivityExtension>() != null) throw new InvalidOperationException($"Interactivity is already enabled for this {(client.IsShard ? "shard" : "client")}.");
- configuration ??= new InteractivityConfiguration();
- var extension = new InteractivityExtension(configuration);
- client.AddExtension(extension);
+ configuration ??= new InteractivityConfiguration();
+ var extension = new InteractivityExtension(configuration);
+ client.AddExtension(extension);
- return extension;
- }
-
- /// <summary>
- /// Enables interactivity for each shard.
- /// </summary>
- /// <param name="client">The shard client to enable interactivity for.</param>
- /// <param name="configuration">Configuration to use for all shards. If one isn't provided, default configuration values will be used.</param>
- /// <returns>A dictionary containing new <see cref="InteractivityExtension"/> instances for each shard.</returns>
- public static async Task<IReadOnlyDictionary<int, InteractivityExtension>> UseInteractivityAsync(this DiscordShardedClient client, InteractivityConfiguration configuration = null)
- {
- var extensions = new Dictionary<int, InteractivityExtension>();
- await client.InitializeShardsAsync().ConfigureAwait(false);
+ return extension;
+ }
- foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ /// <summary>
+ /// Enables interactivity for each shard.
+ /// </summary>
+ /// <param name="client">The shard client to enable interactivity for.</param>
+ /// <param name="configuration">Configuration to use for all shards. If one isn't provided, default configuration values will be used.</param>
+ /// <returns>A dictionary containing new <see cref="InteractivityExtension"/> instances for each shard.</returns>
+ public static async Task<IReadOnlyDictionary<int, InteractivityExtension>> UseInteractivityAsync(this DiscordShardedClient client, InteractivityConfiguration configuration = null)
{
- var extension = shard.GetExtension<InteractivityExtension>() ?? shard.UseInteractivity(configuration);
- extensions.Add(shard.ShardId, extension);
- }
+ var extensions = new Dictionary<int, InteractivityExtension>();
+ await client.InitializeShardsAsync().ConfigureAwait(false);
- return new ReadOnlyDictionary<int, InteractivityExtension>(extensions);
- }
+ foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ {
+ var extension = shard.GetExtension<InteractivityExtension>() ?? shard.UseInteractivity(configuration);
+ extensions.Add(shard.ShardId, extension);
+ }
- /// <summary>
- /// Retrieves the registered <see cref="InteractivityExtension"/> instance for this client.
- /// </summary>
- /// <param name="client">The client to retrieve an <see cref="InteractivityExtension"/> instance from.</param>
- /// <returns>An existing <see cref="InteractivityExtension"/> instance, or <see langword="null"/> if interactivity is not enabled for the <see cref="DiscordClient"/> instance.</returns>
- public static InteractivityExtension GetInteractivity(this DiscordClient client)
- => client.GetExtension<InteractivityExtension>();
+ return new ReadOnlyDictionary<int, InteractivityExtension>(extensions);
+ }
- /// <summary>
- /// Retrieves a <see cref="InteractivityExtension"/> instance for each shard.
- /// </summary>
- /// <param name="client">The shard client to retrieve interactivity instances from.</param>
- /// <returns>A dictionary containing <see cref="InteractivityExtension"/> instances for each shard.</returns>
- public static async Task<ReadOnlyDictionary<int, InteractivityExtension>> GetInteractivityAsync(this DiscordShardedClient client)
- {
- await client.InitializeShardsAsync().ConfigureAwait(false);
- var extensions = new Dictionary<int, InteractivityExtension>();
+ /// <summary>
+ /// Retrieves the registered <see cref="InteractivityExtension"/> instance for this client.
+ /// </summary>
+ /// <param name="client">The client to retrieve an <see cref="InteractivityExtension"/> instance from.</param>
+ /// <returns>An existing <see cref="InteractivityExtension"/> instance, or <see langword="null"/> if interactivity is not enabled for the <see cref="DiscordClient"/> instance.</returns>
+ public static InteractivityExtension GetInteractivity(this DiscordClient client)
+ => client.GetExtension<InteractivityExtension>();
- foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ /// <summary>
+ /// Retrieves a <see cref="InteractivityExtension"/> instance for each shard.
+ /// </summary>
+ /// <param name="client">The shard client to retrieve interactivity instances from.</param>
+ /// <returns>A dictionary containing <see cref="InteractivityExtension"/> instances for each shard.</returns>
+ public static async Task<ReadOnlyDictionary<int, InteractivityExtension>> GetInteractivityAsync(this DiscordShardedClient client)
{
- extensions.Add(shard.ShardId, shard.GetExtension<InteractivityExtension>());
- }
+ await client.InitializeShardsAsync().ConfigureAwait(false);
+ var extensions = new Dictionary<int, InteractivityExtension>();
- return new ReadOnlyDictionary<int, InteractivityExtension>(extensions);
+ foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ {
+ extensions.Add(shard.ShardId, shard.GetExtension<InteractivityExtension>());
+ }
+
+ return new ReadOnlyDictionary<int, InteractivityExtension>(extensions);
+ }
}
}
diff --git a/DisCatSharp.Interactivity/Extensions/InteractionExtensions.cs b/DisCatSharp.Interactivity/Extensions/InteractionExtensions.cs
index baa44ddbf..52c9b930a 100644
--- a/DisCatSharp.Interactivity/Extensions/InteractionExtensions.cs
+++ b/DisCatSharp.Interactivity/Extensions/InteractionExtensions.cs
@@ -1,120 +1,121 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Interactivity.Enums;
using DisCatSharp.Interactivity.EventHandling;
-namespace DisCatSharp.Interactivity.Extensions;
-
-/// <summary>
-/// The interaction extensions.
-/// </summary>
-public static class InteractionExtensions
+namespace DisCatSharp.Interactivity.Extensions
{
/// <summary>
- /// Sends a paginated message in response to an interaction.
- /// <para>
- /// <b>Pass the interaction directly. Interactivity will ACK it.</b>
- /// </para>
+ /// The interaction extensions.
/// </summary>
- /// <param name="interaction">The interaction to create a response to.</param>
- /// <param name="ephemeral">Whether the response should be ephemeral.</param>
- /// <param name="user">The user to listen for button presses from.</param>
- /// <param name="pages">The pages to paginate.</param>
- /// <param name="buttons">Optional: custom buttons</param>
- /// <param name="behaviour">Pagination behaviour.</param>
- /// <param name="deletion">Deletion behaviour</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- public static Task SendPaginatedResponseAsync(this DiscordInteraction interaction, bool ephemeral, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons = null, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
- => MessageExtensions.GetInteractivity(interaction.Message).SendPaginatedResponseAsync(interaction, ephemeral, user, pages, buttons, behaviour, deletion, token);
-
- /// <summary>
- /// Sends multiple modals to the user with a prompt to open the next one.
- /// </summary>
- /// <param name="interaction">The interaction to create a response to.</param>
- /// <param name="modals">The modal pages.</param>
- /// <param name="timeOutOverride">A custom timeout. (Default: 15 minutes)</param>
- /// <returns>A read-only dictionary with the customid of the components as the key.</returns>
- /// <exception cref="ArgumentException">Is thrown when no modals are defined.</exception>
- /// <exception cref="InvalidOperationException">Is thrown when interactivity is not enabled for the client/shard.</exception>
- public static async Task<PaginatedModalResponse> CreatePaginatedModalResponseAsync(this DiscordInteraction interaction, IReadOnlyList<ModalPage> modals, TimeSpan? timeOutOverride = null)
+ public static class InteractionExtensions
{
- if (modals is null || modals.Count == 0)
- throw new ArgumentException("You have to set at least one page");
-
- var client = (DiscordClient)interaction.Discord;
- var interactivity = client.GetInteractivity() ?? throw new InvalidOperationException($"Interactivity is not enabled for this {(client.IsShard ? "shard" : "client")}.");
+ /// <summary>
+ /// Sends a paginated message in response to an interaction.
+ /// <para>
+ /// <b>Pass the interaction directly. Interactivity will ACK it.</b>
+ /// </para>
+ /// </summary>
+ /// <param name="interaction">The interaction to create a response to.</param>
+ /// <param name="ephemeral">Whether the response should be ephemeral.</param>
+ /// <param name="user">The user to listen for button presses from.</param>
+ /// <param name="pages">The pages to paginate.</param>
+ /// <param name="buttons">Optional: custom buttons</param>
+ /// <param name="behaviour">Pagination behaviour.</param>
+ /// <param name="deletion">Deletion behaviour</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ public static Task SendPaginatedResponseAsync(this DiscordInteraction interaction, bool ephemeral, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons = null, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
+ => MessageExtensions.GetInteractivity(interaction.Message).SendPaginatedResponseAsync(interaction, ephemeral, user, pages, buttons, behaviour, deletion, token);
+
+ /// <summary>
+ /// Sends multiple modals to the user with a prompt to open the next one.
+ /// </summary>
+ /// <param name="interaction">The interaction to create a response to.</param>
+ /// <param name="modals">The modal pages.</param>
+ /// <param name="timeOutOverride">A custom timeout. (Default: 15 minutes)</param>
+ /// <returns>A read-only dictionary with the customid of the components as the key.</returns>
+ /// <exception cref="ArgumentException">Is thrown when no modals are defined.</exception>
+ /// <exception cref="InvalidOperationException">Is thrown when interactivity is not enabled for the client/shard.</exception>
+ public static async Task<PaginatedModalResponse> CreatePaginatedModalResponseAsync(this DiscordInteraction interaction, IReadOnlyList<ModalPage> modals, TimeSpan? timeOutOverride = null)
+ {
+ if (modals is null || modals.Count == 0)
+ throw new ArgumentException("You have to set at least one page");
- timeOutOverride ??= TimeSpan.FromMinutes(15);
+ var client = (DiscordClient)interaction.Discord;
+ var interactivity = client.GetInteractivity() ?? throw new InvalidOperationException($"Interactivity is not enabled for this {(client.IsShard ? "shard" : "client")}.");
- Dictionary<string, string> caughtResponses = new();
+ timeOutOverride ??= TimeSpan.FromMinutes(15);
- var previousInteraction = interaction;
+ Dictionary<string, string> caughtResponses = new();
- foreach (var b in modals)
- {
- var modal = b.Modal.WithCustomId(Guid.NewGuid().ToString());
+ var previousInteraction = interaction;
- if (previousInteraction.Type is InteractionType.Ping or InteractionType.ModalSubmit)
+ foreach (var b in modals)
{
- await previousInteraction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, b.OpenMessage.AddComponents(b.OpenButton));
- var originalResponse = await previousInteraction.GetOriginalResponseAsync();
- var modalOpen = await interactivity.WaitForButtonAsync(originalResponse, new List<DiscordButtonComponent> { b.OpenButton }, timeOutOverride);
+ var modal = b.Modal.WithCustomId(Guid.NewGuid().ToString());
- if (modalOpen.TimedOut)
+ if (previousInteraction.Type is InteractionType.Ping or InteractionType.ModalSubmit)
{
- _ = previousInteraction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent(b.OpenMessage.Content).AddComponents(b.OpenButton.Disable()));
- return new PaginatedModalResponse { TimedOut = true };
- }
+ await previousInteraction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, b.OpenMessage.AddComponents(b.OpenButton));
+ var originalResponse = await previousInteraction.GetOriginalResponseAsync();
+ var modalOpen = await interactivity.WaitForButtonAsync(originalResponse, new List<DiscordButtonComponent> { b.OpenButton }, timeOutOverride);
- await modalOpen.Result.Interaction.CreateInteractionModalResponseAsync(modal);
- }
- else
- {
- await previousInteraction.CreateInteractionModalResponseAsync(modal);
- }
+ if (modalOpen.TimedOut)
+ {
+ _ = previousInteraction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent(b.OpenMessage.Content).AddComponents(b.OpenButton.Disable()));
+ return new PaginatedModalResponse { TimedOut = true };
+ }
+
+ await modalOpen.Result.Interaction.CreateInteractionModalResponseAsync(modal);
+ }
+ else
+ {
+ await previousInteraction.CreateInteractionModalResponseAsync(modal);
+ }
- _ = previousInteraction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent(b.OpenMessage.Content).AddComponents(b.OpenButton.Disable()));
+ _ = previousInteraction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent(b.OpenMessage.Content).AddComponents(b.OpenButton.Disable()));
- var modalResult = await interactivity.WaitForModalAsync(modal.CustomId, timeOutOverride);
+ var modalResult = await interactivity.WaitForModalAsync(modal.CustomId, timeOutOverride);
- if (modalResult.TimedOut)
- return new PaginatedModalResponse { TimedOut = true };
+ if (modalResult.TimedOut)
+ return new PaginatedModalResponse { TimedOut = true };
- foreach (var row in modalResult.Result.Interaction.Data.Components)
- foreach (var submissions in row.Components)
- caughtResponses.Add(submissions.CustomId, submissions.Value);
+ foreach (var row in modalResult.Result.Interaction.Data.Components)
+ foreach (var submissions in row.Components)
+ caughtResponses.Add(submissions.CustomId, submissions.Value);
- previousInteraction = modalResult.Result.Interaction;
- }
+ previousInteraction = modalResult.Result.Interaction;
+ }
- await previousInteraction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral());
+ await previousInteraction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral());
- return new PaginatedModalResponse { TimedOut = false, Responses = caughtResponses, Interaction = previousInteraction };
+ return new PaginatedModalResponse { TimedOut = false, Responses = caughtResponses, Interaction = previousInteraction };
+ }
}
}
diff --git a/DisCatSharp.Interactivity/Extensions/MessageExtensions.cs b/DisCatSharp.Interactivity/Extensions/MessageExtensions.cs
index 215cb40a3..10370f14f 100644
--- a/DisCatSharp.Interactivity/Extensions/MessageExtensions.cs
+++ b/DisCatSharp.Interactivity/Extensions/MessageExtensions.cs
@@ -1,243 +1,244 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Interactivity.Enums;
using DisCatSharp.Interactivity.EventHandling;
-namespace DisCatSharp.Interactivity.Extensions;
-
-/// <summary>
-/// Interactivity extension methods for <see cref="DisCatSharp.Entities.DiscordMessage"/>.
-/// </summary>
-public static class MessageExtensions
+namespace DisCatSharp.Interactivity.Extensions
{
/// <summary>
- /// Waits for the next message that has the same author and channel as this message.
- /// </summary>
- /// <param name="message">Original message.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordMessage message, TimeSpan? timeoutOverride = null)
- => message.Channel.GetNextMessageAsync(message.Author, timeoutOverride);
-
- /// <summary>
- /// Waits for the next message with the same author and channel as this message, which also satisfies a predicate.
- /// </summary>
- /// <param name="message">Original message.</param>
- /// <param name="predicate">A predicate that should return <see langword="true"/> if a message matches.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordMessage message, Func<DiscordMessage, bool> predicate, TimeSpan? timeoutOverride = null)
- => message.Channel.GetNextMessageAsync(msg => msg.Author.Id == message.Author.Id && message.ChannelId == msg.ChannelId && predicate(msg), timeoutOverride);
- /// <summary>
- /// Waits for any button to be pressed on the specified message.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message)
- => GetInteractivity(message).WaitForButtonAsync(message);
-
- /// <summary>
- /// Waits for any button to be pressed on the specified message.
+ /// Interactivity extension methods for <see cref="DisCatSharp.Entities.DiscordMessage"/>.
/// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).WaitForButtonAsync(message, timeoutOverride);
- /// <summary>
- /// Waits for any button to be pressed on the specified message.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, CancellationToken token)
- => GetInteractivity(message).WaitForButtonAsync(message, token);
- /// <summary>
- /// Waits for a button with the specified Id to be pressed on the specified message.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="id">The Id of the button to wait for.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, string id, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).WaitForButtonAsync(message, id, timeoutOverride);
-
- /// <summary>
- /// Waits for a button with the specified Id to be pressed on the specified message.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="id">The Id of the button to wait for.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, string id, CancellationToken token)
- => GetInteractivity(message).WaitForButtonAsync(message, id, token);
-
- /// <summary>
- /// Waits for any button to be pressed on the specified message by the specified user.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="user">The user to wait for button input from.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, DiscordUser user, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).WaitForButtonAsync(message, user, timeoutOverride);
-
- /// <summary>
- /// Waits for any button to be pressed on the specified message by the specified user.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="user">The user to wait for button input from.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, DiscordUser user, CancellationToken token)
- => GetInteractivity(message).WaitForButtonAsync(message, user, token);
-
- /// <summary>
- /// Waits for any button to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="predicate">The predicate to filter interactions by.</param>
- /// <param name="timeoutOverride">Override the timeout specified in <see cref="InteractivityConfiguration"/></param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).WaitForButtonAsync(message, predicate, timeoutOverride);
-
- /// <summary>
- /// Waits for any button to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="predicate">The predicate to filter interactions by.</param>
- /// <param name="token">A token to cancel interactivity with at any time. Pass <see cref="System.Threading.CancellationToken.None"/> to wait indefinitely.</param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken token)
- => GetInteractivity(message).WaitForButtonAsync(message, predicate, token);
-
- /// <summary>
- /// Waits for any dropdown to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait for.</param>
- /// <param name="predicate">A filter predicate.</param>
- /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
- /// <exception cref="System.ArgumentException">Thrown when the message doesn't contain any dropdowns</exception>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).WaitForSelectAsync(message, predicate, timeoutOverride);
-
-
- /// <summary>
- /// Waits for any dropdown to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait for.</param>
- /// <param name="predicate">A filter predicate.</param>
- /// <param name="token">A token that can be used to cancel interactivity. Pass <see cref="System.Threading.CancellationToken.None"/> to wait indefinitely.</param>
- /// <exception cref="System.ArgumentException">Thrown when the message doesn't contain any dropdowns</exception>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken token)
- => GetInteractivity(message).WaitForSelectAsync(message, predicate, token);
-
- /// <summary>
- /// Waits for a dropdown to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="id">The Id of the dropdown to wait for.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, string id, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).WaitForSelectAsync(message, id, timeoutOverride);
-
- /// <summary>
- /// Waits for a dropdown to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="id">The Id of the dropdown to wait for.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, string id, CancellationToken token)
- => GetInteractivity(message).WaitForSelectAsync(message, id, token);
-
- /// <summary>
- /// Waits for a dropdown to be interacted with by the specified user.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="user">The user to wait for.</param>
- /// <param name="id">The Id of the dropdown to wait for.</param>
- /// <param name="timeoutOverride"></param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, DiscordUser user, string id, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).WaitForSelectAsync(message, user, id, timeoutOverride);
-
- /// <summary>
- /// Waits for a dropdown to be interacted with by the specified user.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="user">The user to wait for.</param>
- /// <param name="id">The Id of the dropdown to wait for.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, DiscordUser user, string id, CancellationToken token)
- => GetInteractivity(message).WaitForSelectAsync(message, user, id, token);
-
- /// <summary>
- /// Waits for a reaction on this message from a specific user.
- /// </summary>
- /// <param name="message">Target message.</param>
- /// <param name="user">The target user.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the message.</exception>
- public static Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(this DiscordMessage message, DiscordUser user, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).WaitForReactionAsync(message, user, timeoutOverride);
-
- /// <summary>
- /// Waits for a specific reaction on this message from the specified user.
- /// </summary>
- /// <param name="message">Target message.</param>
- /// <param name="user">The target user.</param>
- /// <param name="emoji">The target emoji.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the message.</exception>
- public static Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(this DiscordMessage message, DiscordUser user, DiscordEmoji emoji, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).WaitForReactionAsync(e => e.Emoji == emoji, message, user, timeoutOverride);
-
- /// <summary>
- /// Collects all reactions on this message within the timeout duration.
- /// </summary>
- /// <param name="message">The message to collect reactions from.</param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the message.</exception>
- public static Task<ReadOnlyCollection<Reaction>> CollectReactionsAsync(this DiscordMessage message, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).CollectReactionsAsync(message, timeoutOverride);
-
-
- /// <summary>
- /// Begins a poll using this message.
- /// </summary>
- /// <param name="message">Target message.</param>
- /// <param name="emojis">Options for this poll.</param>
- /// <param name="behaviorOverride">Overrides the action set in <see cref="InteractivityConfiguration.PaginationBehaviour"/></param>
- /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
- /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the message.</exception>
- public static Task<ReadOnlyCollection<PollEmoji>> DoPollAsync(this DiscordMessage message, IEnumerable<DiscordEmoji> emojis, PollBehaviour? behaviorOverride = null, TimeSpan? timeoutOverride = null)
- => GetInteractivity(message).DoPollAsync(message, emojis, behaviorOverride, timeoutOverride);
-
- /// <summary>
- /// Retrieves an interactivity instance from a message instance.
- /// </summary>
- internal static InteractivityExtension GetInteractivity(DiscordMessage message)
+ public static class MessageExtensions
{
- var client = (DiscordClient)message.Discord;
- var interactivity = client.GetInteractivity();
+ /// <summary>
+ /// Waits for the next message that has the same author and channel as this message.
+ /// </summary>
+ /// <param name="message">Original message.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordMessage message, TimeSpan? timeoutOverride = null)
+ => message.Channel.GetNextMessageAsync(message.Author, timeoutOverride);
+
+ /// <summary>
+ /// Waits for the next message with the same author and channel as this message, which also satisfies a predicate.
+ /// </summary>
+ /// <param name="message">Original message.</param>
+ /// <param name="predicate">A predicate that should return <see langword="true"/> if a message matches.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ public static Task<InteractivityResult<DiscordMessage>> GetNextMessageAsync(this DiscordMessage message, Func<DiscordMessage, bool> predicate, TimeSpan? timeoutOverride = null)
+ => message.Channel.GetNextMessageAsync(msg => msg.Author.Id == message.Author.Id && message.ChannelId == msg.ChannelId && predicate(msg), timeoutOverride);
+ /// <summary>
+ /// Waits for any button to be pressed on the specified message.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message)
+ => GetInteractivity(message).WaitForButtonAsync(message);
+
+ /// <summary>
+ /// Waits for any button to be pressed on the specified message.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).WaitForButtonAsync(message, timeoutOverride);
+ /// <summary>
+ /// Waits for any button to be pressed on the specified message.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, CancellationToken token)
+ => GetInteractivity(message).WaitForButtonAsync(message, token);
+ /// <summary>
+ /// Waits for a button with the specified Id to be pressed on the specified message.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="id">The Id of the button to wait for.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, string id, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).WaitForButtonAsync(message, id, timeoutOverride);
+
+ /// <summary>
+ /// Waits for a button with the specified Id to be pressed on the specified message.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="id">The Id of the button to wait for.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, string id, CancellationToken token)
+ => GetInteractivity(message).WaitForButtonAsync(message, id, token);
+
+ /// <summary>
+ /// Waits for any button to be pressed on the specified message by the specified user.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="user">The user to wait for button input from.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, DiscordUser user, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).WaitForButtonAsync(message, user, timeoutOverride);
+
+ /// <summary>
+ /// Waits for any button to be pressed on the specified message by the specified user.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="user">The user to wait for button input from.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, DiscordUser user, CancellationToken token)
+ => GetInteractivity(message).WaitForButtonAsync(message, user, token);
+
+ /// <summary>
+ /// Waits for any button to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="predicate">The predicate to filter interactions by.</param>
+ /// <param name="timeoutOverride">Override the timeout specified in <see cref="InteractivityConfiguration"/></param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).WaitForButtonAsync(message, predicate, timeoutOverride);
+
+ /// <summary>
+ /// Waits for any button to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="predicate">The predicate to filter interactions by.</param>
+ /// <param name="token">A token to cancel interactivity with at any time. Pass <see cref="System.Threading.CancellationToken.None"/> to wait indefinitely.</param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(this DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken token)
+ => GetInteractivity(message).WaitForButtonAsync(message, predicate, token);
+
+ /// <summary>
+ /// Waits for any dropdown to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait for.</param>
+ /// <param name="predicate">A filter predicate.</param>
+ /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the message doesn't contain any dropdowns</exception>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).WaitForSelectAsync(message, predicate, timeoutOverride);
+
+
+ /// <summary>
+ /// Waits for any dropdown to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait for.</param>
+ /// <param name="predicate">A filter predicate.</param>
+ /// <param name="token">A token that can be used to cancel interactivity. Pass <see cref="System.Threading.CancellationToken.None"/> to wait indefinitely.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the message doesn't contain any dropdowns</exception>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken token)
+ => GetInteractivity(message).WaitForSelectAsync(message, predicate, token);
+
+ /// <summary>
+ /// Waits for a dropdown to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="id">The Id of the dropdown to wait for.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, string id, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).WaitForSelectAsync(message, id, timeoutOverride);
+
+ /// <summary>
+ /// Waits for a dropdown to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="id">The Id of the dropdown to wait for.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, string id, CancellationToken token)
+ => GetInteractivity(message).WaitForSelectAsync(message, id, token);
+
+ /// <summary>
+ /// Waits for a dropdown to be interacted with by the specified user.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="user">The user to wait for.</param>
+ /// <param name="id">The Id of the dropdown to wait for.</param>
+ /// <param name="timeoutOverride"></param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, DiscordUser user, string id, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).WaitForSelectAsync(message, user, id, timeoutOverride);
+
+ /// <summary>
+ /// Waits for a dropdown to be interacted with by the specified user.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="user">The user to wait for.</param>
+ /// <param name="id">The Id of the dropdown to wait for.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ public static Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(this DiscordMessage message, DiscordUser user, string id, CancellationToken token)
+ => GetInteractivity(message).WaitForSelectAsync(message, user, id, token);
- return interactivity ?? throw new InvalidOperationException($"Interactivity is not enabled for this {(client.IsShard ? "shard" : "client")}.");
+ /// <summary>
+ /// Waits for a reaction on this message from a specific user.
+ /// </summary>
+ /// <param name="message">Target message.</param>
+ /// <param name="user">The target user.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the message.</exception>
+ public static Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(this DiscordMessage message, DiscordUser user, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).WaitForReactionAsync(message, user, timeoutOverride);
+
+ /// <summary>
+ /// Waits for a specific reaction on this message from the specified user.
+ /// </summary>
+ /// <param name="message">Target message.</param>
+ /// <param name="user">The target user.</param>
+ /// <param name="emoji">The target emoji.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the message.</exception>
+ public static Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(this DiscordMessage message, DiscordUser user, DiscordEmoji emoji, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).WaitForReactionAsync(e => e.Emoji == emoji, message, user, timeoutOverride);
+
+ /// <summary>
+ /// Collects all reactions on this message within the timeout duration.
+ /// </summary>
+ /// <param name="message">The message to collect reactions from.</param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the message.</exception>
+ public static Task<ReadOnlyCollection<Reaction>> CollectReactionsAsync(this DiscordMessage message, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).CollectReactionsAsync(message, timeoutOverride);
+
+
+ /// <summary>
+ /// Begins a poll using this message.
+ /// </summary>
+ /// <param name="message">Target message.</param>
+ /// <param name="emojis">Options for this poll.</param>
+ /// <param name="behaviorOverride">Overrides the action set in <see cref="InteractivityConfiguration.PaginationBehaviour"/></param>
+ /// <param name="timeoutOverride">Overrides the timeout set in <see cref="InteractivityConfiguration.Timeout"/></param>
+ /// <exception cref="System.InvalidOperationException">Thrown if interactivity is not enabled for the client associated with the message.</exception>
+ public static Task<ReadOnlyCollection<PollEmoji>> DoPollAsync(this DiscordMessage message, IEnumerable<DiscordEmoji> emojis, PollBehaviour? behaviorOverride = null, TimeSpan? timeoutOverride = null)
+ => GetInteractivity(message).DoPollAsync(message, emojis, behaviorOverride, timeoutOverride);
+
+ /// <summary>
+ /// Retrieves an interactivity instance from a message instance.
+ /// </summary>
+ internal static InteractivityExtension GetInteractivity(DiscordMessage message)
+ {
+ var client = (DiscordClient)message.Discord;
+ var interactivity = client.GetInteractivity();
+
+ return interactivity ?? throw new InvalidOperationException($"Interactivity is not enabled for this {(client.IsShard ? "shard" : "client")}.");
+ }
}
}
diff --git a/DisCatSharp.Interactivity/InteractivityConfiguration.cs b/DisCatSharp.Interactivity/InteractivityConfiguration.cs
index 87df3cb46..bc2308393 100644
--- a/DisCatSharp.Interactivity/InteractivityConfiguration.cs
+++ b/DisCatSharp.Interactivity/InteractivityConfiguration.cs
@@ -1,116 +1,117 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Interactivity.Enums;
using DisCatSharp.Interactivity.EventHandling;
using Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.Interactivity;
-
-/// <summary>
-/// Configuration class for your Interactivity extension
-/// </summary>
-public sealed class InteractivityConfiguration
+namespace DisCatSharp.Interactivity
{
/// <summary>
- /// <para>Sets the default interactivity action timeout.</para>
- /// <para>Defaults to 1 minute.</para>
- /// </summary>
- public TimeSpan Timeout { internal get; set; } = TimeSpan.FromMinutes(1);
-
- /// <summary>
- /// What to do after the poll ends
- /// </summary>
- public PollBehaviour PollBehaviour { internal get; set; } = PollBehaviour.DeleteEmojis;
-
- /// <summary>
- /// Emojis to use for pagination
- /// </summary>
- public PaginationEmojis PaginationEmojis { internal get; set; } = new();
-
- /// <summary>
- /// Buttons to use for pagination.
- /// </summary>
- public PaginationButtons PaginationButtons { internal get; set; } = new();
-
- /// <summary>
- /// Whether interactivity should ACK buttons that are pushed. Setting this to <see langword="true"/> will also prevent subsequent event handlers from running.
- /// </summary>
- public bool AckPaginationButtons { internal get; set; }
-
- /// <summary>
- /// How to handle buttons after pagination ends.
- /// </summary>
- public ButtonPaginationBehavior ButtonBehavior { internal get; set; }
-
- /// <summary>
- /// How to handle pagination. Defaults to WrapAround.
- /// </summary>
- public PaginationBehaviour PaginationBehaviour { internal get; set; } = PaginationBehaviour.WrapAround;
-
- /// <summary>
- /// How to handle pagination deletion. Defaults to DeleteEmojis.
- /// </summary>
- public PaginationDeletion PaginationDeletion { internal get; set; } = PaginationDeletion.DeleteEmojis;
-
- /// <summary>
- /// How to handle invalid interactions. Defaults to Ignore.
- /// </summary>
- public InteractionResponseBehavior ResponseBehavior { internal get; set; } = InteractionResponseBehavior.Ignore;
-
- /// <summary>
- /// The message to send to the user when processing invalid interactions. Ignored if <see cref="ResponseBehavior"/> is not set to <see cref="DisCatSharp.Interactivity.Enums.InteractionResponseBehavior.Respond"/>.
- /// </summary>
- public string ResponseMessage { internal get; set; }
-
- /// <summary>
- /// Creates a new instance of <see cref="InteractivityConfiguration"/>.
- /// </summary>
- [ActivatorUtilitiesConstructor]
- public InteractivityConfiguration()
- {
- }
-
- /// <summary>
- /// Creates a new instance of <see cref="InteractivityConfiguration"/>, copying the properties of another configuration.
+ /// Configuration class for your Interactivity extension
/// </summary>
- /// <param name="other">Configuration the properties of which are to be copied.</param>
- public InteractivityConfiguration(InteractivityConfiguration other)
+ public sealed class InteractivityConfiguration
{
- this.AckPaginationButtons = other.AckPaginationButtons;
- this.PaginationButtons = other.PaginationButtons;
- this.ButtonBehavior = other.ButtonBehavior;
- this.PaginationBehaviour = other.PaginationBehaviour;
- this.PaginationDeletion = other.PaginationDeletion;
- this.ResponseBehavior = other.ResponseBehavior;
- this.PaginationEmojis = other.PaginationEmojis;
- this.ResponseMessage = other.ResponseMessage;
- this.PollBehaviour = other.PollBehaviour;
- this.Timeout = other.Timeout;
-
- if (this.ResponseBehavior is InteractionResponseBehavior.Respond && string.IsNullOrWhiteSpace(this.ResponseMessage))
- throw new ArgumentException($"{nameof(this.ResponseMessage)} cannot be null, empty, or whitespace when {nameof(this.ResponseBehavior)} is set to respond.");
+ /// <summary>
+ /// <para>Sets the default interactivity action timeout.</para>
+ /// <para>Defaults to 1 minute.</para>
+ /// </summary>
+ public TimeSpan Timeout { internal get; set; } = TimeSpan.FromMinutes(1);
+
+ /// <summary>
+ /// What to do after the poll ends
+ /// </summary>
+ public PollBehaviour PollBehaviour { internal get; set; } = PollBehaviour.DeleteEmojis;
+
+ /// <summary>
+ /// Emojis to use for pagination
+ /// </summary>
+ public PaginationEmojis PaginationEmojis { internal get; set; } = new();
+
+ /// <summary>
+ /// Buttons to use for pagination.
+ /// </summary>
+ public PaginationButtons PaginationButtons { internal get; set; } = new();
+
+ /// <summary>
+ /// Whether interactivity should ACK buttons that are pushed. Setting this to <see langword="true"/> will also prevent subsequent event handlers from running.
+ /// </summary>
+ public bool AckPaginationButtons { internal get; set; }
+
+ /// <summary>
+ /// How to handle buttons after pagination ends.
+ /// </summary>
+ public ButtonPaginationBehavior ButtonBehavior { internal get; set; }
+
+ /// <summary>
+ /// How to handle pagination. Defaults to WrapAround.
+ /// </summary>
+ public PaginationBehaviour PaginationBehaviour { internal get; set; } = PaginationBehaviour.WrapAround;
+
+ /// <summary>
+ /// How to handle pagination deletion. Defaults to DeleteEmojis.
+ /// </summary>
+ public PaginationDeletion PaginationDeletion { internal get; set; } = PaginationDeletion.DeleteEmojis;
+
+ /// <summary>
+ /// How to handle invalid interactions. Defaults to Ignore.
+ /// </summary>
+ public InteractionResponseBehavior ResponseBehavior { internal get; set; } = InteractionResponseBehavior.Ignore;
+
+ /// <summary>
+ /// The message to send to the user when processing invalid interactions. Ignored if <see cref="ResponseBehavior"/> is not set to <see cref="DisCatSharp.Interactivity.Enums.InteractionResponseBehavior.Respond"/>.
+ /// </summary>
+ public string ResponseMessage { internal get; set; }
+
+ /// <summary>
+ /// Creates a new instance of <see cref="InteractivityConfiguration"/>.
+ /// </summary>
+ [ActivatorUtilitiesConstructor]
+ public InteractivityConfiguration()
+ {
+ }
+
+ /// <summary>
+ /// Creates a new instance of <see cref="InteractivityConfiguration"/>, copying the properties of another configuration.
+ /// </summary>
+ /// <param name="other">Configuration the properties of which are to be copied.</param>
+ public InteractivityConfiguration(InteractivityConfiguration other)
+ {
+ this.AckPaginationButtons = other.AckPaginationButtons;
+ this.PaginationButtons = other.PaginationButtons;
+ this.ButtonBehavior = other.ButtonBehavior;
+ this.PaginationBehaviour = other.PaginationBehaviour;
+ this.PaginationDeletion = other.PaginationDeletion;
+ this.ResponseBehavior = other.ResponseBehavior;
+ this.PaginationEmojis = other.PaginationEmojis;
+ this.ResponseMessage = other.ResponseMessage;
+ this.PollBehaviour = other.PollBehaviour;
+ this.Timeout = other.Timeout;
+
+ if (this.ResponseBehavior is InteractionResponseBehavior.Respond && string.IsNullOrWhiteSpace(this.ResponseMessage))
+ throw new ArgumentException($"{nameof(this.ResponseMessage)} cannot be null, empty, or whitespace when {nameof(this.ResponseBehavior)} is set to respond.");
+ }
}
}
diff --git a/DisCatSharp.Interactivity/InteractivityEvents.cs b/DisCatSharp.Interactivity/InteractivityEvents.cs
index 51abdfabd..c75aaf655 100644
--- a/DisCatSharp.Interactivity/InteractivityEvents.cs
+++ b/DisCatSharp.Interactivity/InteractivityEvents.cs
@@ -1,56 +1,57 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Logging;
-namespace DisCatSharp.Interactivity;
-
-/// <summary>
-/// Contains well-defined event IDs used by the Interactivity extension.
-/// </summary>
-public static class InteractivityEvents
+namespace DisCatSharp.Interactivity
{
/// <summary>
- /// Miscellaneous events, that do not fit in any other category.
+ /// Contains well-defined event IDs used by the Interactivity extension.
/// </summary>
- public static EventId Misc { get; } = new(500, "Interactivity");
+ public static class InteractivityEvents
+ {
+ /// <summary>
+ /// Miscellaneous events, that do not fit in any other category.
+ /// </summary>
+ public static EventId Misc { get; } = new(500, "Interactivity");
- /// <summary>
- /// Events pertaining to errors that happen during waiting for events.
- /// </summary>
- public static EventId InteractivityWaitError { get; } = new(501, nameof(InteractivityWaitError));
+ /// <summary>
+ /// Events pertaining to errors that happen during waiting for events.
+ /// </summary>
+ public static EventId InteractivityWaitError { get; } = new(501, nameof(InteractivityWaitError));
- /// <summary>
- /// Events pertaining to pagination.
- /// </summary>
- public static EventId InteractivityPaginationError { get; } = new(502, nameof(InteractivityPaginationError));
+ /// <summary>
+ /// Events pertaining to pagination.
+ /// </summary>
+ public static EventId InteractivityPaginationError { get; } = new(502, nameof(InteractivityPaginationError));
- /// <summary>
- /// Events pertaining to polling.
- /// </summary>
- public static EventId InteractivityPollError { get; } = new(503, nameof(InteractivityPollError));
+ /// <summary>
+ /// Events pertaining to polling.
+ /// </summary>
+ public static EventId InteractivityPollError { get; } = new(503, nameof(InteractivityPollError));
- /// <summary>
- /// Events pertaining to event collection.
- /// </summary>
- public static EventId InteractivityCollectorError { get; } = new(504, nameof(InteractivityCollectorError));
+ /// <summary>
+ /// Events pertaining to event collection.
+ /// </summary>
+ public static EventId InteractivityCollectorError { get; } = new(504, nameof(InteractivityCollectorError));
+ }
}
diff --git a/DisCatSharp.Interactivity/InteractivityExtension.cs b/DisCatSharp.Interactivity/InteractivityExtension.cs
index df4e152db..9ccfa1fa2 100644
--- a/DisCatSharp.Interactivity/InteractivityExtension.cs
+++ b/DisCatSharp.Interactivity/InteractivityExtension.cs
@@ -1,958 +1,959 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.EventArgs;
using DisCatSharp.Interactivity.Enums;
using DisCatSharp.Interactivity.EventHandling;
-namespace DisCatSharp.Interactivity;
-
-/// <summary>
-/// Extension class for DisCatSharp.Interactivity
-/// </summary>
-public class InteractivityExtension : BaseExtension
+namespace DisCatSharp.Interactivity
{
-
- /// <summary>
- /// Gets the config.
- /// </summary>
- internal InteractivityConfiguration Config { get; }
-
- private EventWaiter<MessageCreateEventArgs> _messageCreatedWaiter;
-
- private EventWaiter<MessageReactionAddEventArgs> _messageReactionAddWaiter;
-
- private EventWaiter<TypingStartEventArgs> _typingStartWaiter;
-
- private EventWaiter<ComponentInteractionCreateEventArgs> _modalInteractionWaiter;
-
- private EventWaiter<ComponentInteractionCreateEventArgs> _componentInteractionWaiter;
-
- private ComponentEventWaiter _componentEventWaiter;
-
- private ModalEventWaiter _modalEventWaiter;
-
- private ReactionCollector _reactionCollector;
-
- private Poller _poller;
-
- private Paginator _paginator;
- private ComponentPaginator _compPaginator;
-
/// <summary>
- /// Initializes a new instance of the <see cref="InteractivityExtension"/> class.
+ /// Extension class for DisCatSharp.Interactivity
/// </summary>
- /// <param name="cfg">The configuration.</param>
- internal InteractivityExtension(InteractivityConfiguration cfg)
+ public class InteractivityExtension : BaseExtension
{
- this.Config = new InteractivityConfiguration(cfg);
- }
- /// <summary>
- /// Setups the Interactivity Extension.
- /// </summary>
- /// <param name="client">Discord client.</param>
- protected internal override void Setup(DiscordClient client)
- {
- this.Client = client;
- this._messageCreatedWaiter = new EventWaiter<MessageCreateEventArgs>(this.Client);
- this._messageReactionAddWaiter = new EventWaiter<MessageReactionAddEventArgs>(this.Client);
- this._componentInteractionWaiter = new EventWaiter<ComponentInteractionCreateEventArgs>(this.Client);
- this._modalInteractionWaiter = new EventWaiter<ComponentInteractionCreateEventArgs>(this.Client);
- this._typingStartWaiter = new EventWaiter<TypingStartEventArgs>(this.Client);
- this._poller = new Poller(this.Client);
- this._reactionCollector = new ReactionCollector(this.Client);
- this._paginator = new Paginator(this.Client);
- this._compPaginator = new ComponentPaginator(this.Client, this.Config);
- this._componentEventWaiter = new ComponentEventWaiter(this.Client, this.Config);
- this._modalEventWaiter = new ModalEventWaiter(this.Client, this.Config);
+ /// <summary>
+ /// Gets the config.
+ /// </summary>
+ internal InteractivityConfiguration Config { get; }
- }
+ private EventWaiter<MessageCreateEventArgs> _messageCreatedWaiter;
- /// <summary>
- /// Makes a poll and returns poll results.
- /// </summary>
- /// <param name="m">Message to create poll on.</param>
- /// <param name="emojis">Emojis to use for this poll.</param>
- /// <param name="behaviour">What to do when the poll ends.</param>
- /// <param name="timeout">Override timeout period.</param>
- /// <returns></returns>
- public async Task<ReadOnlyCollection<PollEmoji>> DoPollAsync(DiscordMessage m, IEnumerable<DiscordEmoji> emojis, PollBehaviour? behaviour = default, TimeSpan? timeout = null)
- {
- if (!Utilities.HasReactionIntents(this.Client.Configuration.Intents))
- throw new InvalidOperationException("No reaction intents are enabled.");
+ private EventWaiter<MessageReactionAddEventArgs> _messageReactionAddWaiter;
- if (!emojis.Any())
- throw new ArgumentException("You need to provide at least one emoji for a poll!");
+ private EventWaiter<TypingStartEventArgs> _typingStartWaiter;
- foreach (var em in emojis)
- await m.CreateReactionAsync(em).ConfigureAwait(false);
+ private EventWaiter<ComponentInteractionCreateEventArgs> _modalInteractionWaiter;
- var res = await this._poller.DoPollAsync(new PollRequest(m, timeout ?? this.Config.Timeout, emojis)).ConfigureAwait(false);
+ private EventWaiter<ComponentInteractionCreateEventArgs> _componentInteractionWaiter;
- var pollBehaviour = behaviour ?? this.Config.PollBehaviour;
- var thisMember = await m.Channel.Guild.GetMemberAsync(this.Client.CurrentUser.Id).ConfigureAwait(false);
+ private ComponentEventWaiter _componentEventWaiter;
- if (pollBehaviour == PollBehaviour.DeleteEmojis && m.Channel.PermissionsFor(thisMember).HasPermission(Permissions.ManageMessages))
- await m.DeleteAllReactionsAsync().ConfigureAwait(false);
+ private ModalEventWaiter _modalEventWaiter;
- return new ReadOnlyCollection<PollEmoji>(res.ToList());
- }
+ private ReactionCollector _reactionCollector;
- /// <summary>
- /// Waits for any button in the specified collection to be pressed.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="buttons">A collection of buttons to listen for.</param>
- /// <param name="timeoutOverride">Override the timeout period in <see cref="InteractivityConfiguration"/>.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
- /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
- /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
- public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, IEnumerable<DiscordButtonComponent> buttons, TimeSpan? timeoutOverride = null)
- => this.WaitForButtonAsync(message, buttons, this.GetCancellationToken(timeoutOverride));
+ private Poller _poller;
- /// <summary>
- /// Waits for any button in the specified collection to be pressed.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="buttons">A collection of buttons to listen for.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
- /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
- /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
- public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, IEnumerable<DiscordButtonComponent> buttons, CancellationToken token)
- {
- if (message.Author != this.Client.CurrentUser)
- throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
+ private Paginator _paginator;
+ private ComponentPaginator _compPaginator;
- if (!buttons.Any())
- throw new ArgumentException("You must specify at least one button to listen for.");
-
- if (!message.Components.Any())
- throw new ArgumentException("Provided message does not contain any components.");
-
- if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
- throw new ArgumentException("Provided Message does not contain any button components.");
-
- var res = await this._componentEventWaiter
- .WaitForMatchAsync(new ComponentMatchRequest(message,
- c =>
- c.Interaction.Data.ComponentType == ComponentType.Button &&
- buttons.Any(b => b.CustomId == c.Id), token)).ConfigureAwait(false);
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InteractivityExtension"/> class.
+ /// </summary>
+ /// <param name="cfg">The configuration.</param>
+ internal InteractivityExtension(InteractivityConfiguration cfg)
+ {
+ this.Config = new InteractivityConfiguration(cfg);
+ }
- return new InteractivityResult<ComponentInteractionCreateEventArgs>(res is null, res);
- }
+ /// <summary>
+ /// Setups the Interactivity Extension.
+ /// </summary>
+ /// <param name="client">Discord client.</param>
+ protected internal override void Setup(DiscordClient client)
+ {
+ this.Client = client;
+ this._messageCreatedWaiter = new EventWaiter<MessageCreateEventArgs>(this.Client);
+ this._messageReactionAddWaiter = new EventWaiter<MessageReactionAddEventArgs>(this.Client);
+ this._componentInteractionWaiter = new EventWaiter<ComponentInteractionCreateEventArgs>(this.Client);
+ this._modalInteractionWaiter = new EventWaiter<ComponentInteractionCreateEventArgs>(this.Client);
+ this._typingStartWaiter = new EventWaiter<TypingStartEventArgs>(this.Client);
+ this._poller = new Poller(this.Client);
+ this._reactionCollector = new ReactionCollector(this.Client);
+ this._paginator = new Paginator(this.Client);
+ this._compPaginator = new ComponentPaginator(this.Client, this.Config);
+ this._componentEventWaiter = new ComponentEventWaiter(this.Client, this.Config);
+ this._modalEventWaiter = new ModalEventWaiter(this.Client, this.Config);
- /// <summary>
- /// Waits for a user modal submit.
- /// </summary>
- /// <param name="customId">The custom id of the modal to wait for.</param>
- /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of the modal.</returns>
- public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForModalAsync(string customId, TimeSpan? timeoutOverride = null)
- => this.WaitForModalAsync(customId, this.GetCancellationToken(timeoutOverride));
+ }
- /// <summary>
- /// Waits for a user modal submit.
- /// </summary>
- /// <param name="customId">The custom id of the modal to wait for.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of the modal.</returns>
- public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForModalAsync(string customId, CancellationToken token)
- {
- var result =
- await this
- ._modalEventWaiter
- .WaitForModalMatchAsync(new ModalMatchRequest(customId, c => c.Interaction.Type == InteractionType.ModalSubmit, token))
- .ConfigureAwait(false);
+ /// <summary>
+ /// Makes a poll and returns poll results.
+ /// </summary>
+ /// <param name="m">Message to create poll on.</param>
+ /// <param name="emojis">Emojis to use for this poll.</param>
+ /// <param name="behaviour">What to do when the poll ends.</param>
+ /// <param name="timeout">Override timeout period.</param>
+ /// <returns></returns>
+ public async Task<ReadOnlyCollection<PollEmoji>> DoPollAsync(DiscordMessage m, IEnumerable<DiscordEmoji> emojis, PollBehaviour? behaviour = default, TimeSpan? timeout = null)
+ {
+ if (!Utilities.HasReactionIntents(this.Client.Configuration.Intents))
+ throw new InvalidOperationException("No reaction intents are enabled.");
- return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
- }
+ if (!emojis.Any())
+ throw new ArgumentException("You need to provide at least one emoji for a poll!");
- /// <summary>
- /// Waits for any button on the specified message to be pressed.
- /// </summary>
- /// <param name="message">The message to wait for the button on.</param>
- /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
- /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
- /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
- public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, TimeSpan? timeoutOverride = null)
- => this.WaitForButtonAsync(message, this.GetCancellationToken(timeoutOverride));
+ foreach (var em in emojis)
+ await m.CreateReactionAsync(em).ConfigureAwait(false);
- /// <summary>
- /// Waits for any button on the specified message to be pressed.
- /// </summary>
- /// <param name="message">The message to wait for the button on.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
- /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
- /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
- public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, CancellationToken token)
- {
- if (message.Author != this.Client.CurrentUser)
- throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
+ var res = await this._poller.DoPollAsync(new PollRequest(m, timeout ?? this.Config.Timeout, emojis)).ConfigureAwait(false);
- if (!message.Components.Any())
- throw new ArgumentException("Provided message does not contain any components.");
+ var pollBehaviour = behaviour ?? this.Config.PollBehaviour;
+ var thisMember = await m.Channel.Guild.GetMemberAsync(this.Client.CurrentUser.Id).ConfigureAwait(false);
- if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
- throw new ArgumentException("Message does not contain any button components.");
+ if (pollBehaviour == PollBehaviour.DeleteEmojis && m.Channel.PermissionsFor(thisMember).HasPermission(Permissions.ManageMessages))
+ await m.DeleteAllReactionsAsync().ConfigureAwait(false);
- var ids = message.Components.SelectMany(m => m.Components).Select(c => c.CustomId);
+ return new ReadOnlyCollection<PollEmoji>(res.ToList());
+ }
- var result =
- await this
- ._componentEventWaiter
- .WaitForMatchAsync(new ComponentMatchRequest(message, c => c.Interaction.Data.ComponentType == ComponentType.Button && ids.Contains(c.Id), token))
- .ConfigureAwait(false);
+ /// <summary>
+ /// Waits for any button in the specified collection to be pressed.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="buttons">A collection of buttons to listen for.</param>
+ /// <param name="timeoutOverride">Override the timeout period in <see cref="InteractivityConfiguration"/>.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
+ /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
+ public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, IEnumerable<DiscordButtonComponent> buttons, TimeSpan? timeoutOverride = null)
+ => this.WaitForButtonAsync(message, buttons, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Waits for any button in the specified collection to be pressed.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="buttons">A collection of buttons to listen for.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
+ /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
+ public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, IEnumerable<DiscordButtonComponent> buttons, CancellationToken token)
+ {
+ if (message.Author != this.Client.CurrentUser)
+ throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
- return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
- }
+ if (!buttons.Any())
+ throw new ArgumentException("You must specify at least one button to listen for.");
- /// <summary>
- /// Waits for any button on the specified message to be pressed by the specified user.
- /// </summary>
- /// <param name="message">The message to wait for the button on.</param>
- /// <param name="user">The user to wait for the button press from.</param>
- /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
- /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
- /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
- public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, DiscordUser user, TimeSpan? timeoutOverride = null)
- => this.WaitForButtonAsync(message, user, this.GetCancellationToken(timeoutOverride));
+ if (!message.Components.Any())
+ throw new ArgumentException("Provided message does not contain any components.");
- /// <summary>
- /// Waits for any button on the specified message to be pressed by the specified user.
- /// </summary>
- /// <param name="message">The message to wait for the button on.</param>
- /// <param name="user">The user to wait for the button press from.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
- /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
- /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
- public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, DiscordUser user, CancellationToken token)
- {
- if (message.Author != this.Client.CurrentUser)
- throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
+ if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
+ throw new ArgumentException("Provided Message does not contain any button components.");
- if (!message.Components.Any())
- throw new ArgumentException("Provided message does not contain any components.");
+ var res = await this._componentEventWaiter
+ .WaitForMatchAsync(new ComponentMatchRequest(message,
+ c =>
+ c.Interaction.Data.ComponentType == ComponentType.Button &&
+ buttons.Any(b => b.CustomId == c.Id), token)).ConfigureAwait(false);
- if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
- throw new ArgumentException("Message does not contain any button components.");
-
- var result = await this
- ._componentEventWaiter
- .WaitForMatchAsync(new ComponentMatchRequest(message, (c) => c.Interaction.Data.ComponentType is ComponentType.Button && c.User == user, token))
- .ConfigureAwait(false);
+ return new InteractivityResult<ComponentInteractionCreateEventArgs>(res is null, res);
+ }
- return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
+ /// <summary>
+ /// Waits for a user modal submit.
+ /// </summary>
+ /// <param name="customId">The custom id of the modal to wait for.</param>
+ /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of the modal.</returns>
+ public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForModalAsync(string customId, TimeSpan? timeoutOverride = null)
+ => this.WaitForModalAsync(customId, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Waits for a user modal submit.
+ /// </summary>
+ /// <param name="customId">The custom id of the modal to wait for.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of the modal.</returns>
+ public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForModalAsync(string customId, CancellationToken token)
+ {
+ var result =
+ await this
+ ._modalEventWaiter
+ .WaitForModalMatchAsync(new ModalMatchRequest(customId, c => c.Interaction.Type == InteractionType.ModalSubmit, token))
+ .ConfigureAwait(false);
- }
+ return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
+ }
- /// <summary>
- /// Waits for a button with the specified Id to be pressed.
- /// </summary>
- /// <param name="message">The message to wait for the button on.</param>
- /// <param name="id">The Id of the button to wait for.</param>
- /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of the operation.</returns>
- /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
- /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
- public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, string id, TimeSpan? timeoutOverride = null)
- => this.WaitForButtonAsync(message, id, this.GetCancellationToken(timeoutOverride));
+ /// <summary>
+ /// Waits for any button on the specified message to be pressed.
+ /// </summary>
+ /// <param name="message">The message to wait for the button on.</param>
+ /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
+ /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
+ public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, TimeSpan? timeoutOverride = null)
+ => this.WaitForButtonAsync(message, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Waits for any button on the specified message to be pressed.
+ /// </summary>
+ /// <param name="message">The message to wait for the button on.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
+ /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
+ public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, CancellationToken token)
+ {
+ if (message.Author != this.Client.CurrentUser)
+ throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
- /// <summary>
- /// Waits for a button with the specified Id to be pressed.
- /// </summary>
- /// <param name="message">The message to wait for the button on.</param>
- /// <param name="id">The Id of the button to wait for.</param>
- /// <param name="token">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
- /// <returns>A <see cref="InteractivityResult{T}"/> with the result of the operation.</returns>
- /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
- /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
- public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, string id, CancellationToken token)
- {
- if (message.Author != this.Client.CurrentUser)
- throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
+ if (!message.Components.Any())
+ throw new ArgumentException("Provided message does not contain any components.");
- if (!message.Components.Any())
- throw new ArgumentException("Provided message does not contain any components.");
+ if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
+ throw new ArgumentException("Message does not contain any button components.");
- if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
- throw new ArgumentException("Message does not contain any button components.");
+ var ids = message.Components.SelectMany(m => m.Components).Select(c => c.CustomId);
- if (!message.Components.SelectMany(c => c.Components).OfType<DiscordButtonComponent>().Any(c => c.CustomId == id))
- throw new ArgumentException($"Message does not contain button with Id of '{id}'.");
- var result = await this
- ._componentEventWaiter
- .WaitForMatchAsync(new ComponentMatchRequest(message, (c) => c.Interaction.Data.ComponentType is ComponentType.Button && c.Id == id, token))
- .ConfigureAwait(false);
+ var result =
+ await this
+ ._componentEventWaiter
+ .WaitForMatchAsync(new ComponentMatchRequest(message, c => c.Interaction.Data.ComponentType == ComponentType.Button && ids.Contains(c.Id), token))
+ .ConfigureAwait(false);
- return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
- }
+ return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
+ }
- /// <summary>
- /// Waits for any button to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="predicate">The predicate to filter interactions by.</param>
- /// <param name="timeoutOverride">Override the timeout specified in <see cref="InteractivityConfiguration"/></param>
- public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, TimeSpan? timeoutOverride = null)
- => this.WaitForButtonAsync(message, predicate, this.GetCancellationToken(timeoutOverride));
+ /// <summary>
+ /// Waits for any button on the specified message to be pressed by the specified user.
+ /// </summary>
+ /// <param name="message">The message to wait for the button on.</param>
+ /// <param name="user">The user to wait for the button press from.</param>
+ /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
+ /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
+ public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, DiscordUser user, TimeSpan? timeoutOverride = null)
+ => this.WaitForButtonAsync(message, user, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Waits for any button on the specified message to be pressed by the specified user.
+ /// </summary>
+ /// <param name="message">The message to wait for the button on.</param>
+ /// <param name="user">The user to wait for the button press from.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of button that was pressed, if any.</returns>
+ /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
+ public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, DiscordUser user, CancellationToken token)
+ {
+ if (message.Author != this.Client.CurrentUser)
+ throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
- /// <summary>
- /// Waits for any button to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="predicate">The predicate to filter interactions by.</param>
- /// <param name="token">A token to cancel interactivity with at any time. Pass <see cref="System.Threading.CancellationToken.None"/> to wait indefinitely.</param>
- public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken token)
- {
- if (message.Author != this.Client.CurrentUser)
- throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
+ if (!message.Components.Any())
+ throw new ArgumentException("Provided message does not contain any components.");
- if (!message.Components.Any())
- throw new ArgumentException("Provided message does not contain any components.");
+ if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
+ throw new ArgumentException("Message does not contain any button components.");
- if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
- throw new ArgumentException("Message does not contain any button components.");
+ var result = await this
+ ._componentEventWaiter
+ .WaitForMatchAsync(new ComponentMatchRequest(message, (c) => c.Interaction.Data.ComponentType is ComponentType.Button && c.User == user, token))
+ .ConfigureAwait(false);
- var result = await this
- ._componentEventWaiter
- .WaitForMatchAsync(new ComponentMatchRequest(message, c => c.Interaction.Data.ComponentType is ComponentType.Button && predicate(c), token))
- .ConfigureAwait(false);
+ return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
- return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
- }
+ }
+ /// <summary>
+ /// Waits for a button with the specified Id to be pressed.
+ /// </summary>
+ /// <param name="message">The message to wait for the button on.</param>
+ /// <param name="id">The Id of the button to wait for.</param>
+ /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of the operation.</returns>
+ /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
+ public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, string id, TimeSpan? timeoutOverride = null)
+ => this.WaitForButtonAsync(message, id, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Waits for a button with the specified Id to be pressed.
+ /// </summary>
+ /// <param name="message">The message to wait for the button on.</param>
+ /// <param name="id">The Id of the button to wait for.</param>
+ /// <param name="token">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
+ /// <returns>A <see cref="InteractivityResult{T}"/> with the result of the operation.</returns>
+ /// <exception cref="System.InvalidOperationException">Thrown when attempting to wait for a message that is not authored by the current user.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not contain a button with the specified Id, or any buttons at all.</exception>
+ public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, string id, CancellationToken token)
+ {
+ if (message.Author != this.Client.CurrentUser)
+ throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
- /// <summary>
- /// Waits for any dropdown to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait for.</param>
- /// <param name="predicate">A filter predicate.</param>
- /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
- /// <exception cref="System.ArgumentException">Thrown when the Provided message does not contain any dropdowns</exception>
- public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, TimeSpan? timeoutOverride = null)
- => this.WaitForSelectAsync(message, predicate, this.GetCancellationToken(timeoutOverride));
+ if (!message.Components.Any())
+ throw new ArgumentException("Provided message does not contain any components.");
- /// <summary>
- /// Waits for any dropdown to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait for.</param>
- /// <param name="predicate">A filter predicate.</param>
- /// <param name="token">A token that can be used to cancel interactivity. Pass <see cref="System.Threading.CancellationToken.None"/> to wait indefinitely.</param>
- /// <exception cref="System.ArgumentException">Thrown when the Provided message does not contain any dropdowns</exception>
- public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken token)
- {
- if (message.Author != this.Client.CurrentUser)
- throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
+ if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
+ throw new ArgumentException("Message does not contain any button components.");
- if (!message.Components.Any())
- throw new ArgumentException("Provided message does not contain any components.");
+ if (!message.Components.SelectMany(c => c.Components).OfType<DiscordButtonComponent>().Any(c => c.CustomId == id))
+ throw new ArgumentException($"Message does not contain button with Id of '{id}'.");
+ var result = await this
+ ._componentEventWaiter
+ .WaitForMatchAsync(new ComponentMatchRequest(message, (c) => c.Interaction.Data.ComponentType is ComponentType.Button && c.Id == id, token))
+ .ConfigureAwait(false);
- if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Select))
- throw new ArgumentException("Message does not contain any select components.");
+ return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
+ }
- var result = await this
- ._componentEventWaiter
- .WaitForMatchAsync(new ComponentMatchRequest(message, c => c.Interaction.Data.ComponentType is ComponentType.Select && predicate(c), token))
- .ConfigureAwait(false);
+ /// <summary>
+ /// Waits for any button to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="predicate">The predicate to filter interactions by.</param>
+ /// <param name="timeoutOverride">Override the timeout specified in <see cref="InteractivityConfiguration"/></param>
+ public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, TimeSpan? timeoutOverride = null)
+ => this.WaitForButtonAsync(message, predicate, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Waits for any button to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="predicate">The predicate to filter interactions by.</param>
+ /// <param name="token">A token to cancel interactivity with at any time. Pass <see cref="System.Threading.CancellationToken.None"/> to wait indefinitely.</param>
+ public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForButtonAsync(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken token)
+ {
+ if (message.Author != this.Client.CurrentUser)
+ throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
- return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
- }
+ if (!message.Components.Any())
+ throw new ArgumentException("Provided message does not contain any components.");
- /// <summary>
- /// Waits for a dropdown to be interacted with.
- /// </summary>
- /// <remarks>This is here for backwards-compatibility and will internally create a cancellation token.</remarks>
- /// <param name="message">The message to wait on.</param>
- /// <param name="id">The Id of the dropdown to wait on.</param>
- /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
- /// <exception cref="System.ArgumentException">Thrown when the message does not have any dropdowns or any dropdown with the specified Id.</exception>
- public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, string id, TimeSpan? timeoutOverride = null)
- => this.WaitForSelectAsync(message, id, this.GetCancellationToken(timeoutOverride));
+ if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Button))
+ throw new ArgumentException("Message does not contain any button components.");
- /// <summary>
- /// Waits for a dropdown to be interacted with.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="id">The Id of the dropdown to wait on.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- /// <exception cref="System.ArgumentException">Thrown when the message does not have any dropdowns or any dropdown with the specified Id.</exception>
- public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, string id, CancellationToken token)
- {
- if (message.Author != this.Client.CurrentUser)
- throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
+ var result = await this
+ ._componentEventWaiter
+ .WaitForMatchAsync(new ComponentMatchRequest(message, c => c.Interaction.Data.ComponentType is ComponentType.Button && predicate(c), token))
+ .ConfigureAwait(false);
- if (!message.Components.Any())
- throw new ArgumentException("Provided message does not contain any components.");
+ return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
+ }
- if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Select))
- throw new ArgumentException("Message does not contain any select components.");
- if (message.Components.SelectMany(c => c.Components).OfType<DiscordSelectComponent>().All(c => c.CustomId != id))
- throw new ArgumentException($"Message does not contain select component with Id of '{id}'.");
+ /// <summary>
+ /// Waits for any dropdown to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait for.</param>
+ /// <param name="predicate">A filter predicate.</param>
+ /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the Provided message does not contain any dropdowns</exception>
+ public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, TimeSpan? timeoutOverride = null)
+ => this.WaitForSelectAsync(message, predicate, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Waits for any dropdown to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait for.</param>
+ /// <param name="predicate">A filter predicate.</param>
+ /// <param name="token">A token that can be used to cancel interactivity. Pass <see cref="System.Threading.CancellationToken.None"/> to wait indefinitely.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the Provided message does not contain any dropdowns</exception>
+ public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, Func<ComponentInteractionCreateEventArgs, bool> predicate, CancellationToken token)
+ {
+ if (message.Author != this.Client.CurrentUser)
+ throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
- var result = await this
- ._componentEventWaiter
- .WaitForMatchAsync(new ComponentMatchRequest(message, (c) => c.Interaction.Data.ComponentType is ComponentType.Select && c.Id == id, token))
- .ConfigureAwait(false);
+ if (!message.Components.Any())
+ throw new ArgumentException("Provided message does not contain any components.");
- return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
- }
+ if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Select))
+ throw new ArgumentException("Message does not contain any select components.");
- /// <summary>
- /// Waits for a dropdown to be interacted with by a specific user.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="user">The user to wait on.</param>
- /// <param name="id">The Id of the dropdown to wait on.</param>
- /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
- /// <exception cref="System.ArgumentException">Thrown when the message does not have any dropdowns or any dropdown with the specified Id.</exception>
- public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, DiscordUser user, string id, TimeSpan? timeoutOverride = null)
- => this.WaitForSelectAsync(message, user, id, this.GetCancellationToken(timeoutOverride));
+ var result = await this
+ ._componentEventWaiter
+ .WaitForMatchAsync(new ComponentMatchRequest(message, c => c.Interaction.Data.ComponentType is ComponentType.Select && predicate(c), token))
+ .ConfigureAwait(false);
- /// <summary>
- /// Waits for a dropdown to be interacted with by a specific user.
- /// </summary>
- /// <param name="message">The message to wait on.</param>
- /// <param name="user">The user to wait on.</param>
- /// <param name="id">The Id of the dropdown to wait on.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- /// <exception cref="System.ArgumentException">Thrown when the message does not have any dropdowns or any dropdown with the specified Id.</exception>
- public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, DiscordUser user, string id, CancellationToken token)
- {
- if (message.Author != this.Client.CurrentUser)
- throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
+ return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
+ }
- if (!message.Components.Any())
- throw new ArgumentException("Provided message does not contain any components.");
+ /// <summary>
+ /// Waits for a dropdown to be interacted with.
+ /// </summary>
+ /// <remarks>This is here for backwards-compatibility and will internally create a cancellation token.</remarks>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="id">The Id of the dropdown to wait on.</param>
+ /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not have any dropdowns or any dropdown with the specified Id.</exception>
+ public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, string id, TimeSpan? timeoutOverride = null)
+ => this.WaitForSelectAsync(message, id, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Waits for a dropdown to be interacted with.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="id">The Id of the dropdown to wait on.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not have any dropdowns or any dropdown with the specified Id.</exception>
+ public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, string id, CancellationToken token)
+ {
+ if (message.Author != this.Client.CurrentUser)
+ throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
- if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Select))
- throw new ArgumentException("Message does not contain any select components.");
+ if (!message.Components.Any())
+ throw new ArgumentException("Provided message does not contain any components.");
- if (message.Components.SelectMany(c => c.Components).OfType<DiscordSelectComponent>().All(c => c.CustomId != id))
- throw new ArgumentException($"Message does not contain select with Id of '{id}'.");
+ if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Select))
+ throw new ArgumentException("Message does not contain any select components.");
- var result = await this
- ._componentEventWaiter
- .WaitForMatchAsync(new ComponentMatchRequest(message, (c) => c.Id == id && c.User == user, token)).ConfigureAwait(false);
+ if (message.Components.SelectMany(c => c.Components).OfType<DiscordSelectComponent>().All(c => c.CustomId != id))
+ throw new ArgumentException($"Message does not contain select component with Id of '{id}'.");
- return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
- }
+ var result = await this
+ ._componentEventWaiter
+ .WaitForMatchAsync(new ComponentMatchRequest(message, (c) => c.Interaction.Data.ComponentType is ComponentType.Select && c.Id == id, token))
+ .ConfigureAwait(false);
+ return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
+ }
- /// <summary>
- /// Waits for a specific message.
- /// </summary>
- /// <param name="predicate">Predicate to match.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<InteractivityResult<DiscordMessage>> WaitForMessageAsync(Func<DiscordMessage, bool> predicate,
- TimeSpan? timeoutOverride = null)
- {
- if (!Utilities.HasMessageIntents(this.Client.Configuration.Intents))
- throw new InvalidOperationException("No message intents are enabled.");
+ /// <summary>
+ /// Waits for a dropdown to be interacted with by a specific user.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="user">The user to wait on.</param>
+ /// <param name="id">The Id of the dropdown to wait on.</param>
+ /// <param name="timeoutOverride">Override the timeout period specified in <see cref="InteractivityConfiguration"/>.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not have any dropdowns or any dropdown with the specified Id.</exception>
+ public Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, DiscordUser user, string id, TimeSpan? timeoutOverride = null)
+ => this.WaitForSelectAsync(message, user, id, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Waits for a dropdown to be interacted with by a specific user.
+ /// </summary>
+ /// <param name="message">The message to wait on.</param>
+ /// <param name="user">The user to wait on.</param>
+ /// <param name="id">The Id of the dropdown to wait on.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the message does not have any dropdowns or any dropdown with the specified Id.</exception>
+ public async Task<InteractivityResult<ComponentInteractionCreateEventArgs>> WaitForSelectAsync(DiscordMessage message, DiscordUser user, string id, CancellationToken token)
+ {
+ if (message.Author != this.Client.CurrentUser)
+ throw new InvalidOperationException("Interaction events are only sent to the application that created them.");
- var timeout = timeoutOverride ?? this.Config.Timeout;
- var returns = await this._messageCreatedWaiter.WaitForMatchAsync(new MatchRequest<MessageCreateEventArgs>(x => predicate(x.Message), timeout)).ConfigureAwait(false);
+ if (!message.Components.Any())
+ throw new ArgumentException("Provided message does not contain any components.");
- return new InteractivityResult<DiscordMessage>(returns == null, returns?.Message);
- }
+ if (!message.Components.SelectMany(c => c.Components).Any(c => c.Type is ComponentType.Select))
+ throw new ArgumentException("Message does not contain any select components.");
- /// <summary>
- /// Wait for a specific reaction.
- /// </summary>
- /// <param name="predicate">Predicate to match.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(Func<MessageReactionAddEventArgs, bool> predicate,
- TimeSpan? timeoutOverride = null)
- {
- if (!Utilities.HasReactionIntents(this.Client.Configuration.Intents))
- throw new InvalidOperationException("No reaction intents are enabled.");
+ if (message.Components.SelectMany(c => c.Components).OfType<DiscordSelectComponent>().All(c => c.CustomId != id))
+ throw new ArgumentException($"Message does not contain select with Id of '{id}'.");
- var timeout = timeoutOverride ?? this.Config.Timeout;
- var returns = await this._messageReactionAddWaiter.WaitForMatchAsync(new MatchRequest<MessageReactionAddEventArgs>(predicate, timeout)).ConfigureAwait(false);
+ var result = await this
+ ._componentEventWaiter
+ .WaitForMatchAsync(new ComponentMatchRequest(message, (c) => c.Id == id && c.User == user, token)).ConfigureAwait(false);
- return new InteractivityResult<MessageReactionAddEventArgs>(returns == null, returns);
- }
+ return new InteractivityResult<ComponentInteractionCreateEventArgs>(result is null, result);
+ }
- /// <summary>
- /// Wait for a specific reaction.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- /// <param name="message">Message reaction was added to.</param>
- /// <param name="user">User that made the reaction.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(DiscordMessage message, DiscordUser user,
- TimeSpan? timeoutOverride = null)
- => await this.WaitForReactionAsync(x => x.User.Id == user.Id && x.Message.Id == message.Id, timeoutOverride).ConfigureAwait(false);
- /// <summary>
- /// Waits for a specific reaction.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- /// <param name="predicate">Predicate to match.</param>
- /// <param name="message">Message reaction was added to.</param>
- /// <param name="user">User that made the reaction.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(Func<MessageReactionAddEventArgs, bool> predicate,
- DiscordMessage message, DiscordUser user, TimeSpan? timeoutOverride = null)
- => await this.WaitForReactionAsync(x => predicate(x) && x.User.Id == user.Id && x.Message.Id == message.Id, timeoutOverride).ConfigureAwait(false);
-
- /// <summary>
- /// Waits for a specific reaction.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- /// <param name="predicate">predicate to match.</param>
- /// <param name="user">User that made the reaction.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(Func<MessageReactionAddEventArgs, bool> predicate,
- DiscordUser user, TimeSpan? timeoutOverride = null)
- => await this.WaitForReactionAsync(x => predicate(x) && x.User.Id == user.Id, timeoutOverride).ConfigureAwait(false);
+ /// <summary>
+ /// Waits for a specific message.
+ /// </summary>
+ /// <param name="predicate">Predicate to match.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<InteractivityResult<DiscordMessage>> WaitForMessageAsync(Func<DiscordMessage, bool> predicate,
+ TimeSpan? timeoutOverride = null)
+ {
+ if (!Utilities.HasMessageIntents(this.Client.Configuration.Intents))
+ throw new InvalidOperationException("No message intents are enabled.");
- /// <summary>
- /// Waits for a user to start typing.
- /// </summary>
- /// <param name="user">User that starts typing.</param>
- /// <param name="channel">Channel the user is typing in.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<InteractivityResult<TypingStartEventArgs>> WaitForUserTypingAsync(DiscordUser user,
- DiscordChannel channel, TimeSpan? timeoutOverride = null)
- {
- if (!Utilities.HasTypingIntents(this.Client.Configuration.Intents))
- throw new InvalidOperationException("No typing intents are enabled.");
+ var timeout = timeoutOverride ?? this.Config.Timeout;
+ var returns = await this._messageCreatedWaiter.WaitForMatchAsync(new MatchRequest<MessageCreateEventArgs>(x => predicate(x.Message), timeout)).ConfigureAwait(false);
- var timeout = timeoutOverride ?? this.Config.Timeout;
- var returns = await this._typingStartWaiter.WaitForMatchAsync(
- new MatchRequest<TypingStartEventArgs>(x => x.User.Id == user.Id && x.Channel.Id == channel.Id, timeout))
- .ConfigureAwait(false);
+ return new InteractivityResult<DiscordMessage>(returns == null, returns?.Message);
+ }
- return new InteractivityResult<TypingStartEventArgs>(returns == null, returns);
- }
+ /// <summary>
+ /// Wait for a specific reaction.
+ /// </summary>
+ /// <param name="predicate">Predicate to match.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(Func<MessageReactionAddEventArgs, bool> predicate,
+ TimeSpan? timeoutOverride = null)
+ {
+ if (!Utilities.HasReactionIntents(this.Client.Configuration.Intents))
+ throw new InvalidOperationException("No reaction intents are enabled.");
- /// <summary>
- /// Waits for a user to start typing.
- /// </summary>
- /// <param name="user">User that starts typing.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<InteractivityResult<TypingStartEventArgs>> WaitForUserTypingAsync(DiscordUser user, TimeSpan? timeoutOverride = null)
- {
- if (!Utilities.HasTypingIntents(this.Client.Configuration.Intents))
- throw new InvalidOperationException("No typing intents are enabled.");
+ var timeout = timeoutOverride ?? this.Config.Timeout;
+ var returns = await this._messageReactionAddWaiter.WaitForMatchAsync(new MatchRequest<MessageReactionAddEventArgs>(predicate, timeout)).ConfigureAwait(false);
- var timeout = timeoutOverride ?? this.Config.Timeout;
- var returns = await this._typingStartWaiter.WaitForMatchAsync(
- new MatchRequest<TypingStartEventArgs>(x => x.User.Id == user.Id, timeout))
- .ConfigureAwait(false);
+ return new InteractivityResult<MessageReactionAddEventArgs>(returns == null, returns);
+ }
- return new InteractivityResult<TypingStartEventArgs>(returns == null, returns);
- }
+ /// <summary>
+ /// Wait for a specific reaction.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ /// <param name="message">Message reaction was added to.</param>
+ /// <param name="user">User that made the reaction.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(DiscordMessage message, DiscordUser user,
+ TimeSpan? timeoutOverride = null)
+ => await this.WaitForReactionAsync(x => x.User.Id == user.Id && x.Message.Id == message.Id, timeoutOverride).ConfigureAwait(false);
+
+ /// <summary>
+ /// Waits for a specific reaction.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ /// <param name="predicate">Predicate to match.</param>
+ /// <param name="message">Message reaction was added to.</param>
+ /// <param name="user">User that made the reaction.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(Func<MessageReactionAddEventArgs, bool> predicate,
+ DiscordMessage message, DiscordUser user, TimeSpan? timeoutOverride = null)
+ => await this.WaitForReactionAsync(x => predicate(x) && x.User.Id == user.Id && x.Message.Id == message.Id, timeoutOverride).ConfigureAwait(false);
+
+ /// <summary>
+ /// Waits for a specific reaction.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ /// <param name="predicate">predicate to match.</param>
+ /// <param name="user">User that made the reaction.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<InteractivityResult<MessageReactionAddEventArgs>> WaitForReactionAsync(Func<MessageReactionAddEventArgs, bool> predicate,
+ DiscordUser user, TimeSpan? timeoutOverride = null)
+ => await this.WaitForReactionAsync(x => predicate(x) && x.User.Id == user.Id, timeoutOverride).ConfigureAwait(false);
+
+ /// <summary>
+ /// Waits for a user to start typing.
+ /// </summary>
+ /// <param name="user">User that starts typing.</param>
+ /// <param name="channel">Channel the user is typing in.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<InteractivityResult<TypingStartEventArgs>> WaitForUserTypingAsync(DiscordUser user,
+ DiscordChannel channel, TimeSpan? timeoutOverride = null)
+ {
+ if (!Utilities.HasTypingIntents(this.Client.Configuration.Intents))
+ throw new InvalidOperationException("No typing intents are enabled.");
- /// <summary>
- /// Waits for any user to start typing.
- /// </summary>
- /// <param name="channel">Channel to type in.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<InteractivityResult<TypingStartEventArgs>> WaitForTypingAsync(DiscordChannel channel, TimeSpan? timeoutOverride = null)
- {
- if (!Utilities.HasTypingIntents(this.Client.Configuration.Intents))
- throw new InvalidOperationException("No typing intents are enabled.");
+ var timeout = timeoutOverride ?? this.Config.Timeout;
+ var returns = await this._typingStartWaiter.WaitForMatchAsync(
+ new MatchRequest<TypingStartEventArgs>(x => x.User.Id == user.Id && x.Channel.Id == channel.Id, timeout))
+ .ConfigureAwait(false);
- var timeout = timeoutOverride ?? this.Config.Timeout;
- var returns = await this._typingStartWaiter.WaitForMatchAsync(
- new MatchRequest<TypingStartEventArgs>(x => x.Channel.Id == channel.Id, timeout))
- .ConfigureAwait(false);
+ return new InteractivityResult<TypingStartEventArgs>(returns == null, returns);
+ }
- return new InteractivityResult<TypingStartEventArgs>(returns == null, returns);
- }
+ /// <summary>
+ /// Waits for a user to start typing.
+ /// </summary>
+ /// <param name="user">User that starts typing.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<InteractivityResult<TypingStartEventArgs>> WaitForUserTypingAsync(DiscordUser user, TimeSpan? timeoutOverride = null)
+ {
+ if (!Utilities.HasTypingIntents(this.Client.Configuration.Intents))
+ throw new InvalidOperationException("No typing intents are enabled.");
- /// <summary>
- /// Collects reactions on a specific message.
- /// </summary>
- /// <param name="m">Message to collect reactions on.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<ReadOnlyCollection<Reaction>> CollectReactionsAsync(DiscordMessage m, TimeSpan? timeoutOverride = null)
- {
- if (!Utilities.HasReactionIntents(this.Client.Configuration.Intents))
- throw new InvalidOperationException("No reaction intents are enabled.");
+ var timeout = timeoutOverride ?? this.Config.Timeout;
+ var returns = await this._typingStartWaiter.WaitForMatchAsync(
+ new MatchRequest<TypingStartEventArgs>(x => x.User.Id == user.Id, timeout))
+ .ConfigureAwait(false);
- var timeout = timeoutOverride ?? this.Config.Timeout;
- var collection = await this._reactionCollector.CollectAsync(new ReactionCollectRequest(m, timeout)).ConfigureAwait(false);
+ return new InteractivityResult<TypingStartEventArgs>(returns == null, returns);
+ }
- return collection;
- }
+ /// <summary>
+ /// Waits for any user to start typing.
+ /// </summary>
+ /// <param name="channel">Channel to type in.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<InteractivityResult<TypingStartEventArgs>> WaitForTypingAsync(DiscordChannel channel, TimeSpan? timeoutOverride = null)
+ {
+ if (!Utilities.HasTypingIntents(this.Client.Configuration.Intents))
+ throw new InvalidOperationException("No typing intents are enabled.");
- /// <summary>
- /// Waits for specific event args to be received. Make sure the appropriate <see cref="DiscordIntents"/> are registered, if needed.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="predicate">The predicate.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<InteractivityResult<T>> WaitForEventArgsAsync<T>(Func<T, bool> predicate, TimeSpan? timeoutOverride = null) where T : AsyncEventArgs
- {
- var timeout = timeoutOverride ?? this.Config.Timeout;
+ var timeout = timeoutOverride ?? this.Config.Timeout;
+ var returns = await this._typingStartWaiter.WaitForMatchAsync(
+ new MatchRequest<TypingStartEventArgs>(x => x.Channel.Id == channel.Id, timeout))
+ .ConfigureAwait(false);
- using var waiter = new EventWaiter<T>(this.Client);
- var res = await waiter.WaitForMatchAsync(new MatchRequest<T>(predicate, timeout)).ConfigureAwait(false);
- return new InteractivityResult<T>(res == null, res);
- }
+ return new InteractivityResult<TypingStartEventArgs>(returns == null, returns);
+ }
- /// <summary>
- /// Collects the event arguments.
- /// </summary>
- /// <param name="predicate">The predicate.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task<ReadOnlyCollection<T>> CollectEventArgsAsync<T>(Func<T, bool> predicate, TimeSpan? timeoutOverride = null) where T : AsyncEventArgs
- {
- var timeout = timeoutOverride ?? this.Config.Timeout;
+ /// <summary>
+ /// Collects reactions on a specific message.
+ /// </summary>
+ /// <param name="m">Message to collect reactions on.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<ReadOnlyCollection<Reaction>> CollectReactionsAsync(DiscordMessage m, TimeSpan? timeoutOverride = null)
+ {
+ if (!Utilities.HasReactionIntents(this.Client.Configuration.Intents))
+ throw new InvalidOperationException("No reaction intents are enabled.");
- using var waiter = new EventWaiter<T>(this.Client);
- var res = await waiter.CollectMatchesAsync(new CollectRequest<T>(predicate, timeout)).ConfigureAwait(false);
- return res;
- }
+ var timeout = timeoutOverride ?? this.Config.Timeout;
+ var collection = await this._reactionCollector.CollectAsync(new ReactionCollectRequest(m, timeout)).ConfigureAwait(false);
- /// <summary>
- /// Sends a paginated message with buttons.
- /// </summary>
- /// <param name="channel">The channel to send it on.</param>
- /// <param name="user">User to give control.</param>
- /// <param name="pages">The pages.</param>
- /// <param name="buttons">Pagination buttons (pass null to use buttons defined in <see cref="InteractivityConfiguration"/>).</param>
- /// <param name="behaviour">Pagination behaviour.</param>
- /// <param name="deletion">Deletion behaviour.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- public async Task SendPaginatedMessageAsync(
- DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons,
- PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
- {
- var bhv = behaviour ?? this.Config.PaginationBehaviour;
- var del = deletion ?? this.Config.ButtonBehavior;
- var bts = buttons ?? this.Config.PaginationButtons;
+ return collection;
+ }
- bts = new PaginationButtons(bts);
- if (bhv is PaginationBehaviour.Ignore)
+ /// <summary>
+ /// Waits for specific event args to be received. Make sure the appropriate <see cref="DiscordIntents"/> are registered, if needed.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="predicate">The predicate.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<InteractivityResult<T>> WaitForEventArgsAsync<T>(Func<T, bool> predicate, TimeSpan? timeoutOverride = null) where T : AsyncEventArgs
{
- bts.SkipLeft.Disable();
- bts.Left.Disable();
+ var timeout = timeoutOverride ?? this.Config.Timeout;
+
+ using var waiter = new EventWaiter<T>(this.Client);
+ var res = await waiter.WaitForMatchAsync(new MatchRequest<T>(predicate, timeout)).ConfigureAwait(false);
+ return new InteractivityResult<T>(res == null, res);
}
- var builder = new DiscordMessageBuilder()
- .WithContent(pages.First().Content)
- .WithEmbed(pages.First().Embed)
- .AddComponents(bts.ButtonArray);
+ /// <summary>
+ /// Collects the event arguments.
+ /// </summary>
+ /// <param name="predicate">The predicate.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task<ReadOnlyCollection<T>> CollectEventArgsAsync<T>(Func<T, bool> predicate, TimeSpan? timeoutOverride = null) where T : AsyncEventArgs
+ {
+ var timeout = timeoutOverride ?? this.Config.Timeout;
- var message = await builder.SendAsync(channel).ConfigureAwait(false);
+ using var waiter = new EventWaiter<T>(this.Client);
+ var res = await waiter.CollectMatchesAsync(new CollectRequest<T>(predicate, timeout)).ConfigureAwait(false);
+ return res;
+ }
- var req = new ButtonPaginationRequest(message, user, bhv, del, bts, pages.ToArray(), token == default ? this.GetCancellationToken() : token);
+ /// <summary>
+ /// Sends a paginated message with buttons.
+ /// </summary>
+ /// <param name="channel">The channel to send it on.</param>
+ /// <param name="user">User to give control.</param>
+ /// <param name="pages">The pages.</param>
+ /// <param name="buttons">Pagination buttons (pass null to use buttons defined in <see cref="InteractivityConfiguration"/>).</param>
+ /// <param name="behaviour">Pagination behaviour.</param>
+ /// <param name="deletion">Deletion behaviour.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ public async Task SendPaginatedMessageAsync(
+ DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons,
+ PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
+ {
+ var bhv = behaviour ?? this.Config.PaginationBehaviour;
+ var del = deletion ?? this.Config.ButtonBehavior;
+ var bts = buttons ?? this.Config.PaginationButtons;
- await this._compPaginator.DoPaginationAsync(req).ConfigureAwait(false);
- }
+ bts = new PaginationButtons(bts);
+ if (bhv is PaginationBehaviour.Ignore)
+ {
+ bts.SkipLeft.Disable();
+ bts.Left.Disable();
+ }
- /// <summary>
- /// Sends a paginated message with buttons.
- /// </summary>
- /// <param name="channel">The channel to send it on.</param>
- /// <param name="user">User to give control.</param>
- /// <param name="pages">The pages.</param>
- /// <param name="buttons">Pagination buttons (pass null to use buttons defined in <see cref="InteractivityConfiguration"/>).</param>
- /// <param name="behaviour">Pagination behaviour.</param>
- /// <param name="deletion">Deletion behaviour.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public Task SendPaginatedMessageAsync(
- DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons, TimeSpan? timeoutOverride,
- PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default)
- => this.SendPaginatedMessageAsync(channel, user, pages, buttons, behaviour, deletion, this.GetCancellationToken(timeoutOverride));
+ var builder = new DiscordMessageBuilder()
+ .WithContent(pages.First().Content)
+ .WithEmbed(pages.First().Embed)
+ .AddComponents(bts.ButtonArray);
- /// <summary>
- /// Sends the paginated message.
- /// </summary>
- /// <param name="channel">The channel.</param>
- /// <param name="user">The user.</param>
- /// <param name="pages">The pages.</param>
- /// <param name="behaviour">The behaviour.</param>
- /// <param name="deletion">The deletion.</param>
- /// <param name="token">The token.</param>
- /// <returns>A Task.</returns>
- public Task SendPaginatedMessageAsync(DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
- => this.SendPaginatedMessageAsync(channel, user, pages, default, behaviour, deletion, token);
+ var message = await builder.SendAsync(channel).ConfigureAwait(false);
- /// <summary>
- /// Sends the paginated message.
- /// </summary>
- /// <param name="channel">The channel.</param>
- /// <param name="user">The user.</param>
- /// <param name="pages">The pages.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- /// <param name="behaviour">The behaviour.</param>
- /// <param name="deletion">The deletion.</param>
- /// <returns>A Task.</returns>
- public Task SendPaginatedMessageAsync(DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, TimeSpan? timeoutOverride, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default)
- => this.SendPaginatedMessageAsync(channel, user, pages, timeoutOverride, behaviour, deletion);
+ var req = new ButtonPaginationRequest(message, user, bhv, del, bts, pages.ToArray(), token == default ? this.GetCancellationToken() : token);
- /// <summary>
- /// Sends a paginated message.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- /// <param name="channel">Channel to send paginated message in.</param>
- /// <param name="user">User to give control.</param>
- /// <param name="pages">Pages.</param>
- /// <param name="emojis">Pagination emojis.</param>
- /// <param name="behaviour">Pagination behaviour (when hitting max and min indices).</param>
- /// <param name="deletion">Deletion behaviour.</param>
- /// <param name="timeoutOverride">Override timeout period.</param>
- public async Task SendPaginatedMessageAsync(DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationEmojis emojis,
- PaginationBehaviour? behaviour = default, PaginationDeletion? deletion = default, TimeSpan? timeoutOverride = null)
- {
- var builder = new DiscordMessageBuilder()
- .WithContent(pages.First().Content)
- .WithEmbed(pages.First().Embed);
- var m = await builder.SendAsync(channel).ConfigureAwait(false);
+ await this._compPaginator.DoPaginationAsync(req).ConfigureAwait(false);
+ }
- var timeout = timeoutOverride ?? this.Config.Timeout;
+ /// <summary>
+ /// Sends a paginated message with buttons.
+ /// </summary>
+ /// <param name="channel">The channel to send it on.</param>
+ /// <param name="user">User to give control.</param>
+ /// <param name="pages">The pages.</param>
+ /// <param name="buttons">Pagination buttons (pass null to use buttons defined in <see cref="InteractivityConfiguration"/>).</param>
+ /// <param name="behaviour">Pagination behaviour.</param>
+ /// <param name="deletion">Deletion behaviour.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public Task SendPaginatedMessageAsync(
+ DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons, TimeSpan? timeoutOverride,
+ PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default)
+ => this.SendPaginatedMessageAsync(channel, user, pages, buttons, behaviour, deletion, this.GetCancellationToken(timeoutOverride));
+
+ /// <summary>
+ /// Sends the paginated message.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ /// <param name="user">The user.</param>
+ /// <param name="pages">The pages.</param>
+ /// <param name="behaviour">The behaviour.</param>
+ /// <param name="deletion">The deletion.</param>
+ /// <param name="token">The token.</param>
+ /// <returns>A Task.</returns>
+ public Task SendPaginatedMessageAsync(DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
+ => this.SendPaginatedMessageAsync(channel, user, pages, default, behaviour, deletion, token);
+
+ /// <summary>
+ /// Sends the paginated message.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ /// <param name="user">The user.</param>
+ /// <param name="pages">The pages.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ /// <param name="behaviour">The behaviour.</param>
+ /// <param name="deletion">The deletion.</param>
+ /// <returns>A Task.</returns>
+ public Task SendPaginatedMessageAsync(DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, TimeSpan? timeoutOverride, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default)
+ => this.SendPaginatedMessageAsync(channel, user, pages, timeoutOverride, behaviour, deletion);
+
+ /// <summary>
+ /// Sends a paginated message.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ /// <param name="channel">Channel to send paginated message in.</param>
+ /// <param name="user">User to give control.</param>
+ /// <param name="pages">Pages.</param>
+ /// <param name="emojis">Pagination emojis.</param>
+ /// <param name="behaviour">Pagination behaviour (when hitting max and min indices).</param>
+ /// <param name="deletion">Deletion behaviour.</param>
+ /// <param name="timeoutOverride">Override timeout period.</param>
+ public async Task SendPaginatedMessageAsync(DiscordChannel channel, DiscordUser user, IEnumerable<Page> pages, PaginationEmojis emojis,
+ PaginationBehaviour? behaviour = default, PaginationDeletion? deletion = default, TimeSpan? timeoutOverride = null)
+ {
+ var builder = new DiscordMessageBuilder()
+ .WithContent(pages.First().Content)
+ .WithEmbed(pages.First().Embed);
+ var m = await builder.SendAsync(channel).ConfigureAwait(false);
- var bhv = behaviour ?? this.Config.PaginationBehaviour;
- var del = deletion ?? this.Config.PaginationDeletion;
- var ems = emojis ?? this.Config.PaginationEmojis;
+ var timeout = timeoutOverride ?? this.Config.Timeout;
- var pRequest = new PaginationRequest(m, user, bhv, del, ems, timeout, pages.ToArray());
+ var bhv = behaviour ?? this.Config.PaginationBehaviour;
+ var del = deletion ?? this.Config.PaginationDeletion;
+ var ems = emojis ?? this.Config.PaginationEmojis;
- await this._paginator.DoPaginationAsync(pRequest).ConfigureAwait(false);
- }
+ var pRequest = new PaginationRequest(m, user, bhv, del, ems, timeout, pages.ToArray());
- /// <summary>
- /// Sends a paginated message in response to an interaction.
- /// <para>
- /// <b>Pass the interaction directly. Interactivity will ACK it.</b>
- /// </para>
- /// </summary>
- /// <param name="interaction">The interaction to create a response to.</param>
- /// <param name="ephemeral">Whether the response should be ephemeral.</param>
- /// <param name="user">The user to listen for button presses from.</param>
- /// <param name="pages">The pages to paginate.</param>
- /// <param name="buttons">Optional: custom buttons.</param>
- /// <param name="behaviour">Pagination behaviour.</param>
- /// <param name="deletion">Deletion behaviour.</param>
- /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
- public async Task SendPaginatedResponseAsync(DiscordInteraction interaction, bool ephemeral, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons = null, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
- {
- var bhv = behaviour ?? this.Config.PaginationBehaviour;
- var del = deletion ?? this.Config.ButtonBehavior;
- var bts = buttons ?? this.Config.PaginationButtons;
+ await this._paginator.DoPaginationAsync(pRequest).ConfigureAwait(false);
+ }
- bts = new PaginationButtons(bts);
- if (bhv is PaginationBehaviour.Ignore)
+ /// <summary>
+ /// Sends a paginated message in response to an interaction.
+ /// <para>
+ /// <b>Pass the interaction directly. Interactivity will ACK it.</b>
+ /// </para>
+ /// </summary>
+ /// <param name="interaction">The interaction to create a response to.</param>
+ /// <param name="ephemeral">Whether the response should be ephemeral.</param>
+ /// <param name="user">The user to listen for button presses from.</param>
+ /// <param name="pages">The pages to paginate.</param>
+ /// <param name="buttons">Optional: custom buttons.</param>
+ /// <param name="behaviour">Pagination behaviour.</param>
+ /// <param name="deletion">Deletion behaviour.</param>
+ /// <param name="token">A custom cancellation token that can be cancelled at any point.</param>
+ public async Task SendPaginatedResponseAsync(DiscordInteraction interaction, bool ephemeral, DiscordUser user, IEnumerable<Page> pages, PaginationButtons buttons = null, PaginationBehaviour? behaviour = default, ButtonPaginationBehavior? deletion = default, CancellationToken token = default)
{
- bts.SkipLeft.Disable();
- bts.Left.Disable();
- }
+ var bhv = behaviour ?? this.Config.PaginationBehaviour;
+ var del = deletion ?? this.Config.ButtonBehavior;
+ var bts = buttons ?? this.Config.PaginationButtons;
- var builder = new DiscordInteractionResponseBuilder()
- .WithContent(pages.First().Content)
- .AddEmbed(pages.First().Embed)
- .AsEphemeral(ephemeral)
- .AddComponents(bts.ButtonArray);
+ bts = new PaginationButtons(bts);
+ if (bhv is PaginationBehaviour.Ignore)
+ {
+ bts.SkipLeft.Disable();
+ bts.Left.Disable();
+ }
- await interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder).ConfigureAwait(false);
- var message = await interaction.GetOriginalResponseAsync().ConfigureAwait(false);
+ var builder = new DiscordInteractionResponseBuilder()
+ .WithContent(pages.First().Content)
+ .AddEmbed(pages.First().Embed)
+ .AsEphemeral(ephemeral)
+ .AddComponents(bts.ButtonArray);
- var req = new InteractionPaginationRequest(interaction, message, user, bhv, del, bts, pages, token);
+ await interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder).ConfigureAwait(false);
+ var message = await interaction.GetOriginalResponseAsync().ConfigureAwait(false);
- await this._compPaginator.DoPaginationAsync(req).ConfigureAwait(false);
- }
+ var req = new InteractionPaginationRequest(interaction, message, user, bhv, del, bts, pages, token);
+ await this._compPaginator.DoPaginationAsync(req).ConfigureAwait(false);
+ }
- /// <summary>
- /// Waits for a custom pagination request to finish.
- /// This does NOT handle removing emojis after finishing for you.
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- public async Task WaitForCustomPaginationAsync(IPaginationRequest request) => await this._paginator.DoPaginationAsync(request).ConfigureAwait(false);
- /// <summary>
- /// Waits for custom button-based pagination request to finish.
- /// <br/>
- /// This does <i><b>not</b></i> invoke <see cref="DisCatSharp.Interactivity.EventHandling.IPaginationRequest.DoCleanupAsync"/>.
- /// </summary>
- /// <param name="request">The request to wait for.</param>
- public async Task WaitForCustomComponentPaginationAsync(IPaginationRequest request) => await this._compPaginator.DoPaginationAsync(request).ConfigureAwait(false);
+ /// <summary>
+ /// Waits for a custom pagination request to finish.
+ /// This does NOT handle removing emojis after finishing for you.
+ /// </summary>
+ /// <param name="request"></param>
+ /// <returns></returns>
+ public async Task WaitForCustomPaginationAsync(IPaginationRequest request) => await this._paginator.DoPaginationAsync(request).ConfigureAwait(false);
+
+ /// <summary>
+ /// Waits for custom button-based pagination request to finish.
+ /// <br/>
+ /// This does <i><b>not</b></i> invoke <see cref="DisCatSharp.Interactivity.EventHandling.IPaginationRequest.DoCleanupAsync"/>.
+ /// </summary>
+ /// <param name="request">The request to wait for.</param>
+ public async Task WaitForCustomComponentPaginationAsync(IPaginationRequest request) => await this._compPaginator.DoPaginationAsync(request).ConfigureAwait(false);
+
+ /// <summary>
+ /// Generates pages from a string, and puts them in message content.
+ /// </summary>
+ /// <param name="input">Input string.</param>
+ /// <param name="splitType">How to split input string.</param>
+ /// <returns></returns>
+ public IEnumerable<Page> GeneratePagesInContent(string input, SplitType splitType = SplitType.Character)
+ {
+ if (string.IsNullOrEmpty(input))
+ throw new ArgumentException("You must provide a string that is not null or empty!");
- /// <summary>
- /// Generates pages from a string, and puts them in message content.
- /// </summary>
- /// <param name="input">Input string.</param>
- /// <param name="splitType">How to split input string.</param>
- /// <returns></returns>
- public IEnumerable<Page> GeneratePagesInContent(string input, SplitType splitType = SplitType.Character)
- {
- if (string.IsNullOrEmpty(input))
- throw new ArgumentException("You must provide a string that is not null or empty!");
+ var result = new List<Page>();
+ List<string> split;
- var result = new List<Page>();
- List<string> split;
+ switch (splitType)
+ {
+ default:
+ case SplitType.Character:
+ split = this.SplitString(input, 500).ToList();
+ break;
+ case SplitType.Line:
+ var subsplit = input.Split('\n');
- switch (splitType)
- {
- default:
- case SplitType.Character:
- split = this.SplitString(input, 500).ToList();
- break;
- case SplitType.Line:
- var subsplit = input.Split('\n');
-
- split = new List<string>();
- var s = "";
-
- for (var i = 0; i < subsplit.Length; i++)
- {
- s += subsplit[i];
- if (i >= 15 && i % 15 == 0)
+ split = new List<string>();
+ var s = "";
+
+ for (var i = 0; i < subsplit.Length; i++)
{
- split.Add(s);
- s = "";
+ s += subsplit[i];
+ if (i >= 15 && i % 15 == 0)
+ {
+ split.Add(s);
+ s = "";
+ }
}
- }
- if (split.All(x => x != s))
- split.Add(s);
- break;
+ if (split.All(x => x != s))
+ split.Add(s);
+ break;
+ }
+
+ var page = 1;
+ foreach (var s in split)
+ {
+ result.Add(new Page($"Page {page}:\n{s}"));
+ page++;
+ }
+
+ return result;
}
- var page = 1;
- foreach (var s in split)
+ /// <summary>
+ /// Generates pages from a string, and puts them in message embeds.
+ /// </summary>
+ /// <param name="input">Input string.</param>
+ /// <param name="splitType">How to split input string.</param>
+ /// <param name="embedBase">Base embed for output embeds.</param>
+ /// <returns></returns>
+ public IEnumerable<Page> GeneratePagesInEmbed(string input, SplitType splitType = SplitType.Character, DiscordEmbedBuilder embedBase = null)
{
- result.Add(new Page($"Page {page}:\n{s}"));
- page++;
- }
+ if (string.IsNullOrEmpty(input))
+ throw new ArgumentException("You must provide a string that is not null or empty!");
- return result;
- }
+ var embed = embedBase ?? new DiscordEmbedBuilder();
- /// <summary>
- /// Generates pages from a string, and puts them in message embeds.
- /// </summary>
- /// <param name="input">Input string.</param>
- /// <param name="splitType">How to split input string.</param>
- /// <param name="embedBase">Base embed for output embeds.</param>
- /// <returns></returns>
- public IEnumerable<Page> GeneratePagesInEmbed(string input, SplitType splitType = SplitType.Character, DiscordEmbedBuilder embedBase = null)
- {
- if (string.IsNullOrEmpty(input))
- throw new ArgumentException("You must provide a string that is not null or empty!");
+ var result = new List<Page>();
+ List<string> split;
- var embed = embedBase ?? new DiscordEmbedBuilder();
+ switch (splitType)
+ {
+ default:
+ case SplitType.Character:
+ split = this.SplitString(input, 500).ToList();
+ break;
+ case SplitType.Line:
+ var subsplit = input.Split('\n');
- var result = new List<Page>();
- List<string> split;
+ split = new List<string>();
+ var s = "";
- switch (splitType)
- {
- default:
- case SplitType.Character:
- split = this.SplitString(input, 500).ToList();
- break;
- case SplitType.Line:
- var subsplit = input.Split('\n');
-
- split = new List<string>();
- var s = "";
-
- for (var i = 0; i < subsplit.Length; i++)
- {
- s += $"{subsplit[i]}\n";
- if (i % 15 == 0 && i != 0)
+ for (var i = 0; i < subsplit.Length; i++)
{
- split.Add(s);
- s = "";
+ s += $"{subsplit[i]}\n";
+ if (i % 15 == 0 && i != 0)
+ {
+ split.Add(s);
+ s = "";
+ }
}
- }
- if (!split.Any(x => x == s))
- split.Add(s);
- break;
- }
-
- var page = 1;
- foreach (var s in split)
- {
- result.Add(new Page("", new DiscordEmbedBuilder(embed).WithDescription(s).WithFooter($"Page {page}/{split.Count}")));
- page++;
- }
+ if (!split.Any(x => x == s))
+ split.Add(s);
+ break;
+ }
- return result;
- }
+ var page = 1;
+ foreach (var s in split)
+ {
+ result.Add(new Page("", new DiscordEmbedBuilder(embed).WithDescription(s).WithFooter($"Page {page}/{split.Count}")));
+ page++;
+ }
- /// <summary>
- /// Splits the string.
- /// </summary>
- /// <param name="str">The string.</param>
- /// <param name="chunkSize">The chunk size.</param>
- private List<string> SplitString(string str, int chunkSize)
- {
- var res = new List<string>();
- var len = str.Length;
- var i = 0;
+ return result;
+ }
- while (i < len)
+ /// <summary>
+ /// Splits the string.
+ /// </summary>
+ /// <param name="str">The string.</param>
+ /// <param name="chunkSize">The chunk size.</param>
+ private List<string> SplitString(string str, int chunkSize)
{
- var size = Math.Min(len - i, chunkSize);
- res.Add(str.Substring(i, size));
- i += size;
+ var res = new List<string>();
+ var len = str.Length;
+ var i = 0;
+
+ while (i < len)
+ {
+ var size = Math.Min(len - i, chunkSize);
+ res.Add(str.Substring(i, size));
+ i += size;
+ }
+
+ return res;
}
- return res;
- }
-
- /// <summary>
- /// Gets the cancellation token.
- /// </summary>
- /// <param name="timeout">The timeout.</param>
- private CancellationToken GetCancellationToken(TimeSpan? timeout = null) => new CancellationTokenSource(timeout ?? this.Config.Timeout).Token;
-
- /// <summary>
- /// Handles an invalid interaction.
- /// </summary>
- /// <param name="interaction">The interaction.</param>
- private async Task HandleInvalidInteraction(DiscordInteraction interaction)
- {
- var at = this.Config.ResponseBehavior switch
+ /// <summary>
+ /// Gets the cancellation token.
+ /// </summary>
+ /// <param name="timeout">The timeout.</param>
+ private CancellationToken GetCancellationToken(TimeSpan? timeout = null) => new CancellationTokenSource(timeout ?? this.Config.Timeout).Token;
+
+ /// <summary>
+ /// Handles an invalid interaction.
+ /// </summary>
+ /// <param name="interaction">The interaction.</param>
+ private async Task HandleInvalidInteraction(DiscordInteraction interaction)
{
- InteractionResponseBehavior.Ack => interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate),
- InteractionResponseBehavior.Respond => interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder { Content = this.Config.ResponseMessage, IsEphemeral = true}),
- InteractionResponseBehavior.Ignore => Task.CompletedTask,
- _ => throw new ArgumentException("Unknown enum value.")
- };
-
- await at;
+ var at = this.Config.ResponseBehavior switch
+ {
+ InteractionResponseBehavior.Ack => interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate),
+ InteractionResponseBehavior.Respond => interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder { Content = this.Config.ResponseMessage, IsEphemeral = true}),
+ InteractionResponseBehavior.Ignore => Task.CompletedTask,
+ _ => throw new ArgumentException("Unknown enum value.")
+ };
+
+ await at;
+ }
}
}
diff --git a/DisCatSharp.Interactivity/InteractivityResult.cs b/DisCatSharp.Interactivity/InteractivityResult.cs
index 98273ccb9..b45bc183a 100644
--- a/DisCatSharp.Interactivity/InteractivityResult.cs
+++ b/DisCatSharp.Interactivity/InteractivityResult.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Interactivity;
-
-/// <summary>
-/// Interactivity result
-/// </summary>
-/// <typeparam name="T">Type of result</typeparam>
-public readonly struct InteractivityResult<T>
+namespace DisCatSharp.Interactivity
{
/// <summary>
- /// Whether interactivity was timed out
- /// </summary>
- public bool TimedOut { get; }
- /// <summary>
- /// Result
+ /// Interactivity result
/// </summary>
- public T Result { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="InteractivityResult{T}"/> class.
- /// </summary>
- /// <param name="timedOut">If true, timed out.</param>
- /// <param name="result">The result.</param>
- internal InteractivityResult(bool timedOut, T result)
+ /// <typeparam name="T">Type of result</typeparam>
+ public readonly struct InteractivityResult<T>
{
- this.TimedOut = timedOut;
- this.Result = result;
+ /// <summary>
+ /// Whether interactivity was timed out
+ /// </summary>
+ public bool TimedOut { get; }
+ /// <summary>
+ /// Result
+ /// </summary>
+ public T Result { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InteractivityResult{T}"/> class.
+ /// </summary>
+ /// <param name="timedOut">If true, timed out.</param>
+ /// <param name="result">The result.</param>
+ internal InteractivityResult(bool timedOut, T result)
+ {
+ this.TimedOut = timedOut;
+ this.Result = result;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/DiscordClientExtensions.cs b/DisCatSharp.Lavalink/DiscordClientExtensions.cs
index c30f9038e..831b78952 100644
--- a/DisCatSharp.Lavalink/DiscordClientExtensions.cs
+++ b/DisCatSharp.Lavalink/DiscordClientExtensions.cs
@@ -1,131 +1,132 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// The discord client extensions.
-/// </summary>
-public static class DiscordClientExtensions
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// Creates a new Lavalink client with specified settings.
+ /// The discord client extensions.
/// </summary>
- /// <param name="client">Discord client to create Lavalink instance for.</param>
- /// <returns>Lavalink client instance.</returns>
- public static LavalinkExtension UseLavalink(this DiscordClient client)
+ public static class DiscordClientExtensions
{
- if (client.GetExtension<LavalinkExtension>() != null)
- throw new InvalidOperationException("Lavalink is already enabled for that client.");
-
- if (!client.Configuration.Intents.HasIntent(DiscordIntents.GuildVoiceStates))
- client.Logger.LogCritical(LavalinkEvents.Intents, "The Lavalink extension is registered but the guild voice states intent is not enabled. It is highly recommended to enable it.");
+ /// <summary>
+ /// Creates a new Lavalink client with specified settings.
+ /// </summary>
+ /// <param name="client">Discord client to create Lavalink instance for.</param>
+ /// <returns>Lavalink client instance.</returns>
+ public static LavalinkExtension UseLavalink(this DiscordClient client)
+ {
+ if (client.GetExtension<LavalinkExtension>() != null)
+ throw new InvalidOperationException("Lavalink is already enabled for that client.");
- var lava = new LavalinkExtension();
- client.AddExtension(lava);
- return lava;
- }
+ if (!client.Configuration.Intents.HasIntent(DiscordIntents.GuildVoiceStates))
+ client.Logger.LogCritical(LavalinkEvents.Intents, "The Lavalink extension is registered but the guild voice states intent is not enabled. It is highly recommended to enable it.");
- /// <summary>
- /// Creates new Lavalink clients on all shards in a given sharded client.
- /// </summary>
- /// <param name="client">Discord sharded client to create Lavalink instances for.</param>
- /// <returns>A dictionary of created Lavalink clients.</returns>
- public static async Task<IReadOnlyDictionary<int, LavalinkExtension>> UseLavalinkAsync(this DiscordShardedClient client)
- {
- var modules = new Dictionary<int, LavalinkExtension>();
- await client.InitializeShardsAsync().ConfigureAwait(false);
+ var lava = new LavalinkExtension();
+ client.AddExtension(lava);
+ return lava;
+ }
- foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ /// <summary>
+ /// Creates new Lavalink clients on all shards in a given sharded client.
+ /// </summary>
+ /// <param name="client">Discord sharded client to create Lavalink instances for.</param>
+ /// <returns>A dictionary of created Lavalink clients.</returns>
+ public static async Task<IReadOnlyDictionary<int, LavalinkExtension>> UseLavalinkAsync(this DiscordShardedClient client)
{
- var lava = shard.GetExtension<LavalinkExtension>();
- if (lava == null)
- lava = shard.UseLavalink();
-
- modules[shard.ShardId] = lava;
- }
+ var modules = new Dictionary<int, LavalinkExtension>();
+ await client.InitializeShardsAsync().ConfigureAwait(false);
- return new ReadOnlyDictionary<int, LavalinkExtension>(modules);
- }
+ foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ {
+ var lava = shard.GetExtension<LavalinkExtension>();
+ if (lava == null)
+ lava = shard.UseLavalink();
- /// <summary>
- /// Gets the active instance of the Lavalink client for the DiscordClient.
- /// </summary>
- /// <param name="client">Discord client to get Lavalink instance for.</param>
- /// <returns>Lavalink client instance.</returns>
- public static LavalinkExtension GetLavalink(this DiscordClient client)
- => client.GetExtension<LavalinkExtension>();
+ modules[shard.ShardId] = lava;
+ }
- /// <summary>
- /// Retrieves a <see cref="LavalinkExtension"/> instance for each shard.
- /// </summary>
- /// <param name="client">The shard client to retrieve <see cref="LavalinkExtension"/> instances from.</param>
- /// <returns>A dictionary containing <see cref="LavalinkExtension"/> instances for each shard.</returns>
- public static async Task<IReadOnlyDictionary<int, LavalinkExtension>> GetLavalinkAsync(this DiscordShardedClient client)
- {
- await client.InitializeShardsAsync().ConfigureAwait(false);
- var extensions = new Dictionary<int, LavalinkExtension>();
+ return new ReadOnlyDictionary<int, LavalinkExtension>(modules);
+ }
- foreach (var shard in client.ShardClients.Values)
+ /// <summary>
+ /// Gets the active instance of the Lavalink client for the DiscordClient.
+ /// </summary>
+ /// <param name="client">Discord client to get Lavalink instance for.</param>
+ /// <returns>Lavalink client instance.</returns>
+ public static LavalinkExtension GetLavalink(this DiscordClient client)
+ => client.GetExtension<LavalinkExtension>();
+
+ /// <summary>
+ /// Retrieves a <see cref="LavalinkExtension"/> instance for each shard.
+ /// </summary>
+ /// <param name="client">The shard client to retrieve <see cref="LavalinkExtension"/> instances from.</param>
+ /// <returns>A dictionary containing <see cref="LavalinkExtension"/> instances for each shard.</returns>
+ public static async Task<IReadOnlyDictionary<int, LavalinkExtension>> GetLavalinkAsync(this DiscordShardedClient client)
{
- extensions.Add(shard.ShardId, shard.GetExtension<LavalinkExtension>());
- }
+ await client.InitializeShardsAsync().ConfigureAwait(false);
+ var extensions = new Dictionary<int, LavalinkExtension>();
- return new ReadOnlyDictionary<int, LavalinkExtension>(extensions);
- }
+ foreach (var shard in client.ShardClients.Values)
+ {
+ extensions.Add(shard.ShardId, shard.GetExtension<LavalinkExtension>());
+ }
- /// <summary>
- /// Connects to this voice channel using Lavalink.
- /// </summary>
- /// <param name="channel">Channel to connect to.</param>
- /// <param name="node">Lavalink node to connect through.</param>
- /// <returns>If successful, the Lavalink client.</returns>
- public static Task ConnectAsync(this DiscordChannel channel, LavalinkNodeConnection node)
- {
- if (channel == null)
- throw new NullReferenceException();
+ return new ReadOnlyDictionary<int, LavalinkExtension>(extensions);
+ }
- if (channel.Guild == null)
- throw new InvalidOperationException("Lavalink can only be used with guild channels.");
+ /// <summary>
+ /// Connects to this voice channel using Lavalink.
+ /// </summary>
+ /// <param name="channel">Channel to connect to.</param>
+ /// <param name="node">Lavalink node to connect through.</param>
+ /// <returns>If successful, the Lavalink client.</returns>
+ public static Task ConnectAsync(this DiscordChannel channel, LavalinkNodeConnection node)
+ {
+ if (channel == null)
+ throw new NullReferenceException();
+
+ if (channel.Guild == null)
+ throw new InvalidOperationException("Lavalink can only be used with guild channels.");
- if (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage)
- throw new InvalidOperationException("You can only connect to voice and stage channels.");
+ if (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage)
+ throw new InvalidOperationException("You can only connect to voice and stage channels.");
- if (channel.Discord is not DiscordClient discord || discord == null)
- throw new NullReferenceException();
+ if (channel.Discord is not DiscordClient discord || discord == null)
+ throw new NullReferenceException();
- var lava = discord.GetLavalink();
- return lava == null
- ? throw new InvalidOperationException("Lavalink is not initialized for this Discord client.")
- : node.ConnectAsync(channel);
+ var lava = discord.GetLavalink();
+ return lava == null
+ ? throw new InvalidOperationException("Lavalink is not initialized for this Discord client.")
+ : node.ConnectAsync(channel);
+ }
}
}
diff --git a/DisCatSharp.Lavalink/Entities/LavalinkCommands.cs b/DisCatSharp.Lavalink/Entities/LavalinkCommands.cs
index 17d1060c1..fe6227dd2 100644
--- a/DisCatSharp.Lavalink/Entities/LavalinkCommands.cs
+++ b/DisCatSharp.Lavalink/Entities/LavalinkCommands.cs
@@ -1,240 +1,241 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Lavalink.Entities;
-
-/// <summary>
-/// The lavalink configure resume.
-/// </summary>
-internal sealed class LavalinkConfigureResume : LavalinkPayload
+namespace DisCatSharp.Lavalink.Entities
{
/// <summary>
- /// Gets the key.
- /// </summary>
- [JsonProperty("key")]
- public string Key { get; }
-
- /// <summary>
- /// Gets the timeout.
- /// </summary>
- [JsonProperty("timeout")]
- public int Timeout { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkConfigureResume"/> class.
+ /// The lavalink configure resume.
/// </summary>
- /// <param name="key">The key.</param>
- /// <param name="timeout">The timeout.</param>
- public LavalinkConfigureResume(string key, int timeout)
- : base("configureResuming")
+ internal sealed class LavalinkConfigureResume : LavalinkPayload
{
- this.Key = key;
- this.Timeout = timeout;
+ /// <summary>
+ /// Gets the key.
+ /// </summary>
+ [JsonProperty("key")]
+ public string Key { get; }
+
+ /// <summary>
+ /// Gets the timeout.
+ /// </summary>
+ [JsonProperty("timeout")]
+ public int Timeout { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkConfigureResume"/> class.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="timeout">The timeout.</param>
+ public LavalinkConfigureResume(string key, int timeout)
+ : base("configureResuming")
+ {
+ this.Key = key;
+ this.Timeout = timeout;
+ }
}
-}
-
-/// <summary>
-/// The lavalink destroy.
-/// </summary>
-internal sealed class LavalinkDestroy : LavalinkPayload
-{
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkDestroy"/> class.
- /// </summary>
- /// <param name="lvl">The lvl.</param>
- public LavalinkDestroy(LavalinkGuildConnection lvl)
- : base("destroy", lvl.GuildIdString)
- { }
-}
-
-/// <summary>
-/// The lavalink play.
-/// </summary>
-internal sealed class LavalinkPlay : LavalinkPayload
-{
- /// <summary>
- /// Gets the track.
- /// </summary>
- [JsonProperty("track")]
- public string Track { get; }
/// <summary>
- /// Initializes a new instance of the <see cref="LavalinkPlay"/> class.
+ /// The lavalink destroy.
/// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="track">The track.</param>
- public LavalinkPlay(LavalinkGuildConnection lvl, LavalinkTrack track)
- : base("play", lvl.GuildIdString)
+ internal sealed class LavalinkDestroy : LavalinkPayload
{
- this.Track = track.TrackString;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkDestroy"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ public LavalinkDestroy(LavalinkGuildConnection lvl)
+ : base("destroy", lvl.GuildIdString)
+ { }
}
-}
-/// <summary>
-/// The lavalink play partial.
-/// </summary>
-internal sealed class LavalinkPlayPartial : LavalinkPayload
-{
/// <summary>
- /// Gets the track.
+ /// The lavalink play.
/// </summary>
- [JsonProperty("track")]
- public string Track { get; }
-
- /// <summary>
- /// Gets the start time.
- /// </summary>
- [JsonProperty("startTime")]
- public long StartTime { get; }
-
- /// <summary>
- /// Gets the stop time.
- /// </summary>
- [JsonProperty("endTime")]
- public long StopTime { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkPlayPartial"/> class.
- /// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="track">The track.</param>
- /// <param name="start">The start.</param>
- /// <param name="stop">The stop.</param>
- public LavalinkPlayPartial(LavalinkGuildConnection lvl, LavalinkTrack track, TimeSpan start, TimeSpan stop)
- : base("play", lvl.GuildIdString)
+ internal sealed class LavalinkPlay : LavalinkPayload
{
- this.Track = track.TrackString;
- this.StartTime = (long)start.TotalMilliseconds;
- this.StopTime = (long)stop.TotalMilliseconds;
+ /// <summary>
+ /// Gets the track.
+ /// </summary>
+ [JsonProperty("track")]
+ public string Track { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkPlay"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="track">The track.</param>
+ public LavalinkPlay(LavalinkGuildConnection lvl, LavalinkTrack track)
+ : base("play", lvl.GuildIdString)
+ {
+ this.Track = track.TrackString;
+ }
}
-}
-
-/// <summary>
-/// The lavalink pause.
-/// </summary>
-internal sealed class LavalinkPause : LavalinkPayload
-{
- /// <summary>
- /// Gets a value indicating whether pause.
- /// </summary>
- [JsonProperty("pause")]
- public bool Pause { get; }
/// <summary>
- /// Initializes a new instance of the <see cref="LavalinkPause"/> class.
+ /// The lavalink play partial.
/// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="pause">If true, pause.</param>
- public LavalinkPause(LavalinkGuildConnection lvl, bool pause)
- : base("pause", lvl.GuildIdString)
+ internal sealed class LavalinkPlayPartial : LavalinkPayload
{
- this.Pause = pause;
+ /// <summary>
+ /// Gets the track.
+ /// </summary>
+ [JsonProperty("track")]
+ public string Track { get; }
+
+ /// <summary>
+ /// Gets the start time.
+ /// </summary>
+ [JsonProperty("startTime")]
+ public long StartTime { get; }
+
+ /// <summary>
+ /// Gets the stop time.
+ /// </summary>
+ [JsonProperty("endTime")]
+ public long StopTime { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkPlayPartial"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="track">The track.</param>
+ /// <param name="start">The start.</param>
+ /// <param name="stop">The stop.</param>
+ public LavalinkPlayPartial(LavalinkGuildConnection lvl, LavalinkTrack track, TimeSpan start, TimeSpan stop)
+ : base("play", lvl.GuildIdString)
+ {
+ this.Track = track.TrackString;
+ this.StartTime = (long)start.TotalMilliseconds;
+ this.StopTime = (long)stop.TotalMilliseconds;
+ }
}
-}
-/// <summary>
-/// The lavalink stop.
-/// </summary>
-internal sealed class LavalinkStop : LavalinkPayload
-{
/// <summary>
- /// Initializes a new instance of the <see cref="LavalinkStop"/> class.
+ /// The lavalink pause.
/// </summary>
- /// <param name="lvl">The lvl.</param>
- public LavalinkStop(LavalinkGuildConnection lvl)
- : base("stop", lvl.GuildIdString)
- { }
-}
-
-/// <summary>
-/// The lavalink seek.
-/// </summary>
-internal sealed class LavalinkSeek : LavalinkPayload
-{
- /// <summary>
- /// Gets the position.
- /// </summary>
- [JsonProperty("position")]
- public long Position { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkSeek"/> class.
- /// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="position">The position.</param>
- public LavalinkSeek(LavalinkGuildConnection lvl, TimeSpan position)
- : base("seek", lvl.GuildIdString)
+ internal sealed class LavalinkPause : LavalinkPayload
{
- this.Position = (long)position.TotalMilliseconds;
+ /// <summary>
+ /// Gets a value indicating whether pause.
+ /// </summary>
+ [JsonProperty("pause")]
+ public bool Pause { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkPause"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="pause">If true, pause.</param>
+ public LavalinkPause(LavalinkGuildConnection lvl, bool pause)
+ : base("pause", lvl.GuildIdString)
+ {
+ this.Pause = pause;
+ }
}
-}
-/// <summary>
-/// The lavalink volume.
-/// </summary>
-internal sealed class LavalinkVolume : LavalinkPayload
-{
/// <summary>
- /// Gets the volume.
+ /// The lavalink stop.
/// </summary>
- [JsonProperty("volume")]
- public int Volume { get; }
+ internal sealed class LavalinkStop : LavalinkPayload
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkStop"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ public LavalinkStop(LavalinkGuildConnection lvl)
+ : base("stop", lvl.GuildIdString)
+ { }
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="LavalinkVolume"/> class.
+ /// The lavalink seek.
/// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="volume">The volume.</param>
- public LavalinkVolume(LavalinkGuildConnection lvl, int volume)
- : base("volume", lvl.GuildIdString)
+ internal sealed class LavalinkSeek : LavalinkPayload
{
- this.Volume = volume;
+ /// <summary>
+ /// Gets the position.
+ /// </summary>
+ [JsonProperty("position")]
+ public long Position { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkSeek"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="position">The position.</param>
+ public LavalinkSeek(LavalinkGuildConnection lvl, TimeSpan position)
+ : base("seek", lvl.GuildIdString)
+ {
+ this.Position = (long)position.TotalMilliseconds;
+ }
}
-}
-/// <summary>
-/// The lavalink equalizer.
-/// </summary>
-internal sealed class LavalinkEqualizer : LavalinkPayload
-{
/// <summary>
- /// Gets the bands.
+ /// The lavalink volume.
/// </summary>
- [JsonProperty("bands")]
- public IEnumerable<LavalinkBandAdjustment> Bands { get; }
+ internal sealed class LavalinkVolume : LavalinkPayload
+ {
+ /// <summary>
+ /// Gets the volume.
+ /// </summary>
+ [JsonProperty("volume")]
+ public int Volume { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkVolume"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="volume">The volume.</param>
+ public LavalinkVolume(LavalinkGuildConnection lvl, int volume)
+ : base("volume", lvl.GuildIdString)
+ {
+ this.Volume = volume;
+ }
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="LavalinkEqualizer"/> class.
+ /// The lavalink equalizer.
/// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="bands">The bands.</param>
- public LavalinkEqualizer(LavalinkGuildConnection lvl, IEnumerable<LavalinkBandAdjustment> bands)
- : base("equalizer", lvl.GuildIdString)
+ internal sealed class LavalinkEqualizer : LavalinkPayload
{
- this.Bands = bands;
+ /// <summary>
+ /// Gets the bands.
+ /// </summary>
+ [JsonProperty("bands")]
+ public IEnumerable<LavalinkBandAdjustment> Bands { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkEqualizer"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="bands">The bands.</param>
+ public LavalinkEqualizer(LavalinkGuildConnection lvl, IEnumerable<LavalinkBandAdjustment> bands)
+ : base("equalizer", lvl.GuildIdString)
+ {
+ this.Bands = bands;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/Entities/LavalinkDispatch.cs b/DisCatSharp.Lavalink/Entities/LavalinkDispatch.cs
index e324853cf..7dc763414 100644
--- a/DisCatSharp.Lavalink/Entities/LavalinkDispatch.cs
+++ b/DisCatSharp.Lavalink/Entities/LavalinkDispatch.cs
@@ -1,55 +1,56 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Lavalink.Entities;
-
-/// <summary>
-/// The voice dispatch.
-/// </summary>
-internal sealed class VoiceDispatch
+namespace DisCatSharp.Lavalink.Entities
{
/// <summary>
- /// Gets or sets the op code.
+ /// The voice dispatch.
/// </summary>
- [JsonProperty("op")]
- public int OpCode { get; set; }
+ internal sealed class VoiceDispatch
+ {
+ /// <summary>
+ /// Gets or sets the op code.
+ /// </summary>
+ [JsonProperty("op")]
+ public int OpCode { get; set; }
- /// <summary>
- /// Gets or sets the payload.
- /// </summary>
- [JsonProperty("d")]
- public object Payload { get; set; }
+ /// <summary>
+ /// Gets or sets the payload.
+ /// </summary>
+ [JsonProperty("d")]
+ public object Payload { get; set; }
- /// <summary>
- /// Gets or sets the sequence.
- /// </summary>
- [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)]
- public int? Sequence { get; set; }
+ /// <summary>
+ /// Gets or sets the sequence.
+ /// </summary>
+ [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Sequence { get; set; }
- /// <summary>
- /// Gets or sets the event name.
- /// </summary>
- [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)]
- public string EventName { get; set; }
+ /// <summary>
+ /// Gets or sets the event name.
+ /// </summary>
+ [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)]
+ public string EventName { get; set; }
+ }
}
diff --git a/DisCatSharp.Lavalink/Entities/LavalinkEqualizerTypes.cs b/DisCatSharp.Lavalink/Entities/LavalinkEqualizerTypes.cs
index c06ba9c94..ddf5cec60 100644
--- a/DisCatSharp.Lavalink/Entities/LavalinkEqualizerTypes.cs
+++ b/DisCatSharp.Lavalink/Entities/LavalinkEqualizerTypes.cs
@@ -1,84 +1,85 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// Represents Lavalink equalizer band adjustment. This is used to alter the sound output by using Lavalink's equalizer.
-/// </summary>
-public struct LavalinkBandAdjustment
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// Gets the ID of the band to adjust.
+ /// Represents Lavalink equalizer band adjustment. This is used to alter the sound output by using Lavalink's equalizer.
/// </summary>
- [JsonProperty("band")]
- public int BandId { get; }
+ public struct LavalinkBandAdjustment
+ {
+ /// <summary>
+ /// Gets the ID of the band to adjust.
+ /// </summary>
+ [JsonProperty("band")]
+ public int BandId { get; }
- /// <summary>
- /// Gets the gain of the specified band.
- /// </summary>
- [JsonProperty("gain")]
- public float Gain { get; }
+ /// <summary>
+ /// Gets the gain of the specified band.
+ /// </summary>
+ [JsonProperty("gain")]
+ public float Gain { get; }
- /// <summary>
- /// Creates a new band adjustment with specified parameters.
- /// </summary>
- /// <param name="bandId">Which band to adjust. Must be in 0-14 range.</param>
- /// <param name="gain">By how much to adjust the band. Must be greater than or equal to -0.25 (muted), and less than or equal to +1.0. +0.25 means the band is doubled.</param>
- public LavalinkBandAdjustment(int bandId, float gain)
- {
- if (bandId < 0 || bandId > 14)
- throw new ArgumentOutOfRangeException(nameof(bandId), "Band ID cannot be lower than 0 or greater than 14.");
+ /// <summary>
+ /// Creates a new band adjustment with specified parameters.
+ /// </summary>
+ /// <param name="bandId">Which band to adjust. Must be in 0-14 range.</param>
+ /// <param name="gain">By how much to adjust the band. Must be greater than or equal to -0.25 (muted), and less than or equal to +1.0. +0.25 means the band is doubled.</param>
+ public LavalinkBandAdjustment(int bandId, float gain)
+ {
+ if (bandId < 0 || bandId > 14)
+ throw new ArgumentOutOfRangeException(nameof(bandId), "Band ID cannot be lower than 0 or greater than 14.");
- if (gain < -0.25 || gain > 1.0)
- throw new ArgumentOutOfRangeException(nameof(gain), "Gain cannot be lower than -0.25 or greater than 1.0.");
+ if (gain < -0.25 || gain > 1.0)
+ throw new ArgumentOutOfRangeException(nameof(gain), "Gain cannot be lower than -0.25 or greater than 1.0.");
- this.BandId = bandId;
- this.Gain = gain;
+ this.BandId = bandId;
+ this.Gain = gain;
+ }
}
-}
-/// <summary>
-/// The lavalink band adjustment comparer.
-/// </summary>
-internal class LavalinkBandAdjustmentComparer : IEqualityComparer<LavalinkBandAdjustment>
-{
/// <summary>
- /// Whether two band adjustments are equal.
+ /// The lavalink band adjustment comparer.
/// </summary>
- /// <param name="x">The first band adjustments.</param>
- /// <param name="y">The second band adjustments.</param>
- public bool Equals(LavalinkBandAdjustment x, LavalinkBandAdjustment y)
- => x.BandId == y.BandId;
+ internal class LavalinkBandAdjustmentComparer : IEqualityComparer<LavalinkBandAdjustment>
+ {
+ /// <summary>
+ /// Whether two band adjustments are equal.
+ /// </summary>
+ /// <param name="x">The first band adjustments.</param>
+ /// <param name="y">The second band adjustments.</param>
+ public bool Equals(LavalinkBandAdjustment x, LavalinkBandAdjustment y)
+ => x.BandId == y.BandId;
- /// <summary>
- /// Gets the hash code.
- /// </summary>
- /// <param name="obj">The band adjustments.</param>
- public int GetHashCode(LavalinkBandAdjustment obj)
- => obj.BandId;
+ /// <summary>
+ /// Gets the hash code.
+ /// </summary>
+ /// <param name="obj">The band adjustments.</param>
+ public int GetHashCode(LavalinkBandAdjustment obj)
+ => obj.BandId;
+ }
}
diff --git a/DisCatSharp.Lavalink/Entities/LavalinkPayload.cs b/DisCatSharp.Lavalink/Entities/LavalinkPayload.cs
index 2ba94b6b8..8f9e3129e 100644
--- a/DisCatSharp.Lavalink/Entities/LavalinkPayload.cs
+++ b/DisCatSharp.Lavalink/Entities/LavalinkPayload.cs
@@ -1,63 +1,64 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Lavalink.Entities;
-
-/// <summary>
-/// The lavalink payload.
-/// </summary>
-internal abstract class LavalinkPayload
+namespace DisCatSharp.Lavalink.Entities
{
/// <summary>
- /// Gets the operation.
+ /// The lavalink payload.
/// </summary>
- [JsonProperty("op")]
- public string Operation { get; }
+ internal abstract class LavalinkPayload
+ {
+ /// <summary>
+ /// Gets the operation.
+ /// </summary>
+ [JsonProperty("op")]
+ public string Operation { get; }
- /// <summary>
- /// Gets the guild id.
- /// </summary>
- [JsonProperty("guildId", NullValueHandling = NullValueHandling.Ignore)]
- public string GuildId { get; }
+ /// <summary>
+ /// Gets the guild id.
+ /// </summary>
+ [JsonProperty("guildId", NullValueHandling = NullValueHandling.Ignore)]
+ public string GuildId { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkPayload"/> class.
- /// </summary>
- /// <param name="opcode">The opcode.</param>
- internal LavalinkPayload(string opcode)
- {
- this.Operation = opcode;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkPayload"/> class.
+ /// </summary>
+ /// <param name="opcode">The opcode.</param>
+ internal LavalinkPayload(string opcode)
+ {
+ this.Operation = opcode;
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkPayload"/> class.
- /// </summary>
- /// <param name="opcode">The opcode.</param>
- /// <param name="guildId">The guild id.</param>
- internal LavalinkPayload(string opcode, string guildId)
- {
- this.Operation = opcode;
- this.GuildId = guildId;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkPayload"/> class.
+ /// </summary>
+ /// <param name="opcode">The opcode.</param>
+ /// <param name="guildId">The guild id.</param>
+ internal LavalinkPayload(string opcode, string guildId)
+ {
+ this.Operation = opcode;
+ this.GuildId = guildId;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/Entities/LavalinkRouteStatus.cs b/DisCatSharp.Lavalink/Entities/LavalinkRouteStatus.cs
index 2a5d416b7..468f6a354 100644
--- a/DisCatSharp.Lavalink/Entities/LavalinkRouteStatus.cs
+++ b/DisCatSharp.Lavalink/Entities/LavalinkRouteStatus.cs
@@ -1,155 +1,156 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Lavalink.Entities;
-
-/// <summary>
-/// The lavalink route status.
-/// </summary>
-public class LavalinkRouteStatus
+namespace DisCatSharp.Lavalink.Entities
{
/// <summary>
- /// Gets the route planner type.
- /// </summary>
- [JsonIgnore]
- public LavalinkRoutePlannerType? Class
- => this.GetLavalinkRoutePlannerType(this.PlannerTypeClass);
-
- /// <summary>
- /// Gets the details of the route planner.
- /// </summary>
- [JsonProperty("details", NullValueHandling = NullValueHandling.Ignore)]
- public LavalinkRouteStatusDetails Details { get; internal set; }
-
- /// <summary>
- /// Gets or sets the class.
- /// </summary>
- [JsonProperty("class", NullValueHandling = NullValueHandling.Ignore)]
- internal string PlannerTypeClass { get; set; }
-
- /// <summary>
- /// Gets the lavalink route planner type.
- /// </summary>
- /// <param name="type">The type.</param>
- private LavalinkRoutePlannerType? GetLavalinkRoutePlannerType(string type) =>
- type switch
- {
- "RotatingIpRoutePlanner" => LavalinkRoutePlannerType.RotatingIpRoutePlanner,
- "BalancingIpRoutePlanner" => LavalinkRoutePlannerType.BalancingIpRoutePlanner,
- "NanoIpRoutePlanner" => LavalinkRoutePlannerType.NanoIpRoutePlanner,
- "RotatingNanoIpRoutePlanner" => LavalinkRoutePlannerType.RotatingNanoIpRoutePlanner,
- _ => null,
- };
-}
-
-/// <summary>
-/// The lavalink route status details.
-/// </summary>
-public class LavalinkRouteStatusDetails
-{
- /// <summary>
- /// Gets the details for the current IP block.
- /// </summary>
- [JsonProperty("ipBlock", NullValueHandling = NullValueHandling.Ignore)]
- public LavalinkIpBlock IpBlock { get; internal set; }
-
- /// <summary>
- /// Gets the collection of failed addresses.
- /// </summary>
- [JsonProperty("failingAddresses", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<LavalinkFailedAddress> FailedAddresses { get; internal set; }
-
- /// <summary>
- /// Gets the number of rotations since the restart of Lavalink.
- /// <para>Only present in the <see cref="LavalinkRoutePlannerType.RotatingIpRoutePlanner"/>.</para>
- /// </summary>
- [JsonProperty("rotateIndex", NullValueHandling = NullValueHandling.Ignore)]
- public string RotateIndex { get; internal set; }
-
- /// <summary>
- /// Gets the current offset of the IP block.
- /// <para>Only present in the <see cref="LavalinkRoutePlannerType.RotatingIpRoutePlanner"/>.</para>
- /// </summary>
- [JsonProperty("ipIndex", NullValueHandling = NullValueHandling.Ignore)]
- public string IpIndex { get; internal set; }
-
- /// <summary>
- /// Gets the current IP Address used by the planner.
- /// <para>Only present in the <see cref="LavalinkRoutePlannerType.RotatingIpRoutePlanner"/>.</para>
- /// </summary>
- [JsonProperty("currentAddress", NullValueHandling = NullValueHandling.Ignore)]
- public string CurrentAddress { get; internal set; }
-
- /// <summary>
- /// Gets the current offset of the IP block.
- /// <para>Only present in the <see cref="LavalinkRoutePlannerType.NanoIpRoutePlanner"/> and the <see cref="LavalinkRoutePlannerType.RotatingNanoIpRoutePlanner"/>.</para>
- /// </summary>
- [JsonProperty("currentAddressIndex", NullValueHandling = NullValueHandling.Ignore)]
- public long CurrentAddressIndex { get; internal set; }
-
- /// <summary>
- /// Gets the information in which /64 block ips are chosen. This number increases on each ban.
- /// <para>Only present in the <see cref="LavalinkRoutePlannerType.RotatingNanoIpRoutePlanner"/>.</para>
- /// </summary>
- [JsonProperty("blockIndex", NullValueHandling = NullValueHandling.Ignore)]
- public string BlockIndex { get; internal set; }
-}
-
-public struct LavalinkIpBlock
-{
- /// <summary>
- /// Gets the type of the IP block.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public string Type { get; internal set; }
-
- /// <summary>
- /// Gets the size of the IP block.
- /// </summary>
- [JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)]
- public string Size { get; internal set; }
-}
-
-public struct LavalinkFailedAddress
-{
- /// <summary>
- /// Gets the failed address IP.
- /// </summary>
- [JsonProperty("address", NullValueHandling = NullValueHandling.Ignore)]
- public string Address { get; internal set; }
-
- /// <summary>
- /// Gets the failing timestamp in milliseconds.
- /// </summary>
- [JsonProperty("failingTimestamp", NullValueHandling = NullValueHandling.Ignore)]
- public ulong FailingTimestamp { get; internal set; }
-
- /// <summary>
- /// Gets the DateTime format of the failing address.
- /// </summary>
- [JsonProperty("failingTime", NullValueHandling = NullValueHandling.Ignore)]
- public string FailingTime { get; internal set; }
+ /// The lavalink route status.
+ /// </summary>
+ public class LavalinkRouteStatus
+ {
+ /// <summary>
+ /// Gets the route planner type.
+ /// </summary>
+ [JsonIgnore]
+ public LavalinkRoutePlannerType? Class
+ => this.GetLavalinkRoutePlannerType(this.PlannerTypeClass);
+
+ /// <summary>
+ /// Gets the details of the route planner.
+ /// </summary>
+ [JsonProperty("details", NullValueHandling = NullValueHandling.Ignore)]
+ public LavalinkRouteStatusDetails Details { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets the class.
+ /// </summary>
+ [JsonProperty("class", NullValueHandling = NullValueHandling.Ignore)]
+ internal string PlannerTypeClass { get; set; }
+
+ /// <summary>
+ /// Gets the lavalink route planner type.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ private LavalinkRoutePlannerType? GetLavalinkRoutePlannerType(string type) =>
+ type switch
+ {
+ "RotatingIpRoutePlanner" => LavalinkRoutePlannerType.RotatingIpRoutePlanner,
+ "BalancingIpRoutePlanner" => LavalinkRoutePlannerType.BalancingIpRoutePlanner,
+ "NanoIpRoutePlanner" => LavalinkRoutePlannerType.NanoIpRoutePlanner,
+ "RotatingNanoIpRoutePlanner" => LavalinkRoutePlannerType.RotatingNanoIpRoutePlanner,
+ _ => null,
+ };
+ }
+
+ /// <summary>
+ /// The lavalink route status details.
+ /// </summary>
+ public class LavalinkRouteStatusDetails
+ {
+ /// <summary>
+ /// Gets the details for the current IP block.
+ /// </summary>
+ [JsonProperty("ipBlock", NullValueHandling = NullValueHandling.Ignore)]
+ public LavalinkIpBlock IpBlock { get; internal set; }
+
+ /// <summary>
+ /// Gets the collection of failed addresses.
+ /// </summary>
+ [JsonProperty("failingAddresses", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<LavalinkFailedAddress> FailedAddresses { get; internal set; }
+
+ /// <summary>
+ /// Gets the number of rotations since the restart of Lavalink.
+ /// <para>Only present in the <see cref="LavalinkRoutePlannerType.RotatingIpRoutePlanner"/>.</para>
+ /// </summary>
+ [JsonProperty("rotateIndex", NullValueHandling = NullValueHandling.Ignore)]
+ public string RotateIndex { get; internal set; }
+
+ /// <summary>
+ /// Gets the current offset of the IP block.
+ /// <para>Only present in the <see cref="LavalinkRoutePlannerType.RotatingIpRoutePlanner"/>.</para>
+ /// </summary>
+ [JsonProperty("ipIndex", NullValueHandling = NullValueHandling.Ignore)]
+ public string IpIndex { get; internal set; }
+
+ /// <summary>
+ /// Gets the current IP Address used by the planner.
+ /// <para>Only present in the <see cref="LavalinkRoutePlannerType.RotatingIpRoutePlanner"/>.</para>
+ /// </summary>
+ [JsonProperty("currentAddress", NullValueHandling = NullValueHandling.Ignore)]
+ public string CurrentAddress { get; internal set; }
+
+ /// <summary>
+ /// Gets the current offset of the IP block.
+ /// <para>Only present in the <see cref="LavalinkRoutePlannerType.NanoIpRoutePlanner"/> and the <see cref="LavalinkRoutePlannerType.RotatingNanoIpRoutePlanner"/>.</para>
+ /// </summary>
+ [JsonProperty("currentAddressIndex", NullValueHandling = NullValueHandling.Ignore)]
+ public long CurrentAddressIndex { get; internal set; }
+
+ /// <summary>
+ /// Gets the information in which /64 block ips are chosen. This number increases on each ban.
+ /// <para>Only present in the <see cref="LavalinkRoutePlannerType.RotatingNanoIpRoutePlanner"/>.</para>
+ /// </summary>
+ [JsonProperty("blockIndex", NullValueHandling = NullValueHandling.Ignore)]
+ public string BlockIndex { get; internal set; }
+ }
+
+ public struct LavalinkIpBlock
+ {
+ /// <summary>
+ /// Gets the type of the IP block.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public string Type { get; internal set; }
+
+ /// <summary>
+ /// Gets the size of the IP block.
+ /// </summary>
+ [JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)]
+ public string Size { get; internal set; }
+ }
+
+ public struct LavalinkFailedAddress
+ {
+ /// <summary>
+ /// Gets the failed address IP.
+ /// </summary>
+ [JsonProperty("address", NullValueHandling = NullValueHandling.Ignore)]
+ public string Address { get; internal set; }
+
+ /// <summary>
+ /// Gets the failing timestamp in milliseconds.
+ /// </summary>
+ [JsonProperty("failingTimestamp", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong FailingTimestamp { get; internal set; }
+
+ /// <summary>
+ /// Gets the DateTime format of the failing address.
+ /// </summary>
+ [JsonProperty("failingTime", NullValueHandling = NullValueHandling.Ignore)]
+ public string FailingTime { get; internal set; }
+ }
}
diff --git a/DisCatSharp.Lavalink/Entities/LavalinkUpdates.cs b/DisCatSharp.Lavalink/Entities/LavalinkUpdates.cs
index 8f3d807e5..629ef0ae3 100644
--- a/DisCatSharp.Lavalink/Entities/LavalinkUpdates.cs
+++ b/DisCatSharp.Lavalink/Entities/LavalinkUpdates.cs
@@ -1,303 +1,304 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
#pragma warning disable 0649
using System;
using Newtonsoft.Json;
-namespace DisCatSharp.Lavalink.Entities;
-
-/// <summary>
-/// The lavalink state.
-/// </summary>
-internal sealed class LavalinkState
+namespace DisCatSharp.Lavalink.Entities
{
/// <summary>
- /// Gets the time.
- /// </summary>
- [JsonIgnore]
- public DateTimeOffset Time => Utilities.GetDateTimeOffsetFromMilliseconds(this._time);
- [JsonProperty("time")]
- private readonly long _time;
-
- /// <summary>
- /// Gets the position.
- /// </summary>
- [JsonIgnore]
- public TimeSpan Position => TimeSpan.FromMilliseconds(this._position);
- [JsonProperty("position")]
- private readonly long _position;
-}
-
-/// <summary>
-/// Represents current state of given player.
-/// </summary>
-public sealed class LavalinkPlayerState
-{
- /// <summary>
- /// Gets the timestamp at which this state was last updated.
- /// </summary>
- public DateTimeOffset LastUpdate { get; internal set; }
-
- /// <summary>
- /// Gets the current playback position.
- /// </summary>
- public TimeSpan PlaybackPosition { get; internal set; }
-
- /// <summary>
- /// Gets the currently-played track.
- /// </summary>
- public LavalinkTrack CurrentTrack { get; internal set; }
-}
-
-/// <summary>
-/// The lavalink stats.
-/// </summary>
-internal sealed class LavalinkStats
-{
- /// <summary>
- /// Gets or sets the active players.
- /// </summary>
- [JsonProperty("playingPlayers")]
- public int ActivePlayers { get; set; }
-
- /// <summary>
- /// Gets or sets the total players.
- /// </summary>
- [JsonProperty("players")]
- public int TotalPlayers { get; set; }
-
- /// <summary>
- /// Gets the uptime.
- /// </summary>
- [JsonIgnore]
- public TimeSpan Uptime => TimeSpan.FromMilliseconds(this._uptime);
- [JsonProperty("uptime")]
- private readonly long _uptime;
-
- /// <summary>
- /// Gets or sets the cpu.
+ /// The lavalink state.
/// </summary>
- [JsonProperty("cpu")]
- public CpuStats Cpu { get; set; }
-
- /// <summary>
- /// Gets or sets the memory.
- /// </summary>
- [JsonProperty("memory")]
- public MemoryStats Memory { get; set; }
+ internal sealed class LavalinkState
+ {
+ /// <summary>
+ /// Gets the time.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset Time => Utilities.GetDateTimeOffsetFromMilliseconds(this._time);
+ [JsonProperty("time")]
+ private readonly long _time;
- /// <summary>
- /// Gets or sets the frames.
- /// </summary>
- [JsonProperty("frameStats")]
- public FrameStats Frames { get; set; }
+ /// <summary>
+ /// Gets the position.
+ /// </summary>
+ [JsonIgnore]
+ public TimeSpan Position => TimeSpan.FromMilliseconds(this._position);
+ [JsonProperty("position")]
+ private readonly long _position;
+ }
/// <summary>
- /// The cpu stats.
+ /// Represents current state of given player.
/// </summary>
- internal sealed class CpuStats
+ public sealed class LavalinkPlayerState
{
/// <summary>
- /// Gets or sets the cores.
+ /// Gets the timestamp at which this state was last updated.
/// </summary>
- [JsonProperty("cores")]
- public int Cores { get; set; }
+ public DateTimeOffset LastUpdate { get; internal set; }
/// <summary>
- /// Gets or sets the system load.
+ /// Gets the current playback position.
/// </summary>
- [JsonProperty("systemLoad")]
- public double SystemLoad { get; set; }
+ public TimeSpan PlaybackPosition { get; internal set; }
/// <summary>
- /// Gets or sets the lavalink load.
+ /// Gets the currently-played track.
/// </summary>
- [JsonProperty("lavalinkLoad")]
- public double LavalinkLoad { get; set; }
+ public LavalinkTrack CurrentTrack { get; internal set; }
}
/// <summary>
- /// The memory stats.
+ /// The lavalink stats.
/// </summary>
- internal sealed class MemoryStats
+ internal sealed class LavalinkStats
{
/// <summary>
- /// Gets or sets the reservable.
+ /// Gets or sets the active players.
/// </summary>
- [JsonProperty("reservable")]
- public long Reservable { get; set; }
+ [JsonProperty("playingPlayers")]
+ public int ActivePlayers { get; set; }
/// <summary>
- /// Gets or sets the used.
+ /// Gets or sets the total players.
/// </summary>
- [JsonProperty("used")]
- public long Used { get; set; }
+ [JsonProperty("players")]
+ public int TotalPlayers { get; set; }
/// <summary>
- /// Gets or sets the free.
+ /// Gets the uptime.
/// </summary>
- [JsonProperty("free")]
- public long Free { get; set; }
+ [JsonIgnore]
+ public TimeSpan Uptime => TimeSpan.FromMilliseconds(this._uptime);
+ [JsonProperty("uptime")]
+ private readonly long _uptime;
/// <summary>
- /// Gets or sets the allocated.
+ /// Gets or sets the cpu.
/// </summary>
- [JsonProperty("allocated")]
- public long Allocated { get; set; }
- }
+ [JsonProperty("cpu")]
+ public CpuStats Cpu { get; set; }
+
+ /// <summary>
+ /// Gets or sets the memory.
+ /// </summary>
+ [JsonProperty("memory")]
+ public MemoryStats Memory { get; set; }
+
+ /// <summary>
+ /// Gets or sets the frames.
+ /// </summary>
+ [JsonProperty("frameStats")]
+ public FrameStats Frames { get; set; }
- /// <summary>
- /// The frame stats.
- /// </summary>
- internal sealed class FrameStats
- {
/// <summary>
- /// Gets or sets the sent.
+ /// The cpu stats.
/// </summary>
- [JsonProperty("sent")]
- public int Sent { get; set; }
+ internal sealed class CpuStats
+ {
+ /// <summary>
+ /// Gets or sets the cores.
+ /// </summary>
+ [JsonProperty("cores")]
+ public int Cores { get; set; }
+
+ /// <summary>
+ /// Gets or sets the system load.
+ /// </summary>
+ [JsonProperty("systemLoad")]
+ public double SystemLoad { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lavalink load.
+ /// </summary>
+ [JsonProperty("lavalinkLoad")]
+ public double LavalinkLoad { get; set; }
+ }
/// <summary>
- /// Gets or sets the nulled.
+ /// The memory stats.
/// </summary>
- [JsonProperty("nulled")]
- public int Nulled { get; set; }
+ internal sealed class MemoryStats
+ {
+ /// <summary>
+ /// Gets or sets the reservable.
+ /// </summary>
+ [JsonProperty("reservable")]
+ public long Reservable { get; set; }
+
+ /// <summary>
+ /// Gets or sets the used.
+ /// </summary>
+ [JsonProperty("used")]
+ public long Used { get; set; }
+
+ /// <summary>
+ /// Gets or sets the free.
+ /// </summary>
+ [JsonProperty("free")]
+ public long Free { get; set; }
+
+ /// <summary>
+ /// Gets or sets the allocated.
+ /// </summary>
+ [JsonProperty("allocated")]
+ public long Allocated { get; set; }
+ }
/// <summary>
- /// Gets or sets the deficit.
+ /// The frame stats.
/// </summary>
- [JsonProperty("deficit")]
- public int Deficit { get; set; }
+ internal sealed class FrameStats
+ {
+ /// <summary>
+ /// Gets or sets the sent.
+ /// </summary>
+ [JsonProperty("sent")]
+ public int Sent { get; set; }
+
+ /// <summary>
+ /// Gets or sets the nulled.
+ /// </summary>
+ [JsonProperty("nulled")]
+ public int Nulled { get; set; }
+
+ /// <summary>
+ /// Gets or sets the deficit.
+ /// </summary>
+ [JsonProperty("deficit")]
+ public int Deficit { get; set; }
+ }
}
-}
-/// <summary>
-/// Represents statistics of Lavalink resource usage.
-/// </summary>
-public sealed class LavalinkStatistics
-{
/// <summary>
- /// Gets the number of currently-playing players.
+ /// Represents statistics of Lavalink resource usage.
/// </summary>
- public int ActivePlayers { get; private set; }
+ public sealed class LavalinkStatistics
+ {
+ /// <summary>
+ /// Gets the number of currently-playing players.
+ /// </summary>
+ public int ActivePlayers { get; private set; }
- /// <summary>
- /// Gets the total number of players.
- /// </summary>
- public int TotalPlayers { get; private set; }
+ /// <summary>
+ /// Gets the total number of players.
+ /// </summary>
+ public int TotalPlayers { get; private set; }
- /// <summary>
- /// Gets the node uptime.
- /// </summary>
- public TimeSpan Uptime { get; private set; }
+ /// <summary>
+ /// Gets the node uptime.
+ /// </summary>
+ public TimeSpan Uptime { get; private set; }
- /// <summary>
- /// Gets the number of CPU cores available.
- /// </summary>
- public int CpuCoreCount { get; private set; }
+ /// <summary>
+ /// Gets the number of CPU cores available.
+ /// </summary>
+ public int CpuCoreCount { get; private set; }
- /// <summary>
- /// Gets the total % of CPU resources in use on the system.
- /// </summary>
- public double CpuSystemLoad { get; private set; }
+ /// <summary>
+ /// Gets the total % of CPU resources in use on the system.
+ /// </summary>
+ public double CpuSystemLoad { get; private set; }
- /// <summary>
- /// Gets the total % of CPU resources used by lavalink.
- /// </summary>
- public double CpuLavalinkLoad { get; private set; }
+ /// <summary>
+ /// Gets the total % of CPU resources used by lavalink.
+ /// </summary>
+ public double CpuLavalinkLoad { get; private set; }
- /// <summary>
- /// Gets the amount of reservable RAM, in bytes.
- /// </summary>
- public long RamReservable { get; private set; }
+ /// <summary>
+ /// Gets the amount of reservable RAM, in bytes.
+ /// </summary>
+ public long RamReservable { get; private set; }
- /// <summary>
- /// Gets the amount of used RAM, in bytes.
- /// </summary>
- public long RamUsed { get; private set; }
+ /// <summary>
+ /// Gets the amount of used RAM, in bytes.
+ /// </summary>
+ public long RamUsed { get; private set; }
- /// <summary>
- /// Gets the amount of free RAM, in bytes.
- /// </summary>
- public long RamFree { get; private set; }
+ /// <summary>
+ /// Gets the amount of free RAM, in bytes.
+ /// </summary>
+ public long RamFree { get; private set; }
- /// <summary>
- /// Gets the amount of allocated RAM, in bytes.
- /// </summary>
- public long RamAllocated { get; private set; }
+ /// <summary>
+ /// Gets the amount of allocated RAM, in bytes.
+ /// </summary>
+ public long RamAllocated { get; private set; }
- /// <summary>
- /// Gets the average number of sent frames per minute.
- /// </summary>
- public int AverageSentFramesPerMinute { get; private set; }
+ /// <summary>
+ /// Gets the average number of sent frames per minute.
+ /// </summary>
+ public int AverageSentFramesPerMinute { get; private set; }
- /// <summary>
- /// Gets the average number of frames that were sent as null per minute.
- /// </summary>
- public int AverageNulledFramesPerMinute { get; private set; }
+ /// <summary>
+ /// Gets the average number of frames that were sent as null per minute.
+ /// </summary>
+ public int AverageNulledFramesPerMinute { get; private set; }
- /// <summary>
- /// Gets the average frame deficit per minute.
- /// </summary>
- public int AverageDeficitFramesPerMinute { get; private set; }
+ /// <summary>
+ /// Gets the average frame deficit per minute.
+ /// </summary>
+ public int AverageDeficitFramesPerMinute { get; private set; }
- internal bool Updated;
+ internal bool Updated;
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkStatistics"/> class.
- /// </summary>
- internal LavalinkStatistics()
- {
- this.Updated = false;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkStatistics"/> class.
+ /// </summary>
+ internal LavalinkStatistics()
+ {
+ this.Updated = false;
+ }
- /// <summary>
- /// Updates the stats.
- /// </summary>
- /// <param name="newStats">The new stats.</param>
- internal void Update(LavalinkStats newStats)
- {
- if (!this.Updated)
- this.Updated = true;
-
- this.ActivePlayers = newStats.ActivePlayers;
- this.TotalPlayers = newStats.TotalPlayers;
- this.Uptime = newStats.Uptime;
-
- this.CpuCoreCount = newStats.Cpu.Cores;
- this.CpuSystemLoad = newStats.Cpu.SystemLoad;
- this.CpuLavalinkLoad = newStats.Cpu.LavalinkLoad;
-
- this.RamReservable = newStats.Memory.Reservable;
- this.RamUsed = newStats.Memory.Used;
- this.RamFree = newStats.Memory.Free;
- this.RamAllocated = newStats.Memory.Allocated;
- this.RamReservable = newStats.Memory.Reservable;
-
- this.AverageSentFramesPerMinute = newStats.Frames?.Sent ?? 0;
- this.AverageNulledFramesPerMinute = newStats.Frames?.Nulled ?? 0;
- this.AverageDeficitFramesPerMinute = newStats.Frames?.Deficit ?? 0;
+ /// <summary>
+ /// Updates the stats.
+ /// </summary>
+ /// <param name="newStats">The new stats.</param>
+ internal void Update(LavalinkStats newStats)
+ {
+ if (!this.Updated)
+ this.Updated = true;
+
+ this.ActivePlayers = newStats.ActivePlayers;
+ this.TotalPlayers = newStats.TotalPlayers;
+ this.Uptime = newStats.Uptime;
+
+ this.CpuCoreCount = newStats.Cpu.Cores;
+ this.CpuSystemLoad = newStats.Cpu.SystemLoad;
+ this.CpuLavalinkLoad = newStats.Cpu.LavalinkLoad;
+
+ this.RamReservable = newStats.Memory.Reservable;
+ this.RamUsed = newStats.Memory.Used;
+ this.RamFree = newStats.Memory.Free;
+ this.RamAllocated = newStats.Memory.Allocated;
+ this.RamReservable = newStats.Memory.Reservable;
+
+ this.AverageSentFramesPerMinute = newStats.Frames?.Sent ?? 0;
+ this.AverageNulledFramesPerMinute = newStats.Frames?.Nulled ?? 0;
+ this.AverageDeficitFramesPerMinute = newStats.Frames?.Deficit ?? 0;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/Entities/LavalinkVoiceServerUpdate.cs b/DisCatSharp.Lavalink/Entities/LavalinkVoiceServerUpdate.cs
index f5c50d5aa..c591e2b66 100644
--- a/DisCatSharp.Lavalink/Entities/LavalinkVoiceServerUpdate.cs
+++ b/DisCatSharp.Lavalink/Entities/LavalinkVoiceServerUpdate.cs
@@ -1,94 +1,95 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using DisCatSharp.EventArgs;
using Newtonsoft.Json;
-namespace DisCatSharp.Lavalink.Entities;
-
-/// <summary>
-/// The lavalink voice server update.
-/// </summary>
-internal sealed class LavalinkVoiceServerUpdate
+namespace DisCatSharp.Lavalink.Entities
{
/// <summary>
- /// Gets the token.
+ /// The lavalink voice server update.
/// </summary>
- [JsonProperty("token")]
- public string Token { get; }
+ internal sealed class LavalinkVoiceServerUpdate
+ {
+ /// <summary>
+ /// Gets the token.
+ /// </summary>
+ [JsonProperty("token")]
+ public string Token { get; }
- /// <summary>
- /// Gets the guild id.
- /// </summary>
- [JsonProperty("guild_id")]
- public string GuildId { get; }
+ /// <summary>
+ /// Gets the guild id.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public string GuildId { get; }
- /// <summary>
- /// Gets the endpoint.
- /// </summary>
- [JsonProperty("endpoint")]
- public string Endpoint { get; }
+ /// <summary>
+ /// Gets the endpoint.
+ /// </summary>
+ [JsonProperty("endpoint")]
+ public string Endpoint { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkVoiceServerUpdate"/> class.
- /// </summary>
- /// <param name="vsu">The vsu.</param>
- internal LavalinkVoiceServerUpdate(VoiceServerUpdateEventArgs vsu)
- {
- this.Token = vsu.VoiceToken;
- this.GuildId = vsu.Guild.Id.ToString(CultureInfo.InvariantCulture);
- this.Endpoint = vsu.Endpoint;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkVoiceServerUpdate"/> class.
+ /// </summary>
+ /// <param name="vsu">The vsu.</param>
+ internal LavalinkVoiceServerUpdate(VoiceServerUpdateEventArgs vsu)
+ {
+ this.Token = vsu.VoiceToken;
+ this.GuildId = vsu.Guild.Id.ToString(CultureInfo.InvariantCulture);
+ this.Endpoint = vsu.Endpoint;
+ }
}
-}
-/// <summary>
-/// The lavalink voice update.
-/// </summary>
-internal sealed class LavalinkVoiceUpdate : LavalinkPayload
-{
/// <summary>
- /// Gets the session id.
+ /// The lavalink voice update.
/// </summary>
- [JsonProperty("sessionId")]
- public string SessionId { get; }
+ internal sealed class LavalinkVoiceUpdate : LavalinkPayload
+ {
+ /// <summary>
+ /// Gets the session id.
+ /// </summary>
+ [JsonProperty("sessionId")]
+ public string SessionId { get; }
- /// <summary>
- /// Gets the event.
- /// </summary>
- [JsonProperty("event")]
- internal LavalinkVoiceServerUpdate Event { get; }
+ /// <summary>
+ /// Gets the event.
+ /// </summary>
+ [JsonProperty("event")]
+ internal LavalinkVoiceServerUpdate Event { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkVoiceUpdate"/> class.
- /// </summary>
- /// <param name="vstu">The vstu.</param>
- /// <param name="vsrvu">The vsrvu.</param>
- public LavalinkVoiceUpdate(VoiceStateUpdateEventArgs vstu, VoiceServerUpdateEventArgs vsrvu)
- : base("voiceUpdate", vstu.Guild.Id.ToString(CultureInfo.InvariantCulture))
- {
- this.SessionId = vstu.SessionId;
- this.Event = new LavalinkVoiceServerUpdate(vsrvu);
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkVoiceUpdate"/> class.
+ /// </summary>
+ /// <param name="vstu">The vstu.</param>
+ /// <param name="vsrvu">The vsrvu.</param>
+ public LavalinkVoiceUpdate(VoiceStateUpdateEventArgs vstu, VoiceServerUpdateEventArgs vsrvu)
+ : base("voiceUpdate", vstu.Guild.Id.ToString(CultureInfo.InvariantCulture))
+ {
+ this.SessionId = vstu.SessionId;
+ this.Event = new LavalinkVoiceServerUpdate(vsrvu);
+ }
}
}
diff --git a/DisCatSharp.Lavalink/Entities/LavalinkVoiceStateUpdatePayload.cs b/DisCatSharp.Lavalink/Entities/LavalinkVoiceStateUpdatePayload.cs
index 38a8ba139..f95daaecc 100644
--- a/DisCatSharp.Lavalink/Entities/LavalinkVoiceStateUpdatePayload.cs
+++ b/DisCatSharp.Lavalink/Entities/LavalinkVoiceStateUpdatePayload.cs
@@ -1,67 +1,68 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Lavalink.Entities;
-
-/// <summary>
-/// The voice state update payload.
-/// </summary>
-internal sealed class VoiceStateUpdatePayload
+namespace DisCatSharp.Lavalink.Entities
{
/// <summary>
- /// Gets or sets the guild id.
+ /// The voice state update payload.
/// </summary>
- [JsonProperty("guild_id")]
- public ulong GuildId { get; set; }
+ internal sealed class VoiceStateUpdatePayload
+ {
+ /// <summary>
+ /// Gets or sets the guild id.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id")]
- public ulong? ChannelId { get; set; }
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public ulong? ChannelId { get; set; }
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? UserId { get; set; }
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? UserId { get; set; }
- /// <summary>
- /// Gets or sets the session id.
- /// </summary>
- [JsonProperty("session_id", NullValueHandling = NullValueHandling.Ignore)]
- public string SessionId { get; set; }
+ /// <summary>
+ /// Gets or sets the session id.
+ /// </summary>
+ [JsonProperty("session_id", NullValueHandling = NullValueHandling.Ignore)]
+ public string SessionId { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether deafened.
- /// </summary>
- [JsonProperty("self_deaf")]
- public bool Deafened { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether deafened.
+ /// </summary>
+ [JsonProperty("self_deaf")]
+ public bool Deafened { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether muted.
- /// </summary>
- [JsonProperty("self_mute")]
- public bool Muted { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether muted.
+ /// </summary>
+ [JsonProperty("self_mute")]
+ public bool Muted { get; set; }
+ }
}
diff --git a/DisCatSharp.Lavalink/Enums/LavalinkRoutePlannerType.cs b/DisCatSharp.Lavalink/Enums/LavalinkRoutePlannerType.cs
index 6280aaaf6..247ea78a4 100644
--- a/DisCatSharp.Lavalink/Enums/LavalinkRoutePlannerType.cs
+++ b/DisCatSharp.Lavalink/Enums/LavalinkRoutePlannerType.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// The lavalink route planner type.
-/// </summary>
-public enum LavalinkRoutePlannerType
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// Route planner that switches the IP on ban.
+ /// The lavalink route planner type.
/// </summary>
- RotatingIpRoutePlanner = 1,
+ public enum LavalinkRoutePlannerType
+ {
+ /// <summary>
+ /// Route planner that switches the IP on ban.
+ /// </summary>
+ RotatingIpRoutePlanner = 1,
- /// <summary>
- /// Route planner that selects random IP addresses from the given block.
- /// </summary>
- BalancingIpRoutePlanner = 2,
+ /// <summary>
+ /// Route planner that selects random IP addresses from the given block.
+ /// </summary>
+ BalancingIpRoutePlanner = 2,
- /// <summary>
- /// Route planner that switches the IP on every clock update.
- /// </summary>
- NanoIpRoutePlanner = 3,
+ /// <summary>
+ /// Route planner that switches the IP on every clock update.
+ /// </summary>
+ NanoIpRoutePlanner = 3,
- /// <summary>
- /// Route planner that switches the IP on every clock update and rotates to next IP block on a ban as a fallback.
- /// </summary>
- RotatingNanoIpRoutePlanner = 4
+ /// <summary>
+ /// Route planner that switches the IP on every clock update and rotates to next IP block on a ban as a fallback.
+ /// </summary>
+ RotatingNanoIpRoutePlanner = 4
+ }
}
diff --git a/DisCatSharp.Lavalink/Enums/LavalinkSearchType.cs b/DisCatSharp.Lavalink/Enums/LavalinkSearchType.cs
index f3c1d089e..83d744975 100644
--- a/DisCatSharp.Lavalink/Enums/LavalinkSearchType.cs
+++ b/DisCatSharp.Lavalink/Enums/LavalinkSearchType.cs
@@ -1,44 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// The lavalink search type.
-/// </summary>
-public enum LavalinkSearchType
+namespace DisCatSharp.Lavalink
{
- /// <summary>
- /// Search on SoundCloud
- /// </summary>
- SoundCloud,
/// <summary>
- /// Search on Youtube.
+ /// The lavalink search type.
/// </summary>
- Youtube,
+ public enum LavalinkSearchType
+ {
+ /// <summary>
+ /// Search on SoundCloud
+ /// </summary>
+ SoundCloud,
- /// <summary>
- /// Provide Lavalink with a plain URL.
- /// </summary>
- Plain
+ /// <summary>
+ /// Search on Youtube.
+ /// </summary>
+ Youtube,
+
+ /// <summary>
+ /// Provide Lavalink with a plain URL.
+ /// </summary>
+ Plain
+ }
}
diff --git a/DisCatSharp.Lavalink/EventArgs/NodeDisconnectedEventArgs.cs b/DisCatSharp.Lavalink/EventArgs/NodeDisconnectedEventArgs.cs
index a7eab8b50..94bd001da 100644
--- a/DisCatSharp.Lavalink/EventArgs/NodeDisconnectedEventArgs.cs
+++ b/DisCatSharp.Lavalink/EventArgs/NodeDisconnectedEventArgs.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.EventArgs;
-namespace DisCatSharp.Lavalink.EventArgs;
-
-/// <summary>
-/// Represents event arguments for Lavalink node disconnection.
-/// </summary>
-public sealed class NodeDisconnectedEventArgs : DiscordEventArgs
+namespace DisCatSharp.Lavalink.EventArgs
{
/// <summary>
- /// Gets the node that was disconnected.
+ /// Represents event arguments for Lavalink node disconnection.
/// </summary>
- public LavalinkNodeConnection LavalinkNode { get; }
+ public sealed class NodeDisconnectedEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the node that was disconnected.
+ /// </summary>
+ public LavalinkNodeConnection LavalinkNode { get; }
- /// <summary>
- /// Gets whether disconnect was clean.
- /// </summary>
- public bool IsCleanClose { get; }
+ /// <summary>
+ /// Gets whether disconnect was clean.
+ /// </summary>
+ public bool IsCleanClose { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="NodeDisconnectedEventArgs"/> class.
- /// </summary>
- /// <param name="node">The node.</param>
- /// <param name="isClean">If true, is clean.</param>
- internal NodeDisconnectedEventArgs(LavalinkNodeConnection node, bool isClean) : base(node.Discord.ServiceProvider)
- {
- this.LavalinkNode = node;
- this.IsCleanClose = isClean;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NodeDisconnectedEventArgs"/> class.
+ /// </summary>
+ /// <param name="node">The node.</param>
+ /// <param name="isClean">If true, is clean.</param>
+ internal NodeDisconnectedEventArgs(LavalinkNodeConnection node, bool isClean) : base(node.Discord.ServiceProvider)
+ {
+ this.LavalinkNode = node;
+ this.IsCleanClose = isClean;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/EventArgs/PlayerUpdateEventArgs.cs b/DisCatSharp.Lavalink/EventArgs/PlayerUpdateEventArgs.cs
index 5bed847f9..940ba13f5 100644
--- a/DisCatSharp.Lavalink/EventArgs/PlayerUpdateEventArgs.cs
+++ b/DisCatSharp.Lavalink/EventArgs/PlayerUpdateEventArgs.cs
@@ -1,62 +1,63 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.Lavalink.EventArgs;
-
-/// <summary>
-/// Represents arguments for player update event.
-/// </summary>
-public sealed class PlayerUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.Lavalink.EventArgs
{
/// <summary>
- /// Gets the timestamp at which this event was emitted.
+ /// Represents arguments for player update event.
/// </summary>
- public DateTimeOffset Timestamp { get; }
+ public sealed class PlayerUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the timestamp at which this event was emitted.
+ /// </summary>
+ public DateTimeOffset Timestamp { get; }
- /// <summary>
- /// Gets the position in the playback stream.
- /// </summary>
- public TimeSpan Position { get; }
+ /// <summary>
+ /// Gets the position in the playback stream.
+ /// </summary>
+ public TimeSpan Position { get; }
- /// <summary>
- /// Gets the player that emitted this event.
- /// </summary>
- public LavalinkGuildConnection Player { get; }
+ /// <summary>
+ /// Gets the player that emitted this event.
+ /// </summary>
+ public LavalinkGuildConnection Player { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="PlayerUpdateEventArgs"/> class.
- /// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="timestamp">The timestamp.</param>
- /// <param name="position">The position.</param>
- internal PlayerUpdateEventArgs(LavalinkGuildConnection lvl, DateTimeOffset timestamp,
- TimeSpan position) : base(lvl.Node.Discord.ServiceProvider)
- {
- this.Player = lvl;
- this.Timestamp = timestamp;
- this.Position = position;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PlayerUpdateEventArgs"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="timestamp">The timestamp.</param>
+ /// <param name="position">The position.</param>
+ internal PlayerUpdateEventArgs(LavalinkGuildConnection lvl, DateTimeOffset timestamp,
+ TimeSpan position) : base(lvl.Node.Discord.ServiceProvider)
+ {
+ this.Player = lvl;
+ this.Timestamp = timestamp;
+ this.Position = position;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/EventArgs/StatisticsReceivedEventArgs.cs b/DisCatSharp.Lavalink/EventArgs/StatisticsReceivedEventArgs.cs
index ffb77e07d..e694503da 100644
--- a/DisCatSharp.Lavalink/EventArgs/StatisticsReceivedEventArgs.cs
+++ b/DisCatSharp.Lavalink/EventArgs/StatisticsReceivedEventArgs.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
using DisCatSharp.Lavalink.Entities;
-namespace DisCatSharp.Lavalink.EventArgs;
-
-/// <summary>
-/// Represents arguments for Lavalink statistics received.
-/// </summary>
-public sealed class StatisticsReceivedEventArgs : DiscordEventArgs
+namespace DisCatSharp.Lavalink.EventArgs
{
/// <summary>
- /// Gets the Lavalink statistics received.
+ /// Represents arguments for Lavalink statistics received.
/// </summary>
- public LavalinkStatistics Statistics { get; }
+ public sealed class StatisticsReceivedEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the Lavalink statistics received.
+ /// </summary>
+ public LavalinkStatistics Statistics { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="StatisticsReceivedEventArgs"/> class.
- /// </summary>
- /// <param name="provider">Service provider.</param>
- /// <param name="stats">The stats.</param>
- internal StatisticsReceivedEventArgs(IServiceProvider provider, LavalinkStatistics stats) : base(provider)
- {
- this.Statistics = stats;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StatisticsReceivedEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">Service provider.</param>
+ /// <param name="stats">The stats.</param>
+ internal StatisticsReceivedEventArgs(IServiceProvider provider, LavalinkStatistics stats) : base(provider)
+ {
+ this.Statistics = stats;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/EventArgs/TrackEventArgs.cs b/DisCatSharp.Lavalink/EventArgs/TrackEventArgs.cs
index 8858deaf1..8ef17d050 100644
--- a/DisCatSharp.Lavalink/EventArgs/TrackEventArgs.cs
+++ b/DisCatSharp.Lavalink/EventArgs/TrackEventArgs.cs
@@ -1,261 +1,262 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.EventArgs;
-namespace DisCatSharp.Lavalink.EventArgs;
-
-/// <summary>
-/// The event type.
-/// </summary>
-internal enum EventType
+namespace DisCatSharp.Lavalink.EventArgs
{
/// <summary>
- /// Track start event
+ /// The event type.
/// </summary>
- TrackStartEvent,
+ internal enum EventType
+ {
+ /// <summary>
+ /// Track start event
+ /// </summary>
+ TrackStartEvent,
- /// <summary>
- /// Track end event
- /// </summary>
- TrackEndEvent,
+ /// <summary>
+ /// Track end event
+ /// </summary>
+ TrackEndEvent,
- /// <summary>
- /// Track exception event
- /// </summary>
- TrackExceptionEvent,
+ /// <summary>
+ /// Track exception event
+ /// </summary>
+ TrackExceptionEvent,
- /// <summary>
- /// Track stuck event
- /// </summary>
- TrackStuckEvent,
+ /// <summary>
+ /// Track stuck event
+ /// </summary>
+ TrackStuckEvent,
- /// <summary>
- /// Websocket closed event
- /// </summary>
- WebSocketClosedEvent
-}
+ /// <summary>
+ /// Websocket closed event
+ /// </summary>
+ WebSocketClosedEvent
+ }
-/// <summary>
-/// Represents arguments for a track playback start event.
-/// </summary>
-public sealed class TrackStartEventArgs : DiscordEventArgs
-{
/// <summary>
- /// Gets the track that started playing.
+ /// Represents arguments for a track playback start event.
/// </summary>
- public LavalinkTrack Track { get; }
+ public sealed class TrackStartEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the track that started playing.
+ /// </summary>
+ public LavalinkTrack Track { get; }
- /// <summary>
- /// Gets the player that started playback.
- /// </summary>
- public LavalinkGuildConnection Player { get; }
+ /// <summary>
+ /// Gets the player that started playback.
+ /// </summary>
+ public LavalinkGuildConnection Player { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TrackStartEventArgs"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="track">The track.</param>
+ internal TrackStartEventArgs(LavalinkGuildConnection lvl, LavalinkTrack track) : base(lvl.Node.Discord.ServiceProvider)
+ {
+ this.Track = track;
+ this.Player = lvl;
+ }
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="TrackStartEventArgs"/> class.
+ /// Represents track finish data
/// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="track">The track.</param>
- internal TrackStartEventArgs(LavalinkGuildConnection lvl, LavalinkTrack track) : base(lvl.Node.Discord.ServiceProvider)
+ internal struct TrackFinishData
{
- this.Track = track;
- this.Player = lvl;
+ /// <summary>
+ /// Gets or sets the track.
+ /// </summary>
+ public string Track { get; set; }
+ /// <summary>
+ /// Gets or sets the reason.
+ /// </summary>
+ public TrackEndReason Reason { get; set; }
}
-}
-/// <summary>
-/// Represents track finish data
-/// </summary>
-internal struct TrackFinishData
-{
- /// <summary>
- /// Gets or sets the track.
- /// </summary>
- public string Track { get; set; }
/// <summary>
- /// Gets or sets the reason.
+ /// Represents arguments for a track playback finish event.
/// </summary>
- public TrackEndReason Reason { get; set; }
-}
+ public sealed class TrackFinishEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the track that finished playing.
+ /// </summary>
+ public LavalinkTrack Track { get; }
-/// <summary>
-/// Represents arguments for a track playback finish event.
-/// </summary>
-public sealed class TrackFinishEventArgs : DiscordEventArgs
-{
- /// <summary>
- /// Gets the track that finished playing.
- /// </summary>
- public LavalinkTrack Track { get; }
+ /// <summary>
+ /// Gets the reason why the track stopped playing.
+ /// </summary>
+ public TrackEndReason Reason { get; }
- /// <summary>
- /// Gets the reason why the track stopped playing.
- /// </summary>
- public TrackEndReason Reason { get; }
+ /// <summary>
+ /// Gets the player that finished playback.
+ /// </summary>
+ public LavalinkGuildConnection Player { get; }
- /// <summary>
- /// Gets the player that finished playback.
- /// </summary>
- public LavalinkGuildConnection Player { get; }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TrackFinishEventArgs"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="track">The track.</param>
+ /// <param name="reason">The reason.</param>
+ internal TrackFinishEventArgs(LavalinkGuildConnection lvl, LavalinkTrack track, TrackEndReason reason) : base(lvl.Node.Discord.ServiceProvider)
+ {
+ this.Track = track;
+ this.Reason = reason;
+ this.Player = lvl;
+ }
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="TrackFinishEventArgs"/> class.
+ /// Represents a reason why a track finished playing.
/// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="track">The track.</param>
- /// <param name="reason">The reason.</param>
- internal TrackFinishEventArgs(LavalinkGuildConnection lvl, LavalinkTrack track, TrackEndReason reason) : base(lvl.Node.Discord.ServiceProvider)
+ public enum TrackEndReason
{
- this.Track = track;
- this.Reason = reason;
- this.Player = lvl;
+ /// <summary>
+ /// This means that the track itself emitted a terminator. This is usually caused by the track reaching the end,
+ /// however it will also be used when it ends due to an exception.
+ /// </summary>
+ Finished,
+ /// <summary>
+ /// This means that the track failed to start, throwing an exception before providing any audio.
+ /// </summary>
+ LoadFailed,
+ /// <summary>
+ /// The track was stopped due to the player being stopped by either calling stop() or playTrack(null).
+ /// </summary>
+ Stopped,
+ /// <summary>
+ /// The track stopped playing because a new track started playing. Note that with this reason, the old track will still
+ /// play until either its buffer runs out or audio from the new track is available.
+ /// </summary>
+ Replaced,
+ /// <summary>
+ /// The track was stopped because the cleanup threshold for the audio player was reached. This triggers when the amount
+ /// of time passed since the last call to AudioPlayer#provide() has reached the threshold specified in player manager
+ /// configuration. This may also indicate either a leaked audio player which was discarded, but not stopped.
+ /// </summary>
+ Cleanup
}
-}
-/// <summary>
-/// Represents a reason why a track finished playing.
-/// </summary>
-public enum TrackEndReason
-{
- /// <summary>
- /// This means that the track itself emitted a terminator. This is usually caused by the track reaching the end,
- /// however it will also be used when it ends due to an exception.
- /// </summary>
- Finished,
- /// <summary>
- /// This means that the track failed to start, throwing an exception before providing any audio.
- /// </summary>
- LoadFailed,
/// <summary>
- /// The track was stopped due to the player being stopped by either calling stop() or playTrack(null).
+ /// Represents track stuck data
/// </summary>
- Stopped,
- /// <summary>
- /// The track stopped playing because a new track started playing. Note that with this reason, the old track will still
- /// play until either its buffer runs out or audio from the new track is available.
- /// </summary>
- Replaced,
- /// <summary>
- /// The track was stopped because the cleanup threshold for the audio player was reached. This triggers when the amount
- /// of time passed since the last call to AudioPlayer#provide() has reached the threshold specified in player manager
- /// configuration. This may also indicate either a leaked audio player which was discarded, but not stopped.
- /// </summary>
- Cleanup
-}
+ internal struct TrackStuckData
+ {
+ /// <summary>
+ /// Gets or sets the threshold.
+ /// </summary>
+ public long Threshold { get; set; }
+ /// <summary>
+ /// Gets or sets the track.
+ /// </summary>
+ public string Track { get; set; }
+ }
-/// <summary>
-/// Represents track stuck data
-/// </summary>
-internal struct TrackStuckData
-{
- /// <summary>
- /// Gets or sets the threshold.
- /// </summary>
- public long Threshold { get; set; }
/// <summary>
- /// Gets or sets the track.
+ /// Represents event arguments for a track stuck event.
/// </summary>
- public string Track { get; set; }
-}
+ public sealed class TrackStuckEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the millisecond threshold for the stuck event.
+ /// </summary>
+ public long ThresholdMilliseconds { get; }
-/// <summary>
-/// Represents event arguments for a track stuck event.
-/// </summary>
-public sealed class TrackStuckEventArgs : DiscordEventArgs
-{
- /// <summary>
- /// Gets the millisecond threshold for the stuck event.
- /// </summary>
- public long ThresholdMilliseconds { get; }
+ /// <summary>
+ /// Gets the track that got stuck.
+ /// </summary>
+ public LavalinkTrack Track { get; }
- /// <summary>
- /// Gets the track that got stuck.
- /// </summary>
- public LavalinkTrack Track { get; }
+ /// <summary>
+ /// Gets the player that got stuck.
+ /// </summary>
+ public LavalinkGuildConnection Player { get; }
- /// <summary>
- /// Gets the player that got stuck.
- /// </summary>
- public LavalinkGuildConnection Player { get; }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TrackStuckEventArgs"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="thresholdMs">The threshold ms.</param>
+ /// <param name="track">The track.</param>
+ internal TrackStuckEventArgs(LavalinkGuildConnection lvl, long thresholdMs, LavalinkTrack track) : base(lvl.Node.Discord.ServiceProvider)
+ {
+ this.ThresholdMilliseconds = thresholdMs;
+ this.Track = track;
+ this.Player = lvl;
+ }
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="TrackStuckEventArgs"/> class.
+ /// Represents track exception data
/// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="thresholdMs">The threshold ms.</param>
- /// <param name="track">The track.</param>
- internal TrackStuckEventArgs(LavalinkGuildConnection lvl, long thresholdMs, LavalinkTrack track) : base(lvl.Node.Discord.ServiceProvider)
+ internal struct TrackExceptionData
{
- this.ThresholdMilliseconds = thresholdMs;
- this.Track = track;
- this.Player = lvl;
+ /// <summary>
+ /// Gets or sets the error.
+ /// </summary>
+ public string Error { get; set; }
+ /// <summary>
+ /// Gets or sets the track.
+ /// </summary>
+ public string Track { get; set; }
}
-}
-
-/// <summary>
-/// Represents track exception data
-/// </summary>
-internal struct TrackExceptionData
-{
- /// <summary>
- /// Gets or sets the error.
- /// </summary>
- public string Error { get; set; }
- /// <summary>
- /// Gets or sets the track.
- /// </summary>
- public string Track { get; set; }
-}
-/// <summary>
-/// Represents event arguments for a track exception event.
-/// </summary>
-public sealed class TrackExceptionEventArgs : DiscordEventArgs
-{
/// <summary>
- /// Gets the error that occurred during playback.
+ /// Represents event arguments for a track exception event.
/// </summary>
- public string Error { get; }
+ public sealed class TrackExceptionEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the error that occurred during playback.
+ /// </summary>
+ public string Error { get; }
- /// <summary>
- /// Gets the track that got stuck.
- /// </summary>
- public LavalinkTrack Track { get; }
+ /// <summary>
+ /// Gets the track that got stuck.
+ /// </summary>
+ public LavalinkTrack Track { get; }
- /// <summary>
- /// Gets the player that got stuck.
- /// </summary>
- public LavalinkGuildConnection Player { get; }
+ /// <summary>
+ /// Gets the player that got stuck.
+ /// </summary>
+ public LavalinkGuildConnection Player { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="TrackExceptionEventArgs"/> class.
- /// </summary>
- /// <param name="lvl">The lvl.</param>
- /// <param name="error">The error.</param>
- /// <param name="track">The track.</param>
- internal TrackExceptionEventArgs(LavalinkGuildConnection lvl, string error, LavalinkTrack track) : base(lvl.Node.Discord.ServiceProvider)
- {
- this.Error = error;
- this.Track = track;
- this.Player = lvl;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TrackExceptionEventArgs"/> class.
+ /// </summary>
+ /// <param name="lvl">The lvl.</param>
+ /// <param name="error">The error.</param>
+ /// <param name="track">The track.</param>
+ internal TrackExceptionEventArgs(LavalinkGuildConnection lvl, string error, LavalinkTrack track) : base(lvl.Node.Discord.ServiceProvider)
+ {
+ this.Error = error;
+ this.Track = track;
+ this.Player = lvl;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/EventArgs/WebSocketCloseEventArgs.cs b/DisCatSharp.Lavalink/EventArgs/WebSocketCloseEventArgs.cs
index 3cddb6b0c..1ffaefd25 100644
--- a/DisCatSharp.Lavalink/EventArgs/WebSocketCloseEventArgs.cs
+++ b/DisCatSharp.Lavalink/EventArgs/WebSocketCloseEventArgs.cs
@@ -1,62 +1,63 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.Lavalink.EventArgs;
-
-/// <summary>
-/// Represents arguments for <see cref="LavalinkGuildConnection.DiscordWebSocketClosed"/> event.
-/// </summary>
-public sealed class WebSocketCloseEventArgs : DiscordEventArgs
+namespace DisCatSharp.Lavalink.EventArgs
{
/// <summary>
- /// Gets the WebSocket close code.
+ /// Represents arguments for <see cref="LavalinkGuildConnection.DiscordWebSocketClosed"/> event.
/// </summary>
- public int Code { get; }
+ public sealed class WebSocketCloseEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the WebSocket close code.
+ /// </summary>
+ public int Code { get; }
- /// <summary>
- /// Gets the WebSocket close reason.
- /// </summary>
- public string Reason { get; }
+ /// <summary>
+ /// Gets the WebSocket close reason.
+ /// </summary>
+ public string Reason { get; }
- /// <summary>
- /// Gets whether the termination was initiated by the remote party (i.e. Discord).
- /// </summary>
- public bool Remote { get; }
+ /// <summary>
+ /// Gets whether the termination was initiated by the remote party (i.e. Discord).
+ /// </summary>
+ public bool Remote { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="WebSocketCloseEventArgs"/> class.
- /// </summary>
- /// <param name="code">The code.</param>
- /// <param name="reason">The reason.</param>
- /// <param name="remote">If true, remote.</param>
- /// <param name="provider">Service provider.</param>
- internal WebSocketCloseEventArgs(int code, string reason, bool remote, IServiceProvider provider) : base(provider)
- {
- this.Code = code;
- this.Reason = reason;
- this.Remote = remote;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebSocketCloseEventArgs"/> class.
+ /// </summary>
+ /// <param name="code">The code.</param>
+ /// <param name="reason">The reason.</param>
+ /// <param name="remote">If true, remote.</param>
+ /// <param name="provider">Service provider.</param>
+ internal WebSocketCloseEventArgs(int code, string reason, bool remote, IServiceProvider provider) : base(provider)
+ {
+ this.Code = code;
+ this.Reason = reason;
+ this.Remote = remote;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/LavalinkConfiguration.cs b/DisCatSharp.Lavalink/LavalinkConfiguration.cs
index 3182d62d0..7261a3fd8 100644
--- a/DisCatSharp.Lavalink/LavalinkConfiguration.cs
+++ b/DisCatSharp.Lavalink/LavalinkConfiguration.cs
@@ -1,115 +1,116 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
using DisCatSharp.Net;
using Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// Lavalink connection configuration.
-/// </summary>
-public sealed class LavalinkConfiguration
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// Sets the endpoint for Lavalink REST.
- /// <para>Defaults to 127.0.0.1 on port 2333.</para>
+ /// Lavalink connection configuration.
/// </summary>
- public ConnectionEndpoint RestEndpoint { internal get; set; } = new("127.0.0.1", 2333);
+ public sealed class LavalinkConfiguration
+ {
+ /// <summary>
+ /// Sets the endpoint for Lavalink REST.
+ /// <para>Defaults to 127.0.0.1 on port 2333.</para>
+ /// </summary>
+ public ConnectionEndpoint RestEndpoint { internal get; set; } = new("127.0.0.1", 2333);
- /// <summary>
- /// Sets the endpoint for the Lavalink Websocket connection.
- /// <para>Defaults to 127.0.0.1 on port 2333.</para>
- /// </summary>
- public ConnectionEndpoint SocketEndpoint { internal get; set; } = new("127.0.0.1", 2333);
+ /// <summary>
+ /// Sets the endpoint for the Lavalink Websocket connection.
+ /// <para>Defaults to 127.0.0.1 on port 2333.</para>
+ /// </summary>
+ public ConnectionEndpoint SocketEndpoint { internal get; set; } = new("127.0.0.1", 2333);
- /// <summary>
- /// Sets whether the connection wrapper should attempt automatic reconnects should the connection drop.
- /// <para>Defaults to true.</para>
- /// </summary>
- public bool SocketAutoReconnect { internal get; set; } = true;
+ /// <summary>
+ /// Sets whether the connection wrapper should attempt automatic reconnects should the connection drop.
+ /// <para>Defaults to true.</para>
+ /// </summary>
+ public bool SocketAutoReconnect { internal get; set; } = true;
- /// <summary>
- /// Sets the password for the Lavalink connection.
- /// <para>Defaults to "youshallnotpass".</para>
- /// </summary>
- public string Password { internal get; set; } = "youshallnotpass";
+ /// <summary>
+ /// Sets the password for the Lavalink connection.
+ /// <para>Defaults to "youshallnotpass".</para>
+ /// </summary>
+ public string Password { internal get; set; } = "youshallnotpass";
- /// <summary>
- /// Sets the resume key for the Lavalink connection.
- /// <para>This will allow existing voice sessions to continue for a certain time after the client is disconnected.</para>
- /// </summary>
- public string ResumeKey { internal get; set; }
+ /// <summary>
+ /// Sets the resume key for the Lavalink connection.
+ /// <para>This will allow existing voice sessions to continue for a certain time after the client is disconnected.</para>
+ /// </summary>
+ public string ResumeKey { internal get; set; }
- /// <summary>
- /// Sets the time in seconds when all voice sessions are closed after the client disconnects.
- /// <para>Defaults to 60 seconds.</para>
- /// </summary>
- public int ResumeTimeout { internal get; set; } = 60;
+ /// <summary>
+ /// Sets the time in seconds when all voice sessions are closed after the client disconnects.
+ /// <para>Defaults to 60 seconds.</para>
+ /// </summary>
+ public int ResumeTimeout { internal get; set; } = 60;
- /// <summary>
- /// Sets the time in milliseconds to wait for Lavalink's voice WebSocket to close after leaving a voice channel.
- /// <para>This will be the delay before the guild connection is removed.</para>
- /// <para>Defaults to 3000 milliseconds.</para>
- /// </summary>
- public int WebSocketCloseTimeout { internal get; set; } = 3000;
+ /// <summary>
+ /// Sets the time in milliseconds to wait for Lavalink's voice WebSocket to close after leaving a voice channel.
+ /// <para>This will be the delay before the guild connection is removed.</para>
+ /// <para>Defaults to 3000 milliseconds.</para>
+ /// </summary>
+ public int WebSocketCloseTimeout { internal get; set; } = 3000;
- /// <summary>
- /// Sets the voice region ID for the Lavalink connection.
- /// <para>This should be used if nodes should be filtered by region with <see cref="LavalinkExtension.GetIdealNodeConnection(DiscordVoiceRegion)"/>.</para>
- /// </summary>
- public DiscordVoiceRegion Region { internal get; set; }
+ /// <summary>
+ /// Sets the voice region ID for the Lavalink connection.
+ /// <para>This should be used if nodes should be filtered by region with <see cref="LavalinkExtension.GetIdealNodeConnection(DiscordVoiceRegion)"/>.</para>
+ /// </summary>
+ public DiscordVoiceRegion Region { internal get; set; }
- /// <summary>
- /// Creates a new instance of <see cref="LavalinkConfiguration"/>.
- /// </summary>
- [ActivatorUtilitiesConstructor]
- public LavalinkConfiguration() { }
+ /// <summary>
+ /// Creates a new instance of <see cref="LavalinkConfiguration"/>.
+ /// </summary>
+ [ActivatorUtilitiesConstructor]
+ public LavalinkConfiguration() { }
- /// <summary>
- /// Creates a new instance of <see cref="LavalinkConfiguration"/>, copying the properties of another configuration.
- /// </summary>
- /// <param name="other">Configuration the properties of which are to be copied.</param>
- public LavalinkConfiguration(LavalinkConfiguration other)
- {
- this.RestEndpoint = new ConnectionEndpoint
- {
- Hostname = other.RestEndpoint.Hostname,
- Port = other.RestEndpoint.Port,
- Secured = other.RestEndpoint.Secured
- };
- this.SocketEndpoint = new ConnectionEndpoint
+ /// <summary>
+ /// Creates a new instance of <see cref="LavalinkConfiguration"/>, copying the properties of another configuration.
+ /// </summary>
+ /// <param name="other">Configuration the properties of which are to be copied.</param>
+ public LavalinkConfiguration(LavalinkConfiguration other)
{
- Hostname = other.SocketEndpoint.Hostname,
- Port = other.SocketEndpoint.Port,
- Secured = other.SocketEndpoint.Secured
- };
- this.Password = other.Password;
- this.ResumeKey = other.ResumeKey;
- this.ResumeTimeout = other.ResumeTimeout;
- this.SocketAutoReconnect = other.SocketAutoReconnect;
- this.Region = other.Region;
- this.WebSocketCloseTimeout = other.WebSocketCloseTimeout;
+ this.RestEndpoint = new ConnectionEndpoint
+ {
+ Hostname = other.RestEndpoint.Hostname,
+ Port = other.RestEndpoint.Port,
+ Secured = other.RestEndpoint.Secured
+ };
+ this.SocketEndpoint = new ConnectionEndpoint
+ {
+ Hostname = other.SocketEndpoint.Hostname,
+ Port = other.SocketEndpoint.Port,
+ Secured = other.SocketEndpoint.Secured
+ };
+ this.Password = other.Password;
+ this.ResumeKey = other.ResumeKey;
+ this.ResumeTimeout = other.ResumeTimeout;
+ this.SocketAutoReconnect = other.SocketAutoReconnect;
+ this.Region = other.Region;
+ this.WebSocketCloseTimeout = other.WebSocketCloseTimeout;
+ }
}
}
diff --git a/DisCatSharp.Lavalink/LavalinkEvents.cs b/DisCatSharp.Lavalink/LavalinkEvents.cs
index e094fda15..579265ae4 100644
--- a/DisCatSharp.Lavalink/LavalinkEvents.cs
+++ b/DisCatSharp.Lavalink/LavalinkEvents.cs
@@ -1,76 +1,77 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Logging;
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// Contains well-defined event IDs used by the Lavalink extension.
-/// </summary>
-public static class LavalinkEvents
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// Miscellaneous events, that do not fit in any other category.
+ /// Contains well-defined event IDs used by the Lavalink extension.
/// </summary>
- public static EventId Misc { get; } = new(400, "Lavalink");
+ public static class LavalinkEvents
+ {
+ /// <summary>
+ /// Miscellaneous events, that do not fit in any other category.
+ /// </summary>
+ public static EventId Misc { get; } = new(400, "Lavalink");
- /// <summary>
- /// Events pertaining to Lavalink node connection errors.
- /// </summary>
- public static EventId LavalinkConnectionError { get; } = new(401, nameof(LavalinkConnectionError));
+ /// <summary>
+ /// Events pertaining to Lavalink node connection errors.
+ /// </summary>
+ public static EventId LavalinkConnectionError { get; } = new(401, nameof(LavalinkConnectionError));
- /// <summary>
- /// Events emitted for clean disconnects from Lavalink.
- /// </summary>
- public static EventId LavalinkConnectionClosed { get; } = new(402, nameof(LavalinkConnectionClosed));
+ /// <summary>
+ /// Events emitted for clean disconnects from Lavalink.
+ /// </summary>
+ public static EventId LavalinkConnectionClosed { get; } = new(402, nameof(LavalinkConnectionClosed));
- /// <summary>
- /// Events emitted for successful connections made to Lavalink.
- /// </summary>
- public static EventId LavalinkConnected { get; } = new(403, nameof(LavalinkConnected));
+ /// <summary>
+ /// Events emitted for successful connections made to Lavalink.
+ /// </summary>
+ public static EventId LavalinkConnected { get; } = new(403, nameof(LavalinkConnected));
- /// <summary>
- /// Events pertaining to errors that occur when decoding payloads received from Lavalink nodes.
- /// </summary>
- public static EventId LavalinkDecodeError { get; } = new(404, nameof(LavalinkDecodeError));
+ /// <summary>
+ /// Events pertaining to errors that occur when decoding payloads received from Lavalink nodes.
+ /// </summary>
+ public static EventId LavalinkDecodeError { get; } = new(404, nameof(LavalinkDecodeError));
- /// <summary>
- /// Events emitted when Lavalink's REST API responds with an error.
- /// </summary>
- public static EventId LavalinkRestError { get; } = new(405, nameof(LavalinkRestError));
+ /// <summary>
+ /// Events emitted when Lavalink's REST API responds with an error.
+ /// </summary>
+ public static EventId LavalinkRestError { get; } = new(405, nameof(LavalinkRestError));
- /// <summary>
- /// Events containing raw payloads, received from Lavalink nodes.
- /// </summary>
- public static EventId LavalinkWsRx { get; } = new(406, "Lavalink ↓");
+ /// <summary>
+ /// Events containing raw payloads, received from Lavalink nodes.
+ /// </summary>
+ public static EventId LavalinkWsRx { get; } = new(406, "Lavalink ↓");
- /// <summary>
- /// Events containing raw payloads, as they're being sent to Lavalink nodes.
- /// </summary>
- public static EventId LavalinkWsTx { get; } = new(407, "Lavalink ↑");
+ /// <summary>
+ /// Events containing raw payloads, as they're being sent to Lavalink nodes.
+ /// </summary>
+ public static EventId LavalinkWsTx { get; } = new(407, "Lavalink ↑");
- /// <summary>
- /// Events pertaining to Gateway Intents. Typically diagnostic information.
- /// </summary>
- public static EventId Intents { get; } = new(408, nameof(Intents));
+ /// <summary>
+ /// Events pertaining to Gateway Intents. Typically diagnostic information.
+ /// </summary>
+ public static EventId Intents { get; } = new(408, nameof(Intents));
+ }
}
diff --git a/DisCatSharp.Lavalink/LavalinkExtension.cs b/DisCatSharp.Lavalink/LavalinkExtension.cs
index 9aa3231dd..f14508954 100644
--- a/DisCatSharp.Lavalink/LavalinkExtension.cs
+++ b/DisCatSharp.Lavalink/LavalinkExtension.cs
@@ -1,215 +1,216 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.Lavalink.EventArgs;
using DisCatSharp.Net;
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// The lavalink extension.
-/// </summary>
-public sealed class LavalinkExtension : BaseExtension
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// Triggered whenever a node disconnects.
- /// </summary>
- public event AsyncEventHandler<LavalinkNodeConnection, NodeDisconnectedEventArgs> NodeDisconnected
- {
- add => this._nodeDisconnected.Register(value);
- remove => this._nodeDisconnected.Unregister(value);
- }
- private AsyncEvent<LavalinkNodeConnection, NodeDisconnectedEventArgs> _nodeDisconnected;
-
- /// <summary>
- /// Gets a dictionary of connected Lavalink nodes for the extension.
- /// </summary>
- public IReadOnlyDictionary<ConnectionEndpoint, LavalinkNodeConnection> ConnectedNodes { get; }
- private readonly ConcurrentDictionary<ConnectionEndpoint, LavalinkNodeConnection> _connectedNodes = new();
-
- /// <summary>
- /// Creates a new instance of this Lavalink extension.
- /// </summary>
- internal LavalinkExtension()
- {
- this.ConnectedNodes = new ReadOnlyConcurrentDictionary<ConnectionEndpoint, LavalinkNodeConnection>(this._connectedNodes);
- }
-
- /// <summary>
- /// DO NOT USE THIS MANUALLY.
- /// </summary>
- /// <param name="client">DO NOT USE THIS MANUALLY.</param>
- /// <exception cref="System.InvalidOperationException"/>
- protected internal override void Setup(DiscordClient client)
- {
- if (this.Client != null)
- throw new InvalidOperationException("What did I tell you?");
-
- this.Client = client;
-
- this._nodeDisconnected = new AsyncEvent<LavalinkNodeConnection, NodeDisconnectedEventArgs>("LAVALINK_NODE_DISCONNECTED", TimeSpan.Zero, this.Client.EventErrorHandler);
- }
-
- /// <summary>
- /// Connect to a Lavalink node.
+ /// The lavalink extension.
/// </summary>
- /// <param name="config">Lavalink client configuration.</param>
- /// <returns>The established Lavalink connection.</returns>
- public async Task<LavalinkNodeConnection> ConnectAsync(LavalinkConfiguration config)
+ public sealed class LavalinkExtension : BaseExtension
{
- if (this._connectedNodes.ContainsKey(config.SocketEndpoint))
- return this._connectedNodes[config.SocketEndpoint];
-
- var con = new LavalinkNodeConnection(this.Client, this, config);
- con.NodeDisconnected += this.Con_NodeDisconnected;
- con.Disconnected += this.Con_Disconnected;
- this._connectedNodes[con.NodeEndpoint] = con;
- try
+ /// <summary>
+ /// Triggered whenever a node disconnects.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkNodeConnection, NodeDisconnectedEventArgs> NodeDisconnected
{
- await con.StartAsync().ConfigureAwait(false);
+ add => this._nodeDisconnected.Register(value);
+ remove => this._nodeDisconnected.Unregister(value);
}
- catch
+ private AsyncEvent<LavalinkNodeConnection, NodeDisconnectedEventArgs> _nodeDisconnected;
+
+ /// <summary>
+ /// Gets a dictionary of connected Lavalink nodes for the extension.
+ /// </summary>
+ public IReadOnlyDictionary<ConnectionEndpoint, LavalinkNodeConnection> ConnectedNodes { get; }
+ private readonly ConcurrentDictionary<ConnectionEndpoint, LavalinkNodeConnection> _connectedNodes = new();
+
+ /// <summary>
+ /// Creates a new instance of this Lavalink extension.
+ /// </summary>
+ internal LavalinkExtension()
{
- this.Con_NodeDisconnected(con);
- throw;
+ this.ConnectedNodes = new ReadOnlyConcurrentDictionary<ConnectionEndpoint, LavalinkNodeConnection>(this._connectedNodes);
}
- return con;
- }
+ /// <summary>
+ /// DO NOT USE THIS MANUALLY.
+ /// </summary>
+ /// <param name="client">DO NOT USE THIS MANUALLY.</param>
+ /// <exception cref="System.InvalidOperationException"/>
+ protected internal override void Setup(DiscordClient client)
+ {
+ if (this.Client != null)
+ throw new InvalidOperationException("What did I tell you?");
- /// <summary>
- /// Gets the Lavalink node connection for the specified endpoint.
- /// </summary>
- /// <param name="endpoint">Endpoint at which the node resides.</param>
- /// <returns>Lavalink node connection.</returns>
- public LavalinkNodeConnection GetNodeConnection(ConnectionEndpoint endpoint)
- => this._connectedNodes.ContainsKey(endpoint) ? this._connectedNodes[endpoint] : null;
+ this.Client = client;
- /// <summary>
- /// Gets a Lavalink node connection based on load balancing and an optional voice region.
- /// </summary>
- /// <param name="region">The region to compare with the node's <see cref="LavalinkConfiguration.Region"/>, if any.</param>
- /// <returns>The least load affected node connection, or null if no nodes are present.</returns>
- public LavalinkNodeConnection GetIdealNodeConnection(DiscordVoiceRegion region = null)
- {
- if (this._connectedNodes.Count <= 1)
- return this._connectedNodes.Values.FirstOrDefault();
+ this._nodeDisconnected = new AsyncEvent<LavalinkNodeConnection, NodeDisconnectedEventArgs>("LAVALINK_NODE_DISCONNECTED", TimeSpan.Zero, this.Client.EventErrorHandler);
+ }
- var nodes = this._connectedNodes.Values.ToArray();
+ /// <summary>
+ /// Connect to a Lavalink node.
+ /// </summary>
+ /// <param name="config">Lavalink client configuration.</param>
+ /// <returns>The established Lavalink connection.</returns>
+ public async Task<LavalinkNodeConnection> ConnectAsync(LavalinkConfiguration config)
+ {
+ if (this._connectedNodes.ContainsKey(config.SocketEndpoint))
+ return this._connectedNodes[config.SocketEndpoint];
+
+ var con = new LavalinkNodeConnection(this.Client, this, config);
+ con.NodeDisconnected += this.Con_NodeDisconnected;
+ con.Disconnected += this.Con_Disconnected;
+ this._connectedNodes[con.NodeEndpoint] = con;
+ try
+ {
+ await con.StartAsync().ConfigureAwait(false);
+ }
+ catch
+ {
+ this.Con_NodeDisconnected(con);
+ throw;
+ }
+
+ return con;
+ }
- if (region != null)
+ /// <summary>
+ /// Gets the Lavalink node connection for the specified endpoint.
+ /// </summary>
+ /// <param name="endpoint">Endpoint at which the node resides.</param>
+ /// <returns>Lavalink node connection.</returns>
+ public LavalinkNodeConnection GetNodeConnection(ConnectionEndpoint endpoint)
+ => this._connectedNodes.ContainsKey(endpoint) ? this._connectedNodes[endpoint] : null;
+
+ /// <summary>
+ /// Gets a Lavalink node connection based on load balancing and an optional voice region.
+ /// </summary>
+ /// <param name="region">The region to compare with the node's <see cref="LavalinkConfiguration.Region"/>, if any.</param>
+ /// <returns>The least load affected node connection, or null if no nodes are present.</returns>
+ public LavalinkNodeConnection GetIdealNodeConnection(DiscordVoiceRegion region = null)
{
- var regionPredicate = new Func<LavalinkNodeConnection, bool>(x => x.Region == region);
+ if (this._connectedNodes.Count <= 1)
+ return this._connectedNodes.Values.FirstOrDefault();
- if (nodes.Any(regionPredicate))
- nodes = nodes.Where(regionPredicate).ToArray();
+ var nodes = this._connectedNodes.Values.ToArray();
- if (nodes.Length <= 1)
- return nodes.FirstOrDefault();
- }
+ if (region != null)
+ {
+ var regionPredicate = new Func<LavalinkNodeConnection, bool>(x => x.Region == region);
- return this.FilterByLoad(nodes);
- }
+ if (nodes.Any(regionPredicate))
+ nodes = nodes.Where(regionPredicate).ToArray();
- /// <summary>
- /// Gets a Lavalink guild connection from a <see cref="DisCatSharp.Entities.DiscordGuild"/>.
- /// </summary>
- /// <param name="guild">The guild the connection is on.</param>
- /// <returns>The found guild connection, or null if one could not be found.</returns>
- public LavalinkGuildConnection GetGuildConnection(DiscordGuild guild)
- {
- var nodes = this._connectedNodes.Values;
- var node = nodes.FirstOrDefault(x => x.ConnectedGuildsInternal.ContainsKey(guild.Id));
- return node?.GetGuildConnection(guild);
- }
+ if (nodes.Length <= 1)
+ return nodes.FirstOrDefault();
+ }
- /// <summary>
- /// Filters the by load.
- /// </summary>
- /// <param name="nodes">The nodes.</param>
- private LavalinkNodeConnection FilterByLoad(LavalinkNodeConnection[] nodes)
- {
- Array.Sort(nodes, (a, b) =>
+ return this.FilterByLoad(nodes);
+ }
+
+ /// <summary>
+ /// Gets a Lavalink guild connection from a <see cref="DisCatSharp.Entities.DiscordGuild"/>.
+ /// </summary>
+ /// <param name="guild">The guild the connection is on.</param>
+ /// <returns>The found guild connection, or null if one could not be found.</returns>
+ public LavalinkGuildConnection GetGuildConnection(DiscordGuild guild)
{
- if (!a.Statistics.Updated || !b.Statistics.Updated)
- return 0;
+ var nodes = this._connectedNodes.Values;
+ var node = nodes.FirstOrDefault(x => x.ConnectedGuildsInternal.ContainsKey(guild.Id));
+ return node?.GetGuildConnection(guild);
+ }
- //https://github.com/FredBoat/Lavalink-Client/blob/48bc27784f57be5b95d2ff2eff6665451b9366f5/src/main/java/lavalink/client/io/LavalinkLoadBalancer.java#L122
- //https://github.com/briantanner/eris-lavalink/blob/master/src/PlayerManager.js#L329
+ /// <summary>
+ /// Filters the by load.
+ /// </summary>
+ /// <param name="nodes">The nodes.</param>
+ private LavalinkNodeConnection FilterByLoad(LavalinkNodeConnection[] nodes)
+ {
+ Array.Sort(nodes, (a, b) =>
+ {
+ if (!a.Statistics.Updated || !b.Statistics.Updated)
+ return 0;
- //player count
- var aPenaltyCount = a.Statistics.ActivePlayers;
- var bPenaltyCount = b.Statistics.ActivePlayers;
+ //https://github.com/FredBoat/Lavalink-Client/blob/48bc27784f57be5b95d2ff2eff6665451b9366f5/src/main/java/lavalink/client/io/LavalinkLoadBalancer.java#L122
+ //https://github.com/briantanner/eris-lavalink/blob/master/src/PlayerManager.js#L329
- //cpu load
- aPenaltyCount += (int)Math.Pow(1.05d, (100 * (a.Statistics.CpuSystemLoad / a.Statistics.CpuCoreCount) * 10) - 10);
- bPenaltyCount += (int)Math.Pow(1.05d, (100 * (b.Statistics.CpuSystemLoad / a.Statistics.CpuCoreCount) * 10) - 10);
+ //player count
+ var aPenaltyCount = a.Statistics.ActivePlayers;
+ var bPenaltyCount = b.Statistics.ActivePlayers;
- //frame load
- if (a.Statistics.AverageDeficitFramesPerMinute > 0)
- {
- //deficit frame load
- aPenaltyCount += (int)((Math.Pow(1.03d, 500f * (a.Statistics.AverageDeficitFramesPerMinute / 3000f)) * 600) - 600);
+ //cpu load
+ aPenaltyCount += (int)Math.Pow(1.05d, (100 * (a.Statistics.CpuSystemLoad / a.Statistics.CpuCoreCount) * 10) - 10);
+ bPenaltyCount += (int)Math.Pow(1.05d, (100 * (b.Statistics.CpuSystemLoad / a.Statistics.CpuCoreCount) * 10) - 10);
- //null frame load
- aPenaltyCount += (int)((Math.Pow(1.03d, 500f * (a.Statistics.AverageNulledFramesPerMinute / 3000f)) * 300) - 300);
- }
+ //frame load
+ if (a.Statistics.AverageDeficitFramesPerMinute > 0)
+ {
+ //deficit frame load
+ aPenaltyCount += (int)((Math.Pow(1.03d, 500f * (a.Statistics.AverageDeficitFramesPerMinute / 3000f)) * 600) - 600);
- //frame load
- if (b.Statistics.AverageDeficitFramesPerMinute > 0)
- {
- //deficit frame load
- bPenaltyCount += (int)((Math.Pow(1.03d, 500f * (b.Statistics.AverageDeficitFramesPerMinute / 3000f)) * 600) - 600);
+ //null frame load
+ aPenaltyCount += (int)((Math.Pow(1.03d, 500f * (a.Statistics.AverageNulledFramesPerMinute / 3000f)) * 300) - 300);
+ }
- //null frame load
- bPenaltyCount += (int)((Math.Pow(1.03d, 500f * (b.Statistics.AverageNulledFramesPerMinute / 3000f)) * 300) - 300);
- }
+ //frame load
+ if (b.Statistics.AverageDeficitFramesPerMinute > 0)
+ {
+ //deficit frame load
+ bPenaltyCount += (int)((Math.Pow(1.03d, 500f * (b.Statistics.AverageDeficitFramesPerMinute / 3000f)) * 600) - 600);
- return aPenaltyCount - bPenaltyCount;
- });
+ //null frame load
+ bPenaltyCount += (int)((Math.Pow(1.03d, 500f * (b.Statistics.AverageNulledFramesPerMinute / 3000f)) * 300) - 300);
+ }
- return nodes[0];
- }
+ return aPenaltyCount - bPenaltyCount;
+ });
- /// <summary>
- /// Removes a node.
- /// </summary>
- /// <param name="node">The node to be removed.</param>
- private void Con_NodeDisconnected(LavalinkNodeConnection node)
- => this._connectedNodes.TryRemove(node.NodeEndpoint, out _);
+ return nodes[0];
+ }
- /// <summary>
- /// Disconnects a node.
- /// </summary>
- /// <param name="node">The affected node.</param>
- /// <param name="e">The node disconnected event args.</param>
- private Task Con_Disconnected(LavalinkNodeConnection node, NodeDisconnectedEventArgs e)
- => this._nodeDisconnected.InvokeAsync(node, e);
+ /// <summary>
+ /// Removes a node.
+ /// </summary>
+ /// <param name="node">The node to be removed.</param>
+ private void Con_NodeDisconnected(LavalinkNodeConnection node)
+ => this._connectedNodes.TryRemove(node.NodeEndpoint, out _);
+
+ /// <summary>
+ /// Disconnects a node.
+ /// </summary>
+ /// <param name="node">The affected node.</param>
+ /// <param name="e">The node disconnected event args.</param>
+ private Task Con_Disconnected(LavalinkNodeConnection node, NodeDisconnectedEventArgs e)
+ => this._nodeDisconnected.InvokeAsync(node, e);
+ }
}
diff --git a/DisCatSharp.Lavalink/LavalinkGuildConnection.cs b/DisCatSharp.Lavalink/LavalinkGuildConnection.cs
index aa890c430..46363ffb5 100644
--- a/DisCatSharp.Lavalink/LavalinkGuildConnection.cs
+++ b/DisCatSharp.Lavalink/LavalinkGuildConnection.cs
@@ -1,440 +1,441 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Lavalink.Entities;
using DisCatSharp.Lavalink.EventArgs;
using Newtonsoft.Json;
-namespace DisCatSharp.Lavalink;
-
-internal delegate void ChannelDisconnectedEventHandler(LavalinkGuildConnection node);
-
-/// <summary>
-/// Represents a Lavalink connection to a channel.
-/// </summary>
-public sealed class LavalinkGuildConnection
+namespace DisCatSharp.Lavalink
{
- /// <summary>
- /// Triggered whenever Lavalink updates player status.
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, PlayerUpdateEventArgs> PlayerUpdated
- {
- add => this._playerUpdated.Register(value);
- remove => this._playerUpdated.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, PlayerUpdateEventArgs> _playerUpdated;
-
- /// <summary>
- /// Triggered whenever playback of a track starts.
- /// <para>This is only available for version 3.3.1 and greater.</para>
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, TrackStartEventArgs> PlaybackStarted
- {
- add => this._playbackStarted.Register(value);
- remove => this._playbackStarted.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, TrackStartEventArgs> _playbackStarted;
-
- /// <summary>
- /// Triggered whenever playback of a track finishes.
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, TrackFinishEventArgs> PlaybackFinished
- {
- add => this._playbackFinished.Register(value);
- remove => this._playbackFinished.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, TrackFinishEventArgs> _playbackFinished;
-
- /// <summary>
- /// Triggered whenever playback of a track gets stuck.
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, TrackStuckEventArgs> TrackStuck
- {
- add => this._trackStuck.Register(value);
- remove => this._trackStuck.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, TrackStuckEventArgs> _trackStuck;
-
- /// <summary>
- /// Triggered whenever playback of a track encounters an error.
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, TrackExceptionEventArgs> TrackException
- {
- add => this._trackException.Register(value);
- remove => this._trackException.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, TrackExceptionEventArgs> _trackException;
+ internal delegate void ChannelDisconnectedEventHandler(LavalinkGuildConnection node);
/// <summary>
- /// Triggered whenever Discord Voice WebSocket connection is terminated.
+ /// Represents a Lavalink connection to a channel.
/// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, WebSocketCloseEventArgs> DiscordWebSocketClosed
+ public sealed class LavalinkGuildConnection
{
- add => this._webSocketClosed.Register(value);
- remove => this._webSocketClosed.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, WebSocketCloseEventArgs> _webSocketClosed;
-
- /// <summary>
- /// Gets whether this channel is still connected.
- /// </summary>
- public bool IsConnected => !Volatile.Read(ref this._isDisposed) && this.Channel != null;
- private bool _isDisposed;
-
- /// <summary>
- /// Gets the current player state.
- /// </summary>
- public LavalinkPlayerState CurrentState { get; }
+ /// <summary>
+ /// Triggered whenever Lavalink updates player status.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, PlayerUpdateEventArgs> PlayerUpdated
+ {
+ add => this._playerUpdated.Register(value);
+ remove => this._playerUpdated.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, PlayerUpdateEventArgs> _playerUpdated;
- /// <summary>
- /// Gets the voice channel associated with this connection.
- /// </summary>
- public DiscordChannel Channel => this.VoiceStateUpdate.Channel;
+ /// <summary>
+ /// Triggered whenever playback of a track starts.
+ /// <para>This is only available for version 3.3.1 and greater.</para>
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, TrackStartEventArgs> PlaybackStarted
+ {
+ add => this._playbackStarted.Register(value);
+ remove => this._playbackStarted.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, TrackStartEventArgs> _playbackStarted;
- /// <summary>
- /// Gets the guild associated with this connection.
- /// </summary>
- public DiscordGuild Guild => this.Channel.Guild;
+ /// <summary>
+ /// Triggered whenever playback of a track finishes.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, TrackFinishEventArgs> PlaybackFinished
+ {
+ add => this._playbackFinished.Register(value);
+ remove => this._playbackFinished.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, TrackFinishEventArgs> _playbackFinished;
- /// <summary>
- /// Gets the Lavalink node associated with this connection.
- /// </summary>
- public LavalinkNodeConnection Node { get; }
+ /// <summary>
+ /// Triggered whenever playback of a track gets stuck.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, TrackStuckEventArgs> TrackStuck
+ {
+ add => this._trackStuck.Register(value);
+ remove => this._trackStuck.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, TrackStuckEventArgs> _trackStuck;
- /// <summary>
- /// Gets the guild id string.
- /// </summary>
- internal string GuildIdString => this.GuildId.ToString(CultureInfo.InvariantCulture);
- /// <summary>
- /// Gets the guild id.
- /// </summary>
- internal ulong GuildId => this.Channel.Guild.Id;
- /// <summary>
- /// Gets or sets the voice state update.
- /// </summary>
- internal VoiceStateUpdateEventArgs VoiceStateUpdate { get; set; }
- /// <summary>
- /// Gets or sets the voice ws disconnect tcs.
- /// </summary>
- internal TaskCompletionSource<bool> VoiceWsDisconnectTcs { get; set; }
+ /// <summary>
+ /// Triggered whenever playback of a track encounters an error.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, TrackExceptionEventArgs> TrackException
+ {
+ add => this._trackException.Register(value);
+ remove => this._trackException.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, TrackExceptionEventArgs> _trackException;
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkGuildConnection"/> class.
- /// </summary>
- /// <param name="node">The node.</param>
- /// <param name="channel">The channel.</param>
- /// <param name="vstu">The vstu.</param>
- internal LavalinkGuildConnection(LavalinkNodeConnection node, DiscordChannel channel, VoiceStateUpdateEventArgs vstu)
- {
- this.Node = node;
- this.VoiceStateUpdate = vstu;
- this.CurrentState = new LavalinkPlayerState();
- this.VoiceWsDisconnectTcs = new TaskCompletionSource<bool>();
-
- Volatile.Write(ref this._isDisposed, false);
-
- this._playerUpdated = new AsyncEvent<LavalinkGuildConnection, PlayerUpdateEventArgs>("LAVALINK_PLAYER_UPDATE", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
- this._playbackStarted = new AsyncEvent<LavalinkGuildConnection, TrackStartEventArgs>("LAVALINK_PLAYBACK_STARTED", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
- this._playbackFinished = new AsyncEvent<LavalinkGuildConnection, TrackFinishEventArgs>("LAVALINK_PLAYBACK_FINISHED", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
- this._trackStuck = new AsyncEvent<LavalinkGuildConnection, TrackStuckEventArgs>("LAVALINK_TRACK_STUCK", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
- this._trackException = new AsyncEvent<LavalinkGuildConnection, TrackExceptionEventArgs>("LAVALINK_TRACK_EXCEPTION", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
- this._webSocketClosed = new AsyncEvent<LavalinkGuildConnection, WebSocketCloseEventArgs>("LAVALINK_DISCORD_WEBSOCKET_CLOSED", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
- }
+ /// <summary>
+ /// Triggered whenever Discord Voice WebSocket connection is terminated.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, WebSocketCloseEventArgs> DiscordWebSocketClosed
+ {
+ add => this._webSocketClosed.Register(value);
+ remove => this._webSocketClosed.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, WebSocketCloseEventArgs> _webSocketClosed;
+
+ /// <summary>
+ /// Gets whether this channel is still connected.
+ /// </summary>
+ public bool IsConnected => !Volatile.Read(ref this._isDisposed) && this.Channel != null;
+ private bool _isDisposed;
+
+ /// <summary>
+ /// Gets the current player state.
+ /// </summary>
+ public LavalinkPlayerState CurrentState { get; }
+
+ /// <summary>
+ /// Gets the voice channel associated with this connection.
+ /// </summary>
+ public DiscordChannel Channel => this.VoiceStateUpdate.Channel;
+
+ /// <summary>
+ /// Gets the guild associated with this connection.
+ /// </summary>
+ public DiscordGuild Guild => this.Channel.Guild;
+
+ /// <summary>
+ /// Gets the Lavalink node associated with this connection.
+ /// </summary>
+ public LavalinkNodeConnection Node { get; }
+
+ /// <summary>
+ /// Gets the guild id string.
+ /// </summary>
+ internal string GuildIdString => this.GuildId.ToString(CultureInfo.InvariantCulture);
+ /// <summary>
+ /// Gets the guild id.
+ /// </summary>
+ internal ulong GuildId => this.Channel.Guild.Id;
+ /// <summary>
+ /// Gets or sets the voice state update.
+ /// </summary>
+ internal VoiceStateUpdateEventArgs VoiceStateUpdate { get; set; }
+ /// <summary>
+ /// Gets or sets the voice ws disconnect tcs.
+ /// </summary>
+ internal TaskCompletionSource<bool> VoiceWsDisconnectTcs { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkGuildConnection"/> class.
+ /// </summary>
+ /// <param name="node">The node.</param>
+ /// <param name="channel">The channel.</param>
+ /// <param name="vstu">The vstu.</param>
+ internal LavalinkGuildConnection(LavalinkNodeConnection node, DiscordChannel channel, VoiceStateUpdateEventArgs vstu)
+ {
+ this.Node = node;
+ this.VoiceStateUpdate = vstu;
+ this.CurrentState = new LavalinkPlayerState();
+ this.VoiceWsDisconnectTcs = new TaskCompletionSource<bool>();
+
+ Volatile.Write(ref this._isDisposed, false);
+
+ this._playerUpdated = new AsyncEvent<LavalinkGuildConnection, PlayerUpdateEventArgs>("LAVALINK_PLAYER_UPDATE", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
+ this._playbackStarted = new AsyncEvent<LavalinkGuildConnection, TrackStartEventArgs>("LAVALINK_PLAYBACK_STARTED", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
+ this._playbackFinished = new AsyncEvent<LavalinkGuildConnection, TrackFinishEventArgs>("LAVALINK_PLAYBACK_FINISHED", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
+ this._trackStuck = new AsyncEvent<LavalinkGuildConnection, TrackStuckEventArgs>("LAVALINK_TRACK_STUCK", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
+ this._trackException = new AsyncEvent<LavalinkGuildConnection, TrackExceptionEventArgs>("LAVALINK_TRACK_EXCEPTION", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
+ this._webSocketClosed = new AsyncEvent<LavalinkGuildConnection, WebSocketCloseEventArgs>("LAVALINK_DISCORD_WEBSOCKET_CLOSED", TimeSpan.Zero, this.Node.Discord.EventErrorHandler);
+ }
- /// <summary>
- /// Disconnects the connection from the voice channel.
- /// </summary>
- /// <param name="shouldDestroy">Whether the connection should be destroyed on the Lavalink server when leaving.</param>
+ /// <summary>
+ /// Disconnects the connection from the voice channel.
+ /// </summary>
+ /// <param name="shouldDestroy">Whether the connection should be destroyed on the Lavalink server when leaving.</param>
- public Task DisconnectAsync(bool shouldDestroy = true)
- => this.DisconnectInternalAsync(shouldDestroy);
+ public Task DisconnectAsync(bool shouldDestroy = true)
+ => this.DisconnectInternalAsync(shouldDestroy);
- /// <summary>
- /// Disconnects the internal async.
- /// </summary>
- /// <param name="shouldDestroy">If true, should destroy.</param>
- /// <param name="isManualDisconnection">If true, is manual disconnection.</param>
+ /// <summary>
+ /// Disconnects the internal async.
+ /// </summary>
+ /// <param name="shouldDestroy">If true, should destroy.</param>
+ /// <param name="isManualDisconnection">If true, is manual disconnection.</param>
- internal async Task DisconnectInternalAsync(bool shouldDestroy, bool isManualDisconnection = false)
- {
- if (!this.IsConnected && !isManualDisconnection)
- throw new InvalidOperationException("This connection is not valid.");
+ internal async Task DisconnectInternalAsync(bool shouldDestroy, bool isManualDisconnection = false)
+ {
+ if (!this.IsConnected && !isManualDisconnection)
+ throw new InvalidOperationException("This connection is not valid.");
- Volatile.Write(ref this._isDisposed, true);
+ Volatile.Write(ref this._isDisposed, true);
- if (shouldDestroy)
- await this.Node.SendPayloadAsync(new LavalinkDestroy(this)).ConfigureAwait(false);
+ if (shouldDestroy)
+ await this.Node.SendPayloadAsync(new LavalinkDestroy(this)).ConfigureAwait(false);
- if (!isManualDisconnection)
- {
- await this.SendVoiceUpdateAsync().ConfigureAwait(false);
- this.ChannelDisconnected?.Invoke(this);
+ if (!isManualDisconnection)
+ {
+ await this.SendVoiceUpdateAsync().ConfigureAwait(false);
+ this.ChannelDisconnected?.Invoke(this);
+ }
}
- }
- /// <summary>
- /// Sends the voice update async.
- /// </summary>
+ /// <summary>
+ /// Sends the voice update async.
+ /// </summary>
- internal async Task SendVoiceUpdateAsync()
- {
- var vsd = new VoiceDispatch
+ internal async Task SendVoiceUpdateAsync()
{
- OpCode = 4,
- Payload = new VoiceStateUpdatePayload
+ var vsd = new VoiceDispatch
{
- GuildId = this.GuildId,
- ChannelId = null,
- Deafened = false,
- Muted = false
- }
- };
- var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);
- await (this.Channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
- }
+ OpCode = 4,
+ Payload = new VoiceStateUpdatePayload
+ {
+ GuildId = this.GuildId,
+ ChannelId = null,
+ Deafened = false,
+ Muted = false
+ }
+ };
+ var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);
+ await (this.Channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
+ }
- /// <summary>
- /// Searches for specified terms.
- /// </summary>
- /// <param name="searchQuery">What to search for.</param>
- /// <param name="type">What platform will search for.</param>
- /// <returns>A collection of tracks matching the criteria.</returns>
- public Task<LavalinkLoadResult> GetTracksAsync(string searchQuery, LavalinkSearchType type = LavalinkSearchType.Youtube)
- => this.Node.Rest.GetTracksAsync(searchQuery, type);
+ /// <summary>
+ /// Searches for specified terms.
+ /// </summary>
+ /// <param name="searchQuery">What to search for.</param>
+ /// <param name="type">What platform will search for.</param>
+ /// <returns>A collection of tracks matching the criteria.</returns>
+ public Task<LavalinkLoadResult> GetTracksAsync(string searchQuery, LavalinkSearchType type = LavalinkSearchType.Youtube)
+ => this.Node.Rest.GetTracksAsync(searchQuery, type);
+
+ /// <summary>
+ /// Loads tracks from specified URL.
+ /// </summary>
+ /// <param name="uri">URL to load tracks from.</param>
+ /// <returns>A collection of tracks from the URL.</returns>
+ public Task<LavalinkLoadResult> GetTracksAsync(Uri uri)
+ => this.Node.Rest.GetTracksAsync(uri);
+
+ /// <summary>
+ /// Loads tracks from a local file.
+ /// </summary>
+ /// <param name="file">File to load tracks from.</param>
+ /// <returns>A collection of tracks from the file.</returns>
+ public Task<LavalinkLoadResult> GetTracksAsync(FileInfo file)
+ => this.Node.Rest.GetTracksAsync(file);
+
+ /// <summary>
+ /// Queues the specified track for playback.
+ /// </summary>
+ /// <param name="track">Track to play.</param>
+ public async Task PlayAsync(LavalinkTrack track)
+ {
+ if (!this.IsConnected)
+ throw new InvalidOperationException("This connection is not valid.");
- /// <summary>
- /// Loads tracks from specified URL.
- /// </summary>
- /// <param name="uri">URL to load tracks from.</param>
- /// <returns>A collection of tracks from the URL.</returns>
- public Task<LavalinkLoadResult> GetTracksAsync(Uri uri)
- => this.Node.Rest.GetTracksAsync(uri);
+ this.CurrentState.CurrentTrack = track;
+ await this.Node.SendPayloadAsync(new LavalinkPlay(this, track)).ConfigureAwait(false);
+ }
- /// <summary>
- /// Loads tracks from a local file.
- /// </summary>
- /// <param name="file">File to load tracks from.</param>
- /// <returns>A collection of tracks from the file.</returns>
- public Task<LavalinkLoadResult> GetTracksAsync(FileInfo file)
- => this.Node.Rest.GetTracksAsync(file);
+ /// <summary>
+ /// Queues the specified track for playback. The track will be played from specified start timestamp to specified end timestamp.
+ /// </summary>
+ /// <param name="track">Track to play.</param>
+ /// <param name="start">Timestamp to start playback at.</param>
+ /// <param name="end">Timestamp to stop playback at.</param>
+ public async Task PlayPartialAsync(LavalinkTrack track, TimeSpan start, TimeSpan end)
+ {
+ if (!this.IsConnected)
+ throw new InvalidOperationException("This connection is not valid.");
- /// <summary>
- /// Queues the specified track for playback.
- /// </summary>
- /// <param name="track">Track to play.</param>
- public async Task PlayAsync(LavalinkTrack track)
- {
- if (!this.IsConnected)
- throw new InvalidOperationException("This connection is not valid.");
+ if (start.TotalMilliseconds < 0 || end <= start)
+ throw new ArgumentException("Both start and end timestamps need to be greater or equal to zero, and the end timestamp needs to be greater than start timestamp.");
- this.CurrentState.CurrentTrack = track;
- await this.Node.SendPayloadAsync(new LavalinkPlay(this, track)).ConfigureAwait(false);
- }
+ this.CurrentState.CurrentTrack = track;
+ await this.Node.SendPayloadAsync(new LavalinkPlayPartial(this, track, start, end)).ConfigureAwait(false);
+ }
- /// <summary>
- /// Queues the specified track for playback. The track will be played from specified start timestamp to specified end timestamp.
- /// </summary>
- /// <param name="track">Track to play.</param>
- /// <param name="start">Timestamp to start playback at.</param>
- /// <param name="end">Timestamp to stop playback at.</param>
- public async Task PlayPartialAsync(LavalinkTrack track, TimeSpan start, TimeSpan end)
- {
- if (!this.IsConnected)
- throw new InvalidOperationException("This connection is not valid.");
+ /// <summary>
+ /// Stops the player completely.
+ /// </summary>
+ public async Task StopAsync()
+ {
+ if (!this.IsConnected)
+ throw new InvalidOperationException("This connection is not valid.");
- if (start.TotalMilliseconds < 0 || end <= start)
- throw new ArgumentException("Both start and end timestamps need to be greater or equal to zero, and the end timestamp needs to be greater than start timestamp.");
+ await this.Node.SendPayloadAsync(new LavalinkStop(this)).ConfigureAwait(false);
+ }
- this.CurrentState.CurrentTrack = track;
- await this.Node.SendPayloadAsync(new LavalinkPlayPartial(this, track, start, end)).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Pauses the player.
+ /// </summary>
+ public async Task PauseAsync()
+ {
+ if (!this.IsConnected)
+ throw new InvalidOperationException("This connection is not valid.");
- /// <summary>
- /// Stops the player completely.
- /// </summary>
- public async Task StopAsync()
- {
- if (!this.IsConnected)
- throw new InvalidOperationException("This connection is not valid.");
+ await this.Node.SendPayloadAsync(new LavalinkPause(this, true)).ConfigureAwait(false);
+ }
- await this.Node.SendPayloadAsync(new LavalinkStop(this)).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Resumes playback.
+ /// </summary>
+ public async Task ResumeAsync()
+ {
+ if (!this.IsConnected)
+ throw new InvalidOperationException("This connection is not valid.");
- /// <summary>
- /// Pauses the player.
- /// </summary>
- public async Task PauseAsync()
- {
- if (!this.IsConnected)
- throw new InvalidOperationException("This connection is not valid.");
+ await this.Node.SendPayloadAsync(new LavalinkPause(this, false)).ConfigureAwait(false);
+ }
- await this.Node.SendPayloadAsync(new LavalinkPause(this, true)).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Seeks the current track to specified position.
+ /// </summary>
+ /// <param name="position">Position to seek to.</param>
+ public async Task SeekAsync(TimeSpan position)
+ {
+ if (!this.IsConnected)
+ throw new InvalidOperationException("This connection is not valid.");
- /// <summary>
- /// Resumes playback.
- /// </summary>
- public async Task ResumeAsync()
- {
- if (!this.IsConnected)
- throw new InvalidOperationException("This connection is not valid.");
+ await this.Node.SendPayloadAsync(new LavalinkSeek(this, position)).ConfigureAwait(false);
+ }
- await this.Node.SendPayloadAsync(new LavalinkPause(this, false)).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Sets the playback volume. This might incur a lot of CPU usage.
+ /// </summary>
+ /// <param name="volume">Volume to set. Needs to be greater or equal to 0, and less than or equal to 100. 100 means 100% and is the default value.</param>
+ public async Task SetVolumeAsync(int volume)
+ {
+ if (!this.IsConnected)
+ throw new InvalidOperationException("This connection is not valid.");
- /// <summary>
- /// Seeks the current track to specified position.
- /// </summary>
- /// <param name="position">Position to seek to.</param>
- public async Task SeekAsync(TimeSpan position)
- {
- if (!this.IsConnected)
- throw new InvalidOperationException("This connection is not valid.");
+ if (volume < 0 || volume > 1000)
+ throw new ArgumentOutOfRangeException(nameof(volume), "Volume needs to range from 0 to 1000.");
- await this.Node.SendPayloadAsync(new LavalinkSeek(this, position)).ConfigureAwait(false);
- }
+ await this.Node.SendPayloadAsync(new LavalinkVolume(this, volume)).ConfigureAwait(false);
+ }
- /// <summary>
- /// Sets the playback volume. This might incur a lot of CPU usage.
- /// </summary>
- /// <param name="volume">Volume to set. Needs to be greater or equal to 0, and less than or equal to 100. 100 means 100% and is the default value.</param>
- public async Task SetVolumeAsync(int volume)
- {
- if (!this.IsConnected)
- throw new InvalidOperationException("This connection is not valid.");
+ /// <summary>
+ /// Adjusts the specified bands in the audio equalizer. This will alter the sound output, and might incur a lot of CPU usage.
+ /// </summary>
+ /// <param name="bands">Bands adjustments to make. You must specify one adjustment per band at most.</param>
+ public async Task AdjustEqualizerAsync(params LavalinkBandAdjustment[] bands)
+ {
+ if (!this.IsConnected)
+ throw new InvalidOperationException("This connection is not valid.");
- if (volume < 0 || volume > 1000)
- throw new ArgumentOutOfRangeException(nameof(volume), "Volume needs to range from 0 to 1000.");
+ if (bands?.Any() != true)
+ return;
- await this.Node.SendPayloadAsync(new LavalinkVolume(this, volume)).ConfigureAwait(false);
- }
+ if (bands.Distinct(new LavalinkBandAdjustmentComparer()).Count() != bands.Length)
+ throw new InvalidOperationException("You cannot specify multiple modifiers for the same band.");
- /// <summary>
- /// Adjusts the specified bands in the audio equalizer. This will alter the sound output, and might incur a lot of CPU usage.
- /// </summary>
- /// <param name="bands">Bands adjustments to make. You must specify one adjustment per band at most.</param>
- public async Task AdjustEqualizerAsync(params LavalinkBandAdjustment[] bands)
- {
- if (!this.IsConnected)
- throw new InvalidOperationException("This connection is not valid.");
+ await this.Node.SendPayloadAsync(new LavalinkEqualizer(this, bands)).ConfigureAwait(false);
+ }
- if (bands?.Any() != true)
- return;
+ /// <summary>
+ /// Resets the audio equalizer to default values.
+ /// </summary>
+ public async Task ResetEqualizerAsync()
+ {
+ if (!this.IsConnected)
+ throw new InvalidOperationException("This connection is not valid.");
- if (bands.Distinct(new LavalinkBandAdjustmentComparer()).Count() != bands.Length)
- throw new InvalidOperationException("You cannot specify multiple modifiers for the same band.");
+ await this.Node.SendPayloadAsync(new LavalinkEqualizer(this, Enumerable.Range(0, 15).Select(x => new LavalinkBandAdjustment(x, 0)))).ConfigureAwait(false);
+ }
- await this.Node.SendPayloadAsync(new LavalinkEqualizer(this, bands)).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Internals the update player state async.
+ /// </summary>
+ /// <param name="newState">The new state.</param>
- /// <summary>
- /// Resets the audio equalizer to default values.
- /// </summary>
- public async Task ResetEqualizerAsync()
- {
- if (!this.IsConnected)
- throw new InvalidOperationException("This connection is not valid.");
+ internal Task InternalUpdatePlayerStateAsync(LavalinkState newState)
+ {
+ this.CurrentState.LastUpdate = newState.Time;
+ this.CurrentState.PlaybackPosition = newState.Position;
- await this.Node.SendPayloadAsync(new LavalinkEqualizer(this, Enumerable.Range(0, 15).Select(x => new LavalinkBandAdjustment(x, 0)))).ConfigureAwait(false);
- }
+ return this._playerUpdated.InvokeAsync(this, new PlayerUpdateEventArgs(this, newState.Time, newState.Position));
+ }
- /// <summary>
- /// Internals the update player state async.
- /// </summary>
- /// <param name="newState">The new state.</param>
+ /// <summary>
+ /// Internals the playback started async.
+ /// </summary>
+ /// <param name="track">The track.</param>
- internal Task InternalUpdatePlayerStateAsync(LavalinkState newState)
- {
- this.CurrentState.LastUpdate = newState.Time;
- this.CurrentState.PlaybackPosition = newState.Position;
+ internal Task InternalPlaybackStartedAsync(string track)
+ {
+ var ea = new TrackStartEventArgs(this, LavalinkUtilities.DecodeTrack(track));
+ return this._playbackStarted.InvokeAsync(this, ea);
+ }
- return this._playerUpdated.InvokeAsync(this, new PlayerUpdateEventArgs(this, newState.Time, newState.Position));
- }
+ /// <summary>
+ /// Internals the playback finished async.
+ /// </summary>
+ /// <param name="e">The e.</param>
- /// <summary>
- /// Internals the playback started async.
- /// </summary>
- /// <param name="track">The track.</param>
+ internal Task InternalPlaybackFinishedAsync(TrackFinishData e)
+ {
+ if (e.Reason != TrackEndReason.Replaced)
+ this.CurrentState.CurrentTrack = default;
- internal Task InternalPlaybackStartedAsync(string track)
- {
- var ea = new TrackStartEventArgs(this, LavalinkUtilities.DecodeTrack(track));
- return this._playbackStarted.InvokeAsync(this, ea);
- }
+ var ea = new TrackFinishEventArgs(this, LavalinkUtilities.DecodeTrack(e.Track), e.Reason);
+ return this._playbackFinished.InvokeAsync(this, ea);
+ }
- /// <summary>
- /// Internals the playback finished async.
- /// </summary>
- /// <param name="e">The e.</param>
+ /// <summary>
+ /// Internals the track stuck async.
+ /// </summary>
+ /// <param name="e">The e.</param>
- internal Task InternalPlaybackFinishedAsync(TrackFinishData e)
- {
- if (e.Reason != TrackEndReason.Replaced)
- this.CurrentState.CurrentTrack = default;
+ internal Task InternalTrackStuckAsync(TrackStuckData e)
+ {
+ var ea = new TrackStuckEventArgs(this, e.Threshold, LavalinkUtilities.DecodeTrack(e.Track));
+ return this._trackStuck.InvokeAsync(this, ea);
+ }
- var ea = new TrackFinishEventArgs(this, LavalinkUtilities.DecodeTrack(e.Track), e.Reason);
- return this._playbackFinished.InvokeAsync(this, ea);
- }
+ /// <summary>
+ /// Internals the track exception async.
+ /// </summary>
+ /// <param name="e">The e.</param>
- /// <summary>
- /// Internals the track stuck async.
- /// </summary>
- /// <param name="e">The e.</param>
+ internal Task InternalTrackExceptionAsync(TrackExceptionData e)
+ {
+ var ea = new TrackExceptionEventArgs(this, e.Error, LavalinkUtilities.DecodeTrack(e.Track));
+ return this._trackException.InvokeAsync(this, ea);
+ }
- internal Task InternalTrackStuckAsync(TrackStuckData e)
- {
- var ea = new TrackStuckEventArgs(this, e.Threshold, LavalinkUtilities.DecodeTrack(e.Track));
- return this._trackStuck.InvokeAsync(this, ea);
- }
+ /// <summary>
+ /// Internals the web socket closed async.
+ /// </summary>
+ /// <param name="e">The e.</param>
- /// <summary>
- /// Internals the track exception async.
- /// </summary>
- /// <param name="e">The e.</param>
+ internal Task InternalWebSocketClosedAsync(WebSocketCloseEventArgs e)
+ => this._webSocketClosed.InvokeAsync(this, e);
- internal Task InternalTrackExceptionAsync(TrackExceptionData e)
- {
- var ea = new TrackExceptionEventArgs(this, e.Error, LavalinkUtilities.DecodeTrack(e.Track));
- return this._trackException.InvokeAsync(this, ea);
+ internal event ChannelDisconnectedEventHandler ChannelDisconnected;
}
-
- /// <summary>
- /// Internals the web socket closed async.
- /// </summary>
- /// <param name="e">The e.</param>
-
- internal Task InternalWebSocketClosedAsync(WebSocketCloseEventArgs e)
- => this._webSocketClosed.InvokeAsync(this, e);
-
- internal event ChannelDisconnectedEventHandler ChannelDisconnected;
}
diff --git a/DisCatSharp.Lavalink/LavalinkNodeConnection.cs b/DisCatSharp.Lavalink/LavalinkNodeConnection.cs
index 184942907..8c4cd83c8 100644
--- a/DisCatSharp.Lavalink/LavalinkNodeConnection.cs
+++ b/DisCatSharp.Lavalink/LavalinkNodeConnection.cs
@@ -1,608 +1,609 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Lavalink.Entities;
using DisCatSharp.Lavalink.EventArgs;
using DisCatSharp.Net;
using DisCatSharp.Net.WebSocket;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Lavalink;
-
-internal delegate void NodeDisconnectedEventHandler(LavalinkNodeConnection node);
-
-/// <summary>
-/// Represents a connection to a Lavalink node.
-/// </summary>
-public sealed class LavalinkNodeConnection
+namespace DisCatSharp.Lavalink
{
- /// <summary>
- /// Triggered whenever Lavalink WebSocket throws an exception.
- /// </summary>
- public event AsyncEventHandler<LavalinkNodeConnection, SocketErrorEventArgs> LavalinkSocketErrored
- {
- add => this._lavalinkSocketError.Register(value);
- remove => this._lavalinkSocketError.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkNodeConnection, SocketErrorEventArgs> _lavalinkSocketError;
+ internal delegate void NodeDisconnectedEventHandler(LavalinkNodeConnection node);
/// <summary>
- /// Triggered when this node disconnects.
+ /// Represents a connection to a Lavalink node.
/// </summary>
- public event AsyncEventHandler<LavalinkNodeConnection, NodeDisconnectedEventArgs> Disconnected
+ public sealed class LavalinkNodeConnection
{
- add => this._disconnected.Register(value);
- remove => this._disconnected.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkNodeConnection, NodeDisconnectedEventArgs> _disconnected;
-
- /// <summary>
- /// Triggered when this node receives a statistics update.
- /// </summary>
- public event AsyncEventHandler<LavalinkNodeConnection, StatisticsReceivedEventArgs> StatisticsReceived
- {
- add => this._statsReceived.Register(value);
- remove => this._statsReceived.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkNodeConnection, StatisticsReceivedEventArgs> _statsReceived;
-
- /// <summary>
- /// Triggered whenever any of the players on this node is updated.
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, PlayerUpdateEventArgs> PlayerUpdated
- {
- add => this._playerUpdated.Register(value);
- remove => this._playerUpdated.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, PlayerUpdateEventArgs> _playerUpdated;
-
- /// <summary>
- /// Triggered whenever playback of a track starts.
- /// <para>This is only available for version 3.3.1 and greater.</para>
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, TrackStartEventArgs> PlaybackStarted
- {
- add => this._playbackStarted.Register(value);
- remove => this._playbackStarted.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, TrackStartEventArgs> _playbackStarted;
-
- /// <summary>
- /// Triggered whenever playback of a track finishes.
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, TrackFinishEventArgs> PlaybackFinished
- {
- add => this._playbackFinished.Register(value);
- remove => this._playbackFinished.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, TrackFinishEventArgs> _playbackFinished;
-
- /// <summary>
- /// Triggered whenever playback of a track gets stuck.
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, TrackStuckEventArgs> TrackStuck
- {
- add => this._trackStuck.Register(value);
- remove => this._trackStuck.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, TrackStuckEventArgs> _trackStuck;
-
- /// <summary>
- /// Triggered whenever playback of a track encounters an error.
- /// </summary>
- public event AsyncEventHandler<LavalinkGuildConnection, TrackExceptionEventArgs> TrackException
- {
- add => this._trackException.Register(value);
- remove => this._trackException.Unregister(value);
- }
- private readonly AsyncEvent<LavalinkGuildConnection, TrackExceptionEventArgs> _trackException;
-
- /// <summary>
- /// Gets the remote endpoint of this Lavalink node connection.
- /// </summary>
- public ConnectionEndpoint NodeEndpoint => this.Configuration.SocketEndpoint;
-
- /// <summary>
- /// Gets whether the client is connected to Lavalink.
- /// </summary>
- public bool IsConnected => !Volatile.Read(ref this._isDisposed);
- private bool _isDisposed;
- private int _backoff;
- /// <summary>
- /// The minimum backoff.
- /// </summary>
- private const int MINIMUM_BACKOFF = 7500;
- /// <summary>
- /// The maximum backoff.
- /// </summary>
- private const int MAXIMUM_BACKOFF = 120000;
-
- /// <summary>
- /// Gets the current resource usage statistics.
- /// </summary>
- public LavalinkStatistics Statistics { get; }
-
- /// <summary>
- /// Gets a dictionary of Lavalink guild connections for this node.
- /// </summary>
- public IReadOnlyDictionary<ulong, LavalinkGuildConnection> ConnectedGuilds { get; }
- internal ConcurrentDictionary<ulong, LavalinkGuildConnection> ConnectedGuildsInternal = new();
-
- /// <summary>
- /// Gets the REST client for this Lavalink connection.
- /// </summary>
- public LavalinkRestClient Rest { get; }
+ /// <summary>
+ /// Triggered whenever Lavalink WebSocket throws an exception.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkNodeConnection, SocketErrorEventArgs> LavalinkSocketErrored
+ {
+ add => this._lavalinkSocketError.Register(value);
+ remove => this._lavalinkSocketError.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkNodeConnection, SocketErrorEventArgs> _lavalinkSocketError;
- /// <summary>
- /// Gets the parent extension which this node connection belongs to.
- /// </summary>
- public LavalinkExtension Parent { get; }
+ /// <summary>
+ /// Triggered when this node disconnects.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkNodeConnection, NodeDisconnectedEventArgs> Disconnected
+ {
+ add => this._disconnected.Register(value);
+ remove => this._disconnected.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkNodeConnection, NodeDisconnectedEventArgs> _disconnected;
- /// <summary>
- /// Gets the Discord client this node connection belongs to.
- /// </summary>
- public DiscordClient Discord { get; }
+ /// <summary>
+ /// Triggered when this node receives a statistics update.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkNodeConnection, StatisticsReceivedEventArgs> StatisticsReceived
+ {
+ add => this._statsReceived.Register(value);
+ remove => this._statsReceived.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkNodeConnection, StatisticsReceivedEventArgs> _statsReceived;
- /// <summary>
- /// Gets the configuration.
- /// </summary>
- internal LavalinkConfiguration Configuration { get; }
- /// <summary>
- /// Gets the region.
- /// </summary>
- internal DiscordVoiceRegion Region { get; }
+ /// <summary>
+ /// Triggered whenever any of the players on this node is updated.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, PlayerUpdateEventArgs> PlayerUpdated
+ {
+ add => this._playerUpdated.Register(value);
+ remove => this._playerUpdated.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, PlayerUpdateEventArgs> _playerUpdated;
- /// <summary>
- /// Gets or sets the web socket.
- /// </summary>
- private IWebSocketClient _webSocket;
+ /// <summary>
+ /// Triggered whenever playback of a track starts.
+ /// <para>This is only available for version 3.3.1 and greater.</para>
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, TrackStartEventArgs> PlaybackStarted
+ {
+ add => this._playbackStarted.Register(value);
+ remove => this._playbackStarted.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, TrackStartEventArgs> _playbackStarted;
- /// <summary>
- /// Gets the voice state updates.
- /// </summary>
- private readonly ConcurrentDictionary<ulong, TaskCompletionSource<VoiceStateUpdateEventArgs>> _voiceStateUpdates;
+ /// <summary>
+ /// Triggered whenever playback of a track finishes.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, TrackFinishEventArgs> PlaybackFinished
+ {
+ add => this._playbackFinished.Register(value);
+ remove => this._playbackFinished.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, TrackFinishEventArgs> _playbackFinished;
- /// <summary>
- /// Gets the voice server updates.
- /// </summary>
- private readonly ConcurrentDictionary<ulong, TaskCompletionSource<VoiceServerUpdateEventArgs>> _voiceServerUpdates;
+ /// <summary>
+ /// Triggered whenever playback of a track gets stuck.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, TrackStuckEventArgs> TrackStuck
+ {
+ add => this._trackStuck.Register(value);
+ remove => this._trackStuck.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, TrackStuckEventArgs> _trackStuck;
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkNodeConnection"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="extension">the event.tension.</param>
- /// <param name="config">The config.</param>
- internal LavalinkNodeConnection(DiscordClient client, LavalinkExtension extension, LavalinkConfiguration config)
- {
- this.Discord = client;
- this.Parent = extension;
- this.Configuration = new LavalinkConfiguration(config);
+ /// <summary>
+ /// Triggered whenever playback of a track encounters an error.
+ /// </summary>
+ public event AsyncEventHandler<LavalinkGuildConnection, TrackExceptionEventArgs> TrackException
+ {
+ add => this._trackException.Register(value);
+ remove => this._trackException.Unregister(value);
+ }
+ private readonly AsyncEvent<LavalinkGuildConnection, TrackExceptionEventArgs> _trackException;
+
+ /// <summary>
+ /// Gets the remote endpoint of this Lavalink node connection.
+ /// </summary>
+ public ConnectionEndpoint NodeEndpoint => this.Configuration.SocketEndpoint;
+
+ /// <summary>
+ /// Gets whether the client is connected to Lavalink.
+ /// </summary>
+ public bool IsConnected => !Volatile.Read(ref this._isDisposed);
+ private bool _isDisposed;
+ private int _backoff;
+ /// <summary>
+ /// The minimum backoff.
+ /// </summary>
+ private const int MINIMUM_BACKOFF = 7500;
+ /// <summary>
+ /// The maximum backoff.
+ /// </summary>
+ private const int MAXIMUM_BACKOFF = 120000;
+
+ /// <summary>
+ /// Gets the current resource usage statistics.
+ /// </summary>
+ public LavalinkStatistics Statistics { get; }
+
+ /// <summary>
+ /// Gets a dictionary of Lavalink guild connections for this node.
+ /// </summary>
+ public IReadOnlyDictionary<ulong, LavalinkGuildConnection> ConnectedGuilds { get; }
+ internal ConcurrentDictionary<ulong, LavalinkGuildConnection> ConnectedGuildsInternal = new();
+
+ /// <summary>
+ /// Gets the REST client for this Lavalink connection.
+ /// </summary>
+ public LavalinkRestClient Rest { get; }
+
+ /// <summary>
+ /// Gets the parent extension which this node connection belongs to.
+ /// </summary>
+ public LavalinkExtension Parent { get; }
+
+ /// <summary>
+ /// Gets the Discord client this node connection belongs to.
+ /// </summary>
+ public DiscordClient Discord { get; }
+
+ /// <summary>
+ /// Gets the configuration.
+ /// </summary>
+ internal LavalinkConfiguration Configuration { get; }
+ /// <summary>
+ /// Gets the region.
+ /// </summary>
+ internal DiscordVoiceRegion Region { get; }
+
+ /// <summary>
+ /// Gets or sets the web socket.
+ /// </summary>
+ private IWebSocketClient _webSocket;
+
+ /// <summary>
+ /// Gets the voice state updates.
+ /// </summary>
+ private readonly ConcurrentDictionary<ulong, TaskCompletionSource<VoiceStateUpdateEventArgs>> _voiceStateUpdates;
+
+ /// <summary>
+ /// Gets the voice server updates.
+ /// </summary>
+ private readonly ConcurrentDictionary<ulong, TaskCompletionSource<VoiceServerUpdateEventArgs>> _voiceServerUpdates;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkNodeConnection"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="extension">the event.tension.</param>
+ /// <param name="config">The config.</param>
+ internal LavalinkNodeConnection(DiscordClient client, LavalinkExtension extension, LavalinkConfiguration config)
+ {
+ this.Discord = client;
+ this.Parent = extension;
+ this.Configuration = new LavalinkConfiguration(config);
- if (config.Region != null && this.Discord.VoiceRegions.Values.Contains(config.Region))
- this.Region = config.Region;
+ if (config.Region != null && this.Discord.VoiceRegions.Values.Contains(config.Region))
+ this.Region = config.Region;
- this.ConnectedGuilds = new ReadOnlyConcurrentDictionary<ulong, LavalinkGuildConnection>(this.ConnectedGuildsInternal);
- this.Statistics = new LavalinkStatistics();
+ this.ConnectedGuilds = new ReadOnlyConcurrentDictionary<ulong, LavalinkGuildConnection>(this.ConnectedGuildsInternal);
+ this.Statistics = new LavalinkStatistics();
- this._lavalinkSocketError = new AsyncEvent<LavalinkNodeConnection, SocketErrorEventArgs>("LAVALINK_SOCKET_ERROR", TimeSpan.Zero, this.Discord.EventErrorHandler);
- this._disconnected = new AsyncEvent<LavalinkNodeConnection, NodeDisconnectedEventArgs>("LAVALINK_NODE_DISCONNECTED", TimeSpan.Zero, this.Discord.EventErrorHandler);
- this._statsReceived = new AsyncEvent<LavalinkNodeConnection, StatisticsReceivedEventArgs>("LAVALINK_STATS_RECEIVED", TimeSpan.Zero, this.Discord.EventErrorHandler);
- this._playerUpdated = new AsyncEvent<LavalinkGuildConnection, PlayerUpdateEventArgs>("LAVALINK_PLAYER_UPDATED", TimeSpan.Zero, this.Discord.EventErrorHandler);
- this._playbackStarted = new AsyncEvent<LavalinkGuildConnection, TrackStartEventArgs>("LAVALINK_PLAYBACK_STARTED", TimeSpan.Zero, this.Discord.EventErrorHandler);
- this._playbackFinished = new AsyncEvent<LavalinkGuildConnection, TrackFinishEventArgs>("LAVALINK_PLAYBACK_FINISHED", TimeSpan.Zero, this.Discord.EventErrorHandler);
- this._trackStuck = new AsyncEvent<LavalinkGuildConnection, TrackStuckEventArgs>("LAVALINK_TRACK_STUCK", TimeSpan.Zero, this.Discord.EventErrorHandler);
- this._trackException = new AsyncEvent<LavalinkGuildConnection, TrackExceptionEventArgs>("LAVALINK_TRACK_EXCEPTION", TimeSpan.Zero, this.Discord.EventErrorHandler);
+ this._lavalinkSocketError = new AsyncEvent<LavalinkNodeConnection, SocketErrorEventArgs>("LAVALINK_SOCKET_ERROR", TimeSpan.Zero, this.Discord.EventErrorHandler);
+ this._disconnected = new AsyncEvent<LavalinkNodeConnection, NodeDisconnectedEventArgs>("LAVALINK_NODE_DISCONNECTED", TimeSpan.Zero, this.Discord.EventErrorHandler);
+ this._statsReceived = new AsyncEvent<LavalinkNodeConnection, StatisticsReceivedEventArgs>("LAVALINK_STATS_RECEIVED", TimeSpan.Zero, this.Discord.EventErrorHandler);
+ this._playerUpdated = new AsyncEvent<LavalinkGuildConnection, PlayerUpdateEventArgs>("LAVALINK_PLAYER_UPDATED", TimeSpan.Zero, this.Discord.EventErrorHandler);
+ this._playbackStarted = new AsyncEvent<LavalinkGuildConnection, TrackStartEventArgs>("LAVALINK_PLAYBACK_STARTED", TimeSpan.Zero, this.Discord.EventErrorHandler);
+ this._playbackFinished = new AsyncEvent<LavalinkGuildConnection, TrackFinishEventArgs>("LAVALINK_PLAYBACK_FINISHED", TimeSpan.Zero, this.Discord.EventErrorHandler);
+ this._trackStuck = new AsyncEvent<LavalinkGuildConnection, TrackStuckEventArgs>("LAVALINK_TRACK_STUCK", TimeSpan.Zero, this.Discord.EventErrorHandler);
+ this._trackException = new AsyncEvent<LavalinkGuildConnection, TrackExceptionEventArgs>("LAVALINK_TRACK_EXCEPTION", TimeSpan.Zero, this.Discord.EventErrorHandler);
- this._voiceServerUpdates = new ConcurrentDictionary<ulong, TaskCompletionSource<VoiceServerUpdateEventArgs>>();
- this._voiceStateUpdates = new ConcurrentDictionary<ulong, TaskCompletionSource<VoiceStateUpdateEventArgs>>();
- this.Discord.VoiceStateUpdated += this.Discord_VoiceStateUpdated;
- this.Discord.VoiceServerUpdated += this.Discord_VoiceServerUpdated;
+ this._voiceServerUpdates = new ConcurrentDictionary<ulong, TaskCompletionSource<VoiceServerUpdateEventArgs>>();
+ this._voiceStateUpdates = new ConcurrentDictionary<ulong, TaskCompletionSource<VoiceStateUpdateEventArgs>>();
+ this.Discord.VoiceStateUpdated += this.Discord_VoiceStateUpdated;
+ this.Discord.VoiceServerUpdated += this.Discord_VoiceServerUpdated;
- this.Rest = new LavalinkRestClient(this.Configuration, this.Discord);
+ this.Rest = new LavalinkRestClient(this.Configuration, this.Discord);
- Volatile.Write(ref this._isDisposed, false);
- }
+ Volatile.Write(ref this._isDisposed, false);
+ }
- /// <summary>
- /// Establishes a connection to the Lavalink node.
- /// </summary>
- /// <returns></returns>
- internal async Task StartAsync()
- {
- if (this.Discord?.CurrentUser?.Id == null || this.Discord?.ShardCount == null)
- throw new InvalidOperationException("This operation requires the Discord client to be fully initialized.");
-
- this._webSocket = this.Discord.Configuration.WebSocketClientFactory(this.Discord.Configuration.Proxy, this.Discord.ServiceProvider);
- this._webSocket.Connected += this.WebSocket_OnConnect;
- this._webSocket.Disconnected += this.WebSocket_OnDisconnect;
- this._webSocket.ExceptionThrown += this.WebSocket_OnException;
- this._webSocket.MessageReceived += this.WebSocket_OnMessage;
-
- this._webSocket.AddDefaultHeader("Authorization", this.Configuration.Password);
- this._webSocket.AddDefaultHeader("Num-Shards", this.Discord.ShardCount.ToString(CultureInfo.InvariantCulture));
- this._webSocket.AddDefaultHeader("User-Id", this.Discord.CurrentUser.Id.ToString(CultureInfo.InvariantCulture));
- if (this.Configuration.ResumeKey != null)
- this._webSocket.AddDefaultHeader("Resume-Key", this.Configuration.ResumeKey);
-
- do
+ /// <summary>
+ /// Establishes a connection to the Lavalink node.
+ /// </summary>
+ /// <returns></returns>
+ internal async Task StartAsync()
{
- try
- {
- if (this._backoff != 0)
- {
- await Task.Delay(this._backoff).ConfigureAwait(false);
- this._backoff = Math.Min(this._backoff * 2, MAXIMUM_BACKOFF);
- }
- else
- {
- this._backoff = MINIMUM_BACKOFF;
- }
-
- await this._webSocket.ConnectAsync(new Uri(this.Configuration.SocketEndpoint.ToWebSocketString())).ConfigureAwait(false);
- break;
- }
- catch (PlatformNotSupportedException)
- { throw; }
- catch (NotImplementedException)
- { throw; }
- catch (Exception ex)
+ if (this.Discord?.CurrentUser?.Id == null || this.Discord?.ShardCount == null)
+ throw new InvalidOperationException("This operation requires the Discord client to be fully initialized.");
+
+ this._webSocket = this.Discord.Configuration.WebSocketClientFactory(this.Discord.Configuration.Proxy, this.Discord.ServiceProvider);
+ this._webSocket.Connected += this.WebSocket_OnConnect;
+ this._webSocket.Disconnected += this.WebSocket_OnDisconnect;
+ this._webSocket.ExceptionThrown += this.WebSocket_OnException;
+ this._webSocket.MessageReceived += this.WebSocket_OnMessage;
+
+ this._webSocket.AddDefaultHeader("Authorization", this.Configuration.Password);
+ this._webSocket.AddDefaultHeader("Num-Shards", this.Discord.ShardCount.ToString(CultureInfo.InvariantCulture));
+ this._webSocket.AddDefaultHeader("User-Id", this.Discord.CurrentUser.Id.ToString(CultureInfo.InvariantCulture));
+ if (this.Configuration.ResumeKey != null)
+ this._webSocket.AddDefaultHeader("Resume-Key", this.Configuration.ResumeKey);
+
+ do
{
- if (!this.Configuration.SocketAutoReconnect || this._backoff == MAXIMUM_BACKOFF)
+ try
{
- this.Discord.Logger.LogCritical(LavalinkEvents.LavalinkConnectionError, ex, "Failed to connect to Lavalink.");
- throw;
+ if (this._backoff != 0)
+ {
+ await Task.Delay(this._backoff).ConfigureAwait(false);
+ this._backoff = Math.Min(this._backoff * 2, MAXIMUM_BACKOFF);
+ }
+ else
+ {
+ this._backoff = MINIMUM_BACKOFF;
+ }
+
+ await this._webSocket.ConnectAsync(new Uri(this.Configuration.SocketEndpoint.ToWebSocketString())).ConfigureAwait(false);
+ break;
}
- else
+ catch (PlatformNotSupportedException)
+ { throw; }
+ catch (NotImplementedException)
+ { throw; }
+ catch (Exception ex)
{
- this.Discord.Logger.LogCritical(LavalinkEvents.LavalinkConnectionError, ex, $"Failed to connect to Lavalink, retrying in {this._backoff} ms.");
+ if (!this.Configuration.SocketAutoReconnect || this._backoff == MAXIMUM_BACKOFF)
+ {
+ this.Discord.Logger.LogCritical(LavalinkEvents.LavalinkConnectionError, ex, "Failed to connect to Lavalink.");
+ throw;
+ }
+ else
+ {
+ this.Discord.Logger.LogCritical(LavalinkEvents.LavalinkConnectionError, ex, $"Failed to connect to Lavalink, retrying in {this._backoff} ms.");
+ }
}
}
+ while (this.Configuration.SocketAutoReconnect);
+
+ Volatile.Write(ref this._isDisposed, false);
}
- while (this.Configuration.SocketAutoReconnect);
- Volatile.Write(ref this._isDisposed, false);
- }
+ /// <summary>
+ /// Stops this Lavalink node connection and frees resources.
+ /// </summary>
+ /// <returns></returns>
+ public async Task StopAsync()
+ {
+ foreach (var kvp in this.ConnectedGuildsInternal)
+ await kvp.Value.DisconnectAsync().ConfigureAwait(false);
- /// <summary>
- /// Stops this Lavalink node connection and frees resources.
- /// </summary>
- /// <returns></returns>
- public async Task StopAsync()
- {
- foreach (var kvp in this.ConnectedGuildsInternal)
- await kvp.Value.DisconnectAsync().ConfigureAwait(false);
+ this.NodeDisconnected?.Invoke(this);
- this.NodeDisconnected?.Invoke(this);
+ Volatile.Write(ref this._isDisposed, true);
+ await this._webSocket.DisconnectAsync().ConfigureAwait(false);
+ // this should not be here, no?
+ //await this._disconnected.InvokeAsync(this, new NodeDisconnectedEventArgs(this)).ConfigureAwait(false);
+ }
- Volatile.Write(ref this._isDisposed, true);
- await this._webSocket.DisconnectAsync().ConfigureAwait(false);
- // this should not be here, no?
- //await this._disconnected.InvokeAsync(this, new NodeDisconnectedEventArgs(this)).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Connects this Lavalink node to specified Discord channel.
+ /// </summary>
+ /// <param name="channel">Voice channel to connect to.</param>
+ /// <returns>Channel connection, which allows for playback control.</returns>
+ public async Task<LavalinkGuildConnection> ConnectAsync(DiscordChannel channel)
+ {
+ if (this.ConnectedGuildsInternal.ContainsKey(channel.Guild.Id))
+ return this.ConnectedGuildsInternal[channel.Guild.Id];
- /// <summary>
- /// Connects this Lavalink node to specified Discord channel.
- /// </summary>
- /// <param name="channel">Voice channel to connect to.</param>
- /// <returns>Channel connection, which allows for playback control.</returns>
- public async Task<LavalinkGuildConnection> ConnectAsync(DiscordChannel channel)
- {
- if (this.ConnectedGuildsInternal.ContainsKey(channel.Guild.Id))
- return this.ConnectedGuildsInternal[channel.Guild.Id];
+ if (channel.Guild == null || (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage))
+ throw new ArgumentException("Invalid channel specified.", nameof(channel));
- if (channel.Guild == null || (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage))
- throw new ArgumentException("Invalid channel specified.", nameof(channel));
+ var vstut = new TaskCompletionSource<VoiceStateUpdateEventArgs>();
+ var vsrut = new TaskCompletionSource<VoiceServerUpdateEventArgs>();
+ this._voiceStateUpdates[channel.Guild.Id] = vstut;
+ this._voiceServerUpdates[channel.Guild.Id] = vsrut;
- var vstut = new TaskCompletionSource<VoiceStateUpdateEventArgs>();
- var vsrut = new TaskCompletionSource<VoiceServerUpdateEventArgs>();
- this._voiceStateUpdates[channel.Guild.Id] = vstut;
- this._voiceServerUpdates[channel.Guild.Id] = vsrut;
+ var vsd = new VoiceDispatch
+ {
+ OpCode = 4,
+ Payload = new VoiceStateUpdatePayload
+ {
+ GuildId = channel.Guild.Id,
+ ChannelId = channel.Id,
+ Deafened = false,
+ Muted = false
+ }
+ };
+ var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);
+ await (channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
+ var vstu = await vstut.Task.ConfigureAwait(false);
+ var vsru = await vsrut.Task.ConfigureAwait(false);
+ await this.SendPayloadAsync(new LavalinkVoiceUpdate(vstu, vsru)).ConfigureAwait(false);
+
+ var con = new LavalinkGuildConnection(this, channel, vstu);
+ con.ChannelDisconnected += this.Con_ChannelDisconnected;
+ con.PlayerUpdated += (s, e) => this._playerUpdated.InvokeAsync(s, e);
+ con.PlaybackStarted += (s, e) => this._playbackStarted.InvokeAsync(s, e);
+ con.PlaybackFinished += (s, e) => this._playbackFinished.InvokeAsync(s, e);
+ con.TrackStuck += (s, e) => this._trackStuck.InvokeAsync(s, e);
+ con.TrackException += (s, e) => this._trackException.InvokeAsync(s, e);
+ this.ConnectedGuildsInternal[channel.Guild.Id] = con;
+
+ return con;
+ }
- var vsd = new VoiceDispatch
+ /// <summary>
+ /// Gets a Lavalink connection to specified Discord channel.
+ /// </summary>
+ /// <param name="guild">Guild to get connection for.</param>
+ /// <returns>Channel connection, which allows for playback control.</returns>
+ public LavalinkGuildConnection GetGuildConnection(DiscordGuild guild)
+ => this.ConnectedGuildsInternal.TryGetValue(guild.Id, out var lgc) && lgc.IsConnected ? lgc : null;
+
+ /// <summary>
+ /// Sends the payload async.
+ /// </summary>
+ /// <param name="payload">The payload.</param>
+ internal async Task SendPayloadAsync(LavalinkPayload payload)
+ => await this.WsSendAsync(JsonConvert.SerializeObject(payload, Formatting.None)).ConfigureAwait(false);
+
+ /// <summary>
+ /// Webs the socket_ on message.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">the event.ent.</param>
+ private async Task WebSocket_OnMessage(IWebSocketClient client, SocketMessageEventArgs e)
{
- OpCode = 4,
- Payload = new VoiceStateUpdatePayload
+ if (e is not SocketTextMessageEventArgs et)
{
- GuildId = channel.Guild.Id,
- ChannelId = channel.Id,
- Deafened = false,
- Muted = false
+ this.Discord.Logger.LogCritical(LavalinkEvents.LavalinkConnectionError, "Lavalink sent binary data - unable to process");
+ return;
}
- };
- var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);
- await (channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
- var vstu = await vstut.Task.ConfigureAwait(false);
- var vsru = await vsrut.Task.ConfigureAwait(false);
- await this.SendPayloadAsync(new LavalinkVoiceUpdate(vstu, vsru)).ConfigureAwait(false);
-
- var con = new LavalinkGuildConnection(this, channel, vstu);
- con.ChannelDisconnected += this.Con_ChannelDisconnected;
- con.PlayerUpdated += (s, e) => this._playerUpdated.InvokeAsync(s, e);
- con.PlaybackStarted += (s, e) => this._playbackStarted.InvokeAsync(s, e);
- con.PlaybackFinished += (s, e) => this._playbackFinished.InvokeAsync(s, e);
- con.TrackStuck += (s, e) => this._trackStuck.InvokeAsync(s, e);
- con.TrackException += (s, e) => this._trackException.InvokeAsync(s, e);
- this.ConnectedGuildsInternal[channel.Guild.Id] = con;
-
- return con;
- }
- /// <summary>
- /// Gets a Lavalink connection to specified Discord channel.
- /// </summary>
- /// <param name="guild">Guild to get connection for.</param>
- /// <returns>Channel connection, which allows for playback control.</returns>
- public LavalinkGuildConnection GetGuildConnection(DiscordGuild guild)
- => this.ConnectedGuildsInternal.TryGetValue(guild.Id, out var lgc) && lgc.IsConnected ? lgc : null;
-
- /// <summary>
- /// Sends the payload async.
- /// </summary>
- /// <param name="payload">The payload.</param>
- internal async Task SendPayloadAsync(LavalinkPayload payload)
- => await this.WsSendAsync(JsonConvert.SerializeObject(payload, Formatting.None)).ConfigureAwait(false);
+ this.Discord.Logger.LogTrace(LavalinkEvents.LavalinkWsRx, et.Message);
- /// <summary>
- /// Webs the socket_ on message.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">the event.ent.</param>
- private async Task WebSocket_OnMessage(IWebSocketClient client, SocketMessageEventArgs e)
- {
- if (e is not SocketTextMessageEventArgs et)
- {
- this.Discord.Logger.LogCritical(LavalinkEvents.LavalinkConnectionError, "Lavalink sent binary data - unable to process");
- return;
+ var json = et.Message;
+ var jsonData = JObject.Parse(json);
+ switch (jsonData["op"].ToString())
+ {
+ case "playerUpdate":
+ var gid = (ulong)jsonData["guildId"];
+ var state = jsonData["state"].ToObject<LavalinkState>();
+ if (this.ConnectedGuildsInternal.TryGetValue(gid, out var lvl))
+ await lvl.InternalUpdatePlayerStateAsync(state).ConfigureAwait(false);
+ break;
+
+ case "stats":
+ var statsRaw = jsonData.ToObject<LavalinkStats>();
+ this.Statistics.Update(statsRaw);
+ await this._statsReceived.InvokeAsync(this, new StatisticsReceivedEventArgs(this.Discord.ServiceProvider, this.Statistics)).ConfigureAwait(false);
+ break;
+
+ case "event":
+ var evtype = jsonData["type"].ToObject<EventType>();
+ var guildId = (ulong)jsonData["guildId"];
+ switch (evtype)
+ {
+ case EventType.TrackStartEvent:
+ if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEvtst))
+ await lvlEvtst.InternalPlaybackStartedAsync(jsonData["track"].ToString()).ConfigureAwait(false);
+ break;
+
+ case EventType.TrackEndEvent:
+ var reason = TrackEndReason.Cleanup;
+ switch (jsonData["reason"].ToString())
+ {
+ case "FINISHED":
+ reason = TrackEndReason.Finished;
+ break;
+ case "LOAD_FAILED":
+ reason = TrackEndReason.LoadFailed;
+ break;
+ case "STOPPED":
+ reason = TrackEndReason.Stopped;
+ break;
+ case "REPLACED":
+ reason = TrackEndReason.Replaced;
+ break;
+ case "CLEANUP":
+ reason = TrackEndReason.Cleanup;
+ break;
+ }
+ if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEvtf))
+ await lvlEvtf.InternalPlaybackFinishedAsync(new TrackFinishData { Track = jsonData["track"].ToString(), Reason = reason }).ConfigureAwait(false);
+ break;
+
+ case EventType.TrackStuckEvent:
+ if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEvts))
+ await lvlEvts.InternalTrackStuckAsync(new TrackStuckData { Track = jsonData["track"].ToString(), Threshold = (long)jsonData["thresholdMs"] }).ConfigureAwait(false);
+ break;
+
+ case EventType.TrackExceptionEvent:
+ if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEvte))
+ await lvlEvte.InternalTrackExceptionAsync(new TrackExceptionData { Track = jsonData["track"].ToString(), Error = jsonData["error"].ToString() }).ConfigureAwait(false);
+ break;
+
+ case EventType.WebSocketClosedEvent:
+ if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEwsce))
+ {
+ lvlEwsce.VoiceWsDisconnectTcs.SetResult(true);
+ await lvlEwsce.InternalWebSocketClosedAsync(new WebSocketCloseEventArgs(jsonData["code"].ToObject<int>(), jsonData["reason"].ToString(), jsonData["byRemote"].ToObject<bool>(), this.Discord.ServiceProvider)).ConfigureAwait(false);
+ }
+ break;
+ }
+ break;
+ }
}
- this.Discord.Logger.LogTrace(LavalinkEvents.LavalinkWsRx, et.Message);
-
- var json = et.Message;
- var jsonData = JObject.Parse(json);
- switch (jsonData["op"].ToString())
+ /// <summary>
+ /// Webs the socket_ on exception.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">the event.</param>
+ private Task WebSocket_OnException(IWebSocketClient client, SocketErrorEventArgs e)
+ => this._lavalinkSocketError.InvokeAsync(this, new SocketErrorEventArgs(client.ServiceProvider) { Exception = e.Exception });
+
+ /// <summary>
+ /// Webs the socket_ on disconnect.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">the event.</param>
+ private async Task WebSocket_OnDisconnect(IWebSocketClient client, SocketCloseEventArgs e)
{
- case "playerUpdate":
- var gid = (ulong)jsonData["guildId"];
- var state = jsonData["state"].ToObject<LavalinkState>();
- if (this.ConnectedGuildsInternal.TryGetValue(gid, out var lvl))
- await lvl.InternalUpdatePlayerStateAsync(state).ConfigureAwait(false);
- break;
-
- case "stats":
- var statsRaw = jsonData.ToObject<LavalinkStats>();
- this.Statistics.Update(statsRaw);
- await this._statsReceived.InvokeAsync(this, new StatisticsReceivedEventArgs(this.Discord.ServiceProvider, this.Statistics)).ConfigureAwait(false);
- break;
-
- case "event":
- var evtype = jsonData["type"].ToObject<EventType>();
- var guildId = (ulong)jsonData["guildId"];
- switch (evtype)
+ if (this.IsConnected && e.CloseCode != 1001 && e.CloseCode != -1)
+ {
+ this.Discord.Logger.LogWarning(LavalinkEvents.LavalinkConnectionClosed, "Connection broken ({0}, '{1}'), reconnecting", e.CloseCode, e.CloseMessage);
+ await this._disconnected.InvokeAsync(this, new NodeDisconnectedEventArgs(this, false)).ConfigureAwait(false);
+
+ if (this.Configuration.SocketAutoReconnect)
+ await this.StartAsync().ConfigureAwait(false);
+ }
+ else if (e.CloseCode != 1001 && e.CloseCode != -1)
+ {
+ this.Discord.Logger.LogInformation(LavalinkEvents.LavalinkConnectionClosed, "Connection closed ({0}, '{1}')", e.CloseCode, e.CloseMessage);
+ this.NodeDisconnected?.Invoke(this);
+ await this._disconnected.InvokeAsync(this, new NodeDisconnectedEventArgs(this, true)).ConfigureAwait(false);
+ }
+ else
+ {
+ Volatile.Write(ref this._isDisposed, true);
+ this.Discord.Logger.LogWarning(LavalinkEvents.LavalinkConnectionClosed, "Lavalink died");
+ foreach (var kvp in this.ConnectedGuildsInternal)
{
- case EventType.TrackStartEvent:
- if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEvtst))
- await lvlEvtst.InternalPlaybackStartedAsync(jsonData["track"].ToString()).ConfigureAwait(false);
- break;
-
- case EventType.TrackEndEvent:
- var reason = TrackEndReason.Cleanup;
- switch (jsonData["reason"].ToString())
- {
- case "FINISHED":
- reason = TrackEndReason.Finished;
- break;
- case "LOAD_FAILED":
- reason = TrackEndReason.LoadFailed;
- break;
- case "STOPPED":
- reason = TrackEndReason.Stopped;
- break;
- case "REPLACED":
- reason = TrackEndReason.Replaced;
- break;
- case "CLEANUP":
- reason = TrackEndReason.Cleanup;
- break;
- }
- if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEvtf))
- await lvlEvtf.InternalPlaybackFinishedAsync(new TrackFinishData { Track = jsonData["track"].ToString(), Reason = reason }).ConfigureAwait(false);
- break;
-
- case EventType.TrackStuckEvent:
- if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEvts))
- await lvlEvts.InternalTrackStuckAsync(new TrackStuckData { Track = jsonData["track"].ToString(), Threshold = (long)jsonData["thresholdMs"] }).ConfigureAwait(false);
- break;
-
- case EventType.TrackExceptionEvent:
- if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEvte))
- await lvlEvte.InternalTrackExceptionAsync(new TrackExceptionData { Track = jsonData["track"].ToString(), Error = jsonData["error"].ToString() }).ConfigureAwait(false);
- break;
-
- case EventType.WebSocketClosedEvent:
- if (this.ConnectedGuildsInternal.TryGetValue(guildId, out var lvlEwsce))
- {
- lvlEwsce.VoiceWsDisconnectTcs.SetResult(true);
- await lvlEwsce.InternalWebSocketClosedAsync(new WebSocketCloseEventArgs(jsonData["code"].ToObject<int>(), jsonData["reason"].ToString(), jsonData["byRemote"].ToObject<bool>(), this.Discord.ServiceProvider)).ConfigureAwait(false);
- }
- break;
+ await kvp.Value.SendVoiceUpdateAsync().ConfigureAwait(false);
+ _ = this.ConnectedGuildsInternal.TryRemove(kvp.Key, out _);
}
- break;
- }
- }
+ this.NodeDisconnected?.Invoke(this);
+ await this._disconnected.InvokeAsync(this, new NodeDisconnectedEventArgs(this, false)).ConfigureAwait(false);
- /// <summary>
- /// Webs the socket_ on exception.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">the event.</param>
- private Task WebSocket_OnException(IWebSocketClient client, SocketErrorEventArgs e)
- => this._lavalinkSocketError.InvokeAsync(this, new SocketErrorEventArgs(client.ServiceProvider) { Exception = e.Exception });
+ if (this.Configuration.SocketAutoReconnect)
+ await this.StartAsync().ConfigureAwait(false);
+ }
+ }
- /// <summary>
- /// Webs the socket_ on disconnect.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">the event.</param>
- private async Task WebSocket_OnDisconnect(IWebSocketClient client, SocketCloseEventArgs e)
- {
- if (this.IsConnected && e.CloseCode != 1001 && e.CloseCode != -1)
+ /// <summary>
+ /// Webs the socket_ on connect.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="ea">the event..</param>
+ private async Task WebSocket_OnConnect(IWebSocketClient client, SocketEventArgs ea)
{
- this.Discord.Logger.LogWarning(LavalinkEvents.LavalinkConnectionClosed, "Connection broken ({0}, '{1}'), reconnecting", e.CloseCode, e.CloseMessage);
- await this._disconnected.InvokeAsync(this, new NodeDisconnectedEventArgs(this, false)).ConfigureAwait(false);
+ this.Discord.Logger.LogDebug(LavalinkEvents.LavalinkConnected, "Connection to Lavalink node established");
+ this._backoff = 0;
- if (this.Configuration.SocketAutoReconnect)
- await this.StartAsync().ConfigureAwait(false);
- }
- else if (e.CloseCode != 1001 && e.CloseCode != -1)
- {
- this.Discord.Logger.LogInformation(LavalinkEvents.LavalinkConnectionClosed, "Connection closed ({0}, '{1}')", e.CloseCode, e.CloseMessage);
- this.NodeDisconnected?.Invoke(this);
- await this._disconnected.InvokeAsync(this, new NodeDisconnectedEventArgs(this, true)).ConfigureAwait(false);
+ if (this.Configuration.ResumeKey != null)
+ await this.SendPayloadAsync(new LavalinkConfigureResume(this.Configuration.ResumeKey, this.Configuration.ResumeTimeout)).ConfigureAwait(false);
}
- else
- {
- Volatile.Write(ref this._isDisposed, true);
- this.Discord.Logger.LogWarning(LavalinkEvents.LavalinkConnectionClosed, "Lavalink died");
- foreach (var kvp in this.ConnectedGuildsInternal)
- {
- await kvp.Value.SendVoiceUpdateAsync().ConfigureAwait(false);
- _ = this.ConnectedGuildsInternal.TryRemove(kvp.Key, out _);
- }
- this.NodeDisconnected?.Invoke(this);
- await this._disconnected.InvokeAsync(this, new NodeDisconnectedEventArgs(this, false)).ConfigureAwait(false);
- if (this.Configuration.SocketAutoReconnect)
- await this.StartAsync().ConfigureAwait(false);
- }
- }
+ /// <summary>
+ /// Con_S the channel disconnected.
+ /// </summary>
+ /// <param name="con">The con.</param>
+ private void Con_ChannelDisconnected(LavalinkGuildConnection con)
+ => this.ConnectedGuildsInternal.TryRemove(con.GuildId, out _);
+
+ /// <summary>
+ /// Discord voice state updated.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">the event.</param>
+ private Task Discord_VoiceStateUpdated(DiscordClient client, VoiceStateUpdateEventArgs e)
+ {
+ var gld = e.Guild;
+ if (gld == null)
+ return Task.CompletedTask;
- /// <summary>
- /// Webs the socket_ on connect.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="ea">the event..</param>
- private async Task WebSocket_OnConnect(IWebSocketClient client, SocketEventArgs ea)
- {
- this.Discord.Logger.LogDebug(LavalinkEvents.LavalinkConnected, "Connection to Lavalink node established");
- this._backoff = 0;
+ if (e.User == null)
+ return Task.CompletedTask;
- if (this.Configuration.ResumeKey != null)
- await this.SendPayloadAsync(new LavalinkConfigureResume(this.Configuration.ResumeKey, this.Configuration.ResumeTimeout)).ConfigureAwait(false);
- }
+ if (e.User.Id == this.Discord.CurrentUser.Id)
+ {
+ if (this.ConnectedGuildsInternal.TryGetValue(e.Guild.Id, out var lvlgc))
+ lvlgc.VoiceStateUpdate = e;
- /// <summary>
- /// Con_S the channel disconnected.
- /// </summary>
- /// <param name="con">The con.</param>
- private void Con_ChannelDisconnected(LavalinkGuildConnection con)
- => this.ConnectedGuildsInternal.TryRemove(con.GuildId, out _);
+ if (e.After.Channel == null && this.IsConnected && this.ConnectedGuildsInternal.ContainsKey(gld.Id))
+ {
+ _ = Task.Run(async () =>
+ {
+ var delayTask = Task.Delay(this.Configuration.WebSocketCloseTimeout);
+ var tcs = lvlgc.VoiceWsDisconnectTcs.Task;
+ _ = await Task.WhenAny(delayTask, tcs).ConfigureAwait(false);
+
+ await lvlgc.DisconnectInternalAsync(false, true).ConfigureAwait(false);
+ _ = this.ConnectedGuildsInternal.TryRemove(gld.Id, out _);
+ });
+ }
- /// <summary>
- /// Discord voice state updated.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">the event.</param>
- private Task Discord_VoiceStateUpdated(DiscordClient client, VoiceStateUpdateEventArgs e)
- {
- var gld = e.Guild;
- if (gld == null)
- return Task.CompletedTask;
+ if (!string.IsNullOrWhiteSpace(e.SessionId) && e.Channel != null && this._voiceStateUpdates.TryRemove(gld.Id, out var xe))
+ xe.SetResult(e);
+ }
- if (e.User == null)
return Task.CompletedTask;
+ }
- if (e.User.Id == this.Discord.CurrentUser.Id)
+ /// <summary>
+ /// Discord voice server updated.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">the event.</param>
+ private Task Discord_VoiceServerUpdated(DiscordClient client, VoiceServerUpdateEventArgs e)
{
- if (this.ConnectedGuildsInternal.TryGetValue(e.Guild.Id, out var lvlgc))
- lvlgc.VoiceStateUpdate = e;
+ var gld = e.Guild;
+ if (gld == null)
+ return Task.CompletedTask;
- if (e.After.Channel == null && this.IsConnected && this.ConnectedGuildsInternal.ContainsKey(gld.Id))
+ if (this.ConnectedGuildsInternal.TryGetValue(e.Guild.Id, out var lvlgc))
{
- _ = Task.Run(async () =>
- {
- var delayTask = Task.Delay(this.Configuration.WebSocketCloseTimeout);
- var tcs = lvlgc.VoiceWsDisconnectTcs.Task;
- _ = await Task.WhenAny(delayTask, tcs).ConfigureAwait(false);
-
- await lvlgc.DisconnectInternalAsync(false, true).ConfigureAwait(false);
- _ = this.ConnectedGuildsInternal.TryRemove(gld.Id, out _);
- });
+ var lvlp = new LavalinkVoiceUpdate(lvlgc.VoiceStateUpdate, e);
+ _ = Task.Run(() => this.WsSendAsync(JsonConvert.SerializeObject(lvlp)));
}
- if (!string.IsNullOrWhiteSpace(e.SessionId) && e.Channel != null && this._voiceStateUpdates.TryRemove(gld.Id, out var xe))
+ if (this._voiceServerUpdates.TryRemove(gld.Id, out var xe))
xe.SetResult(e);
- }
-
- return Task.CompletedTask;
- }
- /// <summary>
- /// Discord voice server updated.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">the event.</param>
- private Task Discord_VoiceServerUpdated(DiscordClient client, VoiceServerUpdateEventArgs e)
- {
- var gld = e.Guild;
- if (gld == null)
return Task.CompletedTask;
+ }
+ /// <summary>
+ /// Ws the send async.
+ /// </summary>
+ /// <param name="payload">The payload.</param>
- if (this.ConnectedGuildsInternal.TryGetValue(e.Guild.Id, out var lvlgc))
+ private async Task WsSendAsync(string payload)
{
- var lvlp = new LavalinkVoiceUpdate(lvlgc.VoiceStateUpdate, e);
- _ = Task.Run(() => this.WsSendAsync(JsonConvert.SerializeObject(lvlp)));
+ this.Discord.Logger.LogTrace(LavalinkEvents.LavalinkWsTx, payload);
+ await this._webSocket.SendMessageAsync(payload).ConfigureAwait(false);
}
- if (this._voiceServerUpdates.TryRemove(gld.Id, out var xe))
- xe.SetResult(e);
-
- return Task.CompletedTask;
+ internal event NodeDisconnectedEventHandler NodeDisconnected;
}
- /// <summary>
- /// Ws the send async.
- /// </summary>
- /// <param name="payload">The payload.</param>
-
- private async Task WsSendAsync(string payload)
- {
- this.Discord.Logger.LogTrace(LavalinkEvents.LavalinkWsTx, payload);
- await this._webSocket.SendMessageAsync(payload).ConfigureAwait(false);
- }
-
- internal event NodeDisconnectedEventHandler NodeDisconnected;
}
diff --git a/DisCatSharp.Lavalink/LavalinkRestClient.cs b/DisCatSharp.Lavalink/LavalinkRestClient.cs
index f09721122..3af98915d 100644
--- a/DisCatSharp.Lavalink/LavalinkRestClient.cs
+++ b/DisCatSharp.Lavalink/LavalinkRestClient.cs
@@ -1,426 +1,427 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Http;
using System.Reflection;
using System.Threading.Tasks;
using DisCatSharp.Lavalink.Entities;
using DisCatSharp.Net;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// Represents a class for Lavalink REST calls.
-/// </summary>
-public sealed class LavalinkRestClient
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// Gets the REST connection endpoint for this client.
+ /// Represents a class for Lavalink REST calls.
/// </summary>
- public ConnectionEndpoint RestEndpoint { get; private set; }
-
- private HttpClient _http;
-
- private readonly ILogger _logger;
-
- private readonly Lazy<string> _dcsVersionString = new(() =>
+ public sealed class LavalinkRestClient
{
- var a = typeof(DiscordClient).GetTypeInfo().Assembly;
+ /// <summary>
+ /// Gets the REST connection endpoint for this client.
+ /// </summary>
+ public ConnectionEndpoint RestEndpoint { get; private set; }
- var iv = a.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
- if (iv != null)
- return iv.InformationalVersion;
+ private HttpClient _http;
- var v = a.GetName().Version;
- var vs = v.ToString(3);
+ private readonly ILogger _logger;
- if (v.Revision > 0)
- vs = $"{vs}, CI build {v.Revision}";
+ private readonly Lazy<string> _dcsVersionString = new(() =>
+ {
+ var a = typeof(DiscordClient).GetTypeInfo().Assembly;
- return vs;
- });
+ var iv = a.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
+ if (iv != null)
+ return iv.InformationalVersion;
- /// <summary>
- /// Creates a new Lavalink REST client.
- /// </summary>
- /// <param name="restEndpoint">The REST server endpoint to connect to.</param>
- /// <param name="password">The password for the remote server.</param>
- public LavalinkRestClient(ConnectionEndpoint restEndpoint, string password)
- {
- this.RestEndpoint = restEndpoint;
- this.ConfigureHttpHandling(password);
- }
+ var v = a.GetName().Version;
+ var vs = v.ToString(3);
- /// <summary>
- /// Initializes a new instance of the <see cref="LavalinkRestClient"/> class.
- /// </summary>
- /// <param name="config">The config.</param>
- /// <param name="client">The client.</param>
- internal LavalinkRestClient(LavalinkConfiguration config, BaseDiscordClient client)
- {
- this.RestEndpoint = config.RestEndpoint;
- this._logger = client.Logger;
- this.ConfigureHttpHandling(config.Password, client);
- }
+ if (v.Revision > 0)
+ vs = $"{vs}, CI build {v.Revision}";
- /// <summary>
- /// Gets the version of the Lavalink server.
- /// </summary>
- /// <returns></returns>
- public Task<string> GetVersionAsync()
- {
- var versionUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.VERSION}");
- return this.InternalGetVersionAsync(versionUri);
- }
+ return vs;
+ });
- #region Track_Loading
+ /// <summary>
+ /// Creates a new Lavalink REST client.
+ /// </summary>
+ /// <param name="restEndpoint">The REST server endpoint to connect to.</param>
+ /// <param name="password">The password for the remote server.</param>
+ public LavalinkRestClient(ConnectionEndpoint restEndpoint, string password)
+ {
+ this.RestEndpoint = restEndpoint;
+ this.ConfigureHttpHandling(password);
+ }
- /// <summary>
- /// Searches for specified terms.
- /// </summary>
- /// <param name="searchQuery">What to search for.</param>
- /// <param name="type">What platform will search for.</param>
- /// <returns>A collection of tracks matching the criteria.</returns>
- public Task<LavalinkLoadResult> GetTracksAsync(string searchQuery, LavalinkSearchType type = LavalinkSearchType.Youtube)
- {
- var prefix = type switch
+ /// <summary>
+ /// Initializes a new instance of the <see cref="LavalinkRestClient"/> class.
+ /// </summary>
+ /// <param name="config">The config.</param>
+ /// <param name="client">The client.</param>
+ internal LavalinkRestClient(LavalinkConfiguration config, BaseDiscordClient client)
{
- LavalinkSearchType.Youtube => "ytsearch:",
- LavalinkSearchType.SoundCloud => "scsearch:",
- LavalinkSearchType.Plain => "",
- _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
- };
- var str = WebUtility.UrlEncode(prefix + searchQuery);
- var tracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.LOAD_TRACKS}?identifier={str}");
- return this.InternalResolveTracksAsync(tracksUri);
- }
+ this.RestEndpoint = config.RestEndpoint;
+ this._logger = client.Logger;
+ this.ConfigureHttpHandling(config.Password, client);
+ }
- /// <summary>
- /// Loads tracks from specified URL.
- /// </summary>
- /// <param name="uri">URL to load tracks from.</param>
- /// <returns>A collection of tracks from the URL.</returns>
- public Task<LavalinkLoadResult> GetTracksAsync(Uri uri)
- {
- var str = WebUtility.UrlEncode(uri.ToString());
- var tracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.LOAD_TRACKS}?identifier={str}");
- return this.InternalResolveTracksAsync(tracksUri);
- }
+ /// <summary>
+ /// Gets the version of the Lavalink server.
+ /// </summary>
+ /// <returns></returns>
+ public Task<string> GetVersionAsync()
+ {
+ var versionUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.VERSION}");
+ return this.InternalGetVersionAsync(versionUri);
+ }
- /// <summary>
- /// Loads tracks from a local file.
- /// </summary>
- /// <param name="file">File to load tracks from.</param>
- /// <returns>A collection of tracks from the file.</returns>
- public Task<LavalinkLoadResult> GetTracksAsync(FileInfo file)
- {
- var str = WebUtility.UrlEncode(file.FullName);
- var tracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.LOAD_TRACKS}?identifier={str}");
- return this.InternalResolveTracksAsync(tracksUri);
- }
+ #region Track_Loading
- /// <summary>
- /// Decodes a base64 track string into a Lavalink track object.
- /// </summary>
- /// <param name="trackString">The base64 track string.</param>
- /// <returns></returns>
- public Task<LavalinkTrack> DecodeTrackAsync(string trackString)
- {
- var str = WebUtility.UrlEncode(trackString);
- var decodeTrackUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.DECODE_TRACK}?track={str}");
- return this.InternalDecodeTrackAsync(decodeTrackUri);
- }
+ /// <summary>
+ /// Searches for specified terms.
+ /// </summary>
+ /// <param name="searchQuery">What to search for.</param>
+ /// <param name="type">What platform will search for.</param>
+ /// <returns>A collection of tracks matching the criteria.</returns>
+ public Task<LavalinkLoadResult> GetTracksAsync(string searchQuery, LavalinkSearchType type = LavalinkSearchType.Youtube)
+ {
+ var prefix = type switch
+ {
+ LavalinkSearchType.Youtube => "ytsearch:",
+ LavalinkSearchType.SoundCloud => "scsearch:",
+ LavalinkSearchType.Plain => "",
+ _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
+ };
+ var str = WebUtility.UrlEncode(prefix + searchQuery);
+ var tracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.LOAD_TRACKS}?identifier={str}");
+ return this.InternalResolveTracksAsync(tracksUri);
+ }
- /// <summary>
- /// Decodes an array of base64 track strings into Lavalink track objects.
- /// </summary>
- /// <param name="trackStrings">The array of base64 track strings.</param>
- /// <returns></returns>
- public Task<IEnumerable<LavalinkTrack>> DecodeTracksAsync(string[] trackStrings)
- {
- var decodeTracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.DECODE_TRACKS}");
- return this.InternalDecodeTracksAsync(decodeTracksUri, trackStrings);
- }
+ /// <summary>
+ /// Loads tracks from specified URL.
+ /// </summary>
+ /// <param name="uri">URL to load tracks from.</param>
+ /// <returns>A collection of tracks from the URL.</returns>
+ public Task<LavalinkLoadResult> GetTracksAsync(Uri uri)
+ {
+ var str = WebUtility.UrlEncode(uri.ToString());
+ var tracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.LOAD_TRACKS}?identifier={str}");
+ return this.InternalResolveTracksAsync(tracksUri);
+ }
- /// <summary>
- /// Decodes a list of base64 track strings into Lavalink track objects.
- /// </summary>
- /// <param name="trackStrings">The list of base64 track strings.</param>
- /// <returns></returns>
- public Task<IEnumerable<LavalinkTrack>> DecodeTracksAsync(List<string> trackStrings)
- {
- var decodeTracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.DECODE_TRACKS}");
- return this.InternalDecodeTracksAsync(decodeTracksUri, trackStrings.ToArray());
- }
+ /// <summary>
+ /// Loads tracks from a local file.
+ /// </summary>
+ /// <param name="file">File to load tracks from.</param>
+ /// <returns>A collection of tracks from the file.</returns>
+ public Task<LavalinkLoadResult> GetTracksAsync(FileInfo file)
+ {
+ var str = WebUtility.UrlEncode(file.FullName);
+ var tracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.LOAD_TRACKS}?identifier={str}");
+ return this.InternalResolveTracksAsync(tracksUri);
+ }
- #endregion
+ /// <summary>
+ /// Decodes a base64 track string into a Lavalink track object.
+ /// </summary>
+ /// <param name="trackString">The base64 track string.</param>
+ /// <returns></returns>
+ public Task<LavalinkTrack> DecodeTrackAsync(string trackString)
+ {
+ var str = WebUtility.UrlEncode(trackString);
+ var decodeTrackUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.DECODE_TRACK}?track={str}");
+ return this.InternalDecodeTrackAsync(decodeTrackUri);
+ }
- #region Route_Planner
+ /// <summary>
+ /// Decodes an array of base64 track strings into Lavalink track objects.
+ /// </summary>
+ /// <param name="trackStrings">The array of base64 track strings.</param>
+ /// <returns></returns>
+ public Task<IEnumerable<LavalinkTrack>> DecodeTracksAsync(string[] trackStrings)
+ {
+ var decodeTracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.DECODE_TRACKS}");
+ return this.InternalDecodeTracksAsync(decodeTracksUri, trackStrings);
+ }
- /// <summary>
- /// Retrieves statistics from the route planner.
- /// </summary>
- /// <returns>The status (<see cref="DisCatSharp.Lavalink.Entities.LavalinkRouteStatus"/>) details.</returns>
- public Task<LavalinkRouteStatus> GetRoutePlannerStatusAsync()
- {
- var routeStatusUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.ROUTE_PLANNER}{Endpoints.STATUS}");
- return this.InternalGetRoutePlannerStatusAsync(routeStatusUri);
- }
+ /// <summary>
+ /// Decodes a list of base64 track strings into Lavalink track objects.
+ /// </summary>
+ /// <param name="trackStrings">The list of base64 track strings.</param>
+ /// <returns></returns>
+ public Task<IEnumerable<LavalinkTrack>> DecodeTracksAsync(List<string> trackStrings)
+ {
+ var decodeTracksUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.DECODE_TRACKS}");
+ return this.InternalDecodeTracksAsync(decodeTracksUri, trackStrings.ToArray());
+ }
- /// <summary>
- /// Unmarks a failed route planner IP Address.
- /// </summary>
- /// <param name="address">The IP address name to unmark.</param>
- /// <returns></returns>
- public Task FreeAddressAsync(string address)
- {
- var routeFreeAddressUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.ROUTE_PLANNER}{Endpoints.FREE_ADDRESS}");
- return this.InternalFreeAddressAsync(routeFreeAddressUri, address);
- }
+ #endregion
- /// <summary>
- /// Unmarks all failed route planner IP Addresses.
- /// </summary>
- /// <returns></returns>
- public Task FreeAllAddressesAsync()
- {
- var routeFreeAllAddressesUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.ROUTE_PLANNER}{Endpoints.FREE_ALL}");
- return this.InternalFreeAllAddressesAsync(routeFreeAllAddressesUri);
- }
+ #region Route_Planner
- #endregion
+ /// <summary>
+ /// Retrieves statistics from the route planner.
+ /// </summary>
+ /// <returns>The status (<see cref="DisCatSharp.Lavalink.Entities.LavalinkRouteStatus"/>) details.</returns>
+ public Task<LavalinkRouteStatus> GetRoutePlannerStatusAsync()
+ {
+ var routeStatusUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.ROUTE_PLANNER}{Endpoints.STATUS}");
+ return this.InternalGetRoutePlannerStatusAsync(routeStatusUri);
+ }
- /// <summary>
- /// get version async.
- /// </summary>
- /// <param name="uri">The uri.</param>
- /// <returns>A Task.</returns>
- internal async Task<string> InternalGetVersionAsync(Uri uri)
- {
- using var req = await this._http.GetAsync(uri).ConfigureAwait(false);
- using var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false);
- using var sr = new StreamReader(res, Utilities.UTF8);
- var json = await sr.ReadToEndAsync().ConfigureAwait(false);
- return json;
- }
+ /// <summary>
+ /// Unmarks a failed route planner IP Address.
+ /// </summary>
+ /// <param name="address">The IP address name to unmark.</param>
+ /// <returns></returns>
+ public Task FreeAddressAsync(string address)
+ {
+ var routeFreeAddressUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.ROUTE_PLANNER}{Endpoints.FREE_ADDRESS}");
+ return this.InternalFreeAddressAsync(routeFreeAddressUri, address);
+ }
- #region Internal_Track_Loading
+ /// <summary>
+ /// Unmarks all failed route planner IP Addresses.
+ /// </summary>
+ /// <returns></returns>
+ public Task FreeAllAddressesAsync()
+ {
+ var routeFreeAllAddressesUri = new Uri($"{this.RestEndpoint.ToHttpString()}{Endpoints.ROUTE_PLANNER}{Endpoints.FREE_ALL}");
+ return this.InternalFreeAllAddressesAsync(routeFreeAllAddressesUri);
+ }
- /// <summary>
- /// resolve tracks async.
- /// </summary>
- /// <param name="uri">The uri.</param>
- /// <returns>A Task.</returns>
- internal async Task<LavalinkLoadResult> InternalResolveTracksAsync(Uri uri)
- {
- // this function returns a Lavalink 3-like dataset regardless of input data version
+ #endregion
- var json = "[]";
- using (var req = await this._http.GetAsync(uri).ConfigureAwait(false))
- using (var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false))
- using (var sr = new StreamReader(res, Utilities.UTF8))
- json = await sr.ReadToEndAsync().ConfigureAwait(false);
+ /// <summary>
+ /// get version async.
+ /// </summary>
+ /// <param name="uri">The uri.</param>
+ /// <returns>A Task.</returns>
+ internal async Task<string> InternalGetVersionAsync(Uri uri)
+ {
+ using var req = await this._http.GetAsync(uri).ConfigureAwait(false);
+ using var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ using var sr = new StreamReader(res, Utilities.UTF8);
+ var json = await sr.ReadToEndAsync().ConfigureAwait(false);
+ return json;
+ }
- var jdata = JToken.Parse(json);
- if (jdata is JArray jarr)
+ #region Internal_Track_Loading
+
+ /// <summary>
+ /// resolve tracks async.
+ /// </summary>
+ /// <param name="uri">The uri.</param>
+ /// <returns>A Task.</returns>
+ internal async Task<LavalinkLoadResult> InternalResolveTracksAsync(Uri uri)
{
- // Lavalink 2.x
+ // this function returns a Lavalink 3-like dataset regardless of input data version
- var tracks = new List<LavalinkTrack>(jarr.Count);
- foreach (var jt in jarr)
- {
- var track = jt["info"].ToObject<LavalinkTrack>();
- track.TrackString = jt["track"].ToString();
+ var json = "[]";
+ using (var req = await this._http.GetAsync(uri).ConfigureAwait(false))
+ using (var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false))
+ using (var sr = new StreamReader(res, Utilities.UTF8))
+ json = await sr.ReadToEndAsync().ConfigureAwait(false);
- tracks.Add(track);
+ var jdata = JToken.Parse(json);
+ if (jdata is JArray jarr)
+ {
+ // Lavalink 2.x
+
+ var tracks = new List<LavalinkTrack>(jarr.Count);
+ foreach (var jt in jarr)
+ {
+ var track = jt["info"].ToObject<LavalinkTrack>();
+ track.TrackString = jt["track"].ToString();
+
+ tracks.Add(track);
+ }
+
+ return new LavalinkLoadResult
+ {
+ PlaylistInfo = default,
+ LoadResultType = tracks.Count == 0 ? LavalinkLoadResultType.LoadFailed : LavalinkLoadResultType.TrackLoaded,
+ Tracks = tracks
+ };
}
-
- return new LavalinkLoadResult
+ else if (jdata is JObject jo)
{
- PlaylistInfo = default,
- LoadResultType = tracks.Count == 0 ? LavalinkLoadResultType.LoadFailed : LavalinkLoadResultType.TrackLoaded,
- Tracks = tracks
- };
- }
- else if (jdata is JObject jo)
- {
- // Lavalink 3.x
+ // Lavalink 3.x
- jarr = jo["tracks"] as JArray;
- var loadInfo = jo.ToObject<LavalinkLoadResult>();
- var tracks = new List<LavalinkTrack>(jarr.Count);
- foreach (var jt in jarr)
- {
- var track = jt["info"].ToObject<LavalinkTrack>();
- track.TrackString = jt["track"].ToString();
+ jarr = jo["tracks"] as JArray;
+ var loadInfo = jo.ToObject<LavalinkLoadResult>();
+ var tracks = new List<LavalinkTrack>(jarr.Count);
+ foreach (var jt in jarr)
+ {
+ var track = jt["info"].ToObject<LavalinkTrack>();
+ track.TrackString = jt["track"].ToString();
- tracks.Add(track);
- }
+ tracks.Add(track);
+ }
- loadInfo.Tracks = new ReadOnlyCollection<LavalinkTrack>(tracks);
+ loadInfo.Tracks = new ReadOnlyCollection<LavalinkTrack>(tracks);
- return loadInfo;
+ return loadInfo;
+ }
+ else
+ return null;
}
- else
- return null;
- }
- /// <summary>
- /// decode track async.
- /// </summary>
- /// <param name="uri">The uri.</param>
- /// <returns>A Task.</returns>
- internal async Task<LavalinkTrack> InternalDecodeTrackAsync(Uri uri)
- {
- using var req = await this._http.GetAsync(uri).ConfigureAwait(false);
- using var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false);
- using var sr = new StreamReader(res, Utilities.UTF8);
- var json = await sr.ReadToEndAsync().ConfigureAwait(false);
- if (!req.IsSuccessStatusCode)
+ /// <summary>
+ /// decode track async.
+ /// </summary>
+ /// <param name="uri">The uri.</param>
+ /// <returns>A Task.</returns>
+ internal async Task<LavalinkTrack> InternalDecodeTrackAsync(Uri uri)
{
- var jsonError = JObject.Parse(json);
- this._logger?.LogError(LavalinkEvents.LavalinkDecodeError, "Unable to decode track strings: {0}", jsonError["message"]);
+ using var req = await this._http.GetAsync(uri).ConfigureAwait(false);
+ using var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ using var sr = new StreamReader(res, Utilities.UTF8);
+ var json = await sr.ReadToEndAsync().ConfigureAwait(false);
+ if (!req.IsSuccessStatusCode)
+ {
+ var jsonError = JObject.Parse(json);
+ this._logger?.LogError(LavalinkEvents.LavalinkDecodeError, "Unable to decode track strings: {0}", jsonError["message"]);
- return null;
+ return null;
+ }
+ var track = JsonConvert.DeserializeObject<LavalinkTrack>(json);
+ return track;
}
- var track = JsonConvert.DeserializeObject<LavalinkTrack>(json);
- return track;
- }
- /// <summary>
- /// decode tracks async.
- /// </summary>
- /// <param name="uri">The uri.</param>
- /// <param name="ids">The ids.</param>
- /// <returns>A Task.</returns>
- internal async Task<IEnumerable<LavalinkTrack>> InternalDecodeTracksAsync(Uri uri, string[] ids)
- {
- var jsonOut = JsonConvert.SerializeObject(ids);
- var content = new StringContent(jsonOut, Utilities.UTF8, "application/json");
- using var req = await this._http.PostAsync(uri, content).ConfigureAwait(false);
- using var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false);
- using var sr = new StreamReader(res, Utilities.UTF8);
- var jsonIn = await sr.ReadToEndAsync().ConfigureAwait(false);
- if (!req.IsSuccessStatusCode)
+ /// <summary>
+ /// decode tracks async.
+ /// </summary>
+ /// <param name="uri">The uri.</param>
+ /// <param name="ids">The ids.</param>
+ /// <returns>A Task.</returns>
+ internal async Task<IEnumerable<LavalinkTrack>> InternalDecodeTracksAsync(Uri uri, string[] ids)
{
- var jsonError = JObject.Parse(jsonIn);
- this._logger?.LogError(LavalinkEvents.LavalinkDecodeError, "Unable to decode track strings", jsonError["message"]);
- return null;
- }
+ var jsonOut = JsonConvert.SerializeObject(ids);
+ var content = new StringContent(jsonOut, Utilities.UTF8, "application/json");
+ using var req = await this._http.PostAsync(uri, content).ConfigureAwait(false);
+ using var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ using var sr = new StreamReader(res, Utilities.UTF8);
+ var jsonIn = await sr.ReadToEndAsync().ConfigureAwait(false);
+ if (!req.IsSuccessStatusCode)
+ {
+ var jsonError = JObject.Parse(jsonIn);
+ this._logger?.LogError(LavalinkEvents.LavalinkDecodeError, "Unable to decode track strings", jsonError["message"]);
+ return null;
+ }
- var jarr = JToken.Parse(jsonIn) as JArray;
- var decodedTracks = new LavalinkTrack[jarr.Count];
+ var jarr = JToken.Parse(jsonIn) as JArray;
+ var decodedTracks = new LavalinkTrack[jarr.Count];
- for (var i = 0; i < decodedTracks.Length; i++)
- {
- decodedTracks[i] = JsonConvert.DeserializeObject<LavalinkTrack>(jarr[i]["info"].ToString());
- decodedTracks[i].TrackString = jarr[i]["track"].ToString();
- }
+ for (var i = 0; i < decodedTracks.Length; i++)
+ {
+ decodedTracks[i] = JsonConvert.DeserializeObject<LavalinkTrack>(jarr[i]["info"].ToString());
+ decodedTracks[i].TrackString = jarr[i]["track"].ToString();
+ }
- var decodedTrackList = new ReadOnlyCollection<LavalinkTrack>(decodedTracks);
+ var decodedTrackList = new ReadOnlyCollection<LavalinkTrack>(decodedTracks);
- return decodedTrackList;
- }
+ return decodedTrackList;
+ }
- #endregion
+ #endregion
- #region Internal_Route_Planner
+ #region Internal_Route_Planner
- /// <summary>
- /// get route planner status async.
- /// </summary>
- /// <param name="uri">The uri.</param>
- /// <returns>A Task.</returns>
- internal async Task<LavalinkRouteStatus> InternalGetRoutePlannerStatusAsync(Uri uri)
- {
- using var req = await this._http.GetAsync(uri).ConfigureAwait(false);
- using var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false);
- using var sr = new StreamReader(res, Utilities.UTF8);
- var json = await sr.ReadToEndAsync().ConfigureAwait(false);
- var status = JsonConvert.DeserializeObject<LavalinkRouteStatus>(json);
- return status;
- }
+ /// <summary>
+ /// get route planner status async.
+ /// </summary>
+ /// <param name="uri">The uri.</param>
+ /// <returns>A Task.</returns>
+ internal async Task<LavalinkRouteStatus> InternalGetRoutePlannerStatusAsync(Uri uri)
+ {
+ using var req = await this._http.GetAsync(uri).ConfigureAwait(false);
+ using var res = await req.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ using var sr = new StreamReader(res, Utilities.UTF8);
+ var json = await sr.ReadToEndAsync().ConfigureAwait(false);
+ var status = JsonConvert.DeserializeObject<LavalinkRouteStatus>(json);
+ return status;
+ }
- /// <summary>
- /// free address async.
- /// </summary>
- /// <param name="uri">The uri.</param>
- /// <param name="address">The address.</param>
- /// <returns>A Task.</returns>
- internal async Task InternalFreeAddressAsync(Uri uri, string address)
- {
- var payload = new StringContent(address, Utilities.UTF8, "application/json");
- using var req = await this._http.PostAsync(uri, payload).ConfigureAwait(false);
- if (req.StatusCode == HttpStatusCode.InternalServerError)
- this._logger?.LogWarning(LavalinkEvents.LavalinkRestError, "Request to {0} returned an internal server error - your server route planner configuration is likely incorrect", uri);
+ /// <summary>
+ /// free address async.
+ /// </summary>
+ /// <param name="uri">The uri.</param>
+ /// <param name="address">The address.</param>
+ /// <returns>A Task.</returns>
+ internal async Task InternalFreeAddressAsync(Uri uri, string address)
+ {
+ var payload = new StringContent(address, Utilities.UTF8, "application/json");
+ using var req = await this._http.PostAsync(uri, payload).ConfigureAwait(false);
+ if (req.StatusCode == HttpStatusCode.InternalServerError)
+ this._logger?.LogWarning(LavalinkEvents.LavalinkRestError, "Request to {0} returned an internal server error - your server route planner configuration is likely incorrect", uri);
- }
+ }
- /// <summary>
- /// free all addresses async.
- /// </summary>
- /// <param name="uri">The uri.</param>
- /// <returns>A Task.</returns>
- internal async Task InternalFreeAllAddressesAsync(Uri uri)
- {
- var httpReq = new HttpRequestMessage(HttpMethod.Post, uri);
- using var req = await this._http.SendAsync(httpReq).ConfigureAwait(false);
- if (req.StatusCode == HttpStatusCode.InternalServerError)
- this._logger?.LogWarning(LavalinkEvents.LavalinkRestError, "Request to {0} returned an internal server error - your server route planner configuration is likely incorrect", uri);
- }
+ /// <summary>
+ /// free all addresses async.
+ /// </summary>
+ /// <param name="uri">The uri.</param>
+ /// <returns>A Task.</returns>
+ internal async Task InternalFreeAllAddressesAsync(Uri uri)
+ {
+ var httpReq = new HttpRequestMessage(HttpMethod.Post, uri);
+ using var req = await this._http.SendAsync(httpReq).ConfigureAwait(false);
+ if (req.StatusCode == HttpStatusCode.InternalServerError)
+ this._logger?.LogWarning(LavalinkEvents.LavalinkRestError, "Request to {0} returned an internal server error - your server route planner configuration is likely incorrect", uri);
+ }
- #endregion
+ #endregion
- /// <summary>
- /// Configures the http handling.
- /// </summary>
- /// <param name="password">The password.</param>
- /// <param name="client">The client.</param>
- private void ConfigureHttpHandling(string password, BaseDiscordClient client = null)
- {
- var httphandler = new HttpClientHandler
+ /// <summary>
+ /// Configures the http handling.
+ /// </summary>
+ /// <param name="password">The password.</param>
+ /// <param name="client">The client.</param>
+ private void ConfigureHttpHandling(string password, BaseDiscordClient client = null)
{
- UseCookies = false,
- AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
- UseProxy = client != null && client.Configuration.Proxy != null
- };
- if (httphandler.UseProxy) // because mono doesn't implement this properly
- httphandler.Proxy = client.Configuration.Proxy;
-
- this._http = new HttpClient(httphandler);
-
- this._http.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", $"DisCatSharp.LavaLink/{this._dcsVersionString}");
- this._http.DefaultRequestHeaders.TryAddWithoutValidation("Client-Name", $"DisCatSharp");
- this._http.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", password);
+ var httphandler = new HttpClientHandler
+ {
+ UseCookies = false,
+ AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
+ UseProxy = client != null && client.Configuration.Proxy != null
+ };
+ if (httphandler.UseProxy) // because mono doesn't implement this properly
+ httphandler.Proxy = client.Configuration.Proxy;
+
+ this._http = new HttpClient(httphandler);
+
+ this._http.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", $"DisCatSharp.LavaLink/{this._dcsVersionString}");
+ this._http.DefaultRequestHeaders.TryAddWithoutValidation("Client-Name", $"DisCatSharp");
+ this._http.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", password);
+ }
}
}
diff --git a/DisCatSharp.Lavalink/LavalinkRestEndpoints.cs b/DisCatSharp.Lavalink/LavalinkRestEndpoints.cs
index 0a469be58..b8147fa93 100644
--- a/DisCatSharp.Lavalink/LavalinkRestEndpoints.cs
+++ b/DisCatSharp.Lavalink/LavalinkRestEndpoints.cs
@@ -1,66 +1,67 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// Represents the lavalink endpoints.
-/// </summary>
-internal static class Endpoints
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// The version endpoint.
+ /// Represents the lavalink endpoints.
/// </summary>
- internal const string VERSION = "/version";
+ internal static class Endpoints
+ {
+ /// <summary>
+ /// The version endpoint.
+ /// </summary>
+ internal const string VERSION = "/version";
- //Track loading
- /// <summary>
- /// The load tracks endpoint.
- /// </summary>
- internal const string LOAD_TRACKS = "/loadtracks";
- /// <summary>
- /// The decode track endpoint.
- /// </summary>
- internal const string DECODE_TRACK = "/decodetrack";
- /// <summary>
- /// The decode tracks endpoint.
- /// </summary>
- internal const string DECODE_TRACKS = "/decodetracks";
+ //Track loading
+ /// <summary>
+ /// The load tracks endpoint.
+ /// </summary>
+ internal const string LOAD_TRACKS = "/loadtracks";
+ /// <summary>
+ /// The decode track endpoint.
+ /// </summary>
+ internal const string DECODE_TRACK = "/decodetrack";
+ /// <summary>
+ /// The decode tracks endpoint.
+ /// </summary>
+ internal const string DECODE_TRACKS = "/decodetracks";
- //Route Planner
- /// <summary>
- /// The route planner endpoint.
- /// </summary>
- internal const string ROUTE_PLANNER = "/routeplanner";
- /// <summary>
- /// The status endpoint.
- /// </summary>
- internal const string STATUS = "/status";
- /// <summary>
- /// The free address endpoint.
- /// </summary>
- internal const string FREE_ADDRESS = "/free/address";
- /// <summary>
- /// The free all endpoint.
- /// </summary>
- internal const string FREE_ALL = "/free/all";
+ //Route Planner
+ /// <summary>
+ /// The route planner endpoint.
+ /// </summary>
+ internal const string ROUTE_PLANNER = "/routeplanner";
+ /// <summary>
+ /// The status endpoint.
+ /// </summary>
+ internal const string STATUS = "/status";
+ /// <summary>
+ /// The free address endpoint.
+ /// </summary>
+ internal const string FREE_ADDRESS = "/free/address";
+ /// <summary>
+ /// The free all endpoint.
+ /// </summary>
+ internal const string FREE_ALL = "/free/all";
+ }
}
diff --git a/DisCatSharp.Lavalink/LavalinkTrack.cs b/DisCatSharp.Lavalink/LavalinkTrack.cs
index fd1cc311c..f17384e4b 100644
--- a/DisCatSharp.Lavalink/LavalinkTrack.cs
+++ b/DisCatSharp.Lavalink/LavalinkTrack.cs
@@ -1,225 +1,226 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
#pragma warning disable 0649
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// Represents a lavalink track.
-/// </summary>
-public class LavalinkTrack
-{
- /// <summary>
- /// Gets or sets the ID of the track to play.
- /// </summary>
- [JsonIgnore]
- public string TrackString { get; set; }
-
- /// <summary>
- /// Gets the identifier of the track.
- /// </summary>
- [JsonProperty("identifier")]
- public string Identifier { get; internal set; }
-
- /// <summary>
- /// Gets whether the track is seekable.
- /// </summary>
- [JsonProperty("isSeekable")]
- public bool IsSeekable { get; internal set; }
-
- /// <summary>
- /// Gets the author of the track.
- /// </summary>
- [JsonProperty("author")]
- public string Author { get; internal set; }
-
- /// <summary>
- /// Gets the track's duration.
- /// </summary>
- [JsonIgnore]
- public TimeSpan Length => !this.IsStream ? TimeSpan.FromMilliseconds(this.LengthInternal) : TimeSpan.Zero;
- [JsonProperty("length")]
- internal long LengthInternal;
-
- /// <summary>
- /// Gets whether the track is a stream.
- /// </summary>
- [JsonProperty("isStream")]
- public bool IsStream { get; internal set; }
-
- /// <summary>
- /// Gets the starting position of the track.
- /// </summary>
- [JsonIgnore]
- public TimeSpan Position => TimeSpan.FromMilliseconds(this.PositionInternal);
- [JsonProperty("position")]
- internal long PositionInternal;
-
- /// <summary>
- /// Gets the title of the track.
- /// </summary>
- [JsonProperty("title")]
- public string Title { get; internal set; }
-
- /// <summary>
- /// Gets the source Uri of this track.
- /// </summary>
- [JsonProperty("uri")]
- public Uri Uri { get; internal set; }
-}
-
-/// <summary>
-/// Represents Lavalink track loading results.
-/// </summary>
-[JsonConverter(typeof(StringEnumConverter))]
-public enum LavalinkLoadResultType
-{
- /// <summary>
- /// Specifies that track was loaded successfully.
- /// </summary>
- [EnumMember(Value = "TRACK_LOADED")]
- TrackLoaded,
-
- /// <summary>
- /// Specifies that playlist was loaded successfully.
- /// </summary>
- [EnumMember(Value = "PLAYLIST_LOADED")]
- PlaylistLoaded,
-
- /// <summary>
- /// Specifies that the result set contains search results.
- /// </summary>
- [EnumMember(Value = "SEARCH_RESULT")]
- SearchResult,
-
- /// <summary>
- /// Specifies that the search yielded no results.
- /// </summary>
- [EnumMember(Value = "NO_MATCHES")]
- NoMatches,
-
- /// <summary>
- /// Specifies that the track failed to load.
- /// </summary>
- [EnumMember(Value = "LOAD_FAILED")]
- LoadFailed
-}
-
-/// <summary>
-/// Represents information about playlist that was loaded by Lavalink.
-/// </summary>
-public struct LavalinkPlaylistInfo
-{
- /// <summary>
- /// Gets the name of the playlist being loaded.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the index of the track that was selected in this playlist.
- /// </summary>
- [JsonProperty("selectedTrack")]
- public int SelectedTrack { get; internal set; }
-}
-
-/// <summary>
-/// Represents information about track loading request.
-/// </summary>
-public class LavalinkLoadResult
-{
- /// <summary>
- /// Gets the loading result type for this request.
- /// </summary>
- [JsonProperty("loadType")]
- public LavalinkLoadResultType LoadResultType { get; internal set; }
-
- /// <summary>
- /// <para>Gets the information about the playlist loaded as a result of this request.</para>
- /// <para>Only applicable if <see cref="LoadResultType"/> is set to <see cref="LavalinkLoadResultType.PlaylistLoaded"/>.</para>
- /// </summary>
- [JsonProperty("playlistInfo")]
- public LavalinkPlaylistInfo PlaylistInfo { get; internal set; }
-
- /// <summary>
- /// Gets the exception details if a track loading failed.
- /// </summary>
- [JsonProperty("exception", NullValueHandling = NullValueHandling.Ignore)]
- public LavalinkLoadFailedInfo Exception { get; internal set; }
-
- /// <summary>
- /// Gets the tracks that were loaded as a result of this request.
- /// </summary>
- //[JsonProperty("tracks")]
- [JsonIgnore]
- public IEnumerable<LavalinkTrack> Tracks { get; internal set; }
-}
-
-/// <summary>
-/// Represents properties sent when a Lavalink track is unable to load.
-/// </summary>
-public struct LavalinkLoadFailedInfo
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// Gets the message of the sent exception.
- /// </summary>
- [JsonProperty("message")]
- public string Message { get; internal set; }
-
- /// <summary>
- /// Gets the severity level of the track loading failure.
- /// </summary>
- [JsonProperty("severity")]
- public LoadFailedSeverity Severity { get; internal set; }
-}
-
-/// <summary>
-/// Represents severity level of the track loading failure.
-/// </summary>
-public enum LoadFailedSeverity
-{
- /// <summary>
- /// Indicates a known cause for the failure, and not because of Lavaplayer.
- /// </summary>
- [EnumMember(Value = "COMMON")]
- Common,
-
- /// <summary>
- /// Indicates an unknown cause for the failure, most likely caused by outside sources.
- /// </summary>
- [EnumMember(Value = "SUSPICIOUS")]
- Suspicious,
-
- /// <summary>
- /// Indicates an issue with Lavaplayer or otherwise no other way to determine the cause.
- /// </summary>
- [EnumMember(Value = "FAULT")]
- Fault
+ /// Represents a lavalink track.
+ /// </summary>
+ public class LavalinkTrack
+ {
+ /// <summary>
+ /// Gets or sets the ID of the track to play.
+ /// </summary>
+ [JsonIgnore]
+ public string TrackString { get; set; }
+
+ /// <summary>
+ /// Gets the identifier of the track.
+ /// </summary>
+ [JsonProperty("identifier")]
+ public string Identifier { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the track is seekable.
+ /// </summary>
+ [JsonProperty("isSeekable")]
+ public bool IsSeekable { get; internal set; }
+
+ /// <summary>
+ /// Gets the author of the track.
+ /// </summary>
+ [JsonProperty("author")]
+ public string Author { get; internal set; }
+
+ /// <summary>
+ /// Gets the track's duration.
+ /// </summary>
+ [JsonIgnore]
+ public TimeSpan Length => !this.IsStream ? TimeSpan.FromMilliseconds(this.LengthInternal) : TimeSpan.Zero;
+ [JsonProperty("length")]
+ internal long LengthInternal;
+
+ /// <summary>
+ /// Gets whether the track is a stream.
+ /// </summary>
+ [JsonProperty("isStream")]
+ public bool IsStream { get; internal set; }
+
+ /// <summary>
+ /// Gets the starting position of the track.
+ /// </summary>
+ [JsonIgnore]
+ public TimeSpan Position => TimeSpan.FromMilliseconds(this.PositionInternal);
+ [JsonProperty("position")]
+ internal long PositionInternal;
+
+ /// <summary>
+ /// Gets the title of the track.
+ /// </summary>
+ [JsonProperty("title")]
+ public string Title { get; internal set; }
+
+ /// <summary>
+ /// Gets the source Uri of this track.
+ /// </summary>
+ [JsonProperty("uri")]
+ public Uri Uri { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents Lavalink track loading results.
+ /// </summary>
+ [JsonConverter(typeof(StringEnumConverter))]
+ public enum LavalinkLoadResultType
+ {
+ /// <summary>
+ /// Specifies that track was loaded successfully.
+ /// </summary>
+ [EnumMember(Value = "TRACK_LOADED")]
+ TrackLoaded,
+
+ /// <summary>
+ /// Specifies that playlist was loaded successfully.
+ /// </summary>
+ [EnumMember(Value = "PLAYLIST_LOADED")]
+ PlaylistLoaded,
+
+ /// <summary>
+ /// Specifies that the result set contains search results.
+ /// </summary>
+ [EnumMember(Value = "SEARCH_RESULT")]
+ SearchResult,
+
+ /// <summary>
+ /// Specifies that the search yielded no results.
+ /// </summary>
+ [EnumMember(Value = "NO_MATCHES")]
+ NoMatches,
+
+ /// <summary>
+ /// Specifies that the track failed to load.
+ /// </summary>
+ [EnumMember(Value = "LOAD_FAILED")]
+ LoadFailed
+ }
+
+ /// <summary>
+ /// Represents information about playlist that was loaded by Lavalink.
+ /// </summary>
+ public struct LavalinkPlaylistInfo
+ {
+ /// <summary>
+ /// Gets the name of the playlist being loaded.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the index of the track that was selected in this playlist.
+ /// </summary>
+ [JsonProperty("selectedTrack")]
+ public int SelectedTrack { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents information about track loading request.
+ /// </summary>
+ public class LavalinkLoadResult
+ {
+ /// <summary>
+ /// Gets the loading result type for this request.
+ /// </summary>
+ [JsonProperty("loadType")]
+ public LavalinkLoadResultType LoadResultType { get; internal set; }
+
+ /// <summary>
+ /// <para>Gets the information about the playlist loaded as a result of this request.</para>
+ /// <para>Only applicable if <see cref="LoadResultType"/> is set to <see cref="LavalinkLoadResultType.PlaylistLoaded"/>.</para>
+ /// </summary>
+ [JsonProperty("playlistInfo")]
+ public LavalinkPlaylistInfo PlaylistInfo { get; internal set; }
+
+ /// <summary>
+ /// Gets the exception details if a track loading failed.
+ /// </summary>
+ [JsonProperty("exception", NullValueHandling = NullValueHandling.Ignore)]
+ public LavalinkLoadFailedInfo Exception { get; internal set; }
+
+ /// <summary>
+ /// Gets the tracks that were loaded as a result of this request.
+ /// </summary>
+ //[JsonProperty("tracks")]
+ [JsonIgnore]
+ public IEnumerable<LavalinkTrack> Tracks { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents properties sent when a Lavalink track is unable to load.
+ /// </summary>
+ public struct LavalinkLoadFailedInfo
+ {
+ /// <summary>
+ /// Gets the message of the sent exception.
+ /// </summary>
+ [JsonProperty("message")]
+ public string Message { get; internal set; }
+
+ /// <summary>
+ /// Gets the severity level of the track loading failure.
+ /// </summary>
+ [JsonProperty("severity")]
+ public LoadFailedSeverity Severity { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents severity level of the track loading failure.
+ /// </summary>
+ public enum LoadFailedSeverity
+ {
+ /// <summary>
+ /// Indicates a known cause for the failure, and not because of Lavaplayer.
+ /// </summary>
+ [EnumMember(Value = "COMMON")]
+ Common,
+
+ /// <summary>
+ /// Indicates an unknown cause for the failure, most likely caused by outside sources.
+ /// </summary>
+ [EnumMember(Value = "SUSPICIOUS")]
+ Suspicious,
+
+ /// <summary>
+ /// Indicates an issue with Lavaplayer or otherwise no other way to determine the cause.
+ /// </summary>
+ [EnumMember(Value = "FAULT")]
+ Fault
+ }
}
diff --git a/DisCatSharp.Lavalink/LavalinkUtil.cs b/DisCatSharp.Lavalink/LavalinkUtil.cs
index 479c74ddf..27108708e 100644
--- a/DisCatSharp.Lavalink/LavalinkUtil.cs
+++ b/DisCatSharp.Lavalink/LavalinkUtil.cs
@@ -1,285 +1,286 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using System.Text;
using DisCatSharp.Lavalink.EventArgs;
-namespace DisCatSharp.Lavalink;
-
-/// <summary>
-/// Various utilities for Lavalink.
-/// </summary>
-public static class LavalinkUtilities
+namespace DisCatSharp.Lavalink
{
/// <summary>
- /// Indicates whether a new track should be started after receiving this TrackEndReason. If this is false, either this event is
- /// already triggered because another track started (REPLACED) or because the player is stopped (STOPPED, CLEANUP).
+ /// Various utilities for Lavalink.
/// </summary>
- public static bool MayStartNext(this TrackEndReason reason)
- => reason == TrackEndReason.Finished || reason == TrackEndReason.LoadFailed;
-
- /// <summary>
- /// Decodes a Lavalink track string.
- /// </summary>
- /// <param name="track">Track string to decode.</param>
- /// <returns>Decoded Lavalink track.</returns>
- public static LavalinkTrack DecodeTrack(string track)
+ public static class LavalinkUtilities
{
- // https://github.com/sedmelluq/lavaplayer/blob/804cd1038229230052d9b1dee5e6d1741e30e284/main/src/main/java/com/sedmelluq/discord/lavaplayer/player/DefaultAudioPlayerManager.java#L63-L64
- const int TRACK_INFO_VERSIONED = 1;
- //const int TRACK_INFO_VERSION = 2;
-
- var raw = Convert.FromBase64String(track);
-
- var decoded = new LavalinkTrack
+ /// <summary>
+ /// Indicates whether a new track should be started after receiving this TrackEndReason. If this is false, either this event is
+ /// already triggered because another track started (REPLACED) or because the player is stopped (STOPPED, CLEANUP).
+ /// </summary>
+ public static bool MayStartNext(this TrackEndReason reason)
+ => reason == TrackEndReason.Finished || reason == TrackEndReason.LoadFailed;
+
+ /// <summary>
+ /// Decodes a Lavalink track string.
+ /// </summary>
+ /// <param name="track">Track string to decode.</param>
+ /// <returns>Decoded Lavalink track.</returns>
+ public static LavalinkTrack DecodeTrack(string track)
{
- TrackString = track
- };
+ // https://github.com/sedmelluq/lavaplayer/blob/804cd1038229230052d9b1dee5e6d1741e30e284/main/src/main/java/com/sedmelluq/discord/lavaplayer/player/DefaultAudioPlayerManager.java#L63-L64
+ const int TRACK_INFO_VERSIONED = 1;
+ //const int TRACK_INFO_VERSION = 2;
- using (var ms = new MemoryStream(raw))
- using (var br = new JavaBinaryReader(ms))
- {
- // https://github.com/sedmelluq/lavaplayer/blob/b0c536098c4f92e6d03b00f19221021f8f50b19b/main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/MessageInput.java#L37-L39
- var messageHeader = br.ReadInt32();
- var messageFlags = (int) ((messageHeader & 0xC0000000L) >> 30);
- var messageSize = messageHeader & 0x3FFFFFFF;
- //if (messageSize != raw.Length)
- // Warn($"Size conflict: {messageSize} but was {raw.Length}");
+ var raw = Convert.FromBase64String(track);
- // https://github.com/sedmelluq/lavaplayer/blob/804cd1038229230052d9b1dee5e6d1741e30e284/main/src/main/java/com/sedmelluq/discord/lavaplayer/player/DefaultAudioPlayerManager.java#L268
+ var decoded = new LavalinkTrack
+ {
+ TrackString = track
+ };
- // java bytes are signed
- // https://docs.oracle.com/javase/7/docs/api/java/io/DataInput.html#readByte()
- var version = (messageFlags & TRACK_INFO_VERSIONED) != 0 ? br.ReadSByte() & 0xFF : 1;
- //if (version != TRACK_INFO_VERSION)
- // Warn($"Version conflict: Expected {TRACK_INFO_VERSION} but got {version}");
+ using (var ms = new MemoryStream(raw))
+ using (var br = new JavaBinaryReader(ms))
+ {
+ // https://github.com/sedmelluq/lavaplayer/blob/b0c536098c4f92e6d03b00f19221021f8f50b19b/main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/io/MessageInput.java#L37-L39
+ var messageHeader = br.ReadInt32();
+ var messageFlags = (int) ((messageHeader & 0xC0000000L) >> 30);
+ var messageSize = messageHeader & 0x3FFFFFFF;
+ //if (messageSize != raw.Length)
+ // Warn($"Size conflict: {messageSize} but was {raw.Length}");
- decoded.Title = br.ReadJavaUtf8();
+ // https://github.com/sedmelluq/lavaplayer/blob/804cd1038229230052d9b1dee5e6d1741e30e284/main/src/main/java/com/sedmelluq/discord/lavaplayer/player/DefaultAudioPlayerManager.java#L268
- decoded.Author = br.ReadJavaUtf8();
+ // java bytes are signed
+ // https://docs.oracle.com/javase/7/docs/api/java/io/DataInput.html#readByte()
+ var version = (messageFlags & TRACK_INFO_VERSIONED) != 0 ? br.ReadSByte() & 0xFF : 1;
+ //if (version != TRACK_INFO_VERSION)
+ // Warn($"Version conflict: Expected {TRACK_INFO_VERSION} but got {version}");
- decoded.LengthInternal = br.ReadInt64();
+ decoded.Title = br.ReadJavaUtf8();
- decoded.Identifier = br.ReadJavaUtf8();
+ decoded.Author = br.ReadJavaUtf8();
- decoded.IsStream = br.ReadBoolean();
+ decoded.LengthInternal = br.ReadInt64();
- var uri = br.ReadNullableString();
- decoded.Uri = uri != null && version >= 2 ? new Uri(uri) : null;
- }
+ decoded.Identifier = br.ReadJavaUtf8();
- return decoded;
- }
-}
+ decoded.IsStream = br.ReadBoolean();
-/// <inheritdoc />
-/// <summary>
-/// Java's DataOutputStream always uses big-endian, while BinaryReader always uses little-endian.
-/// This class converts a big-endian stream to little-endian, and includes some helper methods
-/// for interacting with Lavaplayer/Lavalink.
-/// </summary>
-internal class JavaBinaryReader : BinaryReader
-{
- private static readonly Encoding s_utf8NoBom = new UTF8Encoding();
+ var uri = br.ReadNullableString();
+ decoded.Uri = uri != null && version >= 2 ? new Uri(uri) : null;
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="JavaBinaryReader"/> class.
- /// </summary>
- /// <param name="ms">The ms.</param>
- public JavaBinaryReader(Stream ms) : base(ms, s_utf8NoBom)
- {
+ return decoded;
+ }
}
- // https://docs.oracle.com/javase/7/docs/api/java/io/DataInput.html#readUTF()
+ /// <inheritdoc />
/// <summary>
- /// Reads the java utf8.
+ /// Java's DataOutputStream always uses big-endian, while BinaryReader always uses little-endian.
+ /// This class converts a big-endian stream to little-endian, and includes some helper methods
+ /// for interacting with Lavaplayer/Lavalink.
/// </summary>
- /// <returns>A string.</returns>
- public string ReadJavaUtf8()
+ internal class JavaBinaryReader : BinaryReader
{
- var length = this.ReadUInt16(); // string size in bytes
- var bytes = new byte[length];
- var amountRead = this.Read(bytes, 0, length);
- if (amountRead < length)
- throw new InvalidDataException("EOS unexpected");
-
- var output = new char[length];
- var strlen = 0;
+ private static readonly Encoding s_utf8NoBom = new UTF8Encoding();
- // i'm gonna blindly assume here that the javadocs had the correct endianness
-
- for (var i = 0; i < length; i++)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="JavaBinaryReader"/> class.
+ /// </summary>
+ /// <param name="ms">The ms.</param>
+ public JavaBinaryReader(Stream ms) : base(ms, s_utf8NoBom)
{
- var value1 = bytes[i];
- if ((value1 & 0b10000000) == 0) // highest bit 1 is false
- {
- output[strlen++] = (char)value1;
- continue;
- }
-
- // remember to skip one byte for every extra byte
- var value2 = bytes[++i];
- if ((value1 & 0b00100000) == 0 && // highest bit 3 is false
- (value1 & 0b11000000) != 0 && // highest bit 1 and 2 are true
- (value2 & 0b01000000) == 0 && // highest bit 2 is false
- (value2 & 0b10000000) != 0) // highest bit 1 is true
- {
- var value1Chop = (value1 & 0b00011111) << 6;
- var value2Chop = value2 & 0b00111111;
- output[strlen++] = (char)(value1Chop | value2Chop);
- continue;
- }
-
- var value3 = bytes[++i];
- if ((value1 & 0b00010000) == 0 && // highest bit 4 is false
- (value1 & 0b11100000) != 0 && // highest bit 1,2,3 are true
- (value2 & 0b01000000) == 0 && // highest bit 2 is false
- (value2 & 0b10000000) != 0 && // highest bit 1 is true
- (value3 & 0b01000000) == 0 && // highest bit 2 is false
- (value3 & 0b10000000) != 0) // highest bit 1 is true
- {
- var value1Chop = (value1 & 0b00001111) << 12;
- var value2Chop = (value2 & 0b00111111) << 6;
- var value3Chop = value3 & 0b00111111;
- output[strlen++] = (char)(value1Chop | value2Chop | value3Chop);
- continue;
- }
}
- return new string(output, 0, strlen);
- }
-
- // https://github.com/sedmelluq/lavaplayer/blob/b0c536098c4f92e6d03b00f19221021f8f50b19b/main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/DataFormatTools.java#L114-L125
- /// <summary>
- /// Reads the nullable string.
- /// </summary>
- /// <returns>A string.</returns>
- public string ReadNullableString() => this.ReadBoolean() ? this.ReadJavaUtf8() : null;
-
- // swap endianness
- /// <summary>
- /// Reads the decimal.
- /// </summary>
- /// <returns>A decimal.</returns>
- public override decimal ReadDecimal() => throw new MissingMethodException("This method does not have a Java equivalent");
-
- // from https://github.com/Zoltu/Zoltu.EndianAwareBinaryReaderWriter under CC0
- /// <summary>
- /// Reads the single.
- /// </summary>
- /// <returns>A float.</returns>
- public override float ReadSingle() => this.Read(4, BitConverter.ToSingle);
-
- /// <summary>
- /// Reads the double.
- /// </summary>
- /// <returns>A double.</returns>
- public override double ReadDouble() => this.Read(8, BitConverter.ToDouble);
+ // https://docs.oracle.com/javase/7/docs/api/java/io/DataInput.html#readUTF()
+ /// <summary>
+ /// Reads the java utf8.
+ /// </summary>
+ /// <returns>A string.</returns>
+ public string ReadJavaUtf8()
+ {
+ var length = this.ReadUInt16(); // string size in bytes
+ var bytes = new byte[length];
+ var amountRead = this.Read(bytes, 0, length);
+ if (amountRead < length)
+ throw new InvalidDataException("EOS unexpected");
- /// <summary>
- /// Reads the int16.
- /// </summary>
- /// <returns>A short.</returns>
- public override short ReadInt16() => this.Read(2, BitConverter.ToInt16);
+ var output = new char[length];
+ var strlen = 0;
- /// <summary>
- /// Reads the int32.
- /// </summary>
- /// <returns>An int.</returns>
- public override int ReadInt32() => this.Read(4, BitConverter.ToInt32);
+ // i'm gonna blindly assume here that the javadocs had the correct endianness
- /// <summary>
- /// Reads the int64.
- /// </summary>
- /// <returns>A long.</returns>
- public override long ReadInt64() => this.Read(8, BitConverter.ToInt64);
-
- /// <summary>
- /// Reads the u int16.
- /// </summary>
- /// <returns>An ushort.</returns>
- public override ushort ReadUInt16() => this.Read(2, BitConverter.ToUInt16);
-
- /// <summary>
- /// Reads the u int32.
- /// </summary>
- /// <returns>An uint.</returns>
- public override uint ReadUInt32() => this.Read(4, BitConverter.ToUInt32);
+ for (var i = 0; i < length; i++)
+ {
+ var value1 = bytes[i];
+ if ((value1 & 0b10000000) == 0) // highest bit 1 is false
+ {
+ output[strlen++] = (char)value1;
+ continue;
+ }
+
+ // remember to skip one byte for every extra byte
+ var value2 = bytes[++i];
+ if ((value1 & 0b00100000) == 0 && // highest bit 3 is false
+ (value1 & 0b11000000) != 0 && // highest bit 1 and 2 are true
+ (value2 & 0b01000000) == 0 && // highest bit 2 is false
+ (value2 & 0b10000000) != 0) // highest bit 1 is true
+ {
+ var value1Chop = (value1 & 0b00011111) << 6;
+ var value2Chop = value2 & 0b00111111;
+ output[strlen++] = (char)(value1Chop | value2Chop);
+ continue;
+ }
+
+ var value3 = bytes[++i];
+ if ((value1 & 0b00010000) == 0 && // highest bit 4 is false
+ (value1 & 0b11100000) != 0 && // highest bit 1,2,3 are true
+ (value2 & 0b01000000) == 0 && // highest bit 2 is false
+ (value2 & 0b10000000) != 0 && // highest bit 1 is true
+ (value3 & 0b01000000) == 0 && // highest bit 2 is false
+ (value3 & 0b10000000) != 0) // highest bit 1 is true
+ {
+ var value1Chop = (value1 & 0b00001111) << 12;
+ var value2Chop = (value2 & 0b00111111) << 6;
+ var value3Chop = value3 & 0b00111111;
+ output[strlen++] = (char)(value1Chop | value2Chop | value3Chop);
+ continue;
+ }
+ }
- /// <summary>
- /// Reads the u int64.
- /// </summary>
- /// <returns>An ulong.</returns>
- public override ulong ReadUInt64() => this.Read(8, BitConverter.ToUInt64);
+ return new string(output, 0, strlen);
+ }
- /// <summary>
- /// Reads the.
- /// </summary>
- /// <param name="size">The size.</param>
- /// <param name="converter">The converter.</param>
- /// <returns>A T.</returns>
- private T Read<T>(int size, Func<byte[], int, T> converter) where T : struct
- {
- //Contract.Requires(size >= 0);
- //Contract.Requires(converter != null);
+ // https://github.com/sedmelluq/lavaplayer/blob/b0c536098c4f92e6d03b00f19221021f8f50b19b/main/src/main/java/com/sedmelluq/discord/lavaplayer/tools/DataFormatTools.java#L114-L125
+ /// <summary>
+ /// Reads the nullable string.
+ /// </summary>
+ /// <returns>A string.</returns>
+ public string ReadNullableString() => this.ReadBoolean() ? this.ReadJavaUtf8() : null;
+
+ // swap endianness
+ /// <summary>
+ /// Reads the decimal.
+ /// </summary>
+ /// <returns>A decimal.</returns>
+ public override decimal ReadDecimal() => throw new MissingMethodException("This method does not have a Java equivalent");
+
+ // from https://github.com/Zoltu/Zoltu.EndianAwareBinaryReaderWriter under CC0
+ /// <summary>
+ /// Reads the single.
+ /// </summary>
+ /// <returns>A float.</returns>
+ public override float ReadSingle() => this.Read(4, BitConverter.ToSingle);
+
+ /// <summary>
+ /// Reads the double.
+ /// </summary>
+ /// <returns>A double.</returns>
+ public override double ReadDouble() => this.Read(8, BitConverter.ToDouble);
+
+ /// <summary>
+ /// Reads the int16.
+ /// </summary>
+ /// <returns>A short.</returns>
+ public override short ReadInt16() => this.Read(2, BitConverter.ToInt16);
+
+ /// <summary>
+ /// Reads the int32.
+ /// </summary>
+ /// <returns>An int.</returns>
+ public override int ReadInt32() => this.Read(4, BitConverter.ToInt32);
+
+ /// <summary>
+ /// Reads the int64.
+ /// </summary>
+ /// <returns>A long.</returns>
+ public override long ReadInt64() => this.Read(8, BitConverter.ToInt64);
+
+ /// <summary>
+ /// Reads the u int16.
+ /// </summary>
+ /// <returns>An ushort.</returns>
+ public override ushort ReadUInt16() => this.Read(2, BitConverter.ToUInt16);
+
+ /// <summary>
+ /// Reads the u int32.
+ /// </summary>
+ /// <returns>An uint.</returns>
+ public override uint ReadUInt32() => this.Read(4, BitConverter.ToUInt32);
+
+ /// <summary>
+ /// Reads the u int64.
+ /// </summary>
+ /// <returns>An ulong.</returns>
+ public override ulong ReadUInt64() => this.Read(8, BitConverter.ToUInt64);
+
+ /// <summary>
+ /// Reads the.
+ /// </summary>
+ /// <param name="size">The size.</param>
+ /// <param name="converter">The converter.</param>
+ /// <returns>A T.</returns>
+ private T Read<T>(int size, Func<byte[], int, T> converter) where T : struct
+ {
+ //Contract.Requires(size >= 0);
+ //Contract.Requires(converter != null);
- var bytes = this.GetNextBytesNativeEndian(size);
- return converter(bytes, 0);
- }
+ var bytes = this.GetNextBytesNativeEndian(size);
+ return converter(bytes, 0);
+ }
- /// <summary>
- /// Gets the next bytes native endian.
- /// </summary>
- /// <param name="count">The count.</param>
- /// <returns>An array of byte.</returns>
- private byte[] GetNextBytesNativeEndian(int count)
- {
- //Contract.Requires(count >= 0);
- //Contract.Ensures(Contract.Result<Byte[]>() != null);
- //Contract.Ensures(Contract.Result<Byte[]>().Length == count);
-
- var bytes = this.GetNextBytes(count);
- if (BitConverter.IsLittleEndian)
- Array.Reverse(bytes);
- return bytes;
- }
+ /// <summary>
+ /// Gets the next bytes native endian.
+ /// </summary>
+ /// <param name="count">The count.</param>
+ /// <returns>An array of byte.</returns>
+ private byte[] GetNextBytesNativeEndian(int count)
+ {
+ //Contract.Requires(count >= 0);
+ //Contract.Ensures(Contract.Result<Byte[]>() != null);
+ //Contract.Ensures(Contract.Result<Byte[]>().Length == count);
+
+ var bytes = this.GetNextBytes(count);
+ if (BitConverter.IsLittleEndian)
+ Array.Reverse(bytes);
+ return bytes;
+ }
- /// <summary>
- /// Gets the next bytes.
- /// </summary>
- /// <param name="count">The count.</param>
- /// <returns>An array of byte.</returns>
- private byte[] GetNextBytes(int count)
- {
- //Contract.Requires(count >= 0);
- //Contract.Ensures(Contract.Result<Byte[]>() != null);
- //Contract.Ensures(Contract.Result<Byte[]>().Length == count);
+ /// <summary>
+ /// Gets the next bytes.
+ /// </summary>
+ /// <param name="count">The count.</param>
+ /// <returns>An array of byte.</returns>
+ private byte[] GetNextBytes(int count)
+ {
+ //Contract.Requires(count >= 0);
+ //Contract.Ensures(Contract.Result<Byte[]>() != null);
+ //Contract.Ensures(Contract.Result<Byte[]>().Length == count);
- var buffer = new byte[count];
- var bytesRead = this.BaseStream.Read(buffer, 0, count);
+ var buffer = new byte[count];
+ var bytesRead = this.BaseStream.Read(buffer, 0, count);
- return bytesRead != count ? throw new EndOfStreamException() : buffer;
+ return bytesRead != count ? throw new EndOfStreamException() : buffer;
+ }
}
}
diff --git a/DisCatSharp.VoiceNext/AudioFormat.cs b/DisCatSharp.VoiceNext/AudioFormat.cs
index a2405bc26..9bba2134a 100644
--- a/DisCatSharp.VoiceNext/AudioFormat.cs
+++ b/DisCatSharp.VoiceNext/AudioFormat.cs
@@ -1,163 +1,164 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Runtime.CompilerServices;
-namespace DisCatSharp.VoiceNext;
-
-/// <summary>
-/// Defines the format of PCM data consumed or produced by Opus.
-/// </summary>
-public struct AudioFormat
+namespace DisCatSharp.VoiceNext
{
/// <summary>
- /// Gets the collection of sampling rates (in Hz) the Opus encoder can use.
- /// </summary>
- public static IReadOnlyCollection<int> AllowedSampleRates { get; } = new ReadOnlyCollection<int>(new[] { 8000, 12000, 16000, 24000, 48000 });
-
- /// <summary>
- /// Gets the collection of channel counts the Opus encoder can use.
- /// </summary>
- public static IReadOnlyCollection<int> AllowedChannelCounts { get; } = new ReadOnlyCollection<int>(new[] { 1, 2 });
-
- /// <summary>
- /// Gets the collection of sample durations (in ms) the Opus encoder can use.
- /// </summary>
- public static IReadOnlyCollection<int> AllowedSampleDurations { get; } = new ReadOnlyCollection<int>(new[] { 5, 10, 20, 40, 60 });
-
- /// <summary>
- /// Gets the default audio format. This is a format configured for 48kHz sampling rate, 2 channels, with music quality preset.
- /// </summary>
- public static AudioFormat Default { get; } = new(48000, 2, VoiceApplication.Music);
-
- /// <summary>
- /// Gets the audio sampling rate in Hz.
- /// </summary>
- public int SampleRate { get; }
-
- /// <summary>
- /// Gets the audio channel count.
- /// </summary>
- public int ChannelCount { get; }
-
- /// <summary>
- /// Gets the voice application, which dictates the quality preset.
- /// </summary>
- public VoiceApplication VoiceApplication { get; }
-
- /// <summary>
- /// Creates a new audio format for use with Opus encoder.
- /// </summary>
- /// <param name="sampleRate">Audio sampling rate in Hz.</param>
- /// <param name="channelCount">Number of audio channels in the data.</param>
- /// <param name="voiceApplication">Encoder preset to use.</param>
- public AudioFormat(int sampleRate = 48000, int channelCount = 2, VoiceApplication voiceApplication = VoiceApplication.Music)
- {
- if (!AllowedSampleRates.Contains(sampleRate))
- throw new ArgumentOutOfRangeException(nameof(sampleRate), "Invalid sample rate specified.");
-
- if (!AllowedChannelCounts.Contains(channelCount))
- throw new ArgumentOutOfRangeException(nameof(channelCount), "Invalid channel count specified.");
-
- if (voiceApplication != VoiceApplication.Music && voiceApplication != VoiceApplication.Voice && voiceApplication != VoiceApplication.LowLatency)
- throw new ArgumentOutOfRangeException(nameof(voiceApplication), "Invalid voice application specified.");
-
- this.SampleRate = sampleRate;
- this.ChannelCount = channelCount;
- this.VoiceApplication = voiceApplication;
- }
-
- /// <summary>
- /// Calculates a sample size in bytes.
+ /// Defines the format of PCM data consumed or produced by Opus.
/// </summary>
- /// <param name="sampleDuration">Millisecond duration of a sample.</param>
- /// <returns>Calculated sample size in bytes.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int CalculateSampleSize(int sampleDuration)
+ public struct AudioFormat
{
- if (!AllowedSampleDurations.Contains(sampleDuration))
- throw new ArgumentOutOfRangeException(nameof(sampleDuration), "Invalid sample duration specified.");
-
- // Sample size in bytes is a product of the following:
- // - duration in milliseconds
- // - number of channels
- // - sample rate in kHz
- // - size of data (in this case, sizeof(int16_t))
- // which comes down to below:
- return sampleDuration * this.ChannelCount * (this.SampleRate / 1000) * 2;
+ /// <summary>
+ /// Gets the collection of sampling rates (in Hz) the Opus encoder can use.
+ /// </summary>
+ public static IReadOnlyCollection<int> AllowedSampleRates { get; } = new ReadOnlyCollection<int>(new[] { 8000, 12000, 16000, 24000, 48000 });
+
+ /// <summary>
+ /// Gets the collection of channel counts the Opus encoder can use.
+ /// </summary>
+ public static IReadOnlyCollection<int> AllowedChannelCounts { get; } = new ReadOnlyCollection<int>(new[] { 1, 2 });
+
+ /// <summary>
+ /// Gets the collection of sample durations (in ms) the Opus encoder can use.
+ /// </summary>
+ public static IReadOnlyCollection<int> AllowedSampleDurations { get; } = new ReadOnlyCollection<int>(new[] { 5, 10, 20, 40, 60 });
+
+ /// <summary>
+ /// Gets the default audio format. This is a format configured for 48kHz sampling rate, 2 channels, with music quality preset.
+ /// </summary>
+ public static AudioFormat Default { get; } = new(48000, 2, VoiceApplication.Music);
+
+ /// <summary>
+ /// Gets the audio sampling rate in Hz.
+ /// </summary>
+ public int SampleRate { get; }
+
+ /// <summary>
+ /// Gets the audio channel count.
+ /// </summary>
+ public int ChannelCount { get; }
+
+ /// <summary>
+ /// Gets the voice application, which dictates the quality preset.
+ /// </summary>
+ public VoiceApplication VoiceApplication { get; }
+
+ /// <summary>
+ /// Creates a new audio format for use with Opus encoder.
+ /// </summary>
+ /// <param name="sampleRate">Audio sampling rate in Hz.</param>
+ /// <param name="channelCount">Number of audio channels in the data.</param>
+ /// <param name="voiceApplication">Encoder preset to use.</param>
+ public AudioFormat(int sampleRate = 48000, int channelCount = 2, VoiceApplication voiceApplication = VoiceApplication.Music)
+ {
+ if (!AllowedSampleRates.Contains(sampleRate))
+ throw new ArgumentOutOfRangeException(nameof(sampleRate), "Invalid sample rate specified.");
+
+ if (!AllowedChannelCounts.Contains(channelCount))
+ throw new ArgumentOutOfRangeException(nameof(channelCount), "Invalid channel count specified.");
+
+ if (voiceApplication != VoiceApplication.Music && voiceApplication != VoiceApplication.Voice && voiceApplication != VoiceApplication.LowLatency)
+ throw new ArgumentOutOfRangeException(nameof(voiceApplication), "Invalid voice application specified.");
+
+ this.SampleRate = sampleRate;
+ this.ChannelCount = channelCount;
+ this.VoiceApplication = voiceApplication;
+ }
+
+ /// <summary>
+ /// Calculates a sample size in bytes.
+ /// </summary>
+ /// <param name="sampleDuration">Millisecond duration of a sample.</param>
+ /// <returns>Calculated sample size in bytes.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int CalculateSampleSize(int sampleDuration)
+ {
+ if (!AllowedSampleDurations.Contains(sampleDuration))
+ throw new ArgumentOutOfRangeException(nameof(sampleDuration), "Invalid sample duration specified.");
+
+ // Sample size in bytes is a product of the following:
+ // - duration in milliseconds
+ // - number of channels
+ // - sample rate in kHz
+ // - size of data (in this case, sizeof(int16_t))
+ // which comes down to below:
+ return sampleDuration * this.ChannelCount * (this.SampleRate / 1000) * 2;
+ }
+
+ /// <summary>
+ /// Gets the maximum buffer size for decoding. This method should be called when decoding Opus data to PCM, to ensure sufficient buffer size.
+ /// </summary>
+ /// <returns>Buffer size required to decode data.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int GetMaximumBufferSize()
+ => this.CalculateMaximumFrameSize();
+
+ /// <summary>
+ /// Calculates the sample duration.
+ /// </summary>
+ /// <param name="sampleSize">The sample size.</param>
+ /// <returns>An int.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal int CalculateSampleDuration(int sampleSize)
+ => sampleSize / (this.SampleRate / 1000) / this.ChannelCount / 2 /* sizeof(int16_t) */;
+
+ /// <summary>
+ /// Calculates the frame size.
+ /// </summary>
+ /// <param name="sampleDuration">The sample duration.</param>
+ /// <returns>An int.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal int CalculateFrameSize(int sampleDuration)
+ => sampleDuration * (this.SampleRate / 1000);
+
+ /// <summary>
+ /// Calculates the maximum frame size.
+ /// </summary>
+ /// <returns>An int.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal int CalculateMaximumFrameSize()
+ => 120 * (this.SampleRate / 1000);
+
+ /// <summary>
+ /// Samples the count to sample size.
+ /// </summary>
+ /// <param name="sampleCount">The sample count.</param>
+ /// <returns>An int.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal int SampleCountToSampleSize(int sampleCount)
+ => sampleCount * this.ChannelCount * 2 /* sizeof(int16_t) */;
+
+ /// <summary>
+ /// Are the valid.
+ /// </summary>
+ /// <returns>A bool.</returns>
+ internal bool IsValid()
+ => AllowedSampleRates.Contains(this.SampleRate) && AllowedChannelCounts.Contains(this.ChannelCount) &&
+ (this.VoiceApplication == VoiceApplication.Music || this.VoiceApplication == VoiceApplication.Voice || this.VoiceApplication == VoiceApplication.LowLatency);
}
-
- /// <summary>
- /// Gets the maximum buffer size for decoding. This method should be called when decoding Opus data to PCM, to ensure sufficient buffer size.
- /// </summary>
- /// <returns>Buffer size required to decode data.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public int GetMaximumBufferSize()
- => this.CalculateMaximumFrameSize();
-
- /// <summary>
- /// Calculates the sample duration.
- /// </summary>
- /// <param name="sampleSize">The sample size.</param>
- /// <returns>An int.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal int CalculateSampleDuration(int sampleSize)
- => sampleSize / (this.SampleRate / 1000) / this.ChannelCount / 2 /* sizeof(int16_t) */;
-
- /// <summary>
- /// Calculates the frame size.
- /// </summary>
- /// <param name="sampleDuration">The sample duration.</param>
- /// <returns>An int.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal int CalculateFrameSize(int sampleDuration)
- => sampleDuration * (this.SampleRate / 1000);
-
- /// <summary>
- /// Calculates the maximum frame size.
- /// </summary>
- /// <returns>An int.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal int CalculateMaximumFrameSize()
- => 120 * (this.SampleRate / 1000);
-
- /// <summary>
- /// Samples the count to sample size.
- /// </summary>
- /// <param name="sampleCount">The sample count.</param>
- /// <returns>An int.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal int SampleCountToSampleSize(int sampleCount)
- => sampleCount * this.ChannelCount * 2 /* sizeof(int16_t) */;
-
- /// <summary>
- /// Are the valid.
- /// </summary>
- /// <returns>A bool.</returns>
- internal bool IsValid()
- => AllowedSampleRates.Contains(this.SampleRate) && AllowedChannelCounts.Contains(this.ChannelCount) &&
- (this.VoiceApplication == VoiceApplication.Music || this.VoiceApplication == VoiceApplication.Voice || this.VoiceApplication == VoiceApplication.LowLatency);
}
diff --git a/DisCatSharp.VoiceNext/Codec/Helpers.cs b/DisCatSharp.VoiceNext/Codec/Helpers.cs
index f8e069571..d1a50a3de 100644
--- a/DisCatSharp.VoiceNext/Codec/Helpers.cs
+++ b/DisCatSharp.VoiceNext/Codec/Helpers.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-namespace DisCatSharp.VoiceNext.Codec;
-
-/// <summary>
-/// The helpers.
-/// </summary>
-internal static class Helpers
+namespace DisCatSharp.VoiceNext.Codec
{
/// <summary>
- /// Fills the buffer with 0.
+ /// The helpers.
/// </summary>
- /// <param name="buff">The buffer.</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ZeroFill(Span<byte> buff)
+ internal static class Helpers
{
- var zero = 0;
- var i = 0;
- for (; i < buff.Length / 4; i++)
- MemoryMarshal.Write(buff, ref zero);
+ /// <summary>
+ /// Fills the buffer with 0.
+ /// </summary>
+ /// <param name="buff">The buffer.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ZeroFill(Span<byte> buff)
+ {
+ var zero = 0;
+ var i = 0;
+ for (; i < buff.Length / 4; i++)
+ MemoryMarshal.Write(buff, ref zero);
- var remainder = buff.Length % 4;
- if (remainder == 0)
- return;
+ var remainder = buff.Length % 4;
+ if (remainder == 0)
+ return;
- for (; i < buff.Length; i++)
- buff[i] = 0;
+ for (; i < buff.Length; i++)
+ buff[i] = 0;
+ }
}
}
diff --git a/DisCatSharp.VoiceNext/Codec/Interop.cs b/DisCatSharp.VoiceNext/Codec/Interop.cs
index ae1b5d0fc..f859fa9f5 100644
--- a/DisCatSharp.VoiceNext/Codec/Interop.cs
+++ b/DisCatSharp.VoiceNext/Codec/Interop.cs
@@ -1,391 +1,392 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Runtime.InteropServices;
-namespace DisCatSharp.VoiceNext.Codec;
-
-/// <summary>
-/// This is an interop class. It contains wrapper methods for Opus and Sodium.
-/// </summary>
-internal static class Interop
+namespace DisCatSharp.VoiceNext.Codec
{
- #region Sodium wrapper
- /// <summary>
- /// The sodium library name.
- /// </summary>
- private const string SODIUM_LIBRARY_NAME = "libsodium";
-
/// <summary>
- /// Gets the Sodium key size for xsalsa20_poly1305 algorithm.
+ /// This is an interop class. It contains wrapper methods for Opus and Sodium.
/// </summary>
- public static int SodiumKeySize { get; } = (int)_SodiumSecretBoxKeySize();
-
- /// <summary>
- /// Gets the Sodium nonce size for xsalsa20_poly1305 algorithm.
- /// </summary>
- public static int SodiumNonceSize { get; } = (int)_SodiumSecretBoxNonceSize();
-
- /// <summary>
- /// Gets the Sodium MAC size for xsalsa20_poly1305 algorithm.
- /// </summary>
- public static int SodiumMacSize { get; } = (int)_SodiumSecretBoxMacSize();
-
- /// <summary>
- /// _S the sodium secret box key size.
- /// </summary>
- /// <returns>An UIntPtr.</returns>
- [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_xsalsa20poly1305_keybytes")]
- [return: MarshalAs(UnmanagedType.SysUInt)]
- private static extern UIntPtr _SodiumSecretBoxKeySize();
-
- /// <summary>
- /// _S the sodium secret box nonce size.
- /// </summary>
- /// <returns>An UIntPtr.</returns>
- [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_xsalsa20poly1305_noncebytes")]
- [return: MarshalAs(UnmanagedType.SysUInt)]
- private static extern UIntPtr _SodiumSecretBoxNonceSize();
-
- /// <summary>
- /// _S the sodium secret box mac size.
- /// </summary>
- /// <returns>An UIntPtr.</returns>
- [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_xsalsa20poly1305_macbytes")]
- [return: MarshalAs(UnmanagedType.SysUInt)]
- private static extern UIntPtr _SodiumSecretBoxMacSize();
-
- /// <summary>
- /// _S the sodium secret box create.
- /// </summary>
- /// <param name="buffer">The buffer.</param>
- /// <param name="message">The message.</param>
- /// <param name="messageLength">The message length.</param>
- /// <param name="nonce">The nonce.</param>
- /// <param name="key">The key.</param>
- /// <returns>An int.</returns>
- [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_easy")]
- private static unsafe extern int _SodiumSecretBoxCreate(byte* buffer, byte* message, ulong messageLength, byte* nonce, byte* key);
-
- /// <summary>
- /// _S the sodium secret box open.
- /// </summary>
- /// <param name="buffer">The buffer.</param>
- /// <param name="encryptedMessage">The encrypted message.</param>
- /// <param name="encryptedLength">The encrypted length.</param>
- /// <param name="nonce">The nonce.</param>
- /// <param name="key">The key.</param>
- /// <returns>An int.</returns>
- [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_open_easy")]
- private static unsafe extern int _SodiumSecretBoxOpen(byte* buffer, byte* encryptedMessage, ulong encryptedLength, byte* nonce, byte* key);
-
- /// <summary>
- /// Encrypts supplied buffer using xsalsa20_poly1305 algorithm, using supplied key and nonce to perform encryption.
- /// </summary>
- /// <param name="source">Contents to encrypt.</param>
- /// <param name="target">Buffer to encrypt to.</param>
- /// <param name="key">Key to use for encryption.</param>
- /// <param name="nonce">Nonce to use for encryption.</param>
- /// <returns>Encryption status.</returns>
- public static unsafe int Encrypt(ReadOnlySpan<byte> source, Span<byte> target, ReadOnlySpan<byte> key, ReadOnlySpan<byte> nonce)
+ internal static class Interop
{
- var status = 0;
- fixed (byte* sourcePtr = &source.GetPinnableReference())
- fixed (byte* targetPtr = &target.GetPinnableReference())
- fixed (byte* keyPtr = &key.GetPinnableReference())
- fixed (byte* noncePtr = &nonce.GetPinnableReference())
- status = _SodiumSecretBoxCreate(targetPtr, sourcePtr, (ulong)source.Length, noncePtr, keyPtr);
-
- return status;
- }
-
- /// <summary>
- /// Decrypts supplied buffer using xsalsa20_poly1305 algorithm, using supplied key and nonce to perform decryption.
- /// </summary>
- /// <param name="source">Buffer to decrypt from.</param>
- /// <param name="target">Decrypted message buffer.</param>
- /// <param name="key">Key to use for decryption.</param>
- /// <param name="nonce">Nonce to use for decryption.</param>
- /// <returns>Decryption status.</returns>
- public static unsafe int Decrypt(ReadOnlySpan<byte> source, Span<byte> target, ReadOnlySpan<byte> key, ReadOnlySpan<byte> nonce)
- {
- var status = 0;
- fixed (byte* sourcePtr = &source.GetPinnableReference())
- fixed (byte* targetPtr = &target.GetPinnableReference())
- fixed (byte* keyPtr = &key.GetPinnableReference())
- fixed (byte* noncePtr = &nonce.GetPinnableReference())
- status = _SodiumSecretBoxOpen(targetPtr, sourcePtr, (ulong)source.Length, noncePtr, keyPtr);
-
- return status;
- }
- #endregion
-
- #region Opus wrapper
- /// <summary>
- /// The opus library name.
- /// </summary>
- private const string OPUS_LIBRARY_NAME = "libopus";
-
- /// <summary>
- /// _S the opus create encoder.
- /// </summary>
- /// <param name="sampleRate">The sample rate.</param>
- /// <param name="channels">The channels.</param>
- /// <param name="application">The application.</param>
- /// <param name="error">The error.</param>
- /// <returns>An IntPtr.</returns>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_encoder_create")]
- private static extern IntPtr _OpusCreateEncoder(int sampleRate, int channels, int application, out OpusError error);
-
- /// <summary>
- /// Opuses the destroy encoder.
- /// </summary>
- /// <param name="encoder">The encoder.</param>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_encoder_destroy")]
- public static extern void OpusDestroyEncoder(IntPtr encoder);
-
- /// <summary>
- /// _S the opus encode.
- /// </summary>
- /// <param name="encoder">The encoder.</param>
- /// <param name="pcmData">The pcm data.</param>
- /// <param name="frameSize">The frame size.</param>
- /// <param name="data">The data.</param>
- /// <param name="maxDataBytes">The max data bytes.</param>
- /// <returns>An int.</returns>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_encode")]
- private static unsafe extern int _OpusEncode(IntPtr encoder, byte* pcmData, int frameSize, byte* data, int maxDataBytes);
-
- /// <summary>
- /// _S the opus encoder control.
- /// </summary>
- /// <param name="encoder">The encoder.</param>
- /// <param name="request">The request.</param>
- /// <param name="value">The value.</param>
- /// <returns>An OpusError.</returns>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_encoder_ctl")]
- private static extern OpusError _OpusEncoderControl(IntPtr encoder, OpusControl request, int value);
-
- /// <summary>
- /// _S the opus create decoder.
- /// </summary>
- /// <param name="sampleRate">The sample rate.</param>
- /// <param name="channels">The channels.</param>
- /// <param name="error">The error.</param>
- /// <returns>An IntPtr.</returns>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_decoder_create")]
- private static extern IntPtr _OpusCreateDecoder(int sampleRate, int channels, out OpusError error);
-
- /// <summary>
- /// Opuses the destroy decoder.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_decoder_destroy")]
- public static extern void OpusDestroyDecoder(IntPtr decoder);
-
- /// <summary>
- /// _S the opus decode.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- /// <param name="opusData">The opus data.</param>
- /// <param name="opusDataLength">The opus data length.</param>
- /// <param name="data">The data.</param>
- /// <param name="frameSize">The frame size.</param>
- /// <param name="decodeFec">The decode fec.</param>
- /// <returns>An int.</returns>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_decode")]
- private static unsafe extern int _OpusDecode(IntPtr decoder, byte* opusData, int opusDataLength, byte* data, int frameSize, int decodeFec);
-
- /// <summary>
- /// _S the opus get packet chanel count.
- /// </summary>
- /// <param name="opusData">The opus data.</param>
- /// <returns>An int.</returns>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_packet_get_nb_channels")]
- private static unsafe extern int _OpusGetPacketChanelCount(byte* opusData);
+ #region Sodium wrapper
+ /// <summary>
+ /// The sodium library name.
+ /// </summary>
+ private const string SODIUM_LIBRARY_NAME = "libsodium";
+
+ /// <summary>
+ /// Gets the Sodium key size for xsalsa20_poly1305 algorithm.
+ /// </summary>
+ public static int SodiumKeySize { get; } = (int)_SodiumSecretBoxKeySize();
+
+ /// <summary>
+ /// Gets the Sodium nonce size for xsalsa20_poly1305 algorithm.
+ /// </summary>
+ public static int SodiumNonceSize { get; } = (int)_SodiumSecretBoxNonceSize();
+
+ /// <summary>
+ /// Gets the Sodium MAC size for xsalsa20_poly1305 algorithm.
+ /// </summary>
+ public static int SodiumMacSize { get; } = (int)_SodiumSecretBoxMacSize();
+
+ /// <summary>
+ /// _S the sodium secret box key size.
+ /// </summary>
+ /// <returns>An UIntPtr.</returns>
+ [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_xsalsa20poly1305_keybytes")]
+ [return: MarshalAs(UnmanagedType.SysUInt)]
+ private static extern UIntPtr _SodiumSecretBoxKeySize();
+
+ /// <summary>
+ /// _S the sodium secret box nonce size.
+ /// </summary>
+ /// <returns>An UIntPtr.</returns>
+ [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_xsalsa20poly1305_noncebytes")]
+ [return: MarshalAs(UnmanagedType.SysUInt)]
+ private static extern UIntPtr _SodiumSecretBoxNonceSize();
+
+ /// <summary>
+ /// _S the sodium secret box mac size.
+ /// </summary>
+ /// <returns>An UIntPtr.</returns>
+ [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_xsalsa20poly1305_macbytes")]
+ [return: MarshalAs(UnmanagedType.SysUInt)]
+ private static extern UIntPtr _SodiumSecretBoxMacSize();
+
+ /// <summary>
+ /// _S the sodium secret box create.
+ /// </summary>
+ /// <param name="buffer">The buffer.</param>
+ /// <param name="message">The message.</param>
+ /// <param name="messageLength">The message length.</param>
+ /// <param name="nonce">The nonce.</param>
+ /// <param name="key">The key.</param>
+ /// <returns>An int.</returns>
+ [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_easy")]
+ private static unsafe extern int _SodiumSecretBoxCreate(byte* buffer, byte* message, ulong messageLength, byte* nonce, byte* key);
+
+ /// <summary>
+ /// _S the sodium secret box open.
+ /// </summary>
+ /// <param name="buffer">The buffer.</param>
+ /// <param name="encryptedMessage">The encrypted message.</param>
+ /// <param name="encryptedLength">The encrypted length.</param>
+ /// <param name="nonce">The nonce.</param>
+ /// <param name="key">The key.</param>
+ /// <returns>An int.</returns>
+ [DllImport(SODIUM_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "crypto_secretbox_open_easy")]
+ private static unsafe extern int _SodiumSecretBoxOpen(byte* buffer, byte* encryptedMessage, ulong encryptedLength, byte* nonce, byte* key);
+
+ /// <summary>
+ /// Encrypts supplied buffer using xsalsa20_poly1305 algorithm, using supplied key and nonce to perform encryption.
+ /// </summary>
+ /// <param name="source">Contents to encrypt.</param>
+ /// <param name="target">Buffer to encrypt to.</param>
+ /// <param name="key">Key to use for encryption.</param>
+ /// <param name="nonce">Nonce to use for encryption.</param>
+ /// <returns>Encryption status.</returns>
+ public static unsafe int Encrypt(ReadOnlySpan<byte> source, Span<byte> target, ReadOnlySpan<byte> key, ReadOnlySpan<byte> nonce)
+ {
+ var status = 0;
+ fixed (byte* sourcePtr = &source.GetPinnableReference())
+ fixed (byte* targetPtr = &target.GetPinnableReference())
+ fixed (byte* keyPtr = &key.GetPinnableReference())
+ fixed (byte* noncePtr = &nonce.GetPinnableReference())
+ status = _SodiumSecretBoxCreate(targetPtr, sourcePtr, (ulong)source.Length, noncePtr, keyPtr);
+
+ return status;
+ }
- /// <summary>
- /// _S the opus get packet frame count.
- /// </summary>
- /// <param name="opusData">The opus data.</param>
- /// <param name="length">The length.</param>
- /// <returns>An int.</returns>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_packet_get_nb_frames")]
- private static unsafe extern int _OpusGetPacketFrameCount(byte* opusData, int length);
+ /// <summary>
+ /// Decrypts supplied buffer using xsalsa20_poly1305 algorithm, using supplied key and nonce to perform decryption.
+ /// </summary>
+ /// <param name="source">Buffer to decrypt from.</param>
+ /// <param name="target">Decrypted message buffer.</param>
+ /// <param name="key">Key to use for decryption.</param>
+ /// <param name="nonce">Nonce to use for decryption.</param>
+ /// <returns>Decryption status.</returns>
+ public static unsafe int Decrypt(ReadOnlySpan<byte> source, Span<byte> target, ReadOnlySpan<byte> key, ReadOnlySpan<byte> nonce)
+ {
+ var status = 0;
+ fixed (byte* sourcePtr = &source.GetPinnableReference())
+ fixed (byte* targetPtr = &target.GetPinnableReference())
+ fixed (byte* keyPtr = &key.GetPinnableReference())
+ fixed (byte* noncePtr = &nonce.GetPinnableReference())
+ status = _SodiumSecretBoxOpen(targetPtr, sourcePtr, (ulong)source.Length, noncePtr, keyPtr);
+
+ return status;
+ }
+ #endregion
+
+ #region Opus wrapper
+ /// <summary>
+ /// The opus library name.
+ /// </summary>
+ private const string OPUS_LIBRARY_NAME = "libopus";
+
+ /// <summary>
+ /// _S the opus create encoder.
+ /// </summary>
+ /// <param name="sampleRate">The sample rate.</param>
+ /// <param name="channels">The channels.</param>
+ /// <param name="application">The application.</param>
+ /// <param name="error">The error.</param>
+ /// <returns>An IntPtr.</returns>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_encoder_create")]
+ private static extern IntPtr _OpusCreateEncoder(int sampleRate, int channels, int application, out OpusError error);
+
+ /// <summary>
+ /// Opuses the destroy encoder.
+ /// </summary>
+ /// <param name="encoder">The encoder.</param>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_encoder_destroy")]
+ public static extern void OpusDestroyEncoder(IntPtr encoder);
+
+ /// <summary>
+ /// _S the opus encode.
+ /// </summary>
+ /// <param name="encoder">The encoder.</param>
+ /// <param name="pcmData">The pcm data.</param>
+ /// <param name="frameSize">The frame size.</param>
+ /// <param name="data">The data.</param>
+ /// <param name="maxDataBytes">The max data bytes.</param>
+ /// <returns>An int.</returns>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_encode")]
+ private static unsafe extern int _OpusEncode(IntPtr encoder, byte* pcmData, int frameSize, byte* data, int maxDataBytes);
+
+ /// <summary>
+ /// _S the opus encoder control.
+ /// </summary>
+ /// <param name="encoder">The encoder.</param>
+ /// <param name="request">The request.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>An OpusError.</returns>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_encoder_ctl")]
+ private static extern OpusError _OpusEncoderControl(IntPtr encoder, OpusControl request, int value);
+
+ /// <summary>
+ /// _S the opus create decoder.
+ /// </summary>
+ /// <param name="sampleRate">The sample rate.</param>
+ /// <param name="channels">The channels.</param>
+ /// <param name="error">The error.</param>
+ /// <returns>An IntPtr.</returns>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_decoder_create")]
+ private static extern IntPtr _OpusCreateDecoder(int sampleRate, int channels, out OpusError error);
+
+ /// <summary>
+ /// Opuses the destroy decoder.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_decoder_destroy")]
+ public static extern void OpusDestroyDecoder(IntPtr decoder);
+
+ /// <summary>
+ /// _S the opus decode.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ /// <param name="opusData">The opus data.</param>
+ /// <param name="opusDataLength">The opus data length.</param>
+ /// <param name="data">The data.</param>
+ /// <param name="frameSize">The frame size.</param>
+ /// <param name="decodeFec">The decode fec.</param>
+ /// <returns>An int.</returns>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_decode")]
+ private static unsafe extern int _OpusDecode(IntPtr decoder, byte* opusData, int opusDataLength, byte* data, int frameSize, int decodeFec);
+
+ /// <summary>
+ /// _S the opus get packet chanel count.
+ /// </summary>
+ /// <param name="opusData">The opus data.</param>
+ /// <returns>An int.</returns>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_packet_get_nb_channels")]
+ private static unsafe extern int _OpusGetPacketChanelCount(byte* opusData);
+
+ /// <summary>
+ /// _S the opus get packet frame count.
+ /// </summary>
+ /// <param name="opusData">The opus data.</param>
+ /// <param name="length">The length.</param>
+ /// <returns>An int.</returns>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_packet_get_nb_frames")]
+ private static unsafe extern int _OpusGetPacketFrameCount(byte* opusData, int length);
+
+ /// <summary>
+ /// _S the opus get packet sample per frame count.
+ /// </summary>
+ /// <param name="opusData">The opus data.</param>
+ /// <param name="samplingRate">The sampling rate.</param>
+ /// <returns>An int.</returns>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_packet_get_samples_per_frame")]
+ private static unsafe extern int _OpusGetPacketSamplePerFrameCount(byte* opusData, int samplingRate);
+
+ /// <summary>
+ /// _S the opus decoder control.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ /// <param name="request">The request.</param>
+ /// <param name="value">The value.</param>
+ /// <returns>An int.</returns>
+ [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_decoder_ctl")]
+ private static extern int _OpusDecoderControl(IntPtr decoder, OpusControl request, out int value);
+
+ /// <summary>
+ /// Opuses the create encoder.
+ /// </summary>
+ /// <param name="audioFormat">The audio format.</param>
+ /// <returns>An IntPtr.</returns>
+ public static IntPtr OpusCreateEncoder(AudioFormat audioFormat)
+ {
+ var encoder = _OpusCreateEncoder(audioFormat.SampleRate, audioFormat.ChannelCount, (int)audioFormat.VoiceApplication, out var error);
+ return error != OpusError.Ok ? throw new Exception($"Could not instantiate Opus encoder: {error} ({(int)error}).") : encoder;
+ }
- /// <summary>
- /// _S the opus get packet sample per frame count.
- /// </summary>
- /// <param name="opusData">The opus data.</param>
- /// <param name="samplingRate">The sampling rate.</param>
- /// <returns>An int.</returns>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_packet_get_samples_per_frame")]
- private static unsafe extern int _OpusGetPacketSamplePerFrameCount(byte* opusData, int samplingRate);
+ /// <summary>
+ /// Opuses the set encoder option.
+ /// </summary>
+ /// <param name="encoder">The encoder.</param>
+ /// <param name="option">The option.</param>
+ /// <param name="value">The value.</param>
+ public static void OpusSetEncoderOption(IntPtr encoder, OpusControl option, int value)
+ {
+ var error = OpusError.Ok;
+ if ((error = _OpusEncoderControl(encoder, option, value)) != OpusError.Ok)
+ throw new Exception($"Could not set Opus encoder option: {error} ({(int)error}).");
+ }
- /// <summary>
- /// _S the opus decoder control.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- /// <param name="request">The request.</param>
- /// <param name="value">The value.</param>
- /// <returns>An int.</returns>
- [DllImport(OPUS_LIBRARY_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "opus_decoder_ctl")]
- private static extern int _OpusDecoderControl(IntPtr decoder, OpusControl request, out int value);
+ /// <summary>
+ /// Opuses the encode.
+ /// </summary>
+ /// <param name="encoder">The encoder.</param>
+ /// <param name="pcm">The pcm.</param>
+ /// <param name="frameSize">The frame size.</param>
+ /// <param name="opus">The opus.</param>
+ public static unsafe void OpusEncode(IntPtr encoder, ReadOnlySpan<byte> pcm, int frameSize, ref Span<byte> opus)
+ {
+ var len = 0;
- /// <summary>
- /// Opuses the create encoder.
- /// </summary>
- /// <param name="audioFormat">The audio format.</param>
- /// <returns>An IntPtr.</returns>
- public static IntPtr OpusCreateEncoder(AudioFormat audioFormat)
- {
- var encoder = _OpusCreateEncoder(audioFormat.SampleRate, audioFormat.ChannelCount, (int)audioFormat.VoiceApplication, out var error);
- return error != OpusError.Ok ? throw new Exception($"Could not instantiate Opus encoder: {error} ({(int)error}).") : encoder;
- }
+ fixed (byte* pcmPtr = &pcm.GetPinnableReference())
+ fixed (byte* opusPtr = &opus.GetPinnableReference())
+ len = _OpusEncode(encoder, pcmPtr, frameSize, opusPtr, opus.Length);
- /// <summary>
- /// Opuses the set encoder option.
- /// </summary>
- /// <param name="encoder">The encoder.</param>
- /// <param name="option">The option.</param>
- /// <param name="value">The value.</param>
- public static void OpusSetEncoderOption(IntPtr encoder, OpusControl option, int value)
- {
- var error = OpusError.Ok;
- if ((error = _OpusEncoderControl(encoder, option, value)) != OpusError.Ok)
- throw new Exception($"Could not set Opus encoder option: {error} ({(int)error}).");
- }
+ if (len < 0)
+ {
+ var error = (OpusError)len;
+ throw new Exception($"Could not encode PCM data to Opus: {error} ({(int)error}).");
+ }
- /// <summary>
- /// Opuses the encode.
- /// </summary>
- /// <param name="encoder">The encoder.</param>
- /// <param name="pcm">The pcm.</param>
- /// <param name="frameSize">The frame size.</param>
- /// <param name="opus">The opus.</param>
- public static unsafe void OpusEncode(IntPtr encoder, ReadOnlySpan<byte> pcm, int frameSize, ref Span<byte> opus)
- {
- var len = 0;
-
- fixed (byte* pcmPtr = &pcm.GetPinnableReference())
- fixed (byte* opusPtr = &opus.GetPinnableReference())
- len = _OpusEncode(encoder, pcmPtr, frameSize, opusPtr, opus.Length);
+ opus = opus[..len];
+ }
- if (len < 0)
+ /// <summary>
+ /// Opuses the create decoder.
+ /// </summary>
+ /// <param name="audioFormat">The audio format.</param>
+ /// <returns>An IntPtr.</returns>
+ public static IntPtr OpusCreateDecoder(AudioFormat audioFormat)
{
- var error = (OpusError)len;
- throw new Exception($"Could not encode PCM data to Opus: {error} ({(int)error}).");
+ var decoder = _OpusCreateDecoder(audioFormat.SampleRate, audioFormat.ChannelCount, out var error);
+ return error != OpusError.Ok ? throw new Exception($"Could not instantiate Opus decoder: {error} ({(int)error}).") : decoder;
}
- opus = opus[..len];
- }
-
- /// <summary>
- /// Opuses the create decoder.
- /// </summary>
- /// <param name="audioFormat">The audio format.</param>
- /// <returns>An IntPtr.</returns>
- public static IntPtr OpusCreateDecoder(AudioFormat audioFormat)
- {
- var decoder = _OpusCreateDecoder(audioFormat.SampleRate, audioFormat.ChannelCount, out var error);
- return error != OpusError.Ok ? throw new Exception($"Could not instantiate Opus decoder: {error} ({(int)error}).") : decoder;
- }
+ /// <summary>
+ /// Opuses the decode.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ /// <param name="opus">The opus.</param>
+ /// <param name="frameSize">The frame size.</param>
+ /// <param name="pcm">The pcm.</param>
+ /// <param name="useFec">If true, use fec.</param>
+ /// <returns>An int.</returns>
+ public static unsafe int OpusDecode(IntPtr decoder, ReadOnlySpan<byte> opus, int frameSize, Span<byte> pcm, bool useFec)
+ {
+ var len = 0;
- /// <summary>
- /// Opuses the decode.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- /// <param name="opus">The opus.</param>
- /// <param name="frameSize">The frame size.</param>
- /// <param name="pcm">The pcm.</param>
- /// <param name="useFec">If true, use fec.</param>
- /// <returns>An int.</returns>
- public static unsafe int OpusDecode(IntPtr decoder, ReadOnlySpan<byte> opus, int frameSize, Span<byte> pcm, bool useFec)
- {
- var len = 0;
+ fixed (byte* opusPtr = &opus.GetPinnableReference())
+ fixed (byte* pcmPtr = &pcm.GetPinnableReference())
+ len = _OpusDecode(decoder, opusPtr, opus.Length, pcmPtr, frameSize, useFec ? 1 : 0);
- fixed (byte* opusPtr = &opus.GetPinnableReference())
- fixed (byte* pcmPtr = &pcm.GetPinnableReference())
- len = _OpusDecode(decoder, opusPtr, opus.Length, pcmPtr, frameSize, useFec ? 1 : 0);
+ if (len < 0)
+ {
+ var error = (OpusError)len;
+ throw new Exception($"Could not decode PCM data from Opus: {error} ({(int)error}).");
+ }
- if (len < 0)
- {
- var error = (OpusError)len;
- throw new Exception($"Could not decode PCM data from Opus: {error} ({(int)error}).");
+ return len;
}
- return len;
- }
+ /// <summary>
+ /// Opuses the decode.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ /// <param name="frameSize">The frame size.</param>
+ /// <param name="pcm">The pcm.</param>
+ /// <returns>An int.</returns>
+ public static unsafe int OpusDecode(IntPtr decoder, int frameSize, Span<byte> pcm)
+ {
+ var len = 0;
- /// <summary>
- /// Opuses the decode.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- /// <param name="frameSize">The frame size.</param>
- /// <param name="pcm">The pcm.</param>
- /// <returns>An int.</returns>
- public static unsafe int OpusDecode(IntPtr decoder, int frameSize, Span<byte> pcm)
- {
- var len = 0;
+ fixed (byte* pcmPtr = &pcm.GetPinnableReference())
+ len = _OpusDecode(decoder, null, 0, pcmPtr, frameSize, 1);
- fixed (byte* pcmPtr = &pcm.GetPinnableReference())
- len = _OpusDecode(decoder, null, 0, pcmPtr, frameSize, 1);
+ if (len < 0)
+ {
+ var error = (OpusError)len;
+ throw new Exception($"Could not decode PCM data from Opus: {error} ({(int)error}).");
+ }
- if (len < 0)
- {
- var error = (OpusError)len;
- throw new Exception($"Could not decode PCM data from Opus: {error} ({(int)error}).");
+ return len;
}
- return len;
- }
-
- /// <summary>
- /// Opuses the get packet metrics.
- /// </summary>
- /// <param name="opus">The opus.</param>
- /// <param name="samplingRate">The sampling rate.</param>
- /// <param name="channels">The channels.</param>
- /// <param name="frames">The frames.</param>
- /// <param name="samplesPerFrame">The samples per frame.</param>
- /// <param name="frameSize">The frame size.</param>
- public static unsafe void OpusGetPacketMetrics(ReadOnlySpan<byte> opus, int samplingRate, out int channels, out int frames, out int samplesPerFrame, out int frameSize)
- {
- fixed (byte* opusPtr = &opus.GetPinnableReference())
+ /// <summary>
+ /// Opuses the get packet metrics.
+ /// </summary>
+ /// <param name="opus">The opus.</param>
+ /// <param name="samplingRate">The sampling rate.</param>
+ /// <param name="channels">The channels.</param>
+ /// <param name="frames">The frames.</param>
+ /// <param name="samplesPerFrame">The samples per frame.</param>
+ /// <param name="frameSize">The frame size.</param>
+ public static unsafe void OpusGetPacketMetrics(ReadOnlySpan<byte> opus, int samplingRate, out int channels, out int frames, out int samplesPerFrame, out int frameSize)
{
- frames = _OpusGetPacketFrameCount(opusPtr, opus.Length);
- samplesPerFrame = _OpusGetPacketSamplePerFrameCount(opusPtr, samplingRate);
- channels = _OpusGetPacketChanelCount(opusPtr);
+ fixed (byte* opusPtr = &opus.GetPinnableReference())
+ {
+ frames = _OpusGetPacketFrameCount(opusPtr, opus.Length);
+ samplesPerFrame = _OpusGetPacketSamplePerFrameCount(opusPtr, samplingRate);
+ channels = _OpusGetPacketChanelCount(opusPtr);
+ }
+
+ frameSize = frames * samplesPerFrame;
}
- frameSize = frames * samplesPerFrame;
+ /// <summary>
+ /// Opuses the get last packet duration.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ /// <param name="sampleCount">The sample count.</param>
+ public static void OpusGetLastPacketDuration(IntPtr decoder, out int sampleCount) => _OpusDecoderControl(decoder, OpusControl.GetLastPacketDuration, out sampleCount);
+ #endregion
}
-
- /// <summary>
- /// Opuses the get last packet duration.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- /// <param name="sampleCount">The sample count.</param>
- public static void OpusGetLastPacketDuration(IntPtr decoder, out int sampleCount) => _OpusDecoderControl(decoder, OpusControl.GetLastPacketDuration, out sampleCount);
- #endregion
}
diff --git a/DisCatSharp.VoiceNext/Codec/Opus.cs b/DisCatSharp.VoiceNext/Codec/Opus.cs
index ce475554a..07f3d6e90 100644
--- a/DisCatSharp.VoiceNext/Codec/Opus.cs
+++ b/DisCatSharp.VoiceNext/Codec/Opus.cs
@@ -1,287 +1,288 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.VoiceNext.Codec;
-
-/// <summary>
-/// The opus.
-/// </summary>
-internal sealed class Opus : IDisposable
+namespace DisCatSharp.VoiceNext.Codec
{
/// <summary>
- /// Gets the audio format.
- /// </summary>
- public AudioFormat AudioFormat { get; }
-
- /// <summary>
- /// Gets the encoder.
- /// </summary>
- private readonly IntPtr _encoder;
-
- /// <summary>
- /// Gets the managed decoders.
- /// </summary>
- private readonly List<OpusDecoder> _managedDecoders;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Opus"/> class.
+ /// The opus.
/// </summary>
- /// <param name="audioFormat">The audio format.</param>
- public Opus(AudioFormat audioFormat)
+ internal sealed class Opus : IDisposable
{
- if (!audioFormat.IsValid())
- throw new ArgumentException("Invalid audio format specified.", nameof(audioFormat));
-
- this.AudioFormat = audioFormat;
- this._encoder = Interop.OpusCreateEncoder(this.AudioFormat);
-
- // Set appropriate encoder options
- var sig = OpusSignal.Auto;
- switch (this.AudioFormat.VoiceApplication)
+ /// <summary>
+ /// Gets the audio format.
+ /// </summary>
+ public AudioFormat AudioFormat { get; }
+
+ /// <summary>
+ /// Gets the encoder.
+ /// </summary>
+ private readonly IntPtr _encoder;
+
+ /// <summary>
+ /// Gets the managed decoders.
+ /// </summary>
+ private readonly List<OpusDecoder> _managedDecoders;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Opus"/> class.
+ /// </summary>
+ /// <param name="audioFormat">The audio format.</param>
+ public Opus(AudioFormat audioFormat)
{
- case VoiceApplication.Music:
- sig = OpusSignal.Music;
- break;
-
- case VoiceApplication.Voice:
- sig = OpusSignal.Voice;
- break;
+ if (!audioFormat.IsValid())
+ throw new ArgumentException("Invalid audio format specified.", nameof(audioFormat));
+
+ this.AudioFormat = audioFormat;
+ this._encoder = Interop.OpusCreateEncoder(this.AudioFormat);
+
+ // Set appropriate encoder options
+ var sig = OpusSignal.Auto;
+ switch (this.AudioFormat.VoiceApplication)
+ {
+ case VoiceApplication.Music:
+ sig = OpusSignal.Music;
+ break;
+
+ case VoiceApplication.Voice:
+ sig = OpusSignal.Voice;
+ break;
+ }
+ Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetSignal, (int)sig);
+ Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetPacketLossPercent, 15);
+ Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetInBandFec, 1);
+ Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetBitrate, 131072);
+
+ this._managedDecoders = new List<OpusDecoder>();
}
- Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetSignal, (int)sig);
- Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetPacketLossPercent, 15);
- Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetInBandFec, 1);
- Interop.OpusSetEncoderOption(this._encoder, OpusControl.SetBitrate, 131072);
-
- this._managedDecoders = new List<OpusDecoder>();
- }
- /// <summary>
- /// Encodes the Opus.
- /// </summary>
- /// <param name="pcm">The pcm.</param>
- /// <param name="target">The target.</param>
- public void Encode(ReadOnlySpan<byte> pcm, ref Span<byte> target)
- {
- if (pcm.Length != target.Length)
- throw new ArgumentException("PCM and Opus buffer lengths need to be equal.", nameof(target));
+ /// <summary>
+ /// Encodes the Opus.
+ /// </summary>
+ /// <param name="pcm">The pcm.</param>
+ /// <param name="target">The target.</param>
+ public void Encode(ReadOnlySpan<byte> pcm, ref Span<byte> target)
+ {
+ if (pcm.Length != target.Length)
+ throw new ArgumentException("PCM and Opus buffer lengths need to be equal.", nameof(target));
- var duration = this.AudioFormat.CalculateSampleDuration(pcm.Length);
- var frameSize = this.AudioFormat.CalculateFrameSize(duration);
- var sampleSize = this.AudioFormat.CalculateSampleSize(duration);
+ var duration = this.AudioFormat.CalculateSampleDuration(pcm.Length);
+ var frameSize = this.AudioFormat.CalculateFrameSize(duration);
+ var sampleSize = this.AudioFormat.CalculateSampleSize(duration);
- if (pcm.Length != sampleSize)
- throw new ArgumentException("Invalid PCM sample size.", nameof(target));
+ if (pcm.Length != sampleSize)
+ throw new ArgumentException("Invalid PCM sample size.", nameof(target));
- Interop.OpusEncode(this._encoder, pcm, frameSize, ref target);
- }
+ Interop.OpusEncode(this._encoder, pcm, frameSize, ref target);
+ }
- /// <summary>
- /// Decodes the Opus.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- /// <param name="opus">The opus.</param>
- /// <param name="target">The target.</param>
- /// <param name="useFec">If true, use fec.</param>
- /// <param name="outputFormat">The output format.</param>
- public void Decode(OpusDecoder decoder, ReadOnlySpan<byte> opus, ref Span<byte> target, bool useFec, out AudioFormat outputFormat)
- {
- //if (target.Length != this.AudioFormat.CalculateMaximumFrameSize())
- // throw new ArgumentException("PCM target buffer size needs to be equal to maximum buffer size for specified audio format.", nameof(target));
+ /// <summary>
+ /// Decodes the Opus.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ /// <param name="opus">The opus.</param>
+ /// <param name="target">The target.</param>
+ /// <param name="useFec">If true, use fec.</param>
+ /// <param name="outputFormat">The output format.</param>
+ public void Decode(OpusDecoder decoder, ReadOnlySpan<byte> opus, ref Span<byte> target, bool useFec, out AudioFormat outputFormat)
+ {
+ //if (target.Length != this.AudioFormat.CalculateMaximumFrameSize())
+ // throw new ArgumentException("PCM target buffer size needs to be equal to maximum buffer size for specified audio format.", nameof(target));
- Interop.OpusGetPacketMetrics(opus, this.AudioFormat.SampleRate, out var channels, out var frames, out var samplesPerFrame, out var frameSize);
- outputFormat = this.AudioFormat.ChannelCount != channels ? new AudioFormat(this.AudioFormat.SampleRate, channels, this.AudioFormat.VoiceApplication) : this.AudioFormat;
+ Interop.OpusGetPacketMetrics(opus, this.AudioFormat.SampleRate, out var channels, out var frames, out var samplesPerFrame, out var frameSize);
+ outputFormat = this.AudioFormat.ChannelCount != channels ? new AudioFormat(this.AudioFormat.SampleRate, channels, this.AudioFormat.VoiceApplication) : this.AudioFormat;
- if (decoder.AudioFormat.ChannelCount != channels)
- decoder.Initialize(outputFormat);
+ if (decoder.AudioFormat.ChannelCount != channels)
+ decoder.Initialize(outputFormat);
- var sampleCount = Interop.OpusDecode(decoder.Decoder, opus, frameSize, target, useFec);
+ var sampleCount = Interop.OpusDecode(decoder.Decoder, opus, frameSize, target, useFec);
- var sampleSize = outputFormat.SampleCountToSampleSize(sampleCount);
- target = target[..sampleSize];
- }
+ var sampleSize = outputFormat.SampleCountToSampleSize(sampleCount);
+ target = target[..sampleSize];
+ }
- /// <summary>
- /// Processes the packet loss.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- /// <param name="frameSize">The frame size.</param>
- /// <param name="target">The target.</param>
- public void ProcessPacketLoss(OpusDecoder decoder, int frameSize, ref Span<byte> target) => Interop.OpusDecode(decoder.Decoder, frameSize, target);
+ /// <summary>
+ /// Processes the packet loss.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ /// <param name="frameSize">The frame size.</param>
+ /// <param name="target">The target.</param>
+ public void ProcessPacketLoss(OpusDecoder decoder, int frameSize, ref Span<byte> target) => Interop.OpusDecode(decoder.Decoder, frameSize, target);
+
+ /// <summary>
+ /// Gets the last packet sample count.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ /// <returns>An int.</returns>
+ public int GetLastPacketSampleCount(OpusDecoder decoder)
+ {
+ Interop.OpusGetLastPacketDuration(decoder.Decoder, out var sampleCount);
+ return sampleCount;
+ }
- /// <summary>
- /// Gets the last packet sample count.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- /// <returns>An int.</returns>
- public int GetLastPacketSampleCount(OpusDecoder decoder)
- {
- Interop.OpusGetLastPacketDuration(decoder.Decoder, out var sampleCount);
- return sampleCount;
- }
+ /// <summary>
+ /// Creates the decoder.
+ /// </summary>
+ /// <returns>An OpusDecoder.</returns>
+ public OpusDecoder CreateDecoder()
+ {
+ lock (this._managedDecoders)
+ {
+ var managedDecoder = new OpusDecoder(this);
+ this._managedDecoders.Add(managedDecoder);
+ return managedDecoder;
+ }
+ }
- /// <summary>
- /// Creates the decoder.
- /// </summary>
- /// <returns>An OpusDecoder.</returns>
- public OpusDecoder CreateDecoder()
- {
- lock (this._managedDecoders)
+ /// <summary>
+ /// Destroys the decoder.
+ /// </summary>
+ /// <param name="decoder">The decoder.</param>
+ public void DestroyDecoder(OpusDecoder decoder)
{
- var managedDecoder = new OpusDecoder(this);
- this._managedDecoders.Add(managedDecoder);
- return managedDecoder;
+ lock (this._managedDecoders)
+ {
+ if (!this._managedDecoders.Contains(decoder))
+ return;
+
+ this._managedDecoders.Remove(decoder);
+ decoder.Dispose();
+ }
}
- }
- /// <summary>
- /// Destroys the decoder.
- /// </summary>
- /// <param name="decoder">The decoder.</param>
- public void DestroyDecoder(OpusDecoder decoder)
- {
- lock (this._managedDecoders)
+ /// <summary>
+ /// Disposes the Opus.
+ /// </summary>
+ public void Dispose()
{
- if (!this._managedDecoders.Contains(decoder))
- return;
+ Interop.OpusDestroyEncoder(this._encoder);
- this._managedDecoders.Remove(decoder);
- decoder.Dispose();
+ lock (this._managedDecoders)
+ {
+ foreach (var decoder in this._managedDecoders)
+ decoder.Dispose();
+ }
}
}
/// <summary>
- /// Disposes the Opus.
+ /// Represents an Opus decoder.
/// </summary>
- public void Dispose()
+ public class OpusDecoder : IDisposable
{
- Interop.OpusDestroyEncoder(this._encoder);
-
- lock (this._managedDecoders)
+ /// <summary>
+ /// Gets the audio format produced by this decoder.
+ /// </summary>
+ public AudioFormat AudioFormat { get; private set; }
+
+ /// <summary>
+ /// Gets the opus.
+ /// </summary>
+ internal Opus Opus { get; }
+ /// <summary>
+ /// Gets the decoder.
+ /// </summary>
+ internal IntPtr Decoder { get; private set; }
+
+ private volatile bool _isDisposed;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OpusDecoder"/> class.
+ /// </summary>
+ /// <param name="managedOpus">The managed opus.</param>
+ internal OpusDecoder(Opus managedOpus)
{
- foreach (var decoder in this._managedDecoders)
- decoder.Dispose();
+ this.Opus = managedOpus;
}
- }
-}
-/// <summary>
-/// Represents an Opus decoder.
-/// </summary>
-public class OpusDecoder : IDisposable
-{
- /// <summary>
- /// Gets the audio format produced by this decoder.
- /// </summary>
- public AudioFormat AudioFormat { get; private set; }
+ /// <summary>
+ /// Used to lazily initialize the decoder to make sure we're
+ /// using the correct output format, this way we don't end up
+ /// creating more decoders than we need.
+ /// </summary>
+ /// <param name="outputFormat"></param>
+ internal void Initialize(AudioFormat outputFormat)
+ {
+ if (this.Decoder != IntPtr.Zero)
+ Interop.OpusDestroyDecoder(this.Decoder);
- /// <summary>
- /// Gets the opus.
- /// </summary>
- internal Opus Opus { get; }
- /// <summary>
- /// Gets the decoder.
- /// </summary>
- internal IntPtr Decoder { get; private set; }
+ this.AudioFormat = outputFormat;
- private volatile bool _isDisposed;
+ this.Decoder = Interop.OpusCreateDecoder(outputFormat);
+ }
+
+ /// <summary>
+ /// Disposes of this Opus decoder.
+ /// </summary>
+ public void Dispose()
+ {
+ if (this._isDisposed)
+ return;
+
+ this._isDisposed = true;
+ if (this.Decoder != IntPtr.Zero)
+ Interop.OpusDestroyDecoder(this.Decoder);
+ }
+ }
/// <summary>
- /// Initializes a new instance of the <see cref="OpusDecoder"/> class.
+ /// The opus error.
/// </summary>
- /// <param name="managedOpus">The managed opus.</param>
- internal OpusDecoder(Opus managedOpus)
+ [Flags]
+ internal enum OpusError
{
- this.Opus = managedOpus;
+ Ok = 0,
+ BadArgument = -1,
+ BufferTooSmall = -2,
+ InternalError = -3,
+ InvalidPacket = -4,
+ Unimplemented = -5,
+ InvalidState = -6,
+ AllocationFailure = -7
}
/// <summary>
- /// Used to lazily initialize the decoder to make sure we're
- /// using the correct output format, this way we don't end up
- /// creating more decoders than we need.
+ /// The opus control.
/// </summary>
- /// <param name="outputFormat"></param>
- internal void Initialize(AudioFormat outputFormat)
+ internal enum OpusControl : int
{
- if (this.Decoder != IntPtr.Zero)
- Interop.OpusDestroyDecoder(this.Decoder);
-
- this.AudioFormat = outputFormat;
-
- this.Decoder = Interop.OpusCreateDecoder(outputFormat);
+ SetBitrate = 4002,
+ SetBandwidth = 4008,
+ SetInBandFec = 4012,
+ SetPacketLossPercent = 4014,
+ SetSignal = 4024,
+ ResetState = 4028,
+ GetLastPacketDuration = 4039
}
/// <summary>
- /// Disposes of this Opus decoder.
+ /// The opus signal.
/// </summary>
- public void Dispose()
+ internal enum OpusSignal : int
{
- if (this._isDisposed)
- return;
-
- this._isDisposed = true;
- if (this.Decoder != IntPtr.Zero)
- Interop.OpusDestroyDecoder(this.Decoder);
+ Auto = -1000,
+ Voice = 3001,
+ Music = 3002,
}
}
-
-/// <summary>
-/// The opus error.
-/// </summary>
-[Flags]
-internal enum OpusError
-{
- Ok = 0,
- BadArgument = -1,
- BufferTooSmall = -2,
- InternalError = -3,
- InvalidPacket = -4,
- Unimplemented = -5,
- InvalidState = -6,
- AllocationFailure = -7
-}
-
-/// <summary>
-/// The opus control.
-/// </summary>
-internal enum OpusControl : int
-{
- SetBitrate = 4002,
- SetBandwidth = 4008,
- SetInBandFec = 4012,
- SetPacketLossPercent = 4014,
- SetSignal = 4024,
- ResetState = 4028,
- GetLastPacketDuration = 4039
-}
-
-/// <summary>
-/// The opus signal.
-/// </summary>
-internal enum OpusSignal : int
-{
- Auto = -1000,
- Voice = 3001,
- Music = 3002,
-}
diff --git a/DisCatSharp.VoiceNext/Codec/Rtp.cs b/DisCatSharp.VoiceNext/Codec/Rtp.cs
index 1a4653828..d00b76374 100644
--- a/DisCatSharp.VoiceNext/Codec/Rtp.cs
+++ b/DisCatSharp.VoiceNext/Codec/Rtp.cs
@@ -1,158 +1,159 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Buffers.Binary;
-namespace DisCatSharp.VoiceNext.Codec;
-
-/// <summary>
-/// The rtp.
-/// </summary>
-internal sealed class Rtp : IDisposable
+namespace DisCatSharp.VoiceNext.Codec
{
/// <summary>
- /// The header size.
- /// </summary>
- public const int HEADER_SIZE = 12;
-
- /// <summary>
- /// The rtp no extension.
- /// </summary>
- private const byte RTP_NO_EXTENSION = 0x80;
- /// <summary>
- /// The rtp extension.
- /// </summary>
- private const byte RTP_EXTENSION = 0x90;
- /// <summary>
- /// The rtp version.
- /// </summary>
- private const byte RTP_VERSION = 0x78;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Rtp"/> class.
+ /// The rtp.
/// </summary>
- public Rtp()
- { }
-
- /// <summary>
- /// Encodes the header.
- /// </summary>
- /// <param name="sequence">The sequence.</param>
- /// <param name="timestamp">The timestamp.</param>
- /// <param name="ssrc">The ssrc.</param>
- /// <param name="target">The target.</param>
- public void EncodeHeader(ushort sequence, uint timestamp, uint ssrc, Span<byte> target)
+ internal sealed class Rtp : IDisposable
{
- if (target.Length < HEADER_SIZE)
- throw new ArgumentException("Header buffer is too short.", nameof(target));
-
- target[0] = RTP_NO_EXTENSION;
- target[1] = RTP_VERSION;
+ /// <summary>
+ /// The header size.
+ /// </summary>
+ public const int HEADER_SIZE = 12;
+
+ /// <summary>
+ /// The rtp no extension.
+ /// </summary>
+ private const byte RTP_NO_EXTENSION = 0x80;
+ /// <summary>
+ /// The rtp extension.
+ /// </summary>
+ private const byte RTP_EXTENSION = 0x90;
+ /// <summary>
+ /// The rtp version.
+ /// </summary>
+ private const byte RTP_VERSION = 0x78;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Rtp"/> class.
+ /// </summary>
+ public Rtp()
+ { }
+
+ /// <summary>
+ /// Encodes the header.
+ /// </summary>
+ /// <param name="sequence">The sequence.</param>
+ /// <param name="timestamp">The timestamp.</param>
+ /// <param name="ssrc">The ssrc.</param>
+ /// <param name="target">The target.</param>
+ public void EncodeHeader(ushort sequence, uint timestamp, uint ssrc, Span<byte> target)
+ {
+ if (target.Length < HEADER_SIZE)
+ throw new ArgumentException("Header buffer is too short.", nameof(target));
- // Write data big endian
- BinaryPrimitives.WriteUInt16BigEndian(target[2..], sequence); // header + magic
- BinaryPrimitives.WriteUInt32BigEndian(target[4..], timestamp); // header + magic + sizeof(sequence)
- BinaryPrimitives.WriteUInt32BigEndian(target[8..], ssrc); // header + magic + sizeof(sequence) + sizeof(timestamp)
- }
+ target[0] = RTP_NO_EXTENSION;
+ target[1] = RTP_VERSION;
- /// <summary>
- /// Are the rtp header.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <returns>A bool.</returns>
- public bool IsRtpHeader(ReadOnlySpan<byte> source) => source.Length >= HEADER_SIZE && (source[0] == RTP_NO_EXTENSION || source[0] == RTP_EXTENSION) && source[1] == RTP_VERSION;
+ // Write data big endian
+ BinaryPrimitives.WriteUInt16BigEndian(target[2..], sequence); // header + magic
+ BinaryPrimitives.WriteUInt32BigEndian(target[4..], timestamp); // header + magic + sizeof(sequence)
+ BinaryPrimitives.WriteUInt32BigEndian(target[8..], ssrc); // header + magic + sizeof(sequence) + sizeof(timestamp)
+ }
- /// <summary>
- /// Decodes the header.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="sequence">The sequence.</param>
- /// <param name="timestamp">The timestamp.</param>
- /// <param name="ssrc">The ssrc.</param>
- /// <param name="hasExtension">If true, has extension.</param>
- public void DecodeHeader(ReadOnlySpan<byte> source, out ushort sequence, out uint timestamp, out uint ssrc, out bool hasExtension)
- {
- if (source.Length < HEADER_SIZE)
- throw new ArgumentException("Header buffer is too short.", nameof(source));
+ /// <summary>
+ /// Are the rtp header.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <returns>A bool.</returns>
+ public bool IsRtpHeader(ReadOnlySpan<byte> source) => source.Length >= HEADER_SIZE && (source[0] == RTP_NO_EXTENSION || source[0] == RTP_EXTENSION) && source[1] == RTP_VERSION;
+
+ /// <summary>
+ /// Decodes the header.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <param name="sequence">The sequence.</param>
+ /// <param name="timestamp">The timestamp.</param>
+ /// <param name="ssrc">The ssrc.</param>
+ /// <param name="hasExtension">If true, has extension.</param>
+ public void DecodeHeader(ReadOnlySpan<byte> source, out ushort sequence, out uint timestamp, out uint ssrc, out bool hasExtension)
+ {
+ if (source.Length < HEADER_SIZE)
+ throw new ArgumentException("Header buffer is too short.", nameof(source));
- if ((source[0] != RTP_NO_EXTENSION && source[0] != RTP_EXTENSION) || source[1] != RTP_VERSION)
- throw new ArgumentException("Invalid RTP header.", nameof(source));
+ if ((source[0] != RTP_NO_EXTENSION && source[0] != RTP_EXTENSION) || source[1] != RTP_VERSION)
+ throw new ArgumentException("Invalid RTP header.", nameof(source));
- hasExtension = source[0] == RTP_EXTENSION;
+ hasExtension = source[0] == RTP_EXTENSION;
- // Read data big endian
- sequence = BinaryPrimitives.ReadUInt16BigEndian(source[2..]);
- timestamp = BinaryPrimitives.ReadUInt32BigEndian(source[4..]);
- ssrc = BinaryPrimitives.ReadUInt32BigEndian(source[8..]);
- }
+ // Read data big endian
+ sequence = BinaryPrimitives.ReadUInt16BigEndian(source[2..]);
+ timestamp = BinaryPrimitives.ReadUInt32BigEndian(source[4..]);
+ ssrc = BinaryPrimitives.ReadUInt32BigEndian(source[8..]);
+ }
- /// <summary>
- /// Calculates the packet size.
- /// </summary>
- /// <param name="encryptedLength">The encrypted length.</param>
- /// <param name="encryptionMode">The encryption mode.</param>
- /// <returns>An int.</returns>
- public int CalculatePacketSize(int encryptedLength, EncryptionMode encryptionMode) =>
- encryptionMode switch
+ /// <summary>
+ /// Calculates the packet size.
+ /// </summary>
+ /// <param name="encryptedLength">The encrypted length.</param>
+ /// <param name="encryptionMode">The encryption mode.</param>
+ /// <returns>An int.</returns>
+ public int CalculatePacketSize(int encryptedLength, EncryptionMode encryptionMode) =>
+ encryptionMode switch
+ {
+ EncryptionMode.XSalsa20Poly1305 => HEADER_SIZE + encryptedLength,
+ EncryptionMode.XSalsa20Poly1305Suffix => HEADER_SIZE + encryptedLength + Interop.SodiumNonceSize,
+ EncryptionMode.XSalsa20Poly1305Lite => HEADER_SIZE + encryptedLength + 4,
+ _ => throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode)),
+ };
+
+ /// <summary>
+ /// Gets the data from packet.
+ /// </summary>
+ /// <param name="packet">The packet.</param>
+ /// <param name="data">The data.</param>
+ /// <param name="encryptionMode">The encryption mode.</param>
+ public void GetDataFromPacket(ReadOnlySpan<byte> packet, out ReadOnlySpan<byte> data, EncryptionMode encryptionMode)
{
- EncryptionMode.XSalsa20Poly1305 => HEADER_SIZE + encryptedLength,
- EncryptionMode.XSalsa20Poly1305Suffix => HEADER_SIZE + encryptedLength + Interop.SodiumNonceSize,
- EncryptionMode.XSalsa20Poly1305Lite => HEADER_SIZE + encryptedLength + 4,
- _ => throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode)),
- };
+ switch (encryptionMode)
+ {
+ case EncryptionMode.XSalsa20Poly1305:
+ data = packet[HEADER_SIZE..];
+ return;
+
+ case EncryptionMode.XSalsa20Poly1305Suffix:
+ data = packet.Slice(HEADER_SIZE, packet.Length - HEADER_SIZE - Interop.SodiumNonceSize);
+ return;
+
+ case EncryptionMode.XSalsa20Poly1305Lite:
+ data = packet.Slice(HEADER_SIZE, packet.Length - HEADER_SIZE - 4);
+ break;
+
+ default:
+ throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode));
+ }
+ }
- /// <summary>
- /// Gets the data from packet.
- /// </summary>
- /// <param name="packet">The packet.</param>
- /// <param name="data">The data.</param>
- /// <param name="encryptionMode">The encryption mode.</param>
- public void GetDataFromPacket(ReadOnlySpan<byte> packet, out ReadOnlySpan<byte> data, EncryptionMode encryptionMode)
- {
- switch (encryptionMode)
+ /// <summary>
+ /// Disposes the Rtp.
+ /// </summary>
+ public void Dispose()
{
- case EncryptionMode.XSalsa20Poly1305:
- data = packet[HEADER_SIZE..];
- return;
-
- case EncryptionMode.XSalsa20Poly1305Suffix:
- data = packet.Slice(HEADER_SIZE, packet.Length - HEADER_SIZE - Interop.SodiumNonceSize);
- return;
- case EncryptionMode.XSalsa20Poly1305Lite:
- data = packet.Slice(HEADER_SIZE, packet.Length - HEADER_SIZE - 4);
- break;
-
- default:
- throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode));
}
}
-
- /// <summary>
- /// Disposes the Rtp.
- /// </summary>
- public void Dispose()
- {
-
- }
}
diff --git a/DisCatSharp.VoiceNext/Codec/Sodium.cs b/DisCatSharp.VoiceNext/Codec/Sodium.cs
index b1b5e03a0..91892e1da 100644
--- a/DisCatSharp.VoiceNext/Codec/Sodium.cs
+++ b/DisCatSharp.VoiceNext/Codec/Sodium.cs
@@ -1,293 +1,294 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
-namespace DisCatSharp.VoiceNext.Codec;
-
-/// <summary>
-/// The sodium.
-/// </summary>
-internal sealed class Sodium : IDisposable
+namespace DisCatSharp.VoiceNext.Codec
{
/// <summary>
- /// Gets the supported modes.
- /// </summary>
- public static IReadOnlyDictionary<string, EncryptionMode> SupportedModes { get; }
-
- /// <summary>
- /// Gets the nonce size.
- /// </summary>
- public static int NonceSize => Interop.SodiumNonceSize;
-
- /// <summary>
- /// Gets the c s p r n g.
- /// </summary>
- private readonly RandomNumberGenerator _csprng;
-
- /// <summary>
- /// Gets the buffer.
- /// </summary>
- private readonly byte[] _buffer;
-
- /// <summary>
- /// Gets the key.
- /// </summary>
- private readonly ReadOnlyMemory<byte> _key;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Sodium"/> class.
+ /// The sodium.
/// </summary>
- static Sodium()
+ internal sealed class Sodium : IDisposable
{
- SupportedModes = new ReadOnlyDictionary<string, EncryptionMode>(new Dictionary<string, EncryptionMode>()
+ /// <summary>
+ /// Gets the supported modes.
+ /// </summary>
+ public static IReadOnlyDictionary<string, EncryptionMode> SupportedModes { get; }
+
+ /// <summary>
+ /// Gets the nonce size.
+ /// </summary>
+ public static int NonceSize => Interop.SodiumNonceSize;
+
+ /// <summary>
+ /// Gets the c s p r n g.
+ /// </summary>
+ private readonly RandomNumberGenerator _csprng;
+
+ /// <summary>
+ /// Gets the buffer.
+ /// </summary>
+ private readonly byte[] _buffer;
+
+ /// <summary>
+ /// Gets the key.
+ /// </summary>
+ private readonly ReadOnlyMemory<byte> _key;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Sodium"/> class.
+ /// </summary>
+ static Sodium()
{
- ["xsalsa20_poly1305_lite"] = EncryptionMode.XSalsa20Poly1305Lite,
- ["xsalsa20_poly1305_suffix"] = EncryptionMode.XSalsa20Poly1305Suffix,
- ["xsalsa20_poly1305"] = EncryptionMode.XSalsa20Poly1305
- });
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Sodium"/> class.
- /// </summary>
- /// <param name="key">The key.</param>
- public Sodium(ReadOnlyMemory<byte> key)
- {
- if (key.Length != Interop.SodiumKeySize)
- throw new ArgumentException($"Invalid Sodium key size. Key needs to have a length of {Interop.SodiumKeySize} bytes.", nameof(key));
-
- this._key = key;
-
- this._csprng = RandomNumberGenerator.Create();
- this._buffer = new byte[Interop.SodiumNonceSize];
- }
+ SupportedModes = new ReadOnlyDictionary<string, EncryptionMode>(new Dictionary<string, EncryptionMode>()
+ {
+ ["xsalsa20_poly1305_lite"] = EncryptionMode.XSalsa20Poly1305Lite,
+ ["xsalsa20_poly1305_suffix"] = EncryptionMode.XSalsa20Poly1305Suffix,
+ ["xsalsa20_poly1305"] = EncryptionMode.XSalsa20Poly1305
+ });
+ }
- /// <summary>
- /// Generates the nonce.
- /// </summary>
- /// <param name="rtpHeader">The rtp header.</param>
- /// <param name="target">The target.</param>
- public void GenerateNonce(ReadOnlySpan<byte> rtpHeader, Span<byte> target)
- {
- if (rtpHeader.Length != Rtp.HEADER_SIZE)
- throw new ArgumentException($"RTP header needs to have a length of exactly {Rtp.HEADER_SIZE} bytes.", nameof(rtpHeader));
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Sodium"/> class.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ public Sodium(ReadOnlyMemory<byte> key)
+ {
+ if (key.Length != Interop.SodiumKeySize)
+ throw new ArgumentException($"Invalid Sodium key size. Key needs to have a length of {Interop.SodiumKeySize} bytes.", nameof(key));
- if (target.Length != Interop.SodiumNonceSize)
- throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target));
+ this._key = key;
- // Write the header to the beginning of the span.
- rtpHeader.CopyTo(target);
+ this._csprng = RandomNumberGenerator.Create();
+ this._buffer = new byte[Interop.SodiumNonceSize];
+ }
- // Zero rest of the span.
- Helpers.ZeroFill(target[rtpHeader.Length..]);
- }
+ /// <summary>
+ /// Generates the nonce.
+ /// </summary>
+ /// <param name="rtpHeader">The rtp header.</param>
+ /// <param name="target">The target.</param>
+ public void GenerateNonce(ReadOnlySpan<byte> rtpHeader, Span<byte> target)
+ {
+ if (rtpHeader.Length != Rtp.HEADER_SIZE)
+ throw new ArgumentException($"RTP header needs to have a length of exactly {Rtp.HEADER_SIZE} bytes.", nameof(rtpHeader));
- /// <summary>
- /// Generates the nonce.
- /// </summary>
- /// <param name="target">The target.</param>
- public void GenerateNonce(Span<byte> target)
- {
- if (target.Length != Interop.SodiumNonceSize)
- throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target));
+ if (target.Length != Interop.SodiumNonceSize)
+ throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target));
- this._csprng.GetBytes(this._buffer);
- this._buffer.AsSpan().CopyTo(target);
- }
+ // Write the header to the beginning of the span.
+ rtpHeader.CopyTo(target);
- /// <summary>
- /// Generates the nonce.
- /// </summary>
- /// <param name="nonce">The nonce.</param>
- /// <param name="target">The target.</param>
- public void GenerateNonce(uint nonce, Span<byte> target)
- {
- if (target.Length != Interop.SodiumNonceSize)
- throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target));
+ // Zero rest of the span.
+ Helpers.ZeroFill(target[rtpHeader.Length..]);
+ }
- // Write the uint to memory
- BinaryPrimitives.WriteUInt32BigEndian(target, nonce);
+ /// <summary>
+ /// Generates the nonce.
+ /// </summary>
+ /// <param name="target">The target.</param>
+ public void GenerateNonce(Span<byte> target)
+ {
+ if (target.Length != Interop.SodiumNonceSize)
+ throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target));
- // Zero rest of the buffer.
- Helpers.ZeroFill(target[4..]);
- }
+ this._csprng.GetBytes(this._buffer);
+ this._buffer.AsSpan().CopyTo(target);
+ }
- /// <summary>
- /// Appends the nonce.
- /// </summary>
- /// <param name="nonce">The nonce.</param>
- /// <param name="target">The target.</param>
- /// <param name="encryptionMode">The encryption mode.</param>
- public void AppendNonce(ReadOnlySpan<byte> nonce, Span<byte> target, EncryptionMode encryptionMode)
- {
- switch (encryptionMode)
+ /// <summary>
+ /// Generates the nonce.
+ /// </summary>
+ /// <param name="nonce">The nonce.</param>
+ /// <param name="target">The target.</param>
+ public void GenerateNonce(uint nonce, Span<byte> target)
{
- case EncryptionMode.XSalsa20Poly1305:
- return;
-
- case EncryptionMode.XSalsa20Poly1305Suffix:
- nonce.CopyTo(target[^12..]);
- return;
+ if (target.Length != Interop.SodiumNonceSize)
+ throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target));
- case EncryptionMode.XSalsa20Poly1305Lite:
- nonce[..4].CopyTo(target[^4..]);
- return;
+ // Write the uint to memory
+ BinaryPrimitives.WriteUInt32BigEndian(target, nonce);
- default:
- throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode));
+ // Zero rest of the buffer.
+ Helpers.ZeroFill(target[4..]);
}
- }
- /// <summary>
- /// Gets the nonce.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="target">The target.</param>
- /// <param name="encryptionMode">The encryption mode.</param>
- public void GetNonce(ReadOnlySpan<byte> source, Span<byte> target, EncryptionMode encryptionMode)
- {
- if (target.Length != Interop.SodiumNonceSize)
- throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target));
+ /// <summary>
+ /// Appends the nonce.
+ /// </summary>
+ /// <param name="nonce">The nonce.</param>
+ /// <param name="target">The target.</param>
+ /// <param name="encryptionMode">The encryption mode.</param>
+ public void AppendNonce(ReadOnlySpan<byte> nonce, Span<byte> target, EncryptionMode encryptionMode)
+ {
+ switch (encryptionMode)
+ {
+ case EncryptionMode.XSalsa20Poly1305:
+ return;
+
+ case EncryptionMode.XSalsa20Poly1305Suffix:
+ nonce.CopyTo(target[^12..]);
+ return;
+
+ case EncryptionMode.XSalsa20Poly1305Lite:
+ nonce[..4].CopyTo(target[^4..]);
+ return;
+
+ default:
+ throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode));
+ }
+ }
- switch (encryptionMode)
+ /// <summary>
+ /// Gets the nonce.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <param name="target">The target.</param>
+ /// <param name="encryptionMode">The encryption mode.</param>
+ public void GetNonce(ReadOnlySpan<byte> source, Span<byte> target, EncryptionMode encryptionMode)
{
- case EncryptionMode.XSalsa20Poly1305:
- source[..12].CopyTo(target);
- return;
+ if (target.Length != Interop.SodiumNonceSize)
+ throw new ArgumentException($"Invalid nonce buffer size. Target buffer for the nonce needs to have a capacity of {Interop.SodiumNonceSize} bytes.", nameof(target));
+
+ switch (encryptionMode)
+ {
+ case EncryptionMode.XSalsa20Poly1305:
+ source[..12].CopyTo(target);
+ return;
+
+ case EncryptionMode.XSalsa20Poly1305Suffix:
+ source[^Interop.SodiumNonceSize..].CopyTo(target);
+ return;
+
+ case EncryptionMode.XSalsa20Poly1305Lite:
+ source[^4..].CopyTo(target);
+ return;
+
+ default:
+ throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode));
+ }
+ }
- case EncryptionMode.XSalsa20Poly1305Suffix:
- source[^Interop.SodiumNonceSize..].CopyTo(target);
- return;
+ /// <summary>
+ /// Encrypts the Sodium.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <param name="target">The target.</param>
+ /// <param name="nonce">The nonce.</param>
+ public void Encrypt(ReadOnlySpan<byte> source, Span<byte> target, ReadOnlySpan<byte> nonce)
+ {
+ if (nonce.Length != Interop.SodiumNonceSize)
+ throw new ArgumentException($"Invalid nonce size. Nonce needs to have a length of {Interop.SodiumNonceSize} bytes.", nameof(nonce));
- case EncryptionMode.XSalsa20Poly1305Lite:
- source[^4..].CopyTo(target);
- return;
+ if (target.Length != Interop.SodiumMacSize + source.Length)
+ throw new ArgumentException($"Invalid target buffer size. Target buffer needs to have a length that is a sum of input buffer length and Sodium MAC size ({Interop.SodiumMacSize} bytes).", nameof(target));
- default:
- throw new ArgumentException("Unsupported encryption mode.", nameof(encryptionMode));
+ int result;
+ if ((result = Interop.Encrypt(source, target, this._key.Span, nonce)) != 0)
+ throw new CryptographicException($"Could not encrypt the buffer. Sodium returned code {result}.");
}
- }
- /// <summary>
- /// Encrypts the Sodium.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="target">The target.</param>
- /// <param name="nonce">The nonce.</param>
- public void Encrypt(ReadOnlySpan<byte> source, Span<byte> target, ReadOnlySpan<byte> nonce)
- {
- if (nonce.Length != Interop.SodiumNonceSize)
- throw new ArgumentException($"Invalid nonce size. Nonce needs to have a length of {Interop.SodiumNonceSize} bytes.", nameof(nonce));
+ /// <summary>
+ /// Decrypts the Sodium.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <param name="target">The target.</param>
+ /// <param name="nonce">The nonce.</param>
+ public void Decrypt(ReadOnlySpan<byte> source, Span<byte> target, ReadOnlySpan<byte> nonce)
+ {
+ if (nonce.Length != Interop.SodiumNonceSize)
+ throw new ArgumentException($"Invalid nonce size. Nonce needs to have a length of {Interop.SodiumNonceSize} bytes.", nameof(nonce));
- if (target.Length != Interop.SodiumMacSize + source.Length)
- throw new ArgumentException($"Invalid target buffer size. Target buffer needs to have a length that is a sum of input buffer length and Sodium MAC size ({Interop.SodiumMacSize} bytes).", nameof(target));
+ if (target.Length != source.Length - Interop.SodiumMacSize)
+ throw new ArgumentException($"Invalid target buffer size. Target buffer needs to have a length that is input buffer decreased by Sodium MAC size ({Interop.SodiumMacSize} bytes).", nameof(target));
- int result;
- if ((result = Interop.Encrypt(source, target, this._key.Span, nonce)) != 0)
- throw new CryptographicException($"Could not encrypt the buffer. Sodium returned code {result}.");
- }
+ int result;
+ if ((result = Interop.Decrypt(source, target, this._key.Span, nonce)) != 0)
+ throw new CryptographicException($"Could not decrypt the buffer. Sodium returned code {result}.");
+ }
- /// <summary>
- /// Decrypts the Sodium.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <param name="target">The target.</param>
- /// <param name="nonce">The nonce.</param>
- public void Decrypt(ReadOnlySpan<byte> source, Span<byte> target, ReadOnlySpan<byte> nonce)
- {
- if (nonce.Length != Interop.SodiumNonceSize)
- throw new ArgumentException($"Invalid nonce size. Nonce needs to have a length of {Interop.SodiumNonceSize} bytes.", nameof(nonce));
+ /// <summary>
+ /// Disposes the Sodium.
+ /// </summary>
+ public void Dispose() => this._csprng.Dispose();
+
+ /// <summary>
+ /// Selects the mode.
+ /// </summary>
+ /// <param name="availableModes">The available modes.</param>
+ /// <returns>A KeyValuePair.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static KeyValuePair<string, EncryptionMode> SelectMode(IEnumerable<string> availableModes)
+ {
+ foreach (var kvMode in SupportedModes)
+ if (availableModes.Contains(kvMode.Key))
+ return kvMode;
- if (target.Length != source.Length - Interop.SodiumMacSize)
- throw new ArgumentException($"Invalid target buffer size. Target buffer needs to have a length that is input buffer decreased by Sodium MAC size ({Interop.SodiumMacSize} bytes).", nameof(target));
+ throw new CryptographicException("Could not negotiate Sodium encryption modes, as none of the modes offered by Discord are supported. This is usually an indicator that something went very wrong.");
+ }
- int result;
- if ((result = Interop.Decrypt(source, target, this._key.Span, nonce)) != 0)
- throw new CryptographicException($"Could not decrypt the buffer. Sodium returned code {result}.");
+ /// <summary>
+ /// Calculates the target size.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <returns>An int.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateTargetSize(ReadOnlySpan<byte> source)
+ => source.Length + Interop.SodiumMacSize;
+
+ /// <summary>
+ /// Calculates the source size.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <returns>An int.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int CalculateSourceSize(ReadOnlySpan<byte> source)
+ => source.Length - Interop.SodiumMacSize;
}
/// <summary>
- /// Disposes the Sodium.
- /// </summary>
- public void Dispose() => this._csprng.Dispose();
-
- /// <summary>
- /// Selects the mode.
+ /// Specifies an encryption mode to use with Sodium.
/// </summary>
- /// <param name="availableModes">The available modes.</param>
- /// <returns>A KeyValuePair.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static KeyValuePair<string, EncryptionMode> SelectMode(IEnumerable<string> availableModes)
+ public enum EncryptionMode
{
- foreach (var kvMode in SupportedModes)
- if (availableModes.Contains(kvMode.Key))
- return kvMode;
-
- throw new CryptographicException("Could not negotiate Sodium encryption modes, as none of the modes offered by Discord are supported. This is usually an indicator that something went very wrong.");
+ /// <summary>
+ /// The nonce is an incrementing uint32 value. It is encoded as big endian value at the beginning of the nonce buffer. The 4 bytes are also appended at the end of the packet.
+ /// </summary>
+ XSalsa20Poly1305Lite,
+
+ /// <summary>
+ /// The nonce consists of random bytes. It is appended at the end of a packet.
+ /// </summary>
+ XSalsa20Poly1305Suffix,
+
+ /// <summary>
+ /// The nonce consists of the RTP header. Nothing is appended to the packet.
+ /// </summary>
+ XSalsa20Poly1305
}
-
- /// <summary>
- /// Calculates the target size.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <returns>An int.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateTargetSize(ReadOnlySpan<byte> source)
- => source.Length + Interop.SodiumMacSize;
-
- /// <summary>
- /// Calculates the source size.
- /// </summary>
- /// <param name="source">The source.</param>
- /// <returns>An int.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int CalculateSourceSize(ReadOnlySpan<byte> source)
- => source.Length - Interop.SodiumMacSize;
-}
-
-/// <summary>
-/// Specifies an encryption mode to use with Sodium.
-/// </summary>
-public enum EncryptionMode
-{
- /// <summary>
- /// The nonce is an incrementing uint32 value. It is encoded as big endian value at the beginning of the nonce buffer. The 4 bytes are also appended at the end of the packet.
- /// </summary>
- XSalsa20Poly1305Lite,
-
- /// <summary>
- /// The nonce consists of random bytes. It is appended at the end of a packet.
- /// </summary>
- XSalsa20Poly1305Suffix,
-
- /// <summary>
- /// The nonce consists of the RTP header. Nothing is appended to the packet.
- /// </summary>
- XSalsa20Poly1305
}
diff --git a/DisCatSharp.VoiceNext/DiscordClientExtensions.cs b/DisCatSharp.VoiceNext/DiscordClientExtensions.cs
index 738aa4d5b..8a92f0157 100644
--- a/DisCatSharp.VoiceNext/DiscordClientExtensions.cs
+++ b/DisCatSharp.VoiceNext/DiscordClientExtensions.cs
@@ -1,139 +1,140 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
using DisCatSharp.Entities;
-namespace DisCatSharp.VoiceNext;
-
-/// <summary>
-/// The discord client extensions.
-/// </summary>
-public static class DiscordClientExtensions
+namespace DisCatSharp.VoiceNext
{
/// <summary>
- /// Creates a new VoiceNext client with default settings.
- /// </summary>
- /// <param name="client">Discord client to create VoiceNext instance for.</param>
- /// <returns>VoiceNext client instance.</returns>
- public static VoiceNextExtension UseVoiceNext(this DiscordClient client)
- => UseVoiceNext(client, new VoiceNextConfiguration());
-
- /// <summary>
- /// Creates a new VoiceNext client with specified settings.
- /// </summary>
- /// <param name="client">Discord client to create VoiceNext instance for.</param>
- /// <param name="config">Configuration for the VoiceNext client.</param>
- /// <returns>VoiceNext client instance.</returns>
- public static VoiceNextExtension UseVoiceNext(this DiscordClient client, VoiceNextConfiguration config)
- {
- if (client.GetExtension<VoiceNextExtension>() != null)
- throw new InvalidOperationException("VoiceNext is already enabled for that client.");
-
- var vnext = new VoiceNextExtension(config);
- client.AddExtension(vnext);
- return vnext;
- }
-
- /// <summary>
- /// Creates new VoiceNext clients on all shards in a given sharded client.
+ /// The discord client extensions.
/// </summary>
- /// <param name="client">Discord sharded client to create VoiceNext instances for.</param>
- /// <param name="config">Configuration for the VoiceNext clients.</param>
- /// <returns>A dictionary of created VoiceNext clients.</returns>
- public static async Task<IReadOnlyDictionary<int, VoiceNextExtension>> UseVoiceNextAsync(this DiscordShardedClient client, VoiceNextConfiguration config)
+ public static class DiscordClientExtensions
{
- var modules = new Dictionary<int, VoiceNextExtension>();
- await client.InitializeShardsAsync().ConfigureAwait(false);
-
- foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ /// <summary>
+ /// Creates a new VoiceNext client with default settings.
+ /// </summary>
+ /// <param name="client">Discord client to create VoiceNext instance for.</param>
+ /// <returns>VoiceNext client instance.</returns>
+ public static VoiceNextExtension UseVoiceNext(this DiscordClient client)
+ => UseVoiceNext(client, new VoiceNextConfiguration());
+
+ /// <summary>
+ /// Creates a new VoiceNext client with specified settings.
+ /// </summary>
+ /// <param name="client">Discord client to create VoiceNext instance for.</param>
+ /// <param name="config">Configuration for the VoiceNext client.</param>
+ /// <returns>VoiceNext client instance.</returns>
+ public static VoiceNextExtension UseVoiceNext(this DiscordClient client, VoiceNextConfiguration config)
{
- var vnext = shard.GetExtension<VoiceNextExtension>();
- if (vnext == null)
- vnext = shard.UseVoiceNext(config);
+ if (client.GetExtension<VoiceNextExtension>() != null)
+ throw new InvalidOperationException("VoiceNext is already enabled for that client.");
- modules[shard.ShardId] = vnext;
+ var vnext = new VoiceNextExtension(config);
+ client.AddExtension(vnext);
+ return vnext;
}
- return new ReadOnlyDictionary<int, VoiceNextExtension>(modules);
- }
+ /// <summary>
+ /// Creates new VoiceNext clients on all shards in a given sharded client.
+ /// </summary>
+ /// <param name="client">Discord sharded client to create VoiceNext instances for.</param>
+ /// <param name="config">Configuration for the VoiceNext clients.</param>
+ /// <returns>A dictionary of created VoiceNext clients.</returns>
+ public static async Task<IReadOnlyDictionary<int, VoiceNextExtension>> UseVoiceNextAsync(this DiscordShardedClient client, VoiceNextConfiguration config)
+ {
+ var modules = new Dictionary<int, VoiceNextExtension>();
+ await client.InitializeShardsAsync().ConfigureAwait(false);
- /// <summary>
- /// Gets the active instance of VoiceNext client for the DiscordClient.
- /// </summary>
- /// <param name="client">Discord client to get VoiceNext instance for.</param>
- /// <returns>VoiceNext client instance.</returns>
- public static VoiceNextExtension GetVoiceNext(this DiscordClient client)
- => client.GetExtension<VoiceNextExtension>();
+ foreach (var shard in client.ShardClients.Select(xkvp => xkvp.Value))
+ {
+ var vnext = shard.GetExtension<VoiceNextExtension>();
+ if (vnext == null)
+ vnext = shard.UseVoiceNext(config);
- /// <summary>
- /// Retrieves a <see cref="VoiceNextExtension"/> instance for each shard.
- /// </summary>
- /// <param name="client">The shard client to retrieve <see cref="VoiceNextExtension"/> instances from.</param>
- /// <returns>A dictionary containing <see cref="VoiceNextExtension"/> instances for each shard.</returns>
- public static async Task<IReadOnlyDictionary<int, VoiceNextExtension>> GetVoiceNextAsync(this DiscordShardedClient client)
- {
- await client.InitializeShardsAsync().ConfigureAwait(false);
- var extensions = new Dictionary<int, VoiceNextExtension>();
+ modules[shard.ShardId] = vnext;
+ }
- foreach (var shard in client.ShardClients.Values)
- {
- extensions.Add(shard.ShardId, shard.GetExtension<VoiceNextExtension>());
+ return new ReadOnlyDictionary<int, VoiceNextExtension>(modules);
}
- return new ReadOnlyDictionary<int, VoiceNextExtension>(extensions);
- }
+ /// <summary>
+ /// Gets the active instance of VoiceNext client for the DiscordClient.
+ /// </summary>
+ /// <param name="client">Discord client to get VoiceNext instance for.</param>
+ /// <returns>VoiceNext client instance.</returns>
+ public static VoiceNextExtension GetVoiceNext(this DiscordClient client)
+ => client.GetExtension<VoiceNextExtension>();
+
+ /// <summary>
+ /// Retrieves a <see cref="VoiceNextExtension"/> instance for each shard.
+ /// </summary>
+ /// <param name="client">The shard client to retrieve <see cref="VoiceNextExtension"/> instances from.</param>
+ /// <returns>A dictionary containing <see cref="VoiceNextExtension"/> instances for each shard.</returns>
+ public static async Task<IReadOnlyDictionary<int, VoiceNextExtension>> GetVoiceNextAsync(this DiscordShardedClient client)
+ {
+ await client.InitializeShardsAsync().ConfigureAwait(false);
+ var extensions = new Dictionary<int, VoiceNextExtension>();
- /// <summary>
- /// Connects to this voice channel using VoiceNext.
- /// </summary>
- /// <param name="channel">Channel to connect to.</param>
- /// <returns>If successful, the VoiceNext connection.</returns>
- public static Task<VoiceNextConnection> ConnectAsync(this DiscordChannel channel)
- {
- if (channel == null)
- throw new NullReferenceException();
+ foreach (var shard in client.ShardClients.Values)
+ {
+ extensions.Add(shard.ShardId, shard.GetExtension<VoiceNextExtension>());
+ }
+
+ return new ReadOnlyDictionary<int, VoiceNextExtension>(extensions);
+ }
- if (channel.Guild == null)
- throw new InvalidOperationException("VoiceNext can only be used with guild channels.");
+ /// <summary>
+ /// Connects to this voice channel using VoiceNext.
+ /// </summary>
+ /// <param name="channel">Channel to connect to.</param>
+ /// <returns>If successful, the VoiceNext connection.</returns>
+ public static Task<VoiceNextConnection> ConnectAsync(this DiscordChannel channel)
+ {
+ if (channel == null)
+ throw new NullReferenceException();
- if (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage)
- throw new InvalidOperationException("You can only connect to voice or stage channels.");
+ if (channel.Guild == null)
+ throw new InvalidOperationException("VoiceNext can only be used with guild channels.");
- if (channel.Discord is not DiscordClient discord || discord == null)
- throw new NullReferenceException();
+ if (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage)
+ throw new InvalidOperationException("You can only connect to voice or stage channels.");
- var vnext = discord.GetVoiceNext();
- if (vnext == null)
- throw new InvalidOperationException("VoiceNext is not initialized for this Discord client.");
+ if (channel.Discord is not DiscordClient discord || discord == null)
+ throw new NullReferenceException();
- var vnc = vnext.GetConnection(channel.Guild);
- return vnc != null
- ? throw new InvalidOperationException("VoiceNext is already connected in this guild.")
- : vnext.ConnectAsync(channel);
+ var vnext = discord.GetVoiceNext();
+ if (vnext == null)
+ throw new InvalidOperationException("VoiceNext is not initialized for this Discord client.");
+
+ var vnc = vnext.GetConnection(channel.Guild);
+ return vnc != null
+ ? throw new InvalidOperationException("VoiceNext is already connected in this guild.")
+ : vnext.ConnectAsync(channel);
+ }
}
}
diff --git a/DisCatSharp.VoiceNext/Entities/AudioSender.cs b/DisCatSharp.VoiceNext/Entities/AudioSender.cs
index 5de144ba6..19e924bbd 100644
--- a/DisCatSharp.VoiceNext/Entities/AudioSender.cs
+++ b/DisCatSharp.VoiceNext/Entities/AudioSender.cs
@@ -1,167 +1,168 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.VoiceNext.Codec;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The audio sender.
-/// </summary>
-internal class AudioSender : IDisposable
+namespace DisCatSharp.VoiceNext.Entities
{
- // starting the counter a full wrap ahead handles an edge case where the VERY first packets
- // we see are right around the wraparound line.
- private ulong _sequenceBase = 1 << 16;
-
- private SequenceWrapState _currentSequenceWrapState = SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder;
-
- private enum SequenceWrapState
- {
- Normal,
- AssumeNextLowSequenceIsOverflow,
- AssumeNextHighSequenceIsOutOfOrder,
- }
-
- /// <summary>
- /// Gets the s s r c.
- /// </summary>
- public uint Ssrc { get; }
-
- /// <summary>
- /// Gets the id.
- /// </summary>
- public ulong Id => this.User?.Id ?? 0;
-
- /// <summary>
- /// Gets the decoder.
- /// </summary>
- public OpusDecoder Decoder { get; }
-
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- public DiscordUser User { get; set; } = null;
-
- /// <summary>
- /// Gets or sets the last sequence.
- /// </summary>
- public ulong? LastTrueSequence { get; set; } = null;
-
/// <summary>
- /// Initializes a new instance of the <see cref="AudioSender"/> class.
+ /// The audio sender.
/// </summary>
- /// <param name="ssrc">The ssrc.</param>
- /// <param name="decoder">The decoder.</param>
- public AudioSender(uint ssrc, OpusDecoder decoder)
+ internal class AudioSender : IDisposable
{
- this.Ssrc = ssrc;
- this.Decoder = decoder;
- }
+ // starting the counter a full wrap ahead handles an edge case where the VERY first packets
+ // we see are right around the wraparound line.
+ private ulong _sequenceBase = 1 << 16;
+
+ private SequenceWrapState _currentSequenceWrapState = SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder;
- /// <summary>
- /// Disposes .
- /// </summary>
- public void Dispose() => this.Decoder?.Dispose();
+ private enum SequenceWrapState
+ {
+ Normal,
+ AssumeNextLowSequenceIsOverflow,
+ AssumeNextHighSequenceIsOutOfOrder,
+ }
+
+ /// <summary>
+ /// Gets the s s r c.
+ /// </summary>
+ public uint Ssrc { get; }
+
+ /// <summary>
+ /// Gets the id.
+ /// </summary>
+ public ulong Id => this.User?.Id ?? 0;
+
+ /// <summary>
+ /// Gets the decoder.
+ /// </summary>
+ public OpusDecoder Decoder { get; }
+
+ /// <summary>
+ /// Gets or sets the user.
+ /// </summary>
+ public DiscordUser User { get; set; } = null;
+
+ /// <summary>
+ /// Gets or sets the last sequence.
+ /// </summary>
+ public ulong? LastTrueSequence { get; set; } = null;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AudioSender"/> class.
+ /// </summary>
+ /// <param name="ssrc">The ssrc.</param>
+ /// <param name="decoder">The decoder.</param>
+ public AudioSender(uint ssrc, OpusDecoder decoder)
+ {
+ this.Ssrc = ssrc;
+ this.Decoder = decoder;
+ }
+ /// <summary>
+ /// Disposes .
+ /// </summary>
+ public void Dispose() => this.Decoder?.Dispose();
- /// <summary>
- /// Accepts the 16-bit sequence number from the next RTP header in the associated stream and
- /// uses heuristics to (attempt to) convert it into a 64-bit counter that takes into account
- /// overflow wrapping around to zero.
- /// <para/>
- /// This method only works properly if it is called for <b>every</b> sequence number that we
- /// see in the stream.
- /// </summary>
- /// <param name="originalSequence">
- /// The 16-bit sequence number from the next RTP header.
- /// </param>
- /// <returns>
- /// Our best-effort guess of the value that <paramref name="originalSequence"/> <b>would</b>
- /// have been, if the server had given us a 64-bit integer instead of a 16-bit one.
- /// </returns>
- public ulong GetTrueSequenceAfterWrapping(ushort originalSequence)
- {
- // section off a smallish zone at either end of the 16-bit integer range. whenever the
- // sequence numbers creep into the higher zone, we start keeping an eye out for when
- // sequence numbers suddenly start showing up in the lower zone. we expect this to mean
- // that the sequence numbers overflowed and wrapped around. there's a bit of a balance
- // when determining an appropriate size for the buffer zone: if it's too small, then a
- // brief (but recoverable) network interruption could cause us to miss the lead-up to
- // the overflow. on the other hand, if it's too large, then such a network interruption
- // could cause us to misinterpret a normal sequence for one that's out-of-order.
- //
- // at 20 milliseconds per packet, 3,000 packets means that the buffer zone is one minute
- // on either side. in other words, as long as we're getting packets delivered within a
- // minute or so of when they should be, the 64-bit sequence numbers coming out of this
- // method will be perfectly consistent with reality.
- const ushort OVERFLOW_BUFFER_ZONE = 3_000;
- const ushort LOW_THRESHOLD = OVERFLOW_BUFFER_ZONE;
- const ushort HIGH_THRESHOLD = ushort.MaxValue - OVERFLOW_BUFFER_ZONE;
- ulong wrappingAdjustment = 0;
- switch (this._currentSequenceWrapState)
+ /// <summary>
+ /// Accepts the 16-bit sequence number from the next RTP header in the associated stream and
+ /// uses heuristics to (attempt to) convert it into a 64-bit counter that takes into account
+ /// overflow wrapping around to zero.
+ /// <para/>
+ /// This method only works properly if it is called for <b>every</b> sequence number that we
+ /// see in the stream.
+ /// </summary>
+ /// <param name="originalSequence">
+ /// The 16-bit sequence number from the next RTP header.
+ /// </param>
+ /// <returns>
+ /// Our best-effort guess of the value that <paramref name="originalSequence"/> <b>would</b>
+ /// have been, if the server had given us a 64-bit integer instead of a 16-bit one.
+ /// </returns>
+ public ulong GetTrueSequenceAfterWrapping(ushort originalSequence)
{
- case SequenceWrapState.Normal when originalSequence > HIGH_THRESHOLD:
- // we were going about our business up to this point. the sequence numbers have
- // gotten a bit high, so let's start looking out for any sequence numbers that
- // are suddenly WAY lower than where they are right now.
- this._currentSequenceWrapState = SequenceWrapState.AssumeNextLowSequenceIsOverflow;
- break;
+ // section off a smallish zone at either end of the 16-bit integer range. whenever the
+ // sequence numbers creep into the higher zone, we start keeping an eye out for when
+ // sequence numbers suddenly start showing up in the lower zone. we expect this to mean
+ // that the sequence numbers overflowed and wrapped around. there's a bit of a balance
+ // when determining an appropriate size for the buffer zone: if it's too small, then a
+ // brief (but recoverable) network interruption could cause us to miss the lead-up to
+ // the overflow. on the other hand, if it's too large, then such a network interruption
+ // could cause us to misinterpret a normal sequence for one that's out-of-order.
+ //
+ // at 20 milliseconds per packet, 3,000 packets means that the buffer zone is one minute
+ // on either side. in other words, as long as we're getting packets delivered within a
+ // minute or so of when they should be, the 64-bit sequence numbers coming out of this
+ // method will be perfectly consistent with reality.
+ const ushort OVERFLOW_BUFFER_ZONE = 3_000;
+ const ushort LOW_THRESHOLD = OVERFLOW_BUFFER_ZONE;
+ const ushort HIGH_THRESHOLD = ushort.MaxValue - OVERFLOW_BUFFER_ZONE;
- case SequenceWrapState.AssumeNextLowSequenceIsOverflow when originalSequence < LOW_THRESHOLD:
- // we had seen some sequence numbers that got a bit high, and now we see this
- // sequence number that's WAY lower than before. this is a classic sign that
- // the sequence numbers have wrapped around. in order to present a consistently
- // increasing "true" sequence number, add another 65,536 and keep counting. if
- // we see another high sequence number in the near future, assume that it's a
- // packet coming in out of order.
- this._sequenceBase += 1 << 16;
- this._currentSequenceWrapState = SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder;
- break;
+ ulong wrappingAdjustment = 0;
+ switch (this._currentSequenceWrapState)
+ {
+ case SequenceWrapState.Normal when originalSequence > HIGH_THRESHOLD:
+ // we were going about our business up to this point. the sequence numbers have
+ // gotten a bit high, so let's start looking out for any sequence numbers that
+ // are suddenly WAY lower than where they are right now.
+ this._currentSequenceWrapState = SequenceWrapState.AssumeNextLowSequenceIsOverflow;
+ break;
- case SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder when originalSequence > HIGH_THRESHOLD:
- // we're seeing some high sequence numbers EITHER at the beginning of the stream
- // OR very close to the time when we saw some very low sequence numbers. in the
- // latter case, it happened because the packets came in out of order, right when
- // the sequence numbers wrapped around. in the former case, we MIGHT be in the
- // same kind of situation (we can't tell yet), so we err on the side of caution
- // and burn a full cycle before we start counting so that we can handle both
- // cases with the exact same adjustment.
- wrappingAdjustment = 1 << 16;
- break;
+ case SequenceWrapState.AssumeNextLowSequenceIsOverflow when originalSequence < LOW_THRESHOLD:
+ // we had seen some sequence numbers that got a bit high, and now we see this
+ // sequence number that's WAY lower than before. this is a classic sign that
+ // the sequence numbers have wrapped around. in order to present a consistently
+ // increasing "true" sequence number, add another 65,536 and keep counting. if
+ // we see another high sequence number in the near future, assume that it's a
+ // packet coming in out of order.
+ this._sequenceBase += 1 << 16;
+ this._currentSequenceWrapState = SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder;
+ break;
- case SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder when originalSequence > LOW_THRESHOLD:
- // EITHER we're at the very beginning of the stream OR very close to the time
- // when we saw some very low sequence numbers. either way, we're out of the
- // zones where we should consider very low sequence numbers to come AFTER very
- // high ones, so we can go back to normal now.
- this._currentSequenceWrapState = SequenceWrapState.Normal;
- break;
- }
+ case SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder when originalSequence > HIGH_THRESHOLD:
+ // we're seeing some high sequence numbers EITHER at the beginning of the stream
+ // OR very close to the time when we saw some very low sequence numbers. in the
+ // latter case, it happened because the packets came in out of order, right when
+ // the sequence numbers wrapped around. in the former case, we MIGHT be in the
+ // same kind of situation (we can't tell yet), so we err on the side of caution
+ // and burn a full cycle before we start counting so that we can handle both
+ // cases with the exact same adjustment.
+ wrappingAdjustment = 1 << 16;
+ break;
+
+ case SequenceWrapState.AssumeNextHighSequenceIsOutOfOrder when originalSequence > LOW_THRESHOLD:
+ // EITHER we're at the very beginning of the stream OR very close to the time
+ // when we saw some very low sequence numbers. either way, we're out of the
+ // zones where we should consider very low sequence numbers to come AFTER very
+ // high ones, so we can go back to normal now.
+ this._currentSequenceWrapState = SequenceWrapState.Normal;
+ break;
+ }
- return this._sequenceBase + originalSequence - wrappingAdjustment;
+ return this._sequenceBase + originalSequence - wrappingAdjustment;
+ }
}
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceDispatch.cs b/DisCatSharp.VoiceNext/Entities/VoiceDispatch.cs
index f409c139f..14db5246e 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceDispatch.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceDispatch.cs
@@ -1,55 +1,56 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice dispatch.
-/// </summary>
-internal sealed class VoiceDispatch
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets the op code.
+ /// The voice dispatch.
/// </summary>
- [JsonProperty("op")]
- public int OpCode { get; set; }
+ internal sealed class VoiceDispatch
+ {
+ /// <summary>
+ /// Gets or sets the op code.
+ /// </summary>
+ [JsonProperty("op")]
+ public int OpCode { get; set; }
- /// <summary>
- /// Gets or sets the payload.
- /// </summary>
- [JsonProperty("d")]
- public object Payload { get; set; }
+ /// <summary>
+ /// Gets or sets the payload.
+ /// </summary>
+ [JsonProperty("d")]
+ public object Payload { get; set; }
- /// <summary>
- /// Gets or sets the sequence.
- /// </summary>
- [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)]
- public int? Sequence { get; set; }
+ /// <summary>
+ /// Gets or sets the sequence.
+ /// </summary>
+ [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Sequence { get; set; }
- /// <summary>
- /// Gets or sets the event name.
- /// </summary>
- [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)]
- public string EventName { get; set; }
+ /// <summary>
+ /// Gets or sets the event name.
+ /// </summary>
+ [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)]
+ public string EventName { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceIdentifyPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceIdentifyPayload.cs
index f90d38c0a..d0e3413cd 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceIdentifyPayload.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceIdentifyPayload.cs
@@ -1,55 +1,56 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice identify payload.
-/// </summary>
-internal sealed class VoiceIdentifyPayload
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets the server id.
+ /// The voice identify payload.
/// </summary>
- [JsonProperty("server_id")]
- public ulong ServerId { get; set; }
+ internal sealed class VoiceIdentifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the server id.
+ /// </summary>
+ [JsonProperty("server_id")]
+ public ulong ServerId { get; set; }
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? UserId { get; set; }
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? UserId { get; set; }
- /// <summary>
- /// Gets or sets the session id.
- /// </summary>
- [JsonProperty("session_id")]
- public string SessionId { get; set; }
+ /// <summary>
+ /// Gets or sets the session id.
+ /// </summary>
+ [JsonProperty("session_id")]
+ public string SessionId { get; set; }
- /// <summary>
- /// Gets or sets the token.
- /// </summary>
- [JsonProperty("token")]
- public string Token { get; set; }
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ [JsonProperty("token")]
+ public string Token { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoicePacket.cs b/DisCatSharp.VoiceNext/Entities/VoicePacket.cs
index ce14c2072..8b91837c6 100644
--- a/DisCatSharp.VoiceNext/Entities/VoicePacket.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoicePacket.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.VoiceNext.Entities;
-
-internal struct VoicePacket
+namespace DisCatSharp.VoiceNext.Entities
{
- /// <summary>
- /// Gets the bytes.
- /// </summary>
- public ReadOnlyMemory<byte> Bytes { get; }
- /// <summary>
- /// Gets the millisecond duration.
- /// </summary>
- public int MillisecondDuration { get; }
- /// <summary>
- /// Gets or sets a value indicating whether is silence.
- /// </summary>
- public bool IsSilence { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="VoicePacket"/> class.
- /// </summary>
- /// <param name="bytes">The bytes.</param>
- /// <param name="msDuration">The ms duration.</param>
- /// <param name="isSilence">If true, is silence.</param>
- public VoicePacket(ReadOnlyMemory<byte> bytes, int msDuration, bool isSilence = false)
+ internal struct VoicePacket
{
- this.Bytes = bytes;
- this.MillisecondDuration = msDuration;
- this.IsSilence = isSilence;
+ /// <summary>
+ /// Gets the bytes.
+ /// </summary>
+ public ReadOnlyMemory<byte> Bytes { get; }
+ /// <summary>
+ /// Gets the millisecond duration.
+ /// </summary>
+ public int MillisecondDuration { get; }
+ /// <summary>
+ /// Gets or sets a value indicating whether is silence.
+ /// </summary>
+ public bool IsSilence { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VoicePacket"/> class.
+ /// </summary>
+ /// <param name="bytes">The bytes.</param>
+ /// <param name="msDuration">The ms duration.</param>
+ /// <param name="isSilence">If true, is silence.</param>
+ public VoicePacket(ReadOnlyMemory<byte> bytes, int msDuration, bool isSilence = false)
+ {
+ this.Bytes = bytes;
+ this.MillisecondDuration = msDuration;
+ this.IsSilence = isSilence;
+ }
}
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceReadyPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceReadyPayload.cs
index c4babedac..2ed16bd7b 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceReadyPayload.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceReadyPayload.cs
@@ -1,63 +1,64 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice ready payload.
-/// </summary>
-internal sealed class VoiceReadyPayload
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets the s s r c.
+ /// The voice ready payload.
/// </summary>
- [JsonProperty("ssrc")]
- public uint Ssrc { get; set; }
+ internal sealed class VoiceReadyPayload
+ {
+ /// <summary>
+ /// Gets or sets the s s r c.
+ /// </summary>
+ [JsonProperty("ssrc")]
+ public uint Ssrc { get; set; }
- /// <summary>
- /// Gets or sets the address.
- /// </summary>
- [JsonProperty("ip")]
- public string Address { get; set; }
+ /// <summary>
+ /// Gets or sets the address.
+ /// </summary>
+ [JsonProperty("ip")]
+ public string Address { get; set; }
- /// <summary>
- /// Gets or sets the port.
- /// </summary>
- [JsonProperty("port")]
- public ushort Port { get; set; }
+ /// <summary>
+ /// Gets or sets the port.
+ /// </summary>
+ [JsonProperty("port")]
+ public ushort Port { get; set; }
- /// <summary>
- /// Gets or sets the modes.
- /// </summary>
- [JsonProperty("modes")]
- public IReadOnlyList<string> Modes { get; set; }
+ /// <summary>
+ /// Gets or sets the modes.
+ /// </summary>
+ [JsonProperty("modes")]
+ public IReadOnlyList<string> Modes { get; set; }
- /// <summary>
- /// Gets or sets the heartbeat interval.
- /// </summary>
- [JsonProperty("heartbeat_interval")]
- public int HeartbeatInterval { get; set; }
+ /// <summary>
+ /// Gets or sets the heartbeat interval.
+ /// </summary>
+ [JsonProperty("heartbeat_interval")]
+ public int HeartbeatInterval { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayload.cs
index ecc967498..7529cd1fc 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayload.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayload.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice select protocol payload.
-/// </summary>
-internal sealed class VoiceSelectProtocolPayload
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets the protocol.
+ /// The voice select protocol payload.
/// </summary>
- [JsonProperty("protocol")]
- public string Protocol { get; set; }
+ internal sealed class VoiceSelectProtocolPayload
+ {
+ /// <summary>
+ /// Gets or sets the protocol.
+ /// </summary>
+ [JsonProperty("protocol")]
+ public string Protocol { get; set; }
- /// <summary>
- /// Gets or sets the data.
- /// </summary>
- [JsonProperty("data")]
- public VoiceSelectProtocolPayloadData Data { get; set; }
+ /// <summary>
+ /// Gets or sets the data.
+ /// </summary>
+ [JsonProperty("data")]
+ public VoiceSelectProtocolPayloadData Data { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayloadData.cs b/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayloadData.cs
index bf67f27d1..dcbc2b7f2 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayloadData.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceSelectProtocolPayloadData.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice select protocol payload data.
-/// </summary>
-internal class VoiceSelectProtocolPayloadData
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets the address.
+ /// The voice select protocol payload data.
/// </summary>
- [JsonProperty("address")]
- public string Address { get; set; }
+ internal class VoiceSelectProtocolPayloadData
+ {
+ /// <summary>
+ /// Gets or sets the address.
+ /// </summary>
+ [JsonProperty("address")]
+ public string Address { get; set; }
- /// <summary>
- /// Gets or sets the port.
- /// </summary>
- [JsonProperty("port")]
- public ushort Port { get; set; }
+ /// <summary>
+ /// Gets or sets the port.
+ /// </summary>
+ [JsonProperty("port")]
+ public ushort Port { get; set; }
- /// <summary>
- /// Gets or sets the mode.
- /// </summary>
- [JsonProperty("mode")]
- public string Mode { get; set; }
+ /// <summary>
+ /// Gets or sets the mode.
+ /// </summary>
+ [JsonProperty("mode")]
+ public string Mode { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceServerUpdatePayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceServerUpdatePayload.cs
index 572a77e2d..3679cf2ca 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceServerUpdatePayload.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceServerUpdatePayload.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice server update payload.
-/// </summary>
-internal sealed class VoiceServerUpdatePayload
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets the token.
+ /// The voice server update payload.
/// </summary>
- [JsonProperty("token")]
- public string Token { get; set; }
+ internal sealed class VoiceServerUpdatePayload
+ {
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ [JsonProperty("token")]
+ public string Token { get; set; }
- /// <summary>
- /// Gets or sets the guild id.
- /// </summary>
- [JsonProperty("guild_id")]
- public ulong GuildId { get; set; }
+ /// <summary>
+ /// Gets or sets the guild id.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
- /// <summary>
- /// Gets or sets the endpoint.
- /// </summary>
- [JsonProperty("endpoint")]
- public string Endpoint { get; set; }
+ /// <summary>
+ /// Gets or sets the endpoint.
+ /// </summary>
+ [JsonProperty("endpoint")]
+ public string Endpoint { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceSessionDescriptionPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceSessionDescriptionPayload.cs
index 692e0deac..3fe95c232 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceSessionDescriptionPayload.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceSessionDescriptionPayload.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice session description payload.
-/// </summary>
-internal sealed class VoiceSessionDescriptionPayload
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets the secret key.
+ /// The voice session description payload.
/// </summary>
- [JsonProperty("secret_key")]
- public byte[] SecretKey { get; set; }
+ internal sealed class VoiceSessionDescriptionPayload
+ {
+ /// <summary>
+ /// Gets or sets the secret key.
+ /// </summary>
+ [JsonProperty("secret_key")]
+ public byte[] SecretKey { get; set; }
- /// <summary>
- /// Gets or sets the mode.
- /// </summary>
- [JsonProperty("mode")]
- public string Mode { get; set; }
+ /// <summary>
+ /// Gets or sets the mode.
+ /// </summary>
+ [JsonProperty("mode")]
+ public string Mode { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceSpeakingPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceSpeakingPayload.cs
index 80ce120be..f61f61b19 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceSpeakingPayload.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceSpeakingPayload.cs
@@ -1,55 +1,56 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice speaking payload.
-/// </summary>
-internal sealed class VoiceSpeakingPayload
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets a value indicating whether speaking.
+ /// The voice speaking payload.
/// </summary>
- [JsonProperty("speaking")]
- public bool Speaking { get; set; }
+ internal sealed class VoiceSpeakingPayload
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether speaking.
+ /// </summary>
+ [JsonProperty("speaking")]
+ public bool Speaking { get; set; }
- /// <summary>
- /// Gets or sets the delay.
- /// </summary>
- [JsonProperty("delay", NullValueHandling = NullValueHandling.Ignore)]
- public int? Delay { get; set; }
+ /// <summary>
+ /// Gets or sets the delay.
+ /// </summary>
+ [JsonProperty("delay", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Delay { get; set; }
- /// <summary>
- /// Gets or sets the s s r c.
- /// </summary>
- [JsonProperty("ssrc", NullValueHandling = NullValueHandling.Ignore)]
- public uint? Ssrc { get; set; }
+ /// <summary>
+ /// Gets or sets the s s r c.
+ /// </summary>
+ [JsonProperty("ssrc", NullValueHandling = NullValueHandling.Ignore)]
+ public uint? Ssrc { get; set; }
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? UserId { get; set; }
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? UserId { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceStateUpdatePayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceStateUpdatePayload.cs
index 4d6cc4ecc..0dd0d852a 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceStateUpdatePayload.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceStateUpdatePayload.cs
@@ -1,67 +1,68 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice state update payload.
-/// </summary>
-internal sealed class VoiceStateUpdatePayload
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets the guild id.
+ /// The voice state update payload.
/// </summary>
- [JsonProperty("guild_id")]
- public ulong GuildId { get; set; }
+ internal sealed class VoiceStateUpdatePayload
+ {
+ /// <summary>
+ /// Gets or sets the guild id.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id")]
- public ulong? ChannelId { get; set; }
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public ulong? ChannelId { get; set; }
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? UserId { get; set; }
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? UserId { get; set; }
- /// <summary>
- /// Gets or sets the session id.
- /// </summary>
- [JsonProperty("session_id", NullValueHandling = NullValueHandling.Ignore)]
- public string SessionId { get; set; }
+ /// <summary>
+ /// Gets or sets the session id.
+ /// </summary>
+ [JsonProperty("session_id", NullValueHandling = NullValueHandling.Ignore)]
+ public string SessionId { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether deafened.
- /// </summary>
- [JsonProperty("self_deaf")]
- public bool Deafened { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether deafened.
+ /// </summary>
+ [JsonProperty("self_deaf")]
+ public bool Deafened { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether muted.
- /// </summary>
- [JsonProperty("self_mute")]
- public bool Muted { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether muted.
+ /// </summary>
+ [JsonProperty("self_mute")]
+ public bool Muted { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceUserJoinPayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceUserJoinPayload.cs
index 31f89d2b2..6da29f00f 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceUserJoinPayload.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceUserJoinPayload.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice user join payload.
-/// </summary>
-internal sealed class VoiceUserJoinPayload
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets the user id.
+ /// The voice user join payload.
/// </summary>
- [JsonProperty("user_id")]
- public ulong UserId { get; private set; }
+ internal sealed class VoiceUserJoinPayload
+ {
+ /// <summary>
+ /// Gets the user id.
+ /// </summary>
+ [JsonProperty("user_id")]
+ public ulong UserId { get; private set; }
- /// <summary>
- /// Gets the s s r c.
- /// </summary>
- [JsonProperty("audio_ssrc")]
- public uint Ssrc { get; private set; }
+ /// <summary>
+ /// Gets the s s r c.
+ /// </summary>
+ [JsonProperty("audio_ssrc")]
+ public uint Ssrc { get; private set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/Entities/VoiceUserLeavePayload.cs b/DisCatSharp.VoiceNext/Entities/VoiceUserLeavePayload.cs
index 308f95c97..732c64ae9 100644
--- a/DisCatSharp.VoiceNext/Entities/VoiceUserLeavePayload.cs
+++ b/DisCatSharp.VoiceNext/Entities/VoiceUserLeavePayload.cs
@@ -1,37 +1,38 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext.Entities;
-
-/// <summary>
-/// The voice user leave payload.
-/// </summary>
-internal sealed class VoiceUserLeavePayload
+namespace DisCatSharp.VoiceNext.Entities
{
/// <summary>
- /// Gets or sets the user id.
+ /// The voice user leave payload.
/// </summary>
- [JsonProperty("user_id")]
- public ulong UserId { get; set; }
+ internal sealed class VoiceUserLeavePayload
+ {
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ [JsonProperty("user_id")]
+ public ulong UserId { get; set; }
+ }
}
diff --git a/DisCatSharp.VoiceNext/EventArgs/VoiceReceiveEventArgs.cs b/DisCatSharp.VoiceNext/EventArgs/VoiceReceiveEventArgs.cs
index 662d740fe..9f0dd1793 100644
--- a/DisCatSharp.VoiceNext/EventArgs/VoiceReceiveEventArgs.cs
+++ b/DisCatSharp.VoiceNext/EventArgs/VoiceReceiveEventArgs.cs
@@ -1,76 +1,77 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.VoiceNext.EventArgs;
-
-/// <summary>
-/// Represents arguments for VoiceReceived events.
-/// </summary>
-public class VoiceReceiveEventArgs : DiscordEventArgs
+namespace DisCatSharp.VoiceNext.EventArgs
{
/// <summary>
- /// Gets the SSRC of the audio source.
+ /// Represents arguments for VoiceReceived events.
/// </summary>
- public uint Ssrc { get; internal set; }
+ public class VoiceReceiveEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the SSRC of the audio source.
+ /// </summary>
+ public uint Ssrc { get; internal set; }
#pragma warning disable CS8632
- /// <summary>
- /// Gets the user that sent the audio data.
- /// </summary>
- public DiscordUser? User { get; internal set; }
+ /// <summary>
+ /// Gets the user that sent the audio data.
+ /// </summary>
+ public DiscordUser? User { get; internal set; }
#pragma warning restore
- /// <summary>
- /// Gets the received voice data, decoded to PCM format.
- /// </summary>
- public ReadOnlyMemory<byte> PcmData { get; internal set; }
+ /// <summary>
+ /// Gets the received voice data, decoded to PCM format.
+ /// </summary>
+ public ReadOnlyMemory<byte> PcmData { get; internal set; }
- /// <summary>
- /// Gets the received voice data, in Opus format. Note that for packets that were lost and/or compensated for, this will be empty.
- /// </summary>
- public ReadOnlyMemory<byte> OpusData { get; internal set; }
+ /// <summary>
+ /// Gets the received voice data, in Opus format. Note that for packets that were lost and/or compensated for, this will be empty.
+ /// </summary>
+ public ReadOnlyMemory<byte> OpusData { get; internal set; }
- /// <summary>
- /// Gets the format of the received PCM data.
- /// <para>
- /// Important: This isn't always the format set in <see cref="VoiceNextConfiguration.AudioFormat"/>, and depends on the audio data received.
- /// </para>
- /// </summary>
- public AudioFormat AudioFormat { get; internal set; }
+ /// <summary>
+ /// Gets the format of the received PCM data.
+ /// <para>
+ /// Important: This isn't always the format set in <see cref="VoiceNextConfiguration.AudioFormat"/>, and depends on the audio data received.
+ /// </para>
+ /// </summary>
+ public AudioFormat AudioFormat { get; internal set; }
- /// <summary>
- /// Gets the millisecond duration of the PCM audio sample.
- /// </summary>
- public int AudioDuration { get; internal set; }
+ /// <summary>
+ /// Gets the millisecond duration of the PCM audio sample.
+ /// </summary>
+ public int AudioDuration { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="VoiceReceiveEventArgs"/> class.
- /// </summary>
- internal VoiceReceiveEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VoiceReceiveEventArgs"/> class.
+ /// </summary>
+ internal VoiceReceiveEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp.VoiceNext/EventArgs/VoiceUserJoinEventArgs.cs b/DisCatSharp.VoiceNext/EventArgs/VoiceUserJoinEventArgs.cs
index d6d14699e..61bf4c575 100644
--- a/DisCatSharp.VoiceNext/EventArgs/VoiceUserJoinEventArgs.cs
+++ b/DisCatSharp.VoiceNext/EventArgs/VoiceUserJoinEventArgs.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.VoiceNext.EventArgs;
-
-/// <summary>
-/// Arguments for <see cref="VoiceNextConnection.UserJoined"/>.
-/// </summary>
-public sealed class VoiceUserJoinEventArgs : DiscordEventArgs
+namespace DisCatSharp.VoiceNext.EventArgs
{
/// <summary>
- /// Gets the user who left.
+ /// Arguments for <see cref="VoiceNextConnection.UserJoined"/>.
/// </summary>
- public DiscordUser User { get; internal set; }
+ public sealed class VoiceUserJoinEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the user who left.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the SSRC of the user who joined.
- /// </summary>
- public uint Ssrc { get; internal set; }
+ /// <summary>
+ /// Gets the SSRC of the user who joined.
+ /// </summary>
+ public uint Ssrc { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="VoiceUserJoinEventArgs"/> class.
- /// </summary>
- internal VoiceUserJoinEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VoiceUserJoinEventArgs"/> class.
+ /// </summary>
+ internal VoiceUserJoinEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp.VoiceNext/EventArgs/VoiceUserLeaveEventArgs.cs b/DisCatSharp.VoiceNext/EventArgs/VoiceUserLeaveEventArgs.cs
index a8b786610..8c57e8a3c 100644
--- a/DisCatSharp.VoiceNext/EventArgs/VoiceUserLeaveEventArgs.cs
+++ b/DisCatSharp.VoiceNext/EventArgs/VoiceUserLeaveEventArgs.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-namespace DisCatSharp.VoiceNext.EventArgs;
-
-/// <summary>
-/// Arguments for <see cref="VoiceNextConnection.UserLeft"/>.
-/// </summary>
-public sealed class VoiceUserLeaveEventArgs : DiscordEventArgs
+namespace DisCatSharp.VoiceNext.EventArgs
{
/// <summary>
- /// Gets the user who left.
+ /// Arguments for <see cref="VoiceNextConnection.UserLeft"/>.
/// </summary>
- public DiscordUser User { get; internal set; }
+ public sealed class VoiceUserLeaveEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the user who left.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the SSRC of the user who left.
- /// </summary>
- public uint Ssrc { get; internal set; }
+ /// <summary>
+ /// Gets the SSRC of the user who left.
+ /// </summary>
+ public uint Ssrc { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="VoiceUserLeaveEventArgs"/> class.
- /// </summary>
- internal VoiceUserLeaveEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VoiceUserLeaveEventArgs"/> class.
+ /// </summary>
+ internal VoiceUserLeaveEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp.VoiceNext/IVoiceFilter.cs b/DisCatSharp.VoiceNext/IVoiceFilter.cs
index 207a8e329..f7863190f 100644
--- a/DisCatSharp.VoiceNext/IVoiceFilter.cs
+++ b/DisCatSharp.VoiceNext/IVoiceFilter.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.VoiceNext;
-
-/// <summary>
-/// Represents a filter for PCM data. PCM data submitted through a <see cref="VoiceTransmitSink"/> will be sent through all installed instances of <see cref="IVoiceFilter"/> first.
-/// </summary>
-public interface IVoiceFilter
+namespace DisCatSharp.VoiceNext
{
/// <summary>
- /// Transforms the supplied PCM data using this filter.
+ /// Represents a filter for PCM data. PCM data submitted through a <see cref="VoiceTransmitSink"/> will be sent through all installed instances of <see cref="IVoiceFilter"/> first.
/// </summary>
- /// <param name="pcmData">PCM data to transform. The transformation happens in-place.</param>
- /// <param name="pcmFormat">Format of the supplied PCM data.</param>
- /// <param name="duration">Millisecond duration of the supplied PCM data.</param>
- void Transform(Span<short> pcmData, AudioFormat pcmFormat, int duration);
+ public interface IVoiceFilter
+ {
+ /// <summary>
+ /// Transforms the supplied PCM data using this filter.
+ /// </summary>
+ /// <param name="pcmData">PCM data to transform. The transformation happens in-place.</param>
+ /// <param name="pcmFormat">Format of the supplied PCM data.</param>
+ /// <param name="duration">Millisecond duration of the supplied PCM data.</param>
+ void Transform(Span<short> pcmData, AudioFormat pcmFormat, int duration);
+ }
}
diff --git a/DisCatSharp.VoiceNext/RawVoicePacket.cs b/DisCatSharp.VoiceNext/RawVoicePacket.cs
index b4793fd26..46e2042b7 100644
--- a/DisCatSharp.VoiceNext/RawVoicePacket.cs
+++ b/DisCatSharp.VoiceNext/RawVoicePacket.cs
@@ -1,61 +1,62 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.VoiceNext;
-
-internal readonly struct RawVoicePacket
+namespace DisCatSharp.VoiceNext
{
- /// <summary>
- /// Initializes a new instance of the <see cref="RawVoicePacket"/> class.
- /// </summary>
- /// <param name="bytes">The bytes.</param>
- /// <param name="duration">The duration.</param>
- /// <param name="silence">If true, silence.</param>
- public RawVoicePacket(Memory<byte> bytes, int duration, bool silence)
+ internal readonly struct RawVoicePacket
{
- this.Bytes = bytes;
- this.Duration = duration;
- this.Silence = silence;
- this.RentedBuffer = null;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RawVoicePacket"/> class.
+ /// </summary>
+ /// <param name="bytes">The bytes.</param>
+ /// <param name="duration">The duration.</param>
+ /// <param name="silence">If true, silence.</param>
+ public RawVoicePacket(Memory<byte> bytes, int duration, bool silence)
+ {
+ this.Bytes = bytes;
+ this.Duration = duration;
+ this.Silence = silence;
+ this.RentedBuffer = null;
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="RawVoicePacket"/> class.
- /// </summary>
- /// <param name="bytes">The bytes.</param>
- /// <param name="duration">The duration.</param>
- /// <param name="silence">If true, silence.</param>
- /// <param name="rentedBuffer">The rented buffer.</param>
- public RawVoicePacket(Memory<byte> bytes, int duration, bool silence, byte[] rentedBuffer)
- : this(bytes, duration, silence)
- {
- this.RentedBuffer = rentedBuffer;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RawVoicePacket"/> class.
+ /// </summary>
+ /// <param name="bytes">The bytes.</param>
+ /// <param name="duration">The duration.</param>
+ /// <param name="silence">If true, silence.</param>
+ /// <param name="rentedBuffer">The rented buffer.</param>
+ public RawVoicePacket(Memory<byte> bytes, int duration, bool silence, byte[] rentedBuffer)
+ : this(bytes, duration, silence)
+ {
+ this.RentedBuffer = rentedBuffer;
+ }
- public readonly Memory<byte> Bytes;
- public readonly int Duration;
- public readonly bool Silence;
+ public readonly Memory<byte> Bytes;
+ public readonly int Duration;
+ public readonly bool Silence;
- public readonly byte[] RentedBuffer;
+ public readonly byte[] RentedBuffer;
+ }
}
diff --git a/DisCatSharp.VoiceNext/StreamExtensions.cs b/DisCatSharp.VoiceNext/StreamExtensions.cs
index 5a55fd692..604fbf768 100644
--- a/DisCatSharp.VoiceNext/StreamExtensions.cs
+++ b/DisCatSharp.VoiceNext/StreamExtensions.cs
@@ -1,71 +1,72 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-namespace DisCatSharp.VoiceNext;
-
-/// <summary>
-/// The stream extensions.
-/// </summary>
-public static class StreamExtensions
+namespace DisCatSharp.VoiceNext
{
/// <summary>
- /// Asynchronously reads the bytes from the current stream and writes them to the specified <see cref="VoiceTransmitSink"/>.
+ /// The stream extensions.
/// </summary>
- /// <param name="source">The source <see cref="System.IO.Stream"/></param>
- /// <param name="destination">The target <see cref="VoiceTransmitSink"/></param>
- /// <param name="bufferSize">The size, in bytes, of the buffer. This value must be greater than zero. If <see langword="null"/>, defaults to the packet size specified by <paramref name="destination"/>.</param>
- /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
- /// <returns></returns>
- public static async Task CopyToAsync(this Stream source, VoiceTransmitSink destination, int? bufferSize = null, CancellationToken cancellationToken = default)
+ public static class StreamExtensions
{
- // adapted from CoreFX
- // https://source.dot.net/#System.Private.CoreLib/Stream.cs,8048a9680abdd13b
+ /// <summary>
+ /// Asynchronously reads the bytes from the current stream and writes them to the specified <see cref="VoiceTransmitSink"/>.
+ /// </summary>
+ /// <param name="source">The source <see cref="System.IO.Stream"/></param>
+ /// <param name="destination">The target <see cref="VoiceTransmitSink"/></param>
+ /// <param name="bufferSize">The size, in bytes, of the buffer. This value must be greater than zero. If <see langword="null"/>, defaults to the packet size specified by <paramref name="destination"/>.</param>
+ /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+ /// <returns></returns>
+ public static async Task CopyToAsync(this Stream source, VoiceTransmitSink destination, int? bufferSize = null, CancellationToken cancellationToken = default)
+ {
+ // adapted from CoreFX
+ // https://source.dot.net/#System.Private.CoreLib/Stream.cs,8048a9680abdd13b
- if (source is null)
- throw new ArgumentNullException(nameof(source));
- if (destination is null)
- throw new ArgumentNullException(nameof(destination));
- if (bufferSize != null && bufferSize <= 0)
- throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, "bufferSize cannot be less than or equal to zero");
+ if (source is null)
+ throw new ArgumentNullException(nameof(source));
+ if (destination is null)
+ throw new ArgumentNullException(nameof(destination));
+ if (bufferSize != null && bufferSize <= 0)
+ throw new ArgumentOutOfRangeException(nameof(bufferSize), bufferSize, "bufferSize cannot be less than or equal to zero");
- var bufferLength = bufferSize ?? destination.SampleLength;
- var buffer = ArrayPool<byte>.Shared.Rent(bufferLength);
- try
- {
- int bytesRead;
- while ((bytesRead = await source.ReadAsync(buffer, 0, bufferLength, cancellationToken).ConfigureAwait(false)) != 0)
+ var bufferLength = bufferSize ?? destination.SampleLength;
+ var buffer = ArrayPool<byte>.Shared.Rent(bufferLength);
+ try
{
- await destination.WriteAsync(new ReadOnlyMemory<byte>(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false);
+ int bytesRead;
+ while ((bytesRead = await source.ReadAsync(buffer, 0, bufferLength, cancellationToken).ConfigureAwait(false)) != 0)
+ {
+ await destination.WriteAsync(new ReadOnlyMemory<byte>(buffer, 0, bytesRead), cancellationToken).ConfigureAwait(false);
+ }
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buffer);
}
- }
- finally
- {
- ArrayPool<byte>.Shared.Return(buffer);
}
}
}
diff --git a/DisCatSharp.VoiceNext/VoiceApplication.cs b/DisCatSharp.VoiceNext/VoiceApplication.cs
index 034cec8ca..fd5914bfb 100644
--- a/DisCatSharp.VoiceNext/VoiceApplication.cs
+++ b/DisCatSharp.VoiceNext/VoiceApplication.cs
@@ -1,44 +1,45 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.VoiceNext;
-
-/// <summary>
-/// Represents encoder settings preset for Opus.
-/// </summary>
-public enum VoiceApplication : int
+namespace DisCatSharp.VoiceNext
{
/// <summary>
- /// Defines that the encoder must optimize settings for voice data.
+ /// Represents encoder settings preset for Opus.
/// </summary>
- Voice = 2048,
+ public enum VoiceApplication : int
+ {
+ /// <summary>
+ /// Defines that the encoder must optimize settings for voice data.
+ /// </summary>
+ Voice = 2048,
- /// <summary>
- /// Defines that the encoder must optimize settings for music data.
- /// </summary>
- Music = 2049,
+ /// <summary>
+ /// Defines that the encoder must optimize settings for music data.
+ /// </summary>
+ Music = 2049,
- /// <summary>
- /// Defines that the encoder must optimize settings for low latency applications.
- /// </summary>
- LowLatency = 2051
+ /// <summary>
+ /// Defines that the encoder must optimize settings for low latency applications.
+ /// </summary>
+ LowLatency = 2051
+ }
}
diff --git a/DisCatSharp.VoiceNext/VoiceNextConfiguration.cs b/DisCatSharp.VoiceNext/VoiceNextConfiguration.cs
index 065119d04..76be68875 100644
--- a/DisCatSharp.VoiceNext/VoiceNextConfiguration.cs
+++ b/DisCatSharp.VoiceNext/VoiceNextConfiguration.cs
@@ -1,65 +1,66 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.VoiceNext;
-
-/// <summary>
-/// VoiceNext client configuration.
-/// </summary>
-public sealed class VoiceNextConfiguration
+namespace DisCatSharp.VoiceNext
{
/// <summary>
- /// <para>Sets the audio format for Opus. This will determine the quality of the audio output.</para>
- /// <para>Defaults to <see cref="AudioFormat.Default"/>.</para>
+ /// VoiceNext client configuration.
/// </summary>
- public AudioFormat AudioFormat { internal get; set; } = AudioFormat.Default;
+ public sealed class VoiceNextConfiguration
+ {
+ /// <summary>
+ /// <para>Sets the audio format for Opus. This will determine the quality of the audio output.</para>
+ /// <para>Defaults to <see cref="AudioFormat.Default"/>.</para>
+ /// </summary>
+ public AudioFormat AudioFormat { internal get; set; } = AudioFormat.Default;
- /// <summary>
- /// <para>Sets whether incoming voice receiver should be enabled.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool EnableIncoming { internal get; set; }
+ /// <summary>
+ /// <para>Sets whether incoming voice receiver should be enabled.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool EnableIncoming { internal get; set; }
- /// <summary>
- /// <para>Sets the size of the packet queue.</para>
- /// <para>Defaults to 25 or ~500ms.</para>
- /// </summary>
- public int PacketQueueSize { internal get; set; } = 25;
+ /// <summary>
+ /// <para>Sets the size of the packet queue.</para>
+ /// <para>Defaults to 25 or ~500ms.</para>
+ /// </summary>
+ public int PacketQueueSize { internal get; set; } = 25;
- /// <summary>
- /// Creates a new instance of <see cref="VoiceNextConfiguration"/>.
- /// </summary>
- [ActivatorUtilitiesConstructor]
- public VoiceNextConfiguration() { }
+ /// <summary>
+ /// Creates a new instance of <see cref="VoiceNextConfiguration"/>.
+ /// </summary>
+ [ActivatorUtilitiesConstructor]
+ public VoiceNextConfiguration() { }
- /// <summary>
- /// Creates a new instance of <see cref="VoiceNextConfiguration"/>, copying the properties of another configuration.
- /// </summary>
- /// <param name="other">Configuration the properties of which are to be copied.</param>
- public VoiceNextConfiguration(VoiceNextConfiguration other)
- {
- this.AudioFormat = new AudioFormat(other.AudioFormat.SampleRate, other.AudioFormat.ChannelCount, other.AudioFormat.VoiceApplication);
- this.EnableIncoming = other.EnableIncoming;
+ /// <summary>
+ /// Creates a new instance of <see cref="VoiceNextConfiguration"/>, copying the properties of another configuration.
+ /// </summary>
+ /// <param name="other">Configuration the properties of which are to be copied.</param>
+ public VoiceNextConfiguration(VoiceNextConfiguration other)
+ {
+ this.AudioFormat = new AudioFormat(other.AudioFormat.SampleRate, other.AudioFormat.ChannelCount, other.AudioFormat.VoiceApplication);
+ this.EnableIncoming = other.EnableIncoming;
+ }
}
}
diff --git a/DisCatSharp.VoiceNext/VoiceNextConnection.cs b/DisCatSharp.VoiceNext/VoiceNextConnection.cs
index d7c669950..474d295b0 100644
--- a/DisCatSharp.VoiceNext/VoiceNextConnection.cs
+++ b/DisCatSharp.VoiceNext/VoiceNextConnection.cs
@@ -1,1359 +1,1360 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Buffers;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Net;
using DisCatSharp.Net.Udp;
using DisCatSharp.Net.WebSocket;
using DisCatSharp.VoiceNext.Codec;
using DisCatSharp.VoiceNext.Entities;
using DisCatSharp.VoiceNext.EventArgs;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.VoiceNext;
-
-internal delegate Task VoiceDisconnectedEventHandler(DiscordGuild guild);
-
-/// <summary>
-/// VoiceNext connection to a voice channel.
-/// </summary>
-public sealed class VoiceNextConnection : IDisposable
+namespace DisCatSharp.VoiceNext
{
- /// <summary>
- /// Triggered whenever a user speaks in the connected voice channel.
- /// </summary>
- public event AsyncEventHandler<VoiceNextConnection, UserSpeakingEventArgs> UserSpeaking
- {
- add => this._userSpeaking.Register(value);
- remove => this._userSpeaking.Unregister(value);
- }
- private readonly AsyncEvent<VoiceNextConnection, UserSpeakingEventArgs> _userSpeaking;
+ internal delegate Task VoiceDisconnectedEventHandler(DiscordGuild guild);
/// <summary>
- /// Triggered whenever a user joins voice in the connected guild.
+ /// VoiceNext connection to a voice channel.
/// </summary>
- public event AsyncEventHandler<VoiceNextConnection, VoiceUserJoinEventArgs> UserJoined
+ public sealed class VoiceNextConnection : IDisposable
{
- add => this._userJoined.Register(value);
- remove => this._userJoined.Unregister(value);
- }
- private readonly AsyncEvent<VoiceNextConnection, VoiceUserJoinEventArgs> _userJoined;
-
- /// <summary>
- /// Triggered whenever a user leaves voice in the connected guild.
- /// </summary>
- public event AsyncEventHandler<VoiceNextConnection, VoiceUserLeaveEventArgs> UserLeft
- {
- add => this._userLeft.Register(value);
- remove => this._userLeft.Unregister(value);
- }
- private readonly AsyncEvent<VoiceNextConnection, VoiceUserLeaveEventArgs> _userLeft;
-
- /// <summary>
- /// Triggered whenever voice data is received from the connected voice channel.
- /// </summary>
- public event AsyncEventHandler<VoiceNextConnection, VoiceReceiveEventArgs> VoiceReceived
- {
- add => this._voiceReceived.Register(value);
- remove => this._voiceReceived.Unregister(value);
- }
- private readonly AsyncEvent<VoiceNextConnection, VoiceReceiveEventArgs> _voiceReceived;
-
- /// <summary>
- /// Triggered whenever voice WebSocket throws an exception.
- /// </summary>
- public event AsyncEventHandler<VoiceNextConnection, SocketErrorEventArgs> VoiceSocketErrored
- {
- add => this._voiceSocketError.Register(value);
- remove => this._voiceSocketError.Unregister(value);
- }
- private readonly AsyncEvent<VoiceNextConnection, SocketErrorEventArgs> _voiceSocketError;
-
- internal event VoiceDisconnectedEventHandler VoiceDisconnected;
-
- /// <summary>
- /// Gets the unix epoch.
- /// </summary>
- private static DateTimeOffset s_unixEpoch { get; } = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
-
- /// <summary>
- /// Gets the discord.
- /// </summary>
- private readonly DiscordClient _discord;
-
- /// <summary>
- /// Gets the guild.
- /// </summary>
- private readonly DiscordGuild _guild;
-
- /// <summary>
- /// Gets the transmitting s s r cs.
- /// </summary>
- private readonly ConcurrentDictionary<uint, AudioSender> _transmittingSsrCs;
-
- /// <summary>
- /// Gets the udp client.
- /// </summary>
- private readonly BaseUdpClient _udpClient;
-
- /// <summary>
- /// Gets or sets the voice ws.
- /// </summary>
- private IWebSocketClient _voiceWs;
-
- /// <summary>
- /// Gets or sets the heartbeat task.
- /// </summary>
- private Task _heartbeatTask;
-
- /// <summary>
- /// Gets or sets the heartbeat interval.
- /// </summary>
- private int _heartbeatInterval;
-
- /// <summary>
- /// Gets or sets the last heartbeat.
- /// </summary>
- private DateTimeOffset _lastHeartbeat;
-
- /// <summary>
- /// Gets or sets the token source.
- /// </summary>
- private CancellationTokenSource _tokenSource;
- /// <summary>
- /// Gets the token.
- /// </summary>
- private CancellationToken TOKEN
- => this._tokenSource.Token;
-
- /// <summary>
- /// Gets or sets the server data.
- /// </summary>
- internal VoiceServerUpdatePayload ServerData { get; set; }
- /// <summary>
- /// Gets or sets the state data.
- /// </summary>
- internal VoiceStateUpdatePayload StateData { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether resume.
- /// </summary>
- internal bool Resume { get; set; }
-
- /// <summary>
- /// Gets the configuration.
- /// </summary>
- private readonly VoiceNextConfiguration _configuration;
-
- /// <summary>
- /// Gets or sets the opus.
- /// </summary>
- private Opus _opus;
-
- /// <summary>
- /// Gets or sets the sodium.
- /// </summary>
- private Sodium _sodium;
-
- /// <summary>
- /// Gets or sets the rtp.
- /// </summary>
- private Rtp _rtp;
-
- /// <summary>
- /// Gets or sets the selected encryption mode.
- /// </summary>
- private EncryptionMode _selectedEncryptionMode;
- /// <summary>
- /// Gets or sets the nonce.
- /// </summary>
- private uint _nonce;
-
- /// <summary>
- /// Gets or sets the sequence.
- /// </summary>
- private ushort _sequence;
-
- /// <summary>
- /// Gets or sets the timestamp.
- /// </summary>
- private uint _timestamp;
-
- /// <summary>
- /// Gets or sets the s s r c.
- /// </summary>
- private uint _ssrc;
-
- /// <summary>
- /// Gets or sets the key.
- /// </summary>
- private byte[] _key;
-
- /// <summary>
- /// Gets or sets the discovered endpoint.
- /// </summary>
- private IpEndpoint _discoveredEndpoint;
- /// <summary>
- /// Gets or sets the web socket endpoint.
- /// </summary>
- internal ConnectionEndpoint WebSocketEndpoint { get; set; }
- /// <summary>
- /// Gets or sets the udp endpoint.
- /// </summary>
- internal ConnectionEndpoint UdpEndpoint { get; set; }
-
- /// <summary>
- /// Gets or sets the ready wait.
- /// </summary>
- private readonly TaskCompletionSource<bool> _readyWait;
-
- /// <summary>
- /// Gets or sets a value indicating whether is initialized.
- /// </summary>
- private bool _isInitialized;
-
- /// <summary>
- /// Gets or sets a value indicating whether is disposed.
- /// </summary>
- private bool _isDisposed;
-
- /// <summary>
- /// Gets or sets the playing wait.
- /// </summary>
- private TaskCompletionSource<bool> _playingWait;
-
- /// <summary>
- /// Gets the pause event.
- /// </summary>
- private readonly AsyncManualResetEvent _pauseEvent;
-
- /// <summary>
- /// Gets or sets the transmit stream.
- /// </summary>
- private VoiceTransmitSink _transmitStream;
-
- /// <summary>
- /// Gets the transmit channel.
- /// </summary>
- private readonly Channel<RawVoicePacket> _transmitChannel;
-
- /// <summary>
- /// Gets the keepalive timestamps.
- /// </summary>
- private readonly ConcurrentDictionary<ulong, long> _keepaliveTimestamps;
- private ulong _lastKeepalive;
-
- /// <summary>
- /// Gets or sets the sender task.
- /// </summary>
- private Task _senderTask;
-
- /// <summary>
- /// Gets or sets the sender token source.
- /// </summary>
- private CancellationTokenSource _senderTokenSource;
- /// <summary>
- /// Gets the sender token.
- /// </summary>
- private CancellationToken SENDER_TOKEN
- => this._senderTokenSource.Token;
-
- /// <summary>
- /// Gets or sets the receiver task.
- /// </summary>
- private Task _receiverTask;
-
- /// <summary>
- /// Gets or sets the receiver token source.
- /// </summary>
- private CancellationTokenSource _receiverTokenSource;
- /// <summary>
- /// Gets the receiver token.
- /// </summary>
- private CancellationToken RECEIVER_TOKEN
- => this._receiverTokenSource.Token;
-
- /// <summary>
- /// Gets or sets the keepalive task.
- /// </summary>
- private Task _keepaliveTask;
-
- /// <summary>
- /// Gets or sets the keepalive token source.
- /// </summary>
- private CancellationTokenSource _keepaliveTokenSource;
- /// <summary>
- /// Gets the keepalive token.
- /// </summary>
- private CancellationToken KEEPALIVE_TOKEN
- => this._keepaliveTokenSource.Token;
-
- private volatile bool _isSpeaking;
-
- /// <summary>
- /// Gets the audio format used by the Opus encoder.
- /// </summary>
- public AudioFormat AudioFormat => this._configuration.AudioFormat;
-
- /// <summary>
- /// Gets whether this connection is still playing audio.
- /// </summary>
- public bool IsPlaying
- => this._playingWait != null && !this._playingWait.Task.IsCompleted;
-
- /// <summary>
- /// Gets the websocket round-trip time in ms.
- /// </summary>
- public int WebSocketPing
- => Volatile.Read(ref this._wsPing);
- private int _wsPing;
-
- /// <summary>
- /// Gets the UDP round-trip time in ms.
- /// </summary>
- public int UdpPing
- => Volatile.Read(ref this._udpPing);
- private int _udpPing;
+ /// <summary>
+ /// Triggered whenever a user speaks in the connected voice channel.
+ /// </summary>
+ public event AsyncEventHandler<VoiceNextConnection, UserSpeakingEventArgs> UserSpeaking
+ {
+ add => this._userSpeaking.Register(value);
+ remove => this._userSpeaking.Unregister(value);
+ }
+ private readonly AsyncEvent<VoiceNextConnection, UserSpeakingEventArgs> _userSpeaking;
- private int _queueCount;
+ /// <summary>
+ /// Triggered whenever a user joins voice in the connected guild.
+ /// </summary>
+ public event AsyncEventHandler<VoiceNextConnection, VoiceUserJoinEventArgs> UserJoined
+ {
+ add => this._userJoined.Register(value);
+ remove => this._userJoined.Unregister(value);
+ }
+ private readonly AsyncEvent<VoiceNextConnection, VoiceUserJoinEventArgs> _userJoined;
- /// <summary>
- /// Gets the channel this voice client is connected to.
- /// </summary>
- public DiscordChannel TargetChannel { get; internal set; }
+ /// <summary>
+ /// Triggered whenever a user leaves voice in the connected guild.
+ /// </summary>
+ public event AsyncEventHandler<VoiceNextConnection, VoiceUserLeaveEventArgs> UserLeft
+ {
+ add => this._userLeft.Register(value);
+ remove => this._userLeft.Unregister(value);
+ }
+ private readonly AsyncEvent<VoiceNextConnection, VoiceUserLeaveEventArgs> _userLeft;
- /// <summary>
- /// Initializes a new instance of the <see cref="VoiceNextConnection"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="guild">The guild.</param>
- /// <param name="channel">The channel.</param>
- /// <param name="config">The config.</param>
- /// <param name="server">The server.</param>
- /// <param name="state">The state.</param>
- internal VoiceNextConnection(DiscordClient client, DiscordGuild guild, DiscordChannel channel, VoiceNextConfiguration config, VoiceServerUpdatePayload server, VoiceStateUpdatePayload state)
- {
- this._discord = client;
- this._guild = guild;
- this.TargetChannel = channel;
- this._transmittingSsrCs = new ConcurrentDictionary<uint, AudioSender>();
-
- this._userSpeaking = new AsyncEvent<VoiceNextConnection, UserSpeakingEventArgs>("VNEXT_USER_SPEAKING", TimeSpan.Zero, this._discord.EventErrorHandler);
- this._userJoined = new AsyncEvent<VoiceNextConnection, VoiceUserJoinEventArgs>("VNEXT_USER_JOINED", TimeSpan.Zero, this._discord.EventErrorHandler);
- this._userLeft = new AsyncEvent<VoiceNextConnection, VoiceUserLeaveEventArgs>("VNEXT_USER_LEFT", TimeSpan.Zero, this._discord.EventErrorHandler);
- this._voiceReceived = new AsyncEvent<VoiceNextConnection, VoiceReceiveEventArgs>("VNEXT_VOICE_RECEIVED", TimeSpan.Zero, this._discord.EventErrorHandler);
- this._voiceSocketError = new AsyncEvent<VoiceNextConnection, SocketErrorEventArgs>("VNEXT_WS_ERROR", TimeSpan.Zero, this._discord.EventErrorHandler);
- this._tokenSource = new CancellationTokenSource();
-
- this._configuration = config;
- this._isInitialized = false;
- this._isDisposed = false;
- this._opus = new Opus(this.AudioFormat);
- //this.Sodium = new Sodium();
- this._rtp = new Rtp();
-
- this.ServerData = server;
- this.StateData = state;
-
- var eps = this.ServerData.Endpoint;
- var epi = eps.LastIndexOf(':');
- var eph = string.Empty;
- var epp = 443;
- if (epi != -1)
+ /// <summary>
+ /// Triggered whenever voice data is received from the connected voice channel.
+ /// </summary>
+ public event AsyncEventHandler<VoiceNextConnection, VoiceReceiveEventArgs> VoiceReceived
{
- eph = eps[..epi];
- epp = int.Parse(eps[(epi + 1)..]);
+ add => this._voiceReceived.Register(value);
+ remove => this._voiceReceived.Unregister(value);
}
- else
+ private readonly AsyncEvent<VoiceNextConnection, VoiceReceiveEventArgs> _voiceReceived;
+
+ /// <summary>
+ /// Triggered whenever voice WebSocket throws an exception.
+ /// </summary>
+ public event AsyncEventHandler<VoiceNextConnection, SocketErrorEventArgs> VoiceSocketErrored
{
- eph = eps;
+ add => this._voiceSocketError.Register(value);
+ remove => this._voiceSocketError.Unregister(value);
}
- this.WebSocketEndpoint = new ConnectionEndpoint { Hostname = eph, Port = epp };
+ private readonly AsyncEvent<VoiceNextConnection, SocketErrorEventArgs> _voiceSocketError;
+
+ internal event VoiceDisconnectedEventHandler VoiceDisconnected;
+
+ /// <summary>
+ /// Gets the unix epoch.
+ /// </summary>
+ private static DateTimeOffset s_unixEpoch { get; } = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
+
+ /// <summary>
+ /// Gets the discord.
+ /// </summary>
+ private readonly DiscordClient _discord;
+
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ private readonly DiscordGuild _guild;
+
+ /// <summary>
+ /// Gets the transmitting s s r cs.
+ /// </summary>
+ private readonly ConcurrentDictionary<uint, AudioSender> _transmittingSsrCs;
+
+ /// <summary>
+ /// Gets the udp client.
+ /// </summary>
+ private readonly BaseUdpClient _udpClient;
+
+ /// <summary>
+ /// Gets or sets the voice ws.
+ /// </summary>
+ private IWebSocketClient _voiceWs;
+
+ /// <summary>
+ /// Gets or sets the heartbeat task.
+ /// </summary>
+ private Task _heartbeatTask;
+
+ /// <summary>
+ /// Gets or sets the heartbeat interval.
+ /// </summary>
+ private int _heartbeatInterval;
+
+ /// <summary>
+ /// Gets or sets the last heartbeat.
+ /// </summary>
+ private DateTimeOffset _lastHeartbeat;
+
+ /// <summary>
+ /// Gets or sets the token source.
+ /// </summary>
+ private CancellationTokenSource _tokenSource;
+ /// <summary>
+ /// Gets the token.
+ /// </summary>
+ private CancellationToken TOKEN
+ => this._tokenSource.Token;
+
+ /// <summary>
+ /// Gets or sets the server data.
+ /// </summary>
+ internal VoiceServerUpdatePayload ServerData { get; set; }
+ /// <summary>
+ /// Gets or sets the state data.
+ /// </summary>
+ internal VoiceStateUpdatePayload StateData { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether resume.
+ /// </summary>
+ internal bool Resume { get; set; }
+
+ /// <summary>
+ /// Gets the configuration.
+ /// </summary>
+ private readonly VoiceNextConfiguration _configuration;
+
+ /// <summary>
+ /// Gets or sets the opus.
+ /// </summary>
+ private Opus _opus;
+
+ /// <summary>
+ /// Gets or sets the sodium.
+ /// </summary>
+ private Sodium _sodium;
+
+ /// <summary>
+ /// Gets or sets the rtp.
+ /// </summary>
+ private Rtp _rtp;
+
+ /// <summary>
+ /// Gets or sets the selected encryption mode.
+ /// </summary>
+ private EncryptionMode _selectedEncryptionMode;
+ /// <summary>
+ /// Gets or sets the nonce.
+ /// </summary>
+ private uint _nonce;
+
+ /// <summary>
+ /// Gets or sets the sequence.
+ /// </summary>
+ private ushort _sequence;
+
+ /// <summary>
+ /// Gets or sets the timestamp.
+ /// </summary>
+ private uint _timestamp;
+
+ /// <summary>
+ /// Gets or sets the s s r c.
+ /// </summary>
+ private uint _ssrc;
+
+ /// <summary>
+ /// Gets or sets the key.
+ /// </summary>
+ private byte[] _key;
+
+ /// <summary>
+ /// Gets or sets the discovered endpoint.
+ /// </summary>
+ private IpEndpoint _discoveredEndpoint;
+ /// <summary>
+ /// Gets or sets the web socket endpoint.
+ /// </summary>
+ internal ConnectionEndpoint WebSocketEndpoint { get; set; }
+ /// <summary>
+ /// Gets or sets the udp endpoint.
+ /// </summary>
+ internal ConnectionEndpoint UdpEndpoint { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ready wait.
+ /// </summary>
+ private readonly TaskCompletionSource<bool> _readyWait;
+
+ /// <summary>
+ /// Gets or sets a value indicating whether is initialized.
+ /// </summary>
+ private bool _isInitialized;
+
+ /// <summary>
+ /// Gets or sets a value indicating whether is disposed.
+ /// </summary>
+ private bool _isDisposed;
+
+ /// <summary>
+ /// Gets or sets the playing wait.
+ /// </summary>
+ private TaskCompletionSource<bool> _playingWait;
+
+ /// <summary>
+ /// Gets the pause event.
+ /// </summary>
+ private readonly AsyncManualResetEvent _pauseEvent;
+
+ /// <summary>
+ /// Gets or sets the transmit stream.
+ /// </summary>
+ private VoiceTransmitSink _transmitStream;
+
+ /// <summary>
+ /// Gets the transmit channel.
+ /// </summary>
+ private readonly Channel<RawVoicePacket> _transmitChannel;
+
+ /// <summary>
+ /// Gets the keepalive timestamps.
+ /// </summary>
+ private readonly ConcurrentDictionary<ulong, long> _keepaliveTimestamps;
+ private ulong _lastKeepalive;
+
+ /// <summary>
+ /// Gets or sets the sender task.
+ /// </summary>
+ private Task _senderTask;
+
+ /// <summary>
+ /// Gets or sets the sender token source.
+ /// </summary>
+ private CancellationTokenSource _senderTokenSource;
+ /// <summary>
+ /// Gets the sender token.
+ /// </summary>
+ private CancellationToken SENDER_TOKEN
+ => this._senderTokenSource.Token;
+
+ /// <summary>
+ /// Gets or sets the receiver task.
+ /// </summary>
+ private Task _receiverTask;
+
+ /// <summary>
+ /// Gets or sets the receiver token source.
+ /// </summary>
+ private CancellationTokenSource _receiverTokenSource;
+ /// <summary>
+ /// Gets the receiver token.
+ /// </summary>
+ private CancellationToken RECEIVER_TOKEN
+ => this._receiverTokenSource.Token;
+
+ /// <summary>
+ /// Gets or sets the keepalive task.
+ /// </summary>
+ private Task _keepaliveTask;
+
+ /// <summary>
+ /// Gets or sets the keepalive token source.
+ /// </summary>
+ private CancellationTokenSource _keepaliveTokenSource;
+ /// <summary>
+ /// Gets the keepalive token.
+ /// </summary>
+ private CancellationToken KEEPALIVE_TOKEN
+ => this._keepaliveTokenSource.Token;
+
+ private volatile bool _isSpeaking;
+
+ /// <summary>
+ /// Gets the audio format used by the Opus encoder.
+ /// </summary>
+ public AudioFormat AudioFormat => this._configuration.AudioFormat;
+
+ /// <summary>
+ /// Gets whether this connection is still playing audio.
+ /// </summary>
+ public bool IsPlaying
+ => this._playingWait != null && !this._playingWait.Task.IsCompleted;
+
+ /// <summary>
+ /// Gets the websocket round-trip time in ms.
+ /// </summary>
+ public int WebSocketPing
+ => Volatile.Read(ref this._wsPing);
+ private int _wsPing;
+
+ /// <summary>
+ /// Gets the UDP round-trip time in ms.
+ /// </summary>
+ public int UdpPing
+ => Volatile.Read(ref this._udpPing);
+ private int _udpPing;
+
+ private int _queueCount;
+
+ /// <summary>
+ /// Gets the channel this voice client is connected to.
+ /// </summary>
+ public DiscordChannel TargetChannel { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VoiceNextConnection"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="guild">The guild.</param>
+ /// <param name="channel">The channel.</param>
+ /// <param name="config">The config.</param>
+ /// <param name="server">The server.</param>
+ /// <param name="state">The state.</param>
+ internal VoiceNextConnection(DiscordClient client, DiscordGuild guild, DiscordChannel channel, VoiceNextConfiguration config, VoiceServerUpdatePayload server, VoiceStateUpdatePayload state)
+ {
+ this._discord = client;
+ this._guild = guild;
+ this.TargetChannel = channel;
+ this._transmittingSsrCs = new ConcurrentDictionary<uint, AudioSender>();
+
+ this._userSpeaking = new AsyncEvent<VoiceNextConnection, UserSpeakingEventArgs>("VNEXT_USER_SPEAKING", TimeSpan.Zero, this._discord.EventErrorHandler);
+ this._userJoined = new AsyncEvent<VoiceNextConnection, VoiceUserJoinEventArgs>("VNEXT_USER_JOINED", TimeSpan.Zero, this._discord.EventErrorHandler);
+ this._userLeft = new AsyncEvent<VoiceNextConnection, VoiceUserLeaveEventArgs>("VNEXT_USER_LEFT", TimeSpan.Zero, this._discord.EventErrorHandler);
+ this._voiceReceived = new AsyncEvent<VoiceNextConnection, VoiceReceiveEventArgs>("VNEXT_VOICE_RECEIVED", TimeSpan.Zero, this._discord.EventErrorHandler);
+ this._voiceSocketError = new AsyncEvent<VoiceNextConnection, SocketErrorEventArgs>("VNEXT_WS_ERROR", TimeSpan.Zero, this._discord.EventErrorHandler);
+ this._tokenSource = new CancellationTokenSource();
- this._readyWait = new TaskCompletionSource<bool>();
+ this._configuration = config;
+ this._isInitialized = false;
+ this._isDisposed = false;
+ this._opus = new Opus(this.AudioFormat);
+ //this.Sodium = new Sodium();
+ this._rtp = new Rtp();
+
+ this.ServerData = server;
+ this.StateData = state;
+
+ var eps = this.ServerData.Endpoint;
+ var epi = eps.LastIndexOf(':');
+ var eph = string.Empty;
+ var epp = 443;
+ if (epi != -1)
+ {
+ eph = eps[..epi];
+ epp = int.Parse(eps[(epi + 1)..]);
+ }
+ else
+ {
+ eph = eps;
+ }
+ this.WebSocketEndpoint = new ConnectionEndpoint { Hostname = eph, Port = epp };
- this._playingWait = null;
- this._transmitChannel = Channel.CreateBounded<RawVoicePacket>(new BoundedChannelOptions(this._configuration.PacketQueueSize));
- this._keepaliveTimestamps = new ConcurrentDictionary<ulong, long>();
- this._pauseEvent = new AsyncManualResetEvent(true);
+ this._readyWait = new TaskCompletionSource<bool>();
- this._udpClient = this._discord.Configuration.UdpClientFactory();
- this._voiceWs = this._discord.Configuration.WebSocketClientFactory(this._discord.Configuration.Proxy, this._discord.ServiceProvider);
- this._voiceWs.Disconnected += this.VoiceWS_SocketClosed;
- this._voiceWs.MessageReceived += this.VoiceWS_SocketMessage;
- this._voiceWs.Connected += this.VoiceWS_SocketOpened;
- this._voiceWs.ExceptionThrown += this.VoiceWs_SocketException;
- }
+ this._playingWait = null;
+ this._transmitChannel = Channel.CreateBounded<RawVoicePacket>(new BoundedChannelOptions(this._configuration.PacketQueueSize));
+ this._keepaliveTimestamps = new ConcurrentDictionary<ulong, long>();
+ this._pauseEvent = new AsyncManualResetEvent(true);
- ~VoiceNextConnection()
- {
- this.Dispose();
- }
+ this._udpClient = this._discord.Configuration.UdpClientFactory();
+ this._voiceWs = this._discord.Configuration.WebSocketClientFactory(this._discord.Configuration.Proxy, this._discord.ServiceProvider);
+ this._voiceWs.Disconnected += this.VoiceWS_SocketClosed;
+ this._voiceWs.MessageReceived += this.VoiceWS_SocketMessage;
+ this._voiceWs.Connected += this.VoiceWS_SocketOpened;
+ this._voiceWs.ExceptionThrown += this.VoiceWs_SocketException;
+ }
- /// <summary>
- /// Connects to the specified voice channel.
- /// </summary>
- /// <returns>A task representing the connection operation.</returns>
- internal Task ConnectAsync()
- {
- var gwuri = new UriBuilder
+ ~VoiceNextConnection()
{
- Scheme = "wss",
- Host = this.WebSocketEndpoint.Hostname,
- Query = "encoding=json&v=4"
- };
-
- return this._voiceWs.ConnectAsync(gwuri.Uri);
- }
-
- /// <summary>
- /// Reconnects .
- /// </summary>
- /// <returns>A Task.</returns>
- internal Task ReconnectAsync()
- => this._voiceWs.DisconnectAsync();
-
- /// <summary>
- /// Starts .
- /// </summary>
- /// <returns>A Task.</returns>
- internal async Task StartAsync()
- {
- // Let's announce our intentions to the server
- var vdp = new VoiceDispatch();
+ this.Dispose();
+ }
- if (!this.Resume)
+ /// <summary>
+ /// Connects to the specified voice channel.
+ /// </summary>
+ /// <returns>A task representing the connection operation.</returns>
+ internal Task ConnectAsync()
{
- vdp.OpCode = 0;
- vdp.Payload = new VoiceIdentifyPayload
+ var gwuri = new UriBuilder
{
- ServerId = this.ServerData.GuildId,
- UserId = this.StateData.UserId.Value,
- SessionId = this.StateData.SessionId,
- Token = this.ServerData.Token
+ Scheme = "wss",
+ Host = this.WebSocketEndpoint.Hostname,
+ Query = "encoding=json&v=4"
};
- this.Resume = true;
+
+ return this._voiceWs.ConnectAsync(gwuri.Uri);
}
- else
+
+ /// <summary>
+ /// Reconnects .
+ /// </summary>
+ /// <returns>A Task.</returns>
+ internal Task ReconnectAsync()
+ => this._voiceWs.DisconnectAsync();
+
+ /// <summary>
+ /// Starts .
+ /// </summary>
+ /// <returns>A Task.</returns>
+ internal async Task StartAsync()
{
- vdp.OpCode = 7;
- vdp.Payload = new VoiceIdentifyPayload
+ // Let's announce our intentions to the server
+ var vdp = new VoiceDispatch();
+
+ if (!this.Resume)
{
- ServerId = this.ServerData.GuildId,
- SessionId = this.StateData.SessionId,
- Token = this.ServerData.Token
- };
+ vdp.OpCode = 0;
+ vdp.Payload = new VoiceIdentifyPayload
+ {
+ ServerId = this.ServerData.GuildId,
+ UserId = this.StateData.UserId.Value,
+ SessionId = this.StateData.SessionId,
+ Token = this.ServerData.Token
+ };
+ this.Resume = true;
+ }
+ else
+ {
+ vdp.OpCode = 7;
+ vdp.Payload = new VoiceIdentifyPayload
+ {
+ ServerId = this.ServerData.GuildId,
+ SessionId = this.StateData.SessionId,
+ Token = this.ServerData.Token
+ };
+ }
+ var vdj = JsonConvert.SerializeObject(vdp, Formatting.None);
+ await this.WsSendAsync(vdj).ConfigureAwait(false);
}
- var vdj = JsonConvert.SerializeObject(vdp, Formatting.None);
- await this.WsSendAsync(vdj).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Waits the for ready async.
- /// </summary>
- /// <returns>A Task.</returns>
- internal Task WaitForReadyAsync()
- => this._readyWait.Task;
-
- /// <summary>
- /// Enqueues the packet async.
- /// </summary>
- /// <param name="packet">The packet.</param>
- /// <param name="token">The token.</param>
- /// <returns>A Task.</returns>
- internal async Task EnqueuePacketAsync(RawVoicePacket packet, CancellationToken token = default)
- {
- await this._transmitChannel.Writer.WriteAsync(packet, token).ConfigureAwait(false);
- this._queueCount++;
- }
- /// <summary>
- /// Prepares the packet.
- /// </summary>
- /// <param name="pcm">The pcm.</param>
- /// <param name="target">The target.</param>
- /// <param name="length">The length.</param>
- /// <returns>A bool.</returns>
- internal bool PreparePacket(ReadOnlySpan<byte> pcm, out byte[] target, out int length)
- {
- target = null;
- length = 0;
+ /// <summary>
+ /// Waits the for ready async.
+ /// </summary>
+ /// <returns>A Task.</returns>
+ internal Task WaitForReadyAsync()
+ => this._readyWait.Task;
+
+ /// <summary>
+ /// Enqueues the packet async.
+ /// </summary>
+ /// <param name="packet">The packet.</param>
+ /// <param name="token">The token.</param>
+ /// <returns>A Task.</returns>
+ internal async Task EnqueuePacketAsync(RawVoicePacket packet, CancellationToken token = default)
+ {
+ await this._transmitChannel.Writer.WriteAsync(packet, token).ConfigureAwait(false);
+ this._queueCount++;
+ }
- if (this._isDisposed)
- return false;
+ /// <summary>
+ /// Prepares the packet.
+ /// </summary>
+ /// <param name="pcm">The pcm.</param>
+ /// <param name="target">The target.</param>
+ /// <param name="length">The length.</param>
+ /// <returns>A bool.</returns>
+ internal bool PreparePacket(ReadOnlySpan<byte> pcm, out byte[] target, out int length)
+ {
+ target = null;
+ length = 0;
- var audioFormat = this.AudioFormat;
+ if (this._isDisposed)
+ return false;
- var packetArray = ArrayPool<byte>.Shared.Rent(this._rtp.CalculatePacketSize(audioFormat.SampleCountToSampleSize(audioFormat.CalculateMaximumFrameSize()), this._selectedEncryptionMode));
- var packet = packetArray.AsSpan();
+ var audioFormat = this.AudioFormat;
- this._rtp.EncodeHeader(this._sequence, this._timestamp, this._ssrc, packet);
- var opus = packet.Slice(Rtp.HEADER_SIZE, pcm.Length);
- this._opus.Encode(pcm, ref opus);
+ var packetArray = ArrayPool<byte>.Shared.Rent(this._rtp.CalculatePacketSize(audioFormat.SampleCountToSampleSize(audioFormat.CalculateMaximumFrameSize()), this._selectedEncryptionMode));
+ var packet = packetArray.AsSpan();
- this._sequence++;
- this._timestamp += (uint)audioFormat.CalculateFrameSize(audioFormat.CalculateSampleDuration(pcm.Length));
+ this._rtp.EncodeHeader(this._sequence, this._timestamp, this._ssrc, packet);
+ var opus = packet.Slice(Rtp.HEADER_SIZE, pcm.Length);
+ this._opus.Encode(pcm, ref opus);
- Span<byte> nonce = stackalloc byte[Sodium.NonceSize];
- switch (this._selectedEncryptionMode)
- {
- case EncryptionMode.XSalsa20Poly1305:
- this._sodium.GenerateNonce(packet[..Rtp.HEADER_SIZE], nonce);
- break;
+ this._sequence++;
+ this._timestamp += (uint)audioFormat.CalculateFrameSize(audioFormat.CalculateSampleDuration(pcm.Length));
- case EncryptionMode.XSalsa20Poly1305Suffix:
- this._sodium.GenerateNonce(nonce);
- break;
+ Span<byte> nonce = stackalloc byte[Sodium.NonceSize];
+ switch (this._selectedEncryptionMode)
+ {
+ case EncryptionMode.XSalsa20Poly1305:
+ this._sodium.GenerateNonce(packet[..Rtp.HEADER_SIZE], nonce);
+ break;
- case EncryptionMode.XSalsa20Poly1305Lite:
- this._sodium.GenerateNonce(this._nonce++, nonce);
- break;
+ case EncryptionMode.XSalsa20Poly1305Suffix:
+ this._sodium.GenerateNonce(nonce);
+ break;
- default:
- ArrayPool<byte>.Shared.Return(packetArray);
- throw new Exception("Unsupported encryption mode.");
- }
+ case EncryptionMode.XSalsa20Poly1305Lite:
+ this._sodium.GenerateNonce(this._nonce++, nonce);
+ break;
- Span<byte> encrypted = stackalloc byte[Sodium.CalculateTargetSize(opus)];
- this._sodium.Encrypt(opus, encrypted, nonce);
- encrypted.CopyTo(packet[Rtp.HEADER_SIZE..]);
- packet = packet[..this._rtp.CalculatePacketSize(encrypted.Length, this._selectedEncryptionMode)];
- this._sodium.AppendNonce(nonce, packet, this._selectedEncryptionMode);
+ default:
+ ArrayPool<byte>.Shared.Return(packetArray);
+ throw new Exception("Unsupported encryption mode.");
+ }
- target = packetArray;
- length = packet.Length;
- return true;
- }
+ Span<byte> encrypted = stackalloc byte[Sodium.CalculateTargetSize(opus)];
+ this._sodium.Encrypt(opus, encrypted, nonce);
+ encrypted.CopyTo(packet[Rtp.HEADER_SIZE..]);
+ packet = packet[..this._rtp.CalculatePacketSize(encrypted.Length, this._selectedEncryptionMode)];
+ this._sodium.AppendNonce(nonce, packet, this._selectedEncryptionMode);
- /// <summary>
- /// Voices the sender task.
- /// </summary>
- /// <returns>A Task.</returns>
- private async Task VoiceSenderTask()
- {
- var token = this.SENDER_TOKEN;
- var client = this._udpClient;
- var reader = this._transmitChannel.Reader;
+ target = packetArray;
+ length = packet.Length;
+ return true;
+ }
- byte[] data = null;
- var length = 0;
+ /// <summary>
+ /// Voices the sender task.
+ /// </summary>
+ /// <returns>A Task.</returns>
+ private async Task VoiceSenderTask()
+ {
+ var token = this.SENDER_TOKEN;
+ var client = this._udpClient;
+ var reader = this._transmitChannel.Reader;
- var synchronizerTicks = (double)Stopwatch.GetTimestamp();
- var synchronizerResolution = Stopwatch.Frequency * 0.005;
- var tickResolution = 10_000_000.0 / Stopwatch.Frequency;
- this._discord.Logger.LogDebug(VoiceNextEvents.Misc, "Timer accuracy: {0}/{1} (high resolution? {2})", Stopwatch.Frequency, synchronizerResolution, Stopwatch.IsHighResolution);
+ byte[] data = null;
+ var length = 0;
- while (!token.IsCancellationRequested)
- {
- await this._pauseEvent.WaitAsync().ConfigureAwait(false);
+ var synchronizerTicks = (double)Stopwatch.GetTimestamp();
+ var synchronizerResolution = Stopwatch.Frequency * 0.005;
+ var tickResolution = 10_000_000.0 / Stopwatch.Frequency;
+ this._discord.Logger.LogDebug(VoiceNextEvents.Misc, "Timer accuracy: {0}/{1} (high resolution? {2})", Stopwatch.Frequency, synchronizerResolution, Stopwatch.IsHighResolution);
- var hasPacket = reader.TryRead(out var rawPacket);
- if (hasPacket)
+ while (!token.IsCancellationRequested)
{
- this._queueCount--;
+ await this._pauseEvent.WaitAsync().ConfigureAwait(false);
- if (this._playingWait == null || this._playingWait.Task.IsCompleted)
- this._playingWait = new TaskCompletionSource<bool>();
- }
+ var hasPacket = reader.TryRead(out var rawPacket);
+ if (hasPacket)
+ {
+ this._queueCount--;
- // Provided by Laura#0090 (214796473689178133); this is Python, but adaptable:
- //
- // delay = max(0, self.delay + ((start_time + self.delay * loops) + - time.time()))
- //
- // self.delay
- // sample size
- // start_time
- // time since streaming started
- // loops
- // number of samples sent
- // time.time()
- // DateTime.Now
-
- if (hasPacket)
- {
- hasPacket = this.PreparePacket(rawPacket.Bytes.Span, out data, out length);
- if (rawPacket.RentedBuffer != null)
- ArrayPool<byte>.Shared.Return(rawPacket.RentedBuffer);
- }
+ if (this._playingWait == null || this._playingWait.Task.IsCompleted)
+ this._playingWait = new TaskCompletionSource<bool>();
+ }
- var durationModifier = hasPacket ? rawPacket.Duration / 5 : 4;
- var cts = Math.Max(Stopwatch.GetTimestamp() - synchronizerTicks, 0);
- if (cts < synchronizerResolution * durationModifier)
- await Task.Delay(TimeSpan.FromTicks((long)(((synchronizerResolution * durationModifier) - cts) * tickResolution))).ConfigureAwait(false);
+ // Provided by Laura#0090 (214796473689178133); this is Python, but adaptable:
+ //
+ // delay = max(0, self.delay + ((start_time + self.delay * loops) + - time.time()))
+ //
+ // self.delay
+ // sample size
+ // start_time
+ // time since streaming started
+ // loops
+ // number of samples sent
+ // time.time()
+ // DateTime.Now
+
+ if (hasPacket)
+ {
+ hasPacket = this.PreparePacket(rawPacket.Bytes.Span, out data, out length);
+ if (rawPacket.RentedBuffer != null)
+ ArrayPool<byte>.Shared.Return(rawPacket.RentedBuffer);
+ }
- synchronizerTicks += synchronizerResolution * durationModifier;
+ var durationModifier = hasPacket ? rawPacket.Duration / 5 : 4;
+ var cts = Math.Max(Stopwatch.GetTimestamp() - synchronizerTicks, 0);
+ if (cts < synchronizerResolution * durationModifier)
+ await Task.Delay(TimeSpan.FromTicks((long)(((synchronizerResolution * durationModifier) - cts) * tickResolution))).ConfigureAwait(false);
- if (!hasPacket)
- continue;
+ synchronizerTicks += synchronizerResolution * durationModifier;
- await this.SendSpeakingAsync(true).ConfigureAwait(false);
- await client.SendAsync(data, length).ConfigureAwait(false);
- ArrayPool<byte>.Shared.Return(data);
+ if (!hasPacket)
+ continue;
- if (!rawPacket.Silence && this._queueCount == 0)
- {
- var nullpcm = new byte[this.AudioFormat.CalculateSampleSize(20)];
- for (var i = 0; i < 3; i++)
+ await this.SendSpeakingAsync(true).ConfigureAwait(false);
+ await client.SendAsync(data, length).ConfigureAwait(false);
+ ArrayPool<byte>.Shared.Return(data);
+
+ if (!rawPacket.Silence && this._queueCount == 0)
{
- var nullpacket = new byte[nullpcm.Length];
- var nullpacketmem = nullpacket.AsMemory();
- await this.EnqueuePacketAsync(new RawVoicePacket(nullpacketmem, 20, true)).ConfigureAwait(false);
+ var nullpcm = new byte[this.AudioFormat.CalculateSampleSize(20)];
+ for (var i = 0; i < 3; i++)
+ {
+ var nullpacket = new byte[nullpcm.Length];
+ var nullpacketmem = nullpacket.AsMemory();
+ await this.EnqueuePacketAsync(new RawVoicePacket(nullpacketmem, 20, true)).ConfigureAwait(false);
+ }
+ }
+ else if (this._queueCount == 0)
+ {
+ await this.SendSpeakingAsync(false).ConfigureAwait(false);
+ this._playingWait?.SetResult(true);
}
- }
- else if (this._queueCount == 0)
- {
- await this.SendSpeakingAsync(false).ConfigureAwait(false);
- this._playingWait?.SetResult(true);
}
}
- }
-
- /// <summary>
- /// Processes the packet.
- /// </summary>
- /// <param name="data">The data.</param>
- /// <param name="opus">The opus.</param>
- /// <param name="pcm">The pcm.</param>
- /// <param name="pcmPackets">The pcm packets.</param>
- /// <param name="voiceSender">The voice sender.</param>
- /// <param name="outputFormat">The output format.</param>
- /// <returns>A bool.</returns>
- private bool ProcessPacket(ReadOnlySpan<byte> data, ref Memory<byte> opus, ref Memory<byte> pcm, IList<ReadOnlyMemory<byte>> pcmPackets, out AudioSender voiceSender, out AudioFormat outputFormat)
- {
- voiceSender = null;
- outputFormat = default;
- if (!this._rtp.IsRtpHeader(data))
- return false;
+ /// <summary>
+ /// Processes the packet.
+ /// </summary>
+ /// <param name="data">The data.</param>
+ /// <param name="opus">The opus.</param>
+ /// <param name="pcm">The pcm.</param>
+ /// <param name="pcmPackets">The pcm packets.</param>
+ /// <param name="voiceSender">The voice sender.</param>
+ /// <param name="outputFormat">The output format.</param>
+ /// <returns>A bool.</returns>
+ private bool ProcessPacket(ReadOnlySpan<byte> data, ref Memory<byte> opus, ref Memory<byte> pcm, IList<ReadOnlyMemory<byte>> pcmPackets, out AudioSender voiceSender, out AudioFormat outputFormat)
+ {
+ voiceSender = null;
+ outputFormat = default;
- this._rtp.DecodeHeader(data, out var shortSequence, out var timestamp, out var ssrc, out var hasExtension);
+ if (!this._rtp.IsRtpHeader(data))
+ return false;
- if (!this._transmittingSsrCs.TryGetValue(ssrc, out var vtx))
- {
- var decoder = this._opus.CreateDecoder();
+ this._rtp.DecodeHeader(data, out var shortSequence, out var timestamp, out var ssrc, out var hasExtension);
- vtx = new AudioSender(ssrc, decoder)
+ if (!this._transmittingSsrCs.TryGetValue(ssrc, out var vtx))
{
- // user isn't present as we haven't received a speaking event yet.
- User = null
- };
- }
+ var decoder = this._opus.CreateDecoder();
- voiceSender = vtx;
- ulong sequence = vtx.GetTrueSequenceAfterWrapping(shortSequence);
- ushort gap = 0;
- if (vtx.LastTrueSequence is ulong lastTrueSequence)
- {
- if (sequence <= lastTrueSequence) // out-of-order packet; discard
- return false;
+ vtx = new AudioSender(ssrc, decoder)
+ {
+ // user isn't present as we haven't received a speaking event yet.
+ User = null
+ };
+ }
- gap = (ushort)(sequence - 1 - lastTrueSequence);
- if (gap >= 5)
- this._discord.Logger.LogWarning(VoiceNextEvents.VoiceReceiveFailure, "5 or more voice packets were dropped when receiving");
- }
+ voiceSender = vtx;
+ ulong sequence = vtx.GetTrueSequenceAfterWrapping(shortSequence);
+ ushort gap = 0;
+ if (vtx.LastTrueSequence is ulong lastTrueSequence)
+ {
+ if (sequence <= lastTrueSequence) // out-of-order packet; discard
+ return false;
- Span<byte> nonce = stackalloc byte[Sodium.NonceSize];
- this._sodium.GetNonce(data, nonce, this._selectedEncryptionMode);
- this._rtp.GetDataFromPacket(data, out var encryptedOpus, this._selectedEncryptionMode);
+ gap = (ushort)(sequence - 1 - lastTrueSequence);
+ if (gap >= 5)
+ this._discord.Logger.LogWarning(VoiceNextEvents.VoiceReceiveFailure, "5 or more voice packets were dropped when receiving");
+ }
- var opusSize = Sodium.CalculateSourceSize(encryptedOpus);
- opus = opus[..opusSize];
- var opusSpan = opus.Span;
- try
- {
- this._sodium.Decrypt(encryptedOpus, opusSpan, nonce);
+ Span<byte> nonce = stackalloc byte[Sodium.NonceSize];
+ this._sodium.GetNonce(data, nonce, this._selectedEncryptionMode);
+ this._rtp.GetDataFromPacket(data, out var encryptedOpus, this._selectedEncryptionMode);
- // Strip extensions, if any
- if (hasExtension)
+ var opusSize = Sodium.CalculateSourceSize(encryptedOpus);
+ opus = opus[..opusSize];
+ var opusSpan = opus.Span;
+ try
{
- // RFC 5285, 4.2 One-Byte header
- // http://www.rfcreader.com/#rfc5285_line186
- if (opusSpan[0] == 0xBE && opusSpan[1] == 0xDE)
+ this._sodium.Decrypt(encryptedOpus, opusSpan, nonce);
+
+ // Strip extensions, if any
+ if (hasExtension)
{
- var headerLen = (opusSpan[2] << 8) | opusSpan[3];
- var i = 4;
- for (; i < headerLen + 4; i++)
+ // RFC 5285, 4.2 One-Byte header
+ // http://www.rfcreader.com/#rfc5285_line186
+ if (opusSpan[0] == 0xBE && opusSpan[1] == 0xDE)
{
- var @byte = opusSpan[i];
+ var headerLen = (opusSpan[2] << 8) | opusSpan[3];
+ var i = 4;
+ for (; i < headerLen + 4; i++)
+ {
+ var @byte = opusSpan[i];
- // ID is currently unused since we skip it anyway
- //var id = (byte)(@byte >> 4);
- var length = (byte)(@byte & 0x0F) + 1;
+ // ID is currently unused since we skip it anyway
+ //var id = (byte)(@byte >> 4);
+ var length = (byte)(@byte & 0x0F) + 1;
- i += length;
- }
+ i += length;
+ }
- // Strip extension padding too
- while (opusSpan[i] == 0)
- i++;
+ // Strip extension padding too
+ while (opusSpan[i] == 0)
+ i++;
- opusSpan = opusSpan[i..];
- }
+ opusSpan = opusSpan[i..];
+ }
- // TODO: consider implementing RFC 5285, 4.3. Two-Byte Header
- }
+ // TODO: consider implementing RFC 5285, 4.3. Two-Byte Header
+ }
- if (opusSpan[0] == 0x90)
- {
- // I'm not 100% sure what this header is/does, however removing the data causes no
- // real issues, and has the added benefit of removing a lot of noise.
- opusSpan = opusSpan[2..];
- }
+ if (opusSpan[0] == 0x90)
+ {
+ // I'm not 100% sure what this header is/does, however removing the data causes no
+ // real issues, and has the added benefit of removing a lot of noise.
+ opusSpan = opusSpan[2..];
+ }
- if (gap == 1)
- {
- var lastSampleCount = this._opus.GetLastPacketSampleCount(vtx.Decoder);
- var fecpcm = new byte[this.AudioFormat.SampleCountToSampleSize(lastSampleCount)];
- var fecpcmMem = fecpcm.AsSpan();
- this._opus.Decode(vtx.Decoder, opusSpan, ref fecpcmMem, true, out _);
- pcmPackets.Add(fecpcm.AsMemory(0, fecpcmMem.Length));
- }
- else if (gap > 1)
- {
- var lastSampleCount = this._opus.GetLastPacketSampleCount(vtx.Decoder);
- for (var i = 0; i < gap; i++)
+ if (gap == 1)
{
+ var lastSampleCount = this._opus.GetLastPacketSampleCount(vtx.Decoder);
var fecpcm = new byte[this.AudioFormat.SampleCountToSampleSize(lastSampleCount)];
var fecpcmMem = fecpcm.AsSpan();
- this._opus.ProcessPacketLoss(vtx.Decoder, lastSampleCount, ref fecpcmMem);
+ this._opus.Decode(vtx.Decoder, opusSpan, ref fecpcmMem, true, out _);
pcmPackets.Add(fecpcm.AsMemory(0, fecpcmMem.Length));
}
+ else if (gap > 1)
+ {
+ var lastSampleCount = this._opus.GetLastPacketSampleCount(vtx.Decoder);
+ for (var i = 0; i < gap; i++)
+ {
+ var fecpcm = new byte[this.AudioFormat.SampleCountToSampleSize(lastSampleCount)];
+ var fecpcmMem = fecpcm.AsSpan();
+ this._opus.ProcessPacketLoss(vtx.Decoder, lastSampleCount, ref fecpcmMem);
+ pcmPackets.Add(fecpcm.AsMemory(0, fecpcmMem.Length));
+ }
+ }
+
+ var pcmSpan = pcm.Span;
+ this._opus.Decode(vtx.Decoder, opusSpan, ref pcmSpan, false, out outputFormat);
+ pcm = pcm[..pcmSpan.Length];
+ }
+ finally
+ {
+ vtx.LastTrueSequence = sequence;
}
- var pcmSpan = pcm.Span;
- this._opus.Decode(vtx.Decoder, opusSpan, ref pcmSpan, false, out outputFormat);
- pcm = pcm[..pcmSpan.Length];
- }
- finally
- {
- vtx.LastTrueSequence = sequence;
+ return true;
}
- return true;
- }
-
- /// <summary>
- /// Processes the voice packet.
- /// </summary>
- /// <param name="data">The data.</param>
- /// <returns>A Task.</returns>
- private async Task ProcessVoicePacket(byte[] data)
- {
- if (data.Length < 13) // minimum packet length
- return;
-
- try
+ /// <summary>
+ /// Processes the voice packet.
+ /// </summary>
+ /// <param name="data">The data.</param>
+ /// <returns>A Task.</returns>
+ private async Task ProcessVoicePacket(byte[] data)
{
- var pcm = new byte[this.AudioFormat.CalculateMaximumFrameSize()];
- var pcmMem = pcm.AsMemory();
- var opus = new byte[pcm.Length];
- var opusMem = opus.AsMemory();
- var pcmFillers = new List<ReadOnlyMemory<byte>>();
- if (!this.ProcessPacket(data, ref opusMem, ref pcmMem, pcmFillers, out var vtx, out var audioFormat))
+ if (data.Length < 13) // minimum packet length
return;
- foreach (var pcmFiller in pcmFillers)
+ try
+ {
+ var pcm = new byte[this.AudioFormat.CalculateMaximumFrameSize()];
+ var pcmMem = pcm.AsMemory();
+ var opus = new byte[pcm.Length];
+ var opusMem = opus.AsMemory();
+ var pcmFillers = new List<ReadOnlyMemory<byte>>();
+ if (!this.ProcessPacket(data, ref opusMem, ref pcmMem, pcmFillers, out var vtx, out var audioFormat))
+ return;
+
+ foreach (var pcmFiller in pcmFillers)
+ await this._voiceReceived.InvokeAsync(this, new VoiceReceiveEventArgs(this._discord.ServiceProvider)
+ {
+ Ssrc = vtx.Ssrc,
+ User = vtx.User,
+ PcmData = pcmFiller,
+ OpusData = new byte[0].AsMemory(),
+ AudioFormat = audioFormat,
+ AudioDuration = audioFormat.CalculateSampleDuration(pcmFiller.Length)
+ }).ConfigureAwait(false);
+
await this._voiceReceived.InvokeAsync(this, new VoiceReceiveEventArgs(this._discord.ServiceProvider)
{
Ssrc = vtx.Ssrc,
User = vtx.User,
- PcmData = pcmFiller,
- OpusData = new byte[0].AsMemory(),
+ PcmData = pcmMem,
+ OpusData = opusMem,
AudioFormat = audioFormat,
- AudioDuration = audioFormat.CalculateSampleDuration(pcmFiller.Length)
+ AudioDuration = audioFormat.CalculateSampleDuration(pcmMem.Length)
}).ConfigureAwait(false);
-
- await this._voiceReceived.InvokeAsync(this, new VoiceReceiveEventArgs(this._discord.ServiceProvider)
+ }
+ catch (Exception ex)
{
- Ssrc = vtx.Ssrc,
- User = vtx.User,
- PcmData = pcmMem,
- OpusData = opusMem,
- AudioFormat = audioFormat,
- AudioDuration = audioFormat.CalculateSampleDuration(pcmMem.Length)
- }).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- this._discord.Logger.LogError(VoiceNextEvents.VoiceReceiveFailure, ex, "Exception occurred when decoding incoming audio data");
+ this._discord.Logger.LogError(VoiceNextEvents.VoiceReceiveFailure, ex, "Exception occurred when decoding incoming audio data");
+ }
}
- }
- /// <summary>
- /// Processes the keepalive.
- /// </summary>
- /// <param name="data">The data.</param>
- private void ProcessKeepalive(byte[] data)
- {
- try
+ /// <summary>
+ /// Processes the keepalive.
+ /// </summary>
+ /// <param name="data">The data.</param>
+ private void ProcessKeepalive(byte[] data)
{
- var keepalive = BinaryPrimitives.ReadUInt64LittleEndian(data);
+ try
+ {
+ var keepalive = BinaryPrimitives.ReadUInt64LittleEndian(data);
- if (!this._keepaliveTimestamps.TryRemove(keepalive, out var timestamp))
- return;
+ if (!this._keepaliveTimestamps.TryRemove(keepalive, out var timestamp))
+ return;
- var tdelta = (int)((Stopwatch.GetTimestamp() - timestamp) / (double)Stopwatch.Frequency * 1000);
- this._discord.Logger.LogDebug(VoiceNextEvents.VoiceKeepalive, "Received UDP keepalive {0} (ping {1}ms)", keepalive, tdelta);
- Volatile.Write(ref this._udpPing, tdelta);
- }
- catch (Exception ex)
- {
- this._discord.Logger.LogError(VoiceNextEvents.VoiceKeepalive, ex, "Exception occurred when handling keepalive");
+ var tdelta = (int)((Stopwatch.GetTimestamp() - timestamp) / (double)Stopwatch.Frequency * 1000);
+ this._discord.Logger.LogDebug(VoiceNextEvents.VoiceKeepalive, "Received UDP keepalive {0} (ping {1}ms)", keepalive, tdelta);
+ Volatile.Write(ref this._udpPing, tdelta);
+ }
+ catch (Exception ex)
+ {
+ this._discord.Logger.LogError(VoiceNextEvents.VoiceKeepalive, ex, "Exception occurred when handling keepalive");
+ }
}
- }
-
- /// <summary>
- /// Udps the receiver task.
- /// </summary>
- /// <returns>A Task.</returns>
- private async Task UdpReceiverTask()
- {
- var token = this.RECEIVER_TOKEN;
- var client = this._udpClient;
- while (!token.IsCancellationRequested)
+ /// <summary>
+ /// Udps the receiver task.
+ /// </summary>
+ /// <returns>A Task.</returns>
+ private async Task UdpReceiverTask()
{
- var data = await client.ReceiveAsync().ConfigureAwait(false);
- if (data.Length == 8)
- this.ProcessKeepalive(data);
- else if (this._configuration.EnableIncoming)
- await this.ProcessVoicePacket(data).ConfigureAwait(false);
- }
- }
+ var token = this.RECEIVER_TOKEN;
+ var client = this._udpClient;
- /// <summary>
- /// Sends a speaking status to the connected voice channel.
- /// </summary>
- /// <param name="speaking">Whether the current user is speaking or not.</param>
- /// <returns>A task representing the sending operation.</returns>
- public async Task SendSpeakingAsync(bool speaking = true)
- {
- if (!this._isInitialized)
- throw new InvalidOperationException("The connection is not initialized");
+ while (!token.IsCancellationRequested)
+ {
+ var data = await client.ReceiveAsync().ConfigureAwait(false);
+ if (data.Length == 8)
+ this.ProcessKeepalive(data);
+ else if (this._configuration.EnableIncoming)
+ await this.ProcessVoicePacket(data).ConfigureAwait(false);
+ }
+ }
- if (this._isSpeaking != speaking)
+ /// <summary>
+ /// Sends a speaking status to the connected voice channel.
+ /// </summary>
+ /// <param name="speaking">Whether the current user is speaking or not.</param>
+ /// <returns>A task representing the sending operation.</returns>
+ public async Task SendSpeakingAsync(bool speaking = true)
{
- this._isSpeaking = speaking;
- var pld = new VoiceDispatch
+ if (!this._isInitialized)
+ throw new InvalidOperationException("The connection is not initialized");
+
+ if (this._isSpeaking != speaking)
{
- OpCode = 5,
- Payload = new VoiceSpeakingPayload
+ this._isSpeaking = speaking;
+ var pld = new VoiceDispatch
{
- Speaking = speaking,
- Delay = 0
- }
- };
+ OpCode = 5,
+ Payload = new VoiceSpeakingPayload
+ {
+ Speaking = speaking,
+ Delay = 0
+ }
+ };
- var plj = JsonConvert.SerializeObject(pld, Formatting.None);
- await this.WsSendAsync(plj).ConfigureAwait(false);
+ var plj = JsonConvert.SerializeObject(pld, Formatting.None);
+ await this.WsSendAsync(plj).ConfigureAwait(false);
+ }
}
- }
-
- /// <summary>
- /// Gets a transmit stream for this connection, optionally specifying a packet size to use with the stream. If a stream is already configured, it will return the existing one.
- /// </summary>
- /// <param name="sampleDuration">Duration, in ms, to use for audio packets.</param>
- /// <returns>Transmit stream.</returns>
- public VoiceTransmitSink GetTransmitSink(int sampleDuration = 20)
- {
- if (!AudioFormat.AllowedSampleDurations.Contains(sampleDuration))
- throw new ArgumentOutOfRangeException(nameof(sampleDuration), "Invalid PCM sample duration specified.");
-
- if (this._transmitStream == null)
- this._transmitStream = new VoiceTransmitSink(this, sampleDuration);
-
- return this._transmitStream;
- }
-
- /// <summary>
- /// Asynchronously waits for playback to be finished. Playback is finished when speaking = false is signaled.
- /// </summary>
- /// <returns>A task representing the waiting operation.</returns>
- public async Task WaitForPlaybackFinishAsync()
- {
- if (this._playingWait != null)
- await this._playingWait.Task.ConfigureAwait(false);
- }
-
- /// <summary>
- /// Pauses playback.
- /// </summary>
- public void Pause()
- => this._pauseEvent.Reset();
- /// <summary>
- /// Asynchronously resumes playback.
- /// </summary>
- /// <returns></returns>
- public async Task ResumeAsync()
- => await this._pauseEvent.SetAsync().ConfigureAwait(false);
-
- /// <summary>
- /// Disconnects and disposes this voice connection.
- /// </summary>
- public void Disconnect()
- => this.Dispose();
+ /// <summary>
+ /// Gets a transmit stream for this connection, optionally specifying a packet size to use with the stream. If a stream is already configured, it will return the existing one.
+ /// </summary>
+ /// <param name="sampleDuration">Duration, in ms, to use for audio packets.</param>
+ /// <returns>Transmit stream.</returns>
+ public VoiceTransmitSink GetTransmitSink(int sampleDuration = 20)
+ {
+ if (!AudioFormat.AllowedSampleDurations.Contains(sampleDuration))
+ throw new ArgumentOutOfRangeException(nameof(sampleDuration), "Invalid PCM sample duration specified.");
- /// <summary>
- /// Disconnects and disposes this voice connection.
- /// </summary>
- public void Dispose()
- {
- if (this._isDisposed)
- return;
+ if (this._transmitStream == null)
+ this._transmitStream = new VoiceTransmitSink(this, sampleDuration);
- try
- {
- this._isDisposed = true;
- this._isInitialized = false;
- this._tokenSource?.Cancel();
- this._senderTokenSource?.Cancel();
- this._receiverTokenSource?.Cancel();
- }
- catch (Exception ex)
- {
- this._discord.Logger.LogError(ex, ex.Message);
+ return this._transmitStream;
}
- try
+ /// <summary>
+ /// Asynchronously waits for playback to be finished. Playback is finished when speaking = false is signaled.
+ /// </summary>
+ /// <returns>A task representing the waiting operation.</returns>
+ public async Task WaitForPlaybackFinishAsync()
{
- this._voiceWs.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult();
- this._udpClient.Close();
+ if (this._playingWait != null)
+ await this._playingWait.Task.ConfigureAwait(false);
}
- catch { }
- try
- {
- this._keepaliveTokenSource?.Cancel();
- this._tokenSource?.Dispose();
- this._senderTokenSource?.Dispose();
- this._receiverTokenSource?.Dispose();
- this._keepaliveTokenSource?.Dispose();
- this._opus?.Dispose();
- this._opus = null;
- this._sodium?.Dispose();
- this._sodium = null;
- this._rtp?.Dispose();
- this._rtp = null;
- }
- catch (Exception ex)
+ /// <summary>
+ /// Pauses playback.
+ /// </summary>
+ public void Pause()
+ => this._pauseEvent.Reset();
+
+ /// <summary>
+ /// Asynchronously resumes playback.
+ /// </summary>
+ /// <returns></returns>
+ public async Task ResumeAsync()
+ => await this._pauseEvent.SetAsync().ConfigureAwait(false);
+
+ /// <summary>
+ /// Disconnects and disposes this voice connection.
+ /// </summary>
+ public void Disconnect()
+ => this.Dispose();
+
+ /// <summary>
+ /// Disconnects and disposes this voice connection.
+ /// </summary>
+ public void Dispose()
{
- this._discord.Logger.LogError(ex, ex.Message);
- }
-
- this.VoiceDisconnected?.Invoke(this._guild);
- }
-
- /// <summary>
- /// Heartbeats .
- /// </summary>
- /// <returns>A Task.</returns>
- private async Task HeartbeatAsync()
- {
- await Task.Yield();
+ if (this._isDisposed)
+ return;
- var token = this.TOKEN;
- while (true)
- {
try
{
- token.ThrowIfCancellationRequested();
-
- var dt = DateTime.Now;
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHeartbeat, "Sent heartbeat");
+ this._isDisposed = true;
+ this._isInitialized = false;
+ this._tokenSource?.Cancel();
+ this._senderTokenSource?.Cancel();
+ this._receiverTokenSource?.Cancel();
+ }
+ catch (Exception ex)
+ {
+ this._discord.Logger.LogError(ex, ex.Message);
+ }
- var hbd = new VoiceDispatch
- {
- OpCode = 3,
- Payload = UnixTimestamp(dt)
- };
- var hbj = JsonConvert.SerializeObject(hbd);
- await this.WsSendAsync(hbj).ConfigureAwait(false);
+ try
+ {
+ this._voiceWs.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult();
+ this._udpClient.Close();
+ }
+ catch { }
- this._lastHeartbeat = dt;
- await Task.Delay(this._heartbeatInterval).ConfigureAwait(false);
+ try
+ {
+ this._keepaliveTokenSource?.Cancel();
+ this._tokenSource?.Dispose();
+ this._senderTokenSource?.Dispose();
+ this._receiverTokenSource?.Dispose();
+ this._keepaliveTokenSource?.Dispose();
+ this._opus?.Dispose();
+ this._opus = null;
+ this._sodium?.Dispose();
+ this._sodium = null;
+ this._rtp?.Dispose();
+ this._rtp = null;
}
- catch (OperationCanceledException)
+ catch (Exception ex)
{
- return;
+ this._discord.Logger.LogError(ex, ex.Message);
}
- }
- }
-
- /// <summary>
- /// Keepalives .
- /// </summary>
- /// <returns>A Task.</returns>
- private async Task KeepaliveAsync()
- {
- await Task.Yield();
- var token = this.KEEPALIVE_TOKEN;
- var client = this._udpClient;
+ this.VoiceDisconnected?.Invoke(this._guild);
+ }
- while (!token.IsCancellationRequested)
+ /// <summary>
+ /// Heartbeats .
+ /// </summary>
+ /// <returns>A Task.</returns>
+ private async Task HeartbeatAsync()
{
- var timestamp = Stopwatch.GetTimestamp();
- var keepalive = Volatile.Read(ref this._lastKeepalive);
- Volatile.Write(ref this._lastKeepalive, keepalive + 1);
- this._keepaliveTimestamps.TryAdd(keepalive, timestamp);
+ await Task.Yield();
+
+ var token = this.TOKEN;
+ while (true)
+ {
+ try
+ {
+ token.ThrowIfCancellationRequested();
- var packet = new byte[8];
- BinaryPrimitives.WriteUInt64LittleEndian(packet, keepalive);
+ var dt = DateTime.Now;
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHeartbeat, "Sent heartbeat");
- await client.SendAsync(packet, packet.Length).ConfigureAwait(false);
+ var hbd = new VoiceDispatch
+ {
+ OpCode = 3,
+ Payload = UnixTimestamp(dt)
+ };
+ var hbj = JsonConvert.SerializeObject(hbd);
+ await this.WsSendAsync(hbj).ConfigureAwait(false);
- await Task.Delay(5000, token).ConfigureAwait(false);
+ this._lastHeartbeat = dt;
+ await Task.Delay(this._heartbeatInterval).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ return;
+ }
+ }
}
- }
- /// <summary>
- /// Stage1S .
- /// </summary>
- /// <param name="voiceReady">The voice ready.</param>
- /// <returns>A Task.</returns>
- private async Task Stage1(VoiceReadyPayload voiceReady)
- {
- // IP Discovery
- this._udpClient.Setup(this.UdpEndpoint);
+ /// <summary>
+ /// Keepalives .
+ /// </summary>
+ /// <returns>A Task.</returns>
+ private async Task KeepaliveAsync()
+ {
+ await Task.Yield();
- var pck = new byte[70];
- PreparePacket(pck);
- await this._udpClient.SendAsync(pck, pck.Length).ConfigureAwait(false);
+ var token = this.KEEPALIVE_TOKEN;
+ var client = this._udpClient;
- var ipd = await this._udpClient.ReceiveAsync().ConfigureAwait(false);
- ReadPacket(ipd, out var ip, out var port);
- this._discoveredEndpoint = new IpEndpoint
- {
- Address = ip,
- Port = port
- };
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Endpoint discovery finished - discovered endpoint is {0}:{1}", ip, port);
+ while (!token.IsCancellationRequested)
+ {
+ var timestamp = Stopwatch.GetTimestamp();
+ var keepalive = Volatile.Read(ref this._lastKeepalive);
+ Volatile.Write(ref this._lastKeepalive, keepalive + 1);
+ this._keepaliveTimestamps.TryAdd(keepalive, timestamp);
- void PreparePacket(byte[] packet)
- {
- var ssrc = this._ssrc;
- var packetSpan = packet.AsSpan();
- MemoryMarshal.Write(packetSpan, ref ssrc);
- Helpers.ZeroFill(packetSpan);
+ var packet = new byte[8];
+ BinaryPrimitives.WriteUInt64LittleEndian(packet, keepalive);
+
+ await client.SendAsync(packet, packet.Length).ConfigureAwait(false);
+
+ await Task.Delay(5000, token).ConfigureAwait(false);
+ }
}
- void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort)
+ /// <summary>
+ /// Stage1S .
+ /// </summary>
+ /// <param name="voiceReady">The voice ready.</param>
+ /// <returns>A Task.</returns>
+ private async Task Stage1(VoiceReadyPayload voiceReady)
{
- var packetSpan = packet.AsSpan();
+ // IP Discovery
+ this._udpClient.Setup(this.UdpEndpoint);
- var ipString = Utilities.UTF8.GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0');
- decodedIp = System.Net.IPAddress.Parse(ipString);
+ var pck = new byte[70];
+ PreparePacket(pck);
+ await this._udpClient.SendAsync(pck, pck.Length).ConfigureAwait(false);
- decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan[68 /* 70 - 2 */..]);
- }
+ var ipd = await this._udpClient.ReceiveAsync().ConfigureAwait(false);
+ ReadPacket(ipd, out var ip, out var port);
+ this._discoveredEndpoint = new IpEndpoint
+ {
+ Address = ip,
+ Port = port
+ };
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Endpoint discovery finished - discovered endpoint is {0}:{1}", ip, port);
- // Select voice encryption mode
- var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes);
- this._selectedEncryptionMode = selectedEncryptionMode.Value;
+ void PreparePacket(byte[] packet)
+ {
+ var ssrc = this._ssrc;
+ var packetSpan = packet.AsSpan();
+ MemoryMarshal.Write(packetSpan, ref ssrc);
+ Helpers.ZeroFill(packetSpan);
+ }
- // Ready
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Selected encryption mode is {0}", selectedEncryptionMode.Key);
- var vsp = new VoiceDispatch
- {
- OpCode = 1,
- Payload = new VoiceSelectProtocolPayload
+ void ReadPacket(byte[] packet, out System.Net.IPAddress decodedIp, out ushort decodedPort)
{
- Protocol = "udp",
- Data = new VoiceSelectProtocolPayloadData
- {
- Address = this._discoveredEndpoint.Address.ToString(),
- Port = (ushort)this._discoveredEndpoint.Port,
- Mode = selectedEncryptionMode.Key
- }
+ var packetSpan = packet.AsSpan();
+
+ var ipString = Utilities.UTF8.GetString(packet, 4, 64 /* 70 - 6 */).TrimEnd('\0');
+ decodedIp = System.Net.IPAddress.Parse(ipString);
+
+ decodedPort = BinaryPrimitives.ReadUInt16LittleEndian(packetSpan[68 /* 70 - 2 */..]);
}
- };
- var vsj = JsonConvert.SerializeObject(vsp, Formatting.None);
- await this.WsSendAsync(vsj).ConfigureAwait(false);
- this._senderTokenSource = new CancellationTokenSource();
- this._senderTask = Task.Run(this.VoiceSenderTask, this.SENDER_TOKEN);
+ // Select voice encryption mode
+ var selectedEncryptionMode = Sodium.SelectMode(voiceReady.Modes);
+ this._selectedEncryptionMode = selectedEncryptionMode.Value;
- this._receiverTokenSource = new CancellationTokenSource();
- this._receiverTask = Task.Run(this.UdpReceiverTask, this.RECEIVER_TOKEN);
- }
+ // Ready
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Selected encryption mode is {0}", selectedEncryptionMode.Key);
+ var vsp = new VoiceDispatch
+ {
+ OpCode = 1,
+ Payload = new VoiceSelectProtocolPayload
+ {
+ Protocol = "udp",
+ Data = new VoiceSelectProtocolPayloadData
+ {
+ Address = this._discoveredEndpoint.Address.ToString(),
+ Port = (ushort)this._discoveredEndpoint.Port,
+ Mode = selectedEncryptionMode.Key
+ }
+ }
+ };
+ var vsj = JsonConvert.SerializeObject(vsp, Formatting.None);
+ await this.WsSendAsync(vsj).ConfigureAwait(false);
- /// <summary>
- /// Stage2S .
- /// </summary>
- /// <param name="voiceSessionDescription">The voice session description.</param>
- /// <returns>A Task.</returns>
- private async Task Stage2(VoiceSessionDescriptionPayload voiceSessionDescription)
- {
- this._selectedEncryptionMode = Sodium.SupportedModes[voiceSessionDescription.Mode.ToLowerInvariant()];
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Discord updated encryption mode - new mode is {0}", this._selectedEncryptionMode);
+ this._senderTokenSource = new CancellationTokenSource();
+ this._senderTask = Task.Run(this.VoiceSenderTask, this.SENDER_TOKEN);
- // start keepalive
- this._keepaliveTokenSource = new CancellationTokenSource();
- this._keepaliveTask = this.KeepaliveAsync();
+ this._receiverTokenSource = new CancellationTokenSource();
+ this._receiverTask = Task.Run(this.UdpReceiverTask, this.RECEIVER_TOKEN);
+ }
- // send 3 packets of silence to get things going
- var nullpcm = new byte[this.AudioFormat.CalculateSampleSize(20)];
- for (var i = 0; i < 3; i++)
+ /// <summary>
+ /// Stage2S .
+ /// </summary>
+ /// <param name="voiceSessionDescription">The voice session description.</param>
+ /// <returns>A Task.</returns>
+ private async Task Stage2(VoiceSessionDescriptionPayload voiceSessionDescription)
{
- var nullPcm = new byte[nullpcm.Length];
- var nullpacketmem = nullPcm.AsMemory();
- await this.EnqueuePacketAsync(new RawVoicePacket(nullpacketmem, 20, true)).ConfigureAwait(false);
- }
+ this._selectedEncryptionMode = Sodium.SupportedModes[voiceSessionDescription.Mode.ToLowerInvariant()];
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceHandshake, "Discord updated encryption mode - new mode is {0}", this._selectedEncryptionMode);
- this._isInitialized = true;
- this._readyWait.SetResult(true);
- }
+ // start keepalive
+ this._keepaliveTokenSource = new CancellationTokenSource();
+ this._keepaliveTask = this.KeepaliveAsync();
- /// <summary>
- /// Handles the dispatch.
- /// </summary>
- /// <param name="jo">The jo.</param>
- /// <returns>A Task.</returns>
- private async Task HandleDispatch(JObject jo)
- {
- var opc = (int)jo["op"];
- var opp = jo["d"] as JObject;
+ // send 3 packets of silence to get things going
+ var nullpcm = new byte[this.AudioFormat.CalculateSampleSize(20)];
+ for (var i = 0; i < 3; i++)
+ {
+ var nullPcm = new byte[nullpcm.Length];
+ var nullpacketmem = nullPcm.AsMemory();
+ await this.EnqueuePacketAsync(new RawVoicePacket(nullpacketmem, 20, true)).ConfigureAwait(false);
+ }
+
+ this._isInitialized = true;
+ this._readyWait.SetResult(true);
+ }
- switch (opc)
+ /// <summary>
+ /// Handles the dispatch.
+ /// </summary>
+ /// <param name="jo">The jo.</param>
+ /// <returns>A Task.</returns>
+ private async Task HandleDispatch(JObject jo)
{
- case 2: // READY
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received READY (OP2)");
- var vrp = opp.ToObject<VoiceReadyPayload>();
- this._ssrc = vrp.Ssrc;
- this.UdpEndpoint = new ConnectionEndpoint(vrp.Address, vrp.Port);
- // this is not the valid interval
- // oh, discord
- //this.HeartbeatInterval = vrp.HeartbeatInterval;
- this._heartbeatTask = Task.Run(this.HeartbeatAsync);
- await this.Stage1(vrp).ConfigureAwait(false);
- break;
-
- case 4: // SESSION_DESCRIPTION
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received SESSION_DESCRIPTION (OP4)");
- var vsd = opp.ToObject<VoiceSessionDescriptionPayload>();
- this._key = vsd.SecretKey;
- this._sodium = new Sodium(this._key.AsMemory());
- await this.Stage2(vsd).ConfigureAwait(false);
- break;
-
- case 5: // SPEAKING
- // Don't spam OP5
- // No longer spam, Discord supposedly doesn't send many of these
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received SPEAKING (OP5)");
- var spd = opp.ToObject<VoiceSpeakingPayload>();
- var foundUserInCache = this._discord.TryGetCachedUserInternal(spd.UserId.Value, out var resolvedUser);
- var spk = new UserSpeakingEventArgs(this._discord.ServiceProvider)
- {
- Speaking = spd.Speaking,
- Ssrc = spd.Ssrc.Value,
- User = resolvedUser,
- };
+ var opc = (int)jo["op"];
+ var opp = jo["d"] as JObject;
- if (foundUserInCache && this._transmittingSsrCs.TryGetValue(spk.Ssrc, out var txssrc5) && txssrc5.Id == 0)
- {
- txssrc5.User = spk.User;
- }
- else
- {
- var opus = this._opus.CreateDecoder();
- var vtx = new AudioSender(spk.Ssrc, opus)
+ switch (opc)
+ {
+ case 2: // READY
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received READY (OP2)");
+ var vrp = opp.ToObject<VoiceReadyPayload>();
+ this._ssrc = vrp.Ssrc;
+ this.UdpEndpoint = new ConnectionEndpoint(vrp.Address, vrp.Port);
+ // this is not the valid interval
+ // oh, discord
+ //this.HeartbeatInterval = vrp.HeartbeatInterval;
+ this._heartbeatTask = Task.Run(this.HeartbeatAsync);
+ await this.Stage1(vrp).ConfigureAwait(false);
+ break;
+
+ case 4: // SESSION_DESCRIPTION
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received SESSION_DESCRIPTION (OP4)");
+ var vsd = opp.ToObject<VoiceSessionDescriptionPayload>();
+ this._key = vsd.SecretKey;
+ this._sodium = new Sodium(this._key.AsMemory());
+ await this.Stage2(vsd).ConfigureAwait(false);
+ break;
+
+ case 5: // SPEAKING
+ // Don't spam OP5
+ // No longer spam, Discord supposedly doesn't send many of these
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received SPEAKING (OP5)");
+ var spd = opp.ToObject<VoiceSpeakingPayload>();
+ var foundUserInCache = this._discord.TryGetCachedUserInternal(spd.UserId.Value, out var resolvedUser);
+ var spk = new UserSpeakingEventArgs(this._discord.ServiceProvider)
{
- User = await this._discord.GetUserAsync(spd.UserId.Value).ConfigureAwait(false)
+ Speaking = spd.Speaking,
+ Ssrc = spd.Ssrc.Value,
+ User = resolvedUser,
};
- if (!this._transmittingSsrCs.TryAdd(spk.Ssrc, vtx))
- this._opus.DestroyDecoder(opus);
- }
-
- await this._userSpeaking.InvokeAsync(this, spk).ConfigureAwait(false);
- break;
-
- case 6: // HEARTBEAT ACK
- var dt = DateTime.Now;
- var ping = (int)(dt - this._lastHeartbeat).TotalMilliseconds;
- Volatile.Write(ref this._wsPing, ping);
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received HEARTBEAT_ACK (OP6, {0}ms)", ping);
- this._lastHeartbeat = dt;
- break;
-
- case 8: // HELLO
- // this sends a heartbeat interval that we need to use for heartbeating
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received HELLO (OP8)");
- this._heartbeatInterval = opp["heartbeat_interval"].ToObject<int>();
- break;
-
- case 9: // RESUMED
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received RESUMED (OP9)");
- this._heartbeatTask = Task.Run(this.HeartbeatAsync);
- break;
-
- case 12: // CLIENT_CONNECTED
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received CLIENT_CONNECTED (OP12)");
- var ujpd = opp.ToObject<VoiceUserJoinPayload>();
- var usrj = await this._discord.GetUserAsync(ujpd.UserId).ConfigureAwait(false);
- {
- var opus = this._opus.CreateDecoder();
- var vtx = new AudioSender(ujpd.Ssrc, opus)
+ if (foundUserInCache && this._transmittingSsrCs.TryGetValue(spk.Ssrc, out var txssrc5) && txssrc5.Id == 0)
{
- User = usrj
- };
-
- if (!this._transmittingSsrCs.TryAdd(vtx.Ssrc, vtx))
- this._opus.DestroyDecoder(opus);
- }
+ txssrc5.User = spk.User;
+ }
+ else
+ {
+ var opus = this._opus.CreateDecoder();
+ var vtx = new AudioSender(spk.Ssrc, opus)
+ {
+ User = await this._discord.GetUserAsync(spd.UserId.Value).ConfigureAwait(false)
+ };
+
+ if (!this._transmittingSsrCs.TryAdd(spk.Ssrc, vtx))
+ this._opus.DestroyDecoder(opus);
+ }
- await this._userJoined.InvokeAsync(this, new VoiceUserJoinEventArgs(this._discord.ServiceProvider) { User = usrj, Ssrc = ujpd.Ssrc }).ConfigureAwait(false);
- break;
+ await this._userSpeaking.InvokeAsync(this, spk).ConfigureAwait(false);
+ break;
+
+ case 6: // HEARTBEAT ACK
+ var dt = DateTime.Now;
+ var ping = (int)(dt - this._lastHeartbeat).TotalMilliseconds;
+ Volatile.Write(ref this._wsPing, ping);
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received HEARTBEAT_ACK (OP6, {0}ms)", ping);
+ this._lastHeartbeat = dt;
+ break;
+
+ case 8: // HELLO
+ // this sends a heartbeat interval that we need to use for heartbeating
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received HELLO (OP8)");
+ this._heartbeatInterval = opp["heartbeat_interval"].ToObject<int>();
+ break;
+
+ case 9: // RESUMED
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received RESUMED (OP9)");
+ this._heartbeatTask = Task.Run(this.HeartbeatAsync);
+ break;
+
+ case 12: // CLIENT_CONNECTED
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received CLIENT_CONNECTED (OP12)");
+ var ujpd = opp.ToObject<VoiceUserJoinPayload>();
+ var usrj = await this._discord.GetUserAsync(ujpd.UserId).ConfigureAwait(false);
+ {
+ var opus = this._opus.CreateDecoder();
+ var vtx = new AudioSender(ujpd.Ssrc, opus)
+ {
+ User = usrj
+ };
+
+ if (!this._transmittingSsrCs.TryAdd(vtx.Ssrc, vtx))
+ this._opus.DestroyDecoder(opus);
+ }
- case 13: // CLIENT_DISCONNECTED
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received CLIENT_DISCONNECTED (OP13)");
- var ulpd = opp.ToObject<VoiceUserLeavePayload>();
- var txssrc = this._transmittingSsrCs.FirstOrDefault(x => x.Value.Id == ulpd.UserId);
- if (this._transmittingSsrCs.ContainsKey(txssrc.Key))
- {
- this._transmittingSsrCs.TryRemove(txssrc.Key, out var txssrc13);
- this._opus.DestroyDecoder(txssrc13.Decoder);
- }
+ await this._userJoined.InvokeAsync(this, new VoiceUserJoinEventArgs(this._discord.ServiceProvider) { User = usrj, Ssrc = ujpd.Ssrc }).ConfigureAwait(false);
+ break;
- var usrl = await this._discord.GetUserAsync(ulpd.UserId).ConfigureAwait(false);
- await this._userLeft.InvokeAsync(this, new VoiceUserLeaveEventArgs(this._discord.ServiceProvider)
- {
- User = usrl,
- Ssrc = txssrc.Key
- }).ConfigureAwait(false);
- break;
+ case 13: // CLIENT_DISCONNECTED
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received CLIENT_DISCONNECTED (OP13)");
+ var ulpd = opp.ToObject<VoiceUserLeavePayload>();
+ var txssrc = this._transmittingSsrCs.FirstOrDefault(x => x.Value.Id == ulpd.UserId);
+ if (this._transmittingSsrCs.ContainsKey(txssrc.Key))
+ {
+ this._transmittingSsrCs.TryRemove(txssrc.Key, out var txssrc13);
+ this._opus.DestroyDecoder(txssrc13.Decoder);
+ }
- default:
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received unknown voice opcode (OP{0})", opc);
- break;
+ var usrl = await this._discord.GetUserAsync(ulpd.UserId).ConfigureAwait(false);
+ await this._userLeft.InvokeAsync(this, new VoiceUserLeaveEventArgs(this._discord.ServiceProvider)
+ {
+ User = usrl,
+ Ssrc = txssrc.Key
+ }).ConfigureAwait(false);
+ break;
+
+ default:
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceDispatch, "Received unknown voice opcode (OP{0})", opc);
+ break;
+ }
}
- }
- /// <summary>
- /// Voices the w s_ socket closed.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The e.</param>
- /// <returns>A Task.</returns>
- private async Task VoiceWS_SocketClosed(IWebSocketClient client, SocketCloseEventArgs e)
- {
- this._discord.Logger.LogDebug(VoiceNextEvents.VoiceConnectionClose, "Voice WebSocket closed ({0}, '{1}')", e.CloseCode, e.CloseMessage);
+ /// <summary>
+ /// Voices the w s_ socket closed.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The e.</param>
+ /// <returns>A Task.</returns>
+ private async Task VoiceWS_SocketClosed(IWebSocketClient client, SocketCloseEventArgs e)
+ {
+ this._discord.Logger.LogDebug(VoiceNextEvents.VoiceConnectionClose, "Voice WebSocket closed ({0}, '{1}')", e.CloseCode, e.CloseMessage);
+
+ // generally this should not be disposed on all disconnects, only on requested ones
+ // or something
+ // otherwise problems happen
+ //this.Dispose();
- // generally this should not be disposed on all disconnects, only on requested ones
- // or something
- // otherwise problems happen
- //this.Dispose();
+ if (e.CloseCode == 4006 || e.CloseCode == 4009)
+ this.Resume = false;
- if (e.CloseCode == 4006 || e.CloseCode == 4009)
- this.Resume = false;
+ if (!this._isDisposed)
+ {
+ this._tokenSource.Cancel();
+ this._tokenSource = new CancellationTokenSource();
+ this._voiceWs = this._discord.Configuration.WebSocketClientFactory(this._discord.Configuration.Proxy, this._discord.ServiceProvider);
+ this._voiceWs.Disconnected += this.VoiceWS_SocketClosed;
+ this._voiceWs.MessageReceived += this.VoiceWS_SocketMessage;
+ this._voiceWs.Connected += this.VoiceWS_SocketOpened;
+
+ if (this.Resume) // emzi you dipshit
+ await this.ConnectAsync().ConfigureAwait(false);
+ }
+ }
- if (!this._isDisposed)
+ /// <summary>
+ /// Voices the w s_ socket message.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The e.</param>
+ /// <returns>A Task.</returns>
+ private Task VoiceWS_SocketMessage(IWebSocketClient client, SocketMessageEventArgs e)
{
- this._tokenSource.Cancel();
- this._tokenSource = new CancellationTokenSource();
- this._voiceWs = this._discord.Configuration.WebSocketClientFactory(this._discord.Configuration.Proxy, this._discord.ServiceProvider);
- this._voiceWs.Disconnected += this.VoiceWS_SocketClosed;
- this._voiceWs.MessageReceived += this.VoiceWS_SocketMessage;
- this._voiceWs.Connected += this.VoiceWS_SocketOpened;
+ if (e is not SocketTextMessageEventArgs et)
+ {
+ this._discord.Logger.LogCritical(VoiceNextEvents.VoiceGatewayError, "Discord Voice Gateway sent binary data - unable to process");
+ return Task.CompletedTask;
+ }
- if (this.Resume) // emzi you dipshit
- await this.ConnectAsync().ConfigureAwait(false);
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceWsRx, et.Message);
+ return this.HandleDispatch(JObject.Parse(et.Message));
}
- }
- /// <summary>
- /// Voices the w s_ socket message.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The e.</param>
- /// <returns>A Task.</returns>
- private Task VoiceWS_SocketMessage(IWebSocketClient client, SocketMessageEventArgs e)
- {
- if (e is not SocketTextMessageEventArgs et)
+ /// <summary>
+ /// Voices the w s_ socket opened.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The e.</param>
+ /// <returns>A Task.</returns>
+ private Task VoiceWS_SocketOpened(IWebSocketClient client, SocketEventArgs e)
+ => this.StartAsync();
+
+ /// <summary>
+ /// Voices the ws_ socket exception.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The e.</param>
+ /// <returns>A Task.</returns>
+ private Task VoiceWs_SocketException(IWebSocketClient client, SocketErrorEventArgs e)
+ => this._voiceSocketError.InvokeAsync(this, new SocketErrorEventArgs(this._discord.ServiceProvider) { Exception = e.Exception });
+
+ /// <summary>
+ /// Ws the send async.
+ /// </summary>
+ /// <param name="payload">The payload.</param>
+ /// <returns>A Task.</returns>
+ private async Task WsSendAsync(string payload)
{
- this._discord.Logger.LogCritical(VoiceNextEvents.VoiceGatewayError, "Discord Voice Gateway sent binary data - unable to process");
- return Task.CompletedTask;
+ this._discord.Logger.LogTrace(VoiceNextEvents.VoiceWsTx, payload);
+ await this._voiceWs.SendMessageAsync(payload).ConfigureAwait(false);
}
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceWsRx, et.Message);
- return this.HandleDispatch(JObject.Parse(et.Message));
- }
-
- /// <summary>
- /// Voices the w s_ socket opened.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The e.</param>
- /// <returns>A Task.</returns>
- private Task VoiceWS_SocketOpened(IWebSocketClient client, SocketEventArgs e)
- => this.StartAsync();
-
- /// <summary>
- /// Voices the ws_ socket exception.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The e.</param>
- /// <returns>A Task.</returns>
- private Task VoiceWs_SocketException(IWebSocketClient client, SocketErrorEventArgs e)
- => this._voiceSocketError.InvokeAsync(this, new SocketErrorEventArgs(this._discord.ServiceProvider) { Exception = e.Exception });
-
- /// <summary>
- /// Ws the send async.
- /// </summary>
- /// <param name="payload">The payload.</param>
- /// <returns>A Task.</returns>
- private async Task WsSendAsync(string payload)
- {
- this._discord.Logger.LogTrace(VoiceNextEvents.VoiceWsTx, payload);
- await this._voiceWs.SendMessageAsync(payload).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Gets the unix timestamp.
- /// </summary>
- /// <param name="dt">The datetime.</param>
- private static uint UnixTimestamp(DateTime dt)
- {
- var ts = dt - s_unixEpoch;
- var sd = ts.TotalSeconds;
- var si = (uint)sd;
- return si;
+ /// <summary>
+ /// Gets the unix timestamp.
+ /// </summary>
+ /// <param name="dt">The datetime.</param>
+ private static uint UnixTimestamp(DateTime dt)
+ {
+ var ts = dt - s_unixEpoch;
+ var sd = ts.TotalSeconds;
+ var si = (uint)sd;
+ return si;
+ }
}
}
diff --git a/DisCatSharp.VoiceNext/VoiceNextEvents.cs b/DisCatSharp.VoiceNext/VoiceNextEvents.cs
index 940859a53..928ea3211 100644
--- a/DisCatSharp.VoiceNext/VoiceNextEvents.cs
+++ b/DisCatSharp.VoiceNext/VoiceNextEvents.cs
@@ -1,81 +1,82 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Logging;
-namespace DisCatSharp.VoiceNext;
-
-/// <summary>
-/// Contains well-defined event IDs used by the VoiceNext extension.
-/// </summary>
-public static class VoiceNextEvents
+namespace DisCatSharp.VoiceNext
{
/// <summary>
- /// Miscellaneous events, that do not fit in any other category.
+ /// Contains well-defined event IDs used by the VoiceNext extension.
/// </summary>
- public static EventId Misc { get; } = new(300, "VoiceNext");
+ public static class VoiceNextEvents
+ {
+ /// <summary>
+ /// Miscellaneous events, that do not fit in any other category.
+ /// </summary>
+ public static EventId Misc { get; } = new(300, "VoiceNext");
- /// <summary>
- /// Events pertaining to Voice Gateway connection lifespan, specifically, heartbeats.
- /// </summary>
- public static EventId VoiceHeartbeat { get; } = new(301, nameof(VoiceHeartbeat));
+ /// <summary>
+ /// Events pertaining to Voice Gateway connection lifespan, specifically, heartbeats.
+ /// </summary>
+ public static EventId VoiceHeartbeat { get; } = new(301, nameof(VoiceHeartbeat));
- /// <summary>
- /// Events pertaining to Voice Gateway connection early lifespan, specifically, the establishing thereof as well as negotiating various modes.
- /// </summary>
- public static EventId VoiceHandshake { get; } = new(302, nameof(VoiceHandshake));
+ /// <summary>
+ /// Events pertaining to Voice Gateway connection early lifespan, specifically, the establishing thereof as well as negotiating various modes.
+ /// </summary>
+ public static EventId VoiceHandshake { get; } = new(302, nameof(VoiceHandshake));
- /// <summary>
- /// Events emitted when incoming voice data is corrupted, or packets are being dropped.
- /// </summary>
- public static EventId VoiceReceiveFailure { get; } = new(303, nameof(VoiceReceiveFailure));
+ /// <summary>
+ /// Events emitted when incoming voice data is corrupted, or packets are being dropped.
+ /// </summary>
+ public static EventId VoiceReceiveFailure { get; } = new(303, nameof(VoiceReceiveFailure));
- /// <summary>
- /// Events pertaining to UDP connection lifespan, specifically the keepalive (or heartbeats).
- /// </summary>
- public static EventId VoiceKeepalive { get; } = new(304, nameof(VoiceKeepalive));
+ /// <summary>
+ /// Events pertaining to UDP connection lifespan, specifically the keepalive (or heartbeats).
+ /// </summary>
+ public static EventId VoiceKeepalive { get; } = new(304, nameof(VoiceKeepalive));
- /// <summary>
- /// Events emitted for high-level dispatch receive events.
- /// </summary>
- public static EventId VoiceDispatch { get; } = new(305, nameof(VoiceDispatch));
+ /// <summary>
+ /// Events emitted for high-level dispatch receive events.
+ /// </summary>
+ public static EventId VoiceDispatch { get; } = new(305, nameof(VoiceDispatch));
- /// <summary>
- /// Events emitted for Voice Gateway connection closes, clean or otherwise.
- /// </summary>
- public static EventId VoiceConnectionClose { get; } = new(306, nameof(VoiceConnectionClose));
+ /// <summary>
+ /// Events emitted for Voice Gateway connection closes, clean or otherwise.
+ /// </summary>
+ public static EventId VoiceConnectionClose { get; } = new(306, nameof(VoiceConnectionClose));
- /// <summary>
- /// Events emitted when decoding data received via Voice Gateway fails for any reason.
- /// </summary>
- public static EventId VoiceGatewayError { get; } = new(307, nameof(VoiceGatewayError));
+ /// <summary>
+ /// Events emitted when decoding data received via Voice Gateway fails for any reason.
+ /// </summary>
+ public static EventId VoiceGatewayError { get; } = new(307, nameof(VoiceGatewayError));
- /// <summary>
- /// Events containing raw (but decompressed) payloads, received from Discord Voice Gateway.
- /// </summary>
- public static EventId VoiceWsRx { get; } = new(308, "Voice ↓");
+ /// <summary>
+ /// Events containing raw (but decompressed) payloads, received from Discord Voice Gateway.
+ /// </summary>
+ public static EventId VoiceWsRx { get; } = new(308, "Voice ↓");
- /// <summary>
- /// Events containing raw payloads, as they're being sent to Discord Voice Gateway.
- /// </summary>
- public static EventId VoiceWsTx { get; } = new(309, "Voice ↑");
+ /// <summary>
+ /// Events containing raw payloads, as they're being sent to Discord Voice Gateway.
+ /// </summary>
+ public static EventId VoiceWsTx { get; } = new(309, "Voice ↑");
+ }
}
diff --git a/DisCatSharp.VoiceNext/VoiceNextExtension.cs b/DisCatSharp.VoiceNext/VoiceNextExtension.cs
index 98a0524df..44e92f8fd 100644
--- a/DisCatSharp.VoiceNext/VoiceNextExtension.cs
+++ b/DisCatSharp.VoiceNext/VoiceNextExtension.cs
@@ -1,264 +1,265 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Net;
using DisCatSharp.VoiceNext.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.VoiceNext;
-
-/// <summary>
-/// Represents VoiceNext extension, which acts as Discord voice client.
-/// </summary>
-public sealed class VoiceNextExtension : BaseExtension
+namespace DisCatSharp.VoiceNext
{
/// <summary>
- /// Gets or sets the configuration.
- /// </summary>
- private readonly VoiceNextConfiguration _configuration;
-
- /// <summary>
- /// Gets or sets the active connections.
- /// </summary>
- private readonly ConcurrentDictionary<ulong, VoiceNextConnection> _activeConnections;
-
- /// <summary>
- /// Gets or sets the voice state updates.
+ /// Represents VoiceNext extension, which acts as Discord voice client.
/// </summary>
- private readonly ConcurrentDictionary<ulong, TaskCompletionSource<VoiceStateUpdateEventArgs>> _voiceStateUpdates;
+ public sealed class VoiceNextExtension : BaseExtension
+ {
+ /// <summary>
+ /// Gets or sets the configuration.
+ /// </summary>
+ private readonly VoiceNextConfiguration _configuration;
+
+ /// <summary>
+ /// Gets or sets the active connections.
+ /// </summary>
+ private readonly ConcurrentDictionary<ulong, VoiceNextConnection> _activeConnections;
+
+ /// <summary>
+ /// Gets or sets the voice state updates.
+ /// </summary>
+ private readonly ConcurrentDictionary<ulong, TaskCompletionSource<VoiceStateUpdateEventArgs>> _voiceStateUpdates;
+
+ /// <summary>
+ /// Gets or sets the voice server updates.
+ /// </summary>
+ private readonly ConcurrentDictionary<ulong, TaskCompletionSource<VoiceServerUpdateEventArgs>> _voiceServerUpdates;
+
+ /// <summary>
+ /// Gets whether this connection has incoming voice enabled.
+ /// </summary>
+ public bool IsIncomingEnabled { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VoiceNextExtension"/> class.
+ /// </summary>
+ /// <param name="config">The config.</param>
+ internal VoiceNextExtension(VoiceNextConfiguration config)
+ {
+ this._configuration = new VoiceNextConfiguration(config);
+ this.IsIncomingEnabled = config.EnableIncoming;
- /// <summary>
- /// Gets or sets the voice server updates.
- /// </summary>
- private readonly ConcurrentDictionary<ulong, TaskCompletionSource<VoiceServerUpdateEventArgs>> _voiceServerUpdates;
+ this._activeConnections = new ConcurrentDictionary<ulong, VoiceNextConnection>();
+ this._voiceStateUpdates = new ConcurrentDictionary<ulong, TaskCompletionSource<VoiceStateUpdateEventArgs>>();
+ this._voiceServerUpdates = new ConcurrentDictionary<ulong, TaskCompletionSource<VoiceServerUpdateEventArgs>>();
+ }
- /// <summary>
- /// Gets whether this connection has incoming voice enabled.
- /// </summary>
- public bool IsIncomingEnabled { get; }
+ /// <summary>
+ /// DO NOT USE THIS MANUALLY.
+ /// </summary>
+ /// <param name="client">DO NOT USE THIS MANUALLY.</param>
+ /// <exception cref="System.InvalidOperationException"/>
+ protected internal override void Setup(DiscordClient client)
+ {
+ if (this.Client != null)
+ throw new InvalidOperationException("What did I tell you?");
- /// <summary>
- /// Initializes a new instance of the <see cref="VoiceNextExtension"/> class.
- /// </summary>
- /// <param name="config">The config.</param>
- internal VoiceNextExtension(VoiceNextConfiguration config)
- {
- this._configuration = new VoiceNextConfiguration(config);
- this.IsIncomingEnabled = config.EnableIncoming;
+ this.Client = client;
- this._activeConnections = new ConcurrentDictionary<ulong, VoiceNextConnection>();
- this._voiceStateUpdates = new ConcurrentDictionary<ulong, TaskCompletionSource<VoiceStateUpdateEventArgs>>();
- this._voiceServerUpdates = new ConcurrentDictionary<ulong, TaskCompletionSource<VoiceServerUpdateEventArgs>>();
- }
+ this.Client.VoiceStateUpdated += this.Client_VoiceStateUpdate;
+ this.Client.VoiceServerUpdated += this.Client_VoiceServerUpdate;
+ }
- /// <summary>
- /// DO NOT USE THIS MANUALLY.
- /// </summary>
- /// <param name="client">DO NOT USE THIS MANUALLY.</param>
- /// <exception cref="System.InvalidOperationException"/>
- protected internal override void Setup(DiscordClient client)
- {
- if (this.Client != null)
- throw new InvalidOperationException("What did I tell you?");
+ /// <summary>
+ /// Create a VoiceNext connection for the specified channel.
+ /// </summary>
+ /// <param name="channel">Channel to connect to.</param>
+ /// <returns>VoiceNext connection for this channel.</returns>
+ public async Task<VoiceNextConnection> ConnectAsync(DiscordChannel channel)
+ {
+ if (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage)
+ throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be voice or stage channel");
- this.Client = client;
+ if (channel.Guild == null)
+ throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be guild channel");
- this.Client.VoiceStateUpdated += this.Client_VoiceStateUpdate;
- this.Client.VoiceServerUpdated += this.Client_VoiceServerUpdate;
- }
+ if (!channel.PermissionsFor(channel.Guild.CurrentMember).HasPermission(Permissions.AccessChannels | Permissions.UseVoice))
+ throw new InvalidOperationException("You need AccessChannels and UseVoice permission to connect to this voice channel");
- /// <summary>
- /// Create a VoiceNext connection for the specified channel.
- /// </summary>
- /// <param name="channel">Channel to connect to.</param>
- /// <returns>VoiceNext connection for this channel.</returns>
- public async Task<VoiceNextConnection> ConnectAsync(DiscordChannel channel)
- {
- if (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage)
- throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be voice or stage channel");
+ var gld = channel.Guild;
+ if (this._activeConnections.ContainsKey(gld.Id))
+ throw new InvalidOperationException("This guild already has a voice connection");
- if (channel.Guild == null)
- throw new ArgumentException(nameof(channel), "Invalid channel specified; needs to be guild channel");
+ var vstut = new TaskCompletionSource<VoiceStateUpdateEventArgs>();
+ var vsrut = new TaskCompletionSource<VoiceServerUpdateEventArgs>();
+ this._voiceStateUpdates[gld.Id] = vstut;
+ this._voiceServerUpdates[gld.Id] = vsrut;
- if (!channel.PermissionsFor(channel.Guild.CurrentMember).HasPermission(Permissions.AccessChannels | Permissions.UseVoice))
- throw new InvalidOperationException("You need AccessChannels and UseVoice permission to connect to this voice channel");
+ var vsd = new VoiceDispatch
+ {
+ OpCode = 4,
+ Payload = new VoiceStateUpdatePayload
+ {
+ GuildId = gld.Id,
+ ChannelId = channel.Id,
+ Deafened = false,
+ Muted = false
+ }
+ };
+ var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);
+ await (channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
- var gld = channel.Guild;
- if (this._activeConnections.ContainsKey(gld.Id))
- throw new InvalidOperationException("This guild already has a voice connection");
+ var vstu = await vstut.Task.ConfigureAwait(false);
+ var vstup = new VoiceStateUpdatePayload
+ {
+ SessionId = vstu.SessionId,
+ UserId = vstu.User.Id
+ };
+ var vsru = await vsrut.Task.ConfigureAwait(false);
+ var vsrup = new VoiceServerUpdatePayload
+ {
+ Endpoint = vsru.Endpoint,
+ GuildId = vsru.Guild.Id,
+ Token = vsru.VoiceToken
+ };
- var vstut = new TaskCompletionSource<VoiceStateUpdateEventArgs>();
- var vsrut = new TaskCompletionSource<VoiceServerUpdateEventArgs>();
- this._voiceStateUpdates[gld.Id] = vstut;
- this._voiceServerUpdates[gld.Id] = vsrut;
+ var vnc = new VoiceNextConnection(this.Client, gld, channel, this._configuration, vsrup, vstup);
+ vnc.VoiceDisconnected += this.Vnc_VoiceDisconnected;
+ await vnc.ConnectAsync().ConfigureAwait(false);
+ await vnc.WaitForReadyAsync().ConfigureAwait(false);
+ this._activeConnections[gld.Id] = vnc;
+ return vnc;
+ }
- var vsd = new VoiceDispatch
+ /// <summary>
+ /// Gets a VoiceNext connection for specified guild.
+ /// </summary>
+ /// <param name="guild">Guild to get VoiceNext connection for.</param>
+ /// <returns>VoiceNext connection for the specified guild.</returns>
+ public VoiceNextConnection GetConnection(DiscordGuild guild) => this._activeConnections.ContainsKey(guild.Id) ? this._activeConnections[guild.Id] : null;
+
+ /// <summary>
+ /// Vnc_S the voice disconnected.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <returns>A Task.</returns>
+ private async Task Vnc_VoiceDisconnected(DiscordGuild guild)
{
- OpCode = 4,
- Payload = new VoiceStateUpdatePayload
+ VoiceNextConnection vnc = null;
+ if (this._activeConnections.ContainsKey(guild.Id))
+ this._activeConnections.TryRemove(guild.Id, out vnc);
+
+ var vsd = new VoiceDispatch
{
- GuildId = gld.Id,
- ChannelId = channel.Id,
- Deafened = false,
- Muted = false
- }
- };
- var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);
- await (channel.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
+ OpCode = 4,
+ Payload = new VoiceStateUpdatePayload
+ {
+ GuildId = guild.Id,
+ ChannelId = null
+ }
+ };
+ var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);
+ await (guild.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
+ }
- var vstu = await vstut.Task.ConfigureAwait(false);
- var vstup = new VoiceStateUpdatePayload
+ /// <summary>
+ /// Client_S the voice state update.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The e.</param>
+ /// <returns>A Task.</returns>
+ private Task Client_VoiceStateUpdate(DiscordClient client, VoiceStateUpdateEventArgs e)
{
- SessionId = vstu.SessionId,
- UserId = vstu.User.Id
- };
- var vsru = await vsrut.Task.ConfigureAwait(false);
- var vsrup = new VoiceServerUpdatePayload
- {
- Endpoint = vsru.Endpoint,
- GuildId = vsru.Guild.Id,
- Token = vsru.VoiceToken
- };
-
- var vnc = new VoiceNextConnection(this.Client, gld, channel, this._configuration, vsrup, vstup);
- vnc.VoiceDisconnected += this.Vnc_VoiceDisconnected;
- await vnc.ConnectAsync().ConfigureAwait(false);
- await vnc.WaitForReadyAsync().ConfigureAwait(false);
- this._activeConnections[gld.Id] = vnc;
- return vnc;
- }
-
- /// <summary>
- /// Gets a VoiceNext connection for specified guild.
- /// </summary>
- /// <param name="guild">Guild to get VoiceNext connection for.</param>
- /// <returns>VoiceNext connection for the specified guild.</returns>
- public VoiceNextConnection GetConnection(DiscordGuild guild) => this._activeConnections.ContainsKey(guild.Id) ? this._activeConnections[guild.Id] : null;
+ var gld = e.Guild;
+ if (gld == null)
+ return Task.CompletedTask;
- /// <summary>
- /// Vnc_S the voice disconnected.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <returns>A Task.</returns>
- private async Task Vnc_VoiceDisconnected(DiscordGuild guild)
- {
- VoiceNextConnection vnc = null;
- if (this._activeConnections.ContainsKey(guild.Id))
- this._activeConnections.TryRemove(guild.Id, out vnc);
+ if (e.User == null)
+ return Task.CompletedTask;
- var vsd = new VoiceDispatch
- {
- OpCode = 4,
- Payload = new VoiceStateUpdatePayload
+ if (e.User.Id == this.Client.CurrentUser.Id)
{
- GuildId = guild.Id,
- ChannelId = null
- }
- };
- var vsj = JsonConvert.SerializeObject(vsd, Formatting.None);
- await (guild.Discord as DiscordClient).WsSendAsync(vsj).ConfigureAwait(false);
- }
+ if (e.After.Channel == null && this._activeConnections.TryRemove(gld.Id, out var ac))
+ ac.Disconnect();
- /// <summary>
- /// Client_S the voice state update.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The e.</param>
- /// <returns>A Task.</returns>
- private Task Client_VoiceStateUpdate(DiscordClient client, VoiceStateUpdateEventArgs e)
- {
- var gld = e.Guild;
- if (gld == null)
- return Task.CompletedTask;
-
- if (e.User == null)
- return Task.CompletedTask;
-
- if (e.User.Id == this.Client.CurrentUser.Id)
- {
- if (e.After.Channel == null && this._activeConnections.TryRemove(gld.Id, out var ac))
- ac.Disconnect();
+ if (this._activeConnections.TryGetValue(e.Guild.Id, out var vnc))
+ vnc.TargetChannel = e.Channel;
- if (this._activeConnections.TryGetValue(e.Guild.Id, out var vnc))
- vnc.TargetChannel = e.Channel;
+ if (!string.IsNullOrWhiteSpace(e.SessionId) && e.Channel != null && this._voiceStateUpdates.TryRemove(gld.Id, out var xe))
+ xe.SetResult(e);
+ }
- if (!string.IsNullOrWhiteSpace(e.SessionId) && e.Channel != null && this._voiceStateUpdates.TryRemove(gld.Id, out var xe))
- xe.SetResult(e);
+ return Task.CompletedTask;
}
- return Task.CompletedTask;
- }
-
- /// <summary>
- /// Client_S the voice server update.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The e.</param>
- /// <returns>A Task.</returns>
- private async Task Client_VoiceServerUpdate(DiscordClient client, VoiceServerUpdateEventArgs e)
- {
- var gld = e.Guild;
- if (gld == null)
- return;
-
- if (this._activeConnections.TryGetValue(e.Guild.Id, out var vnc))
+ /// <summary>
+ /// Client_S the voice server update.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The e.</param>
+ /// <returns>A Task.</returns>
+ private async Task Client_VoiceServerUpdate(DiscordClient client, VoiceServerUpdateEventArgs e)
{
- vnc.ServerData = new VoiceServerUpdatePayload
- {
- Endpoint = e.Endpoint,
- GuildId = e.Guild.Id,
- Token = e.VoiceToken
- };
+ var gld = e.Guild;
+ if (gld == null)
+ return;
- var eps = e.Endpoint;
- var epi = eps.LastIndexOf(':');
- var eph = string.Empty;
- var epp = 443;
- if (epi != -1)
+ if (this._activeConnections.TryGetValue(e.Guild.Id, out var vnc))
{
- eph = eps[..epi];
- epp = int.Parse(eps[(epi + 1)..]);
+ vnc.ServerData = new VoiceServerUpdatePayload
+ {
+ Endpoint = e.Endpoint,
+ GuildId = e.Guild.Id,
+ Token = e.VoiceToken
+ };
+
+ var eps = e.Endpoint;
+ var epi = eps.LastIndexOf(':');
+ var eph = string.Empty;
+ var epp = 443;
+ if (epi != -1)
+ {
+ eph = eps[..epi];
+ epp = int.Parse(eps[(epi + 1)..]);
+ }
+ else
+ {
+ eph = eps;
+ }
+ vnc.WebSocketEndpoint = new ConnectionEndpoint { Hostname = eph, Port = epp };
+
+ vnc.Resume = false;
+ await vnc.ReconnectAsync().ConfigureAwait(false);
}
- else
+
+ if (this._voiceServerUpdates.ContainsKey(gld.Id))
{
- eph = eps;
+ this._voiceServerUpdates.TryRemove(gld.Id, out var xe);
+ xe.SetResult(e);
}
- vnc.WebSocketEndpoint = new ConnectionEndpoint { Hostname = eph, Port = epp };
-
- vnc.Resume = false;
- await vnc.ReconnectAsync().ConfigureAwait(false);
- }
-
- if (this._voiceServerUpdates.ContainsKey(gld.Id))
- {
- this._voiceServerUpdates.TryRemove(gld.Id, out var xe);
- xe.SetResult(e);
}
}
}
diff --git a/DisCatSharp.VoiceNext/VoiceTransmitSink.cs b/DisCatSharp.VoiceNext/VoiceTransmitSink.cs
index 867a29f5c..5a82fb756 100644
--- a/DisCatSharp.VoiceNext/VoiceTransmitSink.cs
+++ b/DisCatSharp.VoiceNext/VoiceTransmitSink.cs
@@ -1,282 +1,283 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.VoiceNext.Codec;
-namespace DisCatSharp.VoiceNext;
-
-/// <summary>
-/// Sink used to transmit audio data via <see cref="VoiceNextConnection"/>.
-/// </summary>
-public sealed class VoiceTransmitSink : IDisposable
+namespace DisCatSharp.VoiceNext
{
/// <summary>
- /// Gets the PCM sample duration for this sink.
- /// </summary>
- public int SampleDuration { get; }
-
- /// <summary>
- /// Gets the length of the PCM buffer for this sink.
- /// Written packets should adhere to this size, but the sink will adapt to fit.
- /// </summary>
- public int SampleLength
- => this._pcmBuffer.Length;
-
- /// <summary>
- /// Gets or sets the volume modifier for this sink. Changing this will alter the volume of the output. 1.0 is 100%.
+ /// Sink used to transmit audio data via <see cref="VoiceNextConnection"/>.
/// </summary>
- public double VolumeModifier
+ public sealed class VoiceTransmitSink : IDisposable
{
- get => this._volume;
- set
+ /// <summary>
+ /// Gets the PCM sample duration for this sink.
+ /// </summary>
+ public int SampleDuration { get; }
+
+ /// <summary>
+ /// Gets the length of the PCM buffer for this sink.
+ /// Written packets should adhere to this size, but the sink will adapt to fit.
+ /// </summary>
+ public int SampleLength
+ => this._pcmBuffer.Length;
+
+ /// <summary>
+ /// Gets or sets the volume modifier for this sink. Changing this will alter the volume of the output. 1.0 is 100%.
+ /// </summary>
+ public double VolumeModifier
{
- if (value < 0 || value > 2.5)
- throw new ArgumentOutOfRangeException(nameof(value), "Volume needs to be between 0% and 250%.");
+ get => this._volume;
+ set
+ {
+ if (value < 0 || value > 2.5)
+ throw new ArgumentOutOfRangeException(nameof(value), "Volume needs to be between 0% and 250%.");
- this._volume = value;
+ this._volume = value;
+ }
+ }
+ private double _volume = 1.0;
+
+ /// <summary>
+ /// Gets the connection.
+ /// </summary>
+ private readonly VoiceNextConnection _connection;
+
+ /// <summary>
+ /// Gets the pcm buffer.
+ /// </summary>
+ private readonly byte[] _pcmBuffer;
+
+ /// <summary>
+ /// Gets the pcm memory.
+ /// </summary>
+ private readonly Memory<byte> _pcmMemory;
+
+ /// <summary>
+ /// Gets or sets the pcm buffer length.
+ /// </summary>
+ private int _pcmBufferLength;
+
+ /// <summary>
+ /// Gets the write semaphore.
+ /// </summary>
+ private readonly SemaphoreSlim _writeSemaphore;
+
+ /// <summary>
+ /// Gets the filters.
+ /// </summary>
+ private readonly List<IVoiceFilter> _filters;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VoiceTransmitSink"/> class.
+ /// </summary>
+ /// <param name="vnc">The vnc.</param>
+ /// <param name="pcmBufferDuration">The pcm buffer duration.</param>
+ internal VoiceTransmitSink(VoiceNextConnection vnc, int pcmBufferDuration)
+ {
+ this._connection = vnc;
+ this.SampleDuration = pcmBufferDuration;
+ this._pcmBuffer = new byte[vnc.AudioFormat.CalculateSampleSize(pcmBufferDuration)];
+ this._pcmMemory = this._pcmBuffer.AsMemory();
+ this._pcmBufferLength = 0;
+ this._writeSemaphore = new SemaphoreSlim(1, 1);
+ this._filters = new List<IVoiceFilter>();
}
- }
- private double _volume = 1.0;
-
- /// <summary>
- /// Gets the connection.
- /// </summary>
- private readonly VoiceNextConnection _connection;
-
- /// <summary>
- /// Gets the pcm buffer.
- /// </summary>
- private readonly byte[] _pcmBuffer;
-
- /// <summary>
- /// Gets the pcm memory.
- /// </summary>
- private readonly Memory<byte> _pcmMemory;
-
- /// <summary>
- /// Gets or sets the pcm buffer length.
- /// </summary>
- private int _pcmBufferLength;
-
- /// <summary>
- /// Gets the write semaphore.
- /// </summary>
- private readonly SemaphoreSlim _writeSemaphore;
-
- /// <summary>
- /// Gets the filters.
- /// </summary>
- private readonly List<IVoiceFilter> _filters;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="VoiceTransmitSink"/> class.
- /// </summary>
- /// <param name="vnc">The vnc.</param>
- /// <param name="pcmBufferDuration">The pcm buffer duration.</param>
- internal VoiceTransmitSink(VoiceNextConnection vnc, int pcmBufferDuration)
- {
- this._connection = vnc;
- this.SampleDuration = pcmBufferDuration;
- this._pcmBuffer = new byte[vnc.AudioFormat.CalculateSampleSize(pcmBufferDuration)];
- this._pcmMemory = this._pcmBuffer.AsMemory();
- this._pcmBufferLength = 0;
- this._writeSemaphore = new SemaphoreSlim(1, 1);
- this._filters = new List<IVoiceFilter>();
- }
-
- /// <summary>
- /// Writes PCM data to the sink. The data is prepared for transmission, and enqueued.
- /// </summary>
- /// <param name="buffer">PCM data buffer to send.</param>
- /// <param name="offset">Start of the data in the buffer.</param>
- /// <param name="count">Number of bytes from the buffer.</param>
- /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
- public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) => await this.WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).ConfigureAwait(false);
-
- /// <summary>
- /// Writes PCM data to the sink. The data is prepared for transmission, and enqueued.
- /// </summary>
- /// <param name="buffer">PCM data buffer to send.</param>
- /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
- public async Task WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
- {
- await this._writeSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
- try
+ /// <summary>
+ /// Writes PCM data to the sink. The data is prepared for transmission, and enqueued.
+ /// </summary>
+ /// <param name="buffer">PCM data buffer to send.</param>
+ /// <param name="offset">Start of the data in the buffer.</param>
+ /// <param name="count">Number of bytes from the buffer.</param>
+ /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+ public async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default) => await this.WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).ConfigureAwait(false);
+
+ /// <summary>
+ /// Writes PCM data to the sink. The data is prepared for transmission, and enqueued.
+ /// </summary>
+ /// <param name="buffer">PCM data buffer to send.</param>
+ /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+ public async Task WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
- var remaining = buffer.Length;
- var buffSpan = buffer;
- var pcmSpan = this._pcmMemory;
+ await this._writeSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
- while (remaining > 0)
+ try
{
- var len = Math.Min(pcmSpan.Length - this._pcmBufferLength, remaining);
+ var remaining = buffer.Length;
+ var buffSpan = buffer;
+ var pcmSpan = this._pcmMemory;
+
+ while (remaining > 0)
+ {
+ var len = Math.Min(pcmSpan.Length - this._pcmBufferLength, remaining);
- var tgt = pcmSpan[this._pcmBufferLength..];
- var src = buffSpan[..len];
+ var tgt = pcmSpan[this._pcmBufferLength..];
+ var src = buffSpan[..len];
- src.CopyTo(tgt);
- this._pcmBufferLength += len;
- remaining -= len;
- buffSpan = buffSpan[len..];
+ src.CopyTo(tgt);
+ this._pcmBufferLength += len;
+ remaining -= len;
+ buffSpan = buffSpan[len..];
- if (this._pcmBufferLength == this._pcmBuffer.Length)
- {
- this.ApplyFiltersSync(pcmSpan);
+ if (this._pcmBufferLength == this._pcmBuffer.Length)
+ {
+ this.ApplyFiltersSync(pcmSpan);
- this._pcmBufferLength = 0;
+ this._pcmBufferLength = 0;
- var packet = ArrayPool<byte>.Shared.Rent(this._pcmMemory.Length);
- var packetMemory = packet.AsMemory()[..this._pcmMemory.Length];
- this._pcmMemory.CopyTo(packetMemory);
+ var packet = ArrayPool<byte>.Shared.Rent(this._pcmMemory.Length);
+ var packetMemory = packet.AsMemory()[..this._pcmMemory.Length];
+ this._pcmMemory.CopyTo(packetMemory);
- await this._connection.EnqueuePacketAsync(new RawVoicePacket(packetMemory, this.SampleDuration, false, packet), cancellationToken).ConfigureAwait(false);
+ await this._connection.EnqueuePacketAsync(new RawVoicePacket(packetMemory, this.SampleDuration, false, packet), cancellationToken).ConfigureAwait(false);
+ }
}
}
+ finally
+ {
+ this._writeSemaphore.Release();
+ }
}
- finally
- {
- this._writeSemaphore.Release();
- }
- }
-
- /// <summary>
- /// Flushes the rest of the PCM data in this buffer to VoiceNext packet queue.
- /// </summary>
- /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
- public async Task FlushAsync(CancellationToken cancellationToken = default)
- {
- var pcm = this._pcmMemory;
- Helpers.ZeroFill(pcm[this._pcmBufferLength..].Span);
- this.ApplyFiltersSync(pcm);
+ /// <summary>
+ /// Flushes the rest of the PCM data in this buffer to VoiceNext packet queue.
+ /// </summary>
+ /// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
+ public async Task FlushAsync(CancellationToken cancellationToken = default)
+ {
+ var pcm = this._pcmMemory;
+ Helpers.ZeroFill(pcm[this._pcmBufferLength..].Span);
- var packet = ArrayPool<byte>.Shared.Rent(pcm.Length);
- var packetMemory = packet.AsMemory()[..pcm.Length];
- pcm.CopyTo(packetMemory);
+ this.ApplyFiltersSync(pcm);
- await this._connection.EnqueuePacketAsync(new RawVoicePacket(packetMemory, this.SampleDuration, false, packet), cancellationToken).ConfigureAwait(false);
- }
+ var packet = ArrayPool<byte>.Shared.Rent(pcm.Length);
+ var packetMemory = packet.AsMemory()[..pcm.Length];
+ pcm.CopyTo(packetMemory);
- /// <summary>
- /// Pauses playback.
- /// </summary>
- public void Pause()
- => this._connection.Pause();
+ await this._connection.EnqueuePacketAsync(new RawVoicePacket(packetMemory, this.SampleDuration, false, packet), cancellationToken).ConfigureAwait(false);
+ }
- /// <summary>
- /// Resumes playback.
- /// </summary>
- /// <returns></returns>
- public async Task ResumeAsync()
- => await this._connection.ResumeAsync().ConfigureAwait(false);
+ /// <summary>
+ /// Pauses playback.
+ /// </summary>
+ public void Pause()
+ => this._connection.Pause();
+
+ /// <summary>
+ /// Resumes playback.
+ /// </summary>
+ /// <returns></returns>
+ public async Task ResumeAsync()
+ => await this._connection.ResumeAsync().ConfigureAwait(false);
+
+ /// <summary>
+ /// Gets the collection of installed PCM filters, in order of their execution.
+ /// </summary>
+ /// <returns>Installed PCM filters, in order of execution.</returns>
+ public IEnumerable<IVoiceFilter> GetInstalledFilters()
+ {
+ foreach (var filter in this._filters)
+ yield return filter;
+ }
- /// <summary>
- /// Gets the collection of installed PCM filters, in order of their execution.
- /// </summary>
- /// <returns>Installed PCM filters, in order of execution.</returns>
- public IEnumerable<IVoiceFilter> GetInstalledFilters()
- {
- foreach (var filter in this._filters)
- yield return filter;
- }
+ /// <summary>
+ /// Installs a new PCM filter, with specified execution order.
+ /// </summary>
+ /// <param name="filter">Filter to install.</param>
+ /// <param name="order">Order of the new filter. This determines where the filter will be inserted in the filter pipeline.</param>
+ public void InstallFilter(IVoiceFilter filter, int order = int.MaxValue)
+ {
+ if (filter == null)
+ throw new ArgumentNullException(nameof(filter));
- /// <summary>
- /// Installs a new PCM filter, with specified execution order.
- /// </summary>
- /// <param name="filter">Filter to install.</param>
- /// <param name="order">Order of the new filter. This determines where the filter will be inserted in the filter pipeline.</param>
- public void InstallFilter(IVoiceFilter filter, int order = int.MaxValue)
- {
- if (filter == null)
- throw new ArgumentNullException(nameof(filter));
+ if (order < 0)
+ throw new ArgumentOutOfRangeException(nameof(order), "Filter order must be greater than or equal to 0.");
- if (order < 0)
- throw new ArgumentOutOfRangeException(nameof(order), "Filter order must be greater than or equal to 0.");
+ lock (this._filters)
+ {
+ var filters = this._filters;
+ if (order >= filters.Count)
+ filters.Add(filter);
+ else
+ filters.Insert(order, filter);
+ }
+ }
- lock (this._filters)
+ /// <summary>
+ /// Uninstalls an installed PCM filter.
+ /// </summary>
+ /// <param name="filter">Filter to uninstall.</param>
+ /// <returns>Whether the filter was uninstalled.</returns>
+ public bool UninstallFilter(IVoiceFilter filter)
{
- var filters = this._filters;
- if (order >= filters.Count)
- filters.Add(filter);
- else
- filters.Insert(order, filter);
- }
- }
+ if (filter == null)
+ throw new ArgumentNullException(nameof(filter));
- /// <summary>
- /// Uninstalls an installed PCM filter.
- /// </summary>
- /// <param name="filter">Filter to uninstall.</param>
- /// <returns>Whether the filter was uninstalled.</returns>
- public bool UninstallFilter(IVoiceFilter filter)
- {
- if (filter == null)
- throw new ArgumentNullException(nameof(filter));
+ lock (this._filters)
+ {
+ var filters = this._filters;
+ return filters.Contains(filter) && filters.Remove(filter);
+ }
+ }
- lock (this._filters)
+ /// <summary>
+ /// Applies the filters sync.
+ /// </summary>
+ /// <param name="pcmSpan">The pcm span.</param>
+ private void ApplyFiltersSync(Memory<byte> pcmSpan)
{
- var filters = this._filters;
- return filters.Contains(filter) && filters.Remove(filter);
- }
- }
+ var pcm16 = MemoryMarshal.Cast<byte, short>(pcmSpan.Span);
- /// <summary>
- /// Applies the filters sync.
- /// </summary>
- /// <param name="pcmSpan">The pcm span.</param>
- private void ApplyFiltersSync(Memory<byte> pcmSpan)
- {
- var pcm16 = MemoryMarshal.Cast<byte, short>(pcmSpan.Span);
+ // pass through any filters, if applicable
+ lock (this._filters)
+ {
+ if (this._filters.Any())
+ {
+ foreach (var filter in this._filters)
+ filter.Transform(pcm16, this._connection.AudioFormat, this.SampleDuration);
+ }
+ }
- // pass through any filters, if applicable
- lock (this._filters)
- {
- if (this._filters.Any())
+ if (this.VolumeModifier != 1)
{
- foreach (var filter in this._filters)
- filter.Transform(pcm16, this._connection.AudioFormat, this.SampleDuration);
+ // alter volume
+ for (var i = 0; i < pcm16.Length; i++)
+ pcm16[i] = (short)(pcm16[i] * this.VolumeModifier);
}
}
- if (this.VolumeModifier != 1)
- {
- // alter volume
- for (var i = 0; i < pcm16.Length; i++)
- pcm16[i] = (short)(pcm16[i] * this.VolumeModifier);
- }
+ /// <summary>
+ /// Disposes .
+ /// </summary>
+ public void Dispose()
+ => this._writeSemaphore?.Dispose();
}
-
- /// <summary>
- /// Disposes .
- /// </summary>
- public void Dispose()
- => this._writeSemaphore?.Dispose();
}
diff --git a/DisCatSharp/AsyncManualResetEvent.cs b/DisCatSharp/AsyncManualResetEvent.cs
index a45a88ccd..b8269350c 100644
--- a/DisCatSharp/AsyncManualResetEvent.cs
+++ b/DisCatSharp/AsyncManualResetEvent.cs
@@ -1,83 +1,84 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
-namespace DisCatSharp;
-
-// source: https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-1-asyncmanualresetevent/
-/// <summary>
-/// Implements an async version of a <see cref="ManualResetEvent"/>
-/// This class does currently not support Timeouts or the use of CancellationTokens
-/// </summary>
-internal class AsyncManualResetEvent
+namespace DisCatSharp
{
+ // source: https://blogs.msdn.microsoft.com/pfxteam/2012/02/11/building-async-coordination-primitives-part-1-asyncmanualresetevent/
/// <summary>
- /// Gets a value indicating whether this is set.
+ /// Implements an async version of a <see cref="ManualResetEvent"/>
+ /// This class does currently not support Timeouts or the use of CancellationTokens
/// </summary>
- public bool IsSet => this._tsc != null && this._tsc.Task.IsCompleted;
+ internal class AsyncManualResetEvent
+ {
+ /// <summary>
+ /// Gets a value indicating whether this is set.
+ /// </summary>
+ public bool IsSet => this._tsc != null && this._tsc.Task.IsCompleted;
- private TaskCompletionSource<bool> _tsc;
+ private TaskCompletionSource<bool> _tsc;
- /// <summary>
- /// Initializes a new instance of the <see cref="AsyncManualResetEvent"/> class.
- /// </summary>
- public AsyncManualResetEvent()
- : this(false)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AsyncManualResetEvent"/> class.
+ /// </summary>
+ public AsyncManualResetEvent()
+ : this(false)
+ { }
- /// <summary>
- /// Initializes a new instance of the <see cref="AsyncManualResetEvent"/> class.
- /// </summary>
- /// <param name="initialState">If true, initial state.</param>
- public AsyncManualResetEvent(bool initialState)
- {
- this._tsc = new TaskCompletionSource<bool>();
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AsyncManualResetEvent"/> class.
+ /// </summary>
+ /// <param name="initialState">If true, initial state.</param>
+ public AsyncManualResetEvent(bool initialState)
+ {
+ this._tsc = new TaskCompletionSource<bool>();
- if (initialState) this._tsc.TrySetResult(true);
- }
+ if (initialState) this._tsc.TrySetResult(true);
+ }
- /// <summary>
- /// Waits the async waiter.
- /// </summary>
- public Task WaitAsync() => this._tsc.Task;
+ /// <summary>
+ /// Waits the async waiter.
+ /// </summary>
+ public Task WaitAsync() => this._tsc.Task;
- /// <summary>
- /// Sets the async task.
- /// </summary>
- public Task SetAsync() => Task.Run(() => this._tsc.TrySetResult(true));
+ /// <summary>
+ /// Sets the async task.
+ /// </summary>
+ public Task SetAsync() => Task.Run(() => this._tsc.TrySetResult(true));
- /// <summary>
- /// Resets the async waiter.
- /// </summary>
- public void Reset()
- {
- while (true)
+ /// <summary>
+ /// Resets the async waiter.
+ /// </summary>
+ public void Reset()
{
- var tsc = this._tsc;
+ while (true)
+ {
+ var tsc = this._tsc;
- if (!tsc.Task.IsCompleted || Interlocked.CompareExchange(ref this._tsc, new TaskCompletionSource<bool>(), tsc) == tsc)
- return;
+ if (!tsc.Task.IsCompleted || Interlocked.CompareExchange(ref this._tsc, new TaskCompletionSource<bool>(), tsc) == tsc)
+ return;
+ }
}
}
}
diff --git a/DisCatSharp/BaseExtension.cs b/DisCatSharp/BaseExtension.cs
index 2199924bb..e815402ed 100644
--- a/DisCatSharp/BaseExtension.cs
+++ b/DisCatSharp/BaseExtension.cs
@@ -1,40 +1,41 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents base for all DisCatSharp extensions. To implement your own extension, extend this class, and implement its abstract members.
-/// </summary>
-public abstract class BaseExtension
+namespace DisCatSharp
{
/// <summary>
- /// Gets the instance of <see cref="DiscordClient"/> this extension is attached to.
+ /// Represents base for all DisCatSharp extensions. To implement your own extension, extend this class, and implement its abstract members.
/// </summary>
- public DiscordClient Client { get; protected set; }
+ public abstract class BaseExtension
+ {
+ /// <summary>
+ /// Gets the instance of <see cref="DiscordClient"/> this extension is attached to.
+ /// </summary>
+ public DiscordClient Client { get; protected set; }
- /// <summary>
- /// Initializes this extension for given <see cref="DiscordClient"/> instance.
- /// </summary>
- /// <param name="client">Discord client to initialize for.</param>
- protected internal abstract void Setup(DiscordClient client);
+ /// <summary>
+ /// Initializes this extension for given <see cref="DiscordClient"/> instance.
+ /// </summary>
+ /// <param name="client">Discord client to initialize for.</param>
+ protected internal abstract void Setup(DiscordClient client);
+ }
}
diff --git a/DisCatSharp/Clients/BaseDiscordClient.cs b/DisCatSharp/Clients/BaseDiscordClient.cs
index 9b6e68718..e8985a320 100644
--- a/DisCatSharp/Clients/BaseDiscordClient.cs
+++ b/DisCatSharp/Clients/BaseDiscordClient.cs
@@ -1,320 +1,321 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
#pragma warning disable CS0618
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Net;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a common base for various Discord Client implementations.
-/// </summary>
-public abstract class BaseDiscordClient : IDisposable
+namespace DisCatSharp
{
/// <summary>
- /// Gets the api client.
- /// </summary>
- internal protected DiscordApiClient ApiClient { get; }
-
- /// <summary>
- /// Gets the configuration.
- /// </summary>
- internal protected DiscordConfiguration Configuration { get; }
-
- /// <summary>
- /// Gets the instance of the logger for this client.
- /// </summary>
- public ILogger<BaseDiscordClient> Logger { get; internal set; }
-
- /// <summary>
- /// Gets the string representing the version of bot lib.
- /// </summary>
- public string VersionString { get; }
-
- /// <summary>
- /// Gets the bot library name.
- /// </summary>
- public string BotLibrary { get; }
-
- [Obsolete("Use GetLibraryDeveloperTeamAsync")]
- public DisCatSharpTeam LibraryDeveloperTeamAsync
- => this.GetLibraryDevelopmentTeamAsync().Result;
-
- /// <summary>
- /// Gets the current user.
- /// </summary>
- public DiscordUser CurrentUser { get; internal set; }
-
- /// <summary>
- /// Gets the current application.
- /// </summary>
- public DiscordApplication CurrentApplication { get; internal set; }
-
- /// <summary>
- /// Exposes a <see cref="System.Net.Http.HttpClient">Http Client</see> for custom operations.
+ /// Represents a common base for various Discord Client implementations.
/// </summary>
- public HttpClient RestClient { get; internal set; }
-
- /// <summary>
- /// Gets the cached guilds for this client.
- /// </summary>
- public abstract IReadOnlyDictionary<ulong, DiscordGuild> Guilds { get; }
-
- /// <summary>
- /// Gets the cached users for this client.
- /// </summary>
- public ConcurrentDictionary<ulong, DiscordUser> UserCache { get; internal set; }
-
- /// <summary>
- /// <para>Gets the service provider.</para>
- /// <para>This allows passing data around without resorting to static members.</para>
- /// <para>Defaults to null.</para>
- /// </summary>
- internal IServiceProvider ServiceProvider { get; set; }
-
- /// <summary>
- /// Gets the list of available voice regions. Note that this property will not contain VIP voice regions.
- /// </summary>
- public IReadOnlyDictionary<string, DiscordVoiceRegion> VoiceRegions
- => this.VoiceRegionsLazy.Value;
-
- /// <summary>
- /// Gets the list of available voice regions. This property is meant as a way to modify <see cref="VoiceRegions"/>.
- /// </summary>
- protected internal ConcurrentDictionary<string, DiscordVoiceRegion> InternalVoiceRegions { get; set; }
- internal Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>> VoiceRegionsLazy;
-
- /// <summary>
- /// Initializes this Discord API client.
- /// </summary>
- /// <param name="config">Configuration for this client.</param>
- protected BaseDiscordClient(DiscordConfiguration config)
+ public abstract class BaseDiscordClient : IDisposable
{
- this.Configuration = new DiscordConfiguration(config);
- this.ServiceProvider = config.ServiceProvider;
-
- if (this.ServiceProvider != null)
+ /// <summary>
+ /// Gets the api client.
+ /// </summary>
+ internal protected DiscordApiClient ApiClient { get; }
+
+ /// <summary>
+ /// Gets the configuration.
+ /// </summary>
+ internal protected DiscordConfiguration Configuration { get; }
+
+ /// <summary>
+ /// Gets the instance of the logger for this client.
+ /// </summary>
+ public ILogger<BaseDiscordClient> Logger { get; internal set; }
+
+ /// <summary>
+ /// Gets the string representing the version of bot lib.
+ /// </summary>
+ public string VersionString { get; }
+
+ /// <summary>
+ /// Gets the bot library name.
+ /// </summary>
+ public string BotLibrary { get; }
+
+ [Obsolete("Use GetLibraryDeveloperTeamAsync")]
+ public DisCatSharpTeam LibraryDeveloperTeamAsync
+ => this.GetLibraryDevelopmentTeamAsync().Result;
+
+ /// <summary>
+ /// Gets the current user.
+ /// </summary>
+ public DiscordUser CurrentUser { get; internal set; }
+
+ /// <summary>
+ /// Gets the current application.
+ /// </summary>
+ public DiscordApplication CurrentApplication { get; internal set; }
+
+ /// <summary>
+ /// Exposes a <see cref="System.Net.Http.HttpClient">Http Client</see> for custom operations.
+ /// </summary>
+ public HttpClient RestClient { get; internal set; }
+
+ /// <summary>
+ /// Gets the cached guilds for this client.
+ /// </summary>
+ public abstract IReadOnlyDictionary<ulong, DiscordGuild> Guilds { get; }
+
+ /// <summary>
+ /// Gets the cached users for this client.
+ /// </summary>
+ public ConcurrentDictionary<ulong, DiscordUser> UserCache { get; internal set; }
+
+ /// <summary>
+ /// <para>Gets the service provider.</para>
+ /// <para>This allows passing data around without resorting to static members.</para>
+ /// <para>Defaults to null.</para>
+ /// </summary>
+ internal IServiceProvider ServiceProvider { get; set; }
+
+ /// <summary>
+ /// Gets the list of available voice regions. Note that this property will not contain VIP voice regions.
+ /// </summary>
+ public IReadOnlyDictionary<string, DiscordVoiceRegion> VoiceRegions
+ => this.VoiceRegionsLazy.Value;
+
+ /// <summary>
+ /// Gets the list of available voice regions. This property is meant as a way to modify <see cref="VoiceRegions"/>.
+ /// </summary>
+ protected internal ConcurrentDictionary<string, DiscordVoiceRegion> InternalVoiceRegions { get; set; }
+ internal Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>> VoiceRegionsLazy;
+
+ /// <summary>
+ /// Initializes this Discord API client.
+ /// </summary>
+ /// <param name="config">Configuration for this client.</param>
+ protected BaseDiscordClient(DiscordConfiguration config)
{
- this.Configuration.LoggerFactory ??= config.ServiceProvider.GetService<ILoggerFactory>();
- this.Logger = config.ServiceProvider.GetService<ILogger<BaseDiscordClient>>();
+ this.Configuration = new DiscordConfiguration(config);
+ this.ServiceProvider = config.ServiceProvider;
+
+ if (this.ServiceProvider != null)
+ {
+ this.Configuration.LoggerFactory ??= config.ServiceProvider.GetService<ILoggerFactory>();
+ this.Logger = config.ServiceProvider.GetService<ILogger<BaseDiscordClient>>();
+ }
+
+ if (this.Configuration.LoggerFactory == null)
+ {
+ this.Configuration.LoggerFactory = new DefaultLoggerFactory();
+ this.Configuration.LoggerFactory.AddProvider(new DefaultLoggerProvider(this));
+ }
+ this.Logger ??= this.Configuration.LoggerFactory.CreateLogger<BaseDiscordClient>();
+
+ this.ApiClient = new DiscordApiClient(this);
+ this.UserCache = new ConcurrentDictionary<ulong, DiscordUser>();
+ this.InternalVoiceRegions = new ConcurrentDictionary<string, DiscordVoiceRegion>();
+ this.VoiceRegionsLazy = new Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>>(() => new ReadOnlyDictionary<string, DiscordVoiceRegion>(this.InternalVoiceRegions));
+
+ this.RestClient = new();
+ this.RestClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Utilities.GetUserAgent());
+
+ var a = typeof(DiscordClient).GetTypeInfo().Assembly;
+
+ var iv = a.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
+ if (iv != null)
+ {
+ this.VersionString = iv.InformationalVersion;
+ }
+ else
+ {
+ var v = a.GetName().Version;
+ var vs = v.ToString(3);
+
+ if (v.Revision > 0)
+ this.VersionString = $"{vs}, CI build {v.Revision}";
+ }
+
+ this.BotLibrary = "DisCatSharp";
}
- if (this.Configuration.LoggerFactory == null)
+ /// <summary>
+ /// Gets the current API application.
+ /// </summary>
+ public async Task<DiscordApplication> GetCurrentApplicationAsync()
{
- this.Configuration.LoggerFactory = new DefaultLoggerFactory();
- this.Configuration.LoggerFactory.AddProvider(new DefaultLoggerProvider(this));
+ var tapp = await this.ApiClient.GetCurrentApplicationInfoAsync().ConfigureAwait(false);
+ var app = new DiscordApplication
+ {
+ Discord = this,
+ Id = tapp.Id,
+ Name = tapp.Name,
+ Description = tapp.Description,
+ Summary = tapp.Summary,
+ IconHash = tapp.IconHash,
+ RpcOrigins = tapp.RpcOrigins != null ? new ReadOnlyCollection<string>(tapp.RpcOrigins) : null,
+ Flags = tapp.Flags,
+ RequiresCodeGrant = tapp.BotRequiresCodeGrant,
+ IsPublic = tapp.IsPublicBot,
+ IsHook = tapp.IsHook,
+ Type = tapp.Type,
+ PrivacyPolicyUrl = tapp.PrivacyPolicyUrl,
+ TermsOfServiceUrl = tapp.TermsOfServiceUrl,
+ CustomInstallUrl = tapp.CustomInstallUrl,
+ InstallParams = tapp.InstallParams,
+ Tags = (tapp.Tags ?? Enumerable.Empty<string>()).ToArray()
+ };
+
+ if (tapp.Team == null)
+ {
+ app.Owners = new ReadOnlyCollection<DiscordUser>(new[] { new DiscordUser(tapp.Owner) });
+ app.Team = null;
+ app.TeamName = null;
+ }
+ else
+ {
+ app.Team = new DiscordTeam(tapp.Team);
+
+ var members = tapp.Team.Members
+ .Select(x => new DiscordTeamMember(x) { TeamId = app.Team.Id, TeamName = app.Team.Name, User = new DiscordUser(x.User) })
+ .ToArray();
+
+ var owners = members
+ .Where(x => x.MembershipStatus == DiscordTeamMembershipStatus.Accepted)
+ .Select(x => x.User)
+ .ToArray();
+
+ app.Owners = new ReadOnlyCollection<DiscordUser>(owners);
+ app.Team.Owner = owners.FirstOrDefault(x => x.Id == tapp.Team.OwnerId);
+ app.Team.Members = new ReadOnlyCollection<DiscordTeamMember>(members);
+ app.TeamName = app.Team.Name;
+ }
+
+ app.GuildId = tapp.GuildId.ValueOrDefault();
+ app.Slug = tapp.Slug.ValueOrDefault();
+ app.PrimarySkuId = tapp.PrimarySkuId.ValueOrDefault();
+ app.VerifyKey = tapp.VerifyKey.ValueOrDefault();
+ app.CoverImageHash = tapp.CoverImageHash.ValueOrDefault();
+
+ return app;
}
- this.Logger ??= this.Configuration.LoggerFactory.CreateLogger<BaseDiscordClient>();
-
- this.ApiClient = new DiscordApiClient(this);
- this.UserCache = new ConcurrentDictionary<ulong, DiscordUser>();
- this.InternalVoiceRegions = new ConcurrentDictionary<string, DiscordVoiceRegion>();
- this.VoiceRegionsLazy = new Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>>(() => new ReadOnlyDictionary<string, DiscordVoiceRegion>(this.InternalVoiceRegions));
-
- this.RestClient = new();
- this.RestClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Utilities.GetUserAgent());
- var a = typeof(DiscordClient).GetTypeInfo().Assembly;
-
- var iv = a.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
- if (iv != null)
+ /// <summary>
+ /// Gets a list of voice regions.
+ /// </summary>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordVoiceRegion>> ListVoiceRegionsAsync()
+ => this.ApiClient.ListVoiceRegionsAsync();
+
+ /// <summary>
+ /// Initializes this client. This method fetches information about current user, application, and voice regions.
+ /// </summary>
+ public virtual async Task InitializeAsync()
{
- this.VersionString = iv.InformationalVersion;
+ if (this.CurrentUser == null)
+ {
+ this.CurrentUser = await this.ApiClient.GetCurrentUserAsync().ConfigureAwait(false);
+ this.UserCache.AddOrUpdate(this.CurrentUser.Id, this.CurrentUser, (id, xu) => this.CurrentUser);
+ }
+
+ if (this.Configuration.TokenType == TokenType.Bot && this.CurrentApplication == null)
+ this.CurrentApplication = await this.GetCurrentApplicationAsync().ConfigureAwait(false);
+
+ if (this.Configuration.TokenType != TokenType.Bearer && this.InternalVoiceRegions.IsEmpty)
+ {
+ var vrs = await this.ListVoiceRegionsAsync().ConfigureAwait(false);
+ foreach (var xvr in vrs)
+ this.InternalVoiceRegions.TryAdd(xvr.Id, xvr);
+ }
}
- else
- {
- var v = a.GetName().Version;
- var vs = v.ToString(3);
- if (v.Revision > 0)
- this.VersionString = $"{vs}, CI build {v.Revision}";
- }
-
- this.BotLibrary = "DisCatSharp";
- }
-
- /// <summary>
- /// Gets the current API application.
- /// </summary>
- public async Task<DiscordApplication> GetCurrentApplicationAsync()
- {
- var tapp = await this.ApiClient.GetCurrentApplicationInfoAsync().ConfigureAwait(false);
- var app = new DiscordApplication
- {
- Discord = this,
- Id = tapp.Id,
- Name = tapp.Name,
- Description = tapp.Description,
- Summary = tapp.Summary,
- IconHash = tapp.IconHash,
- RpcOrigins = tapp.RpcOrigins != null ? new ReadOnlyCollection<string>(tapp.RpcOrigins) : null,
- Flags = tapp.Flags,
- RequiresCodeGrant = tapp.BotRequiresCodeGrant,
- IsPublic = tapp.IsPublicBot,
- IsHook = tapp.IsHook,
- Type = tapp.Type,
- PrivacyPolicyUrl = tapp.PrivacyPolicyUrl,
- TermsOfServiceUrl = tapp.TermsOfServiceUrl,
- CustomInstallUrl = tapp.CustomInstallUrl,
- InstallParams = tapp.InstallParams,
- Tags = (tapp.Tags ?? Enumerable.Empty<string>()).ToArray()
- };
-
- if (tapp.Team == null)
- {
- app.Owners = new ReadOnlyCollection<DiscordUser>(new[] { new DiscordUser(tapp.Owner) });
- app.Team = null;
- app.TeamName = null;
- }
- else
+ /// <summary>
+ /// Gets the current gateway info for the provided token.
+ /// <para>If no value is provided, the configuration value will be used instead.</para>
+ /// </summary>
+ /// <returns>A gateway info object.</returns>
+ public async Task<GatewayInfo> GetGatewayInfoAsync(string token = null)
{
- app.Team = new DiscordTeam(tapp.Team);
-
- var members = tapp.Team.Members
- .Select(x => new DiscordTeamMember(x) { TeamId = app.Team.Id, TeamName = app.Team.Name, User = new DiscordUser(x.User) })
- .ToArray();
+ if (this.Configuration.TokenType != TokenType.Bot)
+ throw new InvalidOperationException("Only bot tokens can access this info.");
- var owners = members
- .Where(x => x.MembershipStatus == DiscordTeamMembershipStatus.Accepted)
- .Select(x => x.User)
- .ToArray();
+ if (string.IsNullOrEmpty(this.Configuration.Token))
+ {
+ if (string.IsNullOrEmpty(token))
+ throw new InvalidOperationException("Could not locate a valid token.");
- app.Owners = new ReadOnlyCollection<DiscordUser>(owners);
- app.Team.Owner = owners.FirstOrDefault(x => x.Id == tapp.Team.OwnerId);
- app.Team.Members = new ReadOnlyCollection<DiscordTeamMember>(members);
- app.TeamName = app.Team.Name;
- }
+ this.Configuration.Token = token;
- app.GuildId = tapp.GuildId.ValueOrDefault();
- app.Slug = tapp.Slug.ValueOrDefault();
- app.PrimarySkuId = tapp.PrimarySkuId.ValueOrDefault();
- app.VerifyKey = tapp.VerifyKey.ValueOrDefault();
- app.CoverImageHash = tapp.CoverImageHash.ValueOrDefault();
+ var res = await this.ApiClient.GetGatewayInfoAsync().ConfigureAwait(false);
+ this.Configuration.Token = null;
+ return res;
+ }
- return app;
- }
-
- /// <summary>
- /// Gets a list of voice regions.
- /// </summary>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordVoiceRegion>> ListVoiceRegionsAsync()
- => this.ApiClient.ListVoiceRegionsAsync();
-
- /// <summary>
- /// Initializes this client. This method fetches information about current user, application, and voice regions.
- /// </summary>
- public virtual async Task InitializeAsync()
- {
- if (this.CurrentUser == null)
- {
- this.CurrentUser = await this.ApiClient.GetCurrentUserAsync().ConfigureAwait(false);
- this.UserCache.AddOrUpdate(this.CurrentUser.Id, this.CurrentUser, (id, xu) => this.CurrentUser);
+ return await this.ApiClient.GetGatewayInfoAsync().ConfigureAwait(false);
}
- if (this.Configuration.TokenType == TokenType.Bot && this.CurrentApplication == null)
- this.CurrentApplication = await this.GetCurrentApplicationAsync().ConfigureAwait(false);
-
- if (this.Configuration.TokenType != TokenType.Bearer && this.InternalVoiceRegions.IsEmpty)
+ /// <summary>
+ /// Gets some information about the development team behind DisCatSharp.
+ /// Can be used for crediting etc.
+ /// <para>Note: This call contacts servers managed by the DCS team, no information is collected.</para>
+ /// <returns>The team, or null with errors being logged on failure.</returns>
+ /// </summary>
+ public async Task<DisCatSharpTeam> GetLibraryDevelopmentTeamAsync()
+ => await DisCatSharpTeam.Get(this.RestClient, this.Logger, this.ApiClient).ConfigureAwait(false);
+
+ /// <summary>
+ /// Gets a cached user.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ internal DiscordUser GetCachedOrEmptyUserInternal(ulong userId)
{
- var vrs = await this.ListVoiceRegionsAsync().ConfigureAwait(false);
- foreach (var xvr in vrs)
- this.InternalVoiceRegions.TryAdd(xvr.Id, xvr);
+ this.TryGetCachedUserInternal(userId, out var user);
+ return user;
}
- }
- /// <summary>
- /// Gets the current gateway info for the provided token.
- /// <para>If no value is provided, the configuration value will be used instead.</para>
- /// </summary>
- /// <returns>A gateway info object.</returns>
- public async Task<GatewayInfo> GetGatewayInfoAsync(string token = null)
- {
- if (this.Configuration.TokenType != TokenType.Bot)
- throw new InvalidOperationException("Only bot tokens can access this info.");
-
- if (string.IsNullOrEmpty(this.Configuration.Token))
+ /// <summary>
+ /// Tries the get a cached user.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="user">The user.</param>
+ internal bool TryGetCachedUserInternal(ulong userId, out DiscordUser user)
{
- if (string.IsNullOrEmpty(token))
- throw new InvalidOperationException("Could not locate a valid token.");
-
- this.Configuration.Token = token;
+ if (this.UserCache.TryGetValue(userId, out user))
+ return true;
- var res = await this.ApiClient.GetGatewayInfoAsync().ConfigureAwait(false);
- this.Configuration.Token = null;
- return res;
+ user = new DiscordUser { Id = userId, Discord = this };
+ return false;
}
- return await this.ApiClient.GetGatewayInfoAsync().ConfigureAwait(false);
- }
-
- /// <summary>
- /// Gets some information about the development team behind DisCatSharp.
- /// Can be used for crediting etc.
- /// <para>Note: This call contacts servers managed by the DCS team, no information is collected.</para>
- /// <returns>The team, or null with errors being logged on failure.</returns>
- /// </summary>
- public async Task<DisCatSharpTeam> GetLibraryDevelopmentTeamAsync()
- => await DisCatSharpTeam.Get(this.RestClient, this.Logger, this.ApiClient).ConfigureAwait(false);
-
- /// <summary>
- /// Gets a cached user.
- /// </summary>
- /// <param name="userId">The user id.</param>
- internal DiscordUser GetCachedOrEmptyUserInternal(ulong userId)
- {
- this.TryGetCachedUserInternal(userId, out var user);
- return user;
- }
-
- /// <summary>
- /// Tries the get a cached user.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="user">The user.</param>
- internal bool TryGetCachedUserInternal(ulong userId, out DiscordUser user)
- {
- if (this.UserCache.TryGetValue(userId, out user))
- return true;
-
- user = new DiscordUser { Id = userId, Discord = this };
- return false;
+ /// <summary>
+ /// Disposes this client.
+ /// </summary>
+ public abstract void Dispose();
}
-
- /// <summary>
- /// Disposes this client.
- /// </summary>
- public abstract void Dispose();
}
diff --git a/DisCatSharp/Clients/DiscordClient.Dispatch.cs b/DisCatSharp/Clients/DiscordClient.Dispatch.cs
index a194bcc80..daa7954e1 100644
--- a/DisCatSharp/Clients/DiscordClient.Dispatch.cs
+++ b/DisCatSharp/Clients/DiscordClient.Dispatch.cs
@@ -1,3415 +1,3416 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.EventArgs;
using DisCatSharp.Exceptions;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Serialization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a discord client.
-/// </summary>
-public sealed partial class DiscordClient
+namespace DisCatSharp
{
- #region Private Fields
-
- private string _sessionId;
- private bool _guildDownloadCompleted;
-
- private readonly Dictionary<string, KeyValuePair<TimeoutHandler, Timer>> _tempTimers = new();
-
/// <summary>
- /// Represents a timeout handler.
+ /// Represents a discord client.
/// </summary>
- internal class TimeoutHandler
+ public sealed partial class DiscordClient
{
- /// <summary>
- /// Gets the member.
- /// </summary>
- internal readonly DiscordMember Member;
-
- /// <summary>
- /// Gets the guild.
- /// </summary>
- internal readonly DiscordGuild Guild;
+ #region Private Fields
- /// <summary>
- /// Gets the old timeout value.
- /// </summary>
- internal DateTime? TimeoutUntilOld;
+ private string _sessionId;
+ private bool _guildDownloadCompleted;
- /// <summary>
- /// Gets the new timeout value.
- /// </summary>
- internal DateTime? TimeoutUntilNew;
+ private readonly Dictionary<string, KeyValuePair<TimeoutHandler, Timer>> _tempTimers = new();
/// <summary>
- /// Constructs a new <see cref="TimeoutHandler"/>.
+ /// Represents a timeout handler.
/// </summary>
- /// <param name="mbr">The affected member.</param>
- /// <param name="guild">The affected guild.</param>
- /// <param name="too">The old timeout value.</param>
- /// <param name="ton">The new timeout value.</param>
- internal TimeoutHandler(DiscordMember mbr, DiscordGuild guild, DateTime? too, DateTime? ton)
- {
- this.Guild = guild;
- this.Member = mbr;
- this.TimeoutUntilOld = too;
- this.TimeoutUntilNew = ton;
+ internal class TimeoutHandler
+ {
+ /// <summary>
+ /// Gets the member.
+ /// </summary>
+ internal readonly DiscordMember Member;
+
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ internal readonly DiscordGuild Guild;
+
+ /// <summary>
+ /// Gets the old timeout value.
+ /// </summary>
+ internal DateTime? TimeoutUntilOld;
+
+ /// <summary>
+ /// Gets the new timeout value.
+ /// </summary>
+ internal DateTime? TimeoutUntilNew;
+
+ /// <summary>
+ /// Constructs a new <see cref="TimeoutHandler"/>.
+ /// </summary>
+ /// <param name="mbr">The affected member.</param>
+ /// <param name="guild">The affected guild.</param>
+ /// <param name="too">The old timeout value.</param>
+ /// <param name="ton">The new timeout value.</param>
+ internal TimeoutHandler(DiscordMember mbr, DiscordGuild guild, DateTime? too, DateTime? ton)
+ {
+ this.Guild = guild;
+ this.Member = mbr;
+ this.TimeoutUntilOld = too;
+ this.TimeoutUntilNew = ton;
+ }
}
- }
-
- #endregion
-
- #region Dispatch Handler
-
- /// <summary>
- /// Handles the dispatch payloads.
- /// </summary>
- /// <param name="payload">The payload.</param>
- internal async Task HandleDispatchAsync(GatewayPayload payload)
- {
- if (payload.Data is not JObject dat)
- {
- this.Logger.LogWarning(LoggerEvents.WebSocketReceive, "Invalid payload body (this message is probably safe to ignore); opcode: {0} event: {1}; payload: {2}", payload.OpCode, payload.EventName, payload.Data);
- return;
- }
-
- await this._payloadReceived.InvokeAsync(this, new PayloadReceivedEventArgs(this.ServiceProvider)
- {
- EventName = payload.EventName,
- PayloadObject = dat
- }).ConfigureAwait(false);
-
- #region Default objects
- DiscordChannel chn;
- ulong gid;
- ulong cid;
- ulong uid;
- DiscordStageInstance stg = default;
- DiscordIntegration itg = default;
- DiscordThreadChannel trd = default;
- DiscordThreadChannelMember trdm = default;
- DiscordScheduledEvent gse = default;
- TransportUser usr = default;
- TransportMember mbr = default;
- TransportUser refUsr = default;
- TransportMember refMbr = default;
- JToken rawMbr = default;
- var rawRefMsg = dat["referenced_message"];
#endregion
- switch (payload.EventName.ToLowerInvariant())
- {
- #region Gateway Status
+ #region Dispatch Handler
- case "ready":
- var glds = (JArray)dat["guilds"];
- await this.OnReadyEventAsync(dat.ToObject<ReadyPayload>(), glds).ConfigureAwait(false);
- break;
+ /// <summary>
+ /// Handles the dispatch payloads.
+ /// </summary>
+ /// <param name="payload">The payload.</param>
- case "resumed":
- await this.OnResumedAsync().ConfigureAwait(false);
- break;
+ internal async Task HandleDispatchAsync(GatewayPayload payload)
+ {
+ if (payload.Data is not JObject dat)
+ {
+ this.Logger.LogWarning(LoggerEvents.WebSocketReceive, "Invalid payload body (this message is probably safe to ignore); opcode: {0} event: {1}; payload: {2}", payload.OpCode, payload.EventName, payload.Data);
+ return;
+ }
+ await this._payloadReceived.InvokeAsync(this, new PayloadReceivedEventArgs(this.ServiceProvider)
+ {
+ EventName = payload.EventName,
+ PayloadObject = dat
+ }).ConfigureAwait(false);
+
+ #region Default objects
+ DiscordChannel chn;
+ ulong gid;
+ ulong cid;
+ ulong uid;
+ DiscordStageInstance stg = default;
+ DiscordIntegration itg = default;
+ DiscordThreadChannel trd = default;
+ DiscordThreadChannelMember trdm = default;
+ DiscordScheduledEvent gse = default;
+ TransportUser usr = default;
+ TransportMember mbr = default;
+ TransportUser refUsr = default;
+ TransportMember refMbr = default;
+ JToken rawMbr = default;
+ var rawRefMsg = dat["referenced_message"];
#endregion
- #region Channel
-
- case "channel_create":
- chn = dat.ToObject<DiscordChannel>();
- await this.OnChannelCreateEventAsync(chn).ConfigureAwait(false);
- break;
+ switch (payload.EventName.ToLowerInvariant())
+ {
+ #region Gateway Status
- case "channel_update":
- await this.OnChannelUpdateEventAsync(dat.ToObject<DiscordChannel>()).ConfigureAwait(false);
- break;
+ case "ready":
+ var glds = (JArray)dat["guilds"];
+ await this.OnReadyEventAsync(dat.ToObject<ReadyPayload>(), glds).ConfigureAwait(false);
+ break;
- case "channel_delete":
- chn = dat.ToObject<DiscordChannel>();
- await this.OnChannelDeleteEventAsync(chn.IsPrivate ? dat.ToObject<DiscordDmChannel>() : chn).ConfigureAwait(false);
- break;
+ case "resumed":
+ await this.OnResumedAsync().ConfigureAwait(false);
+ break;
- case "channel_pins_update":
- cid = (ulong)dat["channel_id"];
- var ts = (string)dat["last_pin_timestamp"];
- await this.OnChannelPinsUpdateAsync((ulong?)dat["guild_id"], cid, ts != null ? DateTimeOffset.Parse(ts, CultureInfo.InvariantCulture) : default(DateTimeOffset?)).ConfigureAwait(false);
- break;
+ #endregion
- #endregion
+ #region Channel
- #region Guild
+ case "channel_create":
+ chn = dat.ToObject<DiscordChannel>();
+ await this.OnChannelCreateEventAsync(chn).ConfigureAwait(false);
+ break;
- case "guild_create":
- await this.OnGuildCreateEventAsync(dat.ToDiscordObject<DiscordGuild>(), (JArray)dat["members"], dat["presences"].ToDiscordObject<IEnumerable<DiscordPresence>>()).ConfigureAwait(false);
- break;
+ case "channel_update":
+ await this.OnChannelUpdateEventAsync(dat.ToObject<DiscordChannel>()).ConfigureAwait(false);
+ break;
- case "guild_update":
- await this.OnGuildUpdateEventAsync(dat.ToDiscordObject<DiscordGuild>(), (JArray)dat["members"]).ConfigureAwait(false);
- break;
+ case "channel_delete":
+ chn = dat.ToObject<DiscordChannel>();
+ await this.OnChannelDeleteEventAsync(chn.IsPrivate ? dat.ToObject<DiscordDmChannel>() : chn).ConfigureAwait(false);
+ break;
- case "guild_delete":
- await this.OnGuildDeleteEventAsync(dat.ToDiscordObject<DiscordGuild>()).ConfigureAwait(false);
- break;
+ case "channel_pins_update":
+ cid = (ulong)dat["channel_id"];
+ var ts = (string)dat["last_pin_timestamp"];
+ await this.OnChannelPinsUpdateAsync((ulong?)dat["guild_id"], cid, ts != null ? DateTimeOffset.Parse(ts, CultureInfo.InvariantCulture) : default(DateTimeOffset?)).ConfigureAwait(false);
+ break;
- case "guild_sync":
- gid = (ulong)dat["id"];
- await this.OnGuildSyncEventAsync(this.GuildsInternal[gid], (bool)dat["large"], (JArray)dat["members"], dat["presences"].ToDiscordObject<IEnumerable<DiscordPresence>>()).ConfigureAwait(false);
- break;
+ #endregion
- case "guild_emojis_update":
- gid = (ulong)dat["guild_id"];
- var ems = dat["emojis"].ToObject<IEnumerable<DiscordEmoji>>();
- await this.OnGuildEmojisUpdateEventAsync(this.GuildsInternal[gid], ems).ConfigureAwait(false);
- break;
+ #region Guild
- case "guild_stickers_update":
- gid = (ulong)dat["guild_id"];
- var strs = dat["stickers"].ToDiscordObject<IEnumerable<DiscordSticker>>();
- await this.OnStickersUpdatedAsync(strs, gid).ConfigureAwait(false);
- break;
+ case "guild_create":
+ await this.OnGuildCreateEventAsync(dat.ToDiscordObject<DiscordGuild>(), (JArray)dat["members"], dat["presences"].ToDiscordObject<IEnumerable<DiscordPresence>>()).ConfigureAwait(false);
+ break;
- case "guild_integrations_update":
- gid = (ulong)dat["guild_id"];
+ case "guild_update":
+ await this.OnGuildUpdateEventAsync(dat.ToDiscordObject<DiscordGuild>(), (JArray)dat["members"]).ConfigureAwait(false);
+ break;
- // discord fires this event inconsistently if the current user leaves a guild.
- if (!this.GuildsInternal.ContainsKey(gid))
- return;
+ case "guild_delete":
+ await this.OnGuildDeleteEventAsync(dat.ToDiscordObject<DiscordGuild>()).ConfigureAwait(false);
+ break;
- await this.OnGuildIntegrationsUpdateEventAsync(this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ case "guild_sync":
+ gid = (ulong)dat["id"];
+ await this.OnGuildSyncEventAsync(this.GuildsInternal[gid], (bool)dat["large"], (JArray)dat["members"], dat["presences"].ToDiscordObject<IEnumerable<DiscordPresence>>()).ConfigureAwait(false);
+ break;
- case "guild_join_request_create":
- break;
+ case "guild_emojis_update":
+ gid = (ulong)dat["guild_id"];
+ var ems = dat["emojis"].ToObject<IEnumerable<DiscordEmoji>>();
+ await this.OnGuildEmojisUpdateEventAsync(this.GuildsInternal[gid], ems).ConfigureAwait(false);
+ break;
- case "guild_join_request_update":
- break;
+ case "guild_stickers_update":
+ gid = (ulong)dat["guild_id"];
+ var strs = dat["stickers"].ToDiscordObject<IEnumerable<DiscordSticker>>();
+ await this.OnStickersUpdatedAsync(strs, gid).ConfigureAwait(false);
+ break;
- case "guild_join_request_delete":
- break;
+ case "guild_integrations_update":
+ gid = (ulong)dat["guild_id"];
- #endregion
+ // discord fires this event inconsistently if the current user leaves a guild.
+ if (!this.GuildsInternal.ContainsKey(gid))
+ return;
- #region Guild Ban
+ await this.OnGuildIntegrationsUpdateEventAsync(this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- case "guild_ban_add":
- usr = dat["user"].ToObject<TransportUser>();
- gid = (ulong)dat["guild_id"];
- await this.OnGuildBanAddEventAsync(usr, this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ case "guild_join_request_create":
+ break;
- case "guild_ban_remove":
- usr = dat["user"].ToObject<TransportUser>();
- gid = (ulong)dat["guild_id"];
- await this.OnGuildBanRemoveEventAsync(usr, this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ case "guild_join_request_update":
+ break;
- #endregion
+ case "guild_join_request_delete":
+ break;
- #region Guild Event
-
- case "guild_scheduled_event_create":
- gse = dat.ToObject<DiscordScheduledEvent>();
- gid = (ulong)dat["guild_id"];
- await this.OnGuildScheduledEventCreateEventAsync(gse, this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
-
- case "guild_scheduled_event_update":
- gse = dat.ToObject<DiscordScheduledEvent>();
- gid = (ulong)dat["guild_id"];
- await this.OnGuildScheduledEventUpdateEventAsync(gse, this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
-
- case "guild_scheduled_event_delete":
- gse = dat.ToObject<DiscordScheduledEvent>();
- gid = (ulong)dat["guild_id"];
- await this.OnGuildScheduledEventDeleteEventAsync(gse, this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
-
- case "guild_scheduled_event_user_add":
- gid = (ulong)dat["guild_id"];
- uid = (ulong)dat["user_id"];
- await this.OnGuildScheduledEventUserAddedEventAsync((ulong)dat["guild_scheduled_event_id"], uid, this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
-
- case "guild_scheduled_event_user_remove":
- gid = (ulong)dat["guild_id"];
- uid = (ulong)dat["user_id"];
- await this.OnGuildScheduledEventUserRemovedEventAsync((ulong)dat["guild_scheduled_event_id"], uid, this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ #endregion
- #endregion
+ #region Guild Ban
- #region Guild Integration
+ case "guild_ban_add":
+ usr = dat["user"].ToObject<TransportUser>();
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildBanAddEventAsync(usr, this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- case "integration_create":
- gid = (ulong)dat["guild_id"];
- itg = dat.ToObject<DiscordIntegration>();
+ case "guild_ban_remove":
+ usr = dat["user"].ToObject<TransportUser>();
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildBanRemoveEventAsync(usr, this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- // discord fires this event inconsistently if the current user leaves a guild.
- if (!this.GuildsInternal.ContainsKey(gid))
- return;
+ #endregion
- await this.OnGuildIntegrationCreateEventAsync(this.GuildsInternal[gid], itg).ConfigureAwait(false);
- break;
+ #region Guild Event
+
+ case "guild_scheduled_event_create":
+ gse = dat.ToObject<DiscordScheduledEvent>();
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildScheduledEventCreateEventAsync(gse, this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
+
+ case "guild_scheduled_event_update":
+ gse = dat.ToObject<DiscordScheduledEvent>();
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildScheduledEventUpdateEventAsync(gse, this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
+
+ case "guild_scheduled_event_delete":
+ gse = dat.ToObject<DiscordScheduledEvent>();
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildScheduledEventDeleteEventAsync(gse, this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
+
+ case "guild_scheduled_event_user_add":
+ gid = (ulong)dat["guild_id"];
+ uid = (ulong)dat["user_id"];
+ await this.OnGuildScheduledEventUserAddedEventAsync((ulong)dat["guild_scheduled_event_id"], uid, this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
+
+ case "guild_scheduled_event_user_remove":
+ gid = (ulong)dat["guild_id"];
+ uid = (ulong)dat["user_id"];
+ await this.OnGuildScheduledEventUserRemovedEventAsync((ulong)dat["guild_scheduled_event_id"], uid, this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- case "integration_update":
- gid = (ulong)dat["guild_id"];
- itg = dat.ToObject<DiscordIntegration>();
+ #endregion
- // discord fires this event inconsistently if the current user leaves a guild.
- if (!this.GuildsInternal.ContainsKey(gid))
- return;
+ #region Guild Integration
- await this.OnGuildIntegrationUpdateEventAsync(this.GuildsInternal[gid], itg).ConfigureAwait(false);
- break;
+ case "integration_create":
+ gid = (ulong)dat["guild_id"];
+ itg = dat.ToObject<DiscordIntegration>();
- case "integration_delete":
- gid = (ulong)dat["guild_id"];
+ // discord fires this event inconsistently if the current user leaves a guild.
+ if (!this.GuildsInternal.ContainsKey(gid))
+ return;
- // discord fires this event inconsistently if the current user leaves a guild.
- if (!this.GuildsInternal.ContainsKey(gid))
- return;
+ await this.OnGuildIntegrationCreateEventAsync(this.GuildsInternal[gid], itg).ConfigureAwait(false);
+ break;
- await this.OnGuildIntegrationDeleteEventAsync(this.GuildsInternal[gid], (ulong)dat["id"], (ulong?)dat["application_id"]).ConfigureAwait(false);
- break;
- #endregion
+ case "integration_update":
+ gid = (ulong)dat["guild_id"];
+ itg = dat.ToObject<DiscordIntegration>();
- #region Guild Member
+ // discord fires this event inconsistently if the current user leaves a guild.
+ if (!this.GuildsInternal.ContainsKey(gid))
+ return;
- case "guild_member_add":
- gid = (ulong)dat["guild_id"];
- await this.OnGuildMemberAddEventAsync(dat.ToObject<TransportMember>(), this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ await this.OnGuildIntegrationUpdateEventAsync(this.GuildsInternal[gid], itg).ConfigureAwait(false);
+ break;
- case "guild_member_remove":
- gid = (ulong)dat["guild_id"];
- usr = dat["user"].ToObject<TransportUser>();
+ case "integration_delete":
+ gid = (ulong)dat["guild_id"];
- if (!this.GuildsInternal.ContainsKey(gid))
- {
// discord fires this event inconsistently if the current user leaves a guild.
- if (usr.Id != this.CurrentUser.Id)
- this.Logger.LogError(LoggerEvents.WebSocketReceive, "Could not find {0} in guild cache", gid);
- return;
- }
+ if (!this.GuildsInternal.ContainsKey(gid))
+ return;
- await this.OnGuildMemberRemoveEventAsync(usr, this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ await this.OnGuildIntegrationDeleteEventAsync(this.GuildsInternal[gid], (ulong)dat["id"], (ulong?)dat["application_id"]).ConfigureAwait(false);
+ break;
+ #endregion
- case "guild_member_update":
- gid = (ulong)dat["guild_id"];
- await this.OnGuildMemberUpdateEventAsync(dat.ToDiscordObject<TransportMember>(), this.GuildsInternal[gid], dat["roles"].ToObject<IEnumerable<ulong>>(), (string)dat["nick"], (bool?)dat["pending"]).ConfigureAwait(false);
- break;
+ #region Guild Member
- case "guild_members_chunk":
- await this.OnGuildMembersChunkEventAsync(dat).ConfigureAwait(false);
- break;
+ case "guild_member_add":
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildMemberAddEventAsync(dat.ToObject<TransportMember>(), this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- #endregion
+ case "guild_member_remove":
+ gid = (ulong)dat["guild_id"];
+ usr = dat["user"].ToObject<TransportUser>();
- #region Guild Role
+ if (!this.GuildsInternal.ContainsKey(gid))
+ {
+ // discord fires this event inconsistently if the current user leaves a guild.
+ if (usr.Id != this.CurrentUser.Id)
+ this.Logger.LogError(LoggerEvents.WebSocketReceive, "Could not find {0} in guild cache", gid);
+ return;
+ }
- case "guild_role_create":
- gid = (ulong)dat["guild_id"];
- await this.OnGuildRoleCreateEventAsync(dat["role"].ToObject<DiscordRole>(), this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ await this.OnGuildMemberRemoveEventAsync(usr, this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- case "guild_role_update":
- gid = (ulong)dat["guild_id"];
- await this.OnGuildRoleUpdateEventAsync(dat["role"].ToObject<DiscordRole>(), this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ case "guild_member_update":
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildMemberUpdateEventAsync(dat.ToDiscordObject<TransportMember>(), this.GuildsInternal[gid], dat["roles"].ToObject<IEnumerable<ulong>>(), (string)dat["nick"], (bool?)dat["pending"]).ConfigureAwait(false);
+ break;
- case "guild_role_delete":
- gid = (ulong)dat["guild_id"];
- await this.OnGuildRoleDeleteEventAsync((ulong)dat["role_id"], this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ case "guild_members_chunk":
+ await this.OnGuildMembersChunkEventAsync(dat).ConfigureAwait(false);
+ break;
- #endregion
+ #endregion
- #region Invite
+ #region Guild Role
- case "invite_create":
- gid = (ulong)dat["guild_id"];
- cid = (ulong)dat["channel_id"];
- await this.OnInviteCreateEventAsync(cid, gid, dat.ToObject<DiscordInvite>()).ConfigureAwait(false);
- break;
+ case "guild_role_create":
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildRoleCreateEventAsync(dat["role"].ToObject<DiscordRole>(), this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- case "invite_delete":
- gid = (ulong)dat["guild_id"];
- cid = (ulong)dat["channel_id"];
- await this.OnInviteDeleteEventAsync(cid, gid, dat).ConfigureAwait(false);
- break;
+ case "guild_role_update":
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildRoleUpdateEventAsync(dat["role"].ToObject<DiscordRole>(), this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- #endregion
+ case "guild_role_delete":
+ gid = (ulong)dat["guild_id"];
+ await this.OnGuildRoleDeleteEventAsync((ulong)dat["role_id"], this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- #region Message
+ #endregion
- case "message_ack":
- cid = (ulong)dat["channel_id"];
- var mid = (ulong)dat["message_id"];
- await this.OnMessageAckEventAsync(this.InternalGetCachedChannel(cid), mid).ConfigureAwait(false);
- break;
+ #region Invite
- case "message_create":
- rawMbr = dat["member"];
+ case "invite_create":
+ gid = (ulong)dat["guild_id"];
+ cid = (ulong)dat["channel_id"];
+ await this.OnInviteCreateEventAsync(cid, gid, dat.ToObject<DiscordInvite>()).ConfigureAwait(false);
+ break;
- if (rawMbr != null)
- mbr = rawMbr.ToObject<TransportMember>();
+ case "invite_delete":
+ gid = (ulong)dat["guild_id"];
+ cid = (ulong)dat["channel_id"];
+ await this.OnInviteDeleteEventAsync(cid, gid, dat).ConfigureAwait(false);
+ break;
- if (rawRefMsg != null && rawRefMsg.HasValues)
- {
- if (rawRefMsg.SelectToken("author") != null)
- {
- refUsr = rawRefMsg.SelectToken("author").ToObject<TransportUser>();
- }
+ #endregion
- if (rawRefMsg.SelectToken("member") != null)
- {
- refMbr = rawRefMsg.SelectToken("member").ToObject<TransportMember>();
- }
- }
+ #region Message
- await this.OnMessageCreateEventAsync(dat.ToDiscordObject<DiscordMessage>(), dat["author"].ToObject<TransportUser>(), mbr, refUsr, refMbr).ConfigureAwait(false);
- break;
+ case "message_ack":
+ cid = (ulong)dat["channel_id"];
+ var mid = (ulong)dat["message_id"];
+ await this.OnMessageAckEventAsync(this.InternalGetCachedChannel(cid), mid).ConfigureAwait(false);
+ break;
- case "message_update":
- rawMbr = dat["member"];
+ case "message_create":
+ rawMbr = dat["member"];
- if (rawMbr != null)
- mbr = rawMbr.ToObject<TransportMember>();
+ if (rawMbr != null)
+ mbr = rawMbr.ToObject<TransportMember>();
- if (rawRefMsg != null && rawRefMsg.HasValues)
- {
- if (rawRefMsg.SelectToken("author") != null)
+ if (rawRefMsg != null && rawRefMsg.HasValues)
{
- refUsr = rawRefMsg.SelectToken("author").ToObject<TransportUser>();
+ if (rawRefMsg.SelectToken("author") != null)
+ {
+ refUsr = rawRefMsg.SelectToken("author").ToObject<TransportUser>();
+ }
+
+ if (rawRefMsg.SelectToken("member") != null)
+ {
+ refMbr = rawRefMsg.SelectToken("member").ToObject<TransportMember>();
+ }
}
- if (rawRefMsg.SelectToken("member") != null)
- {
- refMbr = rawRefMsg.SelectToken("member").ToObject<TransportMember>();
- }
- }
+ await this.OnMessageCreateEventAsync(dat.ToDiscordObject<DiscordMessage>(), dat["author"].ToObject<TransportUser>(), mbr, refUsr, refMbr).ConfigureAwait(false);
+ break;
- await this.OnMessageUpdateEventAsync(dat.ToDiscordObject<DiscordMessage>(), dat["author"]?.ToObject<TransportUser>(), mbr, refUsr, refMbr).ConfigureAwait(false);
- break;
+ case "message_update":
+ rawMbr = dat["member"];
- // delete event does *not* include message object
- case "message_delete":
- await this.OnMessageDeleteEventAsync((ulong)dat["id"], (ulong)dat["channel_id"], (ulong?)dat["guild_id"]).ConfigureAwait(false);
- break;
+ if (rawMbr != null)
+ mbr = rawMbr.ToObject<TransportMember>();
- case "message_delete_bulk":
- await this.OnMessageBulkDeleteEventAsync(dat["ids"].ToObject<ulong[]>(), (ulong)dat["channel_id"], (ulong?)dat["guild_id"]).ConfigureAwait(false);
- break;
+ if (rawRefMsg != null && rawRefMsg.HasValues)
+ {
+ if (rawRefMsg.SelectToken("author") != null)
+ {
+ refUsr = rawRefMsg.SelectToken("author").ToObject<TransportUser>();
+ }
+
+ if (rawRefMsg.SelectToken("member") != null)
+ {
+ refMbr = rawRefMsg.SelectToken("member").ToObject<TransportMember>();
+ }
+ }
- #endregion
+ await this.OnMessageUpdateEventAsync(dat.ToDiscordObject<DiscordMessage>(), dat["author"]?.ToObject<TransportUser>(), mbr, refUsr, refMbr).ConfigureAwait(false);
+ break;
- #region Message Reaction
+ // delete event does *not* include message object
+ case "message_delete":
+ await this.OnMessageDeleteEventAsync((ulong)dat["id"], (ulong)dat["channel_id"], (ulong?)dat["guild_id"]).ConfigureAwait(false);
+ break;
- case "message_reaction_add":
- rawMbr = dat["member"];
+ case "message_delete_bulk":
+ await this.OnMessageBulkDeleteEventAsync(dat["ids"].ToObject<ulong[]>(), (ulong)dat["channel_id"], (ulong?)dat["guild_id"]).ConfigureAwait(false);
+ break;
- if (rawMbr != null)
- mbr = rawMbr.ToObject<TransportMember>();
+ #endregion
- await this.OnMessageReactionAddAsync((ulong)dat["user_id"], (ulong)dat["message_id"], (ulong)dat["channel_id"], (ulong?)dat["guild_id"], mbr, dat["emoji"].ToObject<DiscordEmoji>()).ConfigureAwait(false);
- break;
+ #region Message Reaction
- case "message_reaction_remove":
- await this.OnMessageReactionRemoveAsync((ulong)dat["user_id"], (ulong)dat["message_id"], (ulong)dat["channel_id"], (ulong?)dat["guild_id"], dat["emoji"].ToObject<DiscordEmoji>()).ConfigureAwait(false);
- break;
+ case "message_reaction_add":
+ rawMbr = dat["member"];
- case "message_reaction_remove_all":
- await this.OnMessageReactionRemoveAllAsync((ulong)dat["message_id"], (ulong)dat["channel_id"], (ulong?)dat["guild_id"]).ConfigureAwait(false);
- break;
+ if (rawMbr != null)
+ mbr = rawMbr.ToObject<TransportMember>();
- case "message_reaction_remove_emoji":
- await this.OnMessageReactionRemoveEmojiAsync((ulong)dat["message_id"], (ulong)dat["channel_id"], (ulong)dat["guild_id"], dat["emoji"]).ConfigureAwait(false);
- break;
+ await this.OnMessageReactionAddAsync((ulong)dat["user_id"], (ulong)dat["message_id"], (ulong)dat["channel_id"], (ulong?)dat["guild_id"], mbr, dat["emoji"].ToObject<DiscordEmoji>()).ConfigureAwait(false);
+ break;
- #endregion
+ case "message_reaction_remove":
+ await this.OnMessageReactionRemoveAsync((ulong)dat["user_id"], (ulong)dat["message_id"], (ulong)dat["channel_id"], (ulong?)dat["guild_id"], dat["emoji"].ToObject<DiscordEmoji>()).ConfigureAwait(false);
+ break;
- #region Stage Instance
+ case "message_reaction_remove_all":
+ await this.OnMessageReactionRemoveAllAsync((ulong)dat["message_id"], (ulong)dat["channel_id"], (ulong?)dat["guild_id"]).ConfigureAwait(false);
+ break;
- case "stage_instance_create":
- stg = dat.ToObject<DiscordStageInstance>();
- await this.OnStageInstanceCreateEventAsync(stg).ConfigureAwait(false);
- break;
+ case "message_reaction_remove_emoji":
+ await this.OnMessageReactionRemoveEmojiAsync((ulong)dat["message_id"], (ulong)dat["channel_id"], (ulong)dat["guild_id"], dat["emoji"]).ConfigureAwait(false);
+ break;
- case "stage_instance_update":
- stg = dat.ToObject<DiscordStageInstance>();
- await this.OnStageInstanceUpdateEventAsync(stg).ConfigureAwait(false);
- break;
+ #endregion
- case "stage_instance_delete":
- stg = dat.ToObject<DiscordStageInstance>();
- await this.OnStageInstanceDeleteEventAsync(stg).ConfigureAwait(false);
- break;
+ #region Stage Instance
- #endregion
+ case "stage_instance_create":
+ stg = dat.ToObject<DiscordStageInstance>();
+ await this.OnStageInstanceCreateEventAsync(stg).ConfigureAwait(false);
+ break;
- #region Thread
+ case "stage_instance_update":
+ stg = dat.ToObject<DiscordStageInstance>();
+ await this.OnStageInstanceUpdateEventAsync(stg).ConfigureAwait(false);
+ break;
- case "thread_create":
- trd = dat.ToObject<DiscordThreadChannel>();
- await this.OnThreadCreateEventAsync(trd).ConfigureAwait(false);
- break;
+ case "stage_instance_delete":
+ stg = dat.ToObject<DiscordStageInstance>();
+ await this.OnStageInstanceDeleteEventAsync(stg).ConfigureAwait(false);
+ break;
- case "thread_update":
- trd = dat.ToObject<DiscordThreadChannel>();
- await this.OnThreadUpdateEventAsync(trd).ConfigureAwait(false);
- break;
+ #endregion
- case "thread_delete":
- trd = dat.ToObject<DiscordThreadChannel>();
- await this.OnThreadDeleteEventAsync(trd).ConfigureAwait(false);
- break;
+ #region Thread
- case "thread_list_sync":
- gid = (ulong)dat["guild_id"]; //get guild
- await this.OnThreadListSyncEventAsync(this.GuildsInternal[gid], dat["channel_ids"].ToObject<IReadOnlyList<ulong?>>(), dat["threads"].ToObject<IReadOnlyList<DiscordThreadChannel>>(), dat["members"].ToObject<IReadOnlyList<DiscordThreadChannelMember>>()).ConfigureAwait(false);
- break;
+ case "thread_create":
+ trd = dat.ToObject<DiscordThreadChannel>();
+ await this.OnThreadCreateEventAsync(trd).ConfigureAwait(false);
+ break;
- case "thread_member_update":
- trdm = dat.ToObject<DiscordThreadChannelMember>();
- await this.OnThreadMemberUpdateEventAsync(trdm).ConfigureAwait(false);
- break;
+ case "thread_update":
+ trd = dat.ToObject<DiscordThreadChannel>();
+ await this.OnThreadUpdateEventAsync(trd).ConfigureAwait(false);
+ break;
- case "thread_members_update":
- gid = (ulong)dat["guild_id"];
+ case "thread_delete":
+ trd = dat.ToObject<DiscordThreadChannel>();
+ await this.OnThreadDeleteEventAsync(trd).ConfigureAwait(false);
+ break;
- await this.OnThreadMembersUpdateEventAsync(this.GuildsInternal[gid], (ulong)dat["id"], (JArray)dat["added_members"], (JArray)dat["removed_member_ids"], (int)dat["member_count"]).ConfigureAwait(false);
- break;
+ case "thread_list_sync":
+ gid = (ulong)dat["guild_id"]; //get guild
+ await this.OnThreadListSyncEventAsync(this.GuildsInternal[gid], dat["channel_ids"].ToObject<IReadOnlyList<ulong?>>(), dat["threads"].ToObject<IReadOnlyList<DiscordThreadChannel>>(), dat["members"].ToObject<IReadOnlyList<DiscordThreadChannelMember>>()).ConfigureAwait(false);
+ break;
- #endregion
+ case "thread_member_update":
+ trdm = dat.ToObject<DiscordThreadChannelMember>();
+ await this.OnThreadMemberUpdateEventAsync(trdm).ConfigureAwait(false);
+ break;
- #region Activities
- case "embedded_activity_update":
- gid = (ulong)dat["guild_id"];
- cid = (ulong)dat["channel_id"];
- await this.OnEmbeddedActivityUpdateAsync((JObject)dat["embedded_activity"], this.GuildsInternal[gid], cid, (JArray)dat["users"], (ulong)dat["embedded_activity"]["application_id"]).ConfigureAwait(false);
- break;
- #endregion
+ case "thread_members_update":
+ gid = (ulong)dat["guild_id"];
- #region User/Presence Update
+ await this.OnThreadMembersUpdateEventAsync(this.GuildsInternal[gid], (ulong)dat["id"], (JArray)dat["added_members"], (JArray)dat["removed_member_ids"], (int)dat["member_count"]).ConfigureAwait(false);
+ break;
- case "presence_update":
- await this.OnPresenceUpdateEventAsync(dat, (JObject)dat["user"]).ConfigureAwait(false);
- break;
+ #endregion
- case "user_settings_update":
- await this.OnUserSettingsUpdateEventAsync(dat.ToObject<TransportUser>()).ConfigureAwait(false);
- break;
+ #region Activities
+ case "embedded_activity_update":
+ gid = (ulong)dat["guild_id"];
+ cid = (ulong)dat["channel_id"];
+ await this.OnEmbeddedActivityUpdateAsync((JObject)dat["embedded_activity"], this.GuildsInternal[gid], cid, (JArray)dat["users"], (ulong)dat["embedded_activity"]["application_id"]).ConfigureAwait(false);
+ break;
+ #endregion
- case "user_update":
- await this.OnUserUpdateEventAsync(dat.ToObject<TransportUser>()).ConfigureAwait(false);
- break;
+ #region User/Presence Update
- #endregion
+ case "presence_update":
+ await this.OnPresenceUpdateEventAsync(dat, (JObject)dat["user"]).ConfigureAwait(false);
+ break;
- #region Voice
+ case "user_settings_update":
+ await this.OnUserSettingsUpdateEventAsync(dat.ToObject<TransportUser>()).ConfigureAwait(false);
+ break;
- case "voice_state_update":
- await this.OnVoiceStateUpdateEventAsync(dat).ConfigureAwait(false);
- break;
+ case "user_update":
+ await this.OnUserUpdateEventAsync(dat.ToObject<TransportUser>()).ConfigureAwait(false);
+ break;
- case "voice_server_update":
- gid = (ulong)dat["guild_id"];
- await this.OnVoiceServerUpdateEventAsync((string)dat["endpoint"], (string)dat["token"], this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ #endregion
- #endregion
+ #region Voice
- #region Interaction/Integration/Application
+ case "voice_state_update":
+ await this.OnVoiceStateUpdateEventAsync(dat).ConfigureAwait(false);
+ break;
- case "interaction_create":
- rawMbr = dat["member"];
+ case "voice_server_update":
+ gid = (ulong)dat["guild_id"];
+ await this.OnVoiceServerUpdateEventAsync((string)dat["endpoint"], (string)dat["token"], this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- if (rawMbr != null)
- {
- mbr = dat["member"].ToObject<TransportMember>();
- usr = mbr.User;
- }
- else
- {
- usr = dat["user"].ToObject<TransportUser>();
- }
+ #endregion
- cid = (ulong)dat["channel_id"];
- await this.OnInteractionCreateAsync((ulong?)dat["guild_id"], cid, usr, mbr, dat.ToDiscordObject<DiscordInteraction>()).ConfigureAwait(false);
- break;
+ #region Interaction/Integration/Application
- case "application_command_create":
- await this.OnApplicationCommandCreateAsync(dat.ToObject<DiscordApplicationCommand>(), (ulong?)dat["guild_id"]).ConfigureAwait(false);
- break;
+ case "interaction_create":
+ rawMbr = dat["member"];
- case "application_command_update":
- await this.OnApplicationCommandUpdateAsync(dat.ToObject<DiscordApplicationCommand>(), (ulong?)dat["guild_id"]).ConfigureAwait(false);
- break;
+ if (rawMbr != null)
+ {
+ mbr = dat["member"].ToObject<TransportMember>();
+ usr = mbr.User;
+ }
+ else
+ {
+ usr = dat["user"].ToObject<TransportUser>();
+ }
- case "application_command_delete":
- await this.OnApplicationCommandDeleteAsync(dat.ToObject<DiscordApplicationCommand>(), (ulong?)dat["guild_id"]).ConfigureAwait(false);
- break;
+ cid = (ulong)dat["channel_id"];
+ await this.OnInteractionCreateAsync((ulong?)dat["guild_id"], cid, usr, mbr, dat.ToDiscordObject<DiscordInteraction>()).ConfigureAwait(false);
+ break;
- case "guild_application_command_counts_update":
- var counts = dat["application_command_counts"];
- await this.OnGuildApplicationCommandCountsUpdateAsync((int)counts["1"], (int)counts["2"], (int)counts["3"], (ulong)dat["guild_id"]).ConfigureAwait(false);
- break;
+ case "application_command_create":
+ await this.OnApplicationCommandCreateAsync(dat.ToObject<DiscordApplicationCommand>(), (ulong?)dat["guild_id"]).ConfigureAwait(false);
+ break;
- case "application_command_permissions_update":
- var aid = (ulong)dat["application_id"];
- if (aid != this.CurrentApplication.Id)
- return;
+ case "application_command_update":
+ await this.OnApplicationCommandUpdateAsync(dat.ToObject<DiscordApplicationCommand>(), (ulong?)dat["guild_id"]).ConfigureAwait(false);
+ break;
- var pms = dat["permissions"].ToObject<IEnumerable<DiscordApplicationCommandPermission>>();
- gid = (ulong)dat["guild_id"];
- await this.OnApplicationCommandPermissionsUpdateAsync(pms, (ulong)dat["id"], gid, aid).ConfigureAwait(false);
- break;
+ case "application_command_delete":
+ await this.OnApplicationCommandDeleteAsync(dat.ToObject<DiscordApplicationCommand>(), (ulong?)dat["guild_id"]).ConfigureAwait(false);
+ break;
- #endregion
+ case "guild_application_command_counts_update":
+ var counts = dat["application_command_counts"];
+ await this.OnGuildApplicationCommandCountsUpdateAsync((int)counts["1"], (int)counts["2"], (int)counts["3"], (ulong)dat["guild_id"]).ConfigureAwait(false);
+ break;
- #region Misc
+ case "application_command_permissions_update":
+ var aid = (ulong)dat["application_id"];
+ if (aid != this.CurrentApplication.Id)
+ return;
- case "gift_code_update": //Not supposed to be dispatched to bots
- break;
+ var pms = dat["permissions"].ToObject<IEnumerable<DiscordApplicationCommandPermission>>();
+ gid = (ulong)dat["guild_id"];
+ await this.OnApplicationCommandPermissionsUpdateAsync(pms, (ulong)dat["id"], gid, aid).ConfigureAwait(false);
+ break;
- case "typing_start":
- cid = (ulong)dat["channel_id"];
- rawMbr = dat["member"];
+ #endregion
- if (rawMbr != null)
- mbr = rawMbr.ToObject<TransportMember>();
+ #region Misc
- await this.OnTypingStartEventAsync((ulong)dat["user_id"], cid, this.InternalGetCachedChannel(cid), (ulong?)dat["guild_id"], Utilities.GetDateTimeOffset((long)dat["timestamp"]), mbr).ConfigureAwait(false);
- break;
+ case "gift_code_update": //Not supposed to be dispatched to bots
+ break;
- case "webhooks_update":
- gid = (ulong)dat["guild_id"];
- cid = (ulong)dat["channel_id"];
- await this.OnWebhooksUpdateAsync(this.GuildsInternal[gid].GetChannel(cid), this.GuildsInternal[gid]).ConfigureAwait(false);
- break;
+ case "typing_start":
+ cid = (ulong)dat["channel_id"];
+ rawMbr = dat["member"];
- default:
- await this.OnUnknownEventAsync(payload).ConfigureAwait(false);
- this.Logger.LogWarning(LoggerEvents.WebSocketReceive, "Unknown event: {0}\npayload: {1}", payload.EventName, payload.Data);
- break;
+ if (rawMbr != null)
+ mbr = rawMbr.ToObject<TransportMember>();
- #endregion
- }
- }
+ await this.OnTypingStartEventAsync((ulong)dat["user_id"], cid, this.InternalGetCachedChannel(cid), (ulong?)dat["guild_id"], Utilities.GetDateTimeOffset((long)dat["timestamp"]), mbr).ConfigureAwait(false);
+ break;
- #endregion
+ case "webhooks_update":
+ gid = (ulong)dat["guild_id"];
+ cid = (ulong)dat["channel_id"];
+ await this.OnWebhooksUpdateAsync(this.GuildsInternal[gid].GetChannel(cid), this.GuildsInternal[gid]).ConfigureAwait(false);
+ break;
- #region Events
+ default:
+ await this.OnUnknownEventAsync(payload).ConfigureAwait(false);
+ this.Logger.LogWarning(LoggerEvents.WebSocketReceive, "Unknown event: {0}\npayload: {1}", payload.EventName, payload.Data);
+ break;
- #region Gateway
+ #endregion
+ }
+ }
- /// <summary>
- /// Handles the ready event.
- /// </summary>
- /// <param name="ready">The ready payload.</param>
- /// <param name="rawGuilds">The raw guilds.</param>
- internal async Task OnReadyEventAsync(ReadyPayload ready, JArray rawGuilds)
- {
- //ready.CurrentUser.Discord = this;
+ #endregion
- var rusr = ready.CurrentUser;
- this.CurrentUser.Username = rusr.Username;
- this.CurrentUser.Discriminator = rusr.Discriminator;
- this.CurrentUser.AvatarHash = rusr.AvatarHash;
- this.CurrentUser.MfaEnabled = rusr.MfaEnabled;
- this.CurrentUser.Verified = rusr.Verified;
- this.CurrentUser.IsBot = rusr.IsBot;
- this.CurrentUser.Flags = rusr.Flags;
+ #region Events
- this.GatewayVersion = ready.GatewayVersion;
- this._sessionId = ready.SessionId;
- var rawGuildIndex = rawGuilds.ToDictionary(xt => (ulong)xt["id"], xt => (JObject)xt);
+ #region Gateway
- this.GuildsInternal.Clear();
- foreach (var guild in ready.Guilds)
- {
- guild.Discord = this;
+ /// <summary>
+ /// Handles the ready event.
+ /// </summary>
+ /// <param name="ready">The ready payload.</param>
+ /// <param name="rawGuilds">The raw guilds.</param>
+ internal async Task OnReadyEventAsync(ReadyPayload ready, JArray rawGuilds)
+ {
+ //ready.CurrentUser.Discord = this;
+
+ var rusr = ready.CurrentUser;
+ this.CurrentUser.Username = rusr.Username;
+ this.CurrentUser.Discriminator = rusr.Discriminator;
+ this.CurrentUser.AvatarHash = rusr.AvatarHash;
+ this.CurrentUser.MfaEnabled = rusr.MfaEnabled;
+ this.CurrentUser.Verified = rusr.Verified;
+ this.CurrentUser.IsBot = rusr.IsBot;
+ this.CurrentUser.Flags = rusr.Flags;
+
+ this.GatewayVersion = ready.GatewayVersion;
+ this._sessionId = ready.SessionId;
+ var rawGuildIndex = rawGuilds.ToDictionary(xt => (ulong)xt["id"], xt => (JObject)xt);
+
+ this.GuildsInternal.Clear();
+ foreach (var guild in ready.Guilds)
+ {
+ guild.Discord = this;
- if (guild.ChannelsInternal == null)
- guild.ChannelsInternal = new ConcurrentDictionary<ulong, DiscordChannel>();
+ if (guild.ChannelsInternal == null)
+ guild.ChannelsInternal = new ConcurrentDictionary<ulong, DiscordChannel>();
- foreach (var xc in guild.Channels.Values)
- {
- xc.GuildId = guild.Id;
- xc.Discord = this;
- foreach (var xo in xc.PermissionOverwritesInternal)
+ foreach (var xc in guild.Channels.Values)
{
- xo.Discord = this;
- xo.ChannelId = xc.Id;
+ xc.GuildId = guild.Id;
+ xc.Discord = this;
+ foreach (var xo in xc.PermissionOverwritesInternal)
+ {
+ xo.Discord = this;
+ xo.ChannelId = xc.Id;
+ }
}
- }
- if (guild.RolesInternal == null)
- guild.RolesInternal = new ConcurrentDictionary<ulong, DiscordRole>();
+ if (guild.RolesInternal == null)
+ guild.RolesInternal = new ConcurrentDictionary<ulong, DiscordRole>();
- foreach (var xr in guild.Roles.Values)
- {
- xr.Discord = this;
- xr.GuildId = guild.Id;
- }
+ foreach (var xr in guild.Roles.Values)
+ {
+ xr.Discord = this;
+ xr.GuildId = guild.Id;
+ }
- var rawGuild = rawGuildIndex[guild.Id];
- var rawMembers = (JArray)rawGuild["members"];
+ var rawGuild = rawGuildIndex[guild.Id];
+ var rawMembers = (JArray)rawGuild["members"];
- if (guild.MembersInternal != null)
- guild.MembersInternal.Clear();
- else
- guild.MembersInternal = new ConcurrentDictionary<ulong, DiscordMember>();
+ if (guild.MembersInternal != null)
+ guild.MembersInternal.Clear();
+ else
+ guild.MembersInternal = new ConcurrentDictionary<ulong, DiscordMember>();
- if (rawMembers != null)
- {
- foreach (var xj in rawMembers)
+ if (rawMembers != null)
{
- var xtm = xj.ToObject<TransportMember>();
-
- var xu = new DiscordUser(xtm.User) { Discord = this };
- xu = this.UserCache.AddOrUpdate(xtm.User.Id, xu, (id, old) =>
+ foreach (var xj in rawMembers)
{
- old.Username = xu.Username;
- old.Discriminator = xu.Discriminator;
- old.AvatarHash = xu.AvatarHash;
- return old;
- });
-
- guild.MembersInternal[xtm.User.Id] = new DiscordMember(xtm) { Discord = this, GuildId = guild.Id };
+ var xtm = xj.ToObject<TransportMember>();
+
+ var xu = new DiscordUser(xtm.User) { Discord = this };
+ xu = this.UserCache.AddOrUpdate(xtm.User.Id, xu, (id, old) =>
+ {
+ old.Username = xu.Username;
+ old.Discriminator = xu.Discriminator;
+ old.AvatarHash = xu.AvatarHash;
+ return old;
+ });
+
+ guild.MembersInternal[xtm.User.Id] = new DiscordMember(xtm) { Discord = this, GuildId = guild.Id };
+ }
}
- }
- if (guild.EmojisInternal == null)
- guild.EmojisInternal = new ConcurrentDictionary<ulong, DiscordEmoji>();
+ if (guild.EmojisInternal == null)
+ guild.EmojisInternal = new ConcurrentDictionary<ulong, DiscordEmoji>();
- foreach (var xe in guild.Emojis.Values)
- xe.Discord = this;
+ foreach (var xe in guild.Emojis.Values)
+ xe.Discord = this;
- if (guild.StickersInternal == null)
- guild.StickersInternal = new ConcurrentDictionary<ulong, DiscordSticker>();
+ if (guild.StickersInternal == null)
+ guild.StickersInternal = new ConcurrentDictionary<ulong, DiscordSticker>();
- foreach (var xs in guild.Stickers.Values)
- xs.Discord = this;
+ foreach (var xs in guild.Stickers.Values)
+ xs.Discord = this;
- if (guild.VoiceStatesInternal == null)
- guild.VoiceStatesInternal = new ConcurrentDictionary<ulong, DiscordVoiceState>();
+ if (guild.VoiceStatesInternal == null)
+ guild.VoiceStatesInternal = new ConcurrentDictionary<ulong, DiscordVoiceState>();
- foreach (var xvs in guild.VoiceStates.Values)
- xvs.Discord = this;
+ foreach (var xvs in guild.VoiceStates.Values)
+ xvs.Discord = this;
- if (guild.ThreadsInternal == null)
- guild.ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
+ if (guild.ThreadsInternal == null)
+ guild.ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
- foreach (var xt in guild.ThreadsInternal.Values)
- xt.Discord = this;
+ foreach (var xt in guild.ThreadsInternal.Values)
+ xt.Discord = this;
- if (guild.StageInstancesInternal == null)
- guild.StageInstancesInternal = new ConcurrentDictionary<ulong, DiscordStageInstance>();
+ if (guild.StageInstancesInternal == null)
+ guild.StageInstancesInternal = new ConcurrentDictionary<ulong, DiscordStageInstance>();
- foreach (var xsi in guild.StageInstancesInternal.Values)
- xsi.Discord = this;
+ foreach (var xsi in guild.StageInstancesInternal.Values)
+ xsi.Discord = this;
- if (guild.ScheduledEventsInternal == null)
- guild.ScheduledEventsInternal = new ConcurrentDictionary<ulong, DiscordScheduledEvent>();
+ if (guild.ScheduledEventsInternal == null)
+ guild.ScheduledEventsInternal = new ConcurrentDictionary<ulong, DiscordScheduledEvent>();
- foreach (var xse in guild.ScheduledEventsInternal.Values)
- xse.Discord = this;
+ foreach (var xse in guild.ScheduledEventsInternal.Values)
+ xse.Discord = this;
- this.GuildsInternal[guild.Id] = guild;
- }
+ this.GuildsInternal[guild.Id] = guild;
+ }
- await this._ready.InvokeAsync(this, new ReadyEventArgs(this.ServiceProvider)).ConfigureAwait(false);
- }
+ await this._ready.InvokeAsync(this, new ReadyEventArgs(this.ServiceProvider)).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the resumed event.
- /// </summary>
- internal Task OnResumedAsync()
- {
- this.Logger.LogInformation(LoggerEvents.SessionUpdate, "Session resumed");
- return this._resumed.InvokeAsync(this, new ReadyEventArgs(this.ServiceProvider));
- }
+ /// <summary>
+ /// Handles the resumed event.
+ /// </summary>
+ internal Task OnResumedAsync()
+ {
+ this.Logger.LogInformation(LoggerEvents.SessionUpdate, "Session resumed");
+ return this._resumed.InvokeAsync(this, new ReadyEventArgs(this.ServiceProvider));
+ }
- #endregion
+ #endregion
- #region Channel
+ #region Channel
- /// <summary>
- /// Handles the channel create event.
- /// </summary>
- /// <param name="channel">The channel.</param>
- internal async Task OnChannelCreateEventAsync(DiscordChannel channel)
- {
- channel.Discord = this;
- foreach (var xo in channel.PermissionOverwritesInternal)
+ /// <summary>
+ /// Handles the channel create event.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ internal async Task OnChannelCreateEventAsync(DiscordChannel channel)
{
- xo.Discord = this;
- xo.ChannelId = channel.Id;
- }
+ channel.Discord = this;
+ foreach (var xo in channel.PermissionOverwritesInternal)
+ {
+ xo.Discord = this;
+ xo.ChannelId = channel.Id;
+ }
- this.GuildsInternal[channel.GuildId.Value].ChannelsInternal[channel.Id] = channel;
+ this.GuildsInternal[channel.GuildId.Value].ChannelsInternal[channel.Id] = channel;
- /*if (this.Configuration.AutoRefreshChannelCache)
+ /*if (this.Configuration.AutoRefreshChannelCache)
{
await this.RefreshChannelsAsync(channel.Guild.Id);
}*/
- await this._channelCreated.InvokeAsync(this, new ChannelCreateEventArgs(this.ServiceProvider) { Channel = channel, Guild = channel.Guild }).ConfigureAwait(false);
- }
+ await this._channelCreated.InvokeAsync(this, new ChannelCreateEventArgs(this.ServiceProvider) { Channel = channel, Guild = channel.Guild }).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the channel update event.
- /// </summary>
- /// <param name="channel">The channel.</param>
- internal async Task OnChannelUpdateEventAsync(DiscordChannel channel)
- {
- if (channel == null)
- return;
+ /// <summary>
+ /// Handles the channel update event.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ internal async Task OnChannelUpdateEventAsync(DiscordChannel channel)
+ {
+ if (channel == null)
+ return;
- channel.Discord = this;
+ channel.Discord = this;
- var gld = channel.Guild;
+ var gld = channel.Guild;
- var channelNew = this.InternalGetCachedChannel(channel.Id);
- DiscordChannel channelOld = null;
+ var channelNew = this.InternalGetCachedChannel(channel.Id);
+ DiscordChannel channelOld = null;
- if (channelNew != null)
- {
- channelOld = new DiscordChannel
+ if (channelNew != null)
{
- Bitrate = channelNew.Bitrate,
- Discord = this,
- GuildId = channelNew.GuildId,
- Id = channelNew.Id,
- LastMessageId = channelNew.LastMessageId,
- Name = channelNew.Name,
- PermissionOverwritesInternal = new List<DiscordOverwrite>(channelNew.PermissionOverwritesInternal),
- Position = channelNew.Position,
- Topic = channelNew.Topic,
- Type = channelNew.Type,
- UserLimit = channelNew.UserLimit,
- ParentId = channelNew.ParentId,
- IsNsfw = channelNew.IsNsfw,
- PerUserRateLimit = channelNew.PerUserRateLimit,
- RtcRegionId = channelNew.RtcRegionId,
- QualityMode = channelNew.QualityMode,
- DefaultAutoArchiveDuration = channelNew.DefaultAutoArchiveDuration
- };
+ channelOld = new DiscordChannel
+ {
+ Bitrate = channelNew.Bitrate,
+ Discord = this,
+ GuildId = channelNew.GuildId,
+ Id = channelNew.Id,
+ LastMessageId = channelNew.LastMessageId,
+ Name = channelNew.Name,
+ PermissionOverwritesInternal = new List<DiscordOverwrite>(channelNew.PermissionOverwritesInternal),
+ Position = channelNew.Position,
+ Topic = channelNew.Topic,
+ Type = channelNew.Type,
+ UserLimit = channelNew.UserLimit,
+ ParentId = channelNew.ParentId,
+ IsNsfw = channelNew.IsNsfw,
+ PerUserRateLimit = channelNew.PerUserRateLimit,
+ RtcRegionId = channelNew.RtcRegionId,
+ QualityMode = channelNew.QualityMode,
+ DefaultAutoArchiveDuration = channelNew.DefaultAutoArchiveDuration
+ };
- channelNew.Bitrate = channel.Bitrate;
- channelNew.Name = channel.Name;
- channelNew.Position = channel.Position;
- channelNew.Topic = channel.Topic;
- channelNew.UserLimit = channel.UserLimit;
- channelNew.ParentId = channel.ParentId;
- channelNew.IsNsfw = channel.IsNsfw;
- channelNew.PerUserRateLimit = channel.PerUserRateLimit;
- channelNew.Type = channel.Type;
- channelNew.RtcRegionId = channel.RtcRegionId;
- channelNew.QualityMode = channel.QualityMode;
- channelNew.DefaultAutoArchiveDuration = channel.DefaultAutoArchiveDuration;
+ channelNew.Bitrate = channel.Bitrate;
+ channelNew.Name = channel.Name;
+ channelNew.Position = channel.Position;
+ channelNew.Topic = channel.Topic;
+ channelNew.UserLimit = channel.UserLimit;
+ channelNew.ParentId = channel.ParentId;
+ channelNew.IsNsfw = channel.IsNsfw;
+ channelNew.PerUserRateLimit = channel.PerUserRateLimit;
+ channelNew.Type = channel.Type;
+ channelNew.RtcRegionId = channel.RtcRegionId;
+ channelNew.QualityMode = channel.QualityMode;
+ channelNew.DefaultAutoArchiveDuration = channel.DefaultAutoArchiveDuration;
+
+ channelNew.PermissionOverwritesInternal.Clear();
+
+ foreach (var po in channel.PermissionOverwritesInternal)
+ {
+ po.Discord = this;
+ po.ChannelId = channel.Id;
+ }
- channelNew.PermissionOverwritesInternal.Clear();
+ channelNew.PermissionOverwritesInternal.AddRange(channel.PermissionOverwritesInternal);
- foreach (var po in channel.PermissionOverwritesInternal)
- {
- po.Discord = this;
- po.ChannelId = channel.Id;
+ if (this.Configuration.AutoRefreshChannelCache && gld != null)
+ {
+ await this.RefreshChannelsAsync(channel.Guild.Id);
+ }
}
-
- channelNew.PermissionOverwritesInternal.AddRange(channel.PermissionOverwritesInternal);
-
- if (this.Configuration.AutoRefreshChannelCache && gld != null)
+ else if (gld != null)
{
- await this.RefreshChannelsAsync(channel.Guild.Id);
- }
- }
- else if (gld != null)
- {
- gld.ChannelsInternal[channel.Id] = channel;
+ gld.ChannelsInternal[channel.Id] = channel;
- if (this.Configuration.AutoRefreshChannelCache)
- {
- await this.RefreshChannelsAsync(channel.Guild.Id);
+ if (this.Configuration.AutoRefreshChannelCache)
+ {
+ await this.RefreshChannelsAsync(channel.Guild.Id);
+ }
}
+
+ await this._channelUpdated.InvokeAsync(this, new ChannelUpdateEventArgs(this.ServiceProvider) { ChannelAfter = channelNew, Guild = gld, ChannelBefore = channelOld }).ConfigureAwait(false);
}
- await this._channelUpdated.InvokeAsync(this, new ChannelUpdateEventArgs(this.ServiceProvider) { ChannelAfter = channelNew, Guild = gld, ChannelBefore = channelOld }).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Handles the channel delete event.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ internal async Task OnChannelDeleteEventAsync(DiscordChannel channel)
+ {
+ if (channel == null)
+ return;
- /// <summary>
- /// Handles the channel delete event.
- /// </summary>
- /// <param name="channel">The channel.</param>
- internal async Task OnChannelDeleteEventAsync(DiscordChannel channel)
- {
- if (channel == null)
- return;
+ channel.Discord = this;
- channel.Discord = this;
+ //if (channel.IsPrivate)
+ if (channel.Type == ChannelType.Group || channel.Type == ChannelType.Private)
+ {
+ var dmChannel = channel as DiscordDmChannel;
- //if (channel.IsPrivate)
- if (channel.Type == ChannelType.Group || channel.Type == ChannelType.Private)
- {
- var dmChannel = channel as DiscordDmChannel;
+ await this._dmChannelDeleted.InvokeAsync(this, new DmChannelDeleteEventArgs(this.ServiceProvider) { Channel = dmChannel }).ConfigureAwait(false);
+ }
+ else
+ {
+ var gld = channel.Guild;
- await this._dmChannelDeleted.InvokeAsync(this, new DmChannelDeleteEventArgs(this.ServiceProvider) { Channel = dmChannel }).ConfigureAwait(false);
- }
- else
- {
- var gld = channel.Guild;
+ if (gld.ChannelsInternal.TryRemove(channel.Id, out var cachedChannel)) channel = cachedChannel;
- if (gld.ChannelsInternal.TryRemove(channel.Id, out var cachedChannel)) channel = cachedChannel;
+ if (this.Configuration.AutoRefreshChannelCache)
+ {
+ await this.RefreshChannelsAsync(channel.Guild.Id);
+ }
- if (this.Configuration.AutoRefreshChannelCache)
- {
- await this.RefreshChannelsAsync(channel.Guild.Id);
+ await this._channelDeleted.InvokeAsync(this, new ChannelDeleteEventArgs(this.ServiceProvider) { Channel = channel, Guild = gld }).ConfigureAwait(false);
}
-
- await this._channelDeleted.InvokeAsync(this, new ChannelDeleteEventArgs(this.ServiceProvider) { Channel = channel, Guild = gld }).ConfigureAwait(false);
}
- }
- /// <summary>
- /// Refreshes the channels.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- internal async Task RefreshChannelsAsync(ulong guildId)
- {
- var guild = this.InternalGetCachedGuild(guildId);
- var channels = await this.ApiClient.GetGuildChannelsAsync(guildId);
- guild.ChannelsInternal.Clear();
- foreach (var channel in channels.ToList())
+ /// <summary>
+ /// Refreshes the channels.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ internal async Task RefreshChannelsAsync(ulong guildId)
{
- channel.Discord = this;
- foreach (var xo in channel.PermissionOverwritesInternal)
+ var guild = this.InternalGetCachedGuild(guildId);
+ var channels = await this.ApiClient.GetGuildChannelsAsync(guildId);
+ guild.ChannelsInternal.Clear();
+ foreach (var channel in channels.ToList())
{
- xo.Discord = this;
- xo.ChannelId = channel.Id;
+ channel.Discord = this;
+ foreach (var xo in channel.PermissionOverwritesInternal)
+ {
+ xo.Discord = this;
+ xo.ChannelId = channel.Id;
+ }
+ guild.ChannelsInternal[channel.Id] = channel;
}
- guild.ChannelsInternal[channel.Id] = channel;
}
- }
-
- /// <summary>
- /// Handles the channel pins update event.
- /// </summary>
- /// <param name="guildId">The optional guild id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="lastPinTimestamp">The optional last pin timestamp.</param>
- internal async Task OnChannelPinsUpdateAsync(ulong? guildId, ulong channelId, DateTimeOffset? lastPinTimestamp)
- {
- var guild = this.InternalGetCachedGuild(guildId);
- var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
- var ea = new ChannelPinsUpdateEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the channel pins update event.
+ /// </summary>
+ /// <param name="guildId">The optional guild id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="lastPinTimestamp">The optional last pin timestamp.</param>
+ internal async Task OnChannelPinsUpdateAsync(ulong? guildId, ulong channelId, DateTimeOffset? lastPinTimestamp)
{
- Guild = guild,
- Channel = channel,
- LastPinTimestamp = lastPinTimestamp
- };
- await this._channelPinsUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ var guild = this.InternalGetCachedGuild(guildId);
+ var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
- #endregion
+ var ea = new ChannelPinsUpdateEventArgs(this.ServiceProvider)
+ {
+ Guild = guild,
+ Channel = channel,
+ LastPinTimestamp = lastPinTimestamp
+ };
+ await this._channelPinsUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- #region Guild
+ #endregion
- /// <summary>
- /// Handles the guild create event.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <param name="rawMembers">The raw members.</param>
- /// <param name="presences">The presences.</param>
- internal async Task OnGuildCreateEventAsync(DiscordGuild guild, JArray rawMembers, IEnumerable<DiscordPresence> presences)
- {
- if (presences != null)
+ #region Guild
+
+ /// <summary>
+ /// Handles the guild create event.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <param name="rawMembers">The raw members.</param>
+ /// <param name="presences">The presences.</param>
+ internal async Task OnGuildCreateEventAsync(DiscordGuild guild, JArray rawMembers, IEnumerable<DiscordPresence> presences)
{
- foreach (var xp in presences)
+ if (presences != null)
{
- xp.Discord = this;
- xp.GuildId = guild.Id;
- xp.Activity = new DiscordActivity(xp.RawActivity);
- if (xp.RawActivities != null)
+ foreach (var xp in presences)
{
- xp.InternalActivities = new DiscordActivity[xp.RawActivities.Length];
- for (var i = 0; i < xp.RawActivities.Length; i++)
- xp.InternalActivities[i] = new DiscordActivity(xp.RawActivities[i]);
+ xp.Discord = this;
+ xp.GuildId = guild.Id;
+ xp.Activity = new DiscordActivity(xp.RawActivity);
+ if (xp.RawActivities != null)
+ {
+ xp.InternalActivities = new DiscordActivity[xp.RawActivities.Length];
+ for (var i = 0; i < xp.RawActivities.Length; i++)
+ xp.InternalActivities[i] = new DiscordActivity(xp.RawActivities[i]);
+ }
+ this.PresencesInternal[xp.InternalUser.Id] = xp;
}
- this.PresencesInternal[xp.InternalUser.Id] = xp;
- }
- }
-
- var exists = this.GuildsInternal.TryGetValue(guild.Id, out var foundGuild);
-
- guild.Discord = this;
- guild.IsUnavailable = false;
- var eventGuild = guild;
- if (exists)
- guild = foundGuild;
-
- if (guild.ChannelsInternal == null)
- guild.ChannelsInternal = new ConcurrentDictionary<ulong, DiscordChannel>();
- if (guild.ThreadsInternal == null)
- guild.ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
- if (guild.RolesInternal == null)
- guild.RolesInternal = new ConcurrentDictionary<ulong, DiscordRole>();
- if (guild.ThreadsInternal == null)
- guild.ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
- if (guild.StickersInternal == null)
- guild.StickersInternal = new ConcurrentDictionary<ulong, DiscordSticker>();
- if (guild.EmojisInternal == null)
- guild.EmojisInternal = new ConcurrentDictionary<ulong, DiscordEmoji>();
- if (guild.VoiceStatesInternal == null)
- guild.VoiceStatesInternal = new ConcurrentDictionary<ulong, DiscordVoiceState>();
- if (guild.MembersInternal == null)
- guild.MembersInternal = new ConcurrentDictionary<ulong, DiscordMember>();
- if (guild.ScheduledEventsInternal == null)
- guild.ScheduledEventsInternal = new ConcurrentDictionary<ulong, DiscordScheduledEvent>();
-
- this.UpdateCachedGuild(eventGuild, rawMembers);
-
- guild.JoinedAt = eventGuild.JoinedAt;
- guild.IsLarge = eventGuild.IsLarge;
- guild.MemberCount = Math.Max(eventGuild.MemberCount, guild.MembersInternal.Count);
- guild.IsUnavailable = eventGuild.IsUnavailable;
- guild.PremiumSubscriptionCount = eventGuild.PremiumSubscriptionCount;
- guild.PremiumTier = eventGuild.PremiumTier;
- guild.BannerHash = eventGuild.BannerHash;
- guild.VanityUrlCode = eventGuild.VanityUrlCode;
- guild.Description = eventGuild.Description;
- guild.IsNsfw = eventGuild.IsNsfw;
-
- foreach (var kvp in eventGuild.VoiceStatesInternal) guild.VoiceStatesInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in eventGuild.ChannelsInternal) guild.ChannelsInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in eventGuild.RolesInternal) guild.RolesInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in eventGuild.EmojisInternal) guild.EmojisInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in eventGuild.ThreadsInternal) guild.ThreadsInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in eventGuild.StickersInternal) guild.StickersInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in eventGuild.StageInstancesInternal) guild.StageInstancesInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in eventGuild.ScheduledEventsInternal) guild.ScheduledEventsInternal[kvp.Key] = kvp.Value;
-
- foreach (var xc in guild.ChannelsInternal.Values)
- {
- xc.GuildId = guild.Id;
- xc.Discord = this;
- foreach (var xo in xc.PermissionOverwritesInternal)
- {
- xo.Discord = this;
- xo.ChannelId = xc.Id;
}
- }
- foreach (var xt in guild.ThreadsInternal.Values)
- {
- xt.GuildId = guild.Id;
- xt.Discord = this;
- }
- foreach (var xe in guild.EmojisInternal.Values)
- xe.Discord = this;
- foreach (var xs in guild.StickersInternal.Values)
- xs.Discord = this;
- foreach (var xvs in guild.VoiceStatesInternal.Values)
- xvs.Discord = this;
- foreach (var xsi in guild.StageInstancesInternal.Values)
- {
- xsi.Discord = this;
- xsi.GuildId = guild.Id;
- }
- foreach (var xr in guild.RolesInternal.Values)
- {
- xr.Discord = this;
- xr.GuildId = guild.Id;
- }
- foreach (var xse in guild.ScheduledEventsInternal.Values)
- {
- xse.Discord = this;
- xse.GuildId = guild.Id;
- if (xse.Creator != null)
- xse.Creator.Discord = this;
- }
-
- var old = Volatile.Read(ref this._guildDownloadCompleted);
- var dcompl = this.GuildsInternal.Values.All(xg => !xg.IsUnavailable);
- Volatile.Write(ref this._guildDownloadCompleted, dcompl);
- if (exists)
- await this._guildAvailable.InvokeAsync(this, new GuildCreateEventArgs(this.ServiceProvider) { Guild = guild }).ConfigureAwait(false);
- else
- await this._guildCreated.InvokeAsync(this, new GuildCreateEventArgs(this.ServiceProvider) { Guild = guild }).ConfigureAwait(false);
+ var exists = this.GuildsInternal.TryGetValue(guild.Id, out var foundGuild);
- if (dcompl && !old)
- await this._guildDownloadCompletedEv.InvokeAsync(this, new GuildDownloadCompletedEventArgs(this.Guilds, this.ServiceProvider)).ConfigureAwait(false);
- }
+ guild.Discord = this;
+ guild.IsUnavailable = false;
+ var eventGuild = guild;
+ if (exists)
+ guild = foundGuild;
- /// <summary>
- /// Handles the guild update event.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <param name="rawMembers">The raw members.</param>
- internal async Task OnGuildUpdateEventAsync(DiscordGuild guild, JArray rawMembers)
- {
- DiscordGuild oldGuild;
-
- if (!this.GuildsInternal.ContainsKey(guild.Id))
- {
- this.GuildsInternal[guild.Id] = guild;
- oldGuild = null;
- }
- else
- {
- var gld = this.GuildsInternal[guild.Id];
-
- oldGuild = new DiscordGuild
- {
- Discord = gld.Discord,
- Name = gld.Name,
- AfkChannelId = gld.AfkChannelId,
- AfkTimeout = gld.AfkTimeout,
- ApplicationId = gld.ApplicationId,
- DefaultMessageNotifications = gld.DefaultMessageNotifications,
- ExplicitContentFilter = gld.ExplicitContentFilter,
- RawFeatures = gld.RawFeatures,
- IconHash = gld.IconHash,
- Id = gld.Id,
- IsLarge = gld.IsLarge,
- IsSynced = gld.IsSynced,
- IsUnavailable = gld.IsUnavailable,
- JoinedAt = gld.JoinedAt,
- MemberCount = gld.MemberCount,
- MaxMembers = gld.MaxMembers,
- MaxPresences = gld.MaxPresences,
- ApproximateMemberCount = gld.ApproximateMemberCount,
- ApproximatePresenceCount = gld.ApproximatePresenceCount,
- MaxVideoChannelUsers = gld.MaxVideoChannelUsers,
- DiscoverySplashHash = gld.DiscoverySplashHash,
- PreferredLocale = gld.PreferredLocale,
- MfaLevel = gld.MfaLevel,
- OwnerId = gld.OwnerId,
- SplashHash = gld.SplashHash,
- SystemChannelId = gld.SystemChannelId,
- SystemChannelFlags = gld.SystemChannelFlags,
- Description = gld.Description,
- WidgetEnabled = gld.WidgetEnabled,
- WidgetChannelId = gld.WidgetChannelId,
- VerificationLevel = gld.VerificationLevel,
- RulesChannelId = gld.RulesChannelId,
- PublicUpdatesChannelId = gld.PublicUpdatesChannelId,
- VoiceRegionId = gld.VoiceRegionId,
- IsNsfw = gld.IsNsfw,
- PremiumProgressBarEnabled = gld.PremiumProgressBarEnabled,
- PremiumSubscriptionCount = gld.PremiumSubscriptionCount,
- PremiumTier = gld.PremiumTier,
- ChannelsInternal = new ConcurrentDictionary<ulong, DiscordChannel>(),
- ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>(),
- EmojisInternal = new ConcurrentDictionary<ulong, DiscordEmoji>(),
- StickersInternal = new ConcurrentDictionary<ulong, DiscordSticker>(),
- MembersInternal = new ConcurrentDictionary<ulong, DiscordMember>(),
- RolesInternal = new ConcurrentDictionary<ulong, DiscordRole>(),
- StageInstancesInternal = new ConcurrentDictionary<ulong, DiscordStageInstance>(),
- VoiceStatesInternal = new ConcurrentDictionary<ulong, DiscordVoiceState>(),
- ScheduledEventsInternal = new ConcurrentDictionary<ulong, DiscordScheduledEvent>()
- };
+ if (guild.ChannelsInternal == null)
+ guild.ChannelsInternal = new ConcurrentDictionary<ulong, DiscordChannel>();
+ if (guild.ThreadsInternal == null)
+ guild.ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
+ if (guild.RolesInternal == null)
+ guild.RolesInternal = new ConcurrentDictionary<ulong, DiscordRole>();
+ if (guild.ThreadsInternal == null)
+ guild.ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
+ if (guild.StickersInternal == null)
+ guild.StickersInternal = new ConcurrentDictionary<ulong, DiscordSticker>();
+ if (guild.EmojisInternal == null)
+ guild.EmojisInternal = new ConcurrentDictionary<ulong, DiscordEmoji>();
+ if (guild.VoiceStatesInternal == null)
+ guild.VoiceStatesInternal = new ConcurrentDictionary<ulong, DiscordVoiceState>();
+ if (guild.MembersInternal == null)
+ guild.MembersInternal = new ConcurrentDictionary<ulong, DiscordMember>();
+ if (guild.ScheduledEventsInternal == null)
+ guild.ScheduledEventsInternal = new ConcurrentDictionary<ulong, DiscordScheduledEvent>();
- foreach (var kvp in gld.ChannelsInternal) oldGuild.ChannelsInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in gld.ThreadsInternal) oldGuild.ThreadsInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in gld.EmojisInternal) oldGuild.EmojisInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in gld.StickersInternal) oldGuild.StickersInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in gld.RolesInternal) oldGuild.RolesInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in gld.VoiceStatesInternal) oldGuild.VoiceStatesInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in gld.MembersInternal) oldGuild.MembersInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in gld.StageInstancesInternal) oldGuild.StageInstancesInternal[kvp.Key] = kvp.Value;
- foreach (var kvp in gld.ScheduledEventsInternal) oldGuild.ScheduledEventsInternal[kvp.Key] = kvp.Value;
- }
-
- guild.Discord = this;
- guild.IsUnavailable = false;
- var eventGuild = guild;
- guild = this.GuildsInternal[eventGuild.Id];
-
- if (guild.ChannelsInternal == null)
- guild.ChannelsInternal = new ConcurrentDictionary<ulong, DiscordChannel>();
- if (guild.ThreadsInternal == null)
- guild.ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
- if (guild.RolesInternal == null)
- guild.RolesInternal = new ConcurrentDictionary<ulong, DiscordRole>();
- if (guild.EmojisInternal == null)
- guild.EmojisInternal = new ConcurrentDictionary<ulong, DiscordEmoji>();
- if (guild.StickersInternal == null)
- guild.StickersInternal = new ConcurrentDictionary<ulong, DiscordSticker>();
- if (guild.VoiceStatesInternal == null)
- guild.VoiceStatesInternal = new ConcurrentDictionary<ulong, DiscordVoiceState>();
- if (guild.StageInstancesInternal == null)
- guild.StageInstancesInternal = new ConcurrentDictionary<ulong, DiscordStageInstance>();
- if (guild.MembersInternal == null)
- guild.MembersInternal = new ConcurrentDictionary<ulong, DiscordMember>();
- if (guild.ScheduledEventsInternal == null)
- guild.ScheduledEventsInternal = new ConcurrentDictionary<ulong, DiscordScheduledEvent>();
-
- this.UpdateCachedGuild(eventGuild, rawMembers);
-
- foreach (var xc in guild.ChannelsInternal.Values)
- {
- xc.GuildId = guild.Id;
- xc.Discord = this;
- foreach (var xo in xc.PermissionOverwritesInternal)
+ this.UpdateCachedGuild(eventGuild, rawMembers);
+
+ guild.JoinedAt = eventGuild.JoinedAt;
+ guild.IsLarge = eventGuild.IsLarge;
+ guild.MemberCount = Math.Max(eventGuild.MemberCount, guild.MembersInternal.Count);
+ guild.IsUnavailable = eventGuild.IsUnavailable;
+ guild.PremiumSubscriptionCount = eventGuild.PremiumSubscriptionCount;
+ guild.PremiumTier = eventGuild.PremiumTier;
+ guild.BannerHash = eventGuild.BannerHash;
+ guild.VanityUrlCode = eventGuild.VanityUrlCode;
+ guild.Description = eventGuild.Description;
+ guild.IsNsfw = eventGuild.IsNsfw;
+
+ foreach (var kvp in eventGuild.VoiceStatesInternal) guild.VoiceStatesInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in eventGuild.ChannelsInternal) guild.ChannelsInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in eventGuild.RolesInternal) guild.RolesInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in eventGuild.EmojisInternal) guild.EmojisInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in eventGuild.ThreadsInternal) guild.ThreadsInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in eventGuild.StickersInternal) guild.StickersInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in eventGuild.StageInstancesInternal) guild.StageInstancesInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in eventGuild.ScheduledEventsInternal) guild.ScheduledEventsInternal[kvp.Key] = kvp.Value;
+
+ foreach (var xc in guild.ChannelsInternal.Values)
{
- xo.Discord = this;
- xo.ChannelId = xc.Id;
+ xc.GuildId = guild.Id;
+ xc.Discord = this;
+ foreach (var xo in xc.PermissionOverwritesInternal)
+ {
+ xo.Discord = this;
+ xo.ChannelId = xc.Id;
+ }
+ }
+ foreach (var xt in guild.ThreadsInternal.Values)
+ {
+ xt.GuildId = guild.Id;
+ xt.Discord = this;
+ }
+ foreach (var xe in guild.EmojisInternal.Values)
+ xe.Discord = this;
+ foreach (var xs in guild.StickersInternal.Values)
+ xs.Discord = this;
+ foreach (var xvs in guild.VoiceStatesInternal.Values)
+ xvs.Discord = this;
+ foreach (var xsi in guild.StageInstancesInternal.Values)
+ {
+ xsi.Discord = this;
+ xsi.GuildId = guild.Id;
+ }
+ foreach (var xr in guild.RolesInternal.Values)
+ {
+ xr.Discord = this;
+ xr.GuildId = guild.Id;
+ }
+ foreach (var xse in guild.ScheduledEventsInternal.Values)
+ {
+ xse.Discord = this;
+ xse.GuildId = guild.Id;
+ if (xse.Creator != null)
+ xse.Creator.Discord = this;
}
- }
- foreach (var xc in guild.ThreadsInternal.Values)
- {
- xc.GuildId = guild.Id;
- xc.Discord = this;
- }
- foreach (var xe in guild.EmojisInternal.Values)
- xe.Discord = this;
- foreach (var xs in guild.StickersInternal.Values)
- xs.Discord = this;
- foreach (var xvs in guild.VoiceStatesInternal.Values)
- xvs.Discord = this;
- foreach (var xr in guild.RolesInternal.Values)
- {
- xr.Discord = this;
- xr.GuildId = guild.Id;
- }
- foreach (var xsi in guild.StageInstancesInternal.Values)
- {
- xsi.Discord = this;
- xsi.GuildId = guild.Id;
- }
- foreach (var xse in guild.ScheduledEventsInternal.Values)
- {
- xse.Discord = this;
- xse.GuildId = guild.Id;
- if (xse.Creator != null)
- xse.Creator.Discord = this;
- }
-
- await this._guildUpdated.InvokeAsync(this, new GuildUpdateEventArgs(this.ServiceProvider) { GuildBefore = oldGuild, GuildAfter = guild }).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Handles the guild delete event.
- /// </summary>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildDeleteEventAsync(DiscordGuild guild)
- {
- if (guild.IsUnavailable)
- {
- if (!this.GuildsInternal.TryGetValue(guild.Id, out var gld))
- return;
- gld.IsUnavailable = true;
+ var old = Volatile.Read(ref this._guildDownloadCompleted);
+ var dcompl = this.GuildsInternal.Values.All(xg => !xg.IsUnavailable);
+ Volatile.Write(ref this._guildDownloadCompleted, dcompl);
- await this._guildUnavailable.InvokeAsync(this, new GuildDeleteEventArgs(this.ServiceProvider) { Guild = guild, Unavailable = true }).ConfigureAwait(false);
- }
- else
- {
- if (!this.GuildsInternal.TryRemove(guild.Id, out var gld))
- return;
+ if (exists)
+ await this._guildAvailable.InvokeAsync(this, new GuildCreateEventArgs(this.ServiceProvider) { Guild = guild }).ConfigureAwait(false);
+ else
+ await this._guildCreated.InvokeAsync(this, new GuildCreateEventArgs(this.ServiceProvider) { Guild = guild }).ConfigureAwait(false);
- await this._guildDeleted.InvokeAsync(this, new GuildDeleteEventArgs(this.ServiceProvider) { Guild = gld }).ConfigureAwait(false);
+ if (dcompl && !old)
+ await this._guildDownloadCompletedEv.InvokeAsync(this, new GuildDownloadCompletedEventArgs(this.Guilds, this.ServiceProvider)).ConfigureAwait(false);
}
- }
- /// <summary>
- /// Handles the guild sync event.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <param name="isLarge">Whether the guild is a large guild..</param>
- /// <param name="rawMembers">The raw members.</param>
- /// <param name="presences">The presences.</param>
- internal async Task OnGuildSyncEventAsync(DiscordGuild guild, bool isLarge, JArray rawMembers, IEnumerable<DiscordPresence> presences)
- {
- presences = presences.Select(xp => { xp.Discord = this; xp.Activity = new DiscordActivity(xp.RawActivity); return xp; });
- foreach (var xp in presences)
- this.PresencesInternal[xp.InternalUser.Id] = xp;
-
- guild.IsSynced = true;
- guild.IsLarge = isLarge;
+ /// <summary>
+ /// Handles the guild update event.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <param name="rawMembers">The raw members.</param>
+ internal async Task OnGuildUpdateEventAsync(DiscordGuild guild, JArray rawMembers)
+ {
+ DiscordGuild oldGuild;
- this.UpdateCachedGuild(guild, rawMembers);
+ if (!this.GuildsInternal.ContainsKey(guild.Id))
+ {
+ this.GuildsInternal[guild.Id] = guild;
+ oldGuild = null;
+ }
+ else
+ {
+ var gld = this.GuildsInternal[guild.Id];
- await this._guildAvailable.InvokeAsync(this, new GuildCreateEventArgs(this.ServiceProvider) { Guild = guild }).ConfigureAwait(false);
- }
+ oldGuild = new DiscordGuild
+ {
+ Discord = gld.Discord,
+ Name = gld.Name,
+ AfkChannelId = gld.AfkChannelId,
+ AfkTimeout = gld.AfkTimeout,
+ ApplicationId = gld.ApplicationId,
+ DefaultMessageNotifications = gld.DefaultMessageNotifications,
+ ExplicitContentFilter = gld.ExplicitContentFilter,
+ RawFeatures = gld.RawFeatures,
+ IconHash = gld.IconHash,
+ Id = gld.Id,
+ IsLarge = gld.IsLarge,
+ IsSynced = gld.IsSynced,
+ IsUnavailable = gld.IsUnavailable,
+ JoinedAt = gld.JoinedAt,
+ MemberCount = gld.MemberCount,
+ MaxMembers = gld.MaxMembers,
+ MaxPresences = gld.MaxPresences,
+ ApproximateMemberCount = gld.ApproximateMemberCount,
+ ApproximatePresenceCount = gld.ApproximatePresenceCount,
+ MaxVideoChannelUsers = gld.MaxVideoChannelUsers,
+ DiscoverySplashHash = gld.DiscoverySplashHash,
+ PreferredLocale = gld.PreferredLocale,
+ MfaLevel = gld.MfaLevel,
+ OwnerId = gld.OwnerId,
+ SplashHash = gld.SplashHash,
+ SystemChannelId = gld.SystemChannelId,
+ SystemChannelFlags = gld.SystemChannelFlags,
+ Description = gld.Description,
+ WidgetEnabled = gld.WidgetEnabled,
+ WidgetChannelId = gld.WidgetChannelId,
+ VerificationLevel = gld.VerificationLevel,
+ RulesChannelId = gld.RulesChannelId,
+ PublicUpdatesChannelId = gld.PublicUpdatesChannelId,
+ VoiceRegionId = gld.VoiceRegionId,
+ IsNsfw = gld.IsNsfw,
+ PremiumProgressBarEnabled = gld.PremiumProgressBarEnabled,
+ PremiumSubscriptionCount = gld.PremiumSubscriptionCount,
+ PremiumTier = gld.PremiumTier,
+ ChannelsInternal = new ConcurrentDictionary<ulong, DiscordChannel>(),
+ ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>(),
+ EmojisInternal = new ConcurrentDictionary<ulong, DiscordEmoji>(),
+ StickersInternal = new ConcurrentDictionary<ulong, DiscordSticker>(),
+ MembersInternal = new ConcurrentDictionary<ulong, DiscordMember>(),
+ RolesInternal = new ConcurrentDictionary<ulong, DiscordRole>(),
+ StageInstancesInternal = new ConcurrentDictionary<ulong, DiscordStageInstance>(),
+ VoiceStatesInternal = new ConcurrentDictionary<ulong, DiscordVoiceState>(),
+ ScheduledEventsInternal = new ConcurrentDictionary<ulong, DiscordScheduledEvent>()
+ };
- /// <summary>
- /// Handles the guild emojis update event.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <param name="newEmojis">The new emojis.</param>
- internal async Task OnGuildEmojisUpdateEventAsync(DiscordGuild guild, IEnumerable<DiscordEmoji> newEmojis)
- {
- var oldEmojis = new ConcurrentDictionary<ulong, DiscordEmoji>(guild.EmojisInternal);
- guild.EmojisInternal.Clear();
+ foreach (var kvp in gld.ChannelsInternal) oldGuild.ChannelsInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in gld.ThreadsInternal) oldGuild.ThreadsInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in gld.EmojisInternal) oldGuild.EmojisInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in gld.StickersInternal) oldGuild.StickersInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in gld.RolesInternal) oldGuild.RolesInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in gld.VoiceStatesInternal) oldGuild.VoiceStatesInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in gld.MembersInternal) oldGuild.MembersInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in gld.StageInstancesInternal) oldGuild.StageInstancesInternal[kvp.Key] = kvp.Value;
+ foreach (var kvp in gld.ScheduledEventsInternal) oldGuild.ScheduledEventsInternal[kvp.Key] = kvp.Value;
+ }
- foreach (var emoji in newEmojis)
- {
- emoji.Discord = this;
- guild.EmojisInternal[emoji.Id] = emoji;
- }
+ guild.Discord = this;
+ guild.IsUnavailable = false;
+ var eventGuild = guild;
+ guild = this.GuildsInternal[eventGuild.Id];
- var ea = new GuildEmojisUpdateEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- EmojisAfter = guild.Emojis,
- EmojisBefore = new ReadOnlyConcurrentDictionary<ulong, DiscordEmoji>(oldEmojis)
- };
- await this._guildEmojisUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ if (guild.ChannelsInternal == null)
+ guild.ChannelsInternal = new ConcurrentDictionary<ulong, DiscordChannel>();
+ if (guild.ThreadsInternal == null)
+ guild.ThreadsInternal = new ConcurrentDictionary<ulong, DiscordThreadChannel>();
+ if (guild.RolesInternal == null)
+ guild.RolesInternal = new ConcurrentDictionary<ulong, DiscordRole>();
+ if (guild.EmojisInternal == null)
+ guild.EmojisInternal = new ConcurrentDictionary<ulong, DiscordEmoji>();
+ if (guild.StickersInternal == null)
+ guild.StickersInternal = new ConcurrentDictionary<ulong, DiscordSticker>();
+ if (guild.VoiceStatesInternal == null)
+ guild.VoiceStatesInternal = new ConcurrentDictionary<ulong, DiscordVoiceState>();
+ if (guild.StageInstancesInternal == null)
+ guild.StageInstancesInternal = new ConcurrentDictionary<ulong, DiscordStageInstance>();
+ if (guild.MembersInternal == null)
+ guild.MembersInternal = new ConcurrentDictionary<ulong, DiscordMember>();
+ if (guild.ScheduledEventsInternal == null)
+ guild.ScheduledEventsInternal = new ConcurrentDictionary<ulong, DiscordScheduledEvent>();
- /// <summary>
- /// Handles the stickers updated.
- /// </summary>
- /// <param name="newStickers">The new stickers.</param>
- /// <param name="guildId">The guild id.</param>
- internal async Task OnStickersUpdatedAsync(IEnumerable<DiscordSticker> newStickers, ulong guildId)
- {
- var guild = this.InternalGetCachedGuild(guildId);
- var oldStickers = new ConcurrentDictionary<ulong, DiscordSticker>(guild.StickersInternal);
- guild.StickersInternal.Clear();
+ this.UpdateCachedGuild(eventGuild, rawMembers);
- foreach (var nst in newStickers)
- {
- if (nst.User is not null)
+ foreach (var xc in guild.ChannelsInternal.Values)
+ {
+ xc.GuildId = guild.Id;
+ xc.Discord = this;
+ foreach (var xo in xc.PermissionOverwritesInternal)
+ {
+ xo.Discord = this;
+ xo.ChannelId = xc.Id;
+ }
+ }
+ foreach (var xc in guild.ThreadsInternal.Values)
+ {
+ xc.GuildId = guild.Id;
+ xc.Discord = this;
+ }
+ foreach (var xe in guild.EmojisInternal.Values)
+ xe.Discord = this;
+ foreach (var xs in guild.StickersInternal.Values)
+ xs.Discord = this;
+ foreach (var xvs in guild.VoiceStatesInternal.Values)
+ xvs.Discord = this;
+ foreach (var xr in guild.RolesInternal.Values)
{
- nst.User.Discord = this;
- this.UserCache.AddOrUpdate(nst.User.Id, nst.User, (old, @new) => @new);
+ xr.Discord = this;
+ xr.GuildId = guild.Id;
+ }
+ foreach (var xsi in guild.StageInstancesInternal.Values)
+ {
+ xsi.Discord = this;
+ xsi.GuildId = guild.Id;
+ }
+ foreach (var xse in guild.ScheduledEventsInternal.Values)
+ {
+ xse.Discord = this;
+ xse.GuildId = guild.Id;
+ if (xse.Creator != null)
+ xse.Creator.Discord = this;
}
- nst.Discord = this;
- guild.StickersInternal[nst.Id] = nst;
+ await this._guildUpdated.InvokeAsync(this, new GuildUpdateEventArgs(this.ServiceProvider) { GuildBefore = oldGuild, GuildAfter = guild }).ConfigureAwait(false);
}
- var sea = new GuildStickersUpdateEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the guild delete event.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildDeleteEventAsync(DiscordGuild guild)
{
- Guild = guild,
- StickersBefore = oldStickers,
- StickersAfter = guild.Stickers
- };
+ if (guild.IsUnavailable)
+ {
+ if (!this.GuildsInternal.TryGetValue(guild.Id, out var gld))
+ return;
- await this._guildStickersUpdated.InvokeAsync(this, sea).ConfigureAwait(false);
- }
+ gld.IsUnavailable = true;
- #endregion
+ await this._guildUnavailable.InvokeAsync(this, new GuildDeleteEventArgs(this.ServiceProvider) { Guild = guild, Unavailable = true }).ConfigureAwait(false);
+ }
+ else
+ {
+ if (!this.GuildsInternal.TryRemove(guild.Id, out var gld))
+ return;
- #region Guild Ban
+ await this._guildDeleted.InvokeAsync(this, new GuildDeleteEventArgs(this.ServiceProvider) { Guild = gld }).ConfigureAwait(false);
+ }
+ }
- /// <summary>
- /// Handles the guild ban add event.
- /// </summary>
- /// <param name="user">The transport user.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildBanAddEventAsync(TransportUser user, DiscordGuild guild)
- {
- var usr = new DiscordUser(user) { Discord = this };
- usr = this.UserCache.AddOrUpdate(user.Id, usr, (id, old) =>
- {
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
-
- if (!guild.Members.TryGetValue(user.Id, out var mbr))
- mbr = new DiscordMember(usr) { Discord = this, GuildId = guild.Id };
- var ea = new GuildBanAddEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- Member = mbr
- };
- await this._guildBanAdded.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Handles the guild sync event.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <param name="isLarge">Whether the guild is a large guild..</param>
+ /// <param name="rawMembers">The raw members.</param>
+ /// <param name="presences">The presences.</param>
+ internal async Task OnGuildSyncEventAsync(DiscordGuild guild, bool isLarge, JArray rawMembers, IEnumerable<DiscordPresence> presences)
+ {
+ presences = presences.Select(xp => { xp.Discord = this; xp.Activity = new DiscordActivity(xp.RawActivity); return xp; });
+ foreach (var xp in presences)
+ this.PresencesInternal[xp.InternalUser.Id] = xp;
- /// <summary>
- /// Handles the guild ban remove event.
- /// </summary>
- /// <param name="user">The transport user.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildBanRemoveEventAsync(TransportUser user, DiscordGuild guild)
- {
- var usr = new DiscordUser(user) { Discord = this };
- usr = this.UserCache.AddOrUpdate(user.Id, usr, (id, old) =>
- {
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
-
- if (!guild.Members.TryGetValue(user.Id, out var mbr))
- mbr = new DiscordMember(usr) { Discord = this, GuildId = guild.Id };
- var ea = new GuildBanRemoveEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- Member = mbr
- };
- await this._guildBanRemoved.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ guild.IsSynced = true;
+ guild.IsLarge = isLarge;
- #endregion
+ this.UpdateCachedGuild(guild, rawMembers);
- #region Guild Scheduled Event
+ await this._guildAvailable.InvokeAsync(this, new GuildCreateEventArgs(this.ServiceProvider) { Guild = guild }).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the scheduled event create event.
- /// </summary>
- /// <param name="scheduledEvent">The created event.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildScheduledEventCreateEventAsync(DiscordScheduledEvent scheduledEvent, DiscordGuild guild)
- {
- scheduledEvent.Discord = this;
+ /// <summary>
+ /// Handles the guild emojis update event.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <param name="newEmojis">The new emojis.</param>
+ internal async Task OnGuildEmojisUpdateEventAsync(DiscordGuild guild, IEnumerable<DiscordEmoji> newEmojis)
+ {
+ var oldEmojis = new ConcurrentDictionary<ulong, DiscordEmoji>(guild.EmojisInternal);
+ guild.EmojisInternal.Clear();
- guild.ScheduledEventsInternal.AddOrUpdate(scheduledEvent.Id, scheduledEvent, (old, newScheduledEvent) => newScheduledEvent);
+ foreach (var emoji in newEmojis)
+ {
+ emoji.Discord = this;
+ guild.EmojisInternal[emoji.Id] = emoji;
+ }
- if (scheduledEvent.Creator != null)
- {
- scheduledEvent.Creator.Discord = this;
- this.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ var ea = new GuildEmojisUpdateEventArgs(this.ServiceProvider)
{
- old.Username = scheduledEvent.Creator.Username;
- old.Discriminator = scheduledEvent.Creator.Discriminator;
- old.AvatarHash = scheduledEvent.Creator.AvatarHash;
- old.Flags = scheduledEvent.Creator.Flags;
- return old;
- });
+ Guild = guild,
+ EmojisAfter = guild.Emojis,
+ EmojisBefore = new ReadOnlyConcurrentDictionary<ulong, DiscordEmoji>(oldEmojis)
+ };
+ await this._guildEmojisUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- await this._guildScheduledEventCreated.InvokeAsync(this, new GuildScheduledEventCreateEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = scheduledEvent.Guild }).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Handles the stickers updated.
+ /// </summary>
+ /// <param name="newStickers">The new stickers.</param>
+ /// <param name="guildId">The guild id.</param>
+ internal async Task OnStickersUpdatedAsync(IEnumerable<DiscordSticker> newStickers, ulong guildId)
+ {
+ var guild = this.InternalGetCachedGuild(guildId);
+ var oldStickers = new ConcurrentDictionary<ulong, DiscordSticker>(guild.StickersInternal);
+ guild.StickersInternal.Clear();
- /// <summary>
- /// Handles the scheduled event update event.
- /// </summary>
- /// <param name="scheduledEvent">The updated event.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildScheduledEventUpdateEventAsync(DiscordScheduledEvent scheduledEvent, DiscordGuild guild)
- {
- if (guild == null)
- return;
+ foreach (var nst in newStickers)
+ {
+ if (nst.User is not null)
+ {
+ nst.User.Discord = this;
+ this.UserCache.AddOrUpdate(nst.User.Id, nst.User, (old, @new) => @new);
+ }
+ nst.Discord = this;
- DiscordScheduledEvent oldEvent;
- if (!guild.ScheduledEventsInternal.ContainsKey(scheduledEvent.Id))
- {
- oldEvent = null;
- }
- else
- {
- var ev = guild.ScheduledEventsInternal[scheduledEvent.Id];
- oldEvent = new DiscordScheduledEvent
+ guild.StickersInternal[nst.Id] = nst;
+ }
+
+ var sea = new GuildStickersUpdateEventArgs(this.ServiceProvider)
{
- Id = ev.Id,
- ChannelId = ev.ChannelId,
- EntityId = ev.EntityId,
- EntityMetadata = ev.EntityMetadata,
- CreatorId = ev.CreatorId,
- Creator = ev.Creator,
- Discord = this,
- Description = ev.Description,
- EntityType = ev.EntityType,
- ScheduledStartTimeRaw = ev.ScheduledStartTimeRaw,
- ScheduledEndTimeRaw = ev.ScheduledEndTimeRaw,
- GuildId = ev.GuildId,
- Status = ev.Status,
- Name = ev.Name,
- UserCount = ev.UserCount,
- CoverImageHash = ev.CoverImageHash
+ Guild = guild,
+ StickersBefore = oldStickers,
+ StickersAfter = guild.Stickers
};
+ await this._guildStickersUpdated.InvokeAsync(this, sea).ConfigureAwait(false);
}
- if (scheduledEvent.Creator != null)
+
+ #endregion
+
+ #region Guild Ban
+
+ /// <summary>
+ /// Handles the guild ban add event.
+ /// </summary>
+ /// <param name="user">The transport user.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildBanAddEventAsync(TransportUser user, DiscordGuild guild)
{
- scheduledEvent.Creator.Discord = this;
- this.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ var usr = new DiscordUser(user) { Discord = this };
+ usr = this.UserCache.AddOrUpdate(user.Id, usr, (id, old) =>
{
- old.Username = scheduledEvent.Creator.Username;
- old.Discriminator = scheduledEvent.Creator.Discriminator;
- old.AvatarHash = scheduledEvent.Creator.AvatarHash;
- old.Flags = scheduledEvent.Creator.Flags;
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
return old;
});
- }
- if (scheduledEvent.Status == ScheduledEventStatus.Completed)
- {
- guild.ScheduledEventsInternal.TryRemove(scheduledEvent.Id, out var deletedEvent);
- await this._guildScheduledEventDeleted.InvokeAsync(this, new GuildScheduledEventDeleteEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = guild, Reason = ScheduledEventStatus.Completed }).ConfigureAwait(false);
- }
- else if (scheduledEvent.Status == ScheduledEventStatus.Canceled)
- {
- guild.ScheduledEventsInternal.TryRemove(scheduledEvent.Id, out var deletedEvent);
- scheduledEvent.Status = ScheduledEventStatus.Canceled;
- await this._guildScheduledEventDeleted.InvokeAsync(this, new GuildScheduledEventDeleteEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = guild, Reason = ScheduledEventStatus.Canceled }).ConfigureAwait(false);
- }
- else
- {
- this.UpdateScheduledEvent(scheduledEvent, guild);
- await this._guildScheduledEventUpdated.InvokeAsync(this, new GuildScheduledEventUpdateEventArgs(this.ServiceProvider) { ScheduledEventBefore = oldEvent, ScheduledEventAfter = scheduledEvent, Guild = guild }).ConfigureAwait(false);
+ if (!guild.Members.TryGetValue(user.Id, out var mbr))
+ mbr = new DiscordMember(usr) { Discord = this, GuildId = guild.Id };
+ var ea = new GuildBanAddEventArgs(this.ServiceProvider)
+ {
+ Guild = guild,
+ Member = mbr
+ };
+ await this._guildBanAdded.InvokeAsync(this, ea).ConfigureAwait(false);
}
- }
-
- /// <summary>
- /// Handles the scheduled event delete event.
- /// </summary>
- /// <param name="scheduledEvent">The deleted event.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildScheduledEventDeleteEventAsync(DiscordScheduledEvent scheduledEvent, DiscordGuild guild)
- {
- scheduledEvent.Discord = this;
- if (scheduledEvent.Status == ScheduledEventStatus.Scheduled)
- scheduledEvent.Status = ScheduledEventStatus.Canceled;
-
- if (scheduledEvent.Creator != null)
+ /// <summary>
+ /// Handles the guild ban remove event.
+ /// </summary>
+ /// <param name="user">The transport user.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildBanRemoveEventAsync(TransportUser user, DiscordGuild guild)
{
- scheduledEvent.Creator.Discord = this;
- this.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ var usr = new DiscordUser(user) { Discord = this };
+ usr = this.UserCache.AddOrUpdate(user.Id, usr, (id, old) =>
{
- old.Username = scheduledEvent.Creator.Username;
- old.Discriminator = scheduledEvent.Creator.Discriminator;
- old.AvatarHash = scheduledEvent.Creator.AvatarHash;
- old.Flags = scheduledEvent.Creator.Flags;
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
return old;
});
- }
-
- await this._guildScheduledEventDeleted.InvokeAsync(this, new GuildScheduledEventDeleteEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = scheduledEvent.Guild, Reason = scheduledEvent.Status }).ConfigureAwait(false);
- guild.ScheduledEventsInternal.TryRemove(scheduledEvent.Id, out var deletedEvent);
- }
- /// <summary>
- /// Handles the scheduled event user add event.
- /// <param name="guildScheduledEventId">The event.</param>
- /// <param name="userId">The added user id.</param>
- /// <param name="guild">The guild.</param>
- /// </summary>
- internal async Task OnGuildScheduledEventUserAddedEventAsync(ulong guildScheduledEventId, ulong userId, DiscordGuild guild)
- {
- var scheduledEvent = this.InternalGetCachedScheduledEvent(guildScheduledEventId) ?? this.UpdateScheduledEvent(new DiscordScheduledEvent
- {
- Id = guildScheduledEventId,
- GuildId = guild.Id,
- Discord = this,
- UserCount = 0
- }, guild);
+ if (!guild.Members.TryGetValue(user.Id, out var mbr))
+ mbr = new DiscordMember(usr) { Discord = this, GuildId = guild.Id };
+ var ea = new GuildBanRemoveEventArgs(this.ServiceProvider)
+ {
+ Guild = guild,
+ Member = mbr
+ };
+ await this._guildBanRemoved.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- scheduledEvent.UserCount++;
- scheduledEvent.Discord = this;
- guild.Discord = this;
+ #endregion
- var user = this.GetUserAsync(userId, true).Result;
- user.Discord = this;
- var member = guild.Members.TryGetValue(userId, out var mem) ? mem : guild.GetMemberAsync(userId).Result;
- member.Discord = this;
+ #region Guild Scheduled Event
- await this._guildScheduledEventUserAdded.InvokeAsync(this, new GuildScheduledEventUserAddEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = guild, User = user, Member = member }).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Handles the scheduled event user remove event.
- /// <param name="guildScheduledEventId">The event.</param>
- /// <param name="userId">The removed user id.</param>
- /// <param name="guild">The guild.</param>
- /// </summary>
- internal async Task OnGuildScheduledEventUserRemovedEventAsync(ulong guildScheduledEventId, ulong userId, DiscordGuild guild)
- {
- var scheduledEvent = this.InternalGetCachedScheduledEvent(guildScheduledEventId) ?? this.UpdateScheduledEvent(new DiscordScheduledEvent
+ /// <summary>
+ /// Handles the scheduled event create event.
+ /// </summary>
+ /// <param name="scheduledEvent">The created event.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildScheduledEventCreateEventAsync(DiscordScheduledEvent scheduledEvent, DiscordGuild guild)
{
- Id = guildScheduledEventId,
- GuildId = guild.Id,
- Discord = this,
- UserCount = 0
- }, guild);
-
- scheduledEvent.UserCount = scheduledEvent.UserCount == 0 ? 0 : scheduledEvent.UserCount - 1;
- scheduledEvent.Discord = this;
- guild.Discord = this;
-
- var user = this.GetUserAsync(userId, true).Result;
- user.Discord = this;
- var member = guild.Members.TryGetValue(userId, out var mem) ? mem : guild.GetMemberAsync(userId).Result;
- member.Discord = this;
-
- await this._guildScheduledEventUserRemoved.InvokeAsync(this, new GuildScheduledEventUserRemoveEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = guild, User = user, Member = member }).ConfigureAwait(false);
- }
-
- #endregion
+ scheduledEvent.Discord = this;
- #region Guild Integration
-
- /// <summary>
- /// Handles the guild integration create event.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <param name="integration">The integration.</param>
- internal async Task OnGuildIntegrationCreateEventAsync(DiscordGuild guild, DiscordIntegration integration)
- {
- integration.Discord = this;
-
- await this._guildIntegrationCreated.InvokeAsync(this, new GuildIntegrationCreateEventArgs(this.ServiceProvider) { Integration = integration, Guild = guild }).ConfigureAwait(false);
- }
+ guild.ScheduledEventsInternal.AddOrUpdate(scheduledEvent.Id, scheduledEvent, (old, newScheduledEvent) => newScheduledEvent);
- /// <summary>
- /// Handles the guild integration update event.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <param name="integration">The integration.</param>
- internal async Task OnGuildIntegrationUpdateEventAsync(DiscordGuild guild, DiscordIntegration integration)
- {
- integration.Discord = this;
+ if (scheduledEvent.Creator != null)
+ {
+ scheduledEvent.Creator.Discord = this;
+ this.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ {
+ old.Username = scheduledEvent.Creator.Username;
+ old.Discriminator = scheduledEvent.Creator.Discriminator;
+ old.AvatarHash = scheduledEvent.Creator.AvatarHash;
+ old.Flags = scheduledEvent.Creator.Flags;
+ return old;
+ });
+ }
- await this._guildIntegrationUpdated.InvokeAsync(this, new GuildIntegrationUpdateEventArgs(this.ServiceProvider) { Integration = integration, Guild = guild }).ConfigureAwait(false);
- }
+ await this._guildScheduledEventCreated.InvokeAsync(this, new GuildScheduledEventCreateEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = scheduledEvent.Guild }).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the guild integrations update event.
- /// </summary>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildIntegrationsUpdateEventAsync(DiscordGuild guild)
- {
- var ea = new GuildIntegrationsUpdateEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the scheduled event update event.
+ /// </summary>
+ /// <param name="scheduledEvent">The updated event.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildScheduledEventUpdateEventAsync(DiscordScheduledEvent scheduledEvent, DiscordGuild guild)
{
- Guild = guild
- };
- await this._guildIntegrationsUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ if (guild == null)
+ return;
- /// <summary>
- /// Handles the guild integration delete event.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <param name="integrationId">The integration id.</param>
- /// <param name="applicationId">The optional application id.</param>
- internal async Task OnGuildIntegrationDeleteEventAsync(DiscordGuild guild, ulong integrationId, ulong? applicationId)
- => await this._guildIntegrationDeleted.InvokeAsync(this, new GuildIntegrationDeleteEventArgs(this.ServiceProvider) { Guild = guild, IntegrationId = integrationId, ApplicationId = applicationId }).ConfigureAwait(false);
+ DiscordScheduledEvent oldEvent;
+ if (!guild.ScheduledEventsInternal.ContainsKey(scheduledEvent.Id))
+ {
+ oldEvent = null;
+ }
+ else
+ {
+ var ev = guild.ScheduledEventsInternal[scheduledEvent.Id];
+ oldEvent = new DiscordScheduledEvent
+ {
+ Id = ev.Id,
+ ChannelId = ev.ChannelId,
+ EntityId = ev.EntityId,
+ EntityMetadata = ev.EntityMetadata,
+ CreatorId = ev.CreatorId,
+ Creator = ev.Creator,
+ Discord = this,
+ Description = ev.Description,
+ EntityType = ev.EntityType,
+ ScheduledStartTimeRaw = ev.ScheduledStartTimeRaw,
+ ScheduledEndTimeRaw = ev.ScheduledEndTimeRaw,
+ GuildId = ev.GuildId,
+ Status = ev.Status,
+ Name = ev.Name,
+ UserCount = ev.UserCount,
+ CoverImageHash = ev.CoverImageHash
+ };
- #endregion
+ }
+ if (scheduledEvent.Creator != null)
+ {
+ scheduledEvent.Creator.Discord = this;
+ this.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ {
+ old.Username = scheduledEvent.Creator.Username;
+ old.Discriminator = scheduledEvent.Creator.Discriminator;
+ old.AvatarHash = scheduledEvent.Creator.AvatarHash;
+ old.Flags = scheduledEvent.Creator.Flags;
+ return old;
+ });
+ }
- #region Guild Member
+ if (scheduledEvent.Status == ScheduledEventStatus.Completed)
+ {
+ guild.ScheduledEventsInternal.TryRemove(scheduledEvent.Id, out var deletedEvent);
+ await this._guildScheduledEventDeleted.InvokeAsync(this, new GuildScheduledEventDeleteEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = guild, Reason = ScheduledEventStatus.Completed }).ConfigureAwait(false);
+ }
+ else if (scheduledEvent.Status == ScheduledEventStatus.Canceled)
+ {
+ guild.ScheduledEventsInternal.TryRemove(scheduledEvent.Id, out var deletedEvent);
+ scheduledEvent.Status = ScheduledEventStatus.Canceled;
+ await this._guildScheduledEventDeleted.InvokeAsync(this, new GuildScheduledEventDeleteEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = guild, Reason = ScheduledEventStatus.Canceled }).ConfigureAwait(false);
+ }
+ else
+ {
+ this.UpdateScheduledEvent(scheduledEvent, guild);
+ await this._guildScheduledEventUpdated.InvokeAsync(this, new GuildScheduledEventUpdateEventArgs(this.ServiceProvider) { ScheduledEventBefore = oldEvent, ScheduledEventAfter = scheduledEvent, Guild = guild }).ConfigureAwait(false);
+ }
+ }
- /// <summary>
- /// Handles the guild member add event.
- /// </summary>
- /// <param name="member">The transport member.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildMemberAddEventAsync(TransportMember member, DiscordGuild guild)
- {
- var usr = new DiscordUser(member.User) { Discord = this };
- usr = this.UserCache.AddOrUpdate(member.User.Id, usr, (id, old) =>
+ /// <summary>
+ /// Handles the scheduled event delete event.
+ /// </summary>
+ /// <param name="scheduledEvent">The deleted event.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildScheduledEventDeleteEventAsync(DiscordScheduledEvent scheduledEvent, DiscordGuild guild)
{
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
+ scheduledEvent.Discord = this;
- var mbr = new DiscordMember(member)
- {
- Discord = this,
- GuildId = guild.Id
- };
+ if (scheduledEvent.Status == ScheduledEventStatus.Scheduled)
+ scheduledEvent.Status = ScheduledEventStatus.Canceled;
- guild.MembersInternal[mbr.Id] = mbr;
- guild.MemberCount++;
+ if (scheduledEvent.Creator != null)
+ {
+ scheduledEvent.Creator.Discord = this;
+ this.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ {
+ old.Username = scheduledEvent.Creator.Username;
+ old.Discriminator = scheduledEvent.Creator.Discriminator;
+ old.AvatarHash = scheduledEvent.Creator.AvatarHash;
+ old.Flags = scheduledEvent.Creator.Flags;
+ return old;
+ });
+ }
- var ea = new GuildMemberAddEventArgs(this.ServiceProvider)
+ await this._guildScheduledEventDeleted.InvokeAsync(this, new GuildScheduledEventDeleteEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = scheduledEvent.Guild, Reason = scheduledEvent.Status }).ConfigureAwait(false);
+ guild.ScheduledEventsInternal.TryRemove(scheduledEvent.Id, out var deletedEvent);
+ }
+
+ /// <summary>
+ /// Handles the scheduled event user add event.
+ /// <param name="guildScheduledEventId">The event.</param>
+ /// <param name="userId">The added user id.</param>
+ /// <param name="guild">The guild.</param>
+ /// </summary>
+ internal async Task OnGuildScheduledEventUserAddedEventAsync(ulong guildScheduledEventId, ulong userId, DiscordGuild guild)
{
- Guild = guild,
- Member = mbr
- };
- await this._guildMemberAdded.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ var scheduledEvent = this.InternalGetCachedScheduledEvent(guildScheduledEventId) ?? this.UpdateScheduledEvent(new DiscordScheduledEvent
+ {
+ Id = guildScheduledEventId,
+ GuildId = guild.Id,
+ Discord = this,
+ UserCount = 0
+ }, guild);
- /// <summary>
- /// Handles the guild member remove event.
- /// </summary>
- /// <param name="user">The transport user.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildMemberRemoveEventAsync(TransportUser user, DiscordGuild guild)
- {
- var usr = new DiscordUser(user);
+ scheduledEvent.UserCount++;
+ scheduledEvent.Discord = this;
+ guild.Discord = this;
- if (!guild.MembersInternal.TryRemove(user.Id, out var mbr))
- mbr = new DiscordMember(usr) { Discord = this, GuildId = guild.Id };
- guild.MemberCount--;
+ var user = this.GetUserAsync(userId, true).Result;
+ user.Discord = this;
+ var member = guild.Members.TryGetValue(userId, out var mem) ? mem : guild.GetMemberAsync(userId).Result;
+ member.Discord = this;
- _ = this.UserCache.AddOrUpdate(user.Id, usr, (old, @new) => @new);
+ await this._guildScheduledEventUserAdded.InvokeAsync(this, new GuildScheduledEventUserAddEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = guild, User = user, Member = member }).ConfigureAwait(false);
+ }
- var ea = new GuildMemberRemoveEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the scheduled event user remove event.
+ /// <param name="guildScheduledEventId">The event.</param>
+ /// <param name="userId">The removed user id.</param>
+ /// <param name="guild">The guild.</param>
+ /// </summary>
+ internal async Task OnGuildScheduledEventUserRemovedEventAsync(ulong guildScheduledEventId, ulong userId, DiscordGuild guild)
{
- Guild = guild,
- Member = mbr
- };
- await this._guildMemberRemoved.InvokeAsync(this, ea).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Handles the guild member update event.
- /// </summary>
- /// <param name="member">The transport member.</param>
- /// <param name="guild">The guild.</param>
- /// <param name="roles">The roles.</param>
- /// <param name="nick">The nick.</param>
- /// <param name="pending">Whether the member is pending.</param>
- internal async Task OnGuildMemberUpdateEventAsync(TransportMember member, DiscordGuild guild, IEnumerable<ulong> roles, string nick, bool? pending)
- {
- var usr = new DiscordUser(member.User) { Discord = this };
- usr = this.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
- {
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
-
- if (!guild.Members.TryGetValue(member.User.Id, out var mbr))
- mbr = new DiscordMember(usr) { Discord = this, GuildId = guild.Id };
- var old = mbr;
-
- var gAvOld = old.GuildAvatarHash;
- var avOld = old.AvatarHash;
- var nickOld = mbr.Nickname;
- var pendingOld = mbr.IsPending;
- var rolesOld = new ReadOnlyCollection<DiscordRole>(new List<DiscordRole>(mbr.Roles));
- var cduOld = mbr.CommunicationDisabledUntil;
- mbr.MemberFlags = member.MemberFlags;
- mbr.AvatarHashInternal = member.AvatarHash;
- mbr.GuildAvatarHash = member.GuildAvatarHash;
- mbr.Nickname = nick;
- mbr.IsPending = pending;
- mbr.CommunicationDisabledUntil = member.CommunicationDisabledUntil;
- mbr.RoleIdsInternal.Clear();
- mbr.RoleIdsInternal.AddRange(roles);
- guild.MembersInternal.AddOrUpdate(member.User.Id, mbr, (id, oldMbr) => oldMbr);
-
- var timeoutUntil = member.CommunicationDisabledUntil;
- /*this.Logger.LogTrace($"Timeout:\nBefore - {cduOld}\nAfter - {timeoutUntil}");
- if ((timeoutUntil.HasValue && cduOld.HasValue) || (timeoutUntil == null && cduOld.HasValue) || (timeoutUntil.HasValue && cduOld == null))
- {
- // We are going to add a scheduled timer to assure that we get a auditlog entry.
-
- var id = $"tt-{mbr.Id}-{guild.Id}-{DateTime.Now.ToLongTimeString()}";
-
- this._tempTimers.Add(
- id,
- new(
- new TimeoutHandler(
- mbr,
- guild,
- cduOld,
- timeoutUntil
- ),
- new Timer(
- this.TimeoutTimer,
- id,
- 2000,
- Timeout.Infinite
- )
- )
- );
+ var scheduledEvent = this.InternalGetCachedScheduledEvent(guildScheduledEventId) ?? this.UpdateScheduledEvent(new DiscordScheduledEvent
+ {
+ Id = guildScheduledEventId,
+ GuildId = guild.Id,
+ Discord = this,
+ UserCount = 0
+ }, guild);
- this.Logger.LogTrace("Scheduling timeout event.");
+ scheduledEvent.UserCount = scheduledEvent.UserCount == 0 ? 0 : scheduledEvent.UserCount - 1;
+ scheduledEvent.Discord = this;
+ guild.Discord = this;
- return;
- }*/
+ var user = this.GetUserAsync(userId, true).Result;
+ user.Discord = this;
+ var member = guild.Members.TryGetValue(userId, out var mem) ? mem : guild.GetMemberAsync(userId).Result;
+ member.Discord = this;
- //this.Logger.LogTrace("No timeout detected. Continuing on normal operation.");
+ await this._guildScheduledEventUserRemoved.InvokeAsync(this, new GuildScheduledEventUserRemoveEventArgs(this.ServiceProvider) { ScheduledEvent = scheduledEvent, Guild = guild, User = user, Member = member }).ConfigureAwait(false);
+ }
- var eargs = new GuildMemberUpdateEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- Member = mbr,
+ #endregion
- NicknameAfter = mbr.Nickname,
- RolesAfter = new ReadOnlyCollection<DiscordRole>(new List<DiscordRole>(mbr.Roles)),
- PendingAfter = mbr.IsPending,
- TimeoutAfter = mbr.CommunicationDisabledUntil,
- AvatarHashAfter = mbr.AvatarHash,
- GuildAvatarHashAfter = mbr.GuildAvatarHash,
+ #region Guild Integration
- NicknameBefore = nickOld,
- RolesBefore = rolesOld,
- PendingBefore = pendingOld,
- TimeoutBefore = cduOld,
- AvatarHashBefore = avOld,
- GuildAvatarHashBefore = gAvOld
- };
- await this._guildMemberUpdated.InvokeAsync(this, eargs).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Handles the guild integration create event.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <param name="integration">The integration.</param>
+ internal async Task OnGuildIntegrationCreateEventAsync(DiscordGuild guild, DiscordIntegration integration)
+ {
+ integration.Discord = this;
- /// <summary>
- /// Handles timeout events.
- /// </summary>
- /// <param name="state">Internally used as uid for the timer data.</param>
- private async void TimeoutTimer(object state)
- {
- var tid = (string)state;
- var data = this._tempTimers.First(x=> x.Key == tid).Value.Key;
- var timer = this._tempTimers.First(x=> x.Key == tid).Value.Value;
+ await this._guildIntegrationCreated.InvokeAsync(this, new GuildIntegrationCreateEventArgs(this.ServiceProvider) { Integration = integration, Guild = guild }).ConfigureAwait(false);
+ }
- IReadOnlyList<DiscordAuditLogEntry> auditlog = null;
- DiscordAuditLogMemberUpdateEntry filtered = null;
- try
+ /// <summary>
+ /// Handles the guild integration update event.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <param name="integration">The integration.</param>
+ internal async Task OnGuildIntegrationUpdateEventAsync(DiscordGuild guild, DiscordIntegration integration)
{
- auditlog = await data.Guild.GetAuditLogsAsync(10, null, AuditLogActionType.MemberUpdate);
- var preFiltered = auditlog.Select(x => x as DiscordAuditLogMemberUpdateEntry).Where(x => x.Target.Id == data.Member.Id);
- filtered = preFiltered.First();
+ integration.Discord = this;
+
+ await this._guildIntegrationUpdated.InvokeAsync(this, new GuildIntegrationUpdateEventArgs(this.ServiceProvider) { Integration = integration, Guild = guild }).ConfigureAwait(false);
}
- catch (UnauthorizedException) { }
- catch (Exception)
+
+ /// <summary>
+ /// Handles the guild integrations update event.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildIntegrationsUpdateEventAsync(DiscordGuild guild)
{
- this.Logger.LogTrace("Failing timeout event.");
- await timer.DisposeAsync();
- this._tempTimers.Remove(tid);
- return;
+ var ea = new GuildIntegrationsUpdateEventArgs(this.ServiceProvider)
+ {
+ Guild = guild
+ };
+ await this._guildIntegrationsUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- var actor = filtered?.UserResponsible as DiscordMember;
+ /// <summary>
+ /// Handles the guild integration delete event.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <param name="integrationId">The integration id.</param>
+ /// <param name="applicationId">The optional application id.</param>
+ internal async Task OnGuildIntegrationDeleteEventAsync(DiscordGuild guild, ulong integrationId, ulong? applicationId)
+ => await this._guildIntegrationDeleted.InvokeAsync(this, new GuildIntegrationDeleteEventArgs(this.ServiceProvider) { Guild = guild, IntegrationId = integrationId, ApplicationId = applicationId }).ConfigureAwait(false);
+
+ #endregion
- this.Logger.LogTrace("Trying to execute timeout event.");
+ #region Guild Member
- if (data.TimeoutUntilOld.HasValue && data.TimeoutUntilNew.HasValue)
+ /// <summary>
+ /// Handles the guild member add event.
+ /// </summary>
+ /// <param name="member">The transport member.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildMemberAddEventAsync(TransportMember member, DiscordGuild guild)
{
- // A timeout was updated.
+ var usr = new DiscordUser(member.User) { Discord = this };
+ usr = this.UserCache.AddOrUpdate(member.User.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ return old;
+ });
- if (filtered != null && auditlog == null)
+ var mbr = new DiscordMember(member)
{
- this.Logger.LogTrace("Re-scheduling timeout event.");
- timer.Change(2000, Timeout.Infinite);
- return;
- }
+ Discord = this,
+ GuildId = guild.Id
+ };
- var ea = new GuildMemberTimeoutUpdateEventArgs(this.ServiceProvider)
+ guild.MembersInternal[mbr.Id] = mbr;
+ guild.MemberCount++;
+
+ var ea = new GuildMemberAddEventArgs(this.ServiceProvider)
{
- Guild = data.Guild,
- Target = data.Member,
- TimeoutBefore = data.TimeoutUntilOld.Value,
- TimeoutAfter = data.TimeoutUntilNew.Value,
- Actor = actor,
- AuditLogId = filtered?.Id,
- AuditLogReason = filtered?.Reason
+ Guild = guild,
+ Member = mbr
};
- await this._guildMemberTimeoutChanged.InvokeAsync(this, ea).ConfigureAwait(false);
+ await this._guildMemberAdded.InvokeAsync(this, ea).ConfigureAwait(false);
}
- else if (!data.TimeoutUntilOld.HasValue && data.TimeoutUntilNew.HasValue)
+
+ /// <summary>
+ /// Handles the guild member remove event.
+ /// </summary>
+ /// <param name="user">The transport user.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildMemberRemoveEventAsync(TransportUser user, DiscordGuild guild)
{
- // A timeout was added.
+ var usr = new DiscordUser(user);
- if (filtered != null && auditlog == null)
- {
- this.Logger.LogTrace("Re-scheduling timeout event.");
- timer.Change(2000, Timeout.Infinite);
- return;
- }
+ if (!guild.MembersInternal.TryRemove(user.Id, out var mbr))
+ mbr = new DiscordMember(usr) { Discord = this, GuildId = guild.Id };
+ guild.MemberCount--;
- var ea = new GuildMemberTimeoutAddEventArgs(this.ServiceProvider)
+ _ = this.UserCache.AddOrUpdate(user.Id, usr, (old, @new) => @new);
+
+ var ea = new GuildMemberRemoveEventArgs(this.ServiceProvider)
{
- Guild = data.Guild,
- Target = data.Member,
- Timeout = data.TimeoutUntilNew.Value,
- Actor = actor,
- AuditLogId = filtered?.Id,
- AuditLogReason = filtered?.Reason
+ Guild = guild,
+ Member = mbr
};
- await this._guildMemberTimeoutAdded.InvokeAsync(this, ea).ConfigureAwait(false);
+ await this._guildMemberRemoved.InvokeAsync(this, ea).ConfigureAwait(false);
}
- else if (data.TimeoutUntilOld.HasValue && !data.TimeoutUntilNew.HasValue)
- {
- // A timeout was removed.
- if (filtered != null && auditlog == null)
+ /// <summary>
+ /// Handles the guild member update event.
+ /// </summary>
+ /// <param name="member">The transport member.</param>
+ /// <param name="guild">The guild.</param>
+ /// <param name="roles">The roles.</param>
+ /// <param name="nick">The nick.</param>
+ /// <param name="pending">Whether the member is pending.</param>
+ internal async Task OnGuildMemberUpdateEventAsync(TransportMember member, DiscordGuild guild, IEnumerable<ulong> roles, string nick, bool? pending)
+ {
+ var usr = new DiscordUser(member.User) { Discord = this };
+ usr = this.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
{
- this.Logger.LogTrace("Re-scheduling timeout event.");
- timer.Change(2000, Timeout.Infinite);
- return;
- }
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ return old;
+ });
- var ea = new GuildMemberTimeoutRemoveEventArgs(this.ServiceProvider)
+ if (!guild.Members.TryGetValue(member.User.Id, out var mbr))
+ mbr = new DiscordMember(usr) { Discord = this, GuildId = guild.Id };
+ var old = mbr;
+
+ var gAvOld = old.GuildAvatarHash;
+ var avOld = old.AvatarHash;
+ var nickOld = mbr.Nickname;
+ var pendingOld = mbr.IsPending;
+ var rolesOld = new ReadOnlyCollection<DiscordRole>(new List<DiscordRole>(mbr.Roles));
+ var cduOld = mbr.CommunicationDisabledUntil;
+ mbr.MemberFlags = member.MemberFlags;
+ mbr.AvatarHashInternal = member.AvatarHash;
+ mbr.GuildAvatarHash = member.GuildAvatarHash;
+ mbr.Nickname = nick;
+ mbr.IsPending = pending;
+ mbr.CommunicationDisabledUntil = member.CommunicationDisabledUntil;
+ mbr.RoleIdsInternal.Clear();
+ mbr.RoleIdsInternal.AddRange(roles);
+ guild.MembersInternal.AddOrUpdate(member.User.Id, mbr, (id, oldMbr) => oldMbr);
+
+ var timeoutUntil = member.CommunicationDisabledUntil;
+ /*this.Logger.LogTrace($"Timeout:\nBefore - {cduOld}\nAfter - {timeoutUntil}");
+ if ((timeoutUntil.HasValue && cduOld.HasValue) || (timeoutUntil == null && cduOld.HasValue) || (timeoutUntil.HasValue && cduOld == null))
{
- Guild = data.Guild,
- Target = data.Member,
- TimeoutBefore = data.TimeoutUntilOld.Value,
- Actor = actor,
- AuditLogId = filtered?.Id,
- AuditLogReason = filtered?.Reason
- };
- await this._guildMemberTimeoutRemoved.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ // We are going to add a scheduled timer to assure that we get a auditlog entry.
+
+ var id = $"tt-{mbr.Id}-{guild.Id}-{DateTime.Now.ToLongTimeString()}";
+
+ this._tempTimers.Add(
+ id,
+ new(
+ new TimeoutHandler(
+ mbr,
+ guild,
+ cduOld,
+ timeoutUntil
+ ),
+ new Timer(
+ this.TimeoutTimer,
+ id,
+ 2000,
+ Timeout.Infinite
+ )
+ )
+ );
- // Ending timer because it worked.
- this.Logger.LogTrace("Removing timeout event.");
- await timer.DisposeAsync();
- this._tempTimers.Remove(tid);
- }
+ this.Logger.LogTrace("Scheduling timeout event.");
- /// <summary>
- /// Handles the guild members chunk event.
- /// </summary>
- /// <param name="dat">The raw chunk data.</param>
- internal async Task OnGuildMembersChunkEventAsync(JObject dat)
- {
- var guild = this.Guilds[(ulong)dat["guild_id"]];
- var chunkIndex = (int)dat["chunk_index"];
- var chunkCount = (int)dat["chunk_count"];
- var nonce = (string)dat["nonce"];
+ return;
+ }*/
- var mbrs = new HashSet<DiscordMember>();
- var pres = new HashSet<DiscordPresence>();
+ //this.Logger.LogTrace("No timeout detected. Continuing on normal operation.");
- var members = dat["members"].ToObject<TransportMember[]>();
+ var eargs = new GuildMemberUpdateEventArgs(this.ServiceProvider)
+ {
+ Guild = guild,
+ Member = mbr,
+
+ NicknameAfter = mbr.Nickname,
+ RolesAfter = new ReadOnlyCollection<DiscordRole>(new List<DiscordRole>(mbr.Roles)),
+ PendingAfter = mbr.IsPending,
+ TimeoutAfter = mbr.CommunicationDisabledUntil,
+ AvatarHashAfter = mbr.AvatarHash,
+ GuildAvatarHashAfter = mbr.GuildAvatarHash,
+
+ NicknameBefore = nickOld,
+ RolesBefore = rolesOld,
+ PendingBefore = pendingOld,
+ TimeoutBefore = cduOld,
+ AvatarHashBefore = avOld,
+ GuildAvatarHashBefore = gAvOld
+ };
+ await this._guildMemberUpdated.InvokeAsync(this, eargs).ConfigureAwait(false);
+ }
- var memCount = members.Length;
- for (var i = 0; i < memCount; i++)
+ /// <summary>
+ /// Handles timeout events.
+ /// </summary>
+ /// <param name="state">Internally used as uid for the timer data.</param>
+ private async void TimeoutTimer(object state)
{
- var mbr = new DiscordMember(members[i]) { Discord = this, GuildId = guild.Id };
+ var tid = (string)state;
+ var data = this._tempTimers.First(x=> x.Key == tid).Value.Key;
+ var timer = this._tempTimers.First(x=> x.Key == tid).Value.Value;
- if (!this.UserCache.ContainsKey(mbr.Id))
- this.UserCache[mbr.Id] = new DiscordUser(members[i].User) { Discord = this };
+ IReadOnlyList<DiscordAuditLogEntry> auditlog = null;
+ DiscordAuditLogMemberUpdateEntry filtered = null;
+ try
+ {
+ auditlog = await data.Guild.GetAuditLogsAsync(10, null, AuditLogActionType.MemberUpdate);
+ var preFiltered = auditlog.Select(x => x as DiscordAuditLogMemberUpdateEntry).Where(x => x.Target.Id == data.Member.Id);
+ filtered = preFiltered.First();
+ }
+ catch (UnauthorizedException) { }
+ catch (Exception)
+ {
+ this.Logger.LogTrace("Failing timeout event.");
+ await timer.DisposeAsync();
+ this._tempTimers.Remove(tid);
+ return;
+ }
- guild.MembersInternal[mbr.Id] = mbr;
+ var actor = filtered?.UserResponsible as DiscordMember;
- mbrs.Add(mbr);
- }
+ this.Logger.LogTrace("Trying to execute timeout event.");
- guild.MemberCount = guild.MembersInternal.Count;
+ if (data.TimeoutUntilOld.HasValue && data.TimeoutUntilNew.HasValue)
+ {
+ // A timeout was updated.
- var ea = new GuildMembersChunkEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- Members = new ReadOnlySet<DiscordMember>(mbrs),
- ChunkIndex = chunkIndex,
- ChunkCount = chunkCount,
- Nonce = nonce,
- };
+ if (filtered != null && auditlog == null)
+ {
+ this.Logger.LogTrace("Re-scheduling timeout event.");
+ timer.Change(2000, Timeout.Infinite);
+ return;
+ }
- if (dat["presences"] != null)
- {
- var presences = dat["presences"].ToObject<DiscordPresence[]>();
+ var ea = new GuildMemberTimeoutUpdateEventArgs(this.ServiceProvider)
+ {
+ Guild = data.Guild,
+ Target = data.Member,
+ TimeoutBefore = data.TimeoutUntilOld.Value,
+ TimeoutAfter = data.TimeoutUntilNew.Value,
+ Actor = actor,
+ AuditLogId = filtered?.Id,
+ AuditLogReason = filtered?.Reason
+ };
+ await this._guildMemberTimeoutChanged.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
+ else if (!data.TimeoutUntilOld.HasValue && data.TimeoutUntilNew.HasValue)
+ {
+ // A timeout was added.
+
+ if (filtered != null && auditlog == null)
+ {
+ this.Logger.LogTrace("Re-scheduling timeout event.");
+ timer.Change(2000, Timeout.Infinite);
+ return;
+ }
- var presCount = presences.Length;
- for (var i = 0; i < presCount; i++)
+ var ea = new GuildMemberTimeoutAddEventArgs(this.ServiceProvider)
+ {
+ Guild = data.Guild,
+ Target = data.Member,
+ Timeout = data.TimeoutUntilNew.Value,
+ Actor = actor,
+ AuditLogId = filtered?.Id,
+ AuditLogReason = filtered?.Reason
+ };
+ await this._guildMemberTimeoutAdded.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
+ else if (data.TimeoutUntilOld.HasValue && !data.TimeoutUntilNew.HasValue)
{
- var xp = presences[i];
- xp.Discord = this;
- xp.Activity = new DiscordActivity(xp.RawActivity);
+ // A timeout was removed.
- if (xp.RawActivities != null)
+ if (filtered != null && auditlog == null)
{
- xp.InternalActivities = new DiscordActivity[xp.RawActivities.Length];
- for (var j = 0; j < xp.RawActivities.Length; j++)
- xp.InternalActivities[j] = new DiscordActivity(xp.RawActivities[j]);
+ this.Logger.LogTrace("Re-scheduling timeout event.");
+ timer.Change(2000, Timeout.Infinite);
+ return;
}
- pres.Add(xp);
+ var ea = new GuildMemberTimeoutRemoveEventArgs(this.ServiceProvider)
+ {
+ Guild = data.Guild,
+ Target = data.Member,
+ TimeoutBefore = data.TimeoutUntilOld.Value,
+ Actor = actor,
+ AuditLogId = filtered?.Id,
+ AuditLogReason = filtered?.Reason
+ };
+ await this._guildMemberTimeoutRemoved.InvokeAsync(this, ea).ConfigureAwait(false);
}
- ea.Presences = new ReadOnlySet<DiscordPresence>(pres);
+ // Ending timer because it worked.
+ this.Logger.LogTrace("Removing timeout event.");
+ await timer.DisposeAsync();
+ this._tempTimers.Remove(tid);
}
- if (dat["not_found"] != null)
+ /// <summary>
+ /// Handles the guild members chunk event.
+ /// </summary>
+ /// <param name="dat">The raw chunk data.</param>
+ internal async Task OnGuildMembersChunkEventAsync(JObject dat)
{
- var nf = dat["not_found"].ToObject<ISet<ulong>>();
- ea.NotFound = new ReadOnlySet<ulong>(nf);
- }
+ var guild = this.Guilds[(ulong)dat["guild_id"]];
+ var chunkIndex = (int)dat["chunk_index"];
+ var chunkCount = (int)dat["chunk_count"];
+ var nonce = (string)dat["nonce"];
- await this._guildMembersChunked.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ var mbrs = new HashSet<DiscordMember>();
+ var pres = new HashSet<DiscordPresence>();
- #endregion
+ var members = dat["members"].ToObject<TransportMember[]>();
- #region Guild Role
+ var memCount = members.Length;
+ for (var i = 0; i < memCount; i++)
+ {
+ var mbr = new DiscordMember(members[i]) { Discord = this, GuildId = guild.Id };
- /// <summary>
- /// Handles the guild role create event.
- /// </summary>
- /// <param name="role">The role.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildRoleCreateEventAsync(DiscordRole role, DiscordGuild guild)
- {
- role.Discord = this;
- role.GuildId = guild.Id;
+ if (!this.UserCache.ContainsKey(mbr.Id))
+ this.UserCache[mbr.Id] = new DiscordUser(members[i].User) { Discord = this };
- guild.RolesInternal[role.Id] = role;
+ guild.MembersInternal[mbr.Id] = mbr;
- var ea = new GuildRoleCreateEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- Role = role
- };
- await this._guildRoleCreated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ mbrs.Add(mbr);
+ }
- /// <summary>
- /// Handles the guild role update event.
- /// </summary>
- /// <param name="role">The role.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildRoleUpdateEventAsync(DiscordRole role, DiscordGuild guild)
- {
- var newRole = guild.GetRole(role.Id);
- var oldRole = new DiscordRole
- {
- GuildId = guild.Id,
- ColorInternal = newRole.ColorInternal,
- Discord = this,
- IsHoisted = newRole.IsHoisted,
- Id = newRole.Id,
- IsManaged = newRole.IsManaged,
- IsMentionable = newRole.IsMentionable,
- Name = newRole.Name,
- Permissions = newRole.Permissions,
- Position = newRole.Position,
- IconHash = newRole.IconHash,
- Tags = newRole.Tags ?? null,
- UnicodeEmojiString = newRole.UnicodeEmojiString
- };
-
- newRole.GuildId = guild.Id;
- newRole.ColorInternal = role.ColorInternal;
- newRole.IsHoisted = role.IsHoisted;
- newRole.IsManaged = role.IsManaged;
- newRole.IsMentionable = role.IsMentionable;
- newRole.Name = role.Name;
- newRole.Permissions = role.Permissions;
- newRole.Position = role.Position;
- newRole.IconHash = role.IconHash;
- newRole.Tags = role.Tags ?? null;
- newRole.UnicodeEmojiString = role.UnicodeEmojiString;
-
- var ea = new GuildRoleUpdateEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- RoleAfter = newRole,
- RoleBefore = oldRole
- };
- await this._guildRoleUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ guild.MemberCount = guild.MembersInternal.Count;
- /// <summary>
- /// Handles the guild role delete event.
- /// </summary>
- /// <param name="roleId">The role id.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnGuildRoleDeleteEventAsync(ulong roleId, DiscordGuild guild)
- {
- if (!guild.RolesInternal.TryRemove(roleId, out var role))
- this.Logger.LogWarning($"Attempted to delete a nonexistent role ({roleId}) from guild ({guild}).");
+ var ea = new GuildMembersChunkEventArgs(this.ServiceProvider)
+ {
+ Guild = guild,
+ Members = new ReadOnlySet<DiscordMember>(mbrs),
+ ChunkIndex = chunkIndex,
+ ChunkCount = chunkCount,
+ Nonce = nonce,
+ };
- var ea = new GuildRoleDeleteEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- Role = role
- };
- await this._guildRoleDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ if (dat["presences"] != null)
+ {
+ var presences = dat["presences"].ToObject<DiscordPresence[]>();
- #endregion
+ var presCount = presences.Length;
+ for (var i = 0; i < presCount; i++)
+ {
+ var xp = presences[i];
+ xp.Discord = this;
+ xp.Activity = new DiscordActivity(xp.RawActivity);
- #region Invite
+ if (xp.RawActivities != null)
+ {
+ xp.InternalActivities = new DiscordActivity[xp.RawActivities.Length];
+ for (var j = 0; j < xp.RawActivities.Length; j++)
+ xp.InternalActivities[j] = new DiscordActivity(xp.RawActivities[j]);
+ }
- /// <summary>
- /// Handles the invite create event.
- /// </summary>
- /// <param name="channelId">The channel id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="invite">The invite.</param>
- internal async Task OnInviteCreateEventAsync(ulong channelId, ulong guildId, DiscordInvite invite)
- {
- var guild = this.InternalGetCachedGuild(guildId);
- var channel = this.InternalGetCachedChannel(channelId);
+ pres.Add(xp);
+ }
- invite.Discord = this;
+ ea.Presences = new ReadOnlySet<DiscordPresence>(pres);
+ }
- if (invite.Inviter is not null)
- {
- invite.Inviter.Discord = this;
- this.UserCache.AddOrUpdate(invite.Inviter.Id, invite.Inviter, (old, @new) => @new);
+ if (dat["not_found"] != null)
+ {
+ var nf = dat["not_found"].ToObject<ISet<ulong>>();
+ ea.NotFound = new ReadOnlySet<ulong>(nf);
+ }
+
+ await this._guildMembersChunked.InvokeAsync(this, ea).ConfigureAwait(false);
}
- guild.Invites[invite.Code] = invite;
+ #endregion
+
+ #region Guild Role
- var ea = new InviteCreateEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the guild role create event.
+ /// </summary>
+ /// <param name="role">The role.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildRoleCreateEventAsync(DiscordRole role, DiscordGuild guild)
{
- Channel = channel,
- Guild = guild,
- Invite = invite
- };
- await this._inviteCreated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ role.Discord = this;
+ role.GuildId = guild.Id;
- /// <summary>
- /// Handles the invite delete event.
- /// </summary>
- /// <param name="channelId">The channel id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="dat">The raw invite.</param>
- internal async Task OnInviteDeleteEventAsync(ulong channelId, ulong guildId, JToken dat)
- {
- var guild = this.InternalGetCachedGuild(guildId);
- var channel = this.InternalGetCachedChannel(channelId);
+ guild.RolesInternal[role.Id] = role;
- if (!guild.Invites.TryRemove(dat["code"].ToString(), out var invite))
- {
- invite = dat.ToObject<DiscordInvite>();
- invite.Discord = this;
+ var ea = new GuildRoleCreateEventArgs(this.ServiceProvider)
+ {
+ Guild = guild,
+ Role = role
+ };
+ await this._guildRoleCreated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- invite.IsRevoked = true;
-
- var ea = new InviteDeleteEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the guild role update event.
+ /// </summary>
+ /// <param name="role">The role.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildRoleUpdateEventAsync(DiscordRole role, DiscordGuild guild)
{
- Channel = channel,
- Guild = guild,
- Invite = invite
- };
- await this._inviteDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
- }
-
- #endregion
+ var newRole = guild.GetRole(role.Id);
+ var oldRole = new DiscordRole
+ {
+ GuildId = guild.Id,
+ ColorInternal = newRole.ColorInternal,
+ Discord = this,
+ IsHoisted = newRole.IsHoisted,
+ Id = newRole.Id,
+ IsManaged = newRole.IsManaged,
+ IsMentionable = newRole.IsMentionable,
+ Name = newRole.Name,
+ Permissions = newRole.Permissions,
+ Position = newRole.Position,
+ IconHash = newRole.IconHash,
+ Tags = newRole.Tags ?? null,
+ UnicodeEmojiString = newRole.UnicodeEmojiString
+ };
- #region Message
+ newRole.GuildId = guild.Id;
+ newRole.ColorInternal = role.ColorInternal;
+ newRole.IsHoisted = role.IsHoisted;
+ newRole.IsManaged = role.IsManaged;
+ newRole.IsMentionable = role.IsMentionable;
+ newRole.Name = role.Name;
+ newRole.Permissions = role.Permissions;
+ newRole.Position = role.Position;
+ newRole.IconHash = role.IconHash;
+ newRole.Tags = role.Tags ?? null;
+ newRole.UnicodeEmojiString = role.UnicodeEmojiString;
+
+ var ea = new GuildRoleUpdateEventArgs(this.ServiceProvider)
+ {
+ Guild = guild,
+ RoleAfter = newRole,
+ RoleBefore = oldRole
+ };
+ await this._guildRoleUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the message acknowledge event.
- /// </summary>
- /// <param name="chn">The channel.</param>
- /// <param name="messageId">The message id.</param>
- internal async Task OnMessageAckEventAsync(DiscordChannel chn, ulong messageId)
- {
- if (this.MessageCache == null || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == chn.Id, out var msg))
+ /// <summary>
+ /// Handles the guild role delete event.
+ /// </summary>
+ /// <param name="roleId">The role id.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnGuildRoleDeleteEventAsync(ulong roleId, DiscordGuild guild)
{
- msg = new DiscordMessage
+ if (!guild.RolesInternal.TryRemove(roleId, out var role))
+ this.Logger.LogWarning($"Attempted to delete a nonexistent role ({roleId}) from guild ({guild}).");
+
+ var ea = new GuildRoleDeleteEventArgs(this.ServiceProvider)
{
- Id = messageId,
- ChannelId = chn.Id,
- Discord = this,
+ Guild = guild,
+ Role = role
};
+ await this._guildRoleDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
}
- await this._messageAcknowledged.InvokeAsync(this, new MessageAcknowledgeEventArgs(this.ServiceProvider) { Message = msg }).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Handles the message create event.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="author">The transport user (author).</param>
- /// <param name="member">The transport member.</param>
- /// <param name="referenceAuthor">The reference transport user (author).</param>
- /// <param name="referenceMember">The reference transport member.</param>
- internal async Task OnMessageCreateEventAsync(DiscordMessage message, TransportUser author, TransportMember member, TransportUser referenceAuthor, TransportMember referenceMember)
- {
- message.Discord = this;
- this.PopulateMessageReactionsAndCache(message, author, member);
- message.PopulateMentions();
+ #endregion
- if (message.Channel == null && message.ChannelId == default)
- this.Logger.LogWarning(LoggerEvents.WebSocketReceive, "Channel which the last message belongs to is not in cache - cache state might be invalid!");
+ #region Invite
- if (message.ReferencedMessage != null)
+ /// <summary>
+ /// Handles the invite create event.
+ /// </summary>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="invite">The invite.</param>
+ internal async Task OnInviteCreateEventAsync(ulong channelId, ulong guildId, DiscordInvite invite)
{
- message.ReferencedMessage.Discord = this;
- this.PopulateMessageReactionsAndCache(message.ReferencedMessage, referenceAuthor, referenceMember);
- message.ReferencedMessage.PopulateMentions();
- }
+ var guild = this.InternalGetCachedGuild(guildId);
+ var channel = this.InternalGetCachedChannel(channelId);
+
+ invite.Discord = this;
+
+ if (invite.Inviter is not null)
+ {
+ invite.Inviter.Discord = this;
+ this.UserCache.AddOrUpdate(invite.Inviter.Id, invite.Inviter, (old, @new) => @new);
+ }
+
+ guild.Invites[invite.Code] = invite;
- foreach (var sticker in message.Stickers)
- sticker.Discord = this;
+ var ea = new InviteCreateEventArgs(this.ServiceProvider)
+ {
+ Channel = channel,
+ Guild = guild,
+ Invite = invite
+ };
+ await this._inviteCreated.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- var ea = new MessageCreateEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the invite delete event.
+ /// </summary>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="dat">The raw invite.</param>
+ internal async Task OnInviteDeleteEventAsync(ulong channelId, ulong guildId, JToken dat)
{
- Message = message,
+ var guild = this.InternalGetCachedGuild(guildId);
+ var channel = this.InternalGetCachedChannel(channelId);
- MentionedUsers = new ReadOnlyCollection<DiscordUser>(message.MentionedUsersInternal),
- MentionedRoles = message.MentionedRolesInternal != null ? new ReadOnlyCollection<DiscordRole>(message.MentionedRolesInternal) : null,
- MentionedChannels = message.MentionedChannelsInternal != null ? new ReadOnlyCollection<DiscordChannel>(message.MentionedChannelsInternal) : null
- };
- await this._messageCreated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ if (!guild.Invites.TryRemove(dat["code"].ToString(), out var invite))
+ {
+ invite = dat.ToObject<DiscordInvite>();
+ invite.Discord = this;
+ }
- /// <summary>
- /// Handles the message update event.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="author">The transport user (author).</param>
- /// <param name="member">The transport member.</param>
- /// <param name="referenceAuthor">The reference transport user (author).</param>
- /// <param name="referenceMember">The reference transport member.</param>
- internal async Task OnMessageUpdateEventAsync(DiscordMessage message, TransportUser author, TransportMember member, TransportUser referenceAuthor, TransportMember referenceMember)
- {
- DiscordGuild guild;
+ invite.IsRevoked = true;
+
+ var ea = new InviteDeleteEventArgs(this.ServiceProvider)
+ {
+ Channel = channel,
+ Guild = guild,
+ Invite = invite
+ };
+ await this._inviteDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
+
+ #endregion
- message.Discord = this;
- var eventMessage = message;
+ #region Message
- DiscordMessage oldmsg = null;
- if (this.Configuration.MessageCacheSize == 0
- || this.MessageCache == null
- || !this.MessageCache.TryGet(xm => xm.Id == eventMessage.Id && xm.ChannelId == eventMessage.ChannelId, out message))
+ /// <summary>
+ /// Handles the message acknowledge event.
+ /// </summary>
+ /// <param name="chn">The channel.</param>
+ /// <param name="messageId">The message id.</param>
+ internal async Task OnMessageAckEventAsync(DiscordChannel chn, ulong messageId)
{
- message = eventMessage;
+ if (this.MessageCache == null || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == chn.Id, out var msg))
+ {
+ msg = new DiscordMessage
+ {
+ Id = messageId,
+ ChannelId = chn.Id,
+ Discord = this,
+ };
+ }
+
+ await this._messageAcknowledged.InvokeAsync(this, new MessageAcknowledgeEventArgs(this.ServiceProvider) { Message = msg }).ConfigureAwait(false);
+ }
+
+ /// <summary>
+ /// Handles the message create event.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <param name="author">The transport user (author).</param>
+ /// <param name="member">The transport member.</param>
+ /// <param name="referenceAuthor">The reference transport user (author).</param>
+ /// <param name="referenceMember">The reference transport member.</param>
+ internal async Task OnMessageCreateEventAsync(DiscordMessage message, TransportUser author, TransportMember member, TransportUser referenceAuthor, TransportMember referenceMember)
+ {
+ message.Discord = this;
this.PopulateMessageReactionsAndCache(message, author, member);
- guild = message.Channel?.Guild;
+ message.PopulateMentions();
+
+ if (message.Channel == null && message.ChannelId == default)
+ this.Logger.LogWarning(LoggerEvents.WebSocketReceive, "Channel which the last message belongs to is not in cache - cache state might be invalid!");
if (message.ReferencedMessage != null)
{
message.ReferencedMessage.Discord = this;
this.PopulateMessageReactionsAndCache(message.ReferencedMessage, referenceAuthor, referenceMember);
message.ReferencedMessage.PopulateMentions();
}
+
+ foreach (var sticker in message.Stickers)
+ sticker.Discord = this;
+
+ var ea = new MessageCreateEventArgs(this.ServiceProvider)
+ {
+ Message = message,
+
+ MentionedUsers = new ReadOnlyCollection<DiscordUser>(message.MentionedUsersInternal),
+ MentionedRoles = message.MentionedRolesInternal != null ? new ReadOnlyCollection<DiscordRole>(message.MentionedRolesInternal) : null,
+ MentionedChannels = message.MentionedChannelsInternal != null ? new ReadOnlyCollection<DiscordChannel>(message.MentionedChannelsInternal) : null
+ };
+ await this._messageCreated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- else
+
+ /// <summary>
+ /// Handles the message update event.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <param name="author">The transport user (author).</param>
+ /// <param name="member">The transport member.</param>
+ /// <param name="referenceAuthor">The reference transport user (author).</param>
+ /// <param name="referenceMember">The reference transport member.</param>
+ internal async Task OnMessageUpdateEventAsync(DiscordMessage message, TransportUser author, TransportMember member, TransportUser referenceAuthor, TransportMember referenceMember)
{
- oldmsg = new DiscordMessage(message);
+ DiscordGuild guild;
- guild = message.Channel?.Guild;
- message.EditedTimestampRaw = eventMessage.EditedTimestampRaw;
- if (eventMessage.Content != null)
- message.Content = eventMessage.Content;
- message.EmbedsInternal.Clear();
- message.EmbedsInternal.AddRange(eventMessage.EmbedsInternal);
- message.Pinned = eventMessage.Pinned;
- message.IsTts = eventMessage.IsTts;
- }
+ message.Discord = this;
+ var eventMessage = message;
+
+ DiscordMessage oldmsg = null;
+ if (this.Configuration.MessageCacheSize == 0
+ || this.MessageCache == null
+ || !this.MessageCache.TryGet(xm => xm.Id == eventMessage.Id && xm.ChannelId == eventMessage.ChannelId, out message))
+ {
+ message = eventMessage;
+ this.PopulateMessageReactionsAndCache(message, author, member);
+ guild = message.Channel?.Guild;
+
+ if (message.ReferencedMessage != null)
+ {
+ message.ReferencedMessage.Discord = this;
+ this.PopulateMessageReactionsAndCache(message.ReferencedMessage, referenceAuthor, referenceMember);
+ message.ReferencedMessage.PopulateMentions();
+ }
+ }
+ else
+ {
+ oldmsg = new DiscordMessage(message);
+
+ guild = message.Channel?.Guild;
+ message.EditedTimestampRaw = eventMessage.EditedTimestampRaw;
+ if (eventMessage.Content != null)
+ message.Content = eventMessage.Content;
+ message.EmbedsInternal.Clear();
+ message.EmbedsInternal.AddRange(eventMessage.EmbedsInternal);
+ message.Pinned = eventMessage.Pinned;
+ message.IsTts = eventMessage.IsTts;
+ }
+
+ message.PopulateMentions();
- message.PopulateMentions();
+ var ea = new MessageUpdateEventArgs(this.ServiceProvider)
+ {
+ Message = message,
+ MessageBefore = oldmsg,
+ MentionedUsers = new ReadOnlyCollection<DiscordUser>(message.MentionedUsersInternal),
+ MentionedRoles = message.MentionedRolesInternal != null ? new ReadOnlyCollection<DiscordRole>(message.MentionedRolesInternal) : null,
+ MentionedChannels = message.MentionedChannelsInternal != null ? new ReadOnlyCollection<DiscordChannel>(message.MentionedChannelsInternal) : null
+ };
+ await this._messageUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- var ea = new MessageUpdateEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the message delete event.
+ /// </summary>
+ /// <param name="messageId">The message id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="guildId">The optional guild id.</param>
+ internal async Task OnMessageDeleteEventAsync(ulong messageId, ulong channelId, ulong? guildId)
{
- Message = message,
- MessageBefore = oldmsg,
- MentionedUsers = new ReadOnlyCollection<DiscordUser>(message.MentionedUsersInternal),
- MentionedRoles = message.MentionedRolesInternal != null ? new ReadOnlyCollection<DiscordRole>(message.MentionedRolesInternal) : null,
- MentionedChannels = message.MentionedChannelsInternal != null ? new ReadOnlyCollection<DiscordChannel>(message.MentionedChannelsInternal) : null
- };
- await this._messageUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
+ var guild = this.InternalGetCachedGuild(guildId);
- /// <summary>
- /// Handles the message delete event.
- /// </summary>
- /// <param name="messageId">The message id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="guildId">The optional guild id.</param>
- internal async Task OnMessageDeleteEventAsync(ulong messageId, ulong channelId, ulong? guildId)
- {
- var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
- var guild = this.InternalGetCachedGuild(guildId);
+ if (channel == null
+ || this.Configuration.MessageCacheSize == 0
+ || this.MessageCache == null
+ || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
+ {
+ msg = new DiscordMessage
+ {
+
+ Id = messageId,
+ ChannelId = channelId,
+ Discord = this,
+ };
+ }
+
+ if (this.Configuration.MessageCacheSize > 0)
+ this.MessageCache?.Remove(xm => xm.Id == msg.Id && xm.ChannelId == channelId);
+
+ var ea = new MessageDeleteEventArgs(this.ServiceProvider)
+ {
+ Channel = channel,
+ Message = msg,
+ Guild = guild
+ };
+ await this._messageDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- if (channel == null
- || this.Configuration.MessageCacheSize == 0
- || this.MessageCache == null
- || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
+ /// <summary>
+ /// Handles the message bulk delete event.
+ /// </summary>
+ /// <param name="messageIds">The message ids.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="guildId">The optional guild id.</param>
+ internal async Task OnMessageBulkDeleteEventAsync(ulong[] messageIds, ulong channelId, ulong? guildId)
{
- msg = new DiscordMessage
+ var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
+
+ var msgs = new List<DiscordMessage>(messageIds.Length);
+ foreach (var messageId in messageIds)
{
+ if (channel == null
+ || this.Configuration.MessageCacheSize == 0
+ || this.MessageCache == null
+ || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
+ {
+ msg = new DiscordMessage
+ {
+ Id = messageId,
+ ChannelId = channelId,
+ Discord = this,
+ };
+ }
+ if (this.Configuration.MessageCacheSize > 0)
+ this.MessageCache?.Remove(xm => xm.Id == msg.Id && xm.ChannelId == channelId);
+ msgs.Add(msg);
+ }
- Id = messageId,
- ChannelId = channelId,
- Discord = this,
+ var guild = this.InternalGetCachedGuild(guildId);
+
+ var ea = new MessageBulkDeleteEventArgs(this.ServiceProvider)
+ {
+ Channel = channel,
+ Messages = new ReadOnlyCollection<DiscordMessage>(msgs),
+ Guild = guild
};
+ await this._messagesBulkDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
}
- if (this.Configuration.MessageCacheSize > 0)
- this.MessageCache?.Remove(xm => xm.Id == msg.Id && xm.ChannelId == channelId);
+ #endregion
- var ea = new MessageDeleteEventArgs(this.ServiceProvider)
- {
- Channel = channel,
- Message = msg,
- Guild = guild
- };
- await this._messageDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ #region Message Reaction
- /// <summary>
- /// Handles the message bulk delete event.
- /// </summary>
- /// <param name="messageIds">The message ids.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="guildId">The optional guild id.</param>
- internal async Task OnMessageBulkDeleteEventAsync(ulong[] messageIds, ulong channelId, ulong? guildId)
- {
- var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
+ /// <summary>
+ /// Handles the message reaction add event.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="messageId">The message id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="guildId">The optional guild id.</param>
+ /// <param name="mbr">The transport member.</param>
+ /// <param name="emoji">The emoji.</param>
+ internal async Task OnMessageReactionAddAsync(ulong userId, ulong messageId, ulong channelId, ulong? guildId, TransportMember mbr, DiscordEmoji emoji)
+ {
+ var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
+ var guild = this.InternalGetCachedGuild(guildId);
+ emoji.Discord = this;
+
+ var usr = this.UpdateUser(new DiscordUser { Id = userId, Discord = this }, guildId, guild, mbr);
- var msgs = new List<DiscordMessage>(messageIds.Length);
- foreach (var messageId in messageIds)
- {
if (channel == null
|| this.Configuration.MessageCacheSize == 0
|| this.MessageCache == null
|| !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
{
msg = new DiscordMessage
{
Id = messageId,
ChannelId = channelId,
Discord = this,
+ ReactionsInternal = new List<DiscordReaction>()
};
}
- if (this.Configuration.MessageCacheSize > 0)
- this.MessageCache?.Remove(xm => xm.Id == msg.Id && xm.ChannelId == channelId);
- msgs.Add(msg);
- }
-
- var guild = this.InternalGetCachedGuild(guildId);
-
- var ea = new MessageBulkDeleteEventArgs(this.ServiceProvider)
- {
- Channel = channel,
- Messages = new ReadOnlyCollection<DiscordMessage>(msgs),
- Guild = guild
- };
- await this._messagesBulkDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
- }
-
- #endregion
- #region Message Reaction
-
- /// <summary>
- /// Handles the message reaction add event.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="messageId">The message id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="guildId">The optional guild id.</param>
- /// <param name="mbr">The transport member.</param>
- /// <param name="emoji">The emoji.</param>
- internal async Task OnMessageReactionAddAsync(ulong userId, ulong messageId, ulong channelId, ulong? guildId, TransportMember mbr, DiscordEmoji emoji)
- {
- var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
- var guild = this.InternalGetCachedGuild(guildId);
- emoji.Discord = this;
-
- var usr = this.UpdateUser(new DiscordUser { Id = userId, Discord = this }, guildId, guild, mbr);
+ var react = msg.ReactionsInternal.FirstOrDefault(xr => xr.Emoji == emoji);
+ if (react == null)
+ {
+ msg.ReactionsInternal.Add(react = new DiscordReaction
+ {
+ Count = 1,
+ Emoji = emoji,
+ IsMe = this.CurrentUser.Id == userId
+ });
+ }
+ else
+ {
+ react.Count++;
+ react.IsMe |= this.CurrentUser.Id == userId;
+ }
- if (channel == null
- || this.Configuration.MessageCacheSize == 0
- || this.MessageCache == null
- || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
- {
- msg = new DiscordMessage
+ var ea = new MessageReactionAddEventArgs(this.ServiceProvider)
{
- Id = messageId,
- ChannelId = channelId,
- Discord = this,
- ReactionsInternal = new List<DiscordReaction>()
+ Message = msg,
+ User = usr,
+ Guild = guild,
+ Emoji = emoji
};
+ await this._messageReactionAdded.InvokeAsync(this, ea).ConfigureAwait(false);
}
- var react = msg.ReactionsInternal.FirstOrDefault(xr => xr.Emoji == emoji);
- if (react == null)
- {
- msg.ReactionsInternal.Add(react = new DiscordReaction
- {
- Count = 1,
- Emoji = emoji,
- IsMe = this.CurrentUser.Id == userId
- });
- }
- else
+ /// <summary>
+ /// Handles the message reaction remove event.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="messageId">The message id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="emoji">The emoji.</param>
+ internal async Task OnMessageReactionRemoveAsync(ulong userId, ulong messageId, ulong channelId, ulong? guildId, DiscordEmoji emoji)
{
- react.Count++;
- react.IsMe |= this.CurrentUser.Id == userId;
- }
+ var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
- var ea = new MessageReactionAddEventArgs(this.ServiceProvider)
- {
- Message = msg,
- User = usr,
- Guild = guild,
- Emoji = emoji
- };
- await this._messageReactionAdded.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ emoji.Discord = this;
- /// <summary>
- /// Handles the message reaction remove event.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="messageId">The message id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="emoji">The emoji.</param>
- internal async Task OnMessageReactionRemoveAsync(ulong userId, ulong messageId, ulong channelId, ulong? guildId, DiscordEmoji emoji)
- {
- var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
+ if (!this.UserCache.TryGetValue(userId, out var usr))
+ usr = new DiscordUser { Id = userId, Discord = this };
- emoji.Discord = this;
+ if (channel?.Guild != null)
+ usr = channel.Guild.Members.TryGetValue(userId, out var member)
+ ? member
+ : new DiscordMember(usr) { Discord = this, GuildId = channel.GuildId.Value };
+
+ if (channel == null
+ || this.Configuration.MessageCacheSize == 0
+ || this.MessageCache == null
+ || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
+ {
+ msg = new DiscordMessage
+ {
+ Id = messageId,
+ ChannelId = channelId,
+ Discord = this
+ };
+ }
- if (!this.UserCache.TryGetValue(userId, out var usr))
- usr = new DiscordUser { Id = userId, Discord = this };
+ var react = msg.ReactionsInternal?.FirstOrDefault(xr => xr.Emoji == emoji);
+ if (react != null)
+ {
+ react.Count--;
+ react.IsMe &= this.CurrentUser.Id != userId;
+
+ if (msg.ReactionsInternal != null && react.Count <= 0) // shit happens
+ for (var i = 0; i < msg.ReactionsInternal.Count; i++)
+ if (msg.ReactionsInternal[i].Emoji == emoji)
+ {
+ msg.ReactionsInternal.RemoveAt(i);
+ break;
+ }
+ }
- if (channel?.Guild != null)
- usr = channel.Guild.Members.TryGetValue(userId, out var member)
- ? member
- : new DiscordMember(usr) { Discord = this, GuildId = channel.GuildId.Value };
+ var guild = this.InternalGetCachedGuild(guildId);
- if (channel == null
- || this.Configuration.MessageCacheSize == 0
- || this.MessageCache == null
- || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
- {
- msg = new DiscordMessage
+ var ea = new MessageReactionRemoveEventArgs(this.ServiceProvider)
{
- Id = messageId,
- ChannelId = channelId,
- Discord = this
+ Message = msg,
+ User = usr,
+ Guild = guild,
+ Emoji = emoji
};
+ await this._messageReactionRemoved.InvokeAsync(this, ea).ConfigureAwait(false);
}
- var react = msg.ReactionsInternal?.FirstOrDefault(xr => xr.Emoji == emoji);
- if (react != null)
+ /// <summary>
+ /// Handles the message reaction remove event.
+ /// Fired when all message reactions were removed.
+ /// </summary>
+ /// <param name="messageId">The message id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="guildId">The optional guild id.</param>
+ internal async Task OnMessageReactionRemoveAllAsync(ulong messageId, ulong channelId, ulong? guildId)
{
- react.Count--;
- react.IsMe &= this.CurrentUser.Id != userId;
-
- if (msg.ReactionsInternal != null && react.Count <= 0) // shit happens
- for (var i = 0; i < msg.ReactionsInternal.Count; i++)
- if (msg.ReactionsInternal[i].Emoji == emoji)
- {
- msg.ReactionsInternal.RemoveAt(i);
- break;
- }
- }
+ var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
- var guild = this.InternalGetCachedGuild(guildId);
+ if (channel == null
+ || this.Configuration.MessageCacheSize == 0
+ || this.MessageCache == null
+ || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
+ {
+ msg = new DiscordMessage
+ {
+ Id = messageId,
+ ChannelId = channelId,
+ Discord = this
+ };
+ }
- var ea = new MessageReactionRemoveEventArgs(this.ServiceProvider)
- {
- Message = msg,
- User = usr,
- Guild = guild,
- Emoji = emoji
- };
- await this._messageReactionRemoved.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ msg.ReactionsInternal?.Clear();
- /// <summary>
- /// Handles the message reaction remove event.
- /// Fired when all message reactions were removed.
- /// </summary>
- /// <param name="messageId">The message id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="guildId">The optional guild id.</param>
- internal async Task OnMessageReactionRemoveAllAsync(ulong messageId, ulong channelId, ulong? guildId)
- {
- var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
+ var guild = this.InternalGetCachedGuild(guildId);
- if (channel == null
- || this.Configuration.MessageCacheSize == 0
- || this.MessageCache == null
- || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
- {
- msg = new DiscordMessage
+ var ea = new MessageReactionsClearEventArgs(this.ServiceProvider)
{
- Id = messageId,
- ChannelId = channelId,
- Discord = this
+ Message = msg
};
+
+ await this._messageReactionsCleared.InvokeAsync(this, ea).ConfigureAwait(false);
}
- msg.ReactionsInternal?.Clear();
+ /// <summary>
+ /// Handles the message reaction remove event.
+ /// Fired when a emoji got removed.
+ /// </summary>
+ /// <param name="messageId">The message id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="dat">The raw discord emoji.</param>
+ internal async Task OnMessageReactionRemoveEmojiAsync(ulong messageId, ulong channelId, ulong guildId, JToken dat)
+ {
+ var guild = this.InternalGetCachedGuild(guildId);
+ var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
- var guild = this.InternalGetCachedGuild(guildId);
+ if (channel == null
+ || this.Configuration.MessageCacheSize == 0
+ || this.MessageCache == null
+ || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
+ {
+ msg = new DiscordMessage
+ {
+ Id = messageId,
+ ChannelId = channelId,
+ Discord = this
+ };
+ }
- var ea = new MessageReactionsClearEventArgs(this.ServiceProvider)
- {
- Message = msg
- };
+ var partialEmoji = dat.ToObject<DiscordEmoji>();
- await this._messageReactionsCleared.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ if (!guild.EmojisInternal.TryGetValue(partialEmoji.Id, out var emoji))
+ {
+ emoji = partialEmoji;
+ emoji.Discord = this;
+ }
- /// <summary>
- /// Handles the message reaction remove event.
- /// Fired when a emoji got removed.
- /// </summary>
- /// <param name="messageId">The message id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="dat">The raw discord emoji.</param>
- internal async Task OnMessageReactionRemoveEmojiAsync(ulong messageId, ulong channelId, ulong guildId, JToken dat)
- {
- var guild = this.InternalGetCachedGuild(guildId);
- var channel = this.InternalGetCachedChannel(channelId) ?? this.InternalGetCachedThread(channelId);
+ msg.ReactionsInternal?.RemoveAll(r => r.Emoji.Equals(emoji));
- if (channel == null
- || this.Configuration.MessageCacheSize == 0
- || this.MessageCache == null
- || !this.MessageCache.TryGet(xm => xm.Id == messageId && xm.ChannelId == channelId, out var msg))
- {
- msg = new DiscordMessage
+ var ea = new MessageReactionRemoveEmojiEventArgs(this.ServiceProvider)
{
- Id = messageId,
- ChannelId = channelId,
- Discord = this
+ Channel = channel,
+ Guild = guild,
+ Message = msg,
+ Emoji = emoji
};
- }
- var partialEmoji = dat.ToObject<DiscordEmoji>();
-
- if (!guild.EmojisInternal.TryGetValue(partialEmoji.Id, out var emoji))
- {
- emoji = partialEmoji;
- emoji.Discord = this;
+ await this._messageReactionRemovedEmoji.InvokeAsync(this, ea).ConfigureAwait(false);
}
- msg.ReactionsInternal?.RemoveAll(r => r.Emoji.Equals(emoji));
-
- var ea = new MessageReactionRemoveEmojiEventArgs(this.ServiceProvider)
- {
- Channel = channel,
- Guild = guild,
- Message = msg,
- Emoji = emoji
- };
+ #endregion
- await this._messageReactionRemovedEmoji.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ #region Stage Instance
- #endregion
+ /// <summary>
+ /// Handles the stage instance create event.
+ /// </summary>
+ /// <param name="stage">The created stage instance.</param>
+ internal async Task OnStageInstanceCreateEventAsync(DiscordStageInstance stage)
+ {
+ stage.Discord = this;
- #region Stage Instance
+ var guild = this.InternalGetCachedGuild(stage.GuildId);
+ guild.StageInstancesInternal[stage.Id] = stage;
- /// <summary>
- /// Handles the stage instance create event.
- /// </summary>
- /// <param name="stage">The created stage instance.</param>
- internal async Task OnStageInstanceCreateEventAsync(DiscordStageInstance stage)
- {
- stage.Discord = this;
+ await this._stageInstanceCreated.InvokeAsync(this, new StageInstanceCreateEventArgs(this.ServiceProvider) { StageInstance = stage, Guild = stage.Guild }).ConfigureAwait(false);
+ }
- var guild = this.InternalGetCachedGuild(stage.GuildId);
- guild.StageInstancesInternal[stage.Id] = stage;
+ /// <summary>
+ /// Handles the stage instance update event.
+ /// </summary>
+ /// <param name="stage">The updated stage instance.</param>
+ internal async Task OnStageInstanceUpdateEventAsync(DiscordStageInstance stage)
+ {
+ stage.Discord = this;
+ var guild = this.InternalGetCachedGuild(stage.GuildId);
+ guild.StageInstancesInternal[stage.Id] = stage;
- await this._stageInstanceCreated.InvokeAsync(this, new StageInstanceCreateEventArgs(this.ServiceProvider) { StageInstance = stage, Guild = stage.Guild }).ConfigureAwait(false);
- }
+ await this._stageInstanceUpdated.InvokeAsync(this, new StageInstanceUpdateEventArgs(this.ServiceProvider) { StageInstance = stage, Guild = stage.Guild }).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the stage instance update event.
- /// </summary>
- /// <param name="stage">The updated stage instance.</param>
- internal async Task OnStageInstanceUpdateEventAsync(DiscordStageInstance stage)
- {
- stage.Discord = this;
- var guild = this.InternalGetCachedGuild(stage.GuildId);
- guild.StageInstancesInternal[stage.Id] = stage;
+ /// <summary>
+ /// Handles the stage instance delete event.
+ /// </summary>
+ /// <param name="stage">The deleted stage instance.</param>
+ internal async Task OnStageInstanceDeleteEventAsync(DiscordStageInstance stage)
+ {
+ stage.Discord = this;
+ var guild = this.InternalGetCachedGuild(stage.GuildId);
+ guild.StageInstancesInternal[stage.Id] = stage;
- await this._stageInstanceUpdated.InvokeAsync(this, new StageInstanceUpdateEventArgs(this.ServiceProvider) { StageInstance = stage, Guild = stage.Guild }).ConfigureAwait(false);
- }
+ await this._stageInstanceDeleted.InvokeAsync(this, new StageInstanceDeleteEventArgs(this.ServiceProvider) { StageInstance = stage, Guild = stage.Guild }).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the stage instance delete event.
- /// </summary>
- /// <param name="stage">The deleted stage instance.</param>
- internal async Task OnStageInstanceDeleteEventAsync(DiscordStageInstance stage)
- {
- stage.Discord = this;
- var guild = this.InternalGetCachedGuild(stage.GuildId);
- guild.StageInstancesInternal[stage.Id] = stage;
+ #endregion
- await this._stageInstanceDeleted.InvokeAsync(this, new StageInstanceDeleteEventArgs(this.ServiceProvider) { StageInstance = stage, Guild = stage.Guild }).ConfigureAwait(false);
- }
+ #region Thread
- #endregion
+ /// <summary>
+ /// Handles the thread create event.
+ /// </summary>
+ /// <param name="thread">The created thread.</param>
+ internal async Task OnThreadCreateEventAsync(DiscordThreadChannel thread)
+ {
+ thread.Discord = this;
+ this.InternalGetCachedGuild(thread.GuildId).ThreadsInternal.AddOrUpdate(thread.Id, thread, (oldThread, newThread) => newThread);
- #region Thread
+ await this._threadCreated.InvokeAsync(this, new ThreadCreateEventArgs(this.ServiceProvider) { Thread = thread, Guild = thread.Guild, Parent = thread.Parent }).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the thread create event.
- /// </summary>
- /// <param name="thread">The created thread.</param>
- internal async Task OnThreadCreateEventAsync(DiscordThreadChannel thread)
- {
- thread.Discord = this;
- this.InternalGetCachedGuild(thread.GuildId).ThreadsInternal.AddOrUpdate(thread.Id, thread, (oldThread, newThread) => newThread);
+ /// <summary>
+ /// Handles the thread update event.
+ /// </summary>
+ /// <param name="thread">The updated thread.</param>
+ internal async Task OnThreadUpdateEventAsync(DiscordThreadChannel thread)
+ {
+ if (thread == null)
+ return;
- await this._threadCreated.InvokeAsync(this, new ThreadCreateEventArgs(this.ServiceProvider) { Thread = thread, Guild = thread.Guild, Parent = thread.Parent }).ConfigureAwait(false);
- }
+ thread.Discord = this;
+ var guild = thread.Guild;
- /// <summary>
- /// Handles the thread update event.
- /// </summary>
- /// <param name="thread">The updated thread.</param>
- internal async Task OnThreadUpdateEventAsync(DiscordThreadChannel thread)
- {
- if (thread == null)
- return;
+ var threadNew = this.InternalGetCachedThread(thread.Id);
+ DiscordThreadChannel threadOld = null;
+ ThreadUpdateEventArgs updateEvent;
- thread.Discord = this;
- var guild = thread.Guild;
+ if (threadNew != null)
+ {
+ threadOld = new DiscordThreadChannel
+ {
+ Discord = this,
+ Type = threadNew.Type,
+ ThreadMetadata = thread.ThreadMetadata,
+ ThreadMembersInternal = threadNew.ThreadMembersInternal,
+ ParentId = thread.ParentId,
+ OwnerId = thread.OwnerId,
+ Name = thread.Name,
+ LastMessageId = threadNew.LastMessageId,
+ MessageCount = thread.MessageCount,
+ MemberCount = thread.MemberCount,
+ GuildId = thread.GuildId,
+ LastPinTimestampRaw = threadNew.LastPinTimestampRaw,
+ PerUserRateLimit = threadNew.PerUserRateLimit,
+ CurrentMember = threadNew.CurrentMember
+ };
- var threadNew = this.InternalGetCachedThread(thread.Id);
- DiscordThreadChannel threadOld = null;
- ThreadUpdateEventArgs updateEvent;
+ threadNew.ThreadMetadata = thread.ThreadMetadata;
+ threadNew.ParentId = thread.ParentId;
+ threadNew.OwnerId = thread.OwnerId;
+ threadNew.Name = thread.Name;
+ threadNew.LastMessageId = thread.LastMessageId.HasValue ? thread.LastMessageId : threadOld.LastMessageId;
+ threadNew.MessageCount = thread.MessageCount;
+ threadNew.MemberCount = thread.MemberCount;
+ threadNew.GuildId = thread.GuildId;
- if (threadNew != null)
- {
- threadOld = new DiscordThreadChannel
+ updateEvent = new ThreadUpdateEventArgs(this.ServiceProvider)
+ {
+ ThreadAfter = thread,
+ ThreadBefore = threadOld,
+ Guild = thread.Guild,
+ Parent = thread.Parent
+ };
+ }
+ else
{
- Discord = this,
- Type = threadNew.Type,
- ThreadMetadata = thread.ThreadMetadata,
- ThreadMembersInternal = threadNew.ThreadMembersInternal,
- ParentId = thread.ParentId,
- OwnerId = thread.OwnerId,
- Name = thread.Name,
- LastMessageId = threadNew.LastMessageId,
- MessageCount = thread.MessageCount,
- MemberCount = thread.MemberCount,
- GuildId = thread.GuildId,
- LastPinTimestampRaw = threadNew.LastPinTimestampRaw,
- PerUserRateLimit = threadNew.PerUserRateLimit,
- CurrentMember = threadNew.CurrentMember
- };
+ updateEvent = new ThreadUpdateEventArgs(this.ServiceProvider)
+ {
+ ThreadAfter = thread,
+ Guild = thread.Guild,
+ Parent = thread.Parent
+ };
+ guild.ThreadsInternal[thread.Id] = thread;
+ }
- threadNew.ThreadMetadata = thread.ThreadMetadata;
- threadNew.ParentId = thread.ParentId;
- threadNew.OwnerId = thread.OwnerId;
- threadNew.Name = thread.Name;
- threadNew.LastMessageId = thread.LastMessageId.HasValue ? thread.LastMessageId : threadOld.LastMessageId;
- threadNew.MessageCount = thread.MessageCount;
- threadNew.MemberCount = thread.MemberCount;
- threadNew.GuildId = thread.GuildId;
-
- updateEvent = new ThreadUpdateEventArgs(this.ServiceProvider)
- {
- ThreadAfter = thread,
- ThreadBefore = threadOld,
- Guild = thread.Guild,
- Parent = thread.Parent
- };
- }
- else
- {
- updateEvent = new ThreadUpdateEventArgs(this.ServiceProvider)
- {
- ThreadAfter = thread,
- Guild = thread.Guild,
- Parent = thread.Parent
- };
- guild.ThreadsInternal[thread.Id] = thread;
+ await this._threadUpdated.InvokeAsync(this, updateEvent).ConfigureAwait(false);
}
- await this._threadUpdated.InvokeAsync(this, updateEvent).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Handles the thread delete event.
- /// </summary>
- /// <param name="thread">The deleted thread.</param>
- internal async Task OnThreadDeleteEventAsync(DiscordThreadChannel thread)
- {
- if (thread == null)
- return;
-
- thread.Discord = this;
+ /// <summary>
+ /// Handles the thread delete event.
+ /// </summary>
+ /// <param name="thread">The deleted thread.</param>
+ internal async Task OnThreadDeleteEventAsync(DiscordThreadChannel thread)
+ {
+ if (thread == null)
+ return;
- var gld = thread.Guild;
- if (gld.ThreadsInternal.TryRemove(thread.Id, out var cachedThread))
- thread = cachedThread;
+ thread.Discord = this;
- await this._threadDeleted.InvokeAsync(this, new ThreadDeleteEventArgs(this.ServiceProvider) { Thread = thread, Guild = thread.Guild, Parent = thread.Parent, Type = thread.Type }).ConfigureAwait(false);
- }
+ var gld = thread.Guild;
+ if (gld.ThreadsInternal.TryRemove(thread.Id, out var cachedThread))
+ thread = cachedThread;
- /// <summary>
- /// Handles the thread list sync event.
- /// </summary>
- /// <param name="guild">The synced guild.</param>
- /// <param name="channelIds">The synced channel ids.</param>
- /// <param name="threads">The synced threads.</param>
- /// <param name="members">The synced thread members.</param>
- internal async Task OnThreadListSyncEventAsync(DiscordGuild guild, IReadOnlyList<ulong?> channelIds, IReadOnlyList<DiscordThreadChannel> threads, IReadOnlyList<DiscordThreadChannelMember> members)
- {
- guild.Discord = this;
+ await this._threadDeleted.InvokeAsync(this, new ThreadDeleteEventArgs(this.ServiceProvider) { Thread = thread, Guild = thread.Guild, Parent = thread.Parent, Type = thread.Type }).ConfigureAwait(false);
+ }
- var channels = channelIds.Select(x => guild.GetChannel(x.Value)); //getting channel objects
- foreach (var chan in channels)
+ /// <summary>
+ /// Handles the thread list sync event.
+ /// </summary>
+ /// <param name="guild">The synced guild.</param>
+ /// <param name="channelIds">The synced channel ids.</param>
+ /// <param name="threads">The synced threads.</param>
+ /// <param name="members">The synced thread members.</param>
+ internal async Task OnThreadListSyncEventAsync(DiscordGuild guild, IReadOnlyList<ulong?> channelIds, IReadOnlyList<DiscordThreadChannel> threads, IReadOnlyList<DiscordThreadChannelMember> members)
{
- chan.Discord = this;
- }
- threads.Select(x => x.Discord = this);
+ guild.Discord = this;
- await this._threadListSynced.InvokeAsync(this, new ThreadListSyncEventArgs(this.ServiceProvider) { Guild = guild, Channels = channels.ToList().AsReadOnly(), Threads = threads, Members = members.ToList().AsReadOnly() }).ConfigureAwait(false);
- }
+ var channels = channelIds.Select(x => guild.GetChannel(x.Value)); //getting channel objects
+ foreach (var chan in channels)
+ {
+ chan.Discord = this;
+ }
+ threads.Select(x => x.Discord = this);
- /// <summary>
- /// Handles the thread member update event.
- /// </summary>
- /// <param name="member">The updated member.</param>
- internal async Task OnThreadMemberUpdateEventAsync(DiscordThreadChannelMember member)
- {
- member.Discord = this;
- var thread = this.InternalGetCachedThread(member.Id);
- if (thread == null)
- {
- var tempThread = await this.ApiClient.GetThreadAsync(member.Id);
- thread = this.GuildsInternal[member.GuildId].ThreadsInternal.AddOrUpdate(member.Id, tempThread, (old, newThread) => newThread);
+ await this._threadListSynced.InvokeAsync(this, new ThreadListSyncEventArgs(this.ServiceProvider) { Guild = guild, Channels = channels.ToList().AsReadOnly(), Threads = threads, Members = members.ToList().AsReadOnly() }).ConfigureAwait(false);
}
- thread.CurrentMember = member;
- thread.Guild.ThreadsInternal.AddOrUpdate(member.Id, thread, (oldThread, newThread) => newThread);
+ /// <summary>
+ /// Handles the thread member update event.
+ /// </summary>
+ /// <param name="member">The updated member.</param>
+ internal async Task OnThreadMemberUpdateEventAsync(DiscordThreadChannelMember member)
+ {
+ member.Discord = this;
+ var thread = this.InternalGetCachedThread(member.Id);
+ if (thread == null)
+ {
+ var tempThread = await this.ApiClient.GetThreadAsync(member.Id);
+ thread = this.GuildsInternal[member.GuildId].ThreadsInternal.AddOrUpdate(member.Id, tempThread, (old, newThread) => newThread);
+ }
+ thread.CurrentMember = member;
+ thread.Guild.ThreadsInternal.AddOrUpdate(member.Id, thread, (oldThread, newThread) => newThread);
- await this._threadMemberUpdated.InvokeAsync(this, new ThreadMemberUpdateEventArgs(this.ServiceProvider) { ThreadMember = member, Thread = thread }).ConfigureAwait(false);
- }
- /// <summary>
- /// Handles the thread members update event.
- /// </summary>
- /// <param name="guild">The target guild.</param>
- /// <param name="threadId">The thread id of the target thread this update belongs to.</param>
- /// <param name="membersAdded">The added members.</param>
- /// <param name="membersRemoved">The ids of the removed members.</param>
- /// <param name="memberCount">The new member count.</param>
- internal async Task OnThreadMembersUpdateEventAsync(DiscordGuild guild, ulong threadId, JArray membersAdded, JArray membersRemoved, int memberCount)
- {
- var thread = this.InternalGetCachedThread(threadId);
- if (thread == null)
- {
- var tempThread = await this.ApiClient.GetThreadAsync(threadId);
- thread = guild.ThreadsInternal.AddOrUpdate(threadId, tempThread, (old, newThread) => newThread);
+ await this._threadMemberUpdated.InvokeAsync(this, new ThreadMemberUpdateEventArgs(this.ServiceProvider) { ThreadMember = member, Thread = thread }).ConfigureAwait(false);
}
- thread.Discord = this;
- guild.Discord = this;
- List<DiscordThreadChannelMember> addedMembers = new();
- List<ulong> removedMemberIds = new();
-
- if (membersAdded != null)
- {
- foreach (var xj in membersAdded)
+ /// <summary>
+ /// Handles the thread members update event.
+ /// </summary>
+ /// <param name="guild">The target guild.</param>
+ /// <param name="threadId">The thread id of the target thread this update belongs to.</param>
+ /// <param name="membersAdded">The added members.</param>
+ /// <param name="membersRemoved">The ids of the removed members.</param>
+ /// <param name="memberCount">The new member count.</param>
+ internal async Task OnThreadMembersUpdateEventAsync(DiscordGuild guild, ulong threadId, JArray membersAdded, JArray membersRemoved, int memberCount)
+ {
+ var thread = this.InternalGetCachedThread(threadId);
+ if (thread == null)
{
- var xtm = xj.ToDiscordObject<DiscordThreadChannelMember>();
- xtm.Discord = this;
- xtm.GuildId = guild.Id;
- if (xtm != null)
- addedMembers.Add(xtm);
+ var tempThread = await this.ApiClient.GetThreadAsync(threadId);
+ thread = guild.ThreadsInternal.AddOrUpdate(threadId, tempThread, (old, newThread) => newThread);
+ }
+
+ thread.Discord = this;
+ guild.Discord = this;
+ List<DiscordThreadChannelMember> addedMembers = new();
+ List<ulong> removedMemberIds = new();
- if (xtm.Id == this.CurrentUser.Id)
- thread.CurrentMember = xtm;
+ if (membersAdded != null)
+ {
+ foreach (var xj in membersAdded)
+ {
+ var xtm = xj.ToDiscordObject<DiscordThreadChannelMember>();
+ xtm.Discord = this;
+ xtm.GuildId = guild.Id;
+ if (xtm != null)
+ addedMembers.Add(xtm);
+
+ if (xtm.Id == this.CurrentUser.Id)
+ thread.CurrentMember = xtm;
+ }
}
- }
- var removedMembers = new List<DiscordMember>();
- if (membersRemoved != null)
- {
- foreach (var removedId in membersRemoved)
+ var removedMembers = new List<DiscordMember>();
+ if (membersRemoved != null)
{
- removedMembers.Add(guild.MembersInternal.TryGetValue((ulong)removedId, out var member) ? member : new DiscordMember { Id = (ulong)removedId, GuildId = guild.Id, Discord = this });
+ foreach (var removedId in membersRemoved)
+ {
+ removedMembers.Add(guild.MembersInternal.TryGetValue((ulong)removedId, out var member) ? member : new DiscordMember { Id = (ulong)removedId, GuildId = guild.Id, Discord = this });
+ }
}
- }
- if (removedMemberIds.Contains(this.CurrentUser.Id)) //indicates the bot was removed from the thread
- thread.CurrentMember = null;
+ if (removedMemberIds.Contains(this.CurrentUser.Id)) //indicates the bot was removed from the thread
+ thread.CurrentMember = null;
- thread.MemberCount = memberCount;
+ thread.MemberCount = memberCount;
- var threadMembersUpdateArg = new ThreadMembersUpdateEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- Thread = thread,
- AddedMembers = addedMembers,
- RemovedMembers = removedMembers,
- MemberCount = memberCount
- };
+ var threadMembersUpdateArg = new ThreadMembersUpdateEventArgs(this.ServiceProvider)
+ {
+ Guild = guild,
+ Thread = thread,
+ AddedMembers = addedMembers,
+ RemovedMembers = removedMembers,
+ MemberCount = memberCount
+ };
- await this._threadMembersUpdated.InvokeAsync(this, threadMembersUpdateArg).ConfigureAwait(false);
- }
+ await this._threadMembersUpdated.InvokeAsync(this, threadMembersUpdateArg).ConfigureAwait(false);
+ }
- #endregion
+ #endregion
- #region Activities
- /// <summary>
- /// Dispatches the <see cref="EmbeddedActivityUpdated"/> event.
- /// </summary>
- /// <param name="trActivity">The transport activity.</param>
- /// <param name="guild">The guild.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="jUsers">The users in the activity.</param>
- /// <param name="appId">The application id.</param>
- internal async Task OnEmbeddedActivityUpdateAsync(JObject trActivity, DiscordGuild guild, ulong channelId, JArray jUsers, ulong appId)
- => await Task.Delay(20);
-
- /*{
+ #region Activities
+ /// <summary>
+ /// Dispatches the <see cref="EmbeddedActivityUpdated"/> event.
+ /// </summary>
+ /// <param name="trActivity">The transport activity.</param>
+ /// <param name="guild">The guild.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="jUsers">The users in the activity.</param>
+ /// <param name="appId">The application id.</param>
+ internal async Task OnEmbeddedActivityUpdateAsync(JObject trActivity, DiscordGuild guild, ulong channelId, JArray jUsers, ulong appId)
+ => await Task.Delay(20);
+
+ /*{
try
{
var users = j_users?.ToObject<List<ulong>>();
DiscordActivity old = null;
var uid = $"{guild.Id}_{channel_id}_{app_id}";
if (this._embeddedActivities.TryGetValue(uid, out var activity))
{
old = new DiscordActivity(activity);
DiscordJson.PopulateObject(tr_activity, activity);
}
else
{
activity = tr_activity.ToObject<DiscordActivity>();
this._embeddedActivities[uid] = activity;
}
var activity_users = new List<DiscordMember>();
var channel = this.InternalGetCachedChannel(channel_id) ?? await this.ApiClient.GetChannelAsync(channel_id);
if (users != null)
{
foreach (var user in users)
{
var activity_user = guild._members.TryGetValue(user, out var member) ? member : new DiscordMember { Id = user, _guild_id = guild.Id, Discord = this };
activity_users.Add(activity_user);
}
}
else
activity_users = null;
var ea = new EmbeddedActivityUpdateEventArgs(this.ServiceProvider)
{
Guild = guild,
Users = activity_users,
Channel = channel
};
await this._embeddedActivityUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
} catch (Exception ex)
{
this.Logger.LogError(ex, ex.Message);
}
}*/
- #endregion
+ #endregion
+
+ #region User/Presence Update
+
+ /// <summary>
+ /// Handles the presence update event.
+ /// </summary>
+ /// <param name="rawPresence">The raw presence.</param>
+ /// <param name="rawUser">The raw user.</param>
+
+ internal async Task OnPresenceUpdateEventAsync(JObject rawPresence, JObject rawUser)
+ {
+ var uid = (ulong)rawUser["id"];
+ DiscordPresence old = null;
+
+ if (this.PresencesInternal.TryGetValue(uid, out var presence))
+ {
+ old = new DiscordPresence(presence);
+ DiscordJson.PopulateObject(rawPresence, presence);
+ }
+ else
+ {
+ presence = rawPresence.ToObject<DiscordPresence>();
+ presence.Discord = this;
+ presence.Activity = new DiscordActivity(presence.RawActivity);
+ this.PresencesInternal[presence.InternalUser.Id] = presence;
+ }
+
+ // reuse arrays / avoid linq (this is a hot zone)
+ if (presence.Activities == null || rawPresence["activities"] == null)
+ {
+ presence.InternalActivities = Array.Empty<DiscordActivity>();
+ }
+ else
+ {
+ if (presence.InternalActivities.Length != presence.RawActivities.Length)
+ presence.InternalActivities = new DiscordActivity[presence.RawActivities.Length];
+
+ for (var i = 0; i < presence.InternalActivities.Length; i++)
+ presence.InternalActivities[i] = new DiscordActivity(presence.RawActivities[i]);
+
+ if (presence.InternalActivities.Length > 0)
+ {
+ presence.RawActivity = presence.RawActivities[0];
+
+ if (presence.Activity != null)
+ presence.Activity.UpdateWith(presence.RawActivity);
+ else
+ presence.Activity = new DiscordActivity(presence.RawActivity);
+ }
+ }
- #region User/Presence Update
+ if (this.UserCache.TryGetValue(uid, out var usr))
+ {
+ if (old != null)
+ {
+ old.InternalUser.Username = usr.Username;
+ old.InternalUser.Discriminator = usr.Discriminator;
+ old.InternalUser.AvatarHash = usr.AvatarHash;
+ }
- /// <summary>
- /// Handles the presence update event.
- /// </summary>
- /// <param name="rawPresence">The raw presence.</param>
- /// <param name="rawUser">The raw user.</param>
+ if (rawUser["username"] is object)
+ usr.Username = (string)rawUser["username"];
+ if (rawUser["discriminator"] is object)
+ usr.Discriminator = (string)rawUser["discriminator"];
+ if (rawUser["avatar"] is object)
+ usr.AvatarHash = (string)rawUser["avatar"];
- internal async Task OnPresenceUpdateEventAsync(JObject rawPresence, JObject rawUser)
- {
- var uid = (ulong)rawUser["id"];
- DiscordPresence old = null;
+ presence.InternalUser.Username = usr.Username;
+ presence.InternalUser.Discriminator = usr.Discriminator;
+ presence.InternalUser.AvatarHash = usr.AvatarHash;
+ }
- if (this.PresencesInternal.TryGetValue(uid, out var presence))
- {
- old = new DiscordPresence(presence);
- DiscordJson.PopulateObject(rawPresence, presence);
- }
- else
- {
- presence = rawPresence.ToObject<DiscordPresence>();
- presence.Discord = this;
- presence.Activity = new DiscordActivity(presence.RawActivity);
- this.PresencesInternal[presence.InternalUser.Id] = presence;
+ var usrafter = usr ?? new DiscordUser(presence.InternalUser);
+ var ea = new PresenceUpdateEventArgs(this.ServiceProvider)
+ {
+ Status = presence.Status,
+ Activity = presence.Activity,
+ User = usr,
+ PresenceBefore = old,
+ PresenceAfter = presence,
+ UserBefore = old != null ? new DiscordUser(old.InternalUser) : usrafter,
+ UserAfter = usrafter
+ };
+ await this._presenceUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- // reuse arrays / avoid linq (this is a hot zone)
- if (presence.Activities == null || rawPresence["activities"] == null)
- {
- presence.InternalActivities = Array.Empty<DiscordActivity>();
- }
- else
- {
- if (presence.InternalActivities.Length != presence.RawActivities.Length)
- presence.InternalActivities = new DiscordActivity[presence.RawActivities.Length];
+ /// <summary>
+ /// Handles the user settings update event.
+ /// </summary>
+ /// <param name="user">The transport user.</param>
- for (var i = 0; i < presence.InternalActivities.Length; i++)
- presence.InternalActivities[i] = new DiscordActivity(presence.RawActivities[i]);
+ internal async Task OnUserSettingsUpdateEventAsync(TransportUser user)
+ {
+ var usr = new DiscordUser(user) { Discord = this };
- if (presence.InternalActivities.Length > 0)
+ var ea = new UserSettingsUpdateEventArgs(this.ServiceProvider)
{
- presence.RawActivity = presence.RawActivities[0];
-
- if (presence.Activity != null)
- presence.Activity.UpdateWith(presence.RawActivity);
- else
- presence.Activity = new DiscordActivity(presence.RawActivity);
- }
+ User = usr
+ };
+ await this._userSettingsUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- if (this.UserCache.TryGetValue(uid, out var usr))
+ /// <summary>
+ /// Handles the user update event.
+ /// </summary>
+ /// <param name="user">The transport user.</param>
+
+ internal async Task OnUserUpdateEventAsync(TransportUser user)
{
- if (old != null)
+ var usrOld = new DiscordUser
{
- old.InternalUser.Username = usr.Username;
- old.InternalUser.Discriminator = usr.Discriminator;
- old.InternalUser.AvatarHash = usr.AvatarHash;
- }
+ AvatarHash = this.CurrentUser.AvatarHash,
+ Discord = this,
+ Discriminator = this.CurrentUser.Discriminator,
+ Email = this.CurrentUser.Email,
+ Id = this.CurrentUser.Id,
+ IsBot = this.CurrentUser.IsBot,
+ MfaEnabled = this.CurrentUser.MfaEnabled,
+ Username = this.CurrentUser.Username,
+ Verified = this.CurrentUser.Verified
+ };
- if (rawUser["username"] is object)
- usr.Username = (string)rawUser["username"];
- if (rawUser["discriminator"] is object)
- usr.Discriminator = (string)rawUser["discriminator"];
- if (rawUser["avatar"] is object)
- usr.AvatarHash = (string)rawUser["avatar"];
+ this.CurrentUser.AvatarHash = user.AvatarHash;
+ this.CurrentUser.Discriminator = user.Discriminator;
+ this.CurrentUser.Email = user.Email;
+ this.CurrentUser.Id = user.Id;
+ this.CurrentUser.IsBot = user.IsBot;
+ this.CurrentUser.MfaEnabled = user.MfaEnabled;
+ this.CurrentUser.Username = user.Username;
+ this.CurrentUser.Verified = user.Verified;
- presence.InternalUser.Username = usr.Username;
- presence.InternalUser.Discriminator = usr.Discriminator;
- presence.InternalUser.AvatarHash = usr.AvatarHash;
+ var ea = new UserUpdateEventArgs(this.ServiceProvider)
+ {
+ UserAfter = this.CurrentUser,
+ UserBefore = usrOld
+ };
+ await this._userUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- var usrafter = usr ?? new DiscordUser(presence.InternalUser);
- var ea = new PresenceUpdateEventArgs(this.ServiceProvider)
- {
- Status = presence.Status,
- Activity = presence.Activity,
- User = usr,
- PresenceBefore = old,
- PresenceAfter = presence,
- UserBefore = old != null ? new DiscordUser(old.InternalUser) : usrafter,
- UserAfter = usrafter
- };
- await this._presenceUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ #endregion
- /// <summary>
- /// Handles the user settings update event.
- /// </summary>
- /// <param name="user">The transport user.</param>
+ #region Voice
- internal async Task OnUserSettingsUpdateEventAsync(TransportUser user)
- {
- var usr = new DiscordUser(user) { Discord = this };
+ /// <summary>
+ /// Handles the voice state update event.
+ /// </summary>
+ /// <param name="raw">The raw voice state update object.</param>
- var ea = new UserSettingsUpdateEventArgs(this.ServiceProvider)
+ internal async Task OnVoiceStateUpdateEventAsync(JObject raw)
{
- User = usr
- };
- await this._userSettingsUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Handles the user update event.
- /// </summary>
- /// <param name="user">The transport user.</param>
-
- internal async Task OnUserUpdateEventAsync(TransportUser user)
- {
- var usrOld = new DiscordUser
- {
- AvatarHash = this.CurrentUser.AvatarHash,
- Discord = this,
- Discriminator = this.CurrentUser.Discriminator,
- Email = this.CurrentUser.Email,
- Id = this.CurrentUser.Id,
- IsBot = this.CurrentUser.IsBot,
- MfaEnabled = this.CurrentUser.MfaEnabled,
- Username = this.CurrentUser.Username,
- Verified = this.CurrentUser.Verified
- };
-
- this.CurrentUser.AvatarHash = user.AvatarHash;
- this.CurrentUser.Discriminator = user.Discriminator;
- this.CurrentUser.Email = user.Email;
- this.CurrentUser.Id = user.Id;
- this.CurrentUser.IsBot = user.IsBot;
- this.CurrentUser.MfaEnabled = user.MfaEnabled;
- this.CurrentUser.Username = user.Username;
- this.CurrentUser.Verified = user.Verified;
-
- var ea = new UserUpdateEventArgs(this.ServiceProvider)
- {
- UserAfter = this.CurrentUser,
- UserBefore = usrOld
- };
- await this._userUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ var gid = (ulong)raw["guild_id"];
+ var uid = (ulong)raw["user_id"];
+ var gld = this.GuildsInternal[gid];
- #endregion
+ var vstateNew = raw.ToObject<DiscordVoiceState>();
+ vstateNew.Discord = this;
- #region Voice
+ gld.VoiceStatesInternal.TryRemove(uid, out var vstateOld);
- /// <summary>
- /// Handles the voice state update event.
- /// </summary>
- /// <param name="raw">The raw voice state update object.</param>
-
- internal async Task OnVoiceStateUpdateEventAsync(JObject raw)
- {
- var gid = (ulong)raw["guild_id"];
- var uid = (ulong)raw["user_id"];
- var gld = this.GuildsInternal[gid];
+ if (vstateNew.Channel != null)
+ {
+ gld.VoiceStatesInternal[vstateNew.UserId] = vstateNew;
+ }
- var vstateNew = raw.ToObject<DiscordVoiceState>();
- vstateNew.Discord = this;
+ if (gld.MembersInternal.TryGetValue(uid, out var mbr))
+ {
+ mbr.IsMuted = vstateNew.IsServerMuted;
+ mbr.IsDeafened = vstateNew.IsServerDeafened;
+ }
+ else
+ {
+ var transportMbr = vstateNew.TransportMember;
+ this.UpdateUser(new DiscordUser(transportMbr.User) { Discord = this }, gid, gld, transportMbr);
+ }
- gld.VoiceStatesInternal.TryRemove(uid, out var vstateOld);
+ var ea = new VoiceStateUpdateEventArgs(this.ServiceProvider)
+ {
+ Guild = vstateNew.Guild,
+ Channel = vstateNew.Channel,
+ User = vstateNew.User,
+ SessionId = vstateNew.SessionId,
- if (vstateNew.Channel != null)
- {
- gld.VoiceStatesInternal[vstateNew.UserId] = vstateNew;
+ Before = vstateOld,
+ After = vstateNew
+ };
+ await this._voiceStateUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- if (gld.MembersInternal.TryGetValue(uid, out var mbr))
- {
- mbr.IsMuted = vstateNew.IsServerMuted;
- mbr.IsDeafened = vstateNew.IsServerDeafened;
- }
- else
+ /// <summary>
+ /// Handles the voice server update event.
+ /// </summary>
+ /// <param name="endpoint">The new endpoint.</param>
+ /// <param name="token">The new token.</param>
+ /// <param name="guild">The guild.</param>
+
+ internal async Task OnVoiceServerUpdateEventAsync(string endpoint, string token, DiscordGuild guild)
{
- var transportMbr = vstateNew.TransportMember;
- this.UpdateUser(new DiscordUser(transportMbr.User) { Discord = this }, gid, gld, transportMbr);
+ var ea = new VoiceServerUpdateEventArgs(this.ServiceProvider)
+ {
+ Endpoint = endpoint,
+ VoiceToken = token,
+ Guild = guild
+ };
+ await this._voiceServerUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- var ea = new VoiceStateUpdateEventArgs(this.ServiceProvider)
- {
- Guild = vstateNew.Guild,
- Channel = vstateNew.Channel,
- User = vstateNew.User,
- SessionId = vstateNew.SessionId,
+ #endregion
- Before = vstateOld,
- After = vstateNew
- };
- await this._voiceStateUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ #region Commands
- /// <summary>
- /// Handles the voice server update event.
- /// </summary>
- /// <param name="endpoint">The new endpoint.</param>
- /// <param name="token">The new token.</param>
- /// <param name="guild">The guild.</param>
+ /// <summary>
+ /// Handles the application command create event.
+ /// </summary>
+ /// <param name="cmd">The application command.</param>
+ /// <param name="guildId">The optional guild id.</param>
- internal async Task OnVoiceServerUpdateEventAsync(string endpoint, string token, DiscordGuild guild)
- {
- var ea = new VoiceServerUpdateEventArgs(this.ServiceProvider)
+ internal async Task OnApplicationCommandCreateAsync(DiscordApplicationCommand cmd, ulong? guildId)
{
- Endpoint = endpoint,
- VoiceToken = token,
- Guild = guild
- };
- await this._voiceServerUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
-
- #endregion
-
- #region Commands
+ cmd.Discord = this;
- /// <summary>
- /// Handles the application command create event.
- /// </summary>
- /// <param name="cmd">The application command.</param>
- /// <param name="guildId">The optional guild id.</param>
-
- internal async Task OnApplicationCommandCreateAsync(DiscordApplicationCommand cmd, ulong? guildId)
- {
- cmd.Discord = this;
+ var guild = this.InternalGetCachedGuild(guildId);
- var guild = this.InternalGetCachedGuild(guildId);
+ if (guild == null && guildId.HasValue)
+ {
+ guild = new DiscordGuild
+ {
+ Id = guildId.Value,
+ Discord = this
+ };
+ }
- if (guild == null && guildId.HasValue)
- {
- guild = new DiscordGuild
+ var ea = new ApplicationCommandEventArgs(this.ServiceProvider)
{
- Id = guildId.Value,
- Discord = this
+ Guild = guild,
+ Command = cmd
};
- }
- var ea = new ApplicationCommandEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- Command = cmd
- };
+ await this._applicationCommandCreated.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- await this._applicationCommandCreated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Handles the application command update event.
+ /// </summary>
+ /// <param name="cmd">The application command.</param>
+ /// <param name="guildId">The optional guild id.</param>
- /// <summary>
- /// Handles the application command update event.
- /// </summary>
- /// <param name="cmd">The application command.</param>
- /// <param name="guildId">The optional guild id.</param>
+ internal async Task OnApplicationCommandUpdateAsync(DiscordApplicationCommand cmd, ulong? guildId)
+ {
+ cmd.Discord = this;
- internal async Task OnApplicationCommandUpdateAsync(DiscordApplicationCommand cmd, ulong? guildId)
- {
- cmd.Discord = this;
+ var guild = this.InternalGetCachedGuild(guildId);
- var guild = this.InternalGetCachedGuild(guildId);
+ if (guild == null && guildId.HasValue)
+ {
+ guild = new DiscordGuild
+ {
+ Id = guildId.Value,
+ Discord = this
+ };
+ }
- if (guild == null && guildId.HasValue)
- {
- guild = new DiscordGuild
+ var ea = new ApplicationCommandEventArgs(this.ServiceProvider)
{
- Id = guildId.Value,
- Discord = this
+ Guild = guild,
+ Command = cmd
};
- }
- var ea = new ApplicationCommandEventArgs(this.ServiceProvider)
- {
- Guild = guild,
- Command = cmd
- };
+ await this._applicationCommandUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- await this._applicationCommandUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Handles the application command delete event.
+ /// </summary>
+ /// <param name="cmd">The application command.</param>
+ /// <param name="guildId">The optional guild id.</param>
- /// <summary>
- /// Handles the application command delete event.
- /// </summary>
- /// <param name="cmd">The application command.</param>
- /// <param name="guildId">The optional guild id.</param>
+ internal async Task OnApplicationCommandDeleteAsync(DiscordApplicationCommand cmd, ulong? guildId)
+ {
+ cmd.Discord = this;
- internal async Task OnApplicationCommandDeleteAsync(DiscordApplicationCommand cmd, ulong? guildId)
- {
- cmd.Discord = this;
+ var guild = this.InternalGetCachedGuild(guildId);
- var guild = this.InternalGetCachedGuild(guildId);
+ if (guild == null && guildId.HasValue)
+ {
+ guild = new DiscordGuild
+ {
+ Id = guildId.Value,
+ Discord = this
+ };
+ }
- if (guild == null && guildId.HasValue)
- {
- guild = new DiscordGuild
+ var ea = new ApplicationCommandEventArgs(this.ServiceProvider)
{
- Id = guildId.Value,
- Discord = this
+ Guild = guild,
+ Command = cmd
};
+
+ await this._applicationCommandDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
}
- var ea = new ApplicationCommandEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the guild application command counts update event.
+ /// </summary>
+ /// <param name="chatInputCommandCount">The <see cref="ApplicationCommandType.ChatInput"/> count.</param>
+ /// <param name="userContextMenuCommandCount">The <see cref="ApplicationCommandType.User"/> count.</param>
+ /// <param name="messageContextMenuCount">The <see cref="ApplicationCommandType.Message"/> count.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <returns>Count of application commands.</returns>
+ internal async Task OnGuildApplicationCommandCountsUpdateAsync(int chatInputCommandCount, int userContextMenuCommandCount, int messageContextMenuCount, ulong guildId)
{
- Guild = guild,
- Command = cmd
- };
-
- await this._applicationCommandDeleted.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ var guild = this.InternalGetCachedGuild(guildId);
- /// <summary>
- /// Handles the guild application command counts update event.
- /// </summary>
- /// <param name="chatInputCommandCount">The <see cref="ApplicationCommandType.ChatInput"/> count.</param>
- /// <param name="userContextMenuCommandCount">The <see cref="ApplicationCommandType.User"/> count.</param>
- /// <param name="messageContextMenuCount">The <see cref="ApplicationCommandType.Message"/> count.</param>
- /// <param name="guildId">The guild id.</param>
- /// <returns>Count of application commands.</returns>
- internal async Task OnGuildApplicationCommandCountsUpdateAsync(int chatInputCommandCount, int userContextMenuCommandCount, int messageContextMenuCount, ulong guildId)
- {
- var guild = this.InternalGetCachedGuild(guildId);
+ if (guild == null)
+ {
+ guild = new DiscordGuild
+ {
+ Id = guildId,
+ Discord = this
+ };
+ }
- if (guild == null)
- {
- guild = new DiscordGuild
+ var ea = new GuildApplicationCommandCountEventArgs(this.ServiceProvider)
{
- Id = guildId,
- Discord = this
+ SlashCommands = chatInputCommandCount,
+ UserContextMenuCommands = userContextMenuCommandCount,
+ MessageContextMenuCommands = messageContextMenuCount,
+ Guild = guild
};
+
+ await this._guildApplicationCommandCountUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
}
- var ea = new GuildApplicationCommandCountEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the application command permissions update event.
+ /// </summary>
+ /// <param name="perms">The new permissions.</param>
+ /// <param name="channelId">The command id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="applicationId">The application id.</param>
+ internal async Task OnApplicationCommandPermissionsUpdateAsync(IEnumerable<DiscordApplicationCommandPermission> perms, ulong channelId, ulong guildId, ulong applicationId)
{
- SlashCommands = chatInputCommandCount,
- UserContextMenuCommands = userContextMenuCommandCount,
- MessageContextMenuCommands = messageContextMenuCount,
- Guild = guild
- };
-
- await this._guildApplicationCommandCountUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ if (applicationId != this.CurrentApplication.Id)
+ return;
- /// <summary>
- /// Handles the application command permissions update event.
- /// </summary>
- /// <param name="perms">The new permissions.</param>
- /// <param name="channelId">The command id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="applicationId">The application id.</param>
- internal async Task OnApplicationCommandPermissionsUpdateAsync(IEnumerable<DiscordApplicationCommandPermission> perms, ulong channelId, ulong guildId, ulong applicationId)
- {
- if (applicationId != this.CurrentApplication.Id)
- return;
+ var guild = this.InternalGetCachedGuild(guildId);
- var guild = this.InternalGetCachedGuild(guildId);
+ DiscordApplicationCommand cmd;
+ try
+ {
+ cmd = await this.GetGuildApplicationCommandAsync(guildId, channelId);
+ }
+ catch (NotFoundException)
+ {
+ cmd = await this.GetGlobalApplicationCommandAsync(channelId);
+ }
- DiscordApplicationCommand cmd;
- try
- {
- cmd = await this.GetGuildApplicationCommandAsync(guildId, channelId);
- }
- catch (NotFoundException)
- {
- cmd = await this.GetGlobalApplicationCommandAsync(channelId);
- }
+ if (guild == null)
+ {
+ guild = new DiscordGuild
+ {
+ Id = guildId,
+ Discord = this
+ };
+ }
- if (guild == null)
- {
- guild = new DiscordGuild
+ var ea = new ApplicationCommandPermissionsUpdateEventArgs(this.ServiceProvider)
{
- Id = guildId,
- Discord = this
+ Permissions = perms.ToList(),
+ Command = cmd,
+ ApplicationId = applicationId,
+ Guild = guild
};
- }
-
- var ea = new ApplicationCommandPermissionsUpdateEventArgs(this.ServiceProvider)
- {
- Permissions = perms.ToList(),
- Command = cmd,
- ApplicationId = applicationId,
- Guild = guild
- };
- await this._applicationCommandPermissionsUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ await this._applicationCommandPermissionsUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- #endregion
+ #endregion
- #region Interaction
+ #region Interaction
- /// <summary>
- /// Handles the interaction create event.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="user">The transport user.</param>
- /// <param name="member">The transport member.</param>
- /// <param name="interaction">The interaction.</param>
- internal async Task OnInteractionCreateAsync(ulong? guildId, ulong channelId, TransportUser user, TransportMember member, DiscordInteraction interaction)
- {
- var usr = new DiscordUser(user) { Discord = this };
+ /// <summary>
+ /// Handles the interaction create event.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="user">The transport user.</param>
+ /// <param name="member">The transport member.</param>
+ /// <param name="interaction">The interaction.</param>
+ internal async Task OnInteractionCreateAsync(ulong? guildId, ulong channelId, TransportUser user, TransportMember member, DiscordInteraction interaction)
+ {
+ var usr = new DiscordUser(user) { Discord = this };
- interaction.ChannelId = channelId;
- interaction.GuildId = guildId;
- interaction.Discord = this;
- interaction.Data.Discord = this;
+ interaction.ChannelId = channelId;
+ interaction.GuildId = guildId;
+ interaction.Discord = this;
+ interaction.Data.Discord = this;
- if (member != null)
- {
- usr = new DiscordMember(member) { GuildId = guildId.Value, Discord = this };
- this.UpdateUser(usr, guildId, interaction.Guild, member);
- }
- else
- {
- this.UserCache.AddOrUpdate(usr.Id, usr, (old, @new) => @new);
- }
+ if (member != null)
+ {
+ usr = new DiscordMember(member) { GuildId = guildId.Value, Discord = this };
+ this.UpdateUser(usr, guildId, interaction.Guild, member);
+ }
+ else
+ {
+ this.UserCache.AddOrUpdate(usr.Id, usr, (old, @new) => @new);
+ }
- interaction.User = usr;
+ interaction.User = usr;
- var resolved = interaction.Data.Resolved;
- if (resolved != null)
- {
- if (resolved.Users != null)
+ var resolved = interaction.Data.Resolved;
+ if (resolved != null)
{
- foreach (var c in resolved.Users)
+ if (resolved.Users != null)
{
- c.Value.Discord = this;
- this.UserCache.AddOrUpdate(c.Value.Id, c.Value, (old, @new) => @new);
+ foreach (var c in resolved.Users)
+ {
+ c.Value.Discord = this;
+ this.UserCache.AddOrUpdate(c.Value.Id, c.Value, (old, @new) => @new);
+ }
}
- }
- if (resolved.Members != null)
- {
- foreach (var c in resolved.Members)
+ if (resolved.Members != null)
{
- c.Value.Discord = this;
- c.Value.Id = c.Key;
- c.Value.GuildId = guildId.Value;
- c.Value.User.Discord = this;
- this.UserCache.AddOrUpdate(c.Value.User.Id, c.Value.User, (old, @new) => @new);
+ foreach (var c in resolved.Members)
+ {
+ c.Value.Discord = this;
+ c.Value.Id = c.Key;
+ c.Value.GuildId = guildId.Value;
+ c.Value.User.Discord = this;
+ this.UserCache.AddOrUpdate(c.Value.User.Id, c.Value.User, (old, @new) => @new);
+ }
}
- }
- if (resolved.Channels != null)
- {
- foreach (var c in resolved.Channels)
+ if (resolved.Channels != null)
{
- c.Value.Discord = this;
+ foreach (var c in resolved.Channels)
+ {
+ c.Value.Discord = this;
- if (guildId.HasValue)
- c.Value.GuildId = guildId.Value;
+ if (guildId.HasValue)
+ c.Value.GuildId = guildId.Value;
+ }
}
- }
- if (resolved.Roles != null)
- {
- foreach (var c in resolved.Roles)
+ if (resolved.Roles != null)
{
- c.Value.Discord = this;
+ foreach (var c in resolved.Roles)
+ {
+ c.Value.Discord = this;
- if (guildId.HasValue)
- c.Value.GuildId = guildId.Value;
+ if (guildId.HasValue)
+ c.Value.GuildId = guildId.Value;
+ }
}
- }
- if (resolved.Messages != null)
- {
- foreach (var m in resolved.Messages)
+ if (resolved.Messages != null)
{
- m.Value.Discord = this;
+ foreach (var m in resolved.Messages)
+ {
+ m.Value.Discord = this;
- if (guildId.HasValue)
- m.Value.GuildId = guildId.Value;
+ if (guildId.HasValue)
+ m.Value.GuildId = guildId.Value;
+ }
}
- }
- if (resolved.Attachments != null)
- foreach (var a in resolved.Attachments)
- a.Value.Discord = this;
- }
-
- if (interaction.Type is InteractionType.Component || interaction.Type is InteractionType.ModalSubmit)
- {
- if (interaction.Message != null)
- {
- interaction.Message.Discord = this;
- interaction.Message.ChannelId = interaction.ChannelId;
+ if (resolved.Attachments != null)
+ foreach (var a in resolved.Attachments)
+ a.Value.Discord = this;
}
- var cea = new ComponentInteractionCreateEventArgs(this.ServiceProvider)
- {
- Message = interaction.Message,
- Interaction = interaction
- };
- await this._componentInteractionCreated.InvokeAsync(this, cea).ConfigureAwait(false);
- }
- else
- {
- if (interaction.Data.Target.HasValue) // Context-Menu. //
+ if (interaction.Type is InteractionType.Component || interaction.Type is InteractionType.ModalSubmit)
{
- var targetId = interaction.Data.Target.Value;
- DiscordUser targetUser = null;
- DiscordMember targetMember = null;
- DiscordMessage targetMessage = null;
-
- interaction.Data.Resolved.Messages?.TryGetValue(targetId, out targetMessage);
- interaction.Data.Resolved.Members?.TryGetValue(targetId, out targetMember);
- interaction.Data.Resolved.Users?.TryGetValue(targetId, out targetUser);
-
- var ctea = new ContextMenuInteractionCreateEventArgs(this.ServiceProvider)
+ if (interaction.Message != null)
{
- Interaction = interaction,
- TargetUser = targetMember ?? targetUser,
- TargetMessage = targetMessage,
- Type = interaction.Data.Type,
+ interaction.Message.Discord = this;
+ interaction.Message.ChannelId = interaction.ChannelId;
+ }
+ var cea = new ComponentInteractionCreateEventArgs(this.ServiceProvider)
+ {
+ Message = interaction.Message,
+ Interaction = interaction
};
- await this._contextMenuInteractionCreated.InvokeAsync(this, ctea).ConfigureAwait(false);
+
+ await this._componentInteractionCreated.InvokeAsync(this, cea).ConfigureAwait(false);
}
else
{
- var ea = new InteractionCreateEventArgs(this.ServiceProvider)
+ if (interaction.Data.Target.HasValue) // Context-Menu. //
{
- Interaction = interaction
- };
+ var targetId = interaction.Data.Target.Value;
+ DiscordUser targetUser = null;
+ DiscordMember targetMember = null;
+ DiscordMessage targetMessage = null;
+
+ interaction.Data.Resolved.Messages?.TryGetValue(targetId, out targetMessage);
+ interaction.Data.Resolved.Members?.TryGetValue(targetId, out targetMember);
+ interaction.Data.Resolved.Users?.TryGetValue(targetId, out targetUser);
+
+ var ctea = new ContextMenuInteractionCreateEventArgs(this.ServiceProvider)
+ {
+ Interaction = interaction,
+ TargetUser = targetMember ?? targetUser,
+ TargetMessage = targetMessage,
+ Type = interaction.Data.Type,
+ };
+ await this._contextMenuInteractionCreated.InvokeAsync(this, ctea).ConfigureAwait(false);
+ }
+ else
+ {
+ var ea = new InteractionCreateEventArgs(this.ServiceProvider)
+ {
+ Interaction = interaction
+ };
- await this._interactionCreated.InvokeAsync(this, ea).ConfigureAwait(false);
+ await this._interactionCreated.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
}
}
- }
- #endregion
+ #endregion
- #region Misc
+ #region Misc
- /// <summary>
- /// Handles the typing start event.
- /// </summary>
- /// <param name="userId">The user id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="channel">The channel.</param>
- /// <param name="guildId">The optional guild id.</param>
- /// <param name="started">The time when the user started typing.</param>
- /// <param name="mbr">The transport member.</param>
- internal async Task OnTypingStartEventAsync(ulong userId, ulong channelId, DiscordChannel channel, ulong? guildId, DateTimeOffset started, TransportMember mbr)
- {
- if (channel == null)
- {
- channel = new DiscordChannel
+ /// <summary>
+ /// Handles the typing start event.
+ /// </summary>
+ /// <param name="userId">The user id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="channel">The channel.</param>
+ /// <param name="guildId">The optional guild id.</param>
+ /// <param name="started">The time when the user started typing.</param>
+ /// <param name="mbr">The transport member.</param>
+ internal async Task OnTypingStartEventAsync(ulong userId, ulong channelId, DiscordChannel channel, ulong? guildId, DateTimeOffset started, TransportMember mbr)
+ {
+ if (channel == null)
{
- Discord = this,
- Id = channelId,
- GuildId = guildId ?? default,
+ channel = new DiscordChannel
+ {
+ Discord = this,
+ Id = channelId,
+ GuildId = guildId ?? default,
+ };
+ }
+
+ var guild = this.InternalGetCachedGuild(guildId);
+ var usr = this.UpdateUser(new DiscordUser { Id = userId, Discord = this }, guildId, guild, mbr);
+
+ var ea = new TypingStartEventArgs(this.ServiceProvider)
+ {
+ Channel = channel,
+ User = usr,
+ Guild = guild,
+ StartedAt = started
};
+ await this._typingStarted.InvokeAsync(this, ea).ConfigureAwait(false);
}
- var guild = this.InternalGetCachedGuild(guildId);
- var usr = this.UpdateUser(new DiscordUser { Id = userId, Discord = this }, guildId, guild, mbr);
-
- var ea = new TypingStartEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles the webhooks update.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ /// <param name="guild">The guild.</param>
+ internal async Task OnWebhooksUpdateAsync(DiscordChannel channel, DiscordGuild guild)
{
- Channel = channel,
- User = usr,
- Guild = guild,
- StartedAt = started
- };
- await this._typingStarted.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ var ea = new WebhooksUpdateEventArgs(this.ServiceProvider)
+ {
+ Channel = channel,
+ Guild = guild
+ };
+ await this._webhooksUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- /// <summary>
- /// Handles the webhooks update.
- /// </summary>
- /// <param name="channel">The channel.</param>
- /// <param name="guild">The guild.</param>
- internal async Task OnWebhooksUpdateAsync(DiscordChannel channel, DiscordGuild guild)
- {
- var ea = new WebhooksUpdateEventArgs(this.ServiceProvider)
+ /// <summary>
+ /// Handles all unknown events.
+ /// </summary>
+ /// <param name="payload">The payload.</param>
+ internal async Task OnUnknownEventAsync(GatewayPayload payload)
{
- Channel = channel,
- Guild = guild
- };
- await this._webhooksUpdated.InvokeAsync(this, ea).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Handles all unknown events.
- /// </summary>
- /// <param name="payload">The payload.</param>
- internal async Task OnUnknownEventAsync(GatewayPayload payload)
- {
- var ea = new UnknownEventArgs(this.ServiceProvider) { EventName = payload.EventName, Json = (payload.Data as JObject)?.ToString() };
- await this._unknownEvent.InvokeAsync(this, ea).ConfigureAwait(false);
- }
+ var ea = new UnknownEventArgs(this.ServiceProvider) { EventName = payload.EventName, Json = (payload.Data as JObject)?.ToString() };
+ await this._unknownEvent.InvokeAsync(this, ea).ConfigureAwait(false);
+ }
- #endregion
+ #endregion
- #endregion
+ #endregion
+ }
}
diff --git a/DisCatSharp/Clients/DiscordClient.EventHandlers.cs b/DisCatSharp/Clients/DiscordClient.EventHandlers.cs
index a5e712ca6..0e3280ac7 100644
--- a/DisCatSharp/Clients/DiscordClient.EventHandlers.cs
+++ b/DisCatSharp/Clients/DiscordClient.EventHandlers.cs
@@ -1,201 +1,202 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp;
-
-/// <summary>
-/// A Discord API wrapper.
-/// </summary>
-public sealed partial class DiscordClient
+namespace DisCatSharp
{
- private readonly Dictionary<(object?, Type, bool), List<(EventInfo, Delegate)[]>> _registrationToDelegate = new();
- private readonly Dictionary<Type, List<object>> _typeToAnonymousHandlers = new();
-
- /// <summary>
- /// Registers all methods annotated with <see cref="EventAttribute"/> from the given object.
- /// </summary>
- /// <param name="handler">The event handler object.</param>
- /// <param name="registerStatic">Whether to consider static methods.</param>
- public void RegisterEventHandler(object handler, bool registerStatic = false)
- => this.RegisterEventHandlerImpl(handler, handler.GetType(), registerStatic);
-
- /// <summary>
- /// Registers all static methods annotated with <see cref="EventAttribute"/> from the given type.
- /// </summary>
- /// <param name="t">The static event handler type.</param>
- public void RegisterStaticEventHandler(Type t)
- => this.RegisterEventHandlerImpl(null, t);
-
- /// <see cref="RegisterStaticEventHandler(Type)"/>
- public void RegisterStaticEventHandler<T>() => this.RegisterStaticEventHandler(typeof(T));
-
/// <summary>
- /// <para>If abstract, registers all static methods of the type.</para>
- /// <para>If non-abstract, tries to instantiate it, optionally using the provided <see cref="DiscordConfiguration.ServiceProvider"/>
- /// and registers all instance and static methods.</para>
+ /// A Discord API wrapper.
/// </summary>
- /// <param name="type"></param>
- public void RegisterEventHandler(Type type)
+ public sealed partial class DiscordClient
{
- if (type.IsAbstract)
- this.RegisterStaticEventHandler(type);
- else
+ private readonly Dictionary<(object?, Type, bool), List<(EventInfo, Delegate)[]>> _registrationToDelegate = new();
+ private readonly Dictionary<Type, List<object>> _typeToAnonymousHandlers = new();
+
+ /// <summary>
+ /// Registers all methods annotated with <see cref="EventAttribute"/> from the given object.
+ /// </summary>
+ /// <param name="handler">The event handler object.</param>
+ /// <param name="registerStatic">Whether to consider static methods.</param>
+ public void RegisterEventHandler(object handler, bool registerStatic = false)
+ => this.RegisterEventHandlerImpl(handler, handler.GetType(), registerStatic);
+
+ /// <summary>
+ /// Registers all static methods annotated with <see cref="EventAttribute"/> from the given type.
+ /// </summary>
+ /// <param name="t">The static event handler type.</param>
+ public void RegisterStaticEventHandler(Type t)
+ => this.RegisterEventHandlerImpl(null, t);
+
+ /// <see cref="RegisterStaticEventHandler(Type)"/>
+ public void RegisterStaticEventHandler<T>() => this.RegisterStaticEventHandler(typeof(T));
+
+ /// <summary>
+ /// <para>If abstract, registers all static methods of the type.</para>
+ /// <para>If non-abstract, tries to instantiate it, optionally using the provided <see cref="DiscordConfiguration.ServiceProvider"/>
+ /// and registers all instance and static methods.</para>
+ /// </summary>
+ /// <param name="type"></param>
+ public void RegisterEventHandler(Type type)
{
- object anon = ActivatorUtilities.CreateInstance(this.Configuration.ServiceProvider, type);
+ if (type.IsAbstract)
+ this.RegisterStaticEventHandler(type);
+ else
+ {
+ object anon = ActivatorUtilities.CreateInstance(this.Configuration.ServiceProvider, type);
- this._typeToAnonymousHandlers[type]
- = this._typeToAnonymousHandlers.TryGetValue(type, out var anonObjs) ? anonObjs : (anonObjs = new());
+ this._typeToAnonymousHandlers[type]
+ = this._typeToAnonymousHandlers.TryGetValue(type, out var anonObjs) ? anonObjs : (anonObjs = new());
- anonObjs.Add(anon);
+ anonObjs.Add(anon);
- this.RegisterEventHandlerImpl(anon, type);
+ this.RegisterEventHandlerImpl(anon, type);
+ }
}
- }
-
- /// <see cref="RegisterEventHandler(Type)"/>
- public void RegisterEventHandler<T>() => this.RegisterEventHandler(typeof(T));
-
- /// <summary>
- /// Registers all types associated with the provided assembly that have the <see cref="EventHandler"/> attribute.
- /// </summary>
- /// <param name="assembly">The assembly from which to get the types.</param>
- public void RegisterEventHandlers(Assembly assembly)
- {
- foreach (Type t in GetEventHandlersFromAssembly(assembly))
- this.RegisterEventHandler(t);
- }
-
- /// <summary>
- /// Perfectly mirrors <see cref="RegisterEventHandler(object, bool)"/>.
- /// </summary>
- /// <param name="handler"></param>
- /// <param name="wasRegisteredWithStatic"></param>
- public void UnregisterEventHandler(object handler, bool wasRegisteredWithStatic = false)
- => this.UnregisterEventHandlerImpl(handler, handler.GetType(), wasRegisteredWithStatic);
-
- /// <summary>
- /// Perfectly mirrors <see cref="RegisterStaticEventHandler(Type)"/>.
- /// </summary>
- /// <param name="t"></param>
- public void UnregisterStaticEventHandler(Type t) => this.UnregisterEventHandlerImpl(null, t);
- /// <summary>
- /// Perfectly mirrors <see cref="RegisterStaticEventHandler{T}()"/>.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public void UnregisterStaticEventHandler<T>() => this.UnregisterEventHandler(typeof(T));
+ /// <see cref="RegisterEventHandler(Type)"/>
+ public void RegisterEventHandler<T>() => this.RegisterEventHandler(typeof(T));
- /// <summary>
- /// Perfectly mirrors <see cref="RegisterEventHandler(Type)"/>.
- /// </summary>
- /// <param name="t"></param>
- public void UnregisterEventHandler(Type t)
- {
- if (t.IsAbstract)
- this.UnregisterStaticEventHandler(t);
- else
+ /// <summary>
+ /// Registers all types associated with the provided assembly that have the <see cref="EventHandler"/> attribute.
+ /// </summary>
+ /// <param name="assembly">The assembly from which to get the types.</param>
+ public void RegisterEventHandlers(Assembly assembly)
{
- if (!this._typeToAnonymousHandlers.TryGetValue(t, out var anonObjs)
- || anonObjs.Count == 0)
- return; // Wasn't registered
+ foreach (Type t in GetEventHandlersFromAssembly(assembly))
+ this.RegisterEventHandler(t);
+ }
- object anon = anonObjs[0];
- anonObjs.RemoveAt(0);
+ /// <summary>
+ /// Perfectly mirrors <see cref="RegisterEventHandler(object, bool)"/>.
+ /// </summary>
+ /// <param name="handler"></param>
+ /// <param name="wasRegisteredWithStatic"></param>
+ public void UnregisterEventHandler(object handler, bool wasRegisteredWithStatic = false)
+ => this.UnregisterEventHandlerImpl(handler, handler.GetType(), wasRegisteredWithStatic);
+
+ /// <summary>
+ /// Perfectly mirrors <see cref="RegisterStaticEventHandler(Type)"/>.
+ /// </summary>
+ /// <param name="t"></param>
+ public void UnregisterStaticEventHandler(Type t) => this.UnregisterEventHandlerImpl(null, t);
+
+ /// <summary>
+ /// Perfectly mirrors <see cref="RegisterStaticEventHandler{T}()"/>.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public void UnregisterStaticEventHandler<T>() => this.UnregisterEventHandler(typeof(T));
+
+ /// <summary>
+ /// Perfectly mirrors <see cref="RegisterEventHandler(Type)"/>.
+ /// </summary>
+ /// <param name="t"></param>
+ public void UnregisterEventHandler(Type t)
+ {
+ if (t.IsAbstract)
+ this.UnregisterStaticEventHandler(t);
+ else
+ {
+ if (!this._typeToAnonymousHandlers.TryGetValue(t, out var anonObjs)
+ || anonObjs.Count == 0)
+ return; // Wasn't registered
- if (anonObjs.Count == 0)
- this._typeToAnonymousHandlers.Remove(t);
+ object anon = anonObjs[0];
+ anonObjs.RemoveAt(0);
- this.UnregisterEventHandlerImpl(anon, t);
- }
+ if (anonObjs.Count == 0)
+ this._typeToAnonymousHandlers.Remove(t);
+ this.UnregisterEventHandlerImpl(anon, t);
+ }
- }
- /// <summary>
- /// Perfectly mirrors <see cref="RegisterEventHandler{T}()"/>.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public void UnregisterEventHandler<T>() => this.UnregisterEventHandler(typeof(T));
+ }
- /// <summary>
- /// Perfectly mirrors <see cref="RegisterEventHandlers(Assembly)"/>.
- /// </summary>
- /// <param name="assembly"></param>
- public void UnregisterEventHandlers(Assembly assembly)
- {
- foreach (Type t in GetEventHandlersFromAssembly(assembly))
- this.UnregisterEventHandler(t);
- }
+ /// <summary>
+ /// Perfectly mirrors <see cref="RegisterEventHandler{T}()"/>.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public void UnregisterEventHandler<T>() => this.UnregisterEventHandler(typeof(T));
+
+ /// <summary>
+ /// Perfectly mirrors <see cref="RegisterEventHandlers(Assembly)"/>.
+ /// </summary>
+ /// <param name="assembly"></param>
+ public void UnregisterEventHandlers(Assembly assembly)
+ {
+ foreach (Type t in GetEventHandlersFromAssembly(assembly))
+ this.UnregisterEventHandler(t);
+ }
- private static IEnumerable<Type> GetEventHandlersFromAssembly(Assembly assembly)
- => assembly.GetTypes()
- .Where(t => t.GetCustomAttribute<EventHandlerAttribute>() is not null);
+ private static IEnumerable<Type> GetEventHandlersFromAssembly(Assembly assembly)
+ => assembly.GetTypes()
+ .Where(t => t.GetCustomAttribute<EventHandlerAttribute>() is not null);
- private void UnregisterEventHandlerImpl(object? handler, Type type, bool registerStatic = true)
- {
- if (!this._registrationToDelegate.TryGetValue((handler, type, registerStatic), out var delegateLists)
- || delegateLists.Count == 0)
- return;
+ private void UnregisterEventHandlerImpl(object? handler, Type type, bool registerStatic = true)
+ {
+ if (!this._registrationToDelegate.TryGetValue((handler, type, registerStatic), out var delegateLists)
+ || delegateLists.Count == 0)
+ return;
- foreach (var (evnt, dlgt) in delegateLists[0])
- evnt.RemoveEventHandler(this, dlgt);
+ foreach (var (evnt, dlgt) in delegateLists[0])
+ evnt.RemoveEventHandler(this, dlgt);
- delegateLists.RemoveAt(0);
- if (delegateLists.Count == 0)
- this._registrationToDelegate.Remove((handler, type, registerStatic));
- }
+ delegateLists.RemoveAt(0);
+ if (delegateLists.Count == 0)
+ this._registrationToDelegate.Remove((handler, type, registerStatic));
+ }
- private void RegisterEventHandlerImpl(object? handler, Type type, bool registerStatic = true)
- {
- var delegates = (
- from method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
- let attribute = method.GetCustomAttribute<EventAttribute>()
- where attribute is not null && ((registerStatic && method.IsStatic) || handler is not null)
- let eventName = attribute.EventName ?? method.Name
- let eventInfo = this.GetType().GetEvent(eventName)
- ?? throw new ArgumentException($"Tried to register handler to non-existent event \"{eventName}\"")
- let eventHandlerType = eventInfo.EventHandlerType
- let dlgt = (method.IsStatic
- ? Delegate.CreateDelegate(eventHandlerType, method, false)
- : Delegate.CreateDelegate(eventHandlerType, handler, method, false))
- ?? throw new ArgumentException($"Method \"{method}\" does not adhere to event specification \"{eventHandlerType}\"")
- select (eventInfo, dlgt)
- ).ToArray();
-
- this._registrationToDelegate[(handler, type, registerStatic)]
- = this._registrationToDelegate.TryGetValue((handler, type, registerStatic), out var delList) ? delList : (delList = new());
-
- delList.Add(delegates);
-
- foreach (var (evnt, dlgt) in delegates)
- evnt.AddEventHandler(this, dlgt);
+ private void RegisterEventHandlerImpl(object? handler, Type type, bool registerStatic = true)
+ {
+ var delegates = (
+ from method in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
+ let attribute = method.GetCustomAttribute<EventAttribute>()
+ where attribute is not null && ((registerStatic && method.IsStatic) || handler is not null)
+ let eventName = attribute.EventName ?? method.Name
+ let eventInfo = this.GetType().GetEvent(eventName)
+ ?? throw new ArgumentException($"Tried to register handler to non-existent event \"{eventName}\"")
+ let eventHandlerType = eventInfo.EventHandlerType
+ let dlgt = (method.IsStatic
+ ? Delegate.CreateDelegate(eventHandlerType, method, false)
+ : Delegate.CreateDelegate(eventHandlerType, handler, method, false))
+ ?? throw new ArgumentException($"Method \"{method}\" does not adhere to event specification \"{eventHandlerType}\"")
+ select (eventInfo, dlgt)
+ ).ToArray();
+
+ this._registrationToDelegate[(handler, type, registerStatic)]
+ = this._registrationToDelegate.TryGetValue((handler, type, registerStatic), out var delList) ? delList : (delList = new());
+
+ delList.Add(delegates);
+
+ foreach (var (evnt, dlgt) in delegates)
+ evnt.AddEventHandler(this, dlgt);
+ }
}
}
diff --git a/DisCatSharp/Clients/DiscordClient.Events.cs b/DisCatSharp/Clients/DiscordClient.Events.cs
index 8dc14d763..8ea7beba5 100644
--- a/DisCatSharp/Clients/DiscordClient.Events.cs
+++ b/DisCatSharp/Clients/DiscordClient.Events.cs
@@ -1,1025 +1,1026 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a discord client.
-/// </summary>
-public sealed partial class DiscordClient
+namespace DisCatSharp
{
/// <summary>
- /// Gets the event execution limit.
- /// </summary>
- internal static TimeSpan EventExecutionLimit { get; } = TimeSpan.FromSeconds(1);
-
- #region WebSocket
-
- /// <summary>
- /// Fired whenever a WebSocket error occurs within the client.
+ /// Represents a discord client.
/// </summary>
- public event AsyncEventHandler<DiscordClient, SocketErrorEventArgs> SocketErrored
+ public sealed partial class DiscordClient
{
- add => this._socketErrored.Register(value);
- remove => this._socketErrored.Unregister(value);
- }
- private AsyncEvent<DiscordClient, SocketErrorEventArgs> _socketErrored;
+ /// <summary>
+ /// Gets the event execution limit.
+ /// </summary>
+ internal static TimeSpan EventExecutionLimit { get; } = TimeSpan.FromSeconds(1);
- /// <summary>
- /// Fired whenever WebSocket connection is established.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, SocketEventArgs> SocketOpened
- {
- add => this._socketOpened.Register(value);
- remove => this._socketOpened.Unregister(value);
- }
- private AsyncEvent<DiscordClient, SocketEventArgs> _socketOpened;
+ #region WebSocket
- /// <summary>
- /// Fired whenever WebSocket connection is terminated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, SocketCloseEventArgs> SocketClosed
- {
- add => this._socketClosed.Register(value);
- remove => this._socketClosed.Unregister(value);
- }
- private AsyncEvent<DiscordClient, SocketCloseEventArgs> _socketClosed;
+ /// <summary>
+ /// Fired whenever a WebSocket error occurs within the client.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, SocketErrorEventArgs> SocketErrored
+ {
+ add => this._socketErrored.Register(value);
+ remove => this._socketErrored.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, SocketErrorEventArgs> _socketErrored;
- /// <summary>
- /// Fired when the client enters ready state.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ReadyEventArgs> Ready
- {
- add => this._ready.Register(value);
- remove => this._ready.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ReadyEventArgs> _ready;
+ /// <summary>
+ /// Fired whenever WebSocket connection is established.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, SocketEventArgs> SocketOpened
+ {
+ add => this._socketOpened.Register(value);
+ remove => this._socketOpened.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, SocketEventArgs> _socketOpened;
- /// <summary>
- /// Fired whenever a session is resumed.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ReadyEventArgs> Resumed
- {
- add => this._resumed.Register(value);
- remove => this._resumed.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ReadyEventArgs> _resumed;
+ /// <summary>
+ /// Fired whenever WebSocket connection is terminated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, SocketCloseEventArgs> SocketClosed
+ {
+ add => this._socketClosed.Register(value);
+ remove => this._socketClosed.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, SocketCloseEventArgs> _socketClosed;
- /// <summary>
- /// Fired on received heartbeat ACK.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, HeartbeatEventArgs> Heartbeated
- {
- add => this._heartbeated.Register(value);
- remove => this._heartbeated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, HeartbeatEventArgs> _heartbeated;
+ /// <summary>
+ /// Fired when the client enters ready state.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ReadyEventArgs> Ready
+ {
+ add => this._ready.Register(value);
+ remove => this._ready.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ReadyEventArgs> _ready;
- #endregion
+ /// <summary>
+ /// Fired whenever a session is resumed.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ReadyEventArgs> Resumed
+ {
+ add => this._resumed.Register(value);
+ remove => this._resumed.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ReadyEventArgs> _resumed;
- #region Channel
+ /// <summary>
+ /// Fired on received heartbeat ACK.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, HeartbeatEventArgs> Heartbeated
+ {
+ add => this._heartbeated.Register(value);
+ remove => this._heartbeated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, HeartbeatEventArgs> _heartbeated;
- /// <summary>
- /// Fired when a new channel is created.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ChannelCreateEventArgs> ChannelCreated
- {
- add => this._channelCreated.Register(value);
- remove => this._channelCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ChannelCreateEventArgs> _channelCreated;
+ #endregion
- /// <summary>
- /// Fired when a channel is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ChannelUpdateEventArgs> ChannelUpdated
- {
- add => this._channelUpdated.Register(value);
- remove => this._channelUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ChannelUpdateEventArgs> _channelUpdated;
+ #region Channel
- /// <summary>
- /// Fired when a channel is deleted
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ChannelDeleteEventArgs> ChannelDeleted
- {
- add => this._channelDeleted.Register(value);
- remove => this._channelDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ChannelDeleteEventArgs> _channelDeleted;
+ /// <summary>
+ /// Fired when a new channel is created.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ChannelCreateEventArgs> ChannelCreated
+ {
+ add => this._channelCreated.Register(value);
+ remove => this._channelCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ChannelCreateEventArgs> _channelCreated;
- /// <summary>
- /// Fired when a dm channel is deleted
- /// For this Event you need the <see cref="DiscordIntents.DirectMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, DmChannelDeleteEventArgs> DmChannelDeleted
- {
- add => this._dmChannelDeleted.Register(value);
- remove => this._dmChannelDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, DmChannelDeleteEventArgs> _dmChannelDeleted;
+ /// <summary>
+ /// Fired when a channel is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ChannelUpdateEventArgs> ChannelUpdated
+ {
+ add => this._channelUpdated.Register(value);
+ remove => this._channelUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ChannelUpdateEventArgs> _channelUpdated;
- /// <summary>
- /// Fired whenever a channel's pinned message list is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ChannelPinsUpdateEventArgs> ChannelPinsUpdated
- {
- add => this._channelPinsUpdated.Register(value);
- remove => this._channelPinsUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ChannelPinsUpdateEventArgs> _channelPinsUpdated;
+ /// <summary>
+ /// Fired when a channel is deleted
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ChannelDeleteEventArgs> ChannelDeleted
+ {
+ add => this._channelDeleted.Register(value);
+ remove => this._channelDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ChannelDeleteEventArgs> _channelDeleted;
- #endregion
+ /// <summary>
+ /// Fired when a dm channel is deleted
+ /// For this Event you need the <see cref="DiscordIntents.DirectMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, DmChannelDeleteEventArgs> DmChannelDeleted
+ {
+ add => this._dmChannelDeleted.Register(value);
+ remove => this._dmChannelDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, DmChannelDeleteEventArgs> _dmChannelDeleted;
- #region Guild
+ /// <summary>
+ /// Fired whenever a channel's pinned message list is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ChannelPinsUpdateEventArgs> ChannelPinsUpdated
+ {
+ add => this._channelPinsUpdated.Register(value);
+ remove => this._channelPinsUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ChannelPinsUpdateEventArgs> _channelPinsUpdated;
- /// <summary>
- /// Fired when the user joins a new guild.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- /// <remarks>[alias="GuildJoined"][alias="JoinedGuild"]</remarks>
- public event AsyncEventHandler<DiscordClient, GuildCreateEventArgs> GuildCreated
- {
- add => this._guildCreated.Register(value);
- remove => this._guildCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildCreateEventArgs> _guildCreated;
+ #endregion
- /// <summary>
- /// Fired when a guild is becoming available.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildCreateEventArgs> GuildAvailable
- {
- add => this._guildAvailable.Register(value);
- remove => this._guildAvailable.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildCreateEventArgs> _guildAvailable;
+ #region Guild
- /// <summary>
- /// Fired when a guild is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildUpdateEventArgs> GuildUpdated
- {
- add => this._guildUpdated.Register(value);
- remove => this._guildUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildUpdateEventArgs> _guildUpdated;
+ /// <summary>
+ /// Fired when the user joins a new guild.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ /// <remarks>[alias="GuildJoined"][alias="JoinedGuild"]</remarks>
+ public event AsyncEventHandler<DiscordClient, GuildCreateEventArgs> GuildCreated
+ {
+ add => this._guildCreated.Register(value);
+ remove => this._guildCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildCreateEventArgs> _guildCreated;
- /// <summary>
- /// Fired when the user leaves or is removed from a guild.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildDeleteEventArgs> GuildDeleted
- {
- add => this._guildDeleted.Register(value);
- remove => this._guildDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildDeleteEventArgs> _guildDeleted;
+ /// <summary>
+ /// Fired when a guild is becoming available.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildCreateEventArgs> GuildAvailable
+ {
+ add => this._guildAvailable.Register(value);
+ remove => this._guildAvailable.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildCreateEventArgs> _guildAvailable;
- /// <summary>
- /// Fired when a guild becomes unavailable.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildDeleteEventArgs> GuildUnavailable
- {
- add => this._guildUnavailable.Register(value);
- remove => this._guildUnavailable.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildDeleteEventArgs> _guildUnavailable;
+ /// <summary>
+ /// Fired when a guild is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildUpdateEventArgs> GuildUpdated
+ {
+ add => this._guildUpdated.Register(value);
+ remove => this._guildUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildUpdateEventArgs> _guildUpdated;
- /// <summary>
- /// Fired when all guilds finish streaming from Discord.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildDownloadCompletedEventArgs> GuildDownloadCompleted
- {
- add => this._guildDownloadCompletedEv.Register(value);
- remove => this._guildDownloadCompletedEv.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildDownloadCompletedEventArgs> _guildDownloadCompletedEv;
+ /// <summary>
+ /// Fired when the user leaves or is removed from a guild.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildDeleteEventArgs> GuildDeleted
+ {
+ add => this._guildDeleted.Register(value);
+ remove => this._guildDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildDeleteEventArgs> _guildDeleted;
- /// <summary>
- /// Fired when a guilds emojis get updated
- /// For this Event you need the <see cref="DiscordIntents.GuildEmojisAndStickers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildEmojisUpdateEventArgs> GuildEmojisUpdated
- {
- add => this._guildEmojisUpdated.Register(value);
- remove => this._guildEmojisUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildEmojisUpdateEventArgs> _guildEmojisUpdated;
+ /// <summary>
+ /// Fired when a guild becomes unavailable.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildDeleteEventArgs> GuildUnavailable
+ {
+ add => this._guildUnavailable.Register(value);
+ remove => this._guildUnavailable.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildDeleteEventArgs> _guildUnavailable;
- /// <summary>
- /// Fired when a guilds stickers get updated
- /// For this Event you need the <see cref="DiscordIntents.GuildEmojisAndStickers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildStickersUpdateEventArgs> GuildStickersUpdated
- {
- add => this._guildStickersUpdated.Register(value);
- remove => this._guildStickersUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildStickersUpdateEventArgs> _guildStickersUpdated;
+ /// <summary>
+ /// Fired when all guilds finish streaming from Discord.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildDownloadCompletedEventArgs> GuildDownloadCompleted
+ {
+ add => this._guildDownloadCompletedEv.Register(value);
+ remove => this._guildDownloadCompletedEv.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildDownloadCompletedEventArgs> _guildDownloadCompletedEv;
- /// <summary>
- /// Fired when a guild integration is updated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildIntegrationsUpdateEventArgs> GuildIntegrationsUpdated
- {
- add => this._guildIntegrationsUpdated.Register(value);
- remove => this._guildIntegrationsUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildIntegrationsUpdateEventArgs> _guildIntegrationsUpdated;
+ /// <summary>
+ /// Fired when a guilds emojis get updated
+ /// For this Event you need the <see cref="DiscordIntents.GuildEmojisAndStickers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildEmojisUpdateEventArgs> GuildEmojisUpdated
+ {
+ add => this._guildEmojisUpdated.Register(value);
+ remove => this._guildEmojisUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildEmojisUpdateEventArgs> _guildEmojisUpdated;
- #endregion
+ /// <summary>
+ /// Fired when a guilds stickers get updated
+ /// For this Event you need the <see cref="DiscordIntents.GuildEmojisAndStickers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildStickersUpdateEventArgs> GuildStickersUpdated
+ {
+ add => this._guildStickersUpdated.Register(value);
+ remove => this._guildStickersUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildStickersUpdateEventArgs> _guildStickersUpdated;
- #region Guild Ban
+ /// <summary>
+ /// Fired when a guild integration is updated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildIntegrationsUpdateEventArgs> GuildIntegrationsUpdated
+ {
+ add => this._guildIntegrationsUpdated.Register(value);
+ remove => this._guildIntegrationsUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildIntegrationsUpdateEventArgs> _guildIntegrationsUpdated;
- /// <summary>
- /// Fired when a guild ban gets added
- /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildBanAddEventArgs> GuildBanAdded
- {
- add => this._guildBanAdded.Register(value);
- remove => this._guildBanAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildBanAddEventArgs> _guildBanAdded;
+ #endregion
- /// <summary>
- /// Fired when a guild ban gets removed
- /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildBanRemoveEventArgs> GuildBanRemoved
- {
- add => this._guildBanRemoved.Register(value);
- remove => this._guildBanRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildBanRemoveEventArgs> _guildBanRemoved;
+ #region Guild Ban
- #endregion
+ /// <summary>
+ /// Fired when a guild ban gets added
+ /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildBanAddEventArgs> GuildBanAdded
+ {
+ add => this._guildBanAdded.Register(value);
+ remove => this._guildBanAdded.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildBanAddEventArgs> _guildBanAdded;
- #region Guild Timeout
+ /// <summary>
+ /// Fired when a guild ban gets removed
+ /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildBanRemoveEventArgs> GuildBanRemoved
+ {
+ add => this._guildBanRemoved.Register(value);
+ remove => this._guildBanRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildBanRemoveEventArgs> _guildBanRemoved;
- /// <summary>
- /// Fired when a guild member timeout gets added.
- /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutAddEventArgs> GuildMemberTimeoutAdded
- {
- add => this._guildMemberTimeoutAdded.Register(value);
- remove => this._guildMemberTimeoutAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberTimeoutAddEventArgs> _guildMemberTimeoutAdded;
+ #endregion
- /// <summary>
- /// Fired when a guild member timeout gets changed.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutUpdateEventArgs> GuildMemberTimeoutChanged
- {
- add => this._guildMemberTimeoutChanged.Register(value);
- remove => this._guildMemberTimeoutChanged.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberTimeoutUpdateEventArgs> _guildMemberTimeoutChanged;
+ #region Guild Timeout
+ /// <summary>
+ /// Fired when a guild member timeout gets added.
+ /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutAddEventArgs> GuildMemberTimeoutAdded
+ {
+ add => this._guildMemberTimeoutAdded.Register(value);
+ remove => this._guildMemberTimeoutAdded.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberTimeoutAddEventArgs> _guildMemberTimeoutAdded;
- /// <summary>
- /// Fired when a guild member timeout gets removed.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutRemoveEventArgs> GuildMemberTimeoutRemoved
- {
- add => this._guildMemberTimeoutRemoved.Register(value);
- remove => this._guildMemberTimeoutRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberTimeoutRemoveEventArgs> _guildMemberTimeoutRemoved;
+ /// <summary>
+ /// Fired when a guild member timeout gets changed.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutUpdateEventArgs> GuildMemberTimeoutChanged
+ {
+ add => this._guildMemberTimeoutChanged.Register(value);
+ remove => this._guildMemberTimeoutChanged.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberTimeoutUpdateEventArgs> _guildMemberTimeoutChanged;
- #endregion
- #region Guild Scheduled Event
+ /// <summary>
+ /// Fired when a guild member timeout gets removed.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutRemoveEventArgs> GuildMemberTimeoutRemoved
+ {
+ add => this._guildMemberTimeoutRemoved.Register(value);
+ remove => this._guildMemberTimeoutRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberTimeoutRemoveEventArgs> _guildMemberTimeoutRemoved;
- /// <summary>
- /// Fired when a scheduled Event is created.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventCreateEventArgs> GuildScheduledEventCreated
- {
- add => this._guildScheduledEventCreated.Register(value);
- remove => this._guildScheduledEventCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventCreateEventArgs> _guildScheduledEventCreated;
+ #endregion
- /// <summary>
- /// Fired when a scheduled Event is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventUpdateEventArgs> GuildScheduledEventUpdated
- {
- add => this._guildScheduledEventUpdated.Register(value);
- remove => this._guildScheduledEventUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventUpdateEventArgs> _guildScheduledEventUpdated;
+ #region Guild Scheduled Event
- /// <summary>
- /// Fired when a scheduled Event is deleted.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventDeleteEventArgs> GuildScheduledEventDeleted
- {
- add => this._guildScheduledEventDeleted.Register(value);
- remove => this._guildScheduledEventDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventDeleteEventArgs> _guildScheduledEventDeleted;
+ /// <summary>
+ /// Fired when a scheduled Event is created.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventCreateEventArgs> GuildScheduledEventCreated
+ {
+ add => this._guildScheduledEventCreated.Register(value);
+ remove => this._guildScheduledEventCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventCreateEventArgs> _guildScheduledEventCreated;
- /// <summary>
- /// Fired when a user subscribes to a scheduled event.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventUserAddEventArgs> GuildScheduledEventUserAdded
- {
- add => this._guildScheduledEventUserAdded.Register(value);
- remove => this._guildScheduledEventUserAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventUserAddEventArgs> _guildScheduledEventUserAdded;
+ /// <summary>
+ /// Fired when a scheduled Event is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventUpdateEventArgs> GuildScheduledEventUpdated
+ {
+ add => this._guildScheduledEventUpdated.Register(value);
+ remove => this._guildScheduledEventUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventUpdateEventArgs> _guildScheduledEventUpdated;
- /// <summary>
- /// Fired when a user unsubscribes from a scheduled event.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventUserRemoveEventArgs> GuildScheduledEventUserRemoved
- {
- add => this._guildScheduledEventUserRemoved.Register(value);
- remove => this._guildScheduledEventUserRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventUserRemoveEventArgs> _guildScheduledEventUserRemoved;
+ /// <summary>
+ /// Fired when a scheduled Event is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventDeleteEventArgs> GuildScheduledEventDeleted
+ {
+ add => this._guildScheduledEventDeleted.Register(value);
+ remove => this._guildScheduledEventDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventDeleteEventArgs> _guildScheduledEventDeleted;
- #endregion
+ /// <summary>
+ /// Fired when a user subscribes to a scheduled event.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventUserAddEventArgs> GuildScheduledEventUserAdded
+ {
+ add => this._guildScheduledEventUserAdded.Register(value);
+ remove => this._guildScheduledEventUserAdded.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventUserAddEventArgs> _guildScheduledEventUserAdded;
- #region Guild Integration
+ /// <summary>
+ /// Fired when a user unsubscribes from a scheduled event.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventUserRemoveEventArgs> GuildScheduledEventUserRemoved
+ {
+ add => this._guildScheduledEventUserRemoved.Register(value);
+ remove => this._guildScheduledEventUserRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventUserRemoveEventArgs> _guildScheduledEventUserRemoved;
- /// <summary>
- /// Fired when a guild integration is created.
- /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildIntegrationCreateEventArgs> GuildIntegrationCreated
- {
- add => this._guildIntegrationCreated.Register(value);
- remove => this._guildIntegrationCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildIntegrationCreateEventArgs> _guildIntegrationCreated;
+ #endregion
- /// <summary>
- /// Fired when a guild integration is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildIntegrationUpdateEventArgs> GuildIntegrationUpdated
- {
- add => this._guildIntegrationUpdated.Register(value);
- remove => this._guildIntegrationUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildIntegrationUpdateEventArgs> _guildIntegrationUpdated;
+ #region Guild Integration
- /// <summary>
- /// Fired when a guild integration is deleted.
- /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildIntegrationDeleteEventArgs> GuildIntegrationDeleted
- {
- add => this._guildIntegrationDeleted.Register(value);
- remove => this._guildIntegrationDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildIntegrationDeleteEventArgs> _guildIntegrationDeleted;
+ /// <summary>
+ /// Fired when a guild integration is created.
+ /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildIntegrationCreateEventArgs> GuildIntegrationCreated
+ {
+ add => this._guildIntegrationCreated.Register(value);
+ remove => this._guildIntegrationCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildIntegrationCreateEventArgs> _guildIntegrationCreated;
- #endregion
+ /// <summary>
+ /// Fired when a guild integration is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildIntegrationUpdateEventArgs> GuildIntegrationUpdated
+ {
+ add => this._guildIntegrationUpdated.Register(value);
+ remove => this._guildIntegrationUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildIntegrationUpdateEventArgs> _guildIntegrationUpdated;
- #region Guild Member
+ /// <summary>
+ /// Fired when a guild integration is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildIntegrationDeleteEventArgs> GuildIntegrationDeleted
+ {
+ add => this._guildIntegrationDeleted.Register(value);
+ remove => this._guildIntegrationDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildIntegrationDeleteEventArgs> _guildIntegrationDeleted;
- /// <summary>
- /// Fired when a new user joins a guild.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberAddEventArgs> GuildMemberAdded
- {
- add => this._guildMemberAdded.Register(value);
- remove => this._guildMemberAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberAddEventArgs> _guildMemberAdded;
+ #endregion
- /// <summary>
- /// Fired when a user is removed from a guild (leave/kick/ban).
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberRemoveEventArgs> GuildMemberRemoved
- {
- add => this._guildMemberRemoved.Register(value);
- remove => this._guildMemberRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberRemoveEventArgs> _guildMemberRemoved;
+ #region Guild Member
- /// <summary>
- /// Fired when a guild member is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberUpdateEventArgs> GuildMemberUpdated
- {
- add => this._guildMemberUpdated.Register(value);
- remove => this._guildMemberUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberUpdateEventArgs> _guildMemberUpdated;
+ /// <summary>
+ /// Fired when a new user joins a guild.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberAddEventArgs> GuildMemberAdded
+ {
+ add => this._guildMemberAdded.Register(value);
+ remove => this._guildMemberAdded.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberAddEventArgs> _guildMemberAdded;
- /// <summary>
- /// Fired in response to Gateway Request Guild Members.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMembersChunkEventArgs> GuildMembersChunked
- {
- add => this._guildMembersChunked.Register(value);
- remove => this._guildMembersChunked.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMembersChunkEventArgs> _guildMembersChunked;
+ /// <summary>
+ /// Fired when a user is removed from a guild (leave/kick/ban).
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberRemoveEventArgs> GuildMemberRemoved
+ {
+ add => this._guildMemberRemoved.Register(value);
+ remove => this._guildMemberRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberRemoveEventArgs> _guildMemberRemoved;
- #endregion
+ /// <summary>
+ /// Fired when a guild member is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberUpdateEventArgs> GuildMemberUpdated
+ {
+ add => this._guildMemberUpdated.Register(value);
+ remove => this._guildMemberUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberUpdateEventArgs> _guildMemberUpdated;
- #region Guild Role
+ /// <summary>
+ /// Fired in response to Gateway Request Guild Members.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMembersChunkEventArgs> GuildMembersChunked
+ {
+ add => this._guildMembersChunked.Register(value);
+ remove => this._guildMembersChunked.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMembersChunkEventArgs> _guildMembersChunked;
- /// <summary>
- /// Fired when a guild role is created.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildRoleCreateEventArgs> GuildRoleCreated
- {
- add => this._guildRoleCreated.Register(value);
- remove => this._guildRoleCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildRoleCreateEventArgs> _guildRoleCreated;
+ #endregion
- /// <summary>
- /// Fired when a guild role is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildRoleUpdateEventArgs> GuildRoleUpdated
- {
- add => this._guildRoleUpdated.Register(value);
- remove => this._guildRoleUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildRoleUpdateEventArgs> _guildRoleUpdated;
+ #region Guild Role
- /// <summary>
- /// Fired when a guild role is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildRoleDeleteEventArgs> GuildRoleDeleted
- {
- add => this._guildRoleDeleted.Register(value);
- remove => this._guildRoleDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildRoleDeleteEventArgs> _guildRoleDeleted;
+ /// <summary>
+ /// Fired when a guild role is created.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildRoleCreateEventArgs> GuildRoleCreated
+ {
+ add => this._guildRoleCreated.Register(value);
+ remove => this._guildRoleCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildRoleCreateEventArgs> _guildRoleCreated;
- #endregion
+ /// <summary>
+ /// Fired when a guild role is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildRoleUpdateEventArgs> GuildRoleUpdated
+ {
+ add => this._guildRoleUpdated.Register(value);
+ remove => this._guildRoleUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildRoleUpdateEventArgs> _guildRoleUpdated;
- #region Invite
+ /// <summary>
+ /// Fired when a guild role is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildRoleDeleteEventArgs> GuildRoleDeleted
+ {
+ add => this._guildRoleDeleted.Register(value);
+ remove => this._guildRoleDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildRoleDeleteEventArgs> _guildRoleDeleted;
- /// <summary>
- /// Fired when an invite is created.
- /// For this Event you need the <see cref="DiscordIntents.GuildInvites"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, InviteCreateEventArgs> InviteCreated
- {
- add => this._inviteCreated.Register(value);
- remove => this._inviteCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, InviteCreateEventArgs> _inviteCreated;
+ #endregion
- /// <summary>
- /// Fired when an invite is deleted.
- /// For this Event you need the <see cref="DiscordIntents.GuildInvites"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, InviteDeleteEventArgs> InviteDeleted
- {
- add => this._inviteDeleted.Register(value);
- remove => this._inviteDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, InviteDeleteEventArgs> _inviteDeleted;
+ #region Invite
- #endregion
+ /// <summary>
+ /// Fired when an invite is created.
+ /// For this Event you need the <see cref="DiscordIntents.GuildInvites"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, InviteCreateEventArgs> InviteCreated
+ {
+ add => this._inviteCreated.Register(value);
+ remove => this._inviteCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, InviteCreateEventArgs> _inviteCreated;
- #region Message
+ /// <summary>
+ /// Fired when an invite is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.GuildInvites"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, InviteDeleteEventArgs> InviteDeleted
+ {
+ add => this._inviteDeleted.Register(value);
+ remove => this._inviteDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, InviteDeleteEventArgs> _inviteDeleted;
- /// <summary>
- /// Fired when a message is created.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageCreateEventArgs> MessageCreated
- {
- add => this._messageCreated.Register(value);
- remove => this._messageCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageCreateEventArgs> _messageCreated;
+ #endregion
- /// <summary>
- /// Fired when message is acknowledged by the user.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageAcknowledgeEventArgs> MessageAcknowledged
- {
- add => this._messageAcknowledged.Register(value);
- remove => this._messageAcknowledged.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageAcknowledgeEventArgs> _messageAcknowledged;
+ #region Message
- /// <summary>
- /// Fired when a message is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageUpdateEventArgs> MessageUpdated
- {
- add => this._messageUpdated.Register(value);
- remove => this._messageUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageUpdateEventArgs> _messageUpdated;
+ /// <summary>
+ /// Fired when a message is created.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageCreateEventArgs> MessageCreated
+ {
+ add => this._messageCreated.Register(value);
+ remove => this._messageCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageCreateEventArgs> _messageCreated;
- /// <summary>
- /// Fired when a message is deleted.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageDeleteEventArgs> MessageDeleted
- {
- add => this._messageDeleted.Register(value);
- remove => this._messageDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageDeleteEventArgs> _messageDeleted;
+ /// <summary>
+ /// Fired when message is acknowledged by the user.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageAcknowledgeEventArgs> MessageAcknowledged
+ {
+ add => this._messageAcknowledged.Register(value);
+ remove => this._messageAcknowledged.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageAcknowledgeEventArgs> _messageAcknowledged;
- /// <summary>
- /// Fired when multiple messages are deleted at once.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageBulkDeleteEventArgs> MessagesBulkDeleted
- {
- add => this._messagesBulkDeleted.Register(value);
- remove => this._messagesBulkDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageBulkDeleteEventArgs> _messagesBulkDeleted;
+ /// <summary>
+ /// Fired when a message is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageUpdateEventArgs> MessageUpdated
+ {
+ add => this._messageUpdated.Register(value);
+ remove => this._messageUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageUpdateEventArgs> _messageUpdated;
- #endregion
+ /// <summary>
+ /// Fired when a message is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageDeleteEventArgs> MessageDeleted
+ {
+ add => this._messageDeleted.Register(value);
+ remove => this._messageDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageDeleteEventArgs> _messageDeleted;
- #region Message Reaction
+ /// <summary>
+ /// Fired when multiple messages are deleted at once.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageBulkDeleteEventArgs> MessagesBulkDeleted
+ {
+ add => this._messagesBulkDeleted.Register(value);
+ remove => this._messagesBulkDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageBulkDeleteEventArgs> _messagesBulkDeleted;
- /// <summary>
- /// Fired when a reaction gets added to a message.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageReactionAddEventArgs> MessageReactionAdded
- {
- add => this._messageReactionAdded.Register(value);
- remove => this._messageReactionAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageReactionAddEventArgs> _messageReactionAdded;
+ #endregion
- /// <summary>
- /// Fired when a reaction gets removed from a message.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageReactionRemoveEventArgs> MessageReactionRemoved
- {
- add => this._messageReactionRemoved.Register(value);
- remove => this._messageReactionRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs> _messageReactionRemoved;
+ #region Message Reaction
- /// <summary>
- /// Fired when all reactions get removed from a message.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageReactionsClearEventArgs> MessageReactionsCleared
- {
- add => this._messageReactionsCleared.Register(value);
- remove => this._messageReactionsCleared.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageReactionsClearEventArgs> _messageReactionsCleared;
+ /// <summary>
+ /// Fired when a reaction gets added to a message.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageReactionAddEventArgs> MessageReactionAdded
+ {
+ add => this._messageReactionAdded.Register(value);
+ remove => this._messageReactionAdded.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageReactionAddEventArgs> _messageReactionAdded;
- /// <summary>
- /// Fired when all reactions of a specific reaction are removed from a message.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageReactionRemoveEmojiEventArgs> MessageReactionRemovedEmoji
- {
- add => this._messageReactionRemovedEmoji.Register(value);
- remove => this._messageReactionRemovedEmoji.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageReactionRemoveEmojiEventArgs> _messageReactionRemovedEmoji;
+ /// <summary>
+ /// Fired when a reaction gets removed from a message.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageReactionRemoveEventArgs> MessageReactionRemoved
+ {
+ add => this._messageReactionRemoved.Register(value);
+ remove => this._messageReactionRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs> _messageReactionRemoved;
- #endregion
+ /// <summary>
+ /// Fired when all reactions get removed from a message.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageReactionsClearEventArgs> MessageReactionsCleared
+ {
+ add => this._messageReactionsCleared.Register(value);
+ remove => this._messageReactionsCleared.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageReactionsClearEventArgs> _messageReactionsCleared;
- #region Activities
+ /// <summary>
+ /// Fired when all reactions of a specific reaction are removed from a message.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageReactionRemoveEmojiEventArgs> MessageReactionRemovedEmoji
+ {
+ add => this._messageReactionRemovedEmoji.Register(value);
+ remove => this._messageReactionRemovedEmoji.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageReactionRemoveEmojiEventArgs> _messageReactionRemovedEmoji;
- /// <summary>
- /// Fired when a embedded activity has been updated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, EmbeddedActivityUpdateEventArgs> EmbeddedActivityUpdated
- {
- add => this._embeddedActivityUpdated.Register(value);
- remove => this._embeddedActivityUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, EmbeddedActivityUpdateEventArgs> _embeddedActivityUpdated;
+ #endregion
- #endregion
+ #region Activities
- #region Presence/User Update
+ /// <summary>
+ /// Fired when a embedded activity has been updated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, EmbeddedActivityUpdateEventArgs> EmbeddedActivityUpdated
+ {
+ add => this._embeddedActivityUpdated.Register(value);
+ remove => this._embeddedActivityUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, EmbeddedActivityUpdateEventArgs> _embeddedActivityUpdated;
- /// <summary>
- /// Fired when a presence has been updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, PresenceUpdateEventArgs> PresenceUpdated
- {
- add => this._presenceUpdated.Register(value);
- remove => this._presenceUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, PresenceUpdateEventArgs> _presenceUpdated;
+ #endregion
+ #region Presence/User Update
- /// <summary>
- /// Fired when the current user updates their settings.
- /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, UserSettingsUpdateEventArgs> UserSettingsUpdated
- {
- add => this._userSettingsUpdated.Register(value);
- remove => this._userSettingsUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, UserSettingsUpdateEventArgs> _userSettingsUpdated;
+ /// <summary>
+ /// Fired when a presence has been updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, PresenceUpdateEventArgs> PresenceUpdated
+ {
+ add => this._presenceUpdated.Register(value);
+ remove => this._presenceUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, PresenceUpdateEventArgs> _presenceUpdated;
- /// <summary>
- /// Fired when properties about the current user change.
- /// </summary>
- /// <remarks>
- /// NB: This event only applies for changes to the <b>current user</b>, the client that is connected to Discord.
- /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </remarks>
- public event AsyncEventHandler<DiscordClient, UserUpdateEventArgs> UserUpdated
- {
- add => this._userUpdated.Register(value);
- remove => this._userUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, UserUpdateEventArgs> _userUpdated;
- #endregion
+ /// <summary>
+ /// Fired when the current user updates their settings.
+ /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, UserSettingsUpdateEventArgs> UserSettingsUpdated
+ {
+ add => this._userSettingsUpdated.Register(value);
+ remove => this._userSettingsUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, UserSettingsUpdateEventArgs> _userSettingsUpdated;
+
+ /// <summary>
+ /// Fired when properties about the current user change.
+ /// </summary>
+ /// <remarks>
+ /// NB: This event only applies for changes to the <b>current user</b>, the client that is connected to Discord.
+ /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </remarks>
+ public event AsyncEventHandler<DiscordClient, UserUpdateEventArgs> UserUpdated
+ {
+ add => this._userUpdated.Register(value);
+ remove => this._userUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, UserUpdateEventArgs> _userUpdated;
- #region Stage Instance
+ #endregion
- /// <summary>
- /// Fired when a Stage Instance is created.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, StageInstanceCreateEventArgs> StageInstanceCreated
- {
- add => this._stageInstanceCreated.Register(value);
- remove => this._stageInstanceCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, StageInstanceCreateEventArgs> _stageInstanceCreated;
+ #region Stage Instance
- /// <summary>
- /// Fired when a Stage Instance is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, StageInstanceUpdateEventArgs> StageInstanceUpdated
- {
- add => this._stageInstanceUpdated.Register(value);
- remove => this._stageInstanceUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, StageInstanceUpdateEventArgs> _stageInstanceUpdated;
+ /// <summary>
+ /// Fired when a Stage Instance is created.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, StageInstanceCreateEventArgs> StageInstanceCreated
+ {
+ add => this._stageInstanceCreated.Register(value);
+ remove => this._stageInstanceCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, StageInstanceCreateEventArgs> _stageInstanceCreated;
- /// <summary>
- /// Fired when a Stage Instance is deleted.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, StageInstanceDeleteEventArgs> StageInstanceDeleted
- {
- add => this._stageInstanceDeleted.Register(value);
- remove => this._stageInstanceDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, StageInstanceDeleteEventArgs> _stageInstanceDeleted;
+ /// <summary>
+ /// Fired when a Stage Instance is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, StageInstanceUpdateEventArgs> StageInstanceUpdated
+ {
+ add => this._stageInstanceUpdated.Register(value);
+ remove => this._stageInstanceUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, StageInstanceUpdateEventArgs> _stageInstanceUpdated;
- #endregion
+ /// <summary>
+ /// Fired when a Stage Instance is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, StageInstanceDeleteEventArgs> StageInstanceDeleted
+ {
+ add => this._stageInstanceDeleted.Register(value);
+ remove => this._stageInstanceDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, StageInstanceDeleteEventArgs> _stageInstanceDeleted;
- #region Thread
+ #endregion
- /// <summary>
- /// Fired when a thread is created.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadCreateEventArgs> ThreadCreated
- {
- add => this._threadCreated.Register(value);
- remove => this._threadCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadCreateEventArgs> _threadCreated;
+ #region Thread
- /// <summary>
- /// Fired when a thread is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadUpdateEventArgs> ThreadUpdated
- {
- add => this._threadUpdated.Register(value);
- remove => this._threadUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadUpdateEventArgs> _threadUpdated;
+ /// <summary>
+ /// Fired when a thread is created.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadCreateEventArgs> ThreadCreated
+ {
+ add => this._threadCreated.Register(value);
+ remove => this._threadCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadCreateEventArgs> _threadCreated;
- /// <summary>
- /// Fired when a thread is deleted.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadDeleteEventArgs> ThreadDeleted
- {
- add => this._threadDeleted.Register(value);
- remove => this._threadDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadDeleteEventArgs> _threadDeleted;
+ /// <summary>
+ /// Fired when a thread is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadUpdateEventArgs> ThreadUpdated
+ {
+ add => this._threadUpdated.Register(value);
+ remove => this._threadUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadUpdateEventArgs> _threadUpdated;
- /// <summary>
- /// Fired when a thread member is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadListSyncEventArgs> ThreadListSynced
- {
- add => this._threadListSynced.Register(value);
- remove => this._threadListSynced.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadListSyncEventArgs> _threadListSynced;
+ /// <summary>
+ /// Fired when a thread is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadDeleteEventArgs> ThreadDeleted
+ {
+ add => this._threadDeleted.Register(value);
+ remove => this._threadDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadDeleteEventArgs> _threadDeleted;
- /// <summary>
- /// Fired when a thread member is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadMemberUpdateEventArgs> ThreadMemberUpdated
- {
- add => this._threadMemberUpdated.Register(value);
- remove => this._threadMemberUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadMemberUpdateEventArgs> _threadMemberUpdated;
+ /// <summary>
+ /// Fired when a thread member is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadListSyncEventArgs> ThreadListSynced
+ {
+ add => this._threadListSynced.Register(value);
+ remove => this._threadListSynced.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadListSyncEventArgs> _threadListSynced;
- /// <summary>
- /// Fired when the thread members are updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> or <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadMembersUpdateEventArgs> ThreadMembersUpdated
- {
- add => this._threadMembersUpdated.Register(value);
- remove => this._threadMembersUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadMembersUpdateEventArgs> _threadMembersUpdated;
+ /// <summary>
+ /// Fired when a thread member is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadMemberUpdateEventArgs> ThreadMemberUpdated
+ {
+ add => this._threadMemberUpdated.Register(value);
+ remove => this._threadMemberUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadMemberUpdateEventArgs> _threadMemberUpdated;
- #endregion
+ /// <summary>
+ /// Fired when the thread members are updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> or <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadMembersUpdateEventArgs> ThreadMembersUpdated
+ {
+ add => this._threadMembersUpdated.Register(value);
+ remove => this._threadMembersUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadMembersUpdateEventArgs> _threadMembersUpdated;
- #region Voice
+ #endregion
- /// <summary>
- /// Fired when someone joins/leaves/moves voice channels.
- /// For this Event you need the <see cref="DiscordIntents.GuildVoiceStates"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, VoiceStateUpdateEventArgs> VoiceStateUpdated
- {
- add => this._voiceStateUpdated.Register(value);
- remove => this._voiceStateUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, VoiceStateUpdateEventArgs> _voiceStateUpdated;
+ #region Voice
- /// <summary>
- /// Fired when a guild's voice server is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildVoiceStates"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, VoiceServerUpdateEventArgs> VoiceServerUpdated
- {
- add => this._voiceServerUpdated.Register(value);
- remove => this._voiceServerUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, VoiceServerUpdateEventArgs> _voiceServerUpdated;
+ /// <summary>
+ /// Fired when someone joins/leaves/moves voice channels.
+ /// For this Event you need the <see cref="DiscordIntents.GuildVoiceStates"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, VoiceStateUpdateEventArgs> VoiceStateUpdated
+ {
+ add => this._voiceStateUpdated.Register(value);
+ remove => this._voiceStateUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, VoiceStateUpdateEventArgs> _voiceStateUpdated;
- #endregion
+ /// <summary>
+ /// Fired when a guild's voice server is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildVoiceStates"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, VoiceServerUpdateEventArgs> VoiceServerUpdated
+ {
+ add => this._voiceServerUpdated.Register(value);
+ remove => this._voiceServerUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, VoiceServerUpdateEventArgs> _voiceServerUpdated;
- #region Application
+ #endregion
- /// <summary>
- /// Fired when a new application command is registered.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandCreated
- {
- add => this._applicationCommandCreated.Register(value);
- remove => this._applicationCommandCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandCreated;
+ #region Application
- /// <summary>
- /// Fired when an application command is updated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandUpdated
- {
- add => this._applicationCommandUpdated.Register(value);
- remove => this._applicationCommandUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandUpdated;
+ /// <summary>
+ /// Fired when a new application command is registered.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandCreated
+ {
+ add => this._applicationCommandCreated.Register(value);
+ remove => this._applicationCommandCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandCreated;
- /// <summary>
- /// Fired when an application command is deleted.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandDeleted
- {
- add => this._applicationCommandDeleted.Register(value);
- remove => this._applicationCommandDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandDeleted;
+ /// <summary>
+ /// Fired when an application command is updated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandUpdated
+ {
+ add => this._applicationCommandUpdated.Register(value);
+ remove => this._applicationCommandUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandUpdated;
- /// <summary>
- /// Fired when a new application command is registered.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildApplicationCommandCountEventArgs> GuildApplicationCommandCountUpdated
- {
- add => this._guildApplicationCommandCountUpdated.Register(value);
- remove => this._guildApplicationCommandCountUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildApplicationCommandCountEventArgs> _guildApplicationCommandCountUpdated;
+ /// <summary>
+ /// Fired when an application command is deleted.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandDeleted
+ {
+ add => this._applicationCommandDeleted.Register(value);
+ remove => this._applicationCommandDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandDeleted;
- /// <summary>
- /// Fired when a user uses a context menu.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ContextMenuInteractionCreateEventArgs> ContextMenuInteractionCreated
- {
- add => this._contextMenuInteractionCreated.Register(value);
- remove => this._contextMenuInteractionCreated.Unregister(value);
- }
+ /// <summary>
+ /// Fired when a new application command is registered.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildApplicationCommandCountEventArgs> GuildApplicationCommandCountUpdated
+ {
+ add => this._guildApplicationCommandCountUpdated.Register(value);
+ remove => this._guildApplicationCommandCountUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildApplicationCommandCountEventArgs> _guildApplicationCommandCountUpdated;
- private AsyncEvent<DiscordClient, ContextMenuInteractionCreateEventArgs> _contextMenuInteractionCreated;
+ /// <summary>
+ /// Fired when a user uses a context menu.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ContextMenuInteractionCreateEventArgs> ContextMenuInteractionCreated
+ {
+ add => this._contextMenuInteractionCreated.Register(value);
+ remove => this._contextMenuInteractionCreated.Unregister(value);
+ }
- /// <summary>
- /// Fired when application command permissions gets updated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs> ApplicationCommandPermissionsUpdated
- {
- add => this._applicationCommandPermissionsUpdated.Register(value);
- remove => this._applicationCommandPermissionsUpdated.Unregister(value);
- }
+ private AsyncEvent<DiscordClient, ContextMenuInteractionCreateEventArgs> _contextMenuInteractionCreated;
- private AsyncEvent<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs> _applicationCommandPermissionsUpdated;
+ /// <summary>
+ /// Fired when application command permissions gets updated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs> ApplicationCommandPermissionsUpdated
+ {
+ add => this._applicationCommandPermissionsUpdated.Register(value);
+ remove => this._applicationCommandPermissionsUpdated.Unregister(value);
+ }
- #endregion
+ private AsyncEvent<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs> _applicationCommandPermissionsUpdated;
- #region Misc
+ #endregion
- /// <summary>
- /// Fired when an interaction is invoked.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, InteractionCreateEventArgs> InteractionCreated
- {
- add => this._interactionCreated.Register(value);
- remove => this._interactionCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, InteractionCreateEventArgs> _interactionCreated;
+ #region Misc
- /// <summary>
- /// Fired when a component is invoked.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ComponentInteractionCreateEventArgs> ComponentInteractionCreated
- {
- add => this._componentInteractionCreated.Register(value);
- remove => this._componentInteractionCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ComponentInteractionCreateEventArgs> _componentInteractionCreated;
+ /// <summary>
+ /// Fired when an interaction is invoked.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, InteractionCreateEventArgs> InteractionCreated
+ {
+ add => this._interactionCreated.Register(value);
+ remove => this._interactionCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, InteractionCreateEventArgs> _interactionCreated;
- /// <summary>
- /// Fired when a user starts typing in a channel.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, TypingStartEventArgs> TypingStarted
- {
- add => this._typingStarted.Register(value);
- remove => this._typingStarted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, TypingStartEventArgs> _typingStarted;
+ /// <summary>
+ /// Fired when a component is invoked.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ComponentInteractionCreateEventArgs> ComponentInteractionCreated
+ {
+ add => this._componentInteractionCreated.Register(value);
+ remove => this._componentInteractionCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ComponentInteractionCreateEventArgs> _componentInteractionCreated;
- /// <summary>
- /// Fired when an unknown event gets received.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, UnknownEventArgs> UnknownEvent
- {
- add => this._unknownEvent.Register(value);
- remove => this._unknownEvent.Unregister(value);
- }
- private AsyncEvent<DiscordClient, UnknownEventArgs> _unknownEvent;
+ /// <summary>
+ /// Fired when a user starts typing in a channel.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, TypingStartEventArgs> TypingStarted
+ {
+ add => this._typingStarted.Register(value);
+ remove => this._typingStarted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, TypingStartEventArgs> _typingStarted;
- /// <summary>
- /// Fired whenever webhooks update.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, WebhooksUpdateEventArgs> WebhooksUpdated
- {
- add => this._webhooksUpdated.Register(value);
- remove => this._webhooksUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, WebhooksUpdateEventArgs> _webhooksUpdated;
+ /// <summary>
+ /// Fired when an unknown event gets received.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, UnknownEventArgs> UnknownEvent
+ {
+ add => this._unknownEvent.Register(value);
+ remove => this._unknownEvent.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, UnknownEventArgs> _unknownEvent;
- /// <summary>
- /// Fired whenever an error occurs within an event handler.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ClientErrorEventArgs> ClientErrored
- {
- add => this._clientErrored.Register(value);
- remove => this._clientErrored.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ClientErrorEventArgs> _clientErrored;
- #endregion
+ /// <summary>
+ /// Fired whenever webhooks update.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, WebhooksUpdateEventArgs> WebhooksUpdated
+ {
+ add => this._webhooksUpdated.Register(value);
+ remove => this._webhooksUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, WebhooksUpdateEventArgs> _webhooksUpdated;
- #region Error Handling
+ /// <summary>
+ /// Fired whenever an error occurs within an event handler.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ClientErrorEventArgs> ClientErrored
+ {
+ add => this._clientErrored.Register(value);
+ remove => this._clientErrored.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ClientErrorEventArgs> _clientErrored;
+ #endregion
+
+ #region Error Handling
+
+ /// <summary>
+ /// Handles event errors.
+ /// </summary>
+ /// <param name="asyncEvent">The event.</param>
+ /// <param name="ex">The exception.</param>
+ /// <param name="handler">The event handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="eventArgs">The event args.</param>
+ internal void EventErrorHandler<TSender, TArgs>(AsyncEvent<TSender, TArgs> asyncEvent, Exception ex, AsyncEventHandler<TSender, TArgs> handler, TSender sender, TArgs eventArgs)
+ where TArgs : AsyncEventArgs
+ {
+ if (ex is AsyncEventTimeoutException)
+ {
+ this.Logger.LogWarning(LoggerEvents.EventHandlerException, $"An event handler for {asyncEvent.Name} took too long to execute. Defined as \"{handler.Method.ToString().Replace(handler.Method.ReturnType.ToString(), "").TrimStart()}\" located in \"{handler.Method.DeclaringType}\".");
+ return;
+ }
+
+ this.Logger.LogError(LoggerEvents.EventHandlerException, ex, "Event handler exception for event {0} thrown from {1} (defined in {2})", asyncEvent.Name, handler.Method, handler.Method.DeclaringType);
+ this._clientErrored.InvokeAsync(this, new ClientErrorEventArgs(this.ServiceProvider) { EventName = asyncEvent.Name, Exception = ex }).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
- /// <summary>
- /// Handles event errors.
- /// </summary>
- /// <param name="asyncEvent">The event.</param>
- /// <param name="ex">The exception.</param>
- /// <param name="handler">The event handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="eventArgs">The event args.</param>
- internal void EventErrorHandler<TSender, TArgs>(AsyncEvent<TSender, TArgs> asyncEvent, Exception ex, AsyncEventHandler<TSender, TArgs> handler, TSender sender, TArgs eventArgs)
- where TArgs : AsyncEventArgs
- {
- if (ex is AsyncEventTimeoutException)
+ /// <summary>
+ /// Fired when a ratelimit was hit.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, RateLimitExceptionEventArgs> RateLimitHit
{
- this.Logger.LogWarning(LoggerEvents.EventHandlerException, $"An event handler for {asyncEvent.Name} took too long to execute. Defined as \"{handler.Method.ToString().Replace(handler.Method.ReturnType.ToString(), "").TrimStart()}\" located in \"{handler.Method.DeclaringType}\".");
- return;
+ add => this._rateLimitHit.Register(value);
+ remove => this._rateLimitHit.Unregister(value);
}
+ internal AsyncEvent<DiscordClient, RateLimitExceptionEventArgs> _rateLimitHit;
- this.Logger.LogError(LoggerEvents.EventHandlerException, ex, "Event handler exception for event {0} thrown from {1} (defined in {2})", asyncEvent.Name, handler.Method, handler.Method.DeclaringType);
- this._clientErrored.InvokeAsync(this, new ClientErrorEventArgs(this.ServiceProvider) { EventName = asyncEvent.Name, Exception = ex }).ConfigureAwait(false).GetAwaiter().GetResult();
- }
+ /// <summary>
+ /// Fired on heartbeat attempt cancellation due to too many failed heartbeats.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ZombiedEventArgs> Zombied
+ {
+ add => this._zombied.Register(value);
+ remove => this._zombied.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ZombiedEventArgs> _zombied;
- /// <summary>
- /// Fired when a ratelimit was hit.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, RateLimitExceptionEventArgs> RateLimitHit
- {
- add => this._rateLimitHit.Register(value);
- remove => this._rateLimitHit.Unregister(value);
- }
- internal AsyncEvent<DiscordClient, RateLimitExceptionEventArgs> _rateLimitHit;
+ /// <summary>
+ /// Fired when a gateway payload is received.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, PayloadReceivedEventArgs> PayloadReceived
+ {
+ add => this._payloadReceived.Register(value);
+ remove => this._payloadReceived.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, PayloadReceivedEventArgs> _payloadReceived;
- /// <summary>
- /// Fired on heartbeat attempt cancellation due to too many failed heartbeats.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ZombiedEventArgs> Zombied
- {
- add => this._zombied.Register(value);
- remove => this._zombied.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ZombiedEventArgs> _zombied;
+ /// <summary>
+ /// Handles event handler exceptions.
+ /// </summary>
+ /// <param name="asyncEvent">The event.</param>
+ /// <param name="ex">The exception.</param>
+ /// <param name="handler">The event handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="eventArgs">The event args.</param>
+ private void Goof<TSender, TArgs>(AsyncEvent<TSender, TArgs> asyncEvent, Exception ex, AsyncEventHandler<TSender, TArgs> handler, TSender sender, TArgs eventArgs)
+ where TArgs : AsyncEventArgs => this.Logger.LogCritical(LoggerEvents.EventHandlerException, ex, "Exception event handler {0} (defined in {1}) threw an exception", handler.Method, handler.Method.DeclaringType);
- /// <summary>
- /// Fired when a gateway payload is received.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, PayloadReceivedEventArgs> PayloadReceived
- {
- add => this._payloadReceived.Register(value);
- remove => this._payloadReceived.Unregister(value);
+ #endregion
}
- private AsyncEvent<DiscordClient, PayloadReceivedEventArgs> _payloadReceived;
-
- /// <summary>
- /// Handles event handler exceptions.
- /// </summary>
- /// <param name="asyncEvent">The event.</param>
- /// <param name="ex">The exception.</param>
- /// <param name="handler">The event handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="eventArgs">The event args.</param>
- private void Goof<TSender, TArgs>(AsyncEvent<TSender, TArgs> asyncEvent, Exception ex, AsyncEventHandler<TSender, TArgs> handler, TSender sender, TArgs eventArgs)
- where TArgs : AsyncEventArgs => this.Logger.LogCritical(LoggerEvents.EventHandlerException, ex, "Exception event handler {0} (defined in {1}) threw an exception", handler.Method, handler.Method.DeclaringType);
-
- #endregion
}
diff --git a/DisCatSharp/Clients/DiscordClient.WebSocket.cs b/DisCatSharp/Clients/DiscordClient.WebSocket.cs
index d48dbc97b..455848168 100644
--- a/DisCatSharp/Clients/DiscordClient.WebSocket.cs
+++ b/DisCatSharp/Clients/DiscordClient.WebSocket.cs
@@ -1,588 +1,589 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.WebSocket;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a discord websocket client.
-/// </summary>
-public sealed partial class DiscordClient
+namespace DisCatSharp
{
- #region Private Fields
+ /// <summary>
+ /// Represents a discord websocket client.
+ /// </summary>
+ public sealed partial class DiscordClient
+ {
+ #region Private Fields
- private int _heartbeatInterval;
- private DateTimeOffset _lastHeartbeat;
- private Task _heartbeatTask;
+ private int _heartbeatInterval;
+ private DateTimeOffset _lastHeartbeat;
+ private Task _heartbeatTask;
- internal static DateTimeOffset DiscordEpoch = new(2015, 1, 1, 0, 0, 0, TimeSpan.Zero);
+ internal static DateTimeOffset DiscordEpoch = new(2015, 1, 1, 0, 0, 0, TimeSpan.Zero);
- private int _skippedHeartbeats;
- private long _lastSequence;
+ private int _skippedHeartbeats;
+ private long _lastSequence;
- internal IWebSocketClient WebSocketClient;
- private PayloadDecompressor _payloadDecompressor;
+ internal IWebSocketClient WebSocketClient;
+ private PayloadDecompressor _payloadDecompressor;
- private CancellationTokenSource _cancelTokenSource;
- private CancellationToken _cancelToken;
+ private CancellationTokenSource _cancelTokenSource;
+ private CancellationToken _cancelToken;
- #endregion
+ #endregion
- #region Connection Semaphore
+ #region Connection Semaphore
- /// <summary>
- /// Gets the socket locks.
- /// </summary>
- private static ConcurrentDictionary<ulong, SocketLock> s_socketLocks { get; } = new();
+ /// <summary>
+ /// Gets the socket locks.
+ /// </summary>
+ private static ConcurrentDictionary<ulong, SocketLock> s_socketLocks { get; } = new();
- /// <summary>
- /// Gets the session lock.
- /// </summary>
- private readonly ManualResetEventSlim _sessionLock = new(true);
-
- #endregion
+ /// <summary>
+ /// Gets the session lock.
+ /// </summary>
+ private readonly ManualResetEventSlim _sessionLock = new(true);
- #region Internal Connection Methods
+ #endregion
- /// <summary>
- /// Reconnects the websocket client.
- /// </summary>
- /// <param name="startNewSession">Whether to start a new session.</param>
- /// <param name="code">The reconnect code.</param>
- /// <param name="message">The reconnect message.</param>
- private Task InternalReconnectAsync(bool startNewSession = false, int code = 1000, string message = "")
- {
- if (startNewSession)
- this._sessionId = null;
+ #region Internal Connection Methods
- _ = this.WebSocketClient.DisconnectAsync(code, message);
- return Task.CompletedTask;
- }
-
- /// <summary>
- /// Connects the websocket client.
- /// </summary>
- internal async Task InternalConnectAsync()
- {
- SocketLock socketLock = null;
- try
+ /// <summary>
+ /// Reconnects the websocket client.
+ /// </summary>
+ /// <param name="startNewSession">Whether to start a new session.</param>
+ /// <param name="code">The reconnect code.</param>
+ /// <param name="message">The reconnect message.</param>
+ private Task InternalReconnectAsync(bool startNewSession = false, int code = 1000, string message = "")
{
- if (this.GatewayInfo == null)
- await this.InternalUpdateGatewayAsync().ConfigureAwait(false);
- await this.InitializeAsync().ConfigureAwait(false);
+ if (startNewSession)
+ this._sessionId = null;
- socketLock = this.GetSocketLock();
- await socketLock.LockAsync().ConfigureAwait(false);
- }
- catch
- {
- socketLock?.UnlockAfter(TimeSpan.Zero);
- throw;
+ _ = this.WebSocketClient.DisconnectAsync(code, message);
+ return Task.CompletedTask;
}
- if (!this.Presences.ContainsKey(this.CurrentUser.Id))
+ /// <summary>
+ /// Connects the websocket client.
+ /// </summary>
+ internal async Task InternalConnectAsync()
{
- this.PresencesInternal[this.CurrentUser.Id] = new DiscordPresence
+ SocketLock socketLock = null;
+ try
+ {
+ if (this.GatewayInfo == null)
+ await this.InternalUpdateGatewayAsync().ConfigureAwait(false);
+ await this.InitializeAsync().ConfigureAwait(false);
+
+ socketLock = this.GetSocketLock();
+ await socketLock.LockAsync().ConfigureAwait(false);
+ }
+ catch
+ {
+ socketLock?.UnlockAfter(TimeSpan.Zero);
+ throw;
+ }
+
+ if (!this.Presences.ContainsKey(this.CurrentUser.Id))
{
- Discord = this,
- RawActivity = new TransportActivity(),
- Activity = new DiscordActivity(),
- Status = UserStatus.Online,
- InternalUser = new TransportUser
+ this.PresencesInternal[this.CurrentUser.Id] = new DiscordPresence
{
- Id = this.CurrentUser.Id,
- Username = this.CurrentUser.Username,
- Discriminator = this.CurrentUser.Discriminator,
- AvatarHash = this.CurrentUser.AvatarHash
- }
- };
- }
- else
- {
- var pr = this.PresencesInternal[this.CurrentUser.Id];
- pr.RawActivity = new TransportActivity();
- pr.Activity = new DiscordActivity();
- pr.Status = UserStatus.Online;
- }
+ Discord = this,
+ RawActivity = new TransportActivity(),
+ Activity = new DiscordActivity(),
+ Status = UserStatus.Online,
+ InternalUser = new TransportUser
+ {
+ Id = this.CurrentUser.Id,
+ Username = this.CurrentUser.Username,
+ Discriminator = this.CurrentUser.Discriminator,
+ AvatarHash = this.CurrentUser.AvatarHash
+ }
+ };
+ }
+ else
+ {
+ var pr = this.PresencesInternal[this.CurrentUser.Id];
+ pr.RawActivity = new TransportActivity();
+ pr.Activity = new DiscordActivity();
+ pr.Status = UserStatus.Online;
+ }
- Volatile.Write(ref this._skippedHeartbeats, 0);
+ Volatile.Write(ref this._skippedHeartbeats, 0);
- this.WebSocketClient = this.Configuration.WebSocketClientFactory(this.Configuration.Proxy, this.ServiceProvider);
- this._payloadDecompressor = this.Configuration.GatewayCompressionLevel != GatewayCompressionLevel.None
- ? new PayloadDecompressor(this.Configuration.GatewayCompressionLevel)
- : null;
+ this.WebSocketClient = this.Configuration.WebSocketClientFactory(this.Configuration.Proxy, this.ServiceProvider);
+ this._payloadDecompressor = this.Configuration.GatewayCompressionLevel != GatewayCompressionLevel.None
+ ? new PayloadDecompressor(this.Configuration.GatewayCompressionLevel)
+ : null;
- this._cancelTokenSource = new CancellationTokenSource();
- this._cancelToken = this._cancelTokenSource.Token;
+ this._cancelTokenSource = new CancellationTokenSource();
+ this._cancelToken = this._cancelTokenSource.Token;
- this.WebSocketClient.Connected += SocketOnConnect;
- this.WebSocketClient.Disconnected += SocketOnDisconnect;
- this.WebSocketClient.MessageReceived += SocketOnMessage;
- this.WebSocketClient.ExceptionThrown += SocketOnException;
+ this.WebSocketClient.Connected += SocketOnConnect;
+ this.WebSocketClient.Disconnected += SocketOnDisconnect;
+ this.WebSocketClient.MessageReceived += SocketOnMessage;
+ this.WebSocketClient.ExceptionThrown += SocketOnException;
- var gwuri = new QueryUriBuilder(this.GatewayUri)
- .AddParameter("v", this.Configuration.ApiVersion)
- .AddParameter("encoding", "json");
+ var gwuri = new QueryUriBuilder(this.GatewayUri)
+ .AddParameter("v", this.Configuration.ApiVersion)
+ .AddParameter("encoding", "json");
- if (this.Configuration.GatewayCompressionLevel == GatewayCompressionLevel.Stream)
- gwuri.AddParameter("compress", "zlib-stream");
+ if (this.Configuration.GatewayCompressionLevel == GatewayCompressionLevel.Stream)
+ gwuri.AddParameter("compress", "zlib-stream");
- await this.WebSocketClient.ConnectAsync(gwuri.Build()).ConfigureAwait(false);
+ await this.WebSocketClient.ConnectAsync(gwuri.Build()).ConfigureAwait(false);
- Task SocketOnConnect(IWebSocketClient sender, SocketEventArgs e)
- => this._socketOpened.InvokeAsync(this, e);
+ Task SocketOnConnect(IWebSocketClient sender, SocketEventArgs e)
+ => this._socketOpened.InvokeAsync(this, e);
- async Task SocketOnMessage(IWebSocketClient sender, SocketMessageEventArgs e)
- {
- string msg = null;
- if (e is SocketTextMessageEventArgs etext)
+ async Task SocketOnMessage(IWebSocketClient sender, SocketMessageEventArgs e)
{
- msg = etext.Message;
- }
- else if (e is SocketBinaryMessageEventArgs ebin) // :DDDD
- {
- using var ms = new MemoryStream();
- if (!this._payloadDecompressor.TryDecompress(new ArraySegment<byte>(ebin.Message), ms))
+ string msg = null;
+ if (e is SocketTextMessageEventArgs etext)
{
- this.Logger.LogError(LoggerEvents.WebSocketReceiveFailure, "Payload decompression failed");
- return;
+ msg = etext.Message;
+ }
+ else if (e is SocketBinaryMessageEventArgs ebin) // :DDDD
+ {
+ using var ms = new MemoryStream();
+ if (!this._payloadDecompressor.TryDecompress(new ArraySegment<byte>(ebin.Message), ms))
+ {
+ this.Logger.LogError(LoggerEvents.WebSocketReceiveFailure, "Payload decompression failed");
+ return;
+ }
+
+ ms.Position = 0;
+ using var sr = new StreamReader(ms, Utilities.UTF8);
+ msg = await sr.ReadToEndAsync().ConfigureAwait(false);
}
- ms.Position = 0;
- using var sr = new StreamReader(ms, Utilities.UTF8);
- msg = await sr.ReadToEndAsync().ConfigureAwait(false);
- }
-
- try
- {
- this.Logger.LogTrace(LoggerEvents.GatewayWsRx, msg);
- await this.HandleSocketMessageAsync(msg).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- this.Logger.LogError(LoggerEvents.WebSocketReceiveFailure, ex, "Socket handler suppressed an exception");
+ try
+ {
+ this.Logger.LogTrace(LoggerEvents.GatewayWsRx, msg);
+ await this.HandleSocketMessageAsync(msg).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ this.Logger.LogError(LoggerEvents.WebSocketReceiveFailure, ex, "Socket handler suppressed an exception");
+ }
}
- }
- Task SocketOnException(IWebSocketClient sender, SocketErrorEventArgs e)
- => this._socketErrored.InvokeAsync(this, e);
-
- async Task SocketOnDisconnect(IWebSocketClient sender, SocketCloseEventArgs e)
- {
- // release session and connection
- this._connectionLock.Set();
- this._sessionLock.Set();
+ Task SocketOnException(IWebSocketClient sender, SocketErrorEventArgs e)
+ => this._socketErrored.InvokeAsync(this, e);
- if (!this._disposed)
- this._cancelTokenSource.Cancel();
+ async Task SocketOnDisconnect(IWebSocketClient sender, SocketCloseEventArgs e)
+ {
+ // release session and connection
+ this._connectionLock.Set();
+ this._sessionLock.Set();
- this.Logger.LogDebug(LoggerEvents.ConnectionClose, "Connection closed ({0}, '{1}')", e.CloseCode, e.CloseMessage);
- await this._socketClosed.InvokeAsync(this, e).ConfigureAwait(false);
+ if (!this._disposed)
+ this._cancelTokenSource.Cancel();
+ this.Logger.LogDebug(LoggerEvents.ConnectionClose, "Connection closed ({0}, '{1}')", e.CloseCode, e.CloseMessage);
+ await this._socketClosed.InvokeAsync(this, e).ConfigureAwait(false);
- if (this.Configuration.AutoReconnect && (e.CloseCode < 4001 || e.CloseCode >= 5000))
- {
- this.Logger.LogCritical(LoggerEvents.ConnectionClose, "Connection terminated ({0}, '{1}'), reconnecting", e.CloseCode, e.CloseMessage);
- if (this._status == null)
- await this.ConnectAsync().ConfigureAwait(false);
- else
- if (this._status.IdleSince.HasValue)
- await this.ConnectAsync(this._status.ActivityInternal, this._status.Status, Utilities.GetDateTimeOffsetFromMilliseconds(this._status.IdleSince.Value)).ConfigureAwait(false);
+ if (this.Configuration.AutoReconnect && (e.CloseCode < 4001 || e.CloseCode >= 5000))
+ {
+ this.Logger.LogCritical(LoggerEvents.ConnectionClose, "Connection terminated ({0}, '{1}'), reconnecting", e.CloseCode, e.CloseMessage);
+
+ if (this._status == null)
+ await this.ConnectAsync().ConfigureAwait(false);
+ else
+ if (this._status.IdleSince.HasValue)
+ await this.ConnectAsync(this._status.ActivityInternal, this._status.Status, Utilities.GetDateTimeOffsetFromMilliseconds(this._status.IdleSince.Value)).ConfigureAwait(false);
+ else
+ await this.ConnectAsync(this._status.ActivityInternal, this._status.Status).ConfigureAwait(false);
+ }
else
- await this.ConnectAsync(this._status.ActivityInternal, this._status.Status).ConfigureAwait(false);
- }
- else
- {
- this.Logger.LogCritical(LoggerEvents.ConnectionClose, "Connection terminated ({0}, '{1}')", e.CloseCode, e.CloseMessage);
+ {
+ this.Logger.LogCritical(LoggerEvents.ConnectionClose, "Connection terminated ({0}, '{1}')", e.CloseCode, e.CloseMessage);
+ }
}
}
- }
- #endregion
+ #endregion
- #region WebSocket (Events)
+ #region WebSocket (Events)
- /// <summary>
- /// Handles the socket message.
- /// </summary>
- /// <param name="data">The data.</param>
- internal async Task HandleSocketMessageAsync(string data)
- {
- var payload = JsonConvert.DeserializeObject<GatewayPayload>(data);
- this._lastSequence = payload.Sequence ?? this._lastSequence;
- switch (payload.OpCode)
+ /// <summary>
+ /// Handles the socket message.
+ /// </summary>
+ /// <param name="data">The data.</param>
+ internal async Task HandleSocketMessageAsync(string data)
{
- case GatewayOpCode.Dispatch:
- await this.HandleDispatchAsync(payload).ConfigureAwait(false);
- break;
+ var payload = JsonConvert.DeserializeObject<GatewayPayload>(data);
+ this._lastSequence = payload.Sequence ?? this._lastSequence;
+ switch (payload.OpCode)
+ {
+ case GatewayOpCode.Dispatch:
+ await this.HandleDispatchAsync(payload).ConfigureAwait(false);
+ break;
- case GatewayOpCode.Heartbeat:
- await this.OnHeartbeatAsync((long)payload.Data).ConfigureAwait(false);
- break;
+ case GatewayOpCode.Heartbeat:
+ await this.OnHeartbeatAsync((long)payload.Data).ConfigureAwait(false);
+ break;
- case GatewayOpCode.Reconnect:
- await this.OnReconnectAsync().ConfigureAwait(false);
- break;
+ case GatewayOpCode.Reconnect:
+ await this.OnReconnectAsync().ConfigureAwait(false);
+ break;
- case GatewayOpCode.InvalidSession:
- await this.OnInvalidateSessionAsync((bool)payload.Data).ConfigureAwait(false);
- break;
+ case GatewayOpCode.InvalidSession:
+ await this.OnInvalidateSessionAsync((bool)payload.Data).ConfigureAwait(false);
+ break;
- case GatewayOpCode.Hello:
- await this.OnHelloAsync((payload.Data as JObject).ToObject<GatewayHello>()).ConfigureAwait(false);
- break;
+ case GatewayOpCode.Hello:
+ await this.OnHelloAsync((payload.Data as JObject).ToObject<GatewayHello>()).ConfigureAwait(false);
+ break;
- case GatewayOpCode.HeartbeatAck:
- await this.OnHeartbeatAckAsync().ConfigureAwait(false);
- break;
+ case GatewayOpCode.HeartbeatAck:
+ await this.OnHeartbeatAckAsync().ConfigureAwait(false);
+ break;
- default:
- this.Logger.LogWarning(LoggerEvents.WebSocketReceive, "Unknown Discord opcode: {0}\nPayload: {1}", payload.OpCode, payload.Data);
- break;
+ default:
+ this.Logger.LogWarning(LoggerEvents.WebSocketReceive, "Unknown Discord opcode: {0}\nPayload: {1}", payload.OpCode, payload.Data);
+ break;
+ }
}
- }
-
- /// <summary>
- /// Handles the heartbeat.
- /// </summary>
- /// <param name="seq">The sequence.</param>
- internal async Task OnHeartbeatAsync(long seq)
- {
- this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received HEARTBEAT (OP1)");
- await this.SendHeartbeatAsync(seq).ConfigureAwait(false);
- }
- /// <summary>
- /// Handles the reconnect event.
- /// </summary>
- internal async Task OnReconnectAsync()
- {
- this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received RECONNECT (OP7)");
- await this.InternalReconnectAsync(code: 4000, message: "OP7 acknowledged").ConfigureAwait(false);
- }
-
- /// <summary>
- /// Handles the invalidate session event
- /// </summary>
- /// <param name="data">Unknown. Please fill documentation.</param>
- internal async Task OnInvalidateSessionAsync(bool data)
- {
- // begin a session if one is not open already
- if (this._sessionLock.Wait(0))
- this._sessionLock.Reset();
-
- // we are sending a fresh resume/identify, so lock the socket
- var socketLock = this.GetSocketLock();
- await socketLock.LockAsync().ConfigureAwait(false);
- socketLock.UnlockAfter(TimeSpan.FromSeconds(5));
-
- if (data)
+ /// <summary>
+ /// Handles the heartbeat.
+ /// </summary>
+ /// <param name="seq">The sequence.</param>
+ internal async Task OnHeartbeatAsync(long seq)
{
- this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received INVALID_SESSION (OP9, true)");
- await Task.Delay(6000).ConfigureAwait(false);
- await this.SendResumeAsync().ConfigureAwait(false);
+ this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received HEARTBEAT (OP1)");
+ await this.SendHeartbeatAsync(seq).ConfigureAwait(false);
}
- else
+
+ /// <summary>
+ /// Handles the reconnect event.
+ /// </summary>
+ internal async Task OnReconnectAsync()
{
- this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received INVALID_SESSION (OP9, false)");
- this._sessionId = null;
- await this.SendIdentifyAsync(this._status).ConfigureAwait(false);
+ this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received RECONNECT (OP7)");
+ await this.InternalReconnectAsync(code: 4000, message: "OP7 acknowledged").ConfigureAwait(false);
}
- }
- /// <summary>
- /// Handles the hello event.
- /// </summary>
- /// <param name="hello">The gateway hello payload.</param>
- internal async Task OnHelloAsync(GatewayHello hello)
- {
- this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received HELLO (OP10)");
-
- if (this._sessionLock.Wait(0))
+ /// <summary>
+ /// Handles the invalidate session event
+ /// </summary>
+ /// <param name="data">Unknown. Please fill documentation.</param>
+ internal async Task OnInvalidateSessionAsync(bool data)
{
- this._sessionLock.Reset();
- this.GetSocketLock().UnlockAfter(TimeSpan.FromSeconds(5));
+ // begin a session if one is not open already
+ if (this._sessionLock.Wait(0))
+ this._sessionLock.Reset();
+
+ // we are sending a fresh resume/identify, so lock the socket
+ var socketLock = this.GetSocketLock();
+ await socketLock.LockAsync().ConfigureAwait(false);
+ socketLock.UnlockAfter(TimeSpan.FromSeconds(5));
+
+ if (data)
+ {
+ this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received INVALID_SESSION (OP9, true)");
+ await Task.Delay(6000).ConfigureAwait(false);
+ await this.SendResumeAsync().ConfigureAwait(false);
+ }
+ else
+ {
+ this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received INVALID_SESSION (OP9, false)");
+ this._sessionId = null;
+ await this.SendIdentifyAsync(this._status).ConfigureAwait(false);
+ }
}
- else
+
+ /// <summary>
+ /// Handles the hello event.
+ /// </summary>
+ /// <param name="hello">The gateway hello payload.</param>
+ internal async Task OnHelloAsync(GatewayHello hello)
{
- this.Logger.LogWarning(LoggerEvents.SessionUpdate, "Attempt to start a session while another session is active");
- return;
- }
+ this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received HELLO (OP10)");
- Interlocked.CompareExchange(ref this._skippedHeartbeats, 0, 0);
- this._heartbeatInterval = hello.HeartbeatInterval;
- this._heartbeatTask = Task.Run(this.HeartbeatLoopAsync, this._cancelToken);
+ if (this._sessionLock.Wait(0))
+ {
+ this._sessionLock.Reset();
+ this.GetSocketLock().UnlockAfter(TimeSpan.FromSeconds(5));
+ }
+ else
+ {
+ this.Logger.LogWarning(LoggerEvents.SessionUpdate, "Attempt to start a session while another session is active");
+ return;
+ }
- if (string.IsNullOrEmpty(this._sessionId))
- await this.SendIdentifyAsync(this._status).ConfigureAwait(false);
- else
- await this.SendResumeAsync().ConfigureAwait(false);
- }
+ Interlocked.CompareExchange(ref this._skippedHeartbeats, 0, 0);
+ this._heartbeatInterval = hello.HeartbeatInterval;
+ this._heartbeatTask = Task.Run(this.HeartbeatLoopAsync, this._cancelToken);
- /// <summary>
- /// Handles the heartbeat acknowledge event.
- /// </summary>
- internal async Task OnHeartbeatAckAsync()
- {
- Interlocked.Decrement(ref this._skippedHeartbeats);
+ if (string.IsNullOrEmpty(this._sessionId))
+ await this.SendIdentifyAsync(this._status).ConfigureAwait(false);
+ else
+ await this.SendResumeAsync().ConfigureAwait(false);
+ }
- var ping = (int)(DateTime.Now - this._lastHeartbeat).TotalMilliseconds;
+ /// <summary>
+ /// Handles the heartbeat acknowledge event.
+ /// </summary>
+ internal async Task OnHeartbeatAckAsync()
+ {
+ Interlocked.Decrement(ref this._skippedHeartbeats);
- this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received HEARTBEAT_ACK (OP11, {0}ms)", ping);
+ var ping = (int)(DateTime.Now - this._lastHeartbeat).TotalMilliseconds;
- Volatile.Write(ref this._ping, ping);
+ this.Logger.LogTrace(LoggerEvents.WebSocketReceive, "Received HEARTBEAT_ACK (OP11, {0}ms)", ping);
- var args = new HeartbeatEventArgs(this.ServiceProvider)
- {
- Ping = this.Ping,
- Timestamp = DateTimeOffset.Now
- };
+ Volatile.Write(ref this._ping, ping);
- await this._heartbeated.InvokeAsync(this, args).ConfigureAwait(false);
- }
+ var args = new HeartbeatEventArgs(this.ServiceProvider)
+ {
+ Ping = this.Ping,
+ Timestamp = DateTimeOffset.Now
+ };
- /// <summary>
- /// Handles the heartbeat loop.
- /// </summary>
- internal async Task HeartbeatLoopAsync()
- {
- this.Logger.LogDebug(LoggerEvents.Heartbeat, "Heartbeat task started");
- var token = this._cancelToken;
- try
+ await this._heartbeated.InvokeAsync(this, args).ConfigureAwait(false);
+ }
+
+ /// <summary>
+ /// Handles the heartbeat loop.
+ /// </summary>
+ internal async Task HeartbeatLoopAsync()
{
- while (true)
+ this.Logger.LogDebug(LoggerEvents.Heartbeat, "Heartbeat task started");
+ var token = this._cancelToken;
+ try
{
- await this.SendHeartbeatAsync(this._lastSequence).ConfigureAwait(false);
- await Task.Delay(this._heartbeatInterval, token).ConfigureAwait(false);
- token.ThrowIfCancellationRequested();
+ while (true)
+ {
+ await this.SendHeartbeatAsync(this._lastSequence).ConfigureAwait(false);
+ await Task.Delay(this._heartbeatInterval, token).ConfigureAwait(false);
+ token.ThrowIfCancellationRequested();
+ }
}
+ catch (OperationCanceledException) { }
}
- catch (OperationCanceledException) { }
- }
-
- #endregion
-
- #region Internal Gateway Methods
- /// <summary>
- /// Updates the status.
- /// </summary>
- /// <param name="activity">The activity.</param>
- /// <param name="userStatus">The optional user status.</param>
- /// <param name="idleSince">Since when is the client performing the specified activity.</param>
- internal async Task InternalUpdateStatusAsync(DiscordActivity activity, UserStatus? userStatus, DateTimeOffset? idleSince)
- {
- if (activity != null && activity.Name != null && activity.Name.Length > 128)
- throw new Exception("Game name can't be longer than 128 characters!");
+ #endregion
- var sinceUnix = idleSince != null ? (long?)Utilities.GetUnixTime(idleSince.Value) : null;
- var act = activity ?? new DiscordActivity();
+ #region Internal Gateway Methods
- var status = new StatusUpdate
+ /// <summary>
+ /// Updates the status.
+ /// </summary>
+ /// <param name="activity">The activity.</param>
+ /// <param name="userStatus">The optional user status.</param>
+ /// <param name="idleSince">Since when is the client performing the specified activity.</param>
+ internal async Task InternalUpdateStatusAsync(DiscordActivity activity, UserStatus? userStatus, DateTimeOffset? idleSince)
{
- Activity = new TransportActivity(act),
- IdleSince = sinceUnix,
- IsAfk = idleSince != null,
- Status = userStatus ?? UserStatus.Online
- };
-
- // Solution to have status persist between sessions
- this._status = status;
- var statusUpdate = new GatewayPayload
- {
- OpCode = GatewayOpCode.StatusUpdate,
- Data = status
- };
+ if (activity != null && activity.Name != null && activity.Name.Length > 128)
+ throw new Exception("Game name can't be longer than 128 characters!");
- var statusstr = JsonConvert.SerializeObject(statusUpdate);
+ var sinceUnix = idleSince != null ? (long?)Utilities.GetUnixTime(idleSince.Value) : null;
+ var act = activity ?? new DiscordActivity();
- await this.WsSendAsync(statusstr).ConfigureAwait(false);
+ var status = new StatusUpdate
+ {
+ Activity = new TransportActivity(act),
+ IdleSince = sinceUnix,
+ IsAfk = idleSince != null,
+ Status = userStatus ?? UserStatus.Online
+ };
- if (!this.PresencesInternal.ContainsKey(this.CurrentUser.Id))
- {
- this.PresencesInternal[this.CurrentUser.Id] = new DiscordPresence
+ // Solution to have status persist between sessions
+ this._status = status;
+ var statusUpdate = new GatewayPayload
{
- Discord = this,
- Activity = act,
- Status = userStatus ?? UserStatus.Online,
- InternalUser = new TransportUser { Id = this.CurrentUser.Id }
+ OpCode = GatewayOpCode.StatusUpdate,
+ Data = status
};
+
+ var statusstr = JsonConvert.SerializeObject(statusUpdate);
+
+ await this.WsSendAsync(statusstr).ConfigureAwait(false);
+
+ if (!this.PresencesInternal.ContainsKey(this.CurrentUser.Id))
+ {
+ this.PresencesInternal[this.CurrentUser.Id] = new DiscordPresence
+ {
+ Discord = this,
+ Activity = act,
+ Status = userStatus ?? UserStatus.Online,
+ InternalUser = new TransportUser { Id = this.CurrentUser.Id }
+ };
+ }
+ else
+ {
+ var pr = this.PresencesInternal[this.CurrentUser.Id];
+ pr.Activity = act;
+ pr.Status = userStatus ?? pr.Status;
+ }
}
- else
- {
- var pr = this.PresencesInternal[this.CurrentUser.Id];
- pr.Activity = act;
- pr.Status = userStatus ?? pr.Status;
- }
- }
- /// <summary>
- /// Sends the heartbeat.
- /// </summary>
- /// <param name="seq">The sequenze.</param>
- internal async Task SendHeartbeatAsync(long seq)
- {
- var moreThan5 = Volatile.Read(ref this._skippedHeartbeats) > 5;
- var guildsComp = Volatile.Read(ref this._guildDownloadCompleted);
- if (guildsComp && moreThan5)
+ /// <summary>
+ /// Sends the heartbeat.
+ /// </summary>
+ /// <param name="seq">The sequenze.</param>
+ internal async Task SendHeartbeatAsync(long seq)
{
- this.Logger.LogCritical(LoggerEvents.HeartbeatFailure, "Server failed to acknowledge more than 5 heartbeats - connection is zombie");
+ var moreThan5 = Volatile.Read(ref this._skippedHeartbeats) > 5;
+ var guildsComp = Volatile.Read(ref this._guildDownloadCompleted);
+ if (guildsComp && moreThan5)
+ {
+ this.Logger.LogCritical(LoggerEvents.HeartbeatFailure, "Server failed to acknowledge more than 5 heartbeats - connection is zombie");
+
+ var args = new ZombiedEventArgs(this.ServiceProvider)
+ {
+ Failures = Volatile.Read(ref this._skippedHeartbeats),
+ GuildDownloadCompleted = true
+ };
+ await this._zombied.InvokeAsync(this, args).ConfigureAwait(false);
- var args = new ZombiedEventArgs(this.ServiceProvider)
+ await this.InternalReconnectAsync(code: 4001, message: "Too many heartbeats missed").ConfigureAwait(false);
+ return;
+ }
+ else if (!guildsComp && moreThan5)
{
- Failures = Volatile.Read(ref this._skippedHeartbeats),
- GuildDownloadCompleted = true
- };
- await this._zombied.InvokeAsync(this, args).ConfigureAwait(false);
+ var args = new ZombiedEventArgs(this.ServiceProvider)
+ {
+ Failures = Volatile.Read(ref this._skippedHeartbeats),
+ GuildDownloadCompleted = false
+ };
+ await this._zombied.InvokeAsync(this, args).ConfigureAwait(false);
- await this.InternalReconnectAsync(code: 4001, message: "Too many heartbeats missed").ConfigureAwait(false);
- return;
- }
- else if (!guildsComp && moreThan5)
- {
- var args = new ZombiedEventArgs(this.ServiceProvider)
+ this.Logger.LogWarning(LoggerEvents.HeartbeatFailure, "Server failed to acknowledge more than 5 heartbeats, but the guild download is still running - check your connection speed");
+ }
+
+ Volatile.Write(ref this._lastSequence, seq);
+ this.Logger.LogTrace(LoggerEvents.Heartbeat, "Sending heartbeat");
+ var heartbeat = new GatewayPayload
{
- Failures = Volatile.Read(ref this._skippedHeartbeats),
- GuildDownloadCompleted = false
+ OpCode = GatewayOpCode.Heartbeat,
+ Data = seq
};
- await this._zombied.InvokeAsync(this, args).ConfigureAwait(false);
+ var heartbeatStr = JsonConvert.SerializeObject(heartbeat);
+ await this.WsSendAsync(heartbeatStr).ConfigureAwait(false);
+
+ this._lastHeartbeat = DateTimeOffset.Now;
- this.Logger.LogWarning(LoggerEvents.HeartbeatFailure, "Server failed to acknowledge more than 5 heartbeats, but the guild download is still running - check your connection speed");
+ Interlocked.Increment(ref this._skippedHeartbeats);
}
- Volatile.Write(ref this._lastSequence, seq);
- this.Logger.LogTrace(LoggerEvents.Heartbeat, "Sending heartbeat");
- var heartbeat = new GatewayPayload
+ /// <summary>
+ /// Sends the identify payload.
+ /// </summary>
+ /// <param name="status">The status update payload.</param>
+ internal async Task SendIdentifyAsync(StatusUpdate status)
{
- OpCode = GatewayOpCode.Heartbeat,
- Data = seq
- };
- var heartbeatStr = JsonConvert.SerializeObject(heartbeat);
- await this.WsSendAsync(heartbeatStr).ConfigureAwait(false);
-
- this._lastHeartbeat = DateTimeOffset.Now;
+ var identify = new GatewayIdentify
+ {
+ Token = Utilities.GetFormattedToken(this),
+ Compress = this.Configuration.GatewayCompressionLevel == GatewayCompressionLevel.Payload,
+ LargeThreshold = this.Configuration.LargeThreshold,
+ ShardInfo = new ShardInfo
+ {
+ ShardId = this.Configuration.ShardId,
+ ShardCount = this.Configuration.ShardCount
+ },
+ Presence = status,
+ Intents = this.Configuration.Intents,
+ Discord = this
+ };
+ var payload = new GatewayPayload
+ {
+ OpCode = GatewayOpCode.Identify,
+ Data = identify
+ };
+ var payloadstr = JsonConvert.SerializeObject(payload);
+ await this.WsSendAsync(payloadstr).ConfigureAwait(false);
- Interlocked.Increment(ref this._skippedHeartbeats);
- }
+ this.Logger.LogDebug(LoggerEvents.Intents, "Registered gateway intents ({0})", this.Configuration.Intents);
+ }
- /// <summary>
- /// Sends the identify payload.
- /// </summary>
- /// <param name="status">The status update payload.</param>
- internal async Task SendIdentifyAsync(StatusUpdate status)
- {
- var identify = new GatewayIdentify
+ /// <summary>
+ /// Sends the resume payload.
+ /// </summary>
+ internal async Task SendResumeAsync()
{
- Token = Utilities.GetFormattedToken(this),
- Compress = this.Configuration.GatewayCompressionLevel == GatewayCompressionLevel.Payload,
- LargeThreshold = this.Configuration.LargeThreshold,
- ShardInfo = new ShardInfo
+ var resume = new GatewayResume
{
- ShardId = this.Configuration.ShardId,
- ShardCount = this.Configuration.ShardCount
- },
- Presence = status,
- Intents = this.Configuration.Intents,
- Discord = this
- };
- var payload = new GatewayPayload
- {
- OpCode = GatewayOpCode.Identify,
- Data = identify
- };
- var payloadstr = JsonConvert.SerializeObject(payload);
- await this.WsSendAsync(payloadstr).ConfigureAwait(false);
-
- this.Logger.LogDebug(LoggerEvents.Intents, "Registered gateway intents ({0})", this.Configuration.Intents);
- }
+ Token = Utilities.GetFormattedToken(this),
+ SessionId = this._sessionId,
+ SequenceNumber = Volatile.Read(ref this._lastSequence)
+ };
+ var resumePayload = new GatewayPayload
+ {
+ OpCode = GatewayOpCode.Resume,
+ Data = resume
+ };
+ var resumestr = JsonConvert.SerializeObject(resumePayload);
- /// <summary>
- /// Sends the resume payload.
- /// </summary>
- internal async Task SendResumeAsync()
- {
- var resume = new GatewayResume
- {
- Token = Utilities.GetFormattedToken(this),
- SessionId = this._sessionId,
- SequenceNumber = Volatile.Read(ref this._lastSequence)
- };
- var resumePayload = new GatewayPayload
+ await this.WsSendAsync(resumestr).ConfigureAwait(false);
+ }
+ /// <summary>
+ /// Internals the update gateway async.
+ /// </summary>
+ /// <returns>A Task.</returns>
+ internal async Task InternalUpdateGatewayAsync()
{
- OpCode = GatewayOpCode.Resume,
- Data = resume
- };
- var resumestr = JsonConvert.SerializeObject(resumePayload);
-
- await this.WsSendAsync(resumestr).ConfigureAwait(false);
- }
- /// <summary>
- /// Internals the update gateway async.
- /// </summary>
- /// <returns>A Task.</returns>
- internal async Task InternalUpdateGatewayAsync()
- {
- var info = await this.GetGatewayInfoAsync().ConfigureAwait(false);
- this.GatewayInfo = info;
- this.GatewayUri = new Uri(info.Url);
- }
+ var info = await this.GetGatewayInfoAsync().ConfigureAwait(false);
+ this.GatewayInfo = info;
+ this.GatewayUri = new Uri(info.Url);
+ }
- /// <summary>
- /// Sends a websocket message.
- /// </summary>
- /// <param name="payload">The payload to send.</param>
- internal async Task WsSendAsync(string payload)
- {
- this.Logger.LogTrace(LoggerEvents.GatewayWsTx, payload);
- await this.WebSocketClient.SendMessageAsync(payload).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Sends a websocket message.
+ /// </summary>
+ /// <param name="payload">The payload to send.</param>
+ internal async Task WsSendAsync(string payload)
+ {
+ this.Logger.LogTrace(LoggerEvents.GatewayWsTx, payload);
+ await this.WebSocketClient.SendMessageAsync(payload).ConfigureAwait(false);
+ }
- #endregion
+ #endregion
- #region Semaphore Methods
+ #region Semaphore Methods
- /// <summary>
- /// Gets the socket lock.
- /// </summary>
- /// <returns>The added socket lock.</returns>
- private SocketLock GetSocketLock()
- => s_socketLocks.GetOrAdd(this.CurrentApplication.Id, appId => new SocketLock(appId, this.GatewayInfo.SessionBucket.MaxConcurrency));
+ /// <summary>
+ /// Gets the socket lock.
+ /// </summary>
+ /// <returns>The added socket lock.</returns>
+ private SocketLock GetSocketLock()
+ => s_socketLocks.GetOrAdd(this.CurrentApplication.Id, appId => new SocketLock(appId, this.GatewayInfo.SessionBucket.MaxConcurrency));
- #endregion
+ #endregion
+ }
}
diff --git a/DisCatSharp/Clients/DiscordClient.cs b/DisCatSharp/Clients/DiscordClient.cs
index 95e4850b4..b1af09f1d 100644
--- a/DisCatSharp/Clients/DiscordClient.cs
+++ b/DisCatSharp/Clients/DiscordClient.cs
@@ -1,1296 +1,1297 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.EventArgs;
using DisCatSharp.Exceptions;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Models;
using DisCatSharp.Net.Serialization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp;
-
-/// <summary>
-/// A Discord API wrapper.
-/// </summary>
-public sealed partial class DiscordClient : BaseDiscordClient
+namespace DisCatSharp
{
- #region Internal Fields/Properties
-
- internal bool IsShard = false;
- /// <summary>
- /// Gets the message cache.
- /// </summary>
- internal RingBuffer<DiscordMessage> MessageCache { get; }
-
- private List<BaseExtension> _extensions = new();
- private StatusUpdate _status;
-
- /// <summary>
- /// Gets the connection lock.
- /// </summary>
- private readonly ManualResetEventSlim _connectionLock = new(true);
-
- #endregion
-
- #region Public Fields/Properties
- /// <summary>
- /// Gets the gateway protocol version.
- /// </summary>
- public int GatewayVersion { get; internal set; }
-
- /// <summary>
- /// Gets the gateway session information for this client.
- /// </summary>
- public GatewayInfo GatewayInfo { get; internal set; }
-
- /// <summary>
- /// Gets the gateway URL.
- /// </summary>
- public Uri GatewayUri { get; internal set; }
-
- /// <summary>
- /// Gets the total number of shards the bot is connected to.
- /// </summary>
- public int ShardCount => this.GatewayInfo != null
- ? this.GatewayInfo.ShardCount
- : this.Configuration.ShardCount;
-
- /// <summary>
- /// Gets the currently connected shard ID.
- /// </summary>
- public int ShardId
- => this.Configuration.ShardId;
-
/// <summary>
- /// Gets the intents configured for this client.
+ /// A Discord API wrapper.
/// </summary>
- public DiscordIntents Intents
- => this.Configuration.Intents;
-
- /// <summary>
- /// Gets a dictionary of guilds that this client is in. The dictionary's key is the guild ID. Note that the
- /// guild objects in this dictionary will not be filled in if the specific guilds aren't available (the
- /// <see cref="GuildAvailable"/> or <see cref="GuildDownloadCompleted"/> events haven't been fired yet)
- /// </summary>
- public override IReadOnlyDictionary<ulong, DiscordGuild> Guilds { get; }
- internal ConcurrentDictionary<ulong, DiscordGuild> GuildsInternal = new();
-
- /// <summary>
- /// Gets the websocket latency for this client.
- /// </summary>
- public int Ping
- => Volatile.Read(ref this._ping);
-
- private int _ping;
+ public sealed partial class DiscordClient : BaseDiscordClient
+ {
+ #region Internal Fields/Properties
+
+ internal bool IsShard = false;
+ /// <summary>
+ /// Gets the message cache.
+ /// </summary>
+ internal RingBuffer<DiscordMessage> MessageCache { get; }
+
+ private List<BaseExtension> _extensions = new();
+ private StatusUpdate _status;
+
+ /// <summary>
+ /// Gets the connection lock.
+ /// </summary>
+ private readonly ManualResetEventSlim _connectionLock = new(true);
+
+ #endregion
+
+ #region Public Fields/Properties
+ /// <summary>
+ /// Gets the gateway protocol version.
+ /// </summary>
+ public int GatewayVersion { get; internal set; }
+
+ /// <summary>
+ /// Gets the gateway session information for this client.
+ /// </summary>
+ public GatewayInfo GatewayInfo { get; internal set; }
+
+ /// <summary>
+ /// Gets the gateway URL.
+ /// </summary>
+ public Uri GatewayUri { get; internal set; }
+
+ /// <summary>
+ /// Gets the total number of shards the bot is connected to.
+ /// </summary>
+ public int ShardCount => this.GatewayInfo != null
+ ? this.GatewayInfo.ShardCount
+ : this.Configuration.ShardCount;
+
+ /// <summary>
+ /// Gets the currently connected shard ID.
+ /// </summary>
+ public int ShardId
+ => this.Configuration.ShardId;
+
+ /// <summary>
+ /// Gets the intents configured for this client.
+ /// </summary>
+ public DiscordIntents Intents
+ => this.Configuration.Intents;
+
+ /// <summary>
+ /// Gets a dictionary of guilds that this client is in. The dictionary's key is the guild ID. Note that the
+ /// guild objects in this dictionary will not be filled in if the specific guilds aren't available (the
+ /// <see cref="GuildAvailable"/> or <see cref="GuildDownloadCompleted"/> events haven't been fired yet)
+ /// </summary>
+ public override IReadOnlyDictionary<ulong, DiscordGuild> Guilds { get; }
+ internal ConcurrentDictionary<ulong, DiscordGuild> GuildsInternal = new();
+
+ /// <summary>
+ /// Gets the websocket latency for this client.
+ /// </summary>
+ public int Ping
+ => Volatile.Read(ref this._ping);
+
+ private int _ping;
+
+ /// <summary>
+ /// Gets the collection of presences held by this client.
+ /// </summary>
+ public IReadOnlyDictionary<ulong, DiscordPresence> Presences
+ => this._presencesLazy.Value;
+
+ internal Dictionary<ulong, DiscordPresence> PresencesInternal = new();
+ private Lazy<IReadOnlyDictionary<ulong, DiscordPresence>> _presencesLazy;
+
+ /// <summary>
+ /// Gets the collection of presences held by this client.
+ /// </summary>
+ public IReadOnlyDictionary<string, DiscordActivity> EmbeddedActivities
+ => this._embeddedActivitiesLazy.Value;
+
+ internal Dictionary<string, DiscordActivity> EmbeddedActivitiesInternal = new();
+ private Lazy<IReadOnlyDictionary<string, DiscordActivity>> _embeddedActivitiesLazy;
+ #endregion
+
+ #region Constructor/Internal Setup
+
+ /// <summary>
+ /// Initializes a new instance of <see cref="DiscordClient"/>.
+ /// </summary>
+ /// <param name="config">Specifies configuration parameters.</param>
+ public DiscordClient(DiscordConfiguration config)
+ : base(config)
+ {
+ if (this.Configuration.MessageCacheSize > 0)
+ {
+ var intents = this.Configuration.Intents;
+ this.MessageCache = intents.HasIntent(DiscordIntents.GuildMessages) || intents.HasIntent(DiscordIntents.DirectMessages)
+ ? new RingBuffer<DiscordMessage>(this.Configuration.MessageCacheSize)
+ : null;
+ }
- /// <summary>
- /// Gets the collection of presences held by this client.
- /// </summary>
- public IReadOnlyDictionary<ulong, DiscordPresence> Presences
- => this._presencesLazy.Value;
+ this.InternalSetup();
- internal Dictionary<ulong, DiscordPresence> PresencesInternal = new();
- private Lazy<IReadOnlyDictionary<ulong, DiscordPresence>> _presencesLazy;
+ this.Guilds = new ReadOnlyConcurrentDictionary<ulong, DiscordGuild>(this.GuildsInternal);
+ }
- /// <summary>
- /// Gets the collection of presences held by this client.
- /// </summary>
- public IReadOnlyDictionary<string, DiscordActivity> EmbeddedActivities
- => this._embeddedActivitiesLazy.Value;
+ /// <summary>
+ /// Internal setup of the Client.
+ /// </summary>
+ internal void InternalSetup()
+ {
+ this._clientErrored = new AsyncEvent<DiscordClient, ClientErrorEventArgs>("CLIENT_ERRORED", EventExecutionLimit, this.Goof);
+ this._socketErrored = new AsyncEvent<DiscordClient, SocketErrorEventArgs>("SOCKET_ERRORED", EventExecutionLimit, this.Goof);
+ this._socketOpened = new AsyncEvent<DiscordClient, SocketEventArgs>("SOCKET_OPENED", EventExecutionLimit, this.EventErrorHandler);
+ this._socketClosed = new AsyncEvent<DiscordClient, SocketCloseEventArgs>("SOCKET_CLOSED", EventExecutionLimit, this.EventErrorHandler);
+ this._ready = new AsyncEvent<DiscordClient, ReadyEventArgs>("READY", EventExecutionLimit, this.EventErrorHandler);
+ this._resumed = new AsyncEvent<DiscordClient, ReadyEventArgs>("RESUMED", EventExecutionLimit, this.EventErrorHandler);
+ this._channelCreated = new AsyncEvent<DiscordClient, ChannelCreateEventArgs>("CHANNEL_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._channelUpdated = new AsyncEvent<DiscordClient, ChannelUpdateEventArgs>("CHANNEL_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._channelDeleted = new AsyncEvent<DiscordClient, ChannelDeleteEventArgs>("CHANNEL_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._dmChannelDeleted = new AsyncEvent<DiscordClient, DmChannelDeleteEventArgs>("DM_CHANNEL_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._channelPinsUpdated = new AsyncEvent<DiscordClient, ChannelPinsUpdateEventArgs>("CHANNEL_PINS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildCreated = new AsyncEvent<DiscordClient, GuildCreateEventArgs>("GUILD_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildAvailable = new AsyncEvent<DiscordClient, GuildCreateEventArgs>("GUILD_AVAILABLE", EventExecutionLimit, this.EventErrorHandler);
+ this._guildUpdated = new AsyncEvent<DiscordClient, GuildUpdateEventArgs>("GUILD_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildDeleted = new AsyncEvent<DiscordClient, GuildDeleteEventArgs>("GUILD_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildUnavailable = new AsyncEvent<DiscordClient, GuildDeleteEventArgs>("GUILD_UNAVAILABLE", EventExecutionLimit, this.EventErrorHandler);
+ this._guildDownloadCompletedEv = new AsyncEvent<DiscordClient, GuildDownloadCompletedEventArgs>("GUILD_DOWNLOAD_COMPLETED", EventExecutionLimit, this.EventErrorHandler);
+ this._inviteCreated = new AsyncEvent<DiscordClient, InviteCreateEventArgs>("INVITE_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._inviteDeleted = new AsyncEvent<DiscordClient, InviteDeleteEventArgs>("INVITE_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._messageCreated = new AsyncEvent<DiscordClient, MessageCreateEventArgs>("MESSAGE_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._presenceUpdated = new AsyncEvent<DiscordClient, PresenceUpdateEventArgs>("PRESENCE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildBanAdded = new AsyncEvent<DiscordClient, GuildBanAddEventArgs>("GUILD_BAN_ADD", EventExecutionLimit, this.EventErrorHandler);
+ this._guildBanRemoved = new AsyncEvent<DiscordClient, GuildBanRemoveEventArgs>("GUILD_BAN_REMOVED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildEmojisUpdated = new AsyncEvent<DiscordClient, GuildEmojisUpdateEventArgs>("GUILD_EMOJI_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildStickersUpdated = new AsyncEvent<DiscordClient, GuildStickersUpdateEventArgs>("GUILD_STICKER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildIntegrationsUpdated = new AsyncEvent<DiscordClient, GuildIntegrationsUpdateEventArgs>("GUILD_INTEGRATIONS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberAdded = new AsyncEvent<DiscordClient, GuildMemberAddEventArgs>("GUILD_MEMBER_ADD", EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberRemoved = new AsyncEvent<DiscordClient, GuildMemberRemoveEventArgs>("GUILD_MEMBER_REMOVED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberUpdated = new AsyncEvent<DiscordClient, GuildMemberUpdateEventArgs>("GUILD_MEMBER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildRoleCreated = new AsyncEvent<DiscordClient, GuildRoleCreateEventArgs>("GUILD_ROLE_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildRoleUpdated = new AsyncEvent<DiscordClient, GuildRoleUpdateEventArgs>("GUILD_ROLE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildRoleDeleted = new AsyncEvent<DiscordClient, GuildRoleDeleteEventArgs>("GUILD_ROLE_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._messageAcknowledged = new AsyncEvent<DiscordClient, MessageAcknowledgeEventArgs>("MESSAGE_ACKNOWLEDGED", EventExecutionLimit, this.EventErrorHandler);
+ this._messageUpdated = new AsyncEvent<DiscordClient, MessageUpdateEventArgs>("MESSAGE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._messageDeleted = new AsyncEvent<DiscordClient, MessageDeleteEventArgs>("MESSAGE_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._messagesBulkDeleted = new AsyncEvent<DiscordClient, MessageBulkDeleteEventArgs>("MESSAGE_BULK_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._interactionCreated = new AsyncEvent<DiscordClient, InteractionCreateEventArgs>("INTERACTION_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._componentInteractionCreated = new AsyncEvent<DiscordClient, ComponentInteractionCreateEventArgs>("COMPONENT_INTERACTED", EventExecutionLimit, this.EventErrorHandler);
+ this._contextMenuInteractionCreated = new AsyncEvent<DiscordClient, ContextMenuInteractionCreateEventArgs>("CONTEXT_MENU_INTERACTED", EventExecutionLimit, this.EventErrorHandler);
+ this._typingStarted = new AsyncEvent<DiscordClient, TypingStartEventArgs>("TYPING_STARTED", EventExecutionLimit, this.EventErrorHandler);
+ this._userSettingsUpdated = new AsyncEvent<DiscordClient, UserSettingsUpdateEventArgs>("USER_SETTINGS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._userUpdated = new AsyncEvent<DiscordClient, UserUpdateEventArgs>("USER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._voiceStateUpdated = new AsyncEvent<DiscordClient, VoiceStateUpdateEventArgs>("VOICE_STATE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._voiceServerUpdated = new AsyncEvent<DiscordClient, VoiceServerUpdateEventArgs>("VOICE_SERVER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildMembersChunked = new AsyncEvent<DiscordClient, GuildMembersChunkEventArgs>("GUILD_MEMBERS_CHUNKED", EventExecutionLimit, this.EventErrorHandler);
+ this._unknownEvent = new AsyncEvent<DiscordClient, UnknownEventArgs>("UNKNOWN_EVENT", EventExecutionLimit, this.EventErrorHandler);
+ this._messageReactionAdded = new AsyncEvent<DiscordClient, MessageReactionAddEventArgs>("MESSAGE_REACTION_ADDED", EventExecutionLimit, this.EventErrorHandler);
+ this._messageReactionRemoved = new AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs>("MESSAGE_REACTION_REMOVED", EventExecutionLimit, this.EventErrorHandler);
+ this._messageReactionsCleared = new AsyncEvent<DiscordClient, MessageReactionsClearEventArgs>("MESSAGE_REACTIONS_CLEARED", EventExecutionLimit, this.EventErrorHandler);
+ this._messageReactionRemovedEmoji = new AsyncEvent<DiscordClient, MessageReactionRemoveEmojiEventArgs>("MESSAGE_REACTION_REMOVED_EMOJI", EventExecutionLimit, this.EventErrorHandler);
+ this._webhooksUpdated = new AsyncEvent<DiscordClient, WebhooksUpdateEventArgs>("WEBHOOKS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._heartbeated = new AsyncEvent<DiscordClient, HeartbeatEventArgs>("HEARTBEATED", EventExecutionLimit, this.EventErrorHandler);
+ this._applicationCommandCreated = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._applicationCommandUpdated = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._applicationCommandDeleted = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildApplicationCommandCountUpdated = new AsyncEvent<DiscordClient, GuildApplicationCommandCountEventArgs>("GUILD_APPLICATION_COMMAND_COUNTS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._applicationCommandPermissionsUpdated = new AsyncEvent<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs>("APPLICATION_COMMAND_PERMISSIONS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildIntegrationCreated = new AsyncEvent<DiscordClient, GuildIntegrationCreateEventArgs>("INTEGRATION_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildIntegrationUpdated = new AsyncEvent<DiscordClient, GuildIntegrationUpdateEventArgs>("INTEGRATION_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildIntegrationDeleted = new AsyncEvent<DiscordClient, GuildIntegrationDeleteEventArgs>("INTEGRATION_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._stageInstanceCreated = new AsyncEvent<DiscordClient, StageInstanceCreateEventArgs>("STAGE_INSTANCE_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._stageInstanceUpdated = new AsyncEvent<DiscordClient, StageInstanceUpdateEventArgs>("STAGE_INSTANCE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._stageInstanceDeleted = new AsyncEvent<DiscordClient, StageInstanceDeleteEventArgs>("STAGE_INSTANCE_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._threadCreated = new AsyncEvent<DiscordClient, ThreadCreateEventArgs>("THREAD_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._threadUpdated = new AsyncEvent<DiscordClient, ThreadUpdateEventArgs>("THREAD_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._threadDeleted = new AsyncEvent<DiscordClient, ThreadDeleteEventArgs>("THREAD_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._threadListSynced = new AsyncEvent<DiscordClient, ThreadListSyncEventArgs>("THREAD_LIST_SYNCED", EventExecutionLimit, this.EventErrorHandler);
+ this._threadMemberUpdated = new AsyncEvent<DiscordClient, ThreadMemberUpdateEventArgs>("THREAD_MEMBER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._threadMembersUpdated = new AsyncEvent<DiscordClient, ThreadMembersUpdateEventArgs>("THREAD_MEMBERS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._zombied = new AsyncEvent<DiscordClient, ZombiedEventArgs>("ZOMBIED", EventExecutionLimit, this.EventErrorHandler);
+ this._payloadReceived = new AsyncEvent<DiscordClient, PayloadReceivedEventArgs>("PAYLOAD_RECEIVED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventCreated = new AsyncEvent<DiscordClient, GuildScheduledEventCreateEventArgs>("GUILD_SCHEDULED_EVENT_CREATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventUpdated = new AsyncEvent<DiscordClient, GuildScheduledEventUpdateEventArgs>("GUILD_SCHEDULED_EVENT_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventDeleted = new AsyncEvent<DiscordClient, GuildScheduledEventDeleteEventArgs>("GUILD_SCHEDULED_EVENT_DELETED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventUserAdded = new AsyncEvent<DiscordClient, GuildScheduledEventUserAddEventArgs>("GUILD_SCHEDULED_EVENT_USER_ADDED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventUserRemoved = new AsyncEvent<DiscordClient, GuildScheduledEventUserRemoveEventArgs>("GUILD_SCHEDULED_EVENT_USER_REMOVED", EventExecutionLimit, this.EventErrorHandler);
+ this._embeddedActivityUpdated = new AsyncEvent<DiscordClient, EmbeddedActivityUpdateEventArgs>("EMBEDDED_ACTIVITY_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberTimeoutAdded = new AsyncEvent<DiscordClient, GuildMemberTimeoutAddEventArgs>("GUILD_MEMBER_TIMEOUT_ADDED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberTimeoutChanged = new AsyncEvent<DiscordClient, GuildMemberTimeoutUpdateEventArgs>("GUILD_MEMBER_TIMEOUT_UPDATED", EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberTimeoutRemoved = new AsyncEvent<DiscordClient, GuildMemberTimeoutRemoveEventArgs>("GUILD_MEMBER_TIMEOUT_REMOVED", EventExecutionLimit, this.EventErrorHandler);
+ this._rateLimitHit = new AsyncEvent<DiscordClient, RateLimitExceptionEventArgs>("RATELIMIT_HIT", EventExecutionLimit, this.EventErrorHandler);
+
+ this.GuildsInternal.Clear();
+
+ this._presencesLazy = new Lazy<IReadOnlyDictionary<ulong, DiscordPresence>>(() => new ReadOnlyDictionary<ulong, DiscordPresence>(this.PresencesInternal));
+ this._embeddedActivitiesLazy = new Lazy<IReadOnlyDictionary<string, DiscordActivity>>(() => new ReadOnlyDictionary<string, DiscordActivity>(this.EmbeddedActivitiesInternal));
+ }
- internal Dictionary<string, DiscordActivity> EmbeddedActivitiesInternal = new();
- private Lazy<IReadOnlyDictionary<string, DiscordActivity>> _embeddedActivitiesLazy;
- #endregion
+ #endregion
- #region Constructor/Internal Setup
+ #region Client Extension Methods
- /// <summary>
- /// Initializes a new instance of <see cref="DiscordClient"/>.
- /// </summary>
- /// <param name="config">Specifies configuration parameters.</param>
- public DiscordClient(DiscordConfiguration config)
- : base(config)
- {
- if (this.Configuration.MessageCacheSize > 0)
+ /// <summary>
+ /// Registers an extension with this client.
+ /// </summary>
+ /// <param name="ext">Extension to register.</param>
+ public void AddExtension(BaseExtension ext)
{
- var intents = this.Configuration.Intents;
- this.MessageCache = intents.HasIntent(DiscordIntents.GuildMessages) || intents.HasIntent(DiscordIntents.DirectMessages)
- ? new RingBuffer<DiscordMessage>(this.Configuration.MessageCacheSize)
- : null;
+ ext.Setup(this);
+ this._extensions.Add(ext);
}
- this.InternalSetup();
-
- this.Guilds = new ReadOnlyConcurrentDictionary<ulong, DiscordGuild>(this.GuildsInternal);
- }
+ /// <summary>
+ /// Retrieves a previously registered extension from this client.
+ /// </summary>
+ /// <typeparam name="T">The type of extension to retrieve.</typeparam>
+ /// <returns>The requested extension.</returns>
+ public T GetExtension<T>() where T : BaseExtension
+ => this._extensions.FirstOrDefault(x => x.GetType() == typeof(T)) as T;
+
+ #endregion
+
+ #region Public Connection Methods
+
+ /// <summary>
+ /// Connects to the gateway.
+ /// </summary>
+ /// <param name="activity">The activity to set. Defaults to null.</param>
+ /// <param name="status">The optional status to set. Defaults to null.</param>
+ /// <param name="idlesince">Since when is the client performing the specified activity. Defaults to null.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when an invalid token was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task ConnectAsync(DiscordActivity activity = null, UserStatus? status = null, DateTimeOffset? idlesince = null)
+ {
+ // Check if connection lock is already set, and set it if it isn't
+ if (!this._connectionLock.Wait(0))
+ throw new InvalidOperationException("This client is already connected.");
+ this._connectionLock.Set();
- /// <summary>
- /// Internal setup of the Client.
- /// </summary>
- internal void InternalSetup()
- {
- this._clientErrored = new AsyncEvent<DiscordClient, ClientErrorEventArgs>("CLIENT_ERRORED", EventExecutionLimit, this.Goof);
- this._socketErrored = new AsyncEvent<DiscordClient, SocketErrorEventArgs>("SOCKET_ERRORED", EventExecutionLimit, this.Goof);
- this._socketOpened = new AsyncEvent<DiscordClient, SocketEventArgs>("SOCKET_OPENED", EventExecutionLimit, this.EventErrorHandler);
- this._socketClosed = new AsyncEvent<DiscordClient, SocketCloseEventArgs>("SOCKET_CLOSED", EventExecutionLimit, this.EventErrorHandler);
- this._ready = new AsyncEvent<DiscordClient, ReadyEventArgs>("READY", EventExecutionLimit, this.EventErrorHandler);
- this._resumed = new AsyncEvent<DiscordClient, ReadyEventArgs>("RESUMED", EventExecutionLimit, this.EventErrorHandler);
- this._channelCreated = new AsyncEvent<DiscordClient, ChannelCreateEventArgs>("CHANNEL_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._channelUpdated = new AsyncEvent<DiscordClient, ChannelUpdateEventArgs>("CHANNEL_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._channelDeleted = new AsyncEvent<DiscordClient, ChannelDeleteEventArgs>("CHANNEL_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._dmChannelDeleted = new AsyncEvent<DiscordClient, DmChannelDeleteEventArgs>("DM_CHANNEL_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._channelPinsUpdated = new AsyncEvent<DiscordClient, ChannelPinsUpdateEventArgs>("CHANNEL_PINS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildCreated = new AsyncEvent<DiscordClient, GuildCreateEventArgs>("GUILD_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildAvailable = new AsyncEvent<DiscordClient, GuildCreateEventArgs>("GUILD_AVAILABLE", EventExecutionLimit, this.EventErrorHandler);
- this._guildUpdated = new AsyncEvent<DiscordClient, GuildUpdateEventArgs>("GUILD_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildDeleted = new AsyncEvent<DiscordClient, GuildDeleteEventArgs>("GUILD_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._guildUnavailable = new AsyncEvent<DiscordClient, GuildDeleteEventArgs>("GUILD_UNAVAILABLE", EventExecutionLimit, this.EventErrorHandler);
- this._guildDownloadCompletedEv = new AsyncEvent<DiscordClient, GuildDownloadCompletedEventArgs>("GUILD_DOWNLOAD_COMPLETED", EventExecutionLimit, this.EventErrorHandler);
- this._inviteCreated = new AsyncEvent<DiscordClient, InviteCreateEventArgs>("INVITE_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._inviteDeleted = new AsyncEvent<DiscordClient, InviteDeleteEventArgs>("INVITE_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._messageCreated = new AsyncEvent<DiscordClient, MessageCreateEventArgs>("MESSAGE_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._presenceUpdated = new AsyncEvent<DiscordClient, PresenceUpdateEventArgs>("PRESENCE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildBanAdded = new AsyncEvent<DiscordClient, GuildBanAddEventArgs>("GUILD_BAN_ADD", EventExecutionLimit, this.EventErrorHandler);
- this._guildBanRemoved = new AsyncEvent<DiscordClient, GuildBanRemoveEventArgs>("GUILD_BAN_REMOVED", EventExecutionLimit, this.EventErrorHandler);
- this._guildEmojisUpdated = new AsyncEvent<DiscordClient, GuildEmojisUpdateEventArgs>("GUILD_EMOJI_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildStickersUpdated = new AsyncEvent<DiscordClient, GuildStickersUpdateEventArgs>("GUILD_STICKER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildIntegrationsUpdated = new AsyncEvent<DiscordClient, GuildIntegrationsUpdateEventArgs>("GUILD_INTEGRATIONS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberAdded = new AsyncEvent<DiscordClient, GuildMemberAddEventArgs>("GUILD_MEMBER_ADD", EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberRemoved = new AsyncEvent<DiscordClient, GuildMemberRemoveEventArgs>("GUILD_MEMBER_REMOVED", EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberUpdated = new AsyncEvent<DiscordClient, GuildMemberUpdateEventArgs>("GUILD_MEMBER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildRoleCreated = new AsyncEvent<DiscordClient, GuildRoleCreateEventArgs>("GUILD_ROLE_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildRoleUpdated = new AsyncEvent<DiscordClient, GuildRoleUpdateEventArgs>("GUILD_ROLE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildRoleDeleted = new AsyncEvent<DiscordClient, GuildRoleDeleteEventArgs>("GUILD_ROLE_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._messageAcknowledged = new AsyncEvent<DiscordClient, MessageAcknowledgeEventArgs>("MESSAGE_ACKNOWLEDGED", EventExecutionLimit, this.EventErrorHandler);
- this._messageUpdated = new AsyncEvent<DiscordClient, MessageUpdateEventArgs>("MESSAGE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._messageDeleted = new AsyncEvent<DiscordClient, MessageDeleteEventArgs>("MESSAGE_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._messagesBulkDeleted = new AsyncEvent<DiscordClient, MessageBulkDeleteEventArgs>("MESSAGE_BULK_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._interactionCreated = new AsyncEvent<DiscordClient, InteractionCreateEventArgs>("INTERACTION_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._componentInteractionCreated = new AsyncEvent<DiscordClient, ComponentInteractionCreateEventArgs>("COMPONENT_INTERACTED", EventExecutionLimit, this.EventErrorHandler);
- this._contextMenuInteractionCreated = new AsyncEvent<DiscordClient, ContextMenuInteractionCreateEventArgs>("CONTEXT_MENU_INTERACTED", EventExecutionLimit, this.EventErrorHandler);
- this._typingStarted = new AsyncEvent<DiscordClient, TypingStartEventArgs>("TYPING_STARTED", EventExecutionLimit, this.EventErrorHandler);
- this._userSettingsUpdated = new AsyncEvent<DiscordClient, UserSettingsUpdateEventArgs>("USER_SETTINGS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._userUpdated = new AsyncEvent<DiscordClient, UserUpdateEventArgs>("USER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._voiceStateUpdated = new AsyncEvent<DiscordClient, VoiceStateUpdateEventArgs>("VOICE_STATE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._voiceServerUpdated = new AsyncEvent<DiscordClient, VoiceServerUpdateEventArgs>("VOICE_SERVER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildMembersChunked = new AsyncEvent<DiscordClient, GuildMembersChunkEventArgs>("GUILD_MEMBERS_CHUNKED", EventExecutionLimit, this.EventErrorHandler);
- this._unknownEvent = new AsyncEvent<DiscordClient, UnknownEventArgs>("UNKNOWN_EVENT", EventExecutionLimit, this.EventErrorHandler);
- this._messageReactionAdded = new AsyncEvent<DiscordClient, MessageReactionAddEventArgs>("MESSAGE_REACTION_ADDED", EventExecutionLimit, this.EventErrorHandler);
- this._messageReactionRemoved = new AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs>("MESSAGE_REACTION_REMOVED", EventExecutionLimit, this.EventErrorHandler);
- this._messageReactionsCleared = new AsyncEvent<DiscordClient, MessageReactionsClearEventArgs>("MESSAGE_REACTIONS_CLEARED", EventExecutionLimit, this.EventErrorHandler);
- this._messageReactionRemovedEmoji = new AsyncEvent<DiscordClient, MessageReactionRemoveEmojiEventArgs>("MESSAGE_REACTION_REMOVED_EMOJI", EventExecutionLimit, this.EventErrorHandler);
- this._webhooksUpdated = new AsyncEvent<DiscordClient, WebhooksUpdateEventArgs>("WEBHOOKS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._heartbeated = new AsyncEvent<DiscordClient, HeartbeatEventArgs>("HEARTBEATED", EventExecutionLimit, this.EventErrorHandler);
- this._applicationCommandCreated = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._applicationCommandUpdated = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._applicationCommandDeleted = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._guildApplicationCommandCountUpdated = new AsyncEvent<DiscordClient, GuildApplicationCommandCountEventArgs>("GUILD_APPLICATION_COMMAND_COUNTS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._applicationCommandPermissionsUpdated = new AsyncEvent<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs>("APPLICATION_COMMAND_PERMISSIONS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildIntegrationCreated = new AsyncEvent<DiscordClient, GuildIntegrationCreateEventArgs>("INTEGRATION_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildIntegrationUpdated = new AsyncEvent<DiscordClient, GuildIntegrationUpdateEventArgs>("INTEGRATION_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildIntegrationDeleted = new AsyncEvent<DiscordClient, GuildIntegrationDeleteEventArgs>("INTEGRATION_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._stageInstanceCreated = new AsyncEvent<DiscordClient, StageInstanceCreateEventArgs>("STAGE_INSTANCE_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._stageInstanceUpdated = new AsyncEvent<DiscordClient, StageInstanceUpdateEventArgs>("STAGE_INSTANCE_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._stageInstanceDeleted = new AsyncEvent<DiscordClient, StageInstanceDeleteEventArgs>("STAGE_INSTANCE_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._threadCreated = new AsyncEvent<DiscordClient, ThreadCreateEventArgs>("THREAD_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._threadUpdated = new AsyncEvent<DiscordClient, ThreadUpdateEventArgs>("THREAD_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._threadDeleted = new AsyncEvent<DiscordClient, ThreadDeleteEventArgs>("THREAD_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._threadListSynced = new AsyncEvent<DiscordClient, ThreadListSyncEventArgs>("THREAD_LIST_SYNCED", EventExecutionLimit, this.EventErrorHandler);
- this._threadMemberUpdated = new AsyncEvent<DiscordClient, ThreadMemberUpdateEventArgs>("THREAD_MEMBER_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._threadMembersUpdated = new AsyncEvent<DiscordClient, ThreadMembersUpdateEventArgs>("THREAD_MEMBERS_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._zombied = new AsyncEvent<DiscordClient, ZombiedEventArgs>("ZOMBIED", EventExecutionLimit, this.EventErrorHandler);
- this._payloadReceived = new AsyncEvent<DiscordClient, PayloadReceivedEventArgs>("PAYLOAD_RECEIVED", EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventCreated = new AsyncEvent<DiscordClient, GuildScheduledEventCreateEventArgs>("GUILD_SCHEDULED_EVENT_CREATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventUpdated = new AsyncEvent<DiscordClient, GuildScheduledEventUpdateEventArgs>("GUILD_SCHEDULED_EVENT_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventDeleted = new AsyncEvent<DiscordClient, GuildScheduledEventDeleteEventArgs>("GUILD_SCHEDULED_EVENT_DELETED", EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventUserAdded = new AsyncEvent<DiscordClient, GuildScheduledEventUserAddEventArgs>("GUILD_SCHEDULED_EVENT_USER_ADDED", EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventUserRemoved = new AsyncEvent<DiscordClient, GuildScheduledEventUserRemoveEventArgs>("GUILD_SCHEDULED_EVENT_USER_REMOVED", EventExecutionLimit, this.EventErrorHandler);
- this._embeddedActivityUpdated = new AsyncEvent<DiscordClient, EmbeddedActivityUpdateEventArgs>("EMBEDDED_ACTIVITY_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberTimeoutAdded = new AsyncEvent<DiscordClient, GuildMemberTimeoutAddEventArgs>("GUILD_MEMBER_TIMEOUT_ADDED", EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberTimeoutChanged = new AsyncEvent<DiscordClient, GuildMemberTimeoutUpdateEventArgs>("GUILD_MEMBER_TIMEOUT_UPDATED", EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberTimeoutRemoved = new AsyncEvent<DiscordClient, GuildMemberTimeoutRemoveEventArgs>("GUILD_MEMBER_TIMEOUT_REMOVED", EventExecutionLimit, this.EventErrorHandler);
- this._rateLimitHit = new AsyncEvent<DiscordClient, RateLimitExceptionEventArgs>("RATELIMIT_HIT", EventExecutionLimit, this.EventErrorHandler);
-
- this.GuildsInternal.Clear();
-
- this._presencesLazy = new Lazy<IReadOnlyDictionary<ulong, DiscordPresence>>(() => new ReadOnlyDictionary<ulong, DiscordPresence>(this.PresencesInternal));
- this._embeddedActivitiesLazy = new Lazy<IReadOnlyDictionary<string, DiscordActivity>>(() => new ReadOnlyDictionary<string, DiscordActivity>(this.EmbeddedActivitiesInternal));
- }
+ var w = 7500;
+ var i = 5;
+ var s = false;
+ Exception cex = null;
- #endregion
+ if (activity == null && status == null && idlesince == null)
+ this._status = null;
+ else
+ {
+ var sinceUnix = idlesince != null ? (long?)Utilities.GetUnixTime(idlesince.Value) : null;
+ this._status = new StatusUpdate()
+ {
+ Activity = new TransportActivity(activity),
+ Status = status ?? UserStatus.Online,
+ IdleSince = sinceUnix,
+ IsAfk = idlesince != null,
+ ActivityInternal = activity
+ };
+ }
- #region Client Extension Methods
+ if (!this.IsShard)
+ {
+ if (this.Configuration.TokenType != TokenType.Bot)
+ this.Logger.LogWarning(LoggerEvents.Misc, "You are logging in with a token that is not a bot token. This is not officially supported by Discord, and can result in your account being terminated if you aren't careful.");
+ this.Logger.LogInformation(LoggerEvents.Startup, "Lib {0}, version {1}", this.BotLibrary, this.VersionString);
+ }
- /// <summary>
- /// Registers an extension with this client.
- /// </summary>
- /// <param name="ext">Extension to register.</param>
- public void AddExtension(BaseExtension ext)
- {
- ext.Setup(this);
- this._extensions.Add(ext);
- }
+ while (i-- > 0 || this.Configuration.ReconnectIndefinitely)
+ {
+ try
+ {
+ await this.InternalConnectAsync().ConfigureAwait(false);
+ s = true;
+ break;
+ }
+ catch (UnauthorizedException e)
+ {
+ FailConnection(this._connectionLock);
+ throw new Exception("Authentication failed. Check your token and try again.", e);
+ }
+ catch (PlatformNotSupportedException)
+ {
+ FailConnection(this._connectionLock);
+ throw;
+ }
+ catch (NotImplementedException)
+ {
+ FailConnection(this._connectionLock);
+ throw;
+ }
+ catch (Exception ex)
+ {
+ FailConnection(null);
- /// <summary>
- /// Retrieves a previously registered extension from this client.
- /// </summary>
- /// <typeparam name="T">The type of extension to retrieve.</typeparam>
- /// <returns>The requested extension.</returns>
- public T GetExtension<T>() where T : BaseExtension
- => this._extensions.FirstOrDefault(x => x.GetType() == typeof(T)) as T;
+ cex = ex;
+ if (i <= 0 && !this.Configuration.ReconnectIndefinitely) break;
- #endregion
+ this.Logger.LogError(LoggerEvents.ConnectionFailure, ex, "Connection attempt failed, retrying in {0}s", w / 1000);
+ await Task.Delay(w).ConfigureAwait(false);
- #region Public Connection Methods
+ if (i > 0)
+ w *= 2;
+ }
+ }
- /// <summary>
- /// Connects to the gateway.
- /// </summary>
- /// <param name="activity">The activity to set. Defaults to null.</param>
- /// <param name="status">The optional status to set. Defaults to null.</param>
- /// <param name="idlesince">Since when is the client performing the specified activity. Defaults to null.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when an invalid token was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task ConnectAsync(DiscordActivity activity = null, UserStatus? status = null, DateTimeOffset? idlesince = null)
- {
- // Check if connection lock is already set, and set it if it isn't
- if (!this._connectionLock.Wait(0))
- throw new InvalidOperationException("This client is already connected.");
- this._connectionLock.Set();
-
- var w = 7500;
- var i = 5;
- var s = false;
- Exception cex = null;
-
- if (activity == null && status == null && idlesince == null)
- this._status = null;
- else
- {
- var sinceUnix = idlesince != null ? (long?)Utilities.GetUnixTime(idlesince.Value) : null;
- this._status = new StatusUpdate()
+ if (!s && cex != null)
{
- Activity = new TransportActivity(activity),
- Status = status ?? UserStatus.Online,
- IdleSince = sinceUnix,
- IsAfk = idlesince != null,
- ActivityInternal = activity
- };
+ this._connectionLock.Set();
+ throw new Exception("Could not connect to Discord.", cex);
+ }
+
+ // non-closure, hence args
+ static void FailConnection(ManualResetEventSlim cl) =>
+ // unlock this (if applicable) so we can let others attempt to connect
+ cl?.Set();
}
- if (!this.IsShard)
+ /// <summary>
+ /// Reconnects to the gateway.
+ /// </summary>
+ /// <param name="startNewSession">Whether to start a new session.</param>
+ public Task ReconnectAsync(bool startNewSession = false)
+ => this.InternalReconnectAsync(startNewSession, code: startNewSession ? 1000 : 4002);
+
+ /// <summary>
+ /// Disconnects from the gateway.
+ /// </summary>
+ public async Task DisconnectAsync()
{
- if (this.Configuration.TokenType != TokenType.Bot)
- this.Logger.LogWarning(LoggerEvents.Misc, "You are logging in with a token that is not a bot token. This is not officially supported by Discord, and can result in your account being terminated if you aren't careful.");
- this.Logger.LogInformation(LoggerEvents.Startup, "Lib {0}, version {1}", this.BotLibrary, this.VersionString);
+ this.Configuration.AutoReconnect = false;
+ if (this.WebSocketClient != null)
+ await this.WebSocketClient.DisconnectAsync().ConfigureAwait(false);
}
- while (i-- > 0 || this.Configuration.ReconnectIndefinitely)
+ #endregion
+
+ #region Public REST Methods
+
+ /// <summary>
+ /// Gets a user.
+ /// </summary>
+ /// <param name="userId">Id of the user</param>
+ /// <param name="fetch">Whether to fetch the user again. Defaults to true.</param>
+ /// <returns>The requested user.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordUser> GetUserAsync(ulong userId, bool fetch = true)
{
- try
- {
- await this.InternalConnectAsync().ConfigureAwait(false);
- s = true;
- break;
- }
- catch (UnauthorizedException e)
+ if (!fetch)
{
- FailConnection(this._connectionLock);
- throw new Exception("Authentication failed. Check your token and try again.", e);
+ return this.TryGetCachedUserInternal(userId, out var usr) ? usr : new DiscordUser { Id = userId, Discord = this };
}
- catch (PlatformNotSupportedException)
+ else
{
- FailConnection(this._connectionLock);
- throw;
- }
- catch (NotImplementedException)
- {
- FailConnection(this._connectionLock);
- throw;
- }
- catch (Exception ex)
- {
- FailConnection(null);
-
- cex = ex;
- if (i <= 0 && !this.Configuration.ReconnectIndefinitely) break;
-
- this.Logger.LogError(LoggerEvents.ConnectionFailure, ex, "Connection attempt failed, retrying in {0}s", w / 1000);
- await Task.Delay(w).ConfigureAwait(false);
+ var usr = await this.ApiClient.GetUserAsync(userId).ConfigureAwait(false);
+ usr = this.UserCache.AddOrUpdate(userId, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ old.BannerHash = usr.BannerHash;
+ old.BannerColorInternal = usr.BannerColorInternal;
+ return old;
+ });
- if (i > 0)
- w *= 2;
+ return usr;
}
}
- if (!s && cex != null)
+ /// <summary>
+ /// Gets a channel.
+ /// </summary>
+ /// <param name="id">The id of the channel to get.</param>
+ /// <returns>The requested channel.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordChannel> GetChannelAsync(ulong id)
+ => this.InternalGetCachedChannel(id) ?? await this.ApiClient.GetChannelAsync(id).ConfigureAwait(false);
+
+ /// <summary>
+ /// Gets a thread.
+ /// </summary>
+ /// <param name="id">The id of the thread to get.</param>
+ /// <returns>The requested thread.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordThreadChannel> GetThreadAsync(ulong id)
+ => this.InternalGetCachedThread(id) ?? await this.ApiClient.GetThreadAsync(id).ConfigureAwait(false);
+
+ /// <summary>
+ /// Sends a normal message.
+ /// </summary>
+ /// <param name="channel">The channel to send to.</param>
+ /// <param name="content">The message content to send.</param>
+ /// <returns>The message that was sent.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, string content)
+ => this.ApiClient.CreateMessageAsync(channel.Id, content, embeds: null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Sends a message with an embed.
+ /// </summary>
+ /// <param name="channel">The channel to send to.</param>
+ /// <param name="embed">The embed to attach to the message.</param>
+ /// <returns>The message that was sent.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, DiscordEmbed embed)
+ => this.ApiClient.CreateMessageAsync(channel.Id, null, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Sends a message with content and an embed.
+ /// </summary>
+ /// <param name="channel">Channel to send to.</param>
+ /// <param name="content">The message content to send.</param>
+ /// <param name="embed">The embed to attach to the message.</param>
+ /// <returns>The message that was sent.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, string content, DiscordEmbed embed)
+ => this.ApiClient.CreateMessageAsync(channel.Id, content, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Sends a message with the <see cref="DisCatSharp.Entities.DiscordMessageBuilder"/>.
+ /// </summary>
+ /// <param name="channel">The channel to send the message to.</param>
+ /// <param name="builder">The message builder.</param>
+ /// <returns>The message that was sent.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission if TTS is false and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, DiscordMessageBuilder builder)
+ => this.ApiClient.CreateMessageAsync(channel.Id, builder);
+
+ /// <summary>
+ /// Sends a message with an <see cref="System.Action{DiscordMessageBuilder}"/>.
+ /// </summary>
+ /// <param name="channel">The channel to send the message to.</param>
+ /// <param name="action">The message builder.</param>
+ /// <returns>The message that was sent.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission if TTS is false and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, Action<DiscordMessageBuilder> action)
{
- this._connectionLock.Set();
- throw new Exception("Could not connect to Discord.", cex);
- }
-
- // non-closure, hence args
- static void FailConnection(ManualResetEventSlim cl) =>
- // unlock this (if applicable) so we can let others attempt to connect
- cl?.Set();
- }
-
- /// <summary>
- /// Reconnects to the gateway.
- /// </summary>
- /// <param name="startNewSession">Whether to start a new session.</param>
- public Task ReconnectAsync(bool startNewSession = false)
- => this.InternalReconnectAsync(startNewSession, code: startNewSession ? 1000 : 4002);
-
- /// <summary>
- /// Disconnects from the gateway.
- /// </summary>
- public async Task DisconnectAsync()
- {
- this.Configuration.AutoReconnect = false;
- if (this.WebSocketClient != null)
- await this.WebSocketClient.DisconnectAsync().ConfigureAwait(false);
- }
-
- #endregion
+ var builder = new DiscordMessageBuilder();
+ action(builder);
- #region Public REST Methods
+ return this.ApiClient.CreateMessageAsync(channel.Id, builder);
+ }
- /// <summary>
- /// Gets a user.
- /// </summary>
- /// <param name="userId">Id of the user</param>
- /// <param name="fetch">Whether to fetch the user again. Defaults to true.</param>
- /// <returns>The requested user.</returns>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordUser> GetUserAsync(ulong userId, bool fetch = true)
- {
- if (!fetch)
+ /// <summary>
+ /// Creates a guild. This requires the bot to be in less than 10 guilds total.
+ /// </summary>
+ /// <param name="name">Name of the guild.</param>
+ /// <param name="region">Voice region of the guild.</param>
+ /// <param name="icon">Stream containing the icon for the guild.</param>
+ /// <param name="verificationLevel">Verification level for the guild.</param>
+ /// <param name="defaultMessageNotifications">Default message notification settings for the guild.</param>
+ /// <param name="systemChannelFlags">System channel flags fopr the guild.</param>
+ /// <returns>The created guild.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuild> CreateGuildAsync(string name, string region = null, Optional<Stream> icon = default, VerificationLevel? verificationLevel = null,
+ DefaultMessageNotifications? defaultMessageNotifications = null, SystemChannelFlags? systemChannelFlags = null)
{
- return this.TryGetCachedUserInternal(userId, out var usr) ? usr : new DiscordUser { Id = userId, Discord = this };
+ var iconb64 = ImageTool.Base64FromStream(icon);
+ return this.ApiClient.CreateGuildAsync(name, region, iconb64, verificationLevel, defaultMessageNotifications, systemChannelFlags);
}
- else
- {
- var usr = await this.ApiClient.GetUserAsync(userId).ConfigureAwait(false);
- usr = this.UserCache.AddOrUpdate(userId, usr, (id, old) =>
- {
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- old.BannerHash = usr.BannerHash;
- old.BannerColorInternal = usr.BannerColorInternal;
- return old;
- });
- return usr;
+ /// <summary>
+ /// Creates a guild from a template. This requires the bot to be in less than 10 guilds total.
+ /// </summary>
+ /// <param name="code">The template code.</param>
+ /// <param name="name">Name of the guild.</param>
+ /// <param name="icon">Stream containing the icon for the guild.</param>
+ /// <returns>The created guild.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuild> CreateGuildFromTemplateAsync(string code, string name, Optional<Stream> icon = default)
+ {
+ var iconb64 = ImageTool.Base64FromStream(icon);
+ return this.ApiClient.CreateGuildFromTemplateAsync(code, name, iconb64);
}
- }
-
- /// <summary>
- /// Gets a channel.
- /// </summary>
- /// <param name="id">The id of the channel to get.</param>
- /// <returns>The requested channel.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordChannel> GetChannelAsync(ulong id)
- => this.InternalGetCachedChannel(id) ?? await this.ApiClient.GetChannelAsync(id).ConfigureAwait(false);
- /// <summary>
- /// Gets a thread.
- /// </summary>
- /// <param name="id">The id of the thread to get.</param>
- /// <returns>The requested thread.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordThreadChannel> GetThreadAsync(ulong id)
- => this.InternalGetCachedThread(id) ?? await this.ApiClient.GetThreadAsync(id).ConfigureAwait(false);
-
- /// <summary>
- /// Sends a normal message.
- /// </summary>
- /// <param name="channel">The channel to send to.</param>
- /// <param name="content">The message content to send.</param>
- /// <returns>The message that was sent.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, string content)
- => this.ApiClient.CreateMessageAsync(channel.Id, content, embeds: null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
-
- /// <summary>
- /// Sends a message with an embed.
- /// </summary>
- /// <param name="channel">The channel to send to.</param>
- /// <param name="embed">The embed to attach to the message.</param>
- /// <returns>The message that was sent.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, DiscordEmbed embed)
- => this.ApiClient.CreateMessageAsync(channel.Id, null, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
-
- /// <summary>
- /// Sends a message with content and an embed.
- /// </summary>
- /// <param name="channel">Channel to send to.</param>
- /// <param name="content">The message content to send.</param>
- /// <param name="embed">The embed to attach to the message.</param>
- /// <returns>The message that was sent.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, string content, DiscordEmbed embed)
- => this.ApiClient.CreateMessageAsync(channel.Id, content, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
-
- /// <summary>
- /// Sends a message with the <see cref="DisCatSharp.Entities.DiscordMessageBuilder"/>.
- /// </summary>
- /// <param name="channel">The channel to send the message to.</param>
- /// <param name="builder">The message builder.</param>
- /// <returns>The message that was sent.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission if TTS is false and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, DiscordMessageBuilder builder)
- => this.ApiClient.CreateMessageAsync(channel.Id, builder);
-
- /// <summary>
- /// Sends a message with an <see cref="System.Action{DiscordMessageBuilder}"/>.
- /// </summary>
- /// <param name="channel">The channel to send the message to.</param>
- /// <param name="action">The message builder.</param>
- /// <returns>The message that was sent.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission if TTS is false and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(DiscordChannel channel, Action<DiscordMessageBuilder> action)
- {
- var builder = new DiscordMessageBuilder();
- action(builder);
-
- return this.ApiClient.CreateMessageAsync(channel.Id, builder);
- }
-
- /// <summary>
- /// Creates a guild. This requires the bot to be in less than 10 guilds total.
- /// </summary>
- /// <param name="name">Name of the guild.</param>
- /// <param name="region">Voice region of the guild.</param>
- /// <param name="icon">Stream containing the icon for the guild.</param>
- /// <param name="verificationLevel">Verification level for the guild.</param>
- /// <param name="defaultMessageNotifications">Default message notification settings for the guild.</param>
- /// <param name="systemChannelFlags">System channel flags fopr the guild.</param>
- /// <returns>The created guild.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuild> CreateGuildAsync(string name, string region = null, Optional<Stream> icon = default, VerificationLevel? verificationLevel = null,
- DefaultMessageNotifications? defaultMessageNotifications = null, SystemChannelFlags? systemChannelFlags = null)
- {
- var iconb64 = ImageTool.Base64FromStream(icon);
- return this.ApiClient.CreateGuildAsync(name, region, iconb64, verificationLevel, defaultMessageNotifications, systemChannelFlags);
- }
+ /// <summary>
+ /// Executes a raw request.
+ /// </summary>
+ /// <example>
+ /// <c>
+ /// var request = await Client.ExecuteRawRequestAsync(RestRequestMethod.GET, $"{Endpoints.CHANNELS}/243184972190742178964/{Endpoints.INVITES}");
+ /// List&lt;DiscordInvite&gt; invites = DiscordJson.ToDiscordObject&lt;List&lt;DiscordInvite&gt;&gt;(request.Response);
+ /// </c>
+ /// </example>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <param name="routeParams">The route parameters.</param>
+ /// <param name="jsonBody">The json body.</param>
+ /// <param name="additionalHeaders">The addditional headers.</param>
+ /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the ressource does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <returns>A awaitable RestResponse</returns>
+ [Obsolete("This is no longer needed. Use DiscordClient.RestClient instead.", false)]
+ public async Task<RestResponse> ExecuteRawRequestAsync(RestRequestMethod method, string route, object routeParams, string jsonBody = null, Dictionary<string, string> additionalHeaders = null, double? ratelimitWaitOverride = null)
+ {
+ var bucket = this.ApiClient.Rest.GetBucket(method, route, routeParams, out var path);
- /// <summary>
- /// Creates a guild from a template. This requires the bot to be in less than 10 guilds total.
- /// </summary>
- /// <param name="code">The template code.</param>
- /// <param name="name">Name of the guild.</param>
- /// <param name="icon">Stream containing the icon for the guild.</param>
- /// <returns>The created guild.</returns>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuild> CreateGuildFromTemplateAsync(string code, string name, Optional<Stream> icon = default)
- {
- var iconb64 = ImageTool.Base64FromStream(icon);
- return this.ApiClient.CreateGuildFromTemplateAsync(code, name, iconb64);
- }
+ var url = Utilities.GetApiUriFor(path, this.Configuration);
+ var res = await this.ApiClient.DoRequestAsync(this, bucket, url, method, route, additionalHeaders, DiscordJson.SerializeObject(jsonBody), ratelimitWaitOverride);
- /// <summary>
- /// Executes a raw request.
- /// </summary>
- /// <example>
- /// <c>
- /// var request = await Client.ExecuteRawRequestAsync(RestRequestMethod.GET, $"{Endpoints.CHANNELS}/243184972190742178964/{Endpoints.INVITES}");
- /// List&lt;DiscordInvite&gt; invites = DiscordJson.ToDiscordObject&lt;List&lt;DiscordInvite&gt;&gt;(request.Response);
- /// </c>
- /// </example>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <param name="routeParams">The route parameters.</param>
- /// <param name="jsonBody">The json body.</param>
- /// <param name="additionalHeaders">The addditional headers.</param>
- /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the ressource does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <returns>A awaitable RestResponse</returns>
- [Obsolete("This is no longer needed. Use DiscordClient.RestClient instead.", false)]
- public async Task<RestResponse> ExecuteRawRequestAsync(RestRequestMethod method, string route, object routeParams, string jsonBody = null, Dictionary<string, string> additionalHeaders = null, double? ratelimitWaitOverride = null)
- {
- var bucket = this.ApiClient.Rest.GetBucket(method, route, routeParams, out var path);
+ return res;
+ }
- var url = Utilities.GetApiUriFor(path, this.Configuration);
- var res = await this.ApiClient.DoRequestAsync(this, bucket, url, method, route, additionalHeaders, DiscordJson.SerializeObject(jsonBody), ratelimitWaitOverride);
+ /// <summary>
+ /// Gets a guild.
+ /// <para>Setting <paramref name="withCounts"/> to true will make a REST request.</para>
+ /// </summary>
+ /// <param name="id">The guild ID to search for.</param>
+ /// <param name="withCounts">Whether to include approximate presence and member counts in the returned guild.</param>
+ /// <returns>The requested Guild.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordGuild> GetGuildAsync(ulong id, bool? withCounts = null)
+ {
+ if (this.GuildsInternal.TryGetValue(id, out var guild) && (!withCounts.HasValue || !withCounts.Value))
+ return guild;
- return res;
- }
+ guild = await this.ApiClient.GetGuildAsync(id, withCounts).ConfigureAwait(false);
+ var channels = await this.ApiClient.GetGuildChannelsAsync(guild.Id).ConfigureAwait(false);
+ foreach (var channel in channels) guild.ChannelsInternal[channel.Id] = channel;
- /// <summary>
- /// Gets a guild.
- /// <para>Setting <paramref name="withCounts"/> to true will make a REST request.</para>
- /// </summary>
- /// <param name="id">The guild ID to search for.</param>
- /// <param name="withCounts">Whether to include approximate presence and member counts in the returned guild.</param>
- /// <returns>The requested Guild.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordGuild> GetGuildAsync(ulong id, bool? withCounts = null)
- {
- if (this.GuildsInternal.TryGetValue(id, out var guild) && (!withCounts.HasValue || !withCounts.Value))
return guild;
+ }
- guild = await this.ApiClient.GetGuildAsync(id, withCounts).ConfigureAwait(false);
- var channels = await this.ApiClient.GetGuildChannelsAsync(guild.Id).ConfigureAwait(false);
- foreach (var channel in channels) guild.ChannelsInternal[channel.Id] = channel;
-
- return guild;
- }
-
- /// <summary>
- /// Gets a guild preview.
- /// </summary>
- /// <param name="id">The guild ID.</param>
- /// <returns></returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildPreview> GetGuildPreviewAsync(ulong id)
- => this.ApiClient.GetGuildPreviewAsync(id);
-
- /// <summary>
- /// Gets an invite.
- /// </summary>
- /// <param name="code">The invite code.</param>
- /// <param name="withCounts">Whether to include presence and total member counts in the returned invite.</param>
- /// <param name="withExpiration">Whether to include the expiration date in the returned invite.</param>
- /// <param name="scheduledEventId">The scheduled event id.</param>
- /// <returns>The requested Invite.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the invite does not exists.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordInvite> GetInviteByCodeAsync(string code, bool? withCounts = null, bool? withExpiration = null, ulong? scheduledEventId = null)
- => this.ApiClient.GetInviteAsync(code, withCounts, withExpiration, scheduledEventId);
-
- /// <summary>
- /// Gets a list of user connections.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordConnection>> GetConnectionsAsync()
- => this.ApiClient.GetUserConnectionsAsync();
-
- /// <summary>
- /// Gets a sticker.
- /// </summary>
- /// <returns>The requested sticker.</returns>
- /// <param name="id">The id of the sticker.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the sticker does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordSticker> GetStickerAsync(ulong id)
- => this.ApiClient.GetStickerAsync(id);
-
-
- /// <summary>
- /// Gets all nitro sticker packs.
- /// </summary>
- /// <returns>List of sticker packs.</returns>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordStickerPack>> GetStickerPacksAsync()
- => this.ApiClient.GetStickerPacksAsync();
-
-
- /// <summary>
- /// Gets the In-App OAuth Url.
- /// </summary>
- /// <param name="scopes">Defaults to <see cref="DisCatSharp.Enums.OAuthScopes.BOT_DEFAULT"/>.</param>
- /// <param name="redir">Redirect Uri.</param>
- /// <param name="permissions">Defaults to <see cref="Permissions.None"/>.</param>
- /// <returns>The OAuth Url</returns>
- public Uri GetInAppOAuth(Permissions permissions = Permissions.None, OAuthScopes scopes = OAuthScopes.BOT_DEFAULT, string redir = null)
- {
- permissions &= PermissionMethods.FullPerms;
- // hey look, it's not all annoying and blue :P
- return new Uri(new QueryUriBuilder($"{DiscordDomain.GetDomain(CoreDomain.Discord).Url}{Endpoints.OAUTH2}{Endpoints.AUTHORIZE}")
- .AddParameter("client_id", this.CurrentApplication.Id.ToString(CultureInfo.InvariantCulture))
- .AddParameter("scope", OAuth.ResolveScopes(scopes))
- .AddParameter("permissions", ((long)permissions).ToString(CultureInfo.InvariantCulture))
- .AddParameter("state", "")
- .AddParameter("redirect_uri", redir ?? "")
- .ToString());
- }
-
- /// <summary>
- /// Gets a webhook.
- /// </summary>
- /// <param name="id">The target webhook id.</param>
- /// <returns>The requested webhook.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordWebhook> GetWebhookAsync(ulong id)
- => this.ApiClient.GetWebhookAsync(id);
-
- /// <summary>
- /// Gets a webhook.
- /// </summary>
- /// <param name="id">The target webhook id.</param>
- /// <param name="token">The target webhook token.</param>
- /// <returns>The requested webhook.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordWebhook> GetWebhookWithTokenAsync(ulong id, string token)
- => this.ApiClient.GetWebhookWithTokenAsync(id, token);
-
- /// <summary>
- /// Updates current user's activity and status.
- /// </summary>
- /// <param name="activity">Activity to set.</param>
- /// <param name="userStatus">Status of the user.</param>
- /// <param name="idleSince">Since when is the client performing the specified activity.</param>
- /// <returns></returns>
- public Task UpdateStatusAsync(DiscordActivity activity = null, UserStatus? userStatus = null, DateTimeOffset? idleSince = null)
- => this.InternalUpdateStatusAsync(activity, userStatus, idleSince);
-
- /// <summary>
- /// Edits current user.
- /// </summary>
- /// <param name="username">New username.</param>
- /// <param name="avatar">New avatar.</param>
- /// <returns>The modified user.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the user does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordUser> UpdateCurrentUserAsync(string username = null, Optional<Stream> avatar = default)
- {
- var av64 = ImageTool.Base64FromStream(avatar);
-
- var usr = await this.ApiClient.ModifyCurrentUserAsync(username, av64).ConfigureAwait(false);
-
- this.CurrentUser.Username = usr.Username;
- this.CurrentUser.Discriminator = usr.Discriminator;
- this.CurrentUser.AvatarHash = usr.AvatarHash;
- return this.CurrentUser;
- }
-
- /// <summary>
- /// Gets a guild template by the code.
- /// </summary>
- /// <param name="code">The code of the template.</param>
- /// <returns>The guild template for the code.</returns>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildTemplate> GetTemplateAsync(string code)
- => this.ApiClient.GetTemplateAsync(code);
-
- /// <summary>
- /// Gets all the global application commands for this application.
- /// </summary>
- /// <param name="withLocalizations">Whether to get the full localization dict.</param>
- /// <returns>A list of global application commands.</returns>
- public Task<IReadOnlyList<DiscordApplicationCommand>> GetGlobalApplicationCommandsAsync(bool withLocalizations = false) =>
- this.ApiClient.GetGlobalApplicationCommandsAsync(this.CurrentApplication.Id, withLocalizations);
-
- /// <summary>
- /// Overwrites the existing global application commands. New commands are automatically created and missing commands are automatically deleted.
- /// </summary>
- /// <param name="commands">The list of commands to overwrite with.</param>
- /// <returns>The list of global commands.</returns>
- public Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteGlobalApplicationCommandsAsync(IEnumerable<DiscordApplicationCommand> commands) =>
- this.ApiClient.BulkOverwriteGlobalApplicationCommandsAsync(this.CurrentApplication.Id, commands);
+ /// <summary>
+ /// Gets a guild preview.
+ /// </summary>
+ /// <param name="id">The guild ID.</param>
+ /// <returns></returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildPreview> GetGuildPreviewAsync(ulong id)
+ => this.ApiClient.GetGuildPreviewAsync(id);
+
+ /// <summary>
+ /// Gets an invite.
+ /// </summary>
+ /// <param name="code">The invite code.</param>
+ /// <param name="withCounts">Whether to include presence and total member counts in the returned invite.</param>
+ /// <param name="withExpiration">Whether to include the expiration date in the returned invite.</param>
+ /// <param name="scheduledEventId">The scheduled event id.</param>
+ /// <returns>The requested Invite.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the invite does not exists.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordInvite> GetInviteByCodeAsync(string code, bool? withCounts = null, bool? withExpiration = null, ulong? scheduledEventId = null)
+ => this.ApiClient.GetInviteAsync(code, withCounts, withExpiration, scheduledEventId);
+
+ /// <summary>
+ /// Gets a list of user connections.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordConnection>> GetConnectionsAsync()
+ => this.ApiClient.GetUserConnectionsAsync();
+
+ /// <summary>
+ /// Gets a sticker.
+ /// </summary>
+ /// <returns>The requested sticker.</returns>
+ /// <param name="id">The id of the sticker.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the sticker does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordSticker> GetStickerAsync(ulong id)
+ => this.ApiClient.GetStickerAsync(id);
+
+
+ /// <summary>
+ /// Gets all nitro sticker packs.
+ /// </summary>
+ /// <returns>List of sticker packs.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordStickerPack>> GetStickerPacksAsync()
+ => this.ApiClient.GetStickerPacksAsync();
+
+
+ /// <summary>
+ /// Gets the In-App OAuth Url.
+ /// </summary>
+ /// <param name="scopes">Defaults to <see cref="DisCatSharp.Enums.OAuthScopes.BOT_DEFAULT"/>.</param>
+ /// <param name="redir">Redirect Uri.</param>
+ /// <param name="permissions">Defaults to <see cref="Permissions.None"/>.</param>
+ /// <returns>The OAuth Url</returns>
+ public Uri GetInAppOAuth(Permissions permissions = Permissions.None, OAuthScopes scopes = OAuthScopes.BOT_DEFAULT, string redir = null)
+ {
+ permissions &= PermissionMethods.FullPerms;
+ // hey look, it's not all annoying and blue :P
+ return new Uri(new QueryUriBuilder($"{DiscordDomain.GetDomain(CoreDomain.Discord).Url}{Endpoints.OAUTH2}{Endpoints.AUTHORIZE}")
+ .AddParameter("client_id", this.CurrentApplication.Id.ToString(CultureInfo.InvariantCulture))
+ .AddParameter("scope", OAuth.ResolveScopes(scopes))
+ .AddParameter("permissions", ((long)permissions).ToString(CultureInfo.InvariantCulture))
+ .AddParameter("state", "")
+ .AddParameter("redirect_uri", redir ?? "")
+ .ToString());
+ }
- /// <summary>
- /// Creates or overwrites a global application command.
- /// </summary>
- /// <param name="command">The command to create.</param>
- /// <returns>The created command.</returns>
- public Task<DiscordApplicationCommand> CreateGlobalApplicationCommandAsync(DiscordApplicationCommand command) =>
- this.ApiClient.CreateGlobalApplicationCommandAsync(this.CurrentApplication.Id, command);
+ /// <summary>
+ /// Gets a webhook.
+ /// </summary>
+ /// <param name="id">The target webhook id.</param>
+ /// <returns>The requested webhook.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordWebhook> GetWebhookAsync(ulong id)
+ => this.ApiClient.GetWebhookAsync(id);
+
+ /// <summary>
+ /// Gets a webhook.
+ /// </summary>
+ /// <param name="id">The target webhook id.</param>
+ /// <param name="token">The target webhook token.</param>
+ /// <returns>The requested webhook.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordWebhook> GetWebhookWithTokenAsync(ulong id, string token)
+ => this.ApiClient.GetWebhookWithTokenAsync(id, token);
+
+ /// <summary>
+ /// Updates current user's activity and status.
+ /// </summary>
+ /// <param name="activity">Activity to set.</param>
+ /// <param name="userStatus">Status of the user.</param>
+ /// <param name="idleSince">Since when is the client performing the specified activity.</param>
+ /// <returns></returns>
+ public Task UpdateStatusAsync(DiscordActivity activity = null, UserStatus? userStatus = null, DateTimeOffset? idleSince = null)
+ => this.InternalUpdateStatusAsync(activity, userStatus, idleSince);
+
+ /// <summary>
+ /// Edits current user.
+ /// </summary>
+ /// <param name="username">New username.</param>
+ /// <param name="avatar">New avatar.</param>
+ /// <returns>The modified user.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the user does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordUser> UpdateCurrentUserAsync(string username = null, Optional<Stream> avatar = default)
+ {
+ var av64 = ImageTool.Base64FromStream(avatar);
- /// <summary>
- /// Gets a global application command by its id.
- /// </summary>
- /// <param name="commandId">The id of the command to get.</param>
- /// <returns>The command with the id.</returns>
- public Task<DiscordApplicationCommand> GetGlobalApplicationCommandAsync(ulong commandId) =>
- this.ApiClient.GetGlobalApplicationCommandAsync(this.CurrentApplication.Id, commandId);
+ var usr = await this.ApiClient.ModifyCurrentUserAsync(username, av64).ConfigureAwait(false);
- /// <summary>
- /// Edits a global application command.
- /// </summary>
- /// <param name="commandId">The id of the command to edit.</param>
- /// <param name="action">Action to perform.</param>
- /// <returns>The edited command.</returns>
- public async Task<DiscordApplicationCommand> EditGlobalApplicationCommandAsync(ulong commandId, Action<ApplicationCommandEditModel> action)
- {
- var mdl = new ApplicationCommandEditModel();
- action(mdl);
- var applicationId = this.CurrentApplication?.Id ?? (await this.GetCurrentApplicationAsync().ConfigureAwait(false)).Id;
- return await this.ApiClient.EditGlobalApplicationCommandAsync(applicationId, commandId, mdl.Name, mdl.Description, mdl.Options, mdl.NameLocalizations, mdl.DescriptionLocalizations, mdl.DefaultMemberPermissions, mdl.DmPermission).ConfigureAwait(false);
- }
+ this.CurrentUser.Username = usr.Username;
+ this.CurrentUser.Discriminator = usr.Discriminator;
+ this.CurrentUser.AvatarHash = usr.AvatarHash;
+ return this.CurrentUser;
+ }
- /// <summary>
- /// Deletes a global application command.
- /// </summary>
- /// <param name="commandId">The id of the command to delete.</param>
- public Task DeleteGlobalApplicationCommandAsync(ulong commandId) =>
- this.ApiClient.DeleteGlobalApplicationCommandAsync(this.CurrentApplication.Id, commandId);
+ /// <summary>
+ /// Gets a guild template by the code.
+ /// </summary>
+ /// <param name="code">The code of the template.</param>
+ /// <returns>The guild template for the code.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildTemplate> GetTemplateAsync(string code)
+ => this.ApiClient.GetTemplateAsync(code);
+
+ /// <summary>
+ /// Gets all the global application commands for this application.
+ /// </summary>
+ /// <param name="withLocalizations">Whether to get the full localization dict.</param>
+ /// <returns>A list of global application commands.</returns>
+ public Task<IReadOnlyList<DiscordApplicationCommand>> GetGlobalApplicationCommandsAsync(bool withLocalizations = false) =>
+ this.ApiClient.GetGlobalApplicationCommandsAsync(this.CurrentApplication.Id, withLocalizations);
+
+ /// <summary>
+ /// Overwrites the existing global application commands. New commands are automatically created and missing commands are automatically deleted.
+ /// </summary>
+ /// <param name="commands">The list of commands to overwrite with.</param>
+ /// <returns>The list of global commands.</returns>
+ public Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteGlobalApplicationCommandsAsync(IEnumerable<DiscordApplicationCommand> commands) =>
+ this.ApiClient.BulkOverwriteGlobalApplicationCommandsAsync(this.CurrentApplication.Id, commands);
+
+ /// <summary>
+ /// Creates or overwrites a global application command.
+ /// </summary>
+ /// <param name="command">The command to create.</param>
+ /// <returns>The created command.</returns>
+ public Task<DiscordApplicationCommand> CreateGlobalApplicationCommandAsync(DiscordApplicationCommand command) =>
+ this.ApiClient.CreateGlobalApplicationCommandAsync(this.CurrentApplication.Id, command);
+
+ /// <summary>
+ /// Gets a global application command by its id.
+ /// </summary>
+ /// <param name="commandId">The id of the command to get.</param>
+ /// <returns>The command with the id.</returns>
+ public Task<DiscordApplicationCommand> GetGlobalApplicationCommandAsync(ulong commandId) =>
+ this.ApiClient.GetGlobalApplicationCommandAsync(this.CurrentApplication.Id, commandId);
+
+ /// <summary>
+ /// Edits a global application command.
+ /// </summary>
+ /// <param name="commandId">The id of the command to edit.</param>
+ /// <param name="action">Action to perform.</param>
+ /// <returns>The edited command.</returns>
+ public async Task<DiscordApplicationCommand> EditGlobalApplicationCommandAsync(ulong commandId, Action<ApplicationCommandEditModel> action)
+ {
+ var mdl = new ApplicationCommandEditModel();
+ action(mdl);
+ var applicationId = this.CurrentApplication?.Id ?? (await this.GetCurrentApplicationAsync().ConfigureAwait(false)).Id;
+ return await this.ApiClient.EditGlobalApplicationCommandAsync(applicationId, commandId, mdl.Name, mdl.Description, mdl.Options, mdl.NameLocalizations, mdl.DescriptionLocalizations, mdl.DefaultMemberPermissions, mdl.DmPermission).ConfigureAwait(false);
+ }
- /// <summary>
- /// Gets all the application commands for a guild.
- /// </summary>
- /// <param name="guildId">The id of the guild to get application commands for.</param>
- /// <param name="withLocalizations">Whether to get the full localization dict.</param>
- /// <returns>A list of application commands in the guild.</returns>
- public Task<IReadOnlyList<DiscordApplicationCommand>> GetGuildApplicationCommandsAsync(ulong guildId, bool withLocalizations = false) =>
- this.ApiClient.GetGuildApplicationCommandsAsync(this.CurrentApplication.Id, guildId, withLocalizations);
+ /// <summary>
+ /// Deletes a global application command.
+ /// </summary>
+ /// <param name="commandId">The id of the command to delete.</param>
+ public Task DeleteGlobalApplicationCommandAsync(ulong commandId) =>
+ this.ApiClient.DeleteGlobalApplicationCommandAsync(this.CurrentApplication.Id, commandId);
+
+ /// <summary>
+ /// Gets all the application commands for a guild.
+ /// </summary>
+ /// <param name="guildId">The id of the guild to get application commands for.</param>
+ /// <param name="withLocalizations">Whether to get the full localization dict.</param>
+ /// <returns>A list of application commands in the guild.</returns>
+ public Task<IReadOnlyList<DiscordApplicationCommand>> GetGuildApplicationCommandsAsync(ulong guildId, bool withLocalizations = false) =>
+ this.ApiClient.GetGuildApplicationCommandsAsync(this.CurrentApplication.Id, guildId, withLocalizations);
+
+ /// <summary>
+ /// Overwrites the existing application commands in a guild. New commands are automatically created and missing commands are automatically deleted.
+ /// </summary>
+ /// <param name="guildId">The id of the guild.</param>
+ /// <param name="commands">The list of commands to overwrite with.</param>
+ /// <returns>The list of guild commands.</returns>
+ public Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteGuildApplicationCommandsAsync(ulong guildId, IEnumerable<DiscordApplicationCommand> commands) =>
+ this.ApiClient.BulkOverwriteGuildApplicationCommandsAsync(this.CurrentApplication.Id, guildId, commands);
+
+ /// <summary>
+ /// Creates or overwrites a guild application command.
+ /// </summary>
+ /// <param name="guildId">The id of the guild to create the application command in.</param>
+ /// <param name="command">The command to create.</param>
+ /// <returns>The created command.</returns>
+ public Task<DiscordApplicationCommand> CreateGuildApplicationCommandAsync(ulong guildId, DiscordApplicationCommand command) =>
+ this.ApiClient.CreateGuildApplicationCommandAsync(this.CurrentApplication.Id, guildId, command);
+
+ /// <summary>
+ /// Gets a application command in a guild by its id.
+ /// </summary>
+ /// <param name="guildId">The id of the guild the application command is in.</param>
+ /// <param name="commandId">The id of the command to get.</param>
+ /// <returns>The command with the id.</returns>
+ public Task<DiscordApplicationCommand> GetGuildApplicationCommandAsync(ulong guildId, ulong commandId) =>
+ this.ApiClient.GetGuildApplicationCommandAsync(this.CurrentApplication.Id, guildId, commandId);
+
+ /// <summary>
+ /// Edits a application command in a guild.
+ /// </summary>
+ /// <param name="guildId">The id of the guild the application command is in.</param>
+ /// <param name="commandId">The id of the command to edit.</param>
+ /// <param name="action">Action to perform.</param>
+ /// <returns>The edited command.</returns>
+ public async Task<DiscordApplicationCommand> EditGuildApplicationCommandAsync(ulong guildId, ulong commandId, Action<ApplicationCommandEditModel> action)
+ {
+ var mdl = new ApplicationCommandEditModel();
+ action(mdl);
+ var applicationId = this.CurrentApplication?.Id ?? (await this.GetCurrentApplicationAsync().ConfigureAwait(false)).Id;
+ return await this.ApiClient.EditGuildApplicationCommandAsync(applicationId, guildId, commandId, mdl.Name, mdl.Description, mdl.Options, mdl.NameLocalizations, mdl.DescriptionLocalizations, mdl.DefaultMemberPermissions, mdl.DmPermission).ConfigureAwait(false);
+ }
- /// <summary>
- /// Overwrites the existing application commands in a guild. New commands are automatically created and missing commands are automatically deleted.
- /// </summary>
- /// <param name="guildId">The id of the guild.</param>
- /// <param name="commands">The list of commands to overwrite with.</param>
- /// <returns>The list of guild commands.</returns>
- public Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteGuildApplicationCommandsAsync(ulong guildId, IEnumerable<DiscordApplicationCommand> commands) =>
- this.ApiClient.BulkOverwriteGuildApplicationCommandsAsync(this.CurrentApplication.Id, guildId, commands);
+ /// <summary>
+ /// Deletes a application command in a guild.
+ /// </summary>
+ /// <param name="guildId">The id of the guild to delete the application command in.</param>
+ /// <param name="commandId">The id of the command.</param>
+ public Task DeleteGuildApplicationCommandAsync(ulong guildId, ulong commandId) =>
+ this.ApiClient.DeleteGuildApplicationCommandAsync(this.CurrentApplication.Id, guildId, commandId);
+
+ /// <summary>
+ /// Gets all command permissions for a guild.
+ /// </summary>
+ /// <param name="guildId">The target guild.</param>
+ public Task<IReadOnlyList<DiscordGuildApplicationCommandPermission>> GetGuildApplicationCommandPermissionsAsync(ulong guildId) =>
+ this.ApiClient.GetGuildApplicationCommandPermissionsAsync(this.CurrentApplication.Id, guildId);
+
+ /// <summary>
+ /// Gets the permissions for a guild command.
+ /// </summary>
+ /// <param name="guildId">The target guild.</param>
+ /// <param name="commandId">The target command id.</param>
+ public Task<DiscordGuildApplicationCommandPermission> GetApplicationCommandPermissionAsync(ulong guildId, ulong commandId) =>
+ this.ApiClient.GetGuildApplicationCommandPermissionAsync(this.CurrentApplication.Id, guildId, commandId);
+
+ #endregion
+
+ #region Internal Caching Methods
+ /// <summary>
+ /// Gets the internal chached threads.
+ /// </summary>
+ /// <param name="threadId">The target thread id.</param>
+ /// <returns>The requested thread.</returns>
+ internal DiscordThreadChannel InternalGetCachedThread(ulong threadId)
+ {
+ foreach (var guild in this.Guilds.Values)
+ if (guild.Threads.TryGetValue(threadId, out var foundThread))
+ return foundThread;
- /// <summary>
- /// Creates or overwrites a guild application command.
- /// </summary>
- /// <param name="guildId">The id of the guild to create the application command in.</param>
- /// <param name="command">The command to create.</param>
- /// <returns>The created command.</returns>
- public Task<DiscordApplicationCommand> CreateGuildApplicationCommandAsync(ulong guildId, DiscordApplicationCommand command) =>
- this.ApiClient.CreateGuildApplicationCommandAsync(this.CurrentApplication.Id, guildId, command);
+ return null;
+ }
- /// <summary>
- /// Gets a application command in a guild by its id.
- /// </summary>
- /// <param name="guildId">The id of the guild the application command is in.</param>
- /// <param name="commandId">The id of the command to get.</param>
- /// <returns>The command with the id.</returns>
- public Task<DiscordApplicationCommand> GetGuildApplicationCommandAsync(ulong guildId, ulong commandId) =>
- this.ApiClient.GetGuildApplicationCommandAsync(this.CurrentApplication.Id, guildId, commandId);
- /// <summary>
- /// Edits a application command in a guild.
- /// </summary>
- /// <param name="guildId">The id of the guild the application command is in.</param>
- /// <param name="commandId">The id of the command to edit.</param>
- /// <param name="action">Action to perform.</param>
- /// <returns>The edited command.</returns>
- public async Task<DiscordApplicationCommand> EditGuildApplicationCommandAsync(ulong guildId, ulong commandId, Action<ApplicationCommandEditModel> action)
- {
- var mdl = new ApplicationCommandEditModel();
- action(mdl);
- var applicationId = this.CurrentApplication?.Id ?? (await this.GetCurrentApplicationAsync().ConfigureAwait(false)).Id;
- return await this.ApiClient.EditGuildApplicationCommandAsync(applicationId, guildId, commandId, mdl.Name, mdl.Description, mdl.Options, mdl.NameLocalizations, mdl.DescriptionLocalizations, mdl.DefaultMemberPermissions, mdl.DmPermission).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Gets the internal cached scheduled event.
+ /// </summary>
+ /// <param name="scheduledEventId">The target scheduled event id.</param>
+ /// <returns>The requested scheduled event.</returns>
+ internal DiscordScheduledEvent InternalGetCachedScheduledEvent(ulong scheduledEventId)
+ {
+ foreach (var guild in this.Guilds.Values)
+ if (guild.ScheduledEvents.TryGetValue(scheduledEventId, out var foundScheduledEvent))
+ return foundScheduledEvent;
- /// <summary>
- /// Deletes a application command in a guild.
- /// </summary>
- /// <param name="guildId">The id of the guild to delete the application command in.</param>
- /// <param name="commandId">The id of the command.</param>
- public Task DeleteGuildApplicationCommandAsync(ulong guildId, ulong commandId) =>
- this.ApiClient.DeleteGuildApplicationCommandAsync(this.CurrentApplication.Id, guildId, commandId);
+ return null;
+ }
- /// <summary>
- /// Gets all command permissions for a guild.
- /// </summary>
- /// <param name="guildId">The target guild.</param>
- public Task<IReadOnlyList<DiscordGuildApplicationCommandPermission>> GetGuildApplicationCommandPermissionsAsync(ulong guildId) =>
- this.ApiClient.GetGuildApplicationCommandPermissionsAsync(this.CurrentApplication.Id, guildId);
+ /// <summary>
+ /// Gets the internal cached channel.
+ /// </summary>
+ /// <param name="channelId">The target channel id.</param>
+ /// <returns>The requested channel.</returns>
+ internal DiscordChannel InternalGetCachedChannel(ulong channelId)
+ {
+ foreach (var guild in this.Guilds.Values)
+ if (guild.Channels.TryGetValue(channelId, out var foundChannel))
+ return foundChannel;
- /// <summary>
- /// Gets the permissions for a guild command.
- /// </summary>
- /// <param name="guildId">The target guild.</param>
- /// <param name="commandId">The target command id.</param>
- public Task<DiscordGuildApplicationCommandPermission> GetApplicationCommandPermissionAsync(ulong guildId, ulong commandId) =>
- this.ApiClient.GetGuildApplicationCommandPermissionAsync(this.CurrentApplication.Id, guildId, commandId);
+ return null;
+ }
- #endregion
+ /// <summary>
+ /// Gets the internal chached guild.
+ /// </summary>
+ /// <param name="guildId">The target guild id.</param>
+ /// <returns>The requested guild.</returns>
+ internal DiscordGuild InternalGetCachedGuild(ulong? guildId)
+ {
+ if (this.GuildsInternal != null && guildId.HasValue)
+ {
+ if (this.GuildsInternal.TryGetValue(guildId.Value, out var guild))
+ return guild;
+ }
- #region Internal Caching Methods
- /// <summary>
- /// Gets the internal chached threads.
- /// </summary>
- /// <param name="threadId">The target thread id.</param>
- /// <returns>The requested thread.</returns>
- internal DiscordThreadChannel InternalGetCachedThread(ulong threadId)
- {
- foreach (var guild in this.Guilds.Values)
- if (guild.Threads.TryGetValue(threadId, out var foundThread))
- return foundThread;
+ return null;
+ }
- return null;
- }
+ /// <summary>
+ /// Updates a message.
+ /// </summary>
+ /// <param name="message">The message to update.</param>
+ /// <param name="author">The author to update.</param>
+ /// <param name="guild">The guild to update.</param>
+ /// <param name="member">The member to update.</param>
+ private void UpdateMessage(DiscordMessage message, TransportUser author, DiscordGuild guild, TransportMember member)
+ {
+ if (author != null)
+ {
+ var usr = new DiscordUser(author) { Discord = this };
+ if (member != null)
+ member.User = author;
- /// <summary>
- /// Gets the internal cached scheduled event.
- /// </summary>
- /// <param name="scheduledEventId">The target scheduled event id.</param>
- /// <returns>The requested scheduled event.</returns>
- internal DiscordScheduledEvent InternalGetCachedScheduledEvent(ulong scheduledEventId)
- {
- foreach (var guild in this.Guilds.Values)
- if (guild.ScheduledEvents.TryGetValue(scheduledEventId, out var foundScheduledEvent))
- return foundScheduledEvent;
+ message.Author = this.UpdateUser(usr, guild?.Id, guild, member);
+ }
- return null;
- }
+ var channel = this.InternalGetCachedChannel(message.ChannelId);
- /// <summary>
- /// Gets the internal cached channel.
- /// </summary>
- /// <param name="channelId">The target channel id.</param>
- /// <returns>The requested channel.</returns>
- internal DiscordChannel InternalGetCachedChannel(ulong channelId)
- {
- foreach (var guild in this.Guilds.Values)
- if (guild.Channels.TryGetValue(channelId, out var foundChannel))
- return foundChannel;
+ if (channel != null) return;
- return null;
- }
+ channel = !message.GuildId.HasValue
+ ? new DiscordDmChannel
+ {
+ Id = message.ChannelId,
+ Discord = this,
+ Type = ChannelType.Private
+ }
+ : new DiscordChannel
+ {
+ Id = message.ChannelId,
+ Discord = this
+ };
- /// <summary>
- /// Gets the internal chached guild.
- /// </summary>
- /// <param name="guildId">The target guild id.</param>
- /// <returns>The requested guild.</returns>
- internal DiscordGuild InternalGetCachedGuild(ulong? guildId)
- {
- if (this.GuildsInternal != null && guildId.HasValue)
- {
- if (this.GuildsInternal.TryGetValue(guildId.Value, out var guild))
- return guild;
+ message.Channel = channel;
}
- return null;
- }
-
- /// <summary>
- /// Updates a message.
- /// </summary>
- /// <param name="message">The message to update.</param>
- /// <param name="author">The author to update.</param>
- /// <param name="guild">The guild to update.</param>
- /// <param name="member">The member to update.</param>
- private void UpdateMessage(DiscordMessage message, TransportUser author, DiscordGuild guild, TransportMember member)
- {
- if (author != null)
+ /// <summary>
+ /// Updates a scheduled event.
+ /// </summary>
+ /// <param name="scheduledEvent">The scheduled event to update.</param>
+ /// <param name="guild">The guild to update.</param>
+ /// <returns>The updated scheduled event.</returns>
+ private DiscordScheduledEvent UpdateScheduledEvent(DiscordScheduledEvent scheduledEvent, DiscordGuild guild)
{
- var usr = new DiscordUser(author) { Discord = this };
-
- if (member != null)
- member.User = author;
-
- message.Author = this.UpdateUser(usr, guild?.Id, guild, member);
- }
-
- var channel = this.InternalGetCachedChannel(message.ChannelId);
-
- if (channel != null) return;
-
- channel = !message.GuildId.HasValue
- ? new DiscordDmChannel
+ if (scheduledEvent != null)
{
- Id = message.ChannelId,
- Discord = this,
- Type = ChannelType.Private
+ _ = guild.ScheduledEventsInternal.AddOrUpdate(scheduledEvent.Id, scheduledEvent, (id, old) =>
+ {
+ old.Discord = this;
+ old.Description = scheduledEvent.Description;
+ old.ChannelId = scheduledEvent.ChannelId;
+ old.EntityId = scheduledEvent.EntityId;
+ old.EntityType = scheduledEvent.EntityType;
+ old.EntityMetadata = scheduledEvent.EntityMetadata;
+ old.PrivacyLevel = scheduledEvent.PrivacyLevel;
+ old.Name = scheduledEvent.Name;
+ old.Status = scheduledEvent.Status;
+ old.UserCount = scheduledEvent.UserCount;
+ old.ScheduledStartTimeRaw = scheduledEvent.ScheduledStartTimeRaw;
+ old.ScheduledEndTimeRaw = scheduledEvent.ScheduledEndTimeRaw;
+ return old;
+ });
}
- : new DiscordChannel
- {
- Id = message.ChannelId,
- Discord = this
- };
-
- message.Channel = channel;
- }
- /// <summary>
- /// Updates a scheduled event.
- /// </summary>
- /// <param name="scheduledEvent">The scheduled event to update.</param>
- /// <param name="guild">The guild to update.</param>
- /// <returns>The updated scheduled event.</returns>
- private DiscordScheduledEvent UpdateScheduledEvent(DiscordScheduledEvent scheduledEvent, DiscordGuild guild)
- {
- if (scheduledEvent != null)
- {
- _ = guild.ScheduledEventsInternal.AddOrUpdate(scheduledEvent.Id, scheduledEvent, (id, old) =>
- {
- old.Discord = this;
- old.Description = scheduledEvent.Description;
- old.ChannelId = scheduledEvent.ChannelId;
- old.EntityId = scheduledEvent.EntityId;
- old.EntityType = scheduledEvent.EntityType;
- old.EntityMetadata = scheduledEvent.EntityMetadata;
- old.PrivacyLevel = scheduledEvent.PrivacyLevel;
- old.Name = scheduledEvent.Name;
- old.Status = scheduledEvent.Status;
- old.UserCount = scheduledEvent.UserCount;
- old.ScheduledStartTimeRaw = scheduledEvent.ScheduledStartTimeRaw;
- old.ScheduledEndTimeRaw = scheduledEvent.ScheduledEndTimeRaw;
- return old;
- });
+ return scheduledEvent;
}
- return scheduledEvent;
- }
-
- /// <summary>
- /// Updates a user.
- /// </summary>
- /// <param name="usr">The user to update.</param>
- /// <param name="guildId">The guild id to update.</param>
- /// <param name="guild">The guild to update.</param>
- /// <param name="mbr">The member to update.</param>
- /// <returns>The updated user.</returns>
- private DiscordUser UpdateUser(DiscordUser usr, ulong? guildId, DiscordGuild guild, TransportMember mbr)
- {
- if (mbr != null)
+ /// <summary>
+ /// Updates a user.
+ /// </summary>
+ /// <param name="usr">The user to update.</param>
+ /// <param name="guildId">The guild id to update.</param>
+ /// <param name="guild">The guild to update.</param>
+ /// <param name="mbr">The member to update.</param>
+ /// <returns>The updated user.</returns>
+ private DiscordUser UpdateUser(DiscordUser usr, ulong? guildId, DiscordGuild guild, TransportMember mbr)
{
- if (mbr.User != null)
+ if (mbr != null)
{
- usr = new DiscordUser(mbr.User) { Discord = this };
-
- _ = this.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
+ if (mbr.User != null)
{
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
+ usr = new DiscordUser(mbr.User) { Discord = this };
- usr = new DiscordMember(mbr) { Discord = this, GuildId = guildId.Value };
- }
+ _ = this.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ return old;
+ });
- var intents = this.Configuration.Intents;
+ usr = new DiscordMember(mbr) { Discord = this, GuildId = guildId.Value };
+ }
- DiscordMember member = default;
+ var intents = this.Configuration.Intents;
- if (!intents.HasAllPrivilegedIntents() || guild.IsLarge) // we have the necessary privileged intents, no need to worry about caching here unless guild is large.
- {
- if (guild?.MembersInternal.TryGetValue(usr.Id, out member) == false)
+ DiscordMember member = default;
+
+ if (!intents.HasAllPrivilegedIntents() || guild.IsLarge) // we have the necessary privileged intents, no need to worry about caching here unless guild is large.
{
- if (intents.HasIntent(DiscordIntents.GuildMembers) || this.Configuration.AlwaysCacheMembers) // member can be updated by events, so cache it
+ if (guild?.MembersInternal.TryGetValue(usr.Id, out member) == false)
{
- guild.MembersInternal.TryAdd(usr.Id, (DiscordMember)usr);
+ if (intents.HasIntent(DiscordIntents.GuildMembers) || this.Configuration.AlwaysCacheMembers) // member can be updated by events, so cache it
+ {
+ guild.MembersInternal.TryAdd(usr.Id, (DiscordMember)usr);
+ }
}
- }
- else if (intents.HasIntent(DiscordIntents.GuildPresences) || this.Configuration.AlwaysCacheMembers) // we can attempt to update it if it's already in cache.
- {
- if (!intents.HasIntent(DiscordIntents.GuildMembers)) // no need to update if we already have the member events
+ else if (intents.HasIntent(DiscordIntents.GuildPresences) || this.Configuration.AlwaysCacheMembers) // we can attempt to update it if it's already in cache.
{
- _ = guild.MembersInternal.TryUpdate(usr.Id, (DiscordMember)usr, member);
+ if (!intents.HasIntent(DiscordIntents.GuildMembers)) // no need to update if we already have the member events
+ {
+ _ = guild.MembersInternal.TryUpdate(usr.Id, (DiscordMember)usr, member);
+ }
}
}
}
- }
- else if (usr.Username != null) // check if not a skeleton user
- {
- _ = this.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
+ else if (usr.Username != null) // check if not a skeleton user
{
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
- }
-
- return usr;
- }
+ _ = this.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ return old;
+ });
+ }
- /// <summary>
- /// Updates the cached scheduled events in a guild.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <param name="rawEvents">The raw events.</param>
- private void UpdateCachedScheduledEvent(DiscordGuild guild, JArray rawEvents)
- {
- if (this._disposed)
- return;
+ return usr;
+ }
- if (rawEvents != null)
+ /// <summary>
+ /// Updates the cached scheduled events in a guild.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <param name="rawEvents">The raw events.</param>
+ private void UpdateCachedScheduledEvent(DiscordGuild guild, JArray rawEvents)
{
- guild.ScheduledEventsInternal.Clear();
+ if (this._disposed)
+ return;
- foreach (var xj in rawEvents)
+ if (rawEvents != null)
{
- var xtm = xj.ToDiscordObject<DiscordScheduledEvent>();
+ guild.ScheduledEventsInternal.Clear();
+
+ foreach (var xj in rawEvents)
+ {
+ var xtm = xj.ToDiscordObject<DiscordScheduledEvent>();
- xtm.Discord = this;
+ xtm.Discord = this;
- guild.ScheduledEventsInternal[xtm.Id] = xtm;
+ guild.ScheduledEventsInternal[xtm.Id] = xtm;
+ }
}
}
- }
- /// <summary>
- /// Updates the cached guild.
- /// </summary>
- /// <param name="newGuild">The new guild.</param>
- /// <param name="rawMembers">The raw members.</param>
- private void UpdateCachedGuild(DiscordGuild newGuild, JArray rawMembers)
- {
- if (this._disposed)
- return;
+ /// <summary>
+ /// Updates the cached guild.
+ /// </summary>
+ /// <param name="newGuild">The new guild.</param>
+ /// <param name="rawMembers">The raw members.</param>
+ private void UpdateCachedGuild(DiscordGuild newGuild, JArray rawMembers)
+ {
+ if (this._disposed)
+ return;
- if (!this.GuildsInternal.ContainsKey(newGuild.Id))
- this.GuildsInternal[newGuild.Id] = newGuild;
+ if (!this.GuildsInternal.ContainsKey(newGuild.Id))
+ this.GuildsInternal[newGuild.Id] = newGuild;
- var guild = this.GuildsInternal[newGuild.Id];
+ var guild = this.GuildsInternal[newGuild.Id];
- if (newGuild.ChannelsInternal != null && !newGuild.ChannelsInternal.IsEmpty)
- {
- foreach (var channel in newGuild.ChannelsInternal.Values)
+ if (newGuild.ChannelsInternal != null && !newGuild.ChannelsInternal.IsEmpty)
{
- if (guild.ChannelsInternal.TryGetValue(channel.Id, out _)) continue;
-
- foreach (var overwrite in channel.PermissionOverwritesInternal)
+ foreach (var channel in newGuild.ChannelsInternal.Values)
{
- overwrite.Discord = this;
- overwrite.ChannelId = channel.Id;
- }
+ if (guild.ChannelsInternal.TryGetValue(channel.Id, out _)) continue;
+
+ foreach (var overwrite in channel.PermissionOverwritesInternal)
+ {
+ overwrite.Discord = this;
+ overwrite.ChannelId = channel.Id;
+ }
- guild.ChannelsInternal[channel.Id] = channel;
+ guild.ChannelsInternal[channel.Id] = channel;
+ }
}
- }
- if (newGuild.ThreadsInternal != null && !newGuild.ThreadsInternal.IsEmpty)
- {
- foreach (var thread in newGuild.ThreadsInternal.Values)
+ if (newGuild.ThreadsInternal != null && !newGuild.ThreadsInternal.IsEmpty)
{
- if (guild.ThreadsInternal.TryGetValue(thread.Id, out _)) continue;
+ foreach (var thread in newGuild.ThreadsInternal.Values)
+ {
+ if (guild.ThreadsInternal.TryGetValue(thread.Id, out _)) continue;
- guild.ThreadsInternal[thread.Id] = thread;
+ guild.ThreadsInternal[thread.Id] = thread;
+ }
}
- }
- if (newGuild.ScheduledEventsInternal != null && !newGuild.ScheduledEventsInternal.IsEmpty)
- {
- foreach (var @event in newGuild.ScheduledEventsInternal.Values)
+ if (newGuild.ScheduledEventsInternal != null && !newGuild.ScheduledEventsInternal.IsEmpty)
{
- if (guild.ScheduledEventsInternal.TryGetValue(@event.Id, out _)) continue;
+ foreach (var @event in newGuild.ScheduledEventsInternal.Values)
+ {
+ if (guild.ScheduledEventsInternal.TryGetValue(@event.Id, out _)) continue;
- guild.ScheduledEventsInternal[@event.Id] = @event;
+ guild.ScheduledEventsInternal[@event.Id] = @event;
+ }
}
- }
-
- foreach (var newEmoji in newGuild.EmojisInternal.Values)
- _ = guild.EmojisInternal.GetOrAdd(newEmoji.Id, _ => newEmoji);
- foreach (var newSticker in newGuild.StickersInternal.Values)
- _ = guild.StickersInternal.GetOrAdd(newSticker.Id, _ => newSticker);
+ foreach (var newEmoji in newGuild.EmojisInternal.Values)
+ _ = guild.EmojisInternal.GetOrAdd(newEmoji.Id, _ => newEmoji);
- foreach (var newStageInstance in newGuild.StageInstancesInternal.Values)
- _ = guild.StageInstancesInternal.GetOrAdd(newStageInstance.Id, _ => newStageInstance);
+ foreach (var newSticker in newGuild.StickersInternal.Values)
+ _ = guild.StickersInternal.GetOrAdd(newSticker.Id, _ => newSticker);
- if (rawMembers != null)
- {
- guild.MembersInternal.Clear();
+ foreach (var newStageInstance in newGuild.StageInstancesInternal.Values)
+ _ = guild.StageInstancesInternal.GetOrAdd(newStageInstance.Id, _ => newStageInstance);
- foreach (var xj in rawMembers)
+ if (rawMembers != null)
{
- var xtm = xj.ToDiscordObject<TransportMember>();
+ guild.MembersInternal.Clear();
- var xu = new DiscordUser(xtm.User) { Discord = this };
- _ = this.UserCache.AddOrUpdate(xtm.User.Id, xu, (id, old) =>
+ foreach (var xj in rawMembers)
{
- old.Username = xu.Username;
- old.Discriminator = xu.Discriminator;
- old.AvatarHash = xu.AvatarHash;
- old.PremiumType = xu.PremiumType;
- return old;
- });
+ var xtm = xj.ToDiscordObject<TransportMember>();
- guild.MembersInternal[xtm.User.Id] = new DiscordMember(xtm) { Discord = this, GuildId = guild.Id };
+ var xu = new DiscordUser(xtm.User) { Discord = this };
+ _ = this.UserCache.AddOrUpdate(xtm.User.Id, xu, (id, old) =>
+ {
+ old.Username = xu.Username;
+ old.Discriminator = xu.Discriminator;
+ old.AvatarHash = xu.AvatarHash;
+ old.PremiumType = xu.PremiumType;
+ return old;
+ });
+
+ guild.MembersInternal[xtm.User.Id] = new DiscordMember(xtm) { Discord = this, GuildId = guild.Id };
+ }
}
- }
- foreach (var role in newGuild.RolesInternal.Values)
- {
- if (guild.RolesInternal.TryGetValue(role.Id, out _)) continue;
+ foreach (var role in newGuild.RolesInternal.Values)
+ {
+ if (guild.RolesInternal.TryGetValue(role.Id, out _)) continue;
+
+ role.GuildId = guild.Id;
+ guild.RolesInternal[role.Id] = role;
+ }
- role.GuildId = guild.Id;
- guild.RolesInternal[role.Id] = role;
+ guild.Name = newGuild.Name;
+ guild.AfkChannelId = newGuild.AfkChannelId;
+ guild.AfkTimeout = newGuild.AfkTimeout;
+ guild.DefaultMessageNotifications = newGuild.DefaultMessageNotifications;
+ guild.RawFeatures = newGuild.RawFeatures;
+ guild.IconHash = newGuild.IconHash;
+ guild.MfaLevel = newGuild.MfaLevel;
+ guild.OwnerId = newGuild.OwnerId;
+ guild.VoiceRegionId = newGuild.VoiceRegionId;
+ guild.SplashHash = newGuild.SplashHash;
+ guild.VerificationLevel = newGuild.VerificationLevel;
+ guild.WidgetEnabled = newGuild.WidgetEnabled;
+ guild.WidgetChannelId = newGuild.WidgetChannelId;
+ guild.ExplicitContentFilter = newGuild.ExplicitContentFilter;
+ guild.PremiumTier = newGuild.PremiumTier;
+ guild.PremiumSubscriptionCount = newGuild.PremiumSubscriptionCount;
+ guild.PremiumProgressBarEnabled = newGuild.PremiumProgressBarEnabled;
+ guild.BannerHash = newGuild.BannerHash;
+ guild.Description = newGuild.Description;
+ guild.VanityUrlCode = newGuild.VanityUrlCode;
+ guild.SystemChannelId = newGuild.SystemChannelId;
+ guild.SystemChannelFlags = newGuild.SystemChannelFlags;
+ guild.DiscoverySplashHash = newGuild.DiscoverySplashHash;
+ guild.MaxMembers = newGuild.MaxMembers;
+ guild.MaxPresences = newGuild.MaxPresences;
+ guild.ApproximateMemberCount = newGuild.ApproximateMemberCount;
+ guild.ApproximatePresenceCount = newGuild.ApproximatePresenceCount;
+ guild.MaxVideoChannelUsers = newGuild.MaxVideoChannelUsers;
+ guild.PreferredLocale = newGuild.PreferredLocale;
+ guild.RulesChannelId = newGuild.RulesChannelId;
+ guild.PublicUpdatesChannelId = newGuild.PublicUpdatesChannelId;
+ guild.ApplicationId = newGuild.ApplicationId;
+
+ // fields not sent for update:
+ // - guild.Channels
+ // - voice states
+ // - guild.JoinedAt = new_guild.JoinedAt;
+ // - guild.Large = new_guild.Large;
+ // - guild.MemberCount = Math.Max(new_guild.MemberCount, guild._members.Count);
+ // - guild.Unavailable = new_guild.Unavailable;
}
- guild.Name = newGuild.Name;
- guild.AfkChannelId = newGuild.AfkChannelId;
- guild.AfkTimeout = newGuild.AfkTimeout;
- guild.DefaultMessageNotifications = newGuild.DefaultMessageNotifications;
- guild.RawFeatures = newGuild.RawFeatures;
- guild.IconHash = newGuild.IconHash;
- guild.MfaLevel = newGuild.MfaLevel;
- guild.OwnerId = newGuild.OwnerId;
- guild.VoiceRegionId = newGuild.VoiceRegionId;
- guild.SplashHash = newGuild.SplashHash;
- guild.VerificationLevel = newGuild.VerificationLevel;
- guild.WidgetEnabled = newGuild.WidgetEnabled;
- guild.WidgetChannelId = newGuild.WidgetChannelId;
- guild.ExplicitContentFilter = newGuild.ExplicitContentFilter;
- guild.PremiumTier = newGuild.PremiumTier;
- guild.PremiumSubscriptionCount = newGuild.PremiumSubscriptionCount;
- guild.PremiumProgressBarEnabled = newGuild.PremiumProgressBarEnabled;
- guild.BannerHash = newGuild.BannerHash;
- guild.Description = newGuild.Description;
- guild.VanityUrlCode = newGuild.VanityUrlCode;
- guild.SystemChannelId = newGuild.SystemChannelId;
- guild.SystemChannelFlags = newGuild.SystemChannelFlags;
- guild.DiscoverySplashHash = newGuild.DiscoverySplashHash;
- guild.MaxMembers = newGuild.MaxMembers;
- guild.MaxPresences = newGuild.MaxPresences;
- guild.ApproximateMemberCount = newGuild.ApproximateMemberCount;
- guild.ApproximatePresenceCount = newGuild.ApproximatePresenceCount;
- guild.MaxVideoChannelUsers = newGuild.MaxVideoChannelUsers;
- guild.PreferredLocale = newGuild.PreferredLocale;
- guild.RulesChannelId = newGuild.RulesChannelId;
- guild.PublicUpdatesChannelId = newGuild.PublicUpdatesChannelId;
- guild.ApplicationId = newGuild.ApplicationId;
-
- // fields not sent for update:
- // - guild.Channels
- // - voice states
- // - guild.JoinedAt = new_guild.JoinedAt;
- // - guild.Large = new_guild.Large;
- // - guild.MemberCount = Math.Max(new_guild.MemberCount, guild._members.Count);
- // - guild.Unavailable = new_guild.Unavailable;
- }
+ /// <summary>
+ /// Populates the message reactions and cache.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <param name="author">The author.</param>
+ /// <param name="member">The member.</param>
+ private void PopulateMessageReactionsAndCache(DiscordMessage message, TransportUser author, TransportMember member)
+ {
+ var guild = message.Channel?.Guild ?? this.InternalGetCachedGuild(message.GuildId);
- /// <summary>
- /// Populates the message reactions and cache.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <param name="author">The author.</param>
- /// <param name="member">The member.</param>
- private void PopulateMessageReactionsAndCache(DiscordMessage message, TransportUser author, TransportMember member)
- {
- var guild = message.Channel?.Guild ?? this.InternalGetCachedGuild(message.GuildId);
+ this.UpdateMessage(message, author, guild, member);
- this.UpdateMessage(message, author, guild, member);
+ if (message.ReactionsInternal == null)
+ message.ReactionsInternal = new List<DiscordReaction>();
+ foreach (var xr in message.ReactionsInternal)
+ xr.Emoji.Discord = this;
- if (message.ReactionsInternal == null)
- message.ReactionsInternal = new List<DiscordReaction>();
- foreach (var xr in message.ReactionsInternal)
- xr.Emoji.Discord = this;
+ if (this.Configuration.MessageCacheSize > 0 && message.Channel != null)
+ this.MessageCache?.Add(message);
+ }
- if (this.Configuration.MessageCacheSize > 0 && message.Channel != null)
- this.MessageCache?.Add(message);
- }
+ #endregion
- #endregion
+ #region Disposal
- #region Disposal
+ ~DiscordClient()
+ {
+ this.Dispose();
+ }
- ~DiscordClient()
- {
- this.Dispose();
- }
+ /// <summary>
+ /// Whether the client is disposed.
+ /// </summary>
- /// <summary>
- /// Whether the client is disposed.
- /// </summary>
+ private bool _disposed;
- private bool _disposed;
+ /// <summary>
+ /// Disposes the client.
+ /// </summary>
+ public override void Dispose()
+ {
+ if (this._disposed)
+ return;
- /// <summary>
- /// Disposes the client.
- /// </summary>
- public override void Dispose()
- {
- if (this._disposed)
- return;
+ this._disposed = true;
+ GC.SuppressFinalize(this);
- this._disposed = true;
- GC.SuppressFinalize(this);
+ this.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult();
+ this.ApiClient.Rest.Dispose();
+ this.CurrentUser = null;
- this.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult();
- this.ApiClient.Rest.Dispose();
- this.CurrentUser = null;
+ var extensions = this._extensions; // prevent _extensions being modified during dispose
+ this._extensions = null;
+ foreach (var extension in extensions)
+ if (extension is IDisposable disposable)
+ disposable.Dispose();
- var extensions = this._extensions; // prevent _extensions being modified during dispose
- this._extensions = null;
- foreach (var extension in extensions)
- if (extension is IDisposable disposable)
- disposable.Dispose();
+ try
+ {
+ this._cancelTokenSource?.Cancel();
+ this._cancelTokenSource?.Dispose();
+ }
+ catch { }
- try
- {
- this._cancelTokenSource?.Cancel();
- this._cancelTokenSource?.Dispose();
+ this.GuildsInternal = null;
+ this._heartbeatTask = null;
}
- catch { }
- this.GuildsInternal = null;
- this._heartbeatTask = null;
+ #endregion
}
-
- #endregion
}
diff --git a/DisCatSharp/Clients/DiscordShardedClient.Events.cs b/DisCatSharp/Clients/DiscordShardedClient.Events.cs
index 12a111485..c5a67e259 100644
--- a/DisCatSharp/Clients/DiscordShardedClient.Events.cs
+++ b/DisCatSharp/Clients/DiscordShardedClient.Events.cs
@@ -1,1641 +1,1642 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.EventArgs;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a discord sharded client.
-/// </summary>
-public sealed partial class DiscordShardedClient
+namespace DisCatSharp
{
- #region WebSocket
-
- /// <summary>
- /// Fired whenever a WebSocket error occurs within the client.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, SocketErrorEventArgs> SocketErrored
- {
- add => this._socketErrored.Register(value);
- remove => this._socketErrored.Unregister(value);
- }
- private AsyncEvent<DiscordClient, SocketErrorEventArgs> _socketErrored;
-
- /// <summary>
- /// Fired whenever WebSocket connection is established.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, SocketEventArgs> SocketOpened
- {
- add => this._socketOpened.Register(value);
- remove => this._socketOpened.Unregister(value);
- }
- private AsyncEvent<DiscordClient, SocketEventArgs> _socketOpened;
-
- /// <summary>
- /// Fired whenever WebSocket connection is terminated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, SocketCloseEventArgs> SocketClosed
- {
- add => this._socketClosed.Register(value);
- remove => this._socketClosed.Unregister(value);
- }
- private AsyncEvent<DiscordClient, SocketCloseEventArgs> _socketClosed;
-
- /// <summary>
- /// Fired when the client enters ready state.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ReadyEventArgs> Ready
- {
- add => this._ready.Register(value);
- remove => this._ready.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ReadyEventArgs> _ready;
-
- /// <summary>
- /// Fired whenever a session is resumed.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ReadyEventArgs> Resumed
- {
- add => this._resumed.Register(value);
- remove => this._resumed.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ReadyEventArgs> _resumed;
-
- /// <summary>
- /// Fired on received heartbeat ACK.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, HeartbeatEventArgs> Heartbeated
- {
- add => this._heartbeated.Register(value);
- remove => this._heartbeated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, HeartbeatEventArgs> _heartbeated;
-
- #endregion
-
- #region Channel
-
- /// <summary>
- /// Fired when a new channel is created.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ChannelCreateEventArgs> ChannelCreated
- {
- add => this._channelCreated.Register(value);
- remove => this._channelCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ChannelCreateEventArgs> _channelCreated;
-
- /// <summary>
- /// Fired when a channel is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ChannelUpdateEventArgs> ChannelUpdated
- {
- add => this._channelUpdated.Register(value);
- remove => this._channelUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ChannelUpdateEventArgs> _channelUpdated;
-
- /// <summary>
- /// Fired when a channel is deleted
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ChannelDeleteEventArgs> ChannelDeleted
- {
- add => this._channelDeleted.Register(value);
- remove => this._channelDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ChannelDeleteEventArgs> _channelDeleted;
-
- /// <summary>
- /// Fired when a dm channel is deleted
- /// For this Event you need the <see cref="DiscordIntents.DirectMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, DmChannelDeleteEventArgs> DmChannelDeleted
- {
- add => this._dmChannelDeleted.Register(value);
- remove => this._dmChannelDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, DmChannelDeleteEventArgs> _dmChannelDeleted;
-
- /// <summary>
- /// Fired whenever a channel's pinned message list is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ChannelPinsUpdateEventArgs> ChannelPinsUpdated
- {
- add => this._channelPinsUpdated.Register(value);
- remove => this._channelPinsUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ChannelPinsUpdateEventArgs> _channelPinsUpdated;
-
- #endregion
-
- #region Guild
-
- /// <summary>
- /// Fired when the user joins a new guild.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- /// <remarks>[alias="GuildJoined"][alias="JoinedGuild"]</remarks>
- public event AsyncEventHandler<DiscordClient, GuildCreateEventArgs> GuildCreated
- {
- add => this._guildCreated.Register(value);
- remove => this._guildCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildCreateEventArgs> _guildCreated;
-
- /// <summary>
- /// Fired when a guild is becoming available.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildCreateEventArgs> GuildAvailable
- {
- add => this._guildAvailable.Register(value);
- remove => this._guildAvailable.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildCreateEventArgs> _guildAvailable;
-
- /// <summary>
- /// Fired when a guild is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildUpdateEventArgs> GuildUpdated
- {
- add => this._guildUpdated.Register(value);
- remove => this._guildUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildUpdateEventArgs> _guildUpdated;
-
- /// <summary>
- /// Fired when the user leaves or is removed from a guild.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildDeleteEventArgs> GuildDeleted
- {
- add => this._guildDeleted.Register(value);
- remove => this._guildDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildDeleteEventArgs> _guildDeleted;
-
- /// <summary>
- /// Fired when a guild becomes unavailable.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildDeleteEventArgs> GuildUnavailable
- {
- add => this._guildUnavailable.Register(value);
- remove => this._guildUnavailable.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildDeleteEventArgs> _guildUnavailable;
-
- /// <summary>
- /// Fired when all guilds finish streaming from Discord.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildDownloadCompletedEventArgs> GuildDownloadCompleted
- {
- add => this._guildDownloadCompleted.Register(value);
- remove => this._guildDownloadCompleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildDownloadCompletedEventArgs> _guildDownloadCompleted;
-
- /// <summary>
- /// Fired when a guilds emojis get updated
- /// For this Event you need the <see cref="DiscordIntents.GuildEmojisAndStickers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildEmojisUpdateEventArgs> GuildEmojisUpdated
- {
- add => this._guildEmojisUpdated.Register(value);
- remove => this._guildEmojisUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildEmojisUpdateEventArgs> _guildEmojisUpdated;
-
- /// <summary>
- /// Fired when a guilds stickers get updated
- /// For this Event you need the <see cref="DiscordIntents.GuildEmojisAndStickers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildStickersUpdateEventArgs> GuildStickersUpdated
- {
- add => this._guildStickersUpdated.Register(value);
- remove => this._guildStickersUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildStickersUpdateEventArgs> _guildStickersUpdated;
-
- /// <summary>
- /// Fired when a guild integration is updated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildIntegrationsUpdateEventArgs> GuildIntegrationsUpdated
- {
- add => this._guildIntegrationsUpdated.Register(value);
- remove => this._guildIntegrationsUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildIntegrationsUpdateEventArgs> _guildIntegrationsUpdated;
-
- #endregion
-
- #region Guild Ban
-
- /// <summary>
- /// Fired when a guild ban gets added
- /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildBanAddEventArgs> GuildBanAdded
- {
- add => this._guildBanAdded.Register(value);
- remove => this._guildBanAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildBanAddEventArgs> _guildBanAdded;
-
- /// <summary>
- /// Fired when a guild ban gets removed
- /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildBanRemoveEventArgs> GuildBanRemoved
- {
- add => this._guildBanRemoved.Register(value);
- remove => this._guildBanRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildBanRemoveEventArgs> _guildBanRemoved;
-
- #endregion
-
- #region Guild Timeout
-
- /// <summary>
- /// Fired when a guild member timeout gets added.
- /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutAddEventArgs> GuildMemberTimeoutAdded
- {
- add => this._guildMemberTimeoutAdded.Register(value);
- remove => this._guildMemberTimeoutAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberTimeoutAddEventArgs> _guildMemberTimeoutAdded;
-
- /// <summary>
- /// Fired when a guild member timeout gets changed.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutUpdateEventArgs> GuildMemberTimeoutChanged
- {
- add => this._guildMemberTimeoutChanged.Register(value);
- remove => this._guildMemberTimeoutChanged.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberTimeoutUpdateEventArgs> _guildMemberTimeoutChanged;
-
-
- /// <summary>
- /// Fired when a guild member timeout gets removed.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutRemoveEventArgs> GuildMemberTimeoutRemoved
- {
- add => this._guildMemberTimeoutRemoved.Register(value);
- remove => this._guildMemberTimeoutRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberTimeoutRemoveEventArgs> _guildMemberTimeoutRemoved;
-
- #endregion
-
- #region Guild Event
-
- /// <summary>
- /// Fired when a scheduled event is created.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventCreateEventArgs> GuildScheduledEventCreated
- {
- add => this._guildScheduledEventCreated.Register(value);
- remove => this._guildScheduledEventCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventCreateEventArgs> _guildScheduledEventCreated;
-
- /// <summary>
- /// Fired when a scheduled event is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventUpdateEventArgs> GuildScheduledEventUpdated
- {
- add => this._guildScheduledEventUpdated.Register(value);
- remove => this._guildScheduledEventUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventUpdateEventArgs> _guildScheduledEventUpdated;
-
- /// <summary>
- /// Fired when a scheduled event is deleted.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventDeleteEventArgs> GuildScheduledEventDeleted
- {
- add => this._guildScheduledEventDeleted.Register(value);
- remove => this._guildScheduledEventDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventDeleteEventArgs> _guildScheduledEventDeleted;
-
- /// <summary>
- /// Fired when a user subscribes to a scheduled event.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventUserAddEventArgs> GuildScheduledEventUserAdded
- {
- add => this._guildScheduledEventUserAdded.Register(value);
- remove => this._guildScheduledEventUserAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventUserAddEventArgs> _guildScheduledEventUserAdded;
-
- /// <summary>
- /// Fired when a user unsubscribes from a scheduled event.
- /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildScheduledEventUserRemoveEventArgs> GuildScheduledEventUserRemoved
- {
- add => this._guildScheduledEventUserRemoved.Register(value);
- remove => this._guildScheduledEventUserRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildScheduledEventUserRemoveEventArgs> _guildScheduledEventUserRemoved;
-
- #endregion
-
- #region Guild Integration
-
- /// <summary>
- /// Fired when a guild integration is created.
- /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildIntegrationCreateEventArgs> GuildIntegrationCreated
- {
- add => this._guildIntegrationCreated.Register(value);
- remove => this._guildIntegrationCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildIntegrationCreateEventArgs> _guildIntegrationCreated;
-
- /// <summary>
- /// Fired when a guild integration is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildIntegrationUpdateEventArgs> GuildIntegrationUpdated
- {
- add => this._guildIntegrationUpdated.Register(value);
- remove => this._guildIntegrationUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildIntegrationUpdateEventArgs> _guildIntegrationUpdated;
-
- /// <summary>
- /// Fired when a guild integration is deleted.
- /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildIntegrationDeleteEventArgs> GuildIntegrationDeleted
- {
- add => this._guildIntegrationDeleted.Register(value);
- remove => this._guildIntegrationDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildIntegrationDeleteEventArgs> _guildIntegrationDeleted;
-
- #endregion
-
- #region Guild Member
-
- /// <summary>
- /// Fired when a new user joins a guild.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberAddEventArgs> GuildMemberAdded
- {
- add => this._guildMemberAdded.Register(value);
- remove => this._guildMemberAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberAddEventArgs> _guildMemberAdded;
-
- /// <summary>
- /// Fired when a user is removed from a guild (leave/kick/ban).
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberRemoveEventArgs> GuildMemberRemoved
- {
- add => this._guildMemberRemoved.Register(value);
- remove => this._guildMemberRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberRemoveEventArgs> _guildMemberRemoved;
-
- /// <summary>
- /// Fired when a guild member is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMemberUpdateEventArgs> GuildMemberUpdated
- {
- add => this._guildMemberUpdated.Register(value);
- remove => this._guildMemberUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMemberUpdateEventArgs> _guildMemberUpdated;
-
- /// <summary>
- /// Fired in response to Gateway Request Guild Members.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildMembersChunkEventArgs> GuildMembersChunked
- {
- add => this._guildMembersChunk.Register(value);
- remove => this._guildMembersChunk.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildMembersChunkEventArgs> _guildMembersChunk;
-
- #endregion
-
- #region Guild Role
-
- /// <summary>
- /// Fired when a guild role is created.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildRoleCreateEventArgs> GuildRoleCreated
- {
- add => this._guildRoleCreated.Register(value);
- remove => this._guildRoleCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildRoleCreateEventArgs> _guildRoleCreated;
-
- /// <summary>
- /// Fired when a guild role is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildRoleUpdateEventArgs> GuildRoleUpdated
- {
- add => this._guildRoleUpdated.Register(value);
- remove => this._guildRoleUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildRoleUpdateEventArgs> _guildRoleUpdated;
-
- /// <summary>
- /// Fired when a guild role is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildRoleDeleteEventArgs> GuildRoleDeleted
- {
- add => this._guildRoleDeleted.Register(value);
- remove => this._guildRoleDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildRoleDeleteEventArgs> _guildRoleDeleted;
-
- #endregion
-
- #region Invite
-
- /// <summary>
- /// Fired when an invite is created.
- /// For this Event you need the <see cref="DiscordIntents.GuildInvites"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, InviteCreateEventArgs> InviteCreated
- {
- add => this._inviteCreated.Register(value);
- remove => this._inviteCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, InviteCreateEventArgs> _inviteCreated;
-
- /// <summary>
- /// Fired when an invite is deleted.
- /// For this Event you need the <see cref="DiscordIntents.GuildInvites"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, InviteDeleteEventArgs> InviteDeleted
- {
- add => this._inviteDeleted.Register(value);
- remove => this._inviteDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, InviteDeleteEventArgs> _inviteDeleted;
-
- #endregion
-
- #region Message
-
- /// <summary>
- /// Fired when a message is created.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageCreateEventArgs> MessageCreated
- {
- add => this._messageCreated.Register(value);
- remove => this._messageCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageCreateEventArgs> _messageCreated;
-
- /// <summary>
- /// Fired when a message is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageUpdateEventArgs> MessageUpdated
- {
- add => this._messageUpdated.Register(value);
- remove => this._messageUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageUpdateEventArgs> _messageUpdated;
-
- /// <summary>
- /// Fired when a message is deleted.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageDeleteEventArgs> MessageDeleted
- {
- add => this._messageDeleted.Register(value);
- remove => this._messageDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageDeleteEventArgs> _messageDeleted;
-
- /// <summary>
- /// Fired when multiple messages are deleted at once.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageBulkDeleteEventArgs> MessagesBulkDeleted
- {
- add => this._messageBulkDeleted.Register(value);
- remove => this._messageBulkDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageBulkDeleteEventArgs> _messageBulkDeleted;
-
- #endregion
-
- #region Message Reaction
-
- /// <summary>
- /// Fired when a reaction gets added to a message.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageReactionAddEventArgs> MessageReactionAdded
- {
- add => this._messageReactionAdded.Register(value);
- remove => this._messageReactionAdded.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageReactionAddEventArgs> _messageReactionAdded;
-
- /// <summary>
- /// Fired when a reaction gets removed from a message.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageReactionRemoveEventArgs> MessageReactionRemoved
- {
- add => this._messageReactionRemoved.Register(value);
- remove => this._messageReactionRemoved.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs> _messageReactionRemoved;
-
- /// <summary>
- /// Fired when all reactions get removed from a message.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageReactionsClearEventArgs> MessageReactionsCleared
- {
- add => this._messageReactionsCleared.Register(value);
- remove => this._messageReactionsCleared.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageReactionsClearEventArgs> _messageReactionsCleared;
-
- /// <summary>
- /// Fired when all reactions of a specific reaction are removed from a message.
- /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, MessageReactionRemoveEmojiEventArgs> MessageReactionRemovedEmoji
- {
- add => this._messageReactionRemovedEmoji.Register(value);
- remove => this._messageReactionRemovedEmoji.Unregister(value);
- }
- private AsyncEvent<DiscordClient, MessageReactionRemoveEmojiEventArgs> _messageReactionRemovedEmoji;
-
- #endregion
-
- #region Stage Instance
-
- /// <summary>
- /// Fired when a Stage Instance is created.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, StageInstanceCreateEventArgs> StageInstanceCreated
- {
- add => this._stageInstanceCreated.Register(value);
- remove => this._stageInstanceCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, StageInstanceCreateEventArgs> _stageInstanceCreated;
-
- /// <summary>
- /// Fired when a Stage Instance is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, StageInstanceUpdateEventArgs> StageInstanceUpdated
- {
- add => this._stageInstanceUpdated.Register(value);
- remove => this._stageInstanceUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, StageInstanceUpdateEventArgs> _stageInstanceUpdated;
-
- /// <summary>
- /// Fired when a Stage Instance is deleted.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, StageInstanceDeleteEventArgs> StageInstanceDeleted
- {
- add => this._stageInstanceDeleted.Register(value);
- remove => this._stageInstanceDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, StageInstanceDeleteEventArgs> _stageInstanceDeleted;
-
- #endregion
-
- #region Thread
-
- /// <summary>
- /// Fired when a thread is created.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadCreateEventArgs> ThreadCreated
- {
- add => this._threadCreated.Register(value);
- remove => this._threadCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadCreateEventArgs> _threadCreated;
-
- /// <summary>
- /// Fired when a thread is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadUpdateEventArgs> ThreadUpdated
- {
- add => this._threadUpdated.Register(value);
- remove => this._threadUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadUpdateEventArgs> _threadUpdated;
-
- /// <summary>
- /// Fired when a thread is deleted.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadDeleteEventArgs> ThreadDeleted
- {
- add => this._threadDeleted.Register(value);
- remove => this._threadDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadDeleteEventArgs> _threadDeleted;
-
- /// <summary>
- /// Fired when a thread member is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadListSyncEventArgs> ThreadListSynced
- {
- add => this._threadListSynced.Register(value);
- remove => this._threadListSynced.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadListSyncEventArgs> _threadListSynced;
-
/// <summary>
- /// Fired when a thread member is updated.
- /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// Represents a discord sharded client.
/// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadMemberUpdateEventArgs> ThreadMemberUpdated
+ public sealed partial class DiscordShardedClient
{
- add => this._threadMemberUpdated.Register(value);
- remove => this._threadMemberUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadMemberUpdateEventArgs> _threadMemberUpdated;
+ #region WebSocket
- /// <summary>
- /// Fired when the thread members are updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> or <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ThreadMembersUpdateEventArgs> ThreadMembersUpdated
- {
- add => this._threadMembersUpdated.Register(value);
- remove => this._threadMembersUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ThreadMembersUpdateEventArgs> _threadMembersUpdated;
-
- #endregion
-
- #region Activities
-
- /// <summary>
- /// Fired when a embedded activity has been updated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, EmbeddedActivityUpdateEventArgs> EmbeddedActivityUpdated
- {
- add => this._embeddedActivityUpdated.Register(value);
- remove => this._embeddedActivityUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, EmbeddedActivityUpdateEventArgs> _embeddedActivityUpdated;
-
- #endregion
-
- #region User/Presence Update
+ /// <summary>
+ /// Fired whenever a WebSocket error occurs within the client.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, SocketErrorEventArgs> SocketErrored
+ {
+ add => this._socketErrored.Register(value);
+ remove => this._socketErrored.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, SocketErrorEventArgs> _socketErrored;
- /// <summary>
- /// Fired when a presence has been updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, PresenceUpdateEventArgs> PresenceUpdated
- {
- add => this._presenceUpdated.Register(value);
- remove => this._presenceUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, PresenceUpdateEventArgs> _presenceUpdated;
+ /// <summary>
+ /// Fired whenever WebSocket connection is established.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, SocketEventArgs> SocketOpened
+ {
+ add => this._socketOpened.Register(value);
+ remove => this._socketOpened.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, SocketEventArgs> _socketOpened;
+ /// <summary>
+ /// Fired whenever WebSocket connection is terminated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, SocketCloseEventArgs> SocketClosed
+ {
+ add => this._socketClosed.Register(value);
+ remove => this._socketClosed.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, SocketCloseEventArgs> _socketClosed;
- /// <summary>
- /// Fired when the current user updates their settings.
- /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, UserSettingsUpdateEventArgs> UserSettingsUpdated
- {
- add => this._userSettingsUpdated.Register(value);
- remove => this._userSettingsUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, UserSettingsUpdateEventArgs> _userSettingsUpdated;
+ /// <summary>
+ /// Fired when the client enters ready state.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ReadyEventArgs> Ready
+ {
+ add => this._ready.Register(value);
+ remove => this._ready.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ReadyEventArgs> _ready;
- /// <summary>
- /// Fired when properties about the current user change.
- /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- /// <remarks>
- /// NB: This event only applies for changes to the <b>current user</b>, the client that is connected to Discord.
- /// </remarks>
- public event AsyncEventHandler<DiscordClient, UserUpdateEventArgs> UserUpdated
- {
- add => this._userUpdated.Register(value);
- remove => this._userUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, UserUpdateEventArgs> _userUpdated;
+ /// <summary>
+ /// Fired whenever a session is resumed.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ReadyEventArgs> Resumed
+ {
+ add => this._resumed.Register(value);
+ remove => this._resumed.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ReadyEventArgs> _resumed;
- #endregion
+ /// <summary>
+ /// Fired on received heartbeat ACK.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, HeartbeatEventArgs> Heartbeated
+ {
+ add => this._heartbeated.Register(value);
+ remove => this._heartbeated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, HeartbeatEventArgs> _heartbeated;
- #region Voice
+ #endregion
- /// <summary>
- /// Fired when someone joins/leaves/moves voice channels.
- /// For this Event you need the <see cref="DiscordIntents.GuildVoiceStates"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, VoiceStateUpdateEventArgs> VoiceStateUpdated
- {
- add => this._voiceStateUpdated.Register(value);
- remove => this._voiceStateUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, VoiceStateUpdateEventArgs> _voiceStateUpdated;
+ #region Channel
- /// <summary>
- /// Fired when a guild's voice server is updated.
- /// For this Event you need the <see cref="DiscordIntents.GuildVoiceStates"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
- /// </summary>
- public event AsyncEventHandler<DiscordClient, VoiceServerUpdateEventArgs> VoiceServerUpdated
- {
- add => this._voiceServerUpdated.Register(value);
- remove => this._voiceServerUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, VoiceServerUpdateEventArgs> _voiceServerUpdated;
+ /// <summary>
+ /// Fired when a new channel is created.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ChannelCreateEventArgs> ChannelCreated
+ {
+ add => this._channelCreated.Register(value);
+ remove => this._channelCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ChannelCreateEventArgs> _channelCreated;
- #endregion
+ /// <summary>
+ /// Fired when a channel is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ChannelUpdateEventArgs> ChannelUpdated
+ {
+ add => this._channelUpdated.Register(value);
+ remove => this._channelUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ChannelUpdateEventArgs> _channelUpdated;
- #region Application
+ /// <summary>
+ /// Fired when a channel is deleted
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ChannelDeleteEventArgs> ChannelDeleted
+ {
+ add => this._channelDeleted.Register(value);
+ remove => this._channelDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ChannelDeleteEventArgs> _channelDeleted;
- /// <summary>
- /// Fired when a new application command is registered.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandCreated
- {
- add => this._applicationCommandCreated.Register(value);
- remove => this._applicationCommandCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandCreated;
+ /// <summary>
+ /// Fired when a dm channel is deleted
+ /// For this Event you need the <see cref="DiscordIntents.DirectMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, DmChannelDeleteEventArgs> DmChannelDeleted
+ {
+ add => this._dmChannelDeleted.Register(value);
+ remove => this._dmChannelDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, DmChannelDeleteEventArgs> _dmChannelDeleted;
- /// <summary>
- /// Fired when an application command is updated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandUpdated
- {
- add => this._applicationCommandUpdated.Register(value);
- remove => this._applicationCommandUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandUpdated;
+ /// <summary>
+ /// Fired whenever a channel's pinned message list is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ChannelPinsUpdateEventArgs> ChannelPinsUpdated
+ {
+ add => this._channelPinsUpdated.Register(value);
+ remove => this._channelPinsUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ChannelPinsUpdateEventArgs> _channelPinsUpdated;
- /// <summary>
- /// Fired when an application command is deleted.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandDeleted
- {
- add => this._applicationCommandDeleted.Register(value);
- remove => this._applicationCommandDeleted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandDeleted;
+ #endregion
- /// <summary>
- /// Fired when a new application command is registered.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, GuildApplicationCommandCountEventArgs> GuildApplicationCommandCountUpdated
- {
- add => this._guildApplicationCommandCountUpdated.Register(value);
- remove => this._guildApplicationCommandCountUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, GuildApplicationCommandCountEventArgs> _guildApplicationCommandCountUpdated;
+ #region Guild
- /// <summary>
- /// Fired when a user uses a context menu.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ContextMenuInteractionCreateEventArgs> ContextMenuInteractionCreated
- {
- add => this._contextMenuInteractionCreated.Register(value);
- remove => this._contextMenuInteractionCreated.Unregister(value);
- }
+ /// <summary>
+ /// Fired when the user joins a new guild.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ /// <remarks>[alias="GuildJoined"][alias="JoinedGuild"]</remarks>
+ public event AsyncEventHandler<DiscordClient, GuildCreateEventArgs> GuildCreated
+ {
+ add => this._guildCreated.Register(value);
+ remove => this._guildCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildCreateEventArgs> _guildCreated;
- private AsyncEvent<DiscordClient, ContextMenuInteractionCreateEventArgs> _contextMenuInteractionCreated;
+ /// <summary>
+ /// Fired when a guild is becoming available.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildCreateEventArgs> GuildAvailable
+ {
+ add => this._guildAvailable.Register(value);
+ remove => this._guildAvailable.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildCreateEventArgs> _guildAvailable;
- /// <summary>
- /// Fired when application command permissions gets updated.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs> ApplicationCommandPermissionsUpdated
- {
- add => this._applicationCommandPermissionsUpdated.Register(value);
- remove => this._applicationCommandPermissionsUpdated.Unregister(value);
- }
+ /// <summary>
+ /// Fired when a guild is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildUpdateEventArgs> GuildUpdated
+ {
+ add => this._guildUpdated.Register(value);
+ remove => this._guildUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildUpdateEventArgs> _guildUpdated;
- private AsyncEvent<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs> _applicationCommandPermissionsUpdated;
+ /// <summary>
+ /// Fired when the user leaves or is removed from a guild.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildDeleteEventArgs> GuildDeleted
+ {
+ add => this._guildDeleted.Register(value);
+ remove => this._guildDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildDeleteEventArgs> _guildDeleted;
+ /// <summary>
+ /// Fired when a guild becomes unavailable.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildDeleteEventArgs> GuildUnavailable
+ {
+ add => this._guildUnavailable.Register(value);
+ remove => this._guildUnavailable.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildDeleteEventArgs> _guildUnavailable;
- #endregion
+ /// <summary>
+ /// Fired when all guilds finish streaming from Discord.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildDownloadCompletedEventArgs> GuildDownloadCompleted
+ {
+ add => this._guildDownloadCompleted.Register(value);
+ remove => this._guildDownloadCompleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildDownloadCompletedEventArgs> _guildDownloadCompleted;
- #region Misc
+ /// <summary>
+ /// Fired when a guilds emojis get updated
+ /// For this Event you need the <see cref="DiscordIntents.GuildEmojisAndStickers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildEmojisUpdateEventArgs> GuildEmojisUpdated
+ {
+ add => this._guildEmojisUpdated.Register(value);
+ remove => this._guildEmojisUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildEmojisUpdateEventArgs> _guildEmojisUpdated;
- /// <summary>
- /// Fired when an interaction is invoked.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, InteractionCreateEventArgs> InteractionCreated
- {
- add => this._interactionCreated.Register(value);
- remove => this._interactionCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, InteractionCreateEventArgs> _interactionCreated;
+ /// <summary>
+ /// Fired when a guilds stickers get updated
+ /// For this Event you need the <see cref="DiscordIntents.GuildEmojisAndStickers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildStickersUpdateEventArgs> GuildStickersUpdated
+ {
+ add => this._guildStickersUpdated.Register(value);
+ remove => this._guildStickersUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildStickersUpdateEventArgs> _guildStickersUpdated;
- /// <summary>
- /// Fired when a component is invoked.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ComponentInteractionCreateEventArgs> ComponentInteractionCreated
- {
- add => this._componentInteractionCreated.Register(value);
- remove => this._componentInteractionCreated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ComponentInteractionCreateEventArgs> _componentInteractionCreated;
+ /// <summary>
+ /// Fired when a guild integration is updated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildIntegrationsUpdateEventArgs> GuildIntegrationsUpdated
+ {
+ add => this._guildIntegrationsUpdated.Register(value);
+ remove => this._guildIntegrationsUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildIntegrationsUpdateEventArgs> _guildIntegrationsUpdated;
- /// <summary>
- /// Fired when a user starts typing in a channel.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, TypingStartEventArgs> TypingStarted
- {
- add => this._typingStarted.Register(value);
- remove => this._typingStarted.Unregister(value);
- }
- private AsyncEvent<DiscordClient, TypingStartEventArgs> _typingStarted;
+ #endregion
- /// <summary>
- /// Fired when an unknown event gets received.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, UnknownEventArgs> UnknownEvent
- {
- add => this._unknownEvent.Register(value);
- remove => this._unknownEvent.Unregister(value);
- }
- private AsyncEvent<DiscordClient, UnknownEventArgs> _unknownEvent;
+ #region Guild Ban
- /// <summary>
- /// Fired whenever webhooks update.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, WebhooksUpdateEventArgs> WebhooksUpdated
- {
- add => this._webhooksUpdated.Register(value);
- remove => this._webhooksUpdated.Unregister(value);
- }
- private AsyncEvent<DiscordClient, WebhooksUpdateEventArgs> _webhooksUpdated;
+ /// <summary>
+ /// Fired when a guild ban gets added
+ /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildBanAddEventArgs> GuildBanAdded
+ {
+ add => this._guildBanAdded.Register(value);
+ remove => this._guildBanAdded.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildBanAddEventArgs> _guildBanAdded;
- /// <summary>
- /// Fired whenever an error occurs within an event handler.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ClientErrorEventArgs> ClientErrored
- {
- add => this._clientErrored.Register(value);
- remove => this._clientErrored.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ClientErrorEventArgs> _clientErrored;
+ /// <summary>
+ /// Fired when a guild ban gets removed
+ /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildBanRemoveEventArgs> GuildBanRemoved
+ {
+ add => this._guildBanRemoved.Register(value);
+ remove => this._guildBanRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildBanRemoveEventArgs> _guildBanRemoved;
- #endregion
+ #endregion
- #region Error Handling
+ #region Guild Timeout
- /// <summary>
- /// Handles event errors.
- /// </summary>
- /// <param name="asyncEvent">The event.</param>
- /// <param name="ex">The exception.</param>
- /// <param name="handler">The event handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="eventArgs">The event args.</param>
- internal void EventErrorHandler<TArgs>(AsyncEvent<DiscordClient, TArgs> asyncEvent, Exception ex, AsyncEventHandler<DiscordClient, TArgs> handler, DiscordClient sender, TArgs eventArgs)
- where TArgs : AsyncEventArgs
- {
- if (ex is AsyncEventTimeoutException)
+ /// <summary>
+ /// Fired when a guild member timeout gets added.
+ /// For this Event you need the <see cref="DiscordIntents.GuildBans"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutAddEventArgs> GuildMemberTimeoutAdded
{
- this.Logger.LogWarning(LoggerEvents.EventHandlerException, $"An event handler for {asyncEvent.Name} took too long to execute. Defined as \"{handler.Method.ToString().Replace(handler.Method.ReturnType.ToString(), "").TrimStart()}\" located in \"{handler.Method.DeclaringType}\".");
- return;
+ add => this._guildMemberTimeoutAdded.Register(value);
+ remove => this._guildMemberTimeoutAdded.Unregister(value);
}
+ private AsyncEvent<DiscordClient, GuildMemberTimeoutAddEventArgs> _guildMemberTimeoutAdded;
- this.Logger.LogError(LoggerEvents.EventHandlerException, ex, "Event handler exception for event {0} thrown from {1} (defined in {2})", asyncEvent.Name, handler.Method, handler.Method.DeclaringType);
- this._clientErrored.InvokeAsync(sender, new ClientErrorEventArgs(this.ShardClients[0].ServiceProvider) { EventName = asyncEvent.Name, Exception = ex }).ConfigureAwait(false).GetAwaiter().GetResult();
- }
+ /// <summary>
+ /// Fired when a guild member timeout gets changed.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutUpdateEventArgs> GuildMemberTimeoutChanged
+ {
+ add => this._guildMemberTimeoutChanged.Register(value);
+ remove => this._guildMemberTimeoutChanged.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberTimeoutUpdateEventArgs> _guildMemberTimeoutChanged;
- /// <summary>
- /// Fired on heartbeat attempt cancellation due to too many failed heartbeats.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, ZombiedEventArgs> Zombied
- {
- add => this._zombied.Register(value);
- remove => this._zombied.Unregister(value);
- }
- private AsyncEvent<DiscordClient, ZombiedEventArgs> _zombied;
+ /// <summary>
+ /// Fired when a guild member timeout gets removed.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberTimeoutRemoveEventArgs> GuildMemberTimeoutRemoved
+ {
+ add => this._guildMemberTimeoutRemoved.Register(value);
+ remove => this._guildMemberTimeoutRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberTimeoutRemoveEventArgs> _guildMemberTimeoutRemoved;
- /// <summary>
- /// Fired when a gateway payload is received.
- /// </summary>
- public event AsyncEventHandler<DiscordClient, PayloadReceivedEventArgs> PayloadReceived
- {
- add => this._payloadReceived.Register(value);
- remove => this._payloadReceived.Unregister(value);
- }
- private AsyncEvent<DiscordClient, PayloadReceivedEventArgs> _payloadReceived;
+ #endregion
- /// <summary>
- /// Fired when a event handler throws an exception.
- /// </summary>
- /// <param name="asyncEvent">The event.</param>
- /// <param name="ex">The exception.</param>
- /// <param name="handler">The event handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="eventArgs">The event args.</param>
- private void Goof<TArgs>(AsyncEvent<DiscordClient, TArgs> asyncEvent, Exception ex, AsyncEventHandler<DiscordClient, TArgs> handler, DiscordClient sender, TArgs eventArgs)
- where TArgs : AsyncEventArgs => this.Logger.LogCritical(LoggerEvents.EventHandlerException, ex, "Exception event handler {0} (defined in {1}) threw an exception", handler.Method, handler.Method.DeclaringType);
+ #region Guild Event
- #endregion
-
- #region Event Dispatchers
+ /// <summary>
+ /// Fired when a scheduled event is created.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventCreateEventArgs> GuildScheduledEventCreated
+ {
+ add => this._guildScheduledEventCreated.Register(value);
+ remove => this._guildScheduledEventCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventCreateEventArgs> _guildScheduledEventCreated;
- /// <summary>
- /// Handles the client zombied event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_Zombied(DiscordClient client, ZombiedEventArgs e)
- => this._zombied.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a scheduled event is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventUpdateEventArgs> GuildScheduledEventUpdated
+ {
+ add => this._guildScheduledEventUpdated.Register(value);
+ remove => this._guildScheduledEventUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventUpdateEventArgs> _guildScheduledEventUpdated;
- /// <summary>
- /// Handles the guild member timeout removed event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildMemberTimeoutRemoved(DiscordClient client, GuildMemberTimeoutRemoveEventArgs e)
- => this._guildMemberTimeoutRemoved.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a scheduled event is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventDeleteEventArgs> GuildScheduledEventDeleted
+ {
+ add => this._guildScheduledEventDeleted.Register(value);
+ remove => this._guildScheduledEventDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventDeleteEventArgs> _guildScheduledEventDeleted;
- /// <summary>
- /// Handles the guild member timeout changed event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildMemberTimeoutChanged(DiscordClient client, GuildMemberTimeoutUpdateEventArgs e)
- => this._guildMemberTimeoutChanged.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a user subscribes to a scheduled event.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventUserAddEventArgs> GuildScheduledEventUserAdded
+ {
+ add => this._guildScheduledEventUserAdded.Register(value);
+ remove => this._guildScheduledEventUserAdded.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventUserAddEventArgs> _guildScheduledEventUserAdded;
- /// <summary>
- /// Handles the guild member timeout added event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildMemberTimeoutAdded(DiscordClient client, GuildMemberTimeoutAddEventArgs e)
- => this._guildMemberTimeoutAdded.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a user unsubscribes from a scheduled event.
+ /// For this Event you need the <see cref="DiscordIntents.GuildScheduledEvents"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildScheduledEventUserRemoveEventArgs> GuildScheduledEventUserRemoved
+ {
+ add => this._guildScheduledEventUserRemoved.Register(value);
+ remove => this._guildScheduledEventUserRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildScheduledEventUserRemoveEventArgs> _guildScheduledEventUserRemoved;
- /// <summary>
- /// Handles the embedded activity updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_EmbeddedActivityUpdated(DiscordClient client, EmbeddedActivityUpdateEventArgs e)
- => this._embeddedActivityUpdated.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the payload received event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_PayloadReceived(DiscordClient client, PayloadReceivedEventArgs e)
- => this._payloadReceived.InvokeAsync(client, e);
+ #region Guild Integration
- /// <summary>
- /// Handles the client error event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ClientError(DiscordClient client, ClientErrorEventArgs e)
- => this._clientErrored.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a guild integration is created.
+ /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildIntegrationCreateEventArgs> GuildIntegrationCreated
+ {
+ add => this._guildIntegrationCreated.Register(value);
+ remove => this._guildIntegrationCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildIntegrationCreateEventArgs> _guildIntegrationCreated;
- /// <summary>
- /// Handles the socket error event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_SocketError(DiscordClient client, SocketErrorEventArgs e)
- => this._socketErrored.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a guild integration is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildIntegrationUpdateEventArgs> GuildIntegrationUpdated
+ {
+ add => this._guildIntegrationUpdated.Register(value);
+ remove => this._guildIntegrationUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildIntegrationUpdateEventArgs> _guildIntegrationUpdated;
- /// <summary>
- /// Handles the socket opened event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_SocketOpened(DiscordClient client, SocketEventArgs e)
- => this._socketOpened.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a guild integration is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.GuildIntegrations"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildIntegrationDeleteEventArgs> GuildIntegrationDeleted
+ {
+ add => this._guildIntegrationDeleted.Register(value);
+ remove => this._guildIntegrationDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildIntegrationDeleteEventArgs> _guildIntegrationDeleted;
- /// <summary>
- /// Handles the socket closed event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_SocketClosed(DiscordClient client, SocketCloseEventArgs e)
- => this._socketClosed.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the ready event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_Ready(DiscordClient client, ReadyEventArgs e)
- => this._ready.InvokeAsync(client, e);
+ #region Guild Member
- /// <summary>
- /// Handles the resumed event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_Resumed(DiscordClient client, ReadyEventArgs e)
- => this._resumed.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a new user joins a guild.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberAddEventArgs> GuildMemberAdded
+ {
+ add => this._guildMemberAdded.Register(value);
+ remove => this._guildMemberAdded.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberAddEventArgs> _guildMemberAdded;
- /// <summary>
- /// Handles the channel created event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ChannelCreated(DiscordClient client, ChannelCreateEventArgs e)
- => this._channelCreated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a user is removed from a guild (leave/kick/ban).
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberRemoveEventArgs> GuildMemberRemoved
+ {
+ add => this._guildMemberRemoved.Register(value);
+ remove => this._guildMemberRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberRemoveEventArgs> _guildMemberRemoved;
- /// <summary>
- /// Handles the channel updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ChannelUpdated(DiscordClient client, ChannelUpdateEventArgs e)
- => this._channelUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a guild member is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMemberUpdateEventArgs> GuildMemberUpdated
+ {
+ add => this._guildMemberUpdated.Register(value);
+ remove => this._guildMemberUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMemberUpdateEventArgs> _guildMemberUpdated;
- /// <summary>
- /// Handles the channel deleted.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ChannelDeleted(DiscordClient client, ChannelDeleteEventArgs e)
- => this._channelDeleted.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired in response to Gateway Request Guild Members.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildMembersChunkEventArgs> GuildMembersChunked
+ {
+ add => this._guildMembersChunk.Register(value);
+ remove => this._guildMembersChunk.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildMembersChunkEventArgs> _guildMembersChunk;
- /// <summary>
- /// Handles the dm channel deleted event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_DMChannelDeleted(DiscordClient client, DmChannelDeleteEventArgs e)
- => this._dmChannelDeleted.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the channel pins updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ChannelPinsUpdated(DiscordClient client, ChannelPinsUpdateEventArgs e)
- => this._channelPinsUpdated.InvokeAsync(client, e);
+ #region Guild Role
- /// <summary>
- /// Handles the guild created event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildCreated(DiscordClient client, GuildCreateEventArgs e)
- => this._guildCreated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a guild role is created.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildRoleCreateEventArgs> GuildRoleCreated
+ {
+ add => this._guildRoleCreated.Register(value);
+ remove => this._guildRoleCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildRoleCreateEventArgs> _guildRoleCreated;
- /// <summary>
- /// Handles the guild available event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildAvailable(DiscordClient client, GuildCreateEventArgs e)
- => this._guildAvailable.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a guild role is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildRoleUpdateEventArgs> GuildRoleUpdated
+ {
+ add => this._guildRoleUpdated.Register(value);
+ remove => this._guildRoleUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildRoleUpdateEventArgs> _guildRoleUpdated;
- /// <summary>
- /// Handles the guild updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildUpdated(DiscordClient client, GuildUpdateEventArgs e)
- => this._guildUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a guild role is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildRoleDeleteEventArgs> GuildRoleDeleted
+ {
+ add => this._guildRoleDeleted.Register(value);
+ remove => this._guildRoleDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildRoleDeleteEventArgs> _guildRoleDeleted;
- /// <summary>
- /// Handles the guild deleted event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildDeleted(DiscordClient client, GuildDeleteEventArgs e)
- => this._guildDeleted.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the guild unavailable event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildUnavailable(DiscordClient client, GuildDeleteEventArgs e)
- => this._guildUnavailable.InvokeAsync(client, e);
+ #region Invite
- /// <summary>
- /// Handles the guild download completed event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildDownloadCompleted(DiscordClient client, GuildDownloadCompletedEventArgs e)
- => this._guildDownloadCompleted.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when an invite is created.
+ /// For this Event you need the <see cref="DiscordIntents.GuildInvites"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, InviteCreateEventArgs> InviteCreated
+ {
+ add => this._inviteCreated.Register(value);
+ remove => this._inviteCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, InviteCreateEventArgs> _inviteCreated;
- /// <summary>
- /// Handles the message created event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_MessageCreated(DiscordClient client, MessageCreateEventArgs e)
- => this._messageCreated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when an invite is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.GuildInvites"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, InviteDeleteEventArgs> InviteDeleted
+ {
+ add => this._inviteDeleted.Register(value);
+ remove => this._inviteDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, InviteDeleteEventArgs> _inviteDeleted;
- /// <summary>
- /// Handles the invite created event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_InviteCreated(DiscordClient client, InviteCreateEventArgs e)
- => this._inviteCreated.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the invite deleted event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_InviteDeleted(DiscordClient client, InviteDeleteEventArgs e)
- => this._inviteDeleted.InvokeAsync(client, e);
+ #region Message
- /// <summary>
- /// Handles the presence update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_PresenceUpdate(DiscordClient client, PresenceUpdateEventArgs e)
- => this._presenceUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a message is created.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageCreateEventArgs> MessageCreated
+ {
+ add => this._messageCreated.Register(value);
+ remove => this._messageCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageCreateEventArgs> _messageCreated;
- /// <summary>
- /// Handles the guild ban add event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildBanAdd(DiscordClient client, GuildBanAddEventArgs e)
- => this._guildBanAdded.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a message is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageUpdateEventArgs> MessageUpdated
+ {
+ add => this._messageUpdated.Register(value);
+ remove => this._messageUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageUpdateEventArgs> _messageUpdated;
- /// <summary>
- /// Handles the guild ban remove event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildBanRemove(DiscordClient client, GuildBanRemoveEventArgs e)
- => this._guildBanRemoved.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a message is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageDeleteEventArgs> MessageDeleted
+ {
+ add => this._messageDeleted.Register(value);
+ remove => this._messageDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageDeleteEventArgs> _messageDeleted;
- /// <summary>
- /// Handles the guild emojis update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildEmojisUpdate(DiscordClient client, GuildEmojisUpdateEventArgs e)
- => this._guildEmojisUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when multiple messages are deleted at once.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessages"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageBulkDeleteEventArgs> MessagesBulkDeleted
+ {
+ add => this._messageBulkDeleted.Register(value);
+ remove => this._messageBulkDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageBulkDeleteEventArgs> _messageBulkDeleted;
- /// <summary>
- /// Handles the guild stickers update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildStickersUpdate(DiscordClient client, GuildStickersUpdateEventArgs e)
- => this._guildStickersUpdated.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the guild integrations update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildIntegrationsUpdate(DiscordClient client, GuildIntegrationsUpdateEventArgs e)
- => this._guildIntegrationsUpdated.InvokeAsync(client, e);
+ #region Message Reaction
- /// <summary>
- /// Handles the guild member add event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildMemberAdd(DiscordClient client, GuildMemberAddEventArgs e)
- => this._guildMemberAdded.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a reaction gets added to a message.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageReactionAddEventArgs> MessageReactionAdded
+ {
+ add => this._messageReactionAdded.Register(value);
+ remove => this._messageReactionAdded.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageReactionAddEventArgs> _messageReactionAdded;
- /// <summary>
- /// Handles the guild member remove event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildMemberRemove(DiscordClient client, GuildMemberRemoveEventArgs e)
- => this._guildMemberRemoved.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a reaction gets removed from a message.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageReactionRemoveEventArgs> MessageReactionRemoved
+ {
+ add => this._messageReactionRemoved.Register(value);
+ remove => this._messageReactionRemoved.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs> _messageReactionRemoved;
- /// <summary>
- /// Handles the guild member update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildMemberUpdate(DiscordClient client, GuildMemberUpdateEventArgs e)
- => this._guildMemberUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when all reactions get removed from a message.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageReactionsClearEventArgs> MessageReactionsCleared
+ {
+ add => this._messageReactionsCleared.Register(value);
+ remove => this._messageReactionsCleared.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageReactionsClearEventArgs> _messageReactionsCleared;
- /// <summary>
- /// Handles the guild role create event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildRoleCreate(DiscordClient client, GuildRoleCreateEventArgs e)
- => this._guildRoleCreated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when all reactions of a specific reaction are removed from a message.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMessageReactions"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, MessageReactionRemoveEmojiEventArgs> MessageReactionRemovedEmoji
+ {
+ add => this._messageReactionRemovedEmoji.Register(value);
+ remove => this._messageReactionRemovedEmoji.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, MessageReactionRemoveEmojiEventArgs> _messageReactionRemovedEmoji;
- /// <summary>
- /// Handles the guild role update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildRoleUpdate(DiscordClient client, GuildRoleUpdateEventArgs e)
- => this._guildRoleUpdated.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the guild role delete event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildRoleDelete(DiscordClient client, GuildRoleDeleteEventArgs e)
- => this._guildRoleDeleted.InvokeAsync(client, e);
+ #region Stage Instance
- /// <summary>
- /// Handles the message update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_MessageUpdate(DiscordClient client, MessageUpdateEventArgs e)
- => this._messageUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a Stage Instance is created.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, StageInstanceCreateEventArgs> StageInstanceCreated
+ {
+ add => this._stageInstanceCreated.Register(value);
+ remove => this._stageInstanceCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, StageInstanceCreateEventArgs> _stageInstanceCreated;
- /// <summary>
- /// Handles the message delete event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_MessageDelete(DiscordClient client, MessageDeleteEventArgs e)
- => this._messageDeleted.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a Stage Instance is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, StageInstanceUpdateEventArgs> StageInstanceUpdated
+ {
+ add => this._stageInstanceUpdated.Register(value);
+ remove => this._stageInstanceUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, StageInstanceUpdateEventArgs> _stageInstanceUpdated;
- /// <summary>
- /// Handles the message bulk delete event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_MessageBulkDelete(DiscordClient client, MessageBulkDeleteEventArgs e)
- => this._messageBulkDeleted.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a Stage Instance is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, StageInstanceDeleteEventArgs> StageInstanceDeleted
+ {
+ add => this._stageInstanceDeleted.Register(value);
+ remove => this._stageInstanceDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, StageInstanceDeleteEventArgs> _stageInstanceDeleted;
- /// <summary>
- /// Handles the typing start event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_TypingStart(DiscordClient client, TypingStartEventArgs e)
- => this._typingStarted.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the user settings update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_UserSettingsUpdate(DiscordClient client, UserSettingsUpdateEventArgs e)
- => this._userSettingsUpdated.InvokeAsync(client, e);
+ #region Thread
- /// <summary>
- /// Handles the user update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_UserUpdate(DiscordClient client, UserUpdateEventArgs e)
- => this._userUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a thread is created.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadCreateEventArgs> ThreadCreated
+ {
+ add => this._threadCreated.Register(value);
+ remove => this._threadCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadCreateEventArgs> _threadCreated;
- /// <summary>
- /// Handles the voice state update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_VoiceStateUpdate(DiscordClient client, VoiceStateUpdateEventArgs e)
- => this._voiceStateUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a thread is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadUpdateEventArgs> ThreadUpdated
+ {
+ add => this._threadUpdated.Register(value);
+ remove => this._threadUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadUpdateEventArgs> _threadUpdated;
- /// <summary>
- /// Handles the voice server update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_VoiceServerUpdate(DiscordClient client, VoiceServerUpdateEventArgs e)
- => this._voiceServerUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a thread is deleted.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadDeleteEventArgs> ThreadDeleted
+ {
+ add => this._threadDeleted.Register(value);
+ remove => this._threadDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadDeleteEventArgs> _threadDeleted;
- /// <summary>
- /// Handles the guild members chunk event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildMembersChunk(DiscordClient client, GuildMembersChunkEventArgs e)
- => this._guildMembersChunk.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a thread member is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadListSyncEventArgs> ThreadListSynced
+ {
+ add => this._threadListSynced.Register(value);
+ remove => this._threadListSynced.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadListSyncEventArgs> _threadListSynced;
- /// <summary>
- /// Handles the unknown events.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_UnknownEvent(DiscordClient client, UnknownEventArgs e)
- => this._unknownEvent.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a thread member is updated.
+ /// For this Event you need the <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadMemberUpdateEventArgs> ThreadMemberUpdated
+ {
+ add => this._threadMemberUpdated.Register(value);
+ remove => this._threadMemberUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadMemberUpdateEventArgs> _threadMemberUpdated;
- /// <summary>
- /// Handles the message reaction add event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_MessageReactionAdd(DiscordClient client, MessageReactionAddEventArgs e)
- => this._messageReactionAdded.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when the thread members are updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildMembers"/> or <see cref="DiscordIntents.Guilds"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ThreadMembersUpdateEventArgs> ThreadMembersUpdated
+ {
+ add => this._threadMembersUpdated.Register(value);
+ remove => this._threadMembersUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ThreadMembersUpdateEventArgs> _threadMembersUpdated;
- /// <summary>
- /// Handles the message reaction remove event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_MessageReactionRemove(DiscordClient client, MessageReactionRemoveEventArgs e)
- => this._messageReactionRemoved.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the message reaction remove all event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_MessageReactionRemoveAll(DiscordClient client, MessageReactionsClearEventArgs e)
- => this._messageReactionsCleared.InvokeAsync(client, e);
+ #region Activities
- /// <summary>
- /// Handles the message reaction removed emoji event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_MessageReactionRemovedEmoji(DiscordClient client, MessageReactionRemoveEmojiEventArgs e)
- => this._messageReactionRemovedEmoji.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a embedded activity has been updated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, EmbeddedActivityUpdateEventArgs> EmbeddedActivityUpdated
+ {
+ add => this._embeddedActivityUpdated.Register(value);
+ remove => this._embeddedActivityUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, EmbeddedActivityUpdateEventArgs> _embeddedActivityUpdated;
- /// <summary>
- /// Handles the interaction create event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_InteractionCreate(DiscordClient client, InteractionCreateEventArgs e)
- => this._interactionCreated.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the component interaction create event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ComponentInteractionCreate(DiscordClient client, ComponentInteractionCreateEventArgs e)
- => this._componentInteractionCreated.InvokeAsync(client, e);
+ #region User/Presence Update
- /// <summary>
- /// Handles the context menu interaction create event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ContextMenuInteractionCreate(DiscordClient client, ContextMenuInteractionCreateEventArgs e)
- => this._contextMenuInteractionCreated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a presence has been updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, PresenceUpdateEventArgs> PresenceUpdated
+ {
+ add => this._presenceUpdated.Register(value);
+ remove => this._presenceUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, PresenceUpdateEventArgs> _presenceUpdated;
- /// <summary>
- /// Handles the webhooks update event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_WebhooksUpdate(DiscordClient client, WebhooksUpdateEventArgs e)
- => this._webhooksUpdated.InvokeAsync(client, e);
- /// <summary>
- /// Handles the heartbeated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_HeartBeated(DiscordClient client, HeartbeatEventArgs e)
- => this._heartbeated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when the current user updates their settings.
+ /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, UserSettingsUpdateEventArgs> UserSettingsUpdated
+ {
+ add => this._userSettingsUpdated.Register(value);
+ remove => this._userSettingsUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, UserSettingsUpdateEventArgs> _userSettingsUpdated;
+
+ /// <summary>
+ /// Fired when properties about the current user change.
+ /// For this Event you need the <see cref="DiscordIntents.GuildPresences"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ /// <remarks>
+ /// NB: This event only applies for changes to the <b>current user</b>, the client that is connected to Discord.
+ /// </remarks>
+ public event AsyncEventHandler<DiscordClient, UserUpdateEventArgs> UserUpdated
+ {
+ add => this._userUpdated.Register(value);
+ remove => this._userUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, UserUpdateEventArgs> _userUpdated;
- /// <summary>
- /// Handles the application command created event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ApplicationCommandCreated(DiscordClient client, ApplicationCommandEventArgs e)
- => this._applicationCommandCreated.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the application command updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ApplicationCommandUpdated(DiscordClient client, ApplicationCommandEventArgs e)
- => this._applicationCommandUpdated.InvokeAsync(client, e);
+ #region Voice
- /// <summary>
- /// Handles the application command deleted event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ApplicationCommandDeleted(DiscordClient client, ApplicationCommandEventArgs e)
- => this._applicationCommandDeleted.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when someone joins/leaves/moves voice channels.
+ /// For this Event you need the <see cref="DiscordIntents.GuildVoiceStates"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, VoiceStateUpdateEventArgs> VoiceStateUpdated
+ {
+ add => this._voiceStateUpdated.Register(value);
+ remove => this._voiceStateUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, VoiceStateUpdateEventArgs> _voiceStateUpdated;
+ /// <summary>
+ /// Fired when a guild's voice server is updated.
+ /// For this Event you need the <see cref="DiscordIntents.GuildVoiceStates"/> intent specified in <seealso cref="DiscordConfiguration.Intents"/>
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, VoiceServerUpdateEventArgs> VoiceServerUpdated
+ {
+ add => this._voiceServerUpdated.Register(value);
+ remove => this._voiceServerUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, VoiceServerUpdateEventArgs> _voiceServerUpdated;
- /// <summary>
- /// Handles the guild application command count updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildApplicationCommandCountUpdated(DiscordClient client, GuildApplicationCommandCountEventArgs e)
- => this._guildApplicationCommandCountUpdated.InvokeAsync(client, e);
+ #endregion
+ #region Application
- /// <summary>
- /// Handles the application command permissions updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ApplicationCommandPermissionsUpdated(DiscordClient client, ApplicationCommandPermissionsUpdateEventArgs e)
- => this._applicationCommandPermissionsUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a new application command is registered.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandCreated
+ {
+ add => this._applicationCommandCreated.Register(value);
+ remove => this._applicationCommandCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandCreated;
- /// <summary>
- /// Handles the guild integration created event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildIntegrationCreated(DiscordClient client, GuildIntegrationCreateEventArgs e)
- => this._guildIntegrationCreated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when an application command is updated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandUpdated
+ {
+ add => this._applicationCommandUpdated.Register(value);
+ remove => this._applicationCommandUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandUpdated;
- /// <summary>
- /// Handles the guild integration updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildIntegrationUpdated(DiscordClient client, GuildIntegrationUpdateEventArgs e)
- => this._guildIntegrationUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when an application command is deleted.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ApplicationCommandEventArgs> ApplicationCommandDeleted
+ {
+ add => this._applicationCommandDeleted.Register(value);
+ remove => this._applicationCommandDeleted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ApplicationCommandEventArgs> _applicationCommandDeleted;
- /// <summary>
- /// Handles the guild integration deleted event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildIntegrationDeleted(DiscordClient client, GuildIntegrationDeleteEventArgs e)
- => this._guildIntegrationDeleted.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a new application command is registered.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, GuildApplicationCommandCountEventArgs> GuildApplicationCommandCountUpdated
+ {
+ add => this._guildApplicationCommandCountUpdated.Register(value);
+ remove => this._guildApplicationCommandCountUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, GuildApplicationCommandCountEventArgs> _guildApplicationCommandCountUpdated;
- /// <summary>
- /// Handles the stage instance created event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_StageInstanceCreated(DiscordClient client, StageInstanceCreateEventArgs e)
- => this._stageInstanceCreated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a user uses a context menu.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ContextMenuInteractionCreateEventArgs> ContextMenuInteractionCreated
+ {
+ add => this._contextMenuInteractionCreated.Register(value);
+ remove => this._contextMenuInteractionCreated.Unregister(value);
+ }
- /// <summary>
- /// Handles the stage instance updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_StageInstanceUpdated(DiscordClient client, StageInstanceUpdateEventArgs e)
- => this._stageInstanceUpdated.InvokeAsync(client, e);
+ private AsyncEvent<DiscordClient, ContextMenuInteractionCreateEventArgs> _contextMenuInteractionCreated;
- /// <summary>
- /// Handles the stage instance deleted event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_StageInstanceDeleted(DiscordClient client, StageInstanceDeleteEventArgs e)
- => this._stageInstanceDeleted.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when application command permissions gets updated.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs> ApplicationCommandPermissionsUpdated
+ {
+ add => this._applicationCommandPermissionsUpdated.Register(value);
+ remove => this._applicationCommandPermissionsUpdated.Unregister(value);
+ }
- /// <summary>
- /// Handles the thread created event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ThreadCreated(DiscordClient client, ThreadCreateEventArgs e)
- => this._threadCreated.InvokeAsync(client, e);
+ private AsyncEvent<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs> _applicationCommandPermissionsUpdated;
- /// <summary>
- /// Handles the thread updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ThreadUpdated(DiscordClient client, ThreadUpdateEventArgs e)
- => this._threadUpdated.InvokeAsync(client, e);
- /// <summary>
- /// Handles the thread deleted event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ThreadDeleted(DiscordClient client, ThreadDeleteEventArgs e)
- => this._threadDeleted.InvokeAsync(client, e);
+ #endregion
- /// <summary>
- /// Handles the thread list synced event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ThreadListSynced(DiscordClient client, ThreadListSyncEventArgs e)
- => this._threadListSynced.InvokeAsync(client, e);
+ #region Misc
- /// <summary>
- /// Handles the thread member updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ThreadMemberUpdated(DiscordClient client, ThreadMemberUpdateEventArgs e)
- => this._threadMemberUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when an interaction is invoked.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, InteractionCreateEventArgs> InteractionCreated
+ {
+ add => this._interactionCreated.Register(value);
+ remove => this._interactionCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, InteractionCreateEventArgs> _interactionCreated;
- /// <summary>
- /// Handles the thread members updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_ThreadMembersUpdated(DiscordClient client, ThreadMembersUpdateEventArgs e)
- => this._threadMembersUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when a component is invoked.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ComponentInteractionCreateEventArgs> ComponentInteractionCreated
+ {
+ add => this._componentInteractionCreated.Register(value);
+ remove => this._componentInteractionCreated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ComponentInteractionCreateEventArgs> _componentInteractionCreated;
+ /// <summary>
+ /// Fired when a user starts typing in a channel.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, TypingStartEventArgs> TypingStarted
+ {
+ add => this._typingStarted.Register(value);
+ remove => this._typingStarted.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, TypingStartEventArgs> _typingStarted;
- /// <summary>
- /// Handles the scheduled event created event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildScheduledEventCreated(DiscordClient client, GuildScheduledEventCreateEventArgs e)
- => this._guildScheduledEventCreated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired when an unknown event gets received.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, UnknownEventArgs> UnknownEvent
+ {
+ add => this._unknownEvent.Register(value);
+ remove => this._unknownEvent.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, UnknownEventArgs> _unknownEvent;
- /// <summary>
- /// Handles the scheduled event updated event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildScheduledEventUpdated(DiscordClient client, GuildScheduledEventUpdateEventArgs e)
- => this._guildScheduledEventUpdated.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired whenever webhooks update.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, WebhooksUpdateEventArgs> WebhooksUpdated
+ {
+ add => this._webhooksUpdated.Register(value);
+ remove => this._webhooksUpdated.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, WebhooksUpdateEventArgs> _webhooksUpdated;
- /// <summary>
- /// Handles the scheduled event deleted event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildScheduledEventDeleted(DiscordClient client, GuildScheduledEventDeleteEventArgs e)
- => this._guildScheduledEventDeleted.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired whenever an error occurs within an event handler.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ClientErrorEventArgs> ClientErrored
+ {
+ add => this._clientErrored.Register(value);
+ remove => this._clientErrored.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ClientErrorEventArgs> _clientErrored;
+
+ #endregion
+
+ #region Error Handling
+
+ /// <summary>
+ /// Handles event errors.
+ /// </summary>
+ /// <param name="asyncEvent">The event.</param>
+ /// <param name="ex">The exception.</param>
+ /// <param name="handler">The event handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="eventArgs">The event args.</param>
+ internal void EventErrorHandler<TArgs>(AsyncEvent<DiscordClient, TArgs> asyncEvent, Exception ex, AsyncEventHandler<DiscordClient, TArgs> handler, DiscordClient sender, TArgs eventArgs)
+ where TArgs : AsyncEventArgs
+ {
+ if (ex is AsyncEventTimeoutException)
+ {
+ this.Logger.LogWarning(LoggerEvents.EventHandlerException, $"An event handler for {asyncEvent.Name} took too long to execute. Defined as \"{handler.Method.ToString().Replace(handler.Method.ReturnType.ToString(), "").TrimStart()}\" located in \"{handler.Method.DeclaringType}\".");
+ return;
+ }
+
+ this.Logger.LogError(LoggerEvents.EventHandlerException, ex, "Event handler exception for event {0} thrown from {1} (defined in {2})", asyncEvent.Name, handler.Method, handler.Method.DeclaringType);
+ this._clientErrored.InvokeAsync(sender, new ClientErrorEventArgs(this.ShardClients[0].ServiceProvider) { EventName = asyncEvent.Name, Exception = ex }).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
- /// <summary>
- /// Handles the scheduled event user added event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildScheduledEventUserAdded(DiscordClient client, GuildScheduledEventUserAddEventArgs e)
- => this._guildScheduledEventUserAdded.InvokeAsync(client, e);
- /// <summary>
- /// Handles the scheduled event user removed event.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="e">The event args.</param>
- private Task Client_GuildScheduledEventUserRemoved(DiscordClient client, GuildScheduledEventUserRemoveEventArgs e)
- => this._guildScheduledEventUserRemoved.InvokeAsync(client, e);
+ /// <summary>
+ /// Fired on heartbeat attempt cancellation due to too many failed heartbeats.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, ZombiedEventArgs> Zombied
+ {
+ add => this._zombied.Register(value);
+ remove => this._zombied.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, ZombiedEventArgs> _zombied;
- #endregion
+ /// <summary>
+ /// Fired when a gateway payload is received.
+ /// </summary>
+ public event AsyncEventHandler<DiscordClient, PayloadReceivedEventArgs> PayloadReceived
+ {
+ add => this._payloadReceived.Register(value);
+ remove => this._payloadReceived.Unregister(value);
+ }
+ private AsyncEvent<DiscordClient, PayloadReceivedEventArgs> _payloadReceived;
+
+ /// <summary>
+ /// Fired when a event handler throws an exception.
+ /// </summary>
+ /// <param name="asyncEvent">The event.</param>
+ /// <param name="ex">The exception.</param>
+ /// <param name="handler">The event handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="eventArgs">The event args.</param>
+ private void Goof<TArgs>(AsyncEvent<DiscordClient, TArgs> asyncEvent, Exception ex, AsyncEventHandler<DiscordClient, TArgs> handler, DiscordClient sender, TArgs eventArgs)
+ where TArgs : AsyncEventArgs => this.Logger.LogCritical(LoggerEvents.EventHandlerException, ex, "Exception event handler {0} (defined in {1}) threw an exception", handler.Method, handler.Method.DeclaringType);
+
+ #endregion
+
+ #region Event Dispatchers
+
+ /// <summary>
+ /// Handles the client zombied event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_Zombied(DiscordClient client, ZombiedEventArgs e)
+ => this._zombied.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild member timeout removed event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildMemberTimeoutRemoved(DiscordClient client, GuildMemberTimeoutRemoveEventArgs e)
+ => this._guildMemberTimeoutRemoved.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild member timeout changed event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildMemberTimeoutChanged(DiscordClient client, GuildMemberTimeoutUpdateEventArgs e)
+ => this._guildMemberTimeoutChanged.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild member timeout added event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildMemberTimeoutAdded(DiscordClient client, GuildMemberTimeoutAddEventArgs e)
+ => this._guildMemberTimeoutAdded.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the embedded activity updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_EmbeddedActivityUpdated(DiscordClient client, EmbeddedActivityUpdateEventArgs e)
+ => this._embeddedActivityUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the payload received event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_PayloadReceived(DiscordClient client, PayloadReceivedEventArgs e)
+ => this._payloadReceived.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the client error event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ClientError(DiscordClient client, ClientErrorEventArgs e)
+ => this._clientErrored.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the socket error event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_SocketError(DiscordClient client, SocketErrorEventArgs e)
+ => this._socketErrored.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the socket opened event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_SocketOpened(DiscordClient client, SocketEventArgs e)
+ => this._socketOpened.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the socket closed event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_SocketClosed(DiscordClient client, SocketCloseEventArgs e)
+ => this._socketClosed.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the ready event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_Ready(DiscordClient client, ReadyEventArgs e)
+ => this._ready.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the resumed event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_Resumed(DiscordClient client, ReadyEventArgs e)
+ => this._resumed.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the channel created event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ChannelCreated(DiscordClient client, ChannelCreateEventArgs e)
+ => this._channelCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the channel updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ChannelUpdated(DiscordClient client, ChannelUpdateEventArgs e)
+ => this._channelUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the channel deleted.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ChannelDeleted(DiscordClient client, ChannelDeleteEventArgs e)
+ => this._channelDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the dm channel deleted event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_DMChannelDeleted(DiscordClient client, DmChannelDeleteEventArgs e)
+ => this._dmChannelDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the channel pins updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ChannelPinsUpdated(DiscordClient client, ChannelPinsUpdateEventArgs e)
+ => this._channelPinsUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild created event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildCreated(DiscordClient client, GuildCreateEventArgs e)
+ => this._guildCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild available event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildAvailable(DiscordClient client, GuildCreateEventArgs e)
+ => this._guildAvailable.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildUpdated(DiscordClient client, GuildUpdateEventArgs e)
+ => this._guildUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild deleted event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildDeleted(DiscordClient client, GuildDeleteEventArgs e)
+ => this._guildDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild unavailable event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildUnavailable(DiscordClient client, GuildDeleteEventArgs e)
+ => this._guildUnavailable.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild download completed event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildDownloadCompleted(DiscordClient client, GuildDownloadCompletedEventArgs e)
+ => this._guildDownloadCompleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the message created event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_MessageCreated(DiscordClient client, MessageCreateEventArgs e)
+ => this._messageCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the invite created event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_InviteCreated(DiscordClient client, InviteCreateEventArgs e)
+ => this._inviteCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the invite deleted event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_InviteDeleted(DiscordClient client, InviteDeleteEventArgs e)
+ => this._inviteDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the presence update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_PresenceUpdate(DiscordClient client, PresenceUpdateEventArgs e)
+ => this._presenceUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild ban add event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildBanAdd(DiscordClient client, GuildBanAddEventArgs e)
+ => this._guildBanAdded.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild ban remove event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildBanRemove(DiscordClient client, GuildBanRemoveEventArgs e)
+ => this._guildBanRemoved.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild emojis update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildEmojisUpdate(DiscordClient client, GuildEmojisUpdateEventArgs e)
+ => this._guildEmojisUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild stickers update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildStickersUpdate(DiscordClient client, GuildStickersUpdateEventArgs e)
+ => this._guildStickersUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild integrations update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildIntegrationsUpdate(DiscordClient client, GuildIntegrationsUpdateEventArgs e)
+ => this._guildIntegrationsUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild member add event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildMemberAdd(DiscordClient client, GuildMemberAddEventArgs e)
+ => this._guildMemberAdded.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild member remove event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildMemberRemove(DiscordClient client, GuildMemberRemoveEventArgs e)
+ => this._guildMemberRemoved.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild member update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildMemberUpdate(DiscordClient client, GuildMemberUpdateEventArgs e)
+ => this._guildMemberUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild role create event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildRoleCreate(DiscordClient client, GuildRoleCreateEventArgs e)
+ => this._guildRoleCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild role update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildRoleUpdate(DiscordClient client, GuildRoleUpdateEventArgs e)
+ => this._guildRoleUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild role delete event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildRoleDelete(DiscordClient client, GuildRoleDeleteEventArgs e)
+ => this._guildRoleDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the message update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_MessageUpdate(DiscordClient client, MessageUpdateEventArgs e)
+ => this._messageUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the message delete event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_MessageDelete(DiscordClient client, MessageDeleteEventArgs e)
+ => this._messageDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the message bulk delete event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_MessageBulkDelete(DiscordClient client, MessageBulkDeleteEventArgs e)
+ => this._messageBulkDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the typing start event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_TypingStart(DiscordClient client, TypingStartEventArgs e)
+ => this._typingStarted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the user settings update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_UserSettingsUpdate(DiscordClient client, UserSettingsUpdateEventArgs e)
+ => this._userSettingsUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the user update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_UserUpdate(DiscordClient client, UserUpdateEventArgs e)
+ => this._userUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the voice state update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_VoiceStateUpdate(DiscordClient client, VoiceStateUpdateEventArgs e)
+ => this._voiceStateUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the voice server update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_VoiceServerUpdate(DiscordClient client, VoiceServerUpdateEventArgs e)
+ => this._voiceServerUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild members chunk event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildMembersChunk(DiscordClient client, GuildMembersChunkEventArgs e)
+ => this._guildMembersChunk.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the unknown events.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_UnknownEvent(DiscordClient client, UnknownEventArgs e)
+ => this._unknownEvent.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the message reaction add event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_MessageReactionAdd(DiscordClient client, MessageReactionAddEventArgs e)
+ => this._messageReactionAdded.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the message reaction remove event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_MessageReactionRemove(DiscordClient client, MessageReactionRemoveEventArgs e)
+ => this._messageReactionRemoved.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the message reaction remove all event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_MessageReactionRemoveAll(DiscordClient client, MessageReactionsClearEventArgs e)
+ => this._messageReactionsCleared.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the message reaction removed emoji event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_MessageReactionRemovedEmoji(DiscordClient client, MessageReactionRemoveEmojiEventArgs e)
+ => this._messageReactionRemovedEmoji.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the interaction create event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_InteractionCreate(DiscordClient client, InteractionCreateEventArgs e)
+ => this._interactionCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the component interaction create event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ComponentInteractionCreate(DiscordClient client, ComponentInteractionCreateEventArgs e)
+ => this._componentInteractionCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the context menu interaction create event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ContextMenuInteractionCreate(DiscordClient client, ContextMenuInteractionCreateEventArgs e)
+ => this._contextMenuInteractionCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the webhooks update event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_WebhooksUpdate(DiscordClient client, WebhooksUpdateEventArgs e)
+ => this._webhooksUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the heartbeated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_HeartBeated(DiscordClient client, HeartbeatEventArgs e)
+ => this._heartbeated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the application command created event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ApplicationCommandCreated(DiscordClient client, ApplicationCommandEventArgs e)
+ => this._applicationCommandCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the application command updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ApplicationCommandUpdated(DiscordClient client, ApplicationCommandEventArgs e)
+ => this._applicationCommandUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the application command deleted event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ApplicationCommandDeleted(DiscordClient client, ApplicationCommandEventArgs e)
+ => this._applicationCommandDeleted.InvokeAsync(client, e);
+
+
+ /// <summary>
+ /// Handles the guild application command count updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildApplicationCommandCountUpdated(DiscordClient client, GuildApplicationCommandCountEventArgs e)
+ => this._guildApplicationCommandCountUpdated.InvokeAsync(client, e);
+
+
+ /// <summary>
+ /// Handles the application command permissions updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ApplicationCommandPermissionsUpdated(DiscordClient client, ApplicationCommandPermissionsUpdateEventArgs e)
+ => this._applicationCommandPermissionsUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild integration created event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildIntegrationCreated(DiscordClient client, GuildIntegrationCreateEventArgs e)
+ => this._guildIntegrationCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild integration updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildIntegrationUpdated(DiscordClient client, GuildIntegrationUpdateEventArgs e)
+ => this._guildIntegrationUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the guild integration deleted event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildIntegrationDeleted(DiscordClient client, GuildIntegrationDeleteEventArgs e)
+ => this._guildIntegrationDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the stage instance created event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_StageInstanceCreated(DiscordClient client, StageInstanceCreateEventArgs e)
+ => this._stageInstanceCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the stage instance updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_StageInstanceUpdated(DiscordClient client, StageInstanceUpdateEventArgs e)
+ => this._stageInstanceUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the stage instance deleted event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_StageInstanceDeleted(DiscordClient client, StageInstanceDeleteEventArgs e)
+ => this._stageInstanceDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the thread created event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ThreadCreated(DiscordClient client, ThreadCreateEventArgs e)
+ => this._threadCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the thread updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ThreadUpdated(DiscordClient client, ThreadUpdateEventArgs e)
+ => this._threadUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the thread deleted event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ThreadDeleted(DiscordClient client, ThreadDeleteEventArgs e)
+ => this._threadDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the thread list synced event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ThreadListSynced(DiscordClient client, ThreadListSyncEventArgs e)
+ => this._threadListSynced.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the thread member updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ThreadMemberUpdated(DiscordClient client, ThreadMemberUpdateEventArgs e)
+ => this._threadMemberUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the thread members updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_ThreadMembersUpdated(DiscordClient client, ThreadMembersUpdateEventArgs e)
+ => this._threadMembersUpdated.InvokeAsync(client, e);
+
+
+ /// <summary>
+ /// Handles the scheduled event created event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildScheduledEventCreated(DiscordClient client, GuildScheduledEventCreateEventArgs e)
+ => this._guildScheduledEventCreated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the scheduled event updated event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildScheduledEventUpdated(DiscordClient client, GuildScheduledEventUpdateEventArgs e)
+ => this._guildScheduledEventUpdated.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the scheduled event deleted event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildScheduledEventDeleted(DiscordClient client, GuildScheduledEventDeleteEventArgs e)
+ => this._guildScheduledEventDeleted.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the scheduled event user added event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildScheduledEventUserAdded(DiscordClient client, GuildScheduledEventUserAddEventArgs e)
+ => this._guildScheduledEventUserAdded.InvokeAsync(client, e);
+
+ /// <summary>
+ /// Handles the scheduled event user removed event.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="e">The event args.</param>
+ private Task Client_GuildScheduledEventUserRemoved(DiscordClient client, GuildScheduledEventUserRemoveEventArgs e)
+ => this._guildScheduledEventUserRemoved.InvokeAsync(client, e);
+
+ #endregion
+ }
}
diff --git a/DisCatSharp/Clients/DiscordShardedClient.cs b/DisCatSharp/Clients/DiscordShardedClient.cs
index b5af00724..790001591 100644
--- a/DisCatSharp/Clients/DiscordShardedClient.cs
+++ b/DisCatSharp/Clients/DiscordShardedClient.cs
@@ -1,778 +1,779 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
#pragma warning disable CS0618
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.Entities;
using DisCatSharp.EventArgs;
using DisCatSharp.Net;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp;
-
-/// <summary>
-/// A Discord client that shards automatically.
-/// </summary>
-public sealed partial class DiscordShardedClient
+namespace DisCatSharp
{
- #region Public Properties
-
- /// <summary>
- /// Gets the logger for this client.
- /// </summary>
- public ILogger<BaseDiscordClient> Logger { get; }
-
- /// <summary>
- /// Gets all client shards.
- /// </summary>
- public IReadOnlyDictionary<int, DiscordClient> ShardClients { get; }
-
- /// <summary>
- /// Gets the gateway info for the client's session.
- /// </summary>
- public GatewayInfo GatewayInfo { get; private set; }
-
- /// <summary>
- /// Gets the current user.
- /// </summary>
- public DiscordUser CurrentUser { get; private set; }
-
- /// <summary>
- /// Gets the current application.
- /// </summary>
- public DiscordApplication CurrentApplication { get; private set; }
-
- [Obsolete("Use GetLibraryDevelopmentTeamAsync")]
- public DisCatSharpTeam LibraryDeveloperTeam
- => this.GetLibraryDevelopmentTeamAsync().Result;
-
- /// <summary>
- /// Gets the list of available voice regions. Note that this property will not contain VIP voice regions.
- /// </summary>
- public IReadOnlyDictionary<string, DiscordVoiceRegion> VoiceRegions
- => this._voiceRegionsLazy?.Value;
-
- #endregion
-
- #region Private Properties/Fields
-
/// <summary>
- /// Gets the configuration.
+ /// A Discord client that shards automatically.
/// </summary>
- private readonly DiscordConfiguration _configuration;
-
- /// <summary>
- /// Gets the list of available voice regions. This property is meant as a way to modify <see cref="VoiceRegions"/>.
- /// </summary>
- private ConcurrentDictionary<string, DiscordVoiceRegion> _internalVoiceRegions;
-
- /// <summary>
- /// Gets a list of shards.
- /// </summary>
- private readonly ConcurrentDictionary<int, DiscordClient> _shards = new();
-
- /// <summary>
- /// Gets a lazy list of voice regions.
- /// </summary>
- private Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>> _voiceRegionsLazy;
-
- /// <summary>
- /// Whether the shard client is started.
- /// </summary>
- private bool _isStarted;
-
- /// <summary>
- /// Whether manual sharding is enabled.
- /// </summary>
- private readonly bool _manuallySharding;
-
- #endregion
-
- #region Constructor
-
- /// <summary>
- /// Initializes a new auto-sharding Discord client.
- /// </summary>
- /// <param name="config">The configuration to use.</param>
- public DiscordShardedClient(DiscordConfiguration config)
+ public sealed partial class DiscordShardedClient
{
- this.InternalSetup();
+ #region Public Properties
+
+ /// <summary>
+ /// Gets the logger for this client.
+ /// </summary>
+ public ILogger<BaseDiscordClient> Logger { get; }
+
+ /// <summary>
+ /// Gets all client shards.
+ /// </summary>
+ public IReadOnlyDictionary<int, DiscordClient> ShardClients { get; }
+
+ /// <summary>
+ /// Gets the gateway info for the client's session.
+ /// </summary>
+ public GatewayInfo GatewayInfo { get; private set; }
+
+ /// <summary>
+ /// Gets the current user.
+ /// </summary>
+ public DiscordUser CurrentUser { get; private set; }
+
+ /// <summary>
+ /// Gets the current application.
+ /// </summary>
+ public DiscordApplication CurrentApplication { get; private set; }
+
+ [Obsolete("Use GetLibraryDevelopmentTeamAsync")]
+ public DisCatSharpTeam LibraryDeveloperTeam
+ => this.GetLibraryDevelopmentTeamAsync().Result;
+
+ /// <summary>
+ /// Gets the list of available voice regions. Note that this property will not contain VIP voice regions.
+ /// </summary>
+ public IReadOnlyDictionary<string, DiscordVoiceRegion> VoiceRegions
+ => this._voiceRegionsLazy?.Value;
+
+ #endregion
+
+ #region Private Properties/Fields
+
+ /// <summary>
+ /// Gets the configuration.
+ /// </summary>
+ private readonly DiscordConfiguration _configuration;
+
+ /// <summary>
+ /// Gets the list of available voice regions. This property is meant as a way to modify <see cref="VoiceRegions"/>.
+ /// </summary>
+ private ConcurrentDictionary<string, DiscordVoiceRegion> _internalVoiceRegions;
+
+ /// <summary>
+ /// Gets a list of shards.
+ /// </summary>
+ private readonly ConcurrentDictionary<int, DiscordClient> _shards = new();
+
+ /// <summary>
+ /// Gets a lazy list of voice regions.
+ /// </summary>
+ private Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>> _voiceRegionsLazy;
+
+ /// <summary>
+ /// Whether the shard client is started.
+ /// </summary>
+ private bool _isStarted;
+
+ /// <summary>
+ /// Whether manual sharding is enabled.
+ /// </summary>
+ private readonly bool _manuallySharding;
+
+ #endregion
+
+ #region Constructor
+
+ /// <summary>
+ /// Initializes a new auto-sharding Discord client.
+ /// </summary>
+ /// <param name="config">The configuration to use.</param>
+ public DiscordShardedClient(DiscordConfiguration config)
+ {
+ this.InternalSetup();
- if (config.ShardCount > 1)
- this._manuallySharding = true;
+ if (config.ShardCount > 1)
+ this._manuallySharding = true;
- this._configuration = config;
- this.ShardClients = new ReadOnlyConcurrentDictionary<int, DiscordClient>(this._shards);
+ this._configuration = config;
+ this.ShardClients = new ReadOnlyConcurrentDictionary<int, DiscordClient>(this._shards);
- if (this._configuration.LoggerFactory == null)
- {
- this._configuration.LoggerFactory = new DefaultLoggerFactory();
- this._configuration.LoggerFactory.AddProvider(new DefaultLoggerProvider(this._configuration.MinimumLogLevel, this._configuration.LogTimestampFormat));
+ if (this._configuration.LoggerFactory == null)
+ {
+ this._configuration.LoggerFactory = new DefaultLoggerFactory();
+ this._configuration.LoggerFactory.AddProvider(new DefaultLoggerProvider(this._configuration.MinimumLogLevel, this._configuration.LogTimestampFormat));
+ }
+ this.Logger = this._configuration.LoggerFactory.CreateLogger<BaseDiscordClient>();
}
- this.Logger = this._configuration.LoggerFactory.CreateLogger<BaseDiscordClient>();
- }
- #endregion
-
- #region Public Methods
-
- /// <summary>
- /// Initializes and connects all shards.
- /// </summary>
- /// <exception cref="System.AggregateException"></exception>
- /// <exception cref="System.InvalidOperationException"></exception>
- public async Task StartAsync()
- {
- if (this._isStarted)
- throw new InvalidOperationException("This client has already been started.");
+ #endregion
- this._isStarted = true;
+ #region Public Methods
- try
+ /// <summary>
+ /// Initializes and connects all shards.
+ /// </summary>
+ /// <exception cref="System.AggregateException"></exception>
+ /// <exception cref="System.InvalidOperationException"></exception>
+ public async Task StartAsync()
{
- if (this._configuration.TokenType != TokenType.Bot)
- this.Logger.LogWarning(LoggerEvents.Misc, "You are logging in with a token that is not a bot token. This is not officially supported by Discord, and can result in your account being terminated if you aren't careful.");
- this.Logger.LogInformation(LoggerEvents.Startup, "Lib {0}, version {1}", this._botLibrary, this._versionString.Value);
+ if (this._isStarted)
+ throw new InvalidOperationException("This client has already been started.");
- var shardc = await this.InitializeShardsAsync().ConfigureAwait(false);
- var connectTasks = new List<Task>();
- this.Logger.LogInformation(LoggerEvents.ShardStartup, "Booting {0} shards.", shardc);
+ this._isStarted = true;
- for (var i = 0; i < shardc; i++)
+ try
{
- //This should never happen, but in case it does...
- if (this.GatewayInfo.SessionBucket.MaxConcurrency < 1)
- this.GatewayInfo.SessionBucket.MaxConcurrency = 1;
+ if (this._configuration.TokenType != TokenType.Bot)
+ this.Logger.LogWarning(LoggerEvents.Misc, "You are logging in with a token that is not a bot token. This is not officially supported by Discord, and can result in your account being terminated if you aren't careful.");
+ this.Logger.LogInformation(LoggerEvents.Startup, "Lib {0}, version {1}", this._botLibrary, this._versionString.Value);
- if (this.GatewayInfo.SessionBucket.MaxConcurrency == 1)
- await this.ConnectShardAsync(i).ConfigureAwait(false);
- else
+ var shardc = await this.InitializeShardsAsync().ConfigureAwait(false);
+ var connectTasks = new List<Task>();
+ this.Logger.LogInformation(LoggerEvents.ShardStartup, "Booting {0} shards.", shardc);
+
+ for (var i = 0; i < shardc; i++)
{
- //Concurrent login.
- connectTasks.Add(this.ConnectShardAsync(i));
+ //This should never happen, but in case it does...
+ if (this.GatewayInfo.SessionBucket.MaxConcurrency < 1)
+ this.GatewayInfo.SessionBucket.MaxConcurrency = 1;
- if (connectTasks.Count == this.GatewayInfo.SessionBucket.MaxConcurrency)
+ if (this.GatewayInfo.SessionBucket.MaxConcurrency == 1)
+ await this.ConnectShardAsync(i).ConfigureAwait(false);
+ else
{
- await Task.WhenAll(connectTasks).ConfigureAwait(false);
- connectTasks.Clear();
+ //Concurrent login.
+ connectTasks.Add(this.ConnectShardAsync(i));
+
+ if (connectTasks.Count == this.GatewayInfo.SessionBucket.MaxConcurrency)
+ {
+ await Task.WhenAll(connectTasks).ConfigureAwait(false);
+ connectTasks.Clear();
+ }
}
}
}
- }
- catch (Exception ex)
- {
- await this.InternalStopAsync(false).ConfigureAwait(false);
+ catch (Exception ex)
+ {
+ await this.InternalStopAsync(false).ConfigureAwait(false);
- var message = $"Shard initialization failed, check inner exceptions for details: ";
+ var message = $"Shard initialization failed, check inner exceptions for details: ";
- this.Logger.LogCritical(LoggerEvents.ShardClientError, $"{message}\n{ex}");
- throw new AggregateException(message, ex);
+ this.Logger.LogCritical(LoggerEvents.ShardClientError, $"{message}\n{ex}");
+ throw new AggregateException(message, ex);
+ }
}
- }
-
- /// <summary>
- /// Disconnects and disposes all shards.
- /// </summary>
- /// <exception cref="System.InvalidOperationException"></exception>
- public Task StopAsync()
- => this.InternalStopAsync();
- /// <summary>
- /// Gets a shard from a guild id.
- /// <para>
- /// If automatically sharding, this will use the <see cref="Utilities.GetShardId(ulong, int)"/> method.
- /// Otherwise if manually sharding, it will instead iterate through each shard's guild caches.
- /// </para>
- /// </summary>
- /// <param name="guildId">The guild ID for the shard.</param>
- /// <returns>The found <see cref="DiscordClient"/> shard. Otherwise null if the shard was not found for the guild id.</returns>
- public DiscordClient GetShard(ulong guildId)
- {
- var index = this._manuallySharding ? this.GetShardIdFromGuilds(guildId) : Utilities.GetShardId(guildId, this.ShardClients.Count);
-
- return index != -1 ? this._shards[index] : null;
- }
-
- /// <summary>
- /// Gets a shard from a guild.
- /// <para>
- /// If automatically sharding, this will use the <see cref="Utilities.GetShardId(ulong, int)"/> method.
- /// Otherwise if manually sharding, it will instead iterate through each shard's guild caches.
- /// </para>
- /// </summary>
- /// <param name="guild">The guild for the shard.</param>
- /// <returns>The found <see cref="DiscordClient"/> shard. Otherwise null if the shard was not found for the guild.</returns>
- public DiscordClient GetShard(DiscordGuild guild)
- => this.GetShard(guild.Id);
+ /// <summary>
+ /// Disconnects and disposes all shards.
+ /// </summary>
+ /// <exception cref="System.InvalidOperationException"></exception>
+ public Task StopAsync()
+ => this.InternalStopAsync();
+
+ /// <summary>
+ /// Gets a shard from a guild id.
+ /// <para>
+ /// If automatically sharding, this will use the <see cref="Utilities.GetShardId(ulong, int)"/> method.
+ /// Otherwise if manually sharding, it will instead iterate through each shard's guild caches.
+ /// </para>
+ /// </summary>
+ /// <param name="guildId">The guild ID for the shard.</param>
+ /// <returns>The found <see cref="DiscordClient"/> shard. Otherwise null if the shard was not found for the guild id.</returns>
+ public DiscordClient GetShard(ulong guildId)
+ {
+ var index = this._manuallySharding ? this.GetShardIdFromGuilds(guildId) : Utilities.GetShardId(guildId, this.ShardClients.Count);
- /// <summary>
- /// Updates the status on all shards.
- /// </summary>
- /// <param name="activity">The activity to set. Defaults to null.</param>
- /// <param name="userStatus">The optional status to set. Defaults to null.</param>
- /// <param name="idleSince">Since when is the client performing the specified activity. Defaults to null.</param>
- /// <returns>Asynchronous operation.</returns>
- public async Task UpdateStatusAsync(DiscordActivity activity = null, UserStatus? userStatus = null, DateTimeOffset? idleSince = null)
- {
- var tasks = new List<Task>();
- foreach (var client in this._shards.Values)
- tasks.Add(client.UpdateStatusAsync(activity, userStatus, idleSince));
+ return index != -1 ? this._shards[index] : null;
+ }
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Gets a shard from a guild.
+ /// <para>
+ /// If automatically sharding, this will use the <see cref="Utilities.GetShardId(ulong, int)"/> method.
+ /// Otherwise if manually sharding, it will instead iterate through each shard's guild caches.
+ /// </para>
+ /// </summary>
+ /// <param name="guild">The guild for the shard.</param>
+ /// <returns>The found <see cref="DiscordClient"/> shard. Otherwise null if the shard was not found for the guild.</returns>
+ public DiscordClient GetShard(DiscordGuild guild)
+ => this.GetShard(guild.Id);
+
+ /// <summary>
+ /// Updates the status on all shards.
+ /// </summary>
+ /// <param name="activity">The activity to set. Defaults to null.</param>
+ /// <param name="userStatus">The optional status to set. Defaults to null.</param>
+ /// <param name="idleSince">Since when is the client performing the specified activity. Defaults to null.</param>
+ /// <returns>Asynchronous operation.</returns>
+ public async Task UpdateStatusAsync(DiscordActivity activity = null, UserStatus? userStatus = null, DateTimeOffset? idleSince = null)
+ {
+ var tasks = new List<Task>();
+ foreach (var client in this._shards.Values)
+ tasks.Add(client.UpdateStatusAsync(activity, userStatus, idleSince));
- /// <summary>
- /// <see cref="BaseDiscordClient.GetLibraryDevelopmentTeamAsync"/>
- /// </summary>
- public async Task<DisCatSharpTeam> GetLibraryDevelopmentTeamAsync()
- => await this.GetShard(0).GetLibraryDevelopmentTeamAsync().ConfigureAwait(false);
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+ }
- #endregion
+ /// <summary>
+ /// <see cref="BaseDiscordClient.GetLibraryDevelopmentTeamAsync"/>
+ /// </summary>
+ public async Task<DisCatSharpTeam> GetLibraryDevelopmentTeamAsync()
+ => await this.GetShard(0).GetLibraryDevelopmentTeamAsync().ConfigureAwait(false);
- #region Internal Methods
+ #endregion
- /// <summary>
- /// Initializes the shards.
- /// </summary>
- /// <returns>The count of initialized shards.</returns>
- internal async Task<int> InitializeShardsAsync()
- {
- if (this._shards.Count != 0)
- return this._shards.Count;
+ #region Internal Methods
- this.GatewayInfo = await this.GetGatewayInfoAsync().ConfigureAwait(false);
- var shardCount = this._configuration.ShardCount == 1 ? this.GatewayInfo.ShardCount : this._configuration.ShardCount;
- var lf = new ShardedLoggerFactory(this.Logger);
- for (var i = 0; i < shardCount; i++)
+ /// <summary>
+ /// Initializes the shards.
+ /// </summary>
+ /// <returns>The count of initialized shards.</returns>
+ internal async Task<int> InitializeShardsAsync()
{
- var cfg = new DiscordConfiguration(this._configuration)
- {
- ShardId = i,
- ShardCount = shardCount,
- LoggerFactory = lf
- };
-
- var client = new DiscordClient(cfg);
- if (!this._shards.TryAdd(i, client))
- throw new InvalidOperationException("Could not initialize shards.");
- }
+ if (this._shards.Count != 0)
+ return this._shards.Count;
- return shardCount;
- }
+ this.GatewayInfo = await this.GetGatewayInfoAsync().ConfigureAwait(false);
+ var shardCount = this._configuration.ShardCount == 1 ? this.GatewayInfo.ShardCount : this._configuration.ShardCount;
+ var lf = new ShardedLoggerFactory(this.Logger);
+ for (var i = 0; i < shardCount; i++)
+ {
+ var cfg = new DiscordConfiguration(this._configuration)
+ {
+ ShardId = i,
+ ShardCount = shardCount,
+ LoggerFactory = lf
+ };
+
+ var client = new DiscordClient(cfg);
+ if (!this._shards.TryAdd(i, client))
+ throw new InvalidOperationException("Could not initialize shards.");
+ }
- #endregion
+ return shardCount;
+ }
- #region Private Methods & Version Property
+ #endregion
- /// <summary>
- /// Gets the gateway info.
- /// </summary>
- private async Task<GatewayInfo> GetGatewayInfoAsync()
- {
- var url = $"{Utilities.GetApiBaseUri(this._configuration)}{Endpoints.GATEWAY}{Endpoints.BOT}";
- var http = new HttpClient();
+ #region Private Methods & Version Property
- http.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Utilities.GetUserAgent());
- http.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", Utilities.GetFormattedToken(this._configuration));
- if (this._configuration != null && this._configuration.Override != null)
+ /// <summary>
+ /// Gets the gateway info.
+ /// </summary>
+ private async Task<GatewayInfo> GetGatewayInfoAsync()
{
- http.DefaultRequestHeaders.TryAddWithoutValidation("x-super-properties", this._configuration.Override);
- }
+ var url = $"{Utilities.GetApiBaseUri(this._configuration)}{Endpoints.GATEWAY}{Endpoints.BOT}";
+ var http = new HttpClient();
- this.Logger.LogDebug(LoggerEvents.ShardRest, $"Obtaining gateway information from GET {Endpoints.GATEWAY}{Endpoints.BOT}...");
- var resp = await http.GetAsync(url).ConfigureAwait(false);
+ http.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Utilities.GetUserAgent());
+ http.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", Utilities.GetFormattedToken(this._configuration));
+ if (this._configuration != null && this._configuration.Override != null)
+ {
+ http.DefaultRequestHeaders.TryAddWithoutValidation("x-super-properties", this._configuration.Override);
+ }
- http.Dispose();
+ this.Logger.LogDebug(LoggerEvents.ShardRest, $"Obtaining gateway information from GET {Endpoints.GATEWAY}{Endpoints.BOT}...");
+ var resp = await http.GetAsync(url).ConfigureAwait(false);
- if (!resp.IsSuccessStatusCode)
- {
- var ratelimited = await HandleHttpError(url, resp).ConfigureAwait(false);
+ http.Dispose();
- if (ratelimited)
- return await this.GetGatewayInfoAsync().ConfigureAwait(false);
- }
+ if (!resp.IsSuccessStatusCode)
+ {
+ var ratelimited = await HandleHttpError(url, resp).ConfigureAwait(false);
- var timer = new Stopwatch();
- timer.Start();
+ if (ratelimited)
+ return await this.GetGatewayInfoAsync().ConfigureAwait(false);
+ }
- var jo = JObject.Parse(await resp.Content.ReadAsStringAsync().ConfigureAwait(false));
- var info = jo.ToObject<GatewayInfo>();
+ var timer = new Stopwatch();
+ timer.Start();
- //There is a delay from parsing here.
- timer.Stop();
+ var jo = JObject.Parse(await resp.Content.ReadAsStringAsync().ConfigureAwait(false));
+ var info = jo.ToObject<GatewayInfo>();
- info.SessionBucket.ResetAfterInternal -= (int)timer.ElapsedMilliseconds;
- info.SessionBucket.ResetAfter = DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(info.SessionBucket.ResetAfterInternal);
+ //There is a delay from parsing here.
+ timer.Stop();
- return info;
+ info.SessionBucket.ResetAfterInternal -= (int)timer.ElapsedMilliseconds;
+ info.SessionBucket.ResetAfter = DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(info.SessionBucket.ResetAfterInternal);
- async Task<bool> HandleHttpError(string reqUrl, HttpResponseMessage msg)
- {
- var code = (int)msg.StatusCode;
+ return info;
- if (code == 401 || code == 403)
+ async Task<bool> HandleHttpError(string reqUrl, HttpResponseMessage msg)
{
- throw new Exception($"Authentication failed, check your token and try again: {code} {msg.ReasonPhrase}");
- }
- else if (code == 429)
- {
- this.Logger.LogError(LoggerEvents.ShardClientError, $"Ratelimit hit, requeuing request to {reqUrl}");
+ var code = (int)msg.StatusCode;
- var hs = msg.Headers.ToDictionary(xh => xh.Key, xh => string.Join("\n", xh.Value), StringComparer.OrdinalIgnoreCase);
- var waitInterval = 0;
+ if (code == 401 || code == 403)
+ {
+ throw new Exception($"Authentication failed, check your token and try again: {code} {msg.ReasonPhrase}");
+ }
+ else if (code == 429)
+ {
+ this.Logger.LogError(LoggerEvents.ShardClientError, $"Ratelimit hit, requeuing request to {reqUrl}");
- if (hs.TryGetValue("Retry-After", out var retryAfterRaw))
- waitInterval = int.Parse(retryAfterRaw, CultureInfo.InvariantCulture);
+ var hs = msg.Headers.ToDictionary(xh => xh.Key, xh => string.Join("\n", xh.Value), StringComparer.OrdinalIgnoreCase);
+ var waitInterval = 0;
- await Task.Delay(waitInterval).ConfigureAwait(false);
- return true;
- }
- else if (code >= 500)
- {
- throw new Exception($"Internal Server Error: {code} {msg.ReasonPhrase}");
- }
- else
- {
- throw new Exception($"An unsuccessful HTTP status code was encountered: {code} {msg.ReasonPhrase}");
+ if (hs.TryGetValue("Retry-After", out var retryAfterRaw))
+ waitInterval = int.Parse(retryAfterRaw, CultureInfo.InvariantCulture);
+
+ await Task.Delay(waitInterval).ConfigureAwait(false);
+ return true;
+ }
+ else if (code >= 500)
+ {
+ throw new Exception($"Internal Server Error: {code} {msg.ReasonPhrase}");
+ }
+ else
+ {
+ throw new Exception($"An unsuccessful HTTP status code was encountered: {code} {msg.ReasonPhrase}");
+ }
}
}
- }
- /// <summary>
- /// Gets the version string.
- /// </summary>
- private readonly Lazy<string> _versionString = new(() =>
- {
- var a = typeof(DiscordShardedClient).GetTypeInfo().Assembly;
-
- var iv = a.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
- if (iv != null)
- return iv.InformationalVersion;
+ /// <summary>
+ /// Gets the version string.
+ /// </summary>
+ private readonly Lazy<string> _versionString = new(() =>
+ {
+ var a = typeof(DiscordShardedClient).GetTypeInfo().Assembly;
- var v = a.GetName().Version;
- var vs = v.ToString(3);
+ var iv = a.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
+ if (iv != null)
+ return iv.InformationalVersion;
- if (v.Revision > 0)
- vs = $"{vs}, CI build {v.Revision}";
+ var v = a.GetName().Version;
+ var vs = v.ToString(3);
- return vs;
- });
+ if (v.Revision > 0)
+ vs = $"{vs}, CI build {v.Revision}";
- /// <summary>
- /// Gets the name of the used bot library.
- /// </summary>
- private readonly string _botLibrary = "DisCatSharp";
+ return vs;
+ });
- #endregion
+ /// <summary>
+ /// Gets the name of the used bot library.
+ /// </summary>
+ private readonly string _botLibrary = "DisCatSharp";
- #region Private Connection Methods
+ #endregion
- /// <summary>
- /// Connects a shard.
- /// </summary>
- /// <param name="i">The shard id.</param>
- private async Task ConnectShardAsync(int i)
- {
- if (!this._shards.TryGetValue(i, out var client))
- throw new Exception($"Could not initialize shard {i}.");
+ #region Private Connection Methods
- if (this.GatewayInfo != null)
+ /// <summary>
+ /// Connects a shard.
+ /// </summary>
+ /// <param name="i">The shard id.</param>
+ private async Task ConnectShardAsync(int i)
{
- client.GatewayInfo = this.GatewayInfo;
- client.GatewayUri = new Uri(client.GatewayInfo.Url);
- }
+ if (!this._shards.TryGetValue(i, out var client))
+ throw new Exception($"Could not initialize shard {i}.");
- if (this.CurrentUser != null)
- client.CurrentUser = this.CurrentUser;
+ if (this.GatewayInfo != null)
+ {
+ client.GatewayInfo = this.GatewayInfo;
+ client.GatewayUri = new Uri(client.GatewayInfo.Url);
+ }
- if (this.CurrentApplication != null)
- client.CurrentApplication = this.CurrentApplication;
+ if (this.CurrentUser != null)
+ client.CurrentUser = this.CurrentUser;
- if (this._internalVoiceRegions != null)
- {
- client.InternalVoiceRegions = this._internalVoiceRegions;
- client.VoiceRegionsLazy = new Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>>(() => new ReadOnlyDictionary<string, DiscordVoiceRegion>(client.InternalVoiceRegions));
- }
+ if (this.CurrentApplication != null)
+ client.CurrentApplication = this.CurrentApplication;
+
+ if (this._internalVoiceRegions != null)
+ {
+ client.InternalVoiceRegions = this._internalVoiceRegions;
+ client.VoiceRegionsLazy = new Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>>(() => new ReadOnlyDictionary<string, DiscordVoiceRegion>(client.InternalVoiceRegions));
+ }
- this.HookEventHandlers(client);
+ this.HookEventHandlers(client);
- client.IsShard = true;
- await client.ConnectAsync().ConfigureAwait(false);
- this.Logger.LogInformation(LoggerEvents.ShardStartup, "Booted shard {0}.", i);
+ client.IsShard = true;
+ await client.ConnectAsync().ConfigureAwait(false);
+ this.Logger.LogInformation(LoggerEvents.ShardStartup, "Booted shard {0}.", i);
- if (this.CurrentUser == null)
- this.CurrentUser = client.CurrentUser;
+ if (this.CurrentUser == null)
+ this.CurrentUser = client.CurrentUser;
- if (this.CurrentApplication == null)
- this.CurrentApplication = client.CurrentApplication;
+ if (this.CurrentApplication == null)
+ this.CurrentApplication = client.CurrentApplication;
- if (this._internalVoiceRegions == null)
- {
- this._internalVoiceRegions = client.InternalVoiceRegions;
- this._voiceRegionsLazy = new Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>>(() => new ReadOnlyDictionary<string, DiscordVoiceRegion>(this._internalVoiceRegions));
+ if (this._internalVoiceRegions == null)
+ {
+ this._internalVoiceRegions = client.InternalVoiceRegions;
+ this._voiceRegionsLazy = new Lazy<IReadOnlyDictionary<string, DiscordVoiceRegion>>(() => new ReadOnlyDictionary<string, DiscordVoiceRegion>(this._internalVoiceRegions));
+ }
}
- }
- /// <summary>
- /// Stops all shards.
- /// </summary>
- /// <param name="enableLogger">Whether to enable the logger.</param>
- private Task InternalStopAsync(bool enableLogger = true)
- {
- if (!this._isStarted)
- throw new InvalidOperationException("This client has not been started.");
+ /// <summary>
+ /// Stops all shards.
+ /// </summary>
+ /// <param name="enableLogger">Whether to enable the logger.</param>
+ private Task InternalStopAsync(bool enableLogger = true)
+ {
+ if (!this._isStarted)
+ throw new InvalidOperationException("This client has not been started.");
- if (enableLogger)
- this.Logger.LogInformation(LoggerEvents.ShardShutdown, "Disposing {0} shards.", this._shards.Count);
+ if (enableLogger)
+ this.Logger.LogInformation(LoggerEvents.ShardShutdown, "Disposing {0} shards.", this._shards.Count);
- this._isStarted = false;
- this._voiceRegionsLazy = null;
+ this._isStarted = false;
+ this._voiceRegionsLazy = null;
- this.GatewayInfo = null;
- this.CurrentUser = null;
- this.CurrentApplication = null;
+ this.GatewayInfo = null;
+ this.CurrentUser = null;
+ this.CurrentApplication = null;
- for (var i = 0; i < this._shards.Count; i++)
- {
- if (this._shards.TryGetValue(i, out var client))
+ for (var i = 0; i < this._shards.Count; i++)
{
- this.UnhookEventHandlers(client);
+ if (this._shards.TryGetValue(i, out var client))
+ {
+ this.UnhookEventHandlers(client);
- client.Dispose();
+ client.Dispose();
- if (enableLogger)
- this.Logger.LogInformation(LoggerEvents.ShardShutdown, "Disconnected shard {0}.", i);
+ if (enableLogger)
+ this.Logger.LogInformation(LoggerEvents.ShardShutdown, "Disconnected shard {0}.", i);
+ }
}
- }
- this._shards.Clear();
+ this._shards.Clear();
- return Task.CompletedTask;
- }
+ return Task.CompletedTask;
+ }
- #endregion
+ #endregion
- #region Event Handler Initialization/Registering
+ #region Event Handler Initialization/Registering
- /// <summary>
- /// Sets the shard client up internally..
- /// </summary>
- private void InternalSetup()
- {
- this._clientErrored = new AsyncEvent<DiscordClient, ClientErrorEventArgs>("CLIENT_ERRORED", DiscordClient.EventExecutionLimit, this.Goof);
- this._socketErrored = new AsyncEvent<DiscordClient, SocketErrorEventArgs>("SOCKET_ERRORED", DiscordClient.EventExecutionLimit, this.Goof);
- this._socketOpened = new AsyncEvent<DiscordClient, SocketEventArgs>("SOCKET_OPENED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._socketClosed = new AsyncEvent<DiscordClient, SocketCloseEventArgs>("SOCKET_CLOSED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._ready = new AsyncEvent<DiscordClient, ReadyEventArgs>("READY", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._resumed = new AsyncEvent<DiscordClient, ReadyEventArgs>("RESUMED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._channelCreated = new AsyncEvent<DiscordClient, ChannelCreateEventArgs>("CHANNEL_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._channelUpdated = new AsyncEvent<DiscordClient, ChannelUpdateEventArgs>("CHANNEL_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._channelDeleted = new AsyncEvent<DiscordClient, ChannelDeleteEventArgs>("CHANNEL_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._dmChannelDeleted = new AsyncEvent<DiscordClient, DmChannelDeleteEventArgs>("DM_CHANNEL_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._channelPinsUpdated = new AsyncEvent<DiscordClient, ChannelPinsUpdateEventArgs>("CHANNEL_PINS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildCreated = new AsyncEvent<DiscordClient, GuildCreateEventArgs>("GUILD_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildAvailable = new AsyncEvent<DiscordClient, GuildCreateEventArgs>("GUILD_AVAILABLE", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildUpdated = new AsyncEvent<DiscordClient, GuildUpdateEventArgs>("GUILD_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildDeleted = new AsyncEvent<DiscordClient, GuildDeleteEventArgs>("GUILD_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildUnavailable = new AsyncEvent<DiscordClient, GuildDeleteEventArgs>("GUILD_UNAVAILABLE", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildDownloadCompleted = new AsyncEvent<DiscordClient, GuildDownloadCompletedEventArgs>("GUILD_DOWNLOAD_COMPLETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._inviteCreated = new AsyncEvent<DiscordClient, InviteCreateEventArgs>("INVITE_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._inviteDeleted = new AsyncEvent<DiscordClient, InviteDeleteEventArgs>("INVITE_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._messageCreated = new AsyncEvent<DiscordClient, MessageCreateEventArgs>("MESSAGE_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._presenceUpdated = new AsyncEvent<DiscordClient, PresenceUpdateEventArgs>("PRESENCE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildBanAdded = new AsyncEvent<DiscordClient, GuildBanAddEventArgs>("GUILD_BAN_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildBanRemoved = new AsyncEvent<DiscordClient, GuildBanRemoveEventArgs>("GUILD_BAN_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildEmojisUpdated = new AsyncEvent<DiscordClient, GuildEmojisUpdateEventArgs>("GUILD_EMOJI_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildStickersUpdated = new AsyncEvent<DiscordClient, GuildStickersUpdateEventArgs>("GUILD_STICKER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildIntegrationsUpdated = new AsyncEvent<DiscordClient, GuildIntegrationsUpdateEventArgs>("GUILD_INTEGRATIONS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberAdded = new AsyncEvent<DiscordClient, GuildMemberAddEventArgs>("GUILD_MEMBER_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberRemoved = new AsyncEvent<DiscordClient, GuildMemberRemoveEventArgs>("GUILD_MEMBER_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberUpdated = new AsyncEvent<DiscordClient, GuildMemberUpdateEventArgs>("GUILD_MEMBER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildRoleCreated = new AsyncEvent<DiscordClient, GuildRoleCreateEventArgs>("GUILD_ROLE_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildRoleUpdated = new AsyncEvent<DiscordClient, GuildRoleUpdateEventArgs>("GUILD_ROLE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildRoleDeleted = new AsyncEvent<DiscordClient, GuildRoleDeleteEventArgs>("GUILD_ROLE_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._messageUpdated = new AsyncEvent<DiscordClient, MessageUpdateEventArgs>("MESSAGE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._messageDeleted = new AsyncEvent<DiscordClient, MessageDeleteEventArgs>("MESSAGE_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._messageBulkDeleted = new AsyncEvent<DiscordClient, MessageBulkDeleteEventArgs>("MESSAGE_BULK_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._interactionCreated = new AsyncEvent<DiscordClient, InteractionCreateEventArgs>("INTERACTION_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._componentInteractionCreated = new AsyncEvent<DiscordClient, ComponentInteractionCreateEventArgs>("COMPONENT_INTERACTED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._contextMenuInteractionCreated = new AsyncEvent<DiscordClient, ContextMenuInteractionCreateEventArgs>("CONTEXT_MENU_INTERACTED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._typingStarted = new AsyncEvent<DiscordClient, TypingStartEventArgs>("TYPING_STARTED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._userSettingsUpdated = new AsyncEvent<DiscordClient, UserSettingsUpdateEventArgs>("USER_SETTINGS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._userUpdated = new AsyncEvent<DiscordClient, UserUpdateEventArgs>("USER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._voiceStateUpdated = new AsyncEvent<DiscordClient, VoiceStateUpdateEventArgs>("VOICE_STATE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._voiceServerUpdated = new AsyncEvent<DiscordClient, VoiceServerUpdateEventArgs>("VOICE_SERVER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildMembersChunk = new AsyncEvent<DiscordClient, GuildMembersChunkEventArgs>("GUILD_MEMBERS_CHUNKED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._unknownEvent = new AsyncEvent<DiscordClient, UnknownEventArgs>("UNKNOWN_EVENT", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._messageReactionAdded = new AsyncEvent<DiscordClient, MessageReactionAddEventArgs>("MESSAGE_REACTION_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._messageReactionRemoved = new AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs>("MESSAGE_REACTION_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._messageReactionsCleared = new AsyncEvent<DiscordClient, MessageReactionsClearEventArgs>("MESSAGE_REACTIONS_CLEARED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._messageReactionRemovedEmoji = new AsyncEvent<DiscordClient, MessageReactionRemoveEmojiEventArgs>("MESSAGE_REACTION_REMOVED_EMOJI", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._webhooksUpdated = new AsyncEvent<DiscordClient, WebhooksUpdateEventArgs>("WEBHOOKS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._heartbeated = new AsyncEvent<DiscordClient, HeartbeatEventArgs>("HEARTBEATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._applicationCommandCreated = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._applicationCommandUpdated = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._applicationCommandDeleted = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildApplicationCommandCountUpdated = new AsyncEvent<DiscordClient, GuildApplicationCommandCountEventArgs>("GUILD_APPLICATION_COMMAND_COUNTS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._applicationCommandPermissionsUpdated = new AsyncEvent<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs>("APPLICATION_COMMAND_PERMISSIONS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildIntegrationCreated = new AsyncEvent<DiscordClient, GuildIntegrationCreateEventArgs>("INTEGRATION_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildIntegrationUpdated = new AsyncEvent<DiscordClient, GuildIntegrationUpdateEventArgs>("INTEGRATION_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildIntegrationDeleted = new AsyncEvent<DiscordClient, GuildIntegrationDeleteEventArgs>("INTEGRATION_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._stageInstanceCreated = new AsyncEvent<DiscordClient, StageInstanceCreateEventArgs>("STAGE_INSTANCE_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._stageInstanceUpdated = new AsyncEvent<DiscordClient, StageInstanceUpdateEventArgs>("STAGE_INSTANCE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._stageInstanceDeleted = new AsyncEvent<DiscordClient, StageInstanceDeleteEventArgs>("STAGE_INSTANCE_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._threadCreated = new AsyncEvent<DiscordClient, ThreadCreateEventArgs>("THREAD_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._threadUpdated = new AsyncEvent<DiscordClient, ThreadUpdateEventArgs>("THREAD_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._threadDeleted = new AsyncEvent<DiscordClient, ThreadDeleteEventArgs>("THREAD_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._threadListSynced = new AsyncEvent<DiscordClient, ThreadListSyncEventArgs>("THREAD_LIST_SYNCED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._threadMemberUpdated = new AsyncEvent<DiscordClient, ThreadMemberUpdateEventArgs>("THREAD_MEMBER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._threadMembersUpdated = new AsyncEvent<DiscordClient, ThreadMembersUpdateEventArgs>("THREAD_MEMBERS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._zombied = new AsyncEvent<DiscordClient, ZombiedEventArgs>("ZOMBIED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._payloadReceived = new AsyncEvent<DiscordClient, PayloadReceivedEventArgs>("PAYLOAD_RECEIVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventCreated = new AsyncEvent<DiscordClient, GuildScheduledEventCreateEventArgs>("GUILD_SCHEDULED_EVENT_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventUpdated = new AsyncEvent<DiscordClient, GuildScheduledEventUpdateEventArgs>("GUILD_SCHEDULED_EVENT_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventDeleted = new AsyncEvent<DiscordClient, GuildScheduledEventDeleteEventArgs>("GUILD_SCHEDULED_EVENT_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventUserAdded = new AsyncEvent<DiscordClient, GuildScheduledEventUserAddEventArgs>("GUILD_SCHEDULED_EVENT_USER_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildScheduledEventUserRemoved = new AsyncEvent<DiscordClient, GuildScheduledEventUserRemoveEventArgs>("GUILD_SCHEDULED_EVENT_USER_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._embeddedActivityUpdated = new AsyncEvent<DiscordClient, EmbeddedActivityUpdateEventArgs>("EMBEDDED_ACTIVITY_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberTimeoutAdded = new AsyncEvent<DiscordClient, GuildMemberTimeoutAddEventArgs>("GUILD_MEMBER_TIMEOUT_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberTimeoutChanged = new AsyncEvent<DiscordClient, GuildMemberTimeoutUpdateEventArgs>("GUILD_MEMBER_TIMEOUT_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- this._guildMemberTimeoutRemoved = new AsyncEvent<DiscordClient, GuildMemberTimeoutRemoveEventArgs>("GUILD_MEMBER_TIMEOUT_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
- }
+ /// <summary>
+ /// Sets the shard client up internally..
+ /// </summary>
+ private void InternalSetup()
+ {
+ this._clientErrored = new AsyncEvent<DiscordClient, ClientErrorEventArgs>("CLIENT_ERRORED", DiscordClient.EventExecutionLimit, this.Goof);
+ this._socketErrored = new AsyncEvent<DiscordClient, SocketErrorEventArgs>("SOCKET_ERRORED", DiscordClient.EventExecutionLimit, this.Goof);
+ this._socketOpened = new AsyncEvent<DiscordClient, SocketEventArgs>("SOCKET_OPENED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._socketClosed = new AsyncEvent<DiscordClient, SocketCloseEventArgs>("SOCKET_CLOSED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._ready = new AsyncEvent<DiscordClient, ReadyEventArgs>("READY", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._resumed = new AsyncEvent<DiscordClient, ReadyEventArgs>("RESUMED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._channelCreated = new AsyncEvent<DiscordClient, ChannelCreateEventArgs>("CHANNEL_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._channelUpdated = new AsyncEvent<DiscordClient, ChannelUpdateEventArgs>("CHANNEL_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._channelDeleted = new AsyncEvent<DiscordClient, ChannelDeleteEventArgs>("CHANNEL_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._dmChannelDeleted = new AsyncEvent<DiscordClient, DmChannelDeleteEventArgs>("DM_CHANNEL_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._channelPinsUpdated = new AsyncEvent<DiscordClient, ChannelPinsUpdateEventArgs>("CHANNEL_PINS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildCreated = new AsyncEvent<DiscordClient, GuildCreateEventArgs>("GUILD_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildAvailable = new AsyncEvent<DiscordClient, GuildCreateEventArgs>("GUILD_AVAILABLE", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildUpdated = new AsyncEvent<DiscordClient, GuildUpdateEventArgs>("GUILD_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildDeleted = new AsyncEvent<DiscordClient, GuildDeleteEventArgs>("GUILD_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildUnavailable = new AsyncEvent<DiscordClient, GuildDeleteEventArgs>("GUILD_UNAVAILABLE", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildDownloadCompleted = new AsyncEvent<DiscordClient, GuildDownloadCompletedEventArgs>("GUILD_DOWNLOAD_COMPLETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._inviteCreated = new AsyncEvent<DiscordClient, InviteCreateEventArgs>("INVITE_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._inviteDeleted = new AsyncEvent<DiscordClient, InviteDeleteEventArgs>("INVITE_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._messageCreated = new AsyncEvent<DiscordClient, MessageCreateEventArgs>("MESSAGE_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._presenceUpdated = new AsyncEvent<DiscordClient, PresenceUpdateEventArgs>("PRESENCE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildBanAdded = new AsyncEvent<DiscordClient, GuildBanAddEventArgs>("GUILD_BAN_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildBanRemoved = new AsyncEvent<DiscordClient, GuildBanRemoveEventArgs>("GUILD_BAN_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildEmojisUpdated = new AsyncEvent<DiscordClient, GuildEmojisUpdateEventArgs>("GUILD_EMOJI_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildStickersUpdated = new AsyncEvent<DiscordClient, GuildStickersUpdateEventArgs>("GUILD_STICKER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildIntegrationsUpdated = new AsyncEvent<DiscordClient, GuildIntegrationsUpdateEventArgs>("GUILD_INTEGRATIONS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberAdded = new AsyncEvent<DiscordClient, GuildMemberAddEventArgs>("GUILD_MEMBER_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberRemoved = new AsyncEvent<DiscordClient, GuildMemberRemoveEventArgs>("GUILD_MEMBER_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberUpdated = new AsyncEvent<DiscordClient, GuildMemberUpdateEventArgs>("GUILD_MEMBER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildRoleCreated = new AsyncEvent<DiscordClient, GuildRoleCreateEventArgs>("GUILD_ROLE_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildRoleUpdated = new AsyncEvent<DiscordClient, GuildRoleUpdateEventArgs>("GUILD_ROLE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildRoleDeleted = new AsyncEvent<DiscordClient, GuildRoleDeleteEventArgs>("GUILD_ROLE_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._messageUpdated = new AsyncEvent<DiscordClient, MessageUpdateEventArgs>("MESSAGE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._messageDeleted = new AsyncEvent<DiscordClient, MessageDeleteEventArgs>("MESSAGE_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._messageBulkDeleted = new AsyncEvent<DiscordClient, MessageBulkDeleteEventArgs>("MESSAGE_BULK_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._interactionCreated = new AsyncEvent<DiscordClient, InteractionCreateEventArgs>("INTERACTION_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._componentInteractionCreated = new AsyncEvent<DiscordClient, ComponentInteractionCreateEventArgs>("COMPONENT_INTERACTED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._contextMenuInteractionCreated = new AsyncEvent<DiscordClient, ContextMenuInteractionCreateEventArgs>("CONTEXT_MENU_INTERACTED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._typingStarted = new AsyncEvent<DiscordClient, TypingStartEventArgs>("TYPING_STARTED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._userSettingsUpdated = new AsyncEvent<DiscordClient, UserSettingsUpdateEventArgs>("USER_SETTINGS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._userUpdated = new AsyncEvent<DiscordClient, UserUpdateEventArgs>("USER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._voiceStateUpdated = new AsyncEvent<DiscordClient, VoiceStateUpdateEventArgs>("VOICE_STATE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._voiceServerUpdated = new AsyncEvent<DiscordClient, VoiceServerUpdateEventArgs>("VOICE_SERVER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildMembersChunk = new AsyncEvent<DiscordClient, GuildMembersChunkEventArgs>("GUILD_MEMBERS_CHUNKED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._unknownEvent = new AsyncEvent<DiscordClient, UnknownEventArgs>("UNKNOWN_EVENT", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._messageReactionAdded = new AsyncEvent<DiscordClient, MessageReactionAddEventArgs>("MESSAGE_REACTION_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._messageReactionRemoved = new AsyncEvent<DiscordClient, MessageReactionRemoveEventArgs>("MESSAGE_REACTION_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._messageReactionsCleared = new AsyncEvent<DiscordClient, MessageReactionsClearEventArgs>("MESSAGE_REACTIONS_CLEARED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._messageReactionRemovedEmoji = new AsyncEvent<DiscordClient, MessageReactionRemoveEmojiEventArgs>("MESSAGE_REACTION_REMOVED_EMOJI", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._webhooksUpdated = new AsyncEvent<DiscordClient, WebhooksUpdateEventArgs>("WEBHOOKS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._heartbeated = new AsyncEvent<DiscordClient, HeartbeatEventArgs>("HEARTBEATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._applicationCommandCreated = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._applicationCommandUpdated = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._applicationCommandDeleted = new AsyncEvent<DiscordClient, ApplicationCommandEventArgs>("APPLICATION_COMMAND_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildApplicationCommandCountUpdated = new AsyncEvent<DiscordClient, GuildApplicationCommandCountEventArgs>("GUILD_APPLICATION_COMMAND_COUNTS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._applicationCommandPermissionsUpdated = new AsyncEvent<DiscordClient, ApplicationCommandPermissionsUpdateEventArgs>("APPLICATION_COMMAND_PERMISSIONS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildIntegrationCreated = new AsyncEvent<DiscordClient, GuildIntegrationCreateEventArgs>("INTEGRATION_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildIntegrationUpdated = new AsyncEvent<DiscordClient, GuildIntegrationUpdateEventArgs>("INTEGRATION_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildIntegrationDeleted = new AsyncEvent<DiscordClient, GuildIntegrationDeleteEventArgs>("INTEGRATION_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._stageInstanceCreated = new AsyncEvent<DiscordClient, StageInstanceCreateEventArgs>("STAGE_INSTANCE_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._stageInstanceUpdated = new AsyncEvent<DiscordClient, StageInstanceUpdateEventArgs>("STAGE_INSTANCE_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._stageInstanceDeleted = new AsyncEvent<DiscordClient, StageInstanceDeleteEventArgs>("STAGE_INSTANCE_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._threadCreated = new AsyncEvent<DiscordClient, ThreadCreateEventArgs>("THREAD_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._threadUpdated = new AsyncEvent<DiscordClient, ThreadUpdateEventArgs>("THREAD_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._threadDeleted = new AsyncEvent<DiscordClient, ThreadDeleteEventArgs>("THREAD_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._threadListSynced = new AsyncEvent<DiscordClient, ThreadListSyncEventArgs>("THREAD_LIST_SYNCED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._threadMemberUpdated = new AsyncEvent<DiscordClient, ThreadMemberUpdateEventArgs>("THREAD_MEMBER_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._threadMembersUpdated = new AsyncEvent<DiscordClient, ThreadMembersUpdateEventArgs>("THREAD_MEMBERS_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._zombied = new AsyncEvent<DiscordClient, ZombiedEventArgs>("ZOMBIED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._payloadReceived = new AsyncEvent<DiscordClient, PayloadReceivedEventArgs>("PAYLOAD_RECEIVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventCreated = new AsyncEvent<DiscordClient, GuildScheduledEventCreateEventArgs>("GUILD_SCHEDULED_EVENT_CREATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventUpdated = new AsyncEvent<DiscordClient, GuildScheduledEventUpdateEventArgs>("GUILD_SCHEDULED_EVENT_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventDeleted = new AsyncEvent<DiscordClient, GuildScheduledEventDeleteEventArgs>("GUILD_SCHEDULED_EVENT_DELETED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventUserAdded = new AsyncEvent<DiscordClient, GuildScheduledEventUserAddEventArgs>("GUILD_SCHEDULED_EVENT_USER_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildScheduledEventUserRemoved = new AsyncEvent<DiscordClient, GuildScheduledEventUserRemoveEventArgs>("GUILD_SCHEDULED_EVENT_USER_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._embeddedActivityUpdated = new AsyncEvent<DiscordClient, EmbeddedActivityUpdateEventArgs>("EMBEDDED_ACTIVITY_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberTimeoutAdded = new AsyncEvent<DiscordClient, GuildMemberTimeoutAddEventArgs>("GUILD_MEMBER_TIMEOUT_ADDED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberTimeoutChanged = new AsyncEvent<DiscordClient, GuildMemberTimeoutUpdateEventArgs>("GUILD_MEMBER_TIMEOUT_UPDATED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ this._guildMemberTimeoutRemoved = new AsyncEvent<DiscordClient, GuildMemberTimeoutRemoveEventArgs>("GUILD_MEMBER_TIMEOUT_REMOVED", DiscordClient.EventExecutionLimit, this.EventErrorHandler);
+ }
- /// <summary>
- /// Hooks the event handlers.
- /// </summary>
- /// <param name="client">The client.</param>
- private void HookEventHandlers(DiscordClient client)
- {
- client.ClientErrored += this.Client_ClientError;
- client.SocketErrored += this.Client_SocketError;
- client.SocketOpened += this.Client_SocketOpened;
- client.SocketClosed += this.Client_SocketClosed;
- client.Ready += this.Client_Ready;
- client.Resumed += this.Client_Resumed;
- client.ChannelCreated += this.Client_ChannelCreated;
- client.ChannelUpdated += this.Client_ChannelUpdated;
- client.ChannelDeleted += this.Client_ChannelDeleted;
- client.DmChannelDeleted += this.Client_DMChannelDeleted;
- client.ChannelPinsUpdated += this.Client_ChannelPinsUpdated;
- client.GuildCreated += this.Client_GuildCreated;
- client.GuildAvailable += this.Client_GuildAvailable;
- client.GuildUpdated += this.Client_GuildUpdated;
- client.GuildDeleted += this.Client_GuildDeleted;
- client.GuildUnavailable += this.Client_GuildUnavailable;
- client.GuildDownloadCompleted += this.Client_GuildDownloadCompleted;
- client.InviteCreated += this.Client_InviteCreated;
- client.InviteDeleted += this.Client_InviteDeleted;
- client.MessageCreated += this.Client_MessageCreated;
- client.PresenceUpdated += this.Client_PresenceUpdate;
- client.GuildBanAdded += this.Client_GuildBanAdd;
- client.GuildBanRemoved += this.Client_GuildBanRemove;
- client.GuildEmojisUpdated += this.Client_GuildEmojisUpdate;
- client.GuildStickersUpdated += this.Client_GuildStickersUpdate;
- client.GuildIntegrationsUpdated += this.Client_GuildIntegrationsUpdate;
- client.GuildMemberAdded += this.Client_GuildMemberAdd;
- client.GuildMemberRemoved += this.Client_GuildMemberRemove;
- client.GuildMemberUpdated += this.Client_GuildMemberUpdate;
- client.GuildRoleCreated += this.Client_GuildRoleCreate;
- client.GuildRoleUpdated += this.Client_GuildRoleUpdate;
- client.GuildRoleDeleted += this.Client_GuildRoleDelete;
- client.MessageUpdated += this.Client_MessageUpdate;
- client.MessageDeleted += this.Client_MessageDelete;
- client.MessagesBulkDeleted += this.Client_MessageBulkDelete;
- client.InteractionCreated += this.Client_InteractionCreate;
- client.ComponentInteractionCreated += this.Client_ComponentInteractionCreate;
- client.ContextMenuInteractionCreated += this.Client_ContextMenuInteractionCreate;
- client.TypingStarted += this.Client_TypingStart;
- client.UserSettingsUpdated += this.Client_UserSettingsUpdate;
- client.UserUpdated += this.Client_UserUpdate;
- client.VoiceStateUpdated += this.Client_VoiceStateUpdate;
- client.VoiceServerUpdated += this.Client_VoiceServerUpdate;
- client.GuildMembersChunked += this.Client_GuildMembersChunk;
- client.UnknownEvent += this.Client_UnknownEvent;
- client.MessageReactionAdded += this.Client_MessageReactionAdd;
- client.MessageReactionRemoved += this.Client_MessageReactionRemove;
- client.MessageReactionsCleared += this.Client_MessageReactionRemoveAll;
- client.MessageReactionRemovedEmoji += this.Client_MessageReactionRemovedEmoji;
- client.WebhooksUpdated += this.Client_WebhooksUpdate;
- client.Heartbeated += this.Client_HeartBeated;
- client.ApplicationCommandCreated += this.Client_ApplicationCommandCreated;
- client.ApplicationCommandUpdated += this.Client_ApplicationCommandUpdated;
- client.ApplicationCommandDeleted += this.Client_ApplicationCommandDeleted;
- client.GuildApplicationCommandCountUpdated += this.Client_GuildApplicationCommandCountUpdated;
- client.ApplicationCommandPermissionsUpdated += this.Client_ApplicationCommandPermissionsUpdated;
- client.GuildIntegrationCreated += this.Client_GuildIntegrationCreated;
- client.GuildIntegrationUpdated += this.Client_GuildIntegrationUpdated;
- client.GuildIntegrationDeleted += this.Client_GuildIntegrationDeleted;
- client.StageInstanceCreated += this.Client_StageInstanceCreated;
- client.StageInstanceUpdated += this.Client_StageInstanceUpdated;
- client.StageInstanceDeleted += this.Client_StageInstanceDeleted;
- client.ThreadCreated += this.Client_ThreadCreated;
- client.ThreadUpdated += this.Client_ThreadUpdated;
- client.ThreadDeleted += this.Client_ThreadDeleted;
- client.ThreadListSynced += this.Client_ThreadListSynced;
- client.ThreadMemberUpdated += this.Client_ThreadMemberUpdated;
- client.ThreadMembersUpdated += this.Client_ThreadMembersUpdated;
- client.Zombied += this.Client_Zombied;
- client.PayloadReceived += this.Client_PayloadReceived;
- client.GuildScheduledEventCreated += this.Client_GuildScheduledEventCreated;
- client.GuildScheduledEventUpdated += this.Client_GuildScheduledEventUpdated;
- client.GuildScheduledEventDeleted += this.Client_GuildScheduledEventDeleted;
- client.GuildScheduledEventUserAdded += this.Client_GuildScheduledEventUserAdded; ;
- client.GuildScheduledEventUserRemoved += this.Client_GuildScheduledEventUserRemoved;
- client.EmbeddedActivityUpdated += this.Client_EmbeddedActivityUpdated;
- client.GuildMemberTimeoutAdded += this.Client_GuildMemberTimeoutAdded;
- client.GuildMemberTimeoutChanged += this.Client_GuildMemberTimeoutChanged;
- client.GuildMemberTimeoutRemoved += this.Client_GuildMemberTimeoutRemoved;
- }
+ /// <summary>
+ /// Hooks the event handlers.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ private void HookEventHandlers(DiscordClient client)
+ {
+ client.ClientErrored += this.Client_ClientError;
+ client.SocketErrored += this.Client_SocketError;
+ client.SocketOpened += this.Client_SocketOpened;
+ client.SocketClosed += this.Client_SocketClosed;
+ client.Ready += this.Client_Ready;
+ client.Resumed += this.Client_Resumed;
+ client.ChannelCreated += this.Client_ChannelCreated;
+ client.ChannelUpdated += this.Client_ChannelUpdated;
+ client.ChannelDeleted += this.Client_ChannelDeleted;
+ client.DmChannelDeleted += this.Client_DMChannelDeleted;
+ client.ChannelPinsUpdated += this.Client_ChannelPinsUpdated;
+ client.GuildCreated += this.Client_GuildCreated;
+ client.GuildAvailable += this.Client_GuildAvailable;
+ client.GuildUpdated += this.Client_GuildUpdated;
+ client.GuildDeleted += this.Client_GuildDeleted;
+ client.GuildUnavailable += this.Client_GuildUnavailable;
+ client.GuildDownloadCompleted += this.Client_GuildDownloadCompleted;
+ client.InviteCreated += this.Client_InviteCreated;
+ client.InviteDeleted += this.Client_InviteDeleted;
+ client.MessageCreated += this.Client_MessageCreated;
+ client.PresenceUpdated += this.Client_PresenceUpdate;
+ client.GuildBanAdded += this.Client_GuildBanAdd;
+ client.GuildBanRemoved += this.Client_GuildBanRemove;
+ client.GuildEmojisUpdated += this.Client_GuildEmojisUpdate;
+ client.GuildStickersUpdated += this.Client_GuildStickersUpdate;
+ client.GuildIntegrationsUpdated += this.Client_GuildIntegrationsUpdate;
+ client.GuildMemberAdded += this.Client_GuildMemberAdd;
+ client.GuildMemberRemoved += this.Client_GuildMemberRemove;
+ client.GuildMemberUpdated += this.Client_GuildMemberUpdate;
+ client.GuildRoleCreated += this.Client_GuildRoleCreate;
+ client.GuildRoleUpdated += this.Client_GuildRoleUpdate;
+ client.GuildRoleDeleted += this.Client_GuildRoleDelete;
+ client.MessageUpdated += this.Client_MessageUpdate;
+ client.MessageDeleted += this.Client_MessageDelete;
+ client.MessagesBulkDeleted += this.Client_MessageBulkDelete;
+ client.InteractionCreated += this.Client_InteractionCreate;
+ client.ComponentInteractionCreated += this.Client_ComponentInteractionCreate;
+ client.ContextMenuInteractionCreated += this.Client_ContextMenuInteractionCreate;
+ client.TypingStarted += this.Client_TypingStart;
+ client.UserSettingsUpdated += this.Client_UserSettingsUpdate;
+ client.UserUpdated += this.Client_UserUpdate;
+ client.VoiceStateUpdated += this.Client_VoiceStateUpdate;
+ client.VoiceServerUpdated += this.Client_VoiceServerUpdate;
+ client.GuildMembersChunked += this.Client_GuildMembersChunk;
+ client.UnknownEvent += this.Client_UnknownEvent;
+ client.MessageReactionAdded += this.Client_MessageReactionAdd;
+ client.MessageReactionRemoved += this.Client_MessageReactionRemove;
+ client.MessageReactionsCleared += this.Client_MessageReactionRemoveAll;
+ client.MessageReactionRemovedEmoji += this.Client_MessageReactionRemovedEmoji;
+ client.WebhooksUpdated += this.Client_WebhooksUpdate;
+ client.Heartbeated += this.Client_HeartBeated;
+ client.ApplicationCommandCreated += this.Client_ApplicationCommandCreated;
+ client.ApplicationCommandUpdated += this.Client_ApplicationCommandUpdated;
+ client.ApplicationCommandDeleted += this.Client_ApplicationCommandDeleted;
+ client.GuildApplicationCommandCountUpdated += this.Client_GuildApplicationCommandCountUpdated;
+ client.ApplicationCommandPermissionsUpdated += this.Client_ApplicationCommandPermissionsUpdated;
+ client.GuildIntegrationCreated += this.Client_GuildIntegrationCreated;
+ client.GuildIntegrationUpdated += this.Client_GuildIntegrationUpdated;
+ client.GuildIntegrationDeleted += this.Client_GuildIntegrationDeleted;
+ client.StageInstanceCreated += this.Client_StageInstanceCreated;
+ client.StageInstanceUpdated += this.Client_StageInstanceUpdated;
+ client.StageInstanceDeleted += this.Client_StageInstanceDeleted;
+ client.ThreadCreated += this.Client_ThreadCreated;
+ client.ThreadUpdated += this.Client_ThreadUpdated;
+ client.ThreadDeleted += this.Client_ThreadDeleted;
+ client.ThreadListSynced += this.Client_ThreadListSynced;
+ client.ThreadMemberUpdated += this.Client_ThreadMemberUpdated;
+ client.ThreadMembersUpdated += this.Client_ThreadMembersUpdated;
+ client.Zombied += this.Client_Zombied;
+ client.PayloadReceived += this.Client_PayloadReceived;
+ client.GuildScheduledEventCreated += this.Client_GuildScheduledEventCreated;
+ client.GuildScheduledEventUpdated += this.Client_GuildScheduledEventUpdated;
+ client.GuildScheduledEventDeleted += this.Client_GuildScheduledEventDeleted;
+ client.GuildScheduledEventUserAdded += this.Client_GuildScheduledEventUserAdded; ;
+ client.GuildScheduledEventUserRemoved += this.Client_GuildScheduledEventUserRemoved;
+ client.EmbeddedActivityUpdated += this.Client_EmbeddedActivityUpdated;
+ client.GuildMemberTimeoutAdded += this.Client_GuildMemberTimeoutAdded;
+ client.GuildMemberTimeoutChanged += this.Client_GuildMemberTimeoutChanged;
+ client.GuildMemberTimeoutRemoved += this.Client_GuildMemberTimeoutRemoved;
+ }
- /// <summary>
- /// Unhooks the event handlers.
- /// </summary>
- /// <param name="client">The client.</param>
- private void UnhookEventHandlers(DiscordClient client)
- {
- client.ClientErrored -= this.Client_ClientError;
- client.SocketErrored -= this.Client_SocketError;
- client.SocketOpened -= this.Client_SocketOpened;
- client.SocketClosed -= this.Client_SocketClosed;
- client.Ready -= this.Client_Ready;
- client.Resumed -= this.Client_Resumed;
- client.ChannelCreated -= this.Client_ChannelCreated;
- client.ChannelUpdated -= this.Client_ChannelUpdated;
- client.ChannelDeleted -= this.Client_ChannelDeleted;
- client.DmChannelDeleted -= this.Client_DMChannelDeleted;
- client.ChannelPinsUpdated -= this.Client_ChannelPinsUpdated;
- client.GuildCreated -= this.Client_GuildCreated;
- client.GuildAvailable -= this.Client_GuildAvailable;
- client.GuildUpdated -= this.Client_GuildUpdated;
- client.GuildDeleted -= this.Client_GuildDeleted;
- client.GuildUnavailable -= this.Client_GuildUnavailable;
- client.GuildDownloadCompleted -= this.Client_GuildDownloadCompleted;
- client.InviteCreated -= this.Client_InviteCreated;
- client.InviteDeleted -= this.Client_InviteDeleted;
- client.MessageCreated -= this.Client_MessageCreated;
- client.PresenceUpdated -= this.Client_PresenceUpdate;
- client.GuildBanAdded -= this.Client_GuildBanAdd;
- client.GuildBanRemoved -= this.Client_GuildBanRemove;
- client.GuildEmojisUpdated -= this.Client_GuildEmojisUpdate;
- client.GuildStickersUpdated -= this.Client_GuildStickersUpdate;
- client.GuildIntegrationsUpdated -= this.Client_GuildIntegrationsUpdate;
- client.GuildMemberAdded -= this.Client_GuildMemberAdd;
- client.GuildMemberRemoved -= this.Client_GuildMemberRemove;
- client.GuildMemberUpdated -= this.Client_GuildMemberUpdate;
- client.GuildRoleCreated -= this.Client_GuildRoleCreate;
- client.GuildRoleUpdated -= this.Client_GuildRoleUpdate;
- client.GuildRoleDeleted -= this.Client_GuildRoleDelete;
- client.MessageUpdated -= this.Client_MessageUpdate;
- client.MessageDeleted -= this.Client_MessageDelete;
- client.MessagesBulkDeleted -= this.Client_MessageBulkDelete;
- client.InteractionCreated -= this.Client_InteractionCreate;
- client.ComponentInteractionCreated -= this.Client_ComponentInteractionCreate;
- client.ContextMenuInteractionCreated -= this.Client_ContextMenuInteractionCreate;
- client.TypingStarted -= this.Client_TypingStart;
- client.UserSettingsUpdated -= this.Client_UserSettingsUpdate;
- client.UserUpdated -= this.Client_UserUpdate;
- client.VoiceStateUpdated -= this.Client_VoiceStateUpdate;
- client.VoiceServerUpdated -= this.Client_VoiceServerUpdate;
- client.GuildMembersChunked -= this.Client_GuildMembersChunk;
- client.UnknownEvent -= this.Client_UnknownEvent;
- client.MessageReactionAdded -= this.Client_MessageReactionAdd;
- client.MessageReactionRemoved -= this.Client_MessageReactionRemove;
- client.MessageReactionsCleared -= this.Client_MessageReactionRemoveAll;
- client.MessageReactionRemovedEmoji -= this.Client_MessageReactionRemovedEmoji;
- client.WebhooksUpdated -= this.Client_WebhooksUpdate;
- client.Heartbeated -= this.Client_HeartBeated;
- client.ApplicationCommandCreated -= this.Client_ApplicationCommandCreated;
- client.ApplicationCommandUpdated -= this.Client_ApplicationCommandUpdated;
- client.ApplicationCommandDeleted -= this.Client_ApplicationCommandDeleted;
- client.GuildApplicationCommandCountUpdated -= this.Client_GuildApplicationCommandCountUpdated;
- client.ApplicationCommandPermissionsUpdated -= this.Client_ApplicationCommandPermissionsUpdated;
- client.GuildIntegrationCreated -= this.Client_GuildIntegrationCreated;
- client.GuildIntegrationUpdated -= this.Client_GuildIntegrationUpdated;
- client.GuildIntegrationDeleted -= this.Client_GuildIntegrationDeleted;
- client.StageInstanceCreated -= this.Client_StageInstanceCreated;
- client.StageInstanceUpdated -= this.Client_StageInstanceUpdated;
- client.StageInstanceDeleted -= this.Client_StageInstanceDeleted;
- client.ThreadCreated -= this.Client_ThreadCreated;
- client.ThreadUpdated -= this.Client_ThreadUpdated;
- client.ThreadDeleted -= this.Client_ThreadDeleted;
- client.ThreadListSynced -= this.Client_ThreadListSynced;
- client.ThreadMemberUpdated -= this.Client_ThreadMemberUpdated;
- client.ThreadMembersUpdated -= this.Client_ThreadMembersUpdated;
- client.Zombied -= this.Client_Zombied;
- client.PayloadReceived -= this.Client_PayloadReceived;
- client.GuildScheduledEventCreated -= this.Client_GuildScheduledEventCreated;
- client.GuildScheduledEventUpdated -= this.Client_GuildScheduledEventUpdated;
- client.GuildScheduledEventDeleted -= this.Client_GuildScheduledEventDeleted;
- client.GuildScheduledEventUserAdded -= this.Client_GuildScheduledEventUserAdded; ;
- client.GuildScheduledEventUserRemoved -= this.Client_GuildScheduledEventUserRemoved;
- client.EmbeddedActivityUpdated -= this.Client_EmbeddedActivityUpdated;
- client.GuildMemberTimeoutAdded -= this.Client_GuildMemberTimeoutAdded;
- client.GuildMemberTimeoutChanged -= this.Client_GuildMemberTimeoutChanged;
- client.GuildMemberTimeoutRemoved -= this.Client_GuildMemberTimeoutRemoved;
- }
+ /// <summary>
+ /// Unhooks the event handlers.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ private void UnhookEventHandlers(DiscordClient client)
+ {
+ client.ClientErrored -= this.Client_ClientError;
+ client.SocketErrored -= this.Client_SocketError;
+ client.SocketOpened -= this.Client_SocketOpened;
+ client.SocketClosed -= this.Client_SocketClosed;
+ client.Ready -= this.Client_Ready;
+ client.Resumed -= this.Client_Resumed;
+ client.ChannelCreated -= this.Client_ChannelCreated;
+ client.ChannelUpdated -= this.Client_ChannelUpdated;
+ client.ChannelDeleted -= this.Client_ChannelDeleted;
+ client.DmChannelDeleted -= this.Client_DMChannelDeleted;
+ client.ChannelPinsUpdated -= this.Client_ChannelPinsUpdated;
+ client.GuildCreated -= this.Client_GuildCreated;
+ client.GuildAvailable -= this.Client_GuildAvailable;
+ client.GuildUpdated -= this.Client_GuildUpdated;
+ client.GuildDeleted -= this.Client_GuildDeleted;
+ client.GuildUnavailable -= this.Client_GuildUnavailable;
+ client.GuildDownloadCompleted -= this.Client_GuildDownloadCompleted;
+ client.InviteCreated -= this.Client_InviteCreated;
+ client.InviteDeleted -= this.Client_InviteDeleted;
+ client.MessageCreated -= this.Client_MessageCreated;
+ client.PresenceUpdated -= this.Client_PresenceUpdate;
+ client.GuildBanAdded -= this.Client_GuildBanAdd;
+ client.GuildBanRemoved -= this.Client_GuildBanRemove;
+ client.GuildEmojisUpdated -= this.Client_GuildEmojisUpdate;
+ client.GuildStickersUpdated -= this.Client_GuildStickersUpdate;
+ client.GuildIntegrationsUpdated -= this.Client_GuildIntegrationsUpdate;
+ client.GuildMemberAdded -= this.Client_GuildMemberAdd;
+ client.GuildMemberRemoved -= this.Client_GuildMemberRemove;
+ client.GuildMemberUpdated -= this.Client_GuildMemberUpdate;
+ client.GuildRoleCreated -= this.Client_GuildRoleCreate;
+ client.GuildRoleUpdated -= this.Client_GuildRoleUpdate;
+ client.GuildRoleDeleted -= this.Client_GuildRoleDelete;
+ client.MessageUpdated -= this.Client_MessageUpdate;
+ client.MessageDeleted -= this.Client_MessageDelete;
+ client.MessagesBulkDeleted -= this.Client_MessageBulkDelete;
+ client.InteractionCreated -= this.Client_InteractionCreate;
+ client.ComponentInteractionCreated -= this.Client_ComponentInteractionCreate;
+ client.ContextMenuInteractionCreated -= this.Client_ContextMenuInteractionCreate;
+ client.TypingStarted -= this.Client_TypingStart;
+ client.UserSettingsUpdated -= this.Client_UserSettingsUpdate;
+ client.UserUpdated -= this.Client_UserUpdate;
+ client.VoiceStateUpdated -= this.Client_VoiceStateUpdate;
+ client.VoiceServerUpdated -= this.Client_VoiceServerUpdate;
+ client.GuildMembersChunked -= this.Client_GuildMembersChunk;
+ client.UnknownEvent -= this.Client_UnknownEvent;
+ client.MessageReactionAdded -= this.Client_MessageReactionAdd;
+ client.MessageReactionRemoved -= this.Client_MessageReactionRemove;
+ client.MessageReactionsCleared -= this.Client_MessageReactionRemoveAll;
+ client.MessageReactionRemovedEmoji -= this.Client_MessageReactionRemovedEmoji;
+ client.WebhooksUpdated -= this.Client_WebhooksUpdate;
+ client.Heartbeated -= this.Client_HeartBeated;
+ client.ApplicationCommandCreated -= this.Client_ApplicationCommandCreated;
+ client.ApplicationCommandUpdated -= this.Client_ApplicationCommandUpdated;
+ client.ApplicationCommandDeleted -= this.Client_ApplicationCommandDeleted;
+ client.GuildApplicationCommandCountUpdated -= this.Client_GuildApplicationCommandCountUpdated;
+ client.ApplicationCommandPermissionsUpdated -= this.Client_ApplicationCommandPermissionsUpdated;
+ client.GuildIntegrationCreated -= this.Client_GuildIntegrationCreated;
+ client.GuildIntegrationUpdated -= this.Client_GuildIntegrationUpdated;
+ client.GuildIntegrationDeleted -= this.Client_GuildIntegrationDeleted;
+ client.StageInstanceCreated -= this.Client_StageInstanceCreated;
+ client.StageInstanceUpdated -= this.Client_StageInstanceUpdated;
+ client.StageInstanceDeleted -= this.Client_StageInstanceDeleted;
+ client.ThreadCreated -= this.Client_ThreadCreated;
+ client.ThreadUpdated -= this.Client_ThreadUpdated;
+ client.ThreadDeleted -= this.Client_ThreadDeleted;
+ client.ThreadListSynced -= this.Client_ThreadListSynced;
+ client.ThreadMemberUpdated -= this.Client_ThreadMemberUpdated;
+ client.ThreadMembersUpdated -= this.Client_ThreadMembersUpdated;
+ client.Zombied -= this.Client_Zombied;
+ client.PayloadReceived -= this.Client_PayloadReceived;
+ client.GuildScheduledEventCreated -= this.Client_GuildScheduledEventCreated;
+ client.GuildScheduledEventUpdated -= this.Client_GuildScheduledEventUpdated;
+ client.GuildScheduledEventDeleted -= this.Client_GuildScheduledEventDeleted;
+ client.GuildScheduledEventUserAdded -= this.Client_GuildScheduledEventUserAdded; ;
+ client.GuildScheduledEventUserRemoved -= this.Client_GuildScheduledEventUserRemoved;
+ client.EmbeddedActivityUpdated -= this.Client_EmbeddedActivityUpdated;
+ client.GuildMemberTimeoutAdded -= this.Client_GuildMemberTimeoutAdded;
+ client.GuildMemberTimeoutChanged -= this.Client_GuildMemberTimeoutChanged;
+ client.GuildMemberTimeoutRemoved -= this.Client_GuildMemberTimeoutRemoved;
+ }
- /// <summary>
- /// Gets the shard id from guilds.
- /// </summary>
- /// <param name="id">The id.</param>
- /// <returns>An int.</returns>
- private int GetShardIdFromGuilds(ulong id)
- {
- foreach (var s in this._shards.Values)
+ /// <summary>
+ /// Gets the shard id from guilds.
+ /// </summary>
+ /// <param name="id">The id.</param>
+ /// <returns>An int.</returns>
+ private int GetShardIdFromGuilds(ulong id)
{
- if (s.GuildsInternal.TryGetValue(id, out _))
+ foreach (var s in this._shards.Values)
{
- return s.ShardId;
+ if (s.GuildsInternal.TryGetValue(id, out _))
+ {
+ return s.ShardId;
+ }
}
+
+ return -1;
}
- return -1;
- }
+ #endregion
- #endregion
+ #region Destructor
- #region Destructor
+ ~DiscordShardedClient()
+ {
+ this.InternalStopAsync(false).GetAwaiter().GetResult();
+ }
- ~DiscordShardedClient()
- {
- this.InternalStopAsync(false).GetAwaiter().GetResult();
+ #endregion
}
-
- #endregion
}
diff --git a/DisCatSharp/Clients/DiscordWebhookClient.cs b/DisCatSharp/Clients/DiscordWebhookClient.cs
index ed0ade4d5..6f889aced 100644
--- a/DisCatSharp/Clients/DiscordWebhookClient.cs
+++ b/DisCatSharp/Clients/DiscordWebhookClient.cs
@@ -1,282 +1,283 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Exceptions;
using DisCatSharp.Net;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a webhook-only client. This client can be used to execute Discord Webhooks.
-/// </summary>
-public class DiscordWebhookClient
+namespace DisCatSharp
{
/// <summary>
- /// Gets the logger for this client.
- /// </summary>
- public ILogger<DiscordWebhookClient> Logger { get; }
-
- /// <summary>
- /// Gets the webhook regex.
- /// This regex has 2 named capture groups: "id" and "token".
- /// </summary>
- private static Regex s_webhookRegex { get; } = new(@"(?:https?:\/\/)?discord(?:app)?.com\/api\/(?:v\d\/)?webhooks\/(?<id>\d+)\/(?<token>[A-Za-z0-9_\-]+)", RegexOptions.ECMAScript);
-
- /// <summary>
- /// Gets the collection of registered webhooks.
- /// </summary>
- public IReadOnlyList<DiscordWebhook> Webhooks { get; }
-
- /// <summary>
- /// Gets or sets the username for registered webhooks. Note that this only takes effect when broadcasting.
- /// </summary>
- public string Username { get; set; }
-
- /// <summary>
- /// Gets or set the avatar for registered webhooks. Note that this only takes effect when broadcasting.
- /// </summary>
- public string AvatarUrl { get; set; }
-
- internal List<DiscordWebhook> Hooks;
- internal DiscordApiClient Apiclient;
-
- internal LogLevel MinimumLogLevel;
- internal string LogTimestampFormat;
-
- /// <summary>
- /// Creates a new webhook client.
+ /// Represents a webhook-only client. This client can be used to execute Discord Webhooks.
/// </summary>
- public DiscordWebhookClient()
- : this(null, null)
- { }
-
- /// <summary>
- /// Creates a new webhook client, with specified HTTP proxy, timeout, and logging settings.
- /// </summary>
- /// <param name="proxy">The proxy to use for HTTP connections. Defaults to null.</param>
- /// <param name="timeout">The optional timeout to use for HTTP requests. Set to <see cref="System.Threading.Timeout.InfiniteTimeSpan"/> to disable timeouts. Defaults to null.</param>
- /// <param name="useRelativeRateLimit">Whether to use the system clock for computing rate limit resets. See <see cref="DiscordConfiguration.UseRelativeRatelimit"/> for more details. Defaults to true.</param>
- /// <param name="loggerFactory">The optional logging factory to use for this client. Defaults to null.</param>
- /// <param name="minimumLogLevel">The minimum logging level for messages. Defaults to information.</param>
- /// <param name="logTimestampFormat">The timestamp format to use for the logger.</param>
- public DiscordWebhookClient(IWebProxy proxy = null, TimeSpan? timeout = null, bool useRelativeRateLimit = true,
- ILoggerFactory loggerFactory = null, LogLevel minimumLogLevel = LogLevel.Information, string logTimestampFormat = "yyyy-MM-dd HH:mm:ss zzz")
+ public class DiscordWebhookClient
{
- this.MinimumLogLevel = minimumLogLevel;
- this.LogTimestampFormat = logTimestampFormat;
-
- if (loggerFactory == null)
+ /// <summary>
+ /// Gets the logger for this client.
+ /// </summary>
+ public ILogger<DiscordWebhookClient> Logger { get; }
+
+ /// <summary>
+ /// Gets the webhook regex.
+ /// This regex has 2 named capture groups: "id" and "token".
+ /// </summary>
+ private static Regex s_webhookRegex { get; } = new(@"(?:https?:\/\/)?discord(?:app)?.com\/api\/(?:v\d\/)?webhooks\/(?<id>\d+)\/(?<token>[A-Za-z0-9_\-]+)", RegexOptions.ECMAScript);
+
+ /// <summary>
+ /// Gets the collection of registered webhooks.
+ /// </summary>
+ public IReadOnlyList<DiscordWebhook> Webhooks { get; }
+
+ /// <summary>
+ /// Gets or sets the username for registered webhooks. Note that this only takes effect when broadcasting.
+ /// </summary>
+ public string Username { get; set; }
+
+ /// <summary>
+ /// Gets or set the avatar for registered webhooks. Note that this only takes effect when broadcasting.
+ /// </summary>
+ public string AvatarUrl { get; set; }
+
+ internal List<DiscordWebhook> Hooks;
+ internal DiscordApiClient Apiclient;
+
+ internal LogLevel MinimumLogLevel;
+ internal string LogTimestampFormat;
+
+ /// <summary>
+ /// Creates a new webhook client.
+ /// </summary>
+ public DiscordWebhookClient()
+ : this(null, null)
+ { }
+
+ /// <summary>
+ /// Creates a new webhook client, with specified HTTP proxy, timeout, and logging settings.
+ /// </summary>
+ /// <param name="proxy">The proxy to use for HTTP connections. Defaults to null.</param>
+ /// <param name="timeout">The optional timeout to use for HTTP requests. Set to <see cref="System.Threading.Timeout.InfiniteTimeSpan"/> to disable timeouts. Defaults to null.</param>
+ /// <param name="useRelativeRateLimit">Whether to use the system clock for computing rate limit resets. See <see cref="DiscordConfiguration.UseRelativeRatelimit"/> for more details. Defaults to true.</param>
+ /// <param name="loggerFactory">The optional logging factory to use for this client. Defaults to null.</param>
+ /// <param name="minimumLogLevel">The minimum logging level for messages. Defaults to information.</param>
+ /// <param name="logTimestampFormat">The timestamp format to use for the logger.</param>
+ public DiscordWebhookClient(IWebProxy proxy = null, TimeSpan? timeout = null, bool useRelativeRateLimit = true,
+ ILoggerFactory loggerFactory = null, LogLevel minimumLogLevel = LogLevel.Information, string logTimestampFormat = "yyyy-MM-dd HH:mm:ss zzz")
{
- loggerFactory = new DefaultLoggerFactory();
- loggerFactory.AddProvider(new DefaultLoggerProvider(this));
- }
+ this.MinimumLogLevel = minimumLogLevel;
+ this.LogTimestampFormat = logTimestampFormat;
- this.Logger = loggerFactory.CreateLogger<DiscordWebhookClient>();
-
- var parsedTimeout = timeout ?? TimeSpan.FromSeconds(10);
+ if (loggerFactory == null)
+ {
+ loggerFactory = new DefaultLoggerFactory();
+ loggerFactory.AddProvider(new DefaultLoggerProvider(this));
+ }
- this.Apiclient = new DiscordApiClient(proxy, parsedTimeout, useRelativeRateLimit, this.Logger);
- this.Hooks = new List<DiscordWebhook>();
- this.Webhooks = new ReadOnlyCollection<DiscordWebhook>(this.Hooks);
- }
+ this.Logger = loggerFactory.CreateLogger<DiscordWebhookClient>();
- /// <summary>
- /// Registers a webhook with this client. This retrieves a webhook based on the ID and token supplied.
- /// </summary>
- /// <param name="id">The ID of the webhook to add.</param>
- /// <param name="token">The token of the webhook to add.</param>
- /// <returns>The registered webhook.</returns>
- public async Task<DiscordWebhook> AddWebhookAsync(ulong id, string token)
- {
- if (string.IsNullOrWhiteSpace(token))
- throw new ArgumentNullException(nameof(token));
- token = token.Trim();
+ var parsedTimeout = timeout ?? TimeSpan.FromSeconds(10);
- if (this.Hooks.Any(x => x.Id == id))
- throw new InvalidOperationException("This webhook is registered with this client.");
+ this.Apiclient = new DiscordApiClient(proxy, parsedTimeout, useRelativeRateLimit, this.Logger);
+ this.Hooks = new List<DiscordWebhook>();
+ this.Webhooks = new ReadOnlyCollection<DiscordWebhook>(this.Hooks);
+ }
- var wh = await this.Apiclient.GetWebhookWithTokenAsync(id, token).ConfigureAwait(false);
- this.Hooks.Add(wh);
+ /// <summary>
+ /// Registers a webhook with this client. This retrieves a webhook based on the ID and token supplied.
+ /// </summary>
+ /// <param name="id">The ID of the webhook to add.</param>
+ /// <param name="token">The token of the webhook to add.</param>
+ /// <returns>The registered webhook.</returns>
+ public async Task<DiscordWebhook> AddWebhookAsync(ulong id, string token)
+ {
+ if (string.IsNullOrWhiteSpace(token))
+ throw new ArgumentNullException(nameof(token));
+ token = token.Trim();
- return wh;
- }
+ if (this.Hooks.Any(x => x.Id == id))
+ throw new InvalidOperationException("This webhook is registered with this client.");
- /// <summary>
- /// Registers a webhook with this client. This retrieves a webhook from webhook URL.
- /// </summary>
- /// <param name="url">URL of the webhook to retrieve. This URL must contain both ID and token.</param>
- /// <returns>The registered webhook.</returns>
- public Task<DiscordWebhook> AddWebhookAsync(Uri url)
- {
- if (url == null)
- throw new ArgumentNullException(nameof(url));
+ var wh = await this.Apiclient.GetWebhookWithTokenAsync(id, token).ConfigureAwait(false);
+ this.Hooks.Add(wh);
- var m = s_webhookRegex.Match(url.ToString());
- if (!m.Success)
- throw new ArgumentException("Invalid webhook URL supplied.", nameof(url));
+ return wh;
+ }
- var idraw = m.Groups["id"];
- var tokenraw = m.Groups["token"];
- if (!ulong.TryParse(idraw.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var id))
- throw new ArgumentException("Invalid webhook URL supplied.", nameof(url));
+ /// <summary>
+ /// Registers a webhook with this client. This retrieves a webhook from webhook URL.
+ /// </summary>
+ /// <param name="url">URL of the webhook to retrieve. This URL must contain both ID and token.</param>
+ /// <returns>The registered webhook.</returns>
+ public Task<DiscordWebhook> AddWebhookAsync(Uri url)
+ {
+ if (url == null)
+ throw new ArgumentNullException(nameof(url));
- var token = tokenraw.Value;
- return this.AddWebhookAsync(id, token);
- }
+ var m = s_webhookRegex.Match(url.ToString());
+ if (!m.Success)
+ throw new ArgumentException("Invalid webhook URL supplied.", nameof(url));
- /// <summary>
- /// Registers a webhook with this client. This retrieves a webhook using the supplied full discord client.
- /// </summary>
- /// <param name="id">ID of the webhook to register.</param>
- /// <param name="client">Discord client to which the webhook will belong.</param>
- /// <returns>The registered webhook.</returns>
- public async Task<DiscordWebhook> AddWebhookAsync(ulong id, BaseDiscordClient client)
- {
- if (client == null)
- throw new ArgumentNullException(nameof(client));
-
- if (this.Hooks.Any(x => x.Id == id))
- throw new ArgumentException("This webhook is already registered with this client.");
-
- var wh = await client.ApiClient.GetWebhookAsync(id).ConfigureAwait(false);
-
- // personally I don't think we need to override anything.
- // it would even make sense to keep the hook as-is, in case
- // it's returned without a token for some bizarre reason
- // remember -- discord is not really consistent
- //var nwh = new DiscordWebhook()
- //{
- // ApiClient = _apiclient,
- // AvatarHash = wh.AvatarHash,
- // ChannelId = wh.ChannelId,
- // GuildId = wh.GuildId,
- // Id = wh.Id,
- // Name = wh.Name,
- // Token = wh.Token,
- // User = wh.User,
- // Discord = null
- //};
- this.Hooks.Add(wh);
-
- return wh;
- }
+ var idraw = m.Groups["id"];
+ var tokenraw = m.Groups["token"];
+ if (!ulong.TryParse(idraw.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var id))
+ throw new ArgumentException("Invalid webhook URL supplied.", nameof(url));
- /// <summary>
- /// Registers a webhook with this client. This reuses the supplied webhook object.
- /// </summary>
- /// <param name="webhook">Webhook to register.</param>
- /// <returns>The registered webhook.</returns>
- public DiscordWebhook AddWebhook(DiscordWebhook webhook)
- {
- if (webhook == null)
- throw new ArgumentNullException(nameof(webhook));
-
- if (this.Hooks.Any(x => x.Id == webhook.Id))
- throw new ArgumentException("This webhook is already registered with this client.");
-
- //var nwh = new DiscordWebhook()
- //{
- // ApiClient = _apiclient,
- // AvatarHash = webhook.AvatarHash,
- // ChannelId = webhook.ChannelId,
- // GuildId = webhook.GuildId,
- // Id = webhook.Id,
- // Name = webhook.Name,
- // Token = webhook.Token,
- // User = webhook.User,
- // Discord = null
- //};
- this.Hooks.Add(webhook);
-
- return webhook;
- }
+ var token = tokenraw.Value;
+ return this.AddWebhookAsync(id, token);
+ }
- /// <summary>
- /// Unregisters a webhook with this client.
- /// </summary>
- /// <param name="id">ID of the webhook to unregister.</param>
- /// <returns>The unregistered webhook.</returns>
- public DiscordWebhook RemoveWebhook(ulong id)
- {
- if (!this.Hooks.Any(x => x.Id == id))
- throw new ArgumentException("This webhook is not registered with this client.");
+ /// <summary>
+ /// Registers a webhook with this client. This retrieves a webhook using the supplied full discord client.
+ /// </summary>
+ /// <param name="id">ID of the webhook to register.</param>
+ /// <param name="client">Discord client to which the webhook will belong.</param>
+ /// <returns>The registered webhook.</returns>
+ public async Task<DiscordWebhook> AddWebhookAsync(ulong id, BaseDiscordClient client)
+ {
+ if (client == null)
+ throw new ArgumentNullException(nameof(client));
+
+ if (this.Hooks.Any(x => x.Id == id))
+ throw new ArgumentException("This webhook is already registered with this client.");
+
+ var wh = await client.ApiClient.GetWebhookAsync(id).ConfigureAwait(false);
+
+ // personally I don't think we need to override anything.
+ // it would even make sense to keep the hook as-is, in case
+ // it's returned without a token for some bizarre reason
+ // remember -- discord is not really consistent
+ //var nwh = new DiscordWebhook()
+ //{
+ // ApiClient = _apiclient,
+ // AvatarHash = wh.AvatarHash,
+ // ChannelId = wh.ChannelId,
+ // GuildId = wh.GuildId,
+ // Id = wh.Id,
+ // Name = wh.Name,
+ // Token = wh.Token,
+ // User = wh.User,
+ // Discord = null
+ //};
+ this.Hooks.Add(wh);
+
+ return wh;
+ }
- var wh = this.GetRegisteredWebhook(id);
- this.Hooks.Remove(wh);
- return wh;
- }
+ /// <summary>
+ /// Registers a webhook with this client. This reuses the supplied webhook object.
+ /// </summary>
+ /// <param name="webhook">Webhook to register.</param>
+ /// <returns>The registered webhook.</returns>
+ public DiscordWebhook AddWebhook(DiscordWebhook webhook)
+ {
+ if (webhook == null)
+ throw new ArgumentNullException(nameof(webhook));
+
+ if (this.Hooks.Any(x => x.Id == webhook.Id))
+ throw new ArgumentException("This webhook is already registered with this client.");
+
+ //var nwh = new DiscordWebhook()
+ //{
+ // ApiClient = _apiclient,
+ // AvatarHash = webhook.AvatarHash,
+ // ChannelId = webhook.ChannelId,
+ // GuildId = webhook.GuildId,
+ // Id = webhook.Id,
+ // Name = webhook.Name,
+ // Token = webhook.Token,
+ // User = webhook.User,
+ // Discord = null
+ //};
+ this.Hooks.Add(webhook);
+
+ return webhook;
+ }
- /// <summary>
- /// Gets a registered webhook with specified ID.
- /// </summary>
- /// <param name="id">ID of the registered webhook to retrieve.</param>
- /// <returns>The requested webhook.</returns>
- public DiscordWebhook GetRegisteredWebhook(ulong id)
- => this.Hooks.FirstOrDefault(xw => xw.Id == id);
+ /// <summary>
+ /// Unregisters a webhook with this client.
+ /// </summary>
+ /// <param name="id">ID of the webhook to unregister.</param>
+ /// <returns>The unregistered webhook.</returns>
+ public DiscordWebhook RemoveWebhook(ulong id)
+ {
+ if (!this.Hooks.Any(x => x.Id == id))
+ throw new ArgumentException("This webhook is not registered with this client.");
- /// <summary>
- /// Broadcasts a message to all registered webhooks.
- /// </summary>
- /// <param name="builder">Webhook builder filled with data to send.</param>
- /// <returns>A dictionary of <see cref="DisCatSharp.Entities.DiscordWebhook"/>s and <see cref="DisCatSharp.Entities.DiscordMessage"/>s.</returns>
- public async Task<Dictionary<DiscordWebhook, DiscordMessage>> BroadcastMessageAsync(DiscordWebhookBuilder builder)
- {
- var deadhooks = new List<DiscordWebhook>();
- var messages = new Dictionary<DiscordWebhook, DiscordMessage>();
+ var wh = this.GetRegisteredWebhook(id);
+ this.Hooks.Remove(wh);
+ return wh;
+ }
- foreach (var hook in this.Hooks)
+ /// <summary>
+ /// Gets a registered webhook with specified ID.
+ /// </summary>
+ /// <param name="id">ID of the registered webhook to retrieve.</param>
+ /// <returns>The requested webhook.</returns>
+ public DiscordWebhook GetRegisteredWebhook(ulong id)
+ => this.Hooks.FirstOrDefault(xw => xw.Id == id);
+
+ /// <summary>
+ /// Broadcasts a message to all registered webhooks.
+ /// </summary>
+ /// <param name="builder">Webhook builder filled with data to send.</param>
+ /// <returns>A dictionary of <see cref="DisCatSharp.Entities.DiscordWebhook"/>s and <see cref="DisCatSharp.Entities.DiscordMessage"/>s.</returns>
+ public async Task<Dictionary<DiscordWebhook, DiscordMessage>> BroadcastMessageAsync(DiscordWebhookBuilder builder)
{
- try
- {
- messages.Add(hook, await hook.ExecuteAsync(builder).ConfigureAwait(false));
- }
- catch (NotFoundException)
+ var deadhooks = new List<DiscordWebhook>();
+ var messages = new Dictionary<DiscordWebhook, DiscordMessage>();
+
+ foreach (var hook in this.Hooks)
{
- deadhooks.Add(hook);
+ try
+ {
+ messages.Add(hook, await hook.ExecuteAsync(builder).ConfigureAwait(false));
+ }
+ catch (NotFoundException)
+ {
+ deadhooks.Add(hook);
+ }
}
- }
- // Removing dead webhooks from collection
- foreach (var xwh in deadhooks) this.Hooks.Remove(xwh);
+ // Removing dead webhooks from collection
+ foreach (var xwh in deadhooks) this.Hooks.Remove(xwh);
- return messages;
- }
+ return messages;
+ }
- ~DiscordWebhookClient()
- {
- this.Hooks.Clear();
- this.Hooks = null;
- this.Apiclient.Rest.Dispose();
+ ~DiscordWebhookClient()
+ {
+ this.Hooks.Clear();
+ this.Hooks = null;
+ this.Apiclient.Rest.Dispose();
+ }
}
}
diff --git a/DisCatSharp/DiscordConfiguration.cs b/DisCatSharp/DiscordConfiguration.cs
index 639e16653..9952d6db2 100644
--- a/DisCatSharp/DiscordConfiguration.cs
+++ b/DisCatSharp/DiscordConfiguration.cs
@@ -1,283 +1,284 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
using DisCatSharp.Net.Udp;
using DisCatSharp.Net.WebSocket;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents configuration for <see cref="DiscordClient"/> and <see cref="DiscordShardedClient"/>.
-/// </summary>
-public sealed class DiscordConfiguration
+namespace DisCatSharp
{
/// <summary>
- /// Sets the token used to identify the client.
+ /// Represents configuration for <see cref="DiscordClient"/> and <see cref="DiscordShardedClient"/>.
/// </summary>
- public string Token
+ public sealed class DiscordConfiguration
{
- internal get => this._token;
- set
+ /// <summary>
+ /// Sets the token used to identify the client.
+ /// </summary>
+ public string Token
{
- if (string.IsNullOrWhiteSpace(value))
- throw new ArgumentNullException(nameof(value), "Token cannot be null, empty, or all whitespace.");
-
- this._token = value.Trim();
+ internal get => this._token;
+ set
+ {
+ if (string.IsNullOrWhiteSpace(value))
+ throw new ArgumentNullException(nameof(value), "Token cannot be null, empty, or all whitespace.");
+
+ this._token = value.Trim();
+ }
}
- }
- private string _token = "";
-
- /// <summary>
- /// <para>Sets the type of the token used to identify the client.</para>
- /// <para>Defaults to <see cref="TokenType.Bot"/>.</para>
- /// </summary>
- public TokenType TokenType { internal get; set; } = TokenType.Bot;
-
- /// <summary>
- /// <para>Sets the minimum logging level for messages.</para>
- /// <para>Typically, the default value of <see cref="Microsoft.Extensions.Logging.LogLevel.Information"/> is ok for most uses.</para>
- /// </summary>
- public LogLevel MinimumLogLevel { internal get; set; } = LogLevel.Information;
-
- /// <summary>
- /// Overwrites the api version.
- /// Defaults to 10.
- /// </summary>
- public string ApiVersion { internal get; set; } = "10";
-
- /// <summary>
- /// <para>Sets whether to rely on Discord for NTP (Network Time Protocol) synchronization with the "X-Ratelimit-Reset-After" header.</para>
- /// <para>If the system clock is unsynced, setting this to true will ensure ratelimits are synced with Discord and reduce the risk of hitting one.</para>
- /// <para>This should only be set to false if the system clock is synced with NTP.</para>
- /// <para>Defaults to true.</para>
- /// </summary>
- public bool UseRelativeRatelimit { internal get; set; } = true;
-
- /// <summary>
- /// <para>Allows you to overwrite the time format used by the internal debug logger.</para>
- /// <para>Only applicable when <see cref="LoggerFactory"/> is set left at default value. Defaults to ISO 8601-like format.</para>
- /// </summary>
- public string LogTimestampFormat { internal get; set; } = "yyyy-MM-dd HH:mm:ss zzz";
-
- /// <summary>
- /// <para>Sets the member count threshold at which guilds are considered large.</para>
- /// <para>Defaults to 250.</para>
- /// </summary>
- public int LargeThreshold { internal get; set; } = 250;
-
- /// <summary>
- /// <para>Sets whether to automatically reconnect in case a connection is lost.</para>
- /// <para>Defaults to true.</para>
- /// </summary>
- public bool AutoReconnect { internal get; set; } = true;
-
- /// <summary>
- /// <para>Sets the ID of the shard to connect to.</para>
- /// <para>If not sharding, or sharding automatically, this value should be left with the default value of 0.</para>
- /// </summary>
- public int ShardId { internal get; set; }
-
- /// <summary>
- /// <para>Sets the total number of shards the bot is on. If not sharding, this value should be left with a default value of 1.</para>
- /// <para>If sharding automatically, this value will indicate how many shards to boot. If left default for automatic sharding, the client will determine the shard count automatically.</para>
- /// </summary>
- public int ShardCount { internal get; set; } = 1;
-
- /// <summary>
- /// <para>Sets the level of compression for WebSocket traffic.</para>
- /// <para>Disabling this option will increase the amount of traffic sent via WebSocket. Setting <see cref="GatewayCompressionLevel.Payload"/> will enable compression for READY and GUILD_CREATE payloads. Setting <see cref="GatewayCompressionLevel.Stream"/> will enable compression for the entire WebSocket stream, drastically reducing amount of traffic.</para>
- /// <para>Defaults to <see cref="GatewayCompressionLevel.Stream"/>.</para>
- /// </summary>
- public GatewayCompressionLevel GatewayCompressionLevel { internal get; set; } = GatewayCompressionLevel.Stream;
-
- /// <summary>
- /// <para>Sets the size of the global message cache.</para>
- /// <para>Setting this to 0 will disable message caching entirely. Defaults to 1024.</para>
- /// </summary>
- public int MessageCacheSize { internal get; set; } = 1024;
-
- /// <summary>
- /// <para>Sets the proxy to use for HTTP and WebSocket connections to Discord.</para>
- /// <para>Defaults to null.</para>
- /// </summary>
- public IWebProxy Proxy { internal get; set; }
-
- /// <summary>
- /// <para>Sets the timeout for HTTP requests.</para>
- /// <para>Set to <see cref="System.Threading.Timeout.InfiniteTimeSpan"/> to disable timeouts.</para>
- /// <para>Defaults to 20 seconds.</para>
- /// </summary>
- public TimeSpan HttpTimeout { internal get; set; } = TimeSpan.FromSeconds(20);
-
- /// <summary>
- /// <para>Defines that the client should attempt to reconnect indefinitely.</para>
- /// <para>This is typically a very bad idea to set to <c>true</c>, as it will swallow all connection errors.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool ReconnectIndefinitely { internal get; set; }
-
- /// <summary>
- /// Sets whether the client should attempt to cache members if exclusively using unprivileged intents.
- /// <para>
- /// This will only take effect if there are no <see cref="DiscordIntents.GuildMembers"/> or <see cref="DiscordIntents.GuildPresences"/>
- /// intents specified. Otherwise, this will always be overwritten to true.
- /// </para>
- /// <para>Defaults to true.</para>
- /// </summary>
- public bool AlwaysCacheMembers { internal get; set; } = true;
-
- /// <summary>
- /// <para>Sets the gateway intents for this client.</para>
- /// <para>If set, the client will only receive events that they specify with intents.</para>
- /// <para>Defaults to <see cref="DiscordIntents.AllUnprivileged"/>.</para>
- /// </summary>
- public DiscordIntents Intents { internal get; set; } = DiscordIntents.AllUnprivileged;
-
- /// <summary>
- /// <para>Sets the factory method used to create instances of WebSocket clients.</para>
- /// <para>Use <see cref="DisCatSharp.Net.WebSocket.WebSocketClient.CreateNew(IWebProxy, IServiceProvider)"/> and equivalents on other implementations to switch out client implementations.</para>
- /// <para>Defaults to <see cref="DisCatSharp.Net.WebSocket.WebSocketClient.CreateNew(IWebProxy, IServiceProvider)"/>.</para>
- /// </summary>
- public WebSocketClientFactoryDelegate WebSocketClientFactory
- {
- internal get => this._webSocketClientFactory;
- set
+ private string _token = "";
+
+ /// <summary>
+ /// <para>Sets the type of the token used to identify the client.</para>
+ /// <para>Defaults to <see cref="TokenType.Bot"/>.</para>
+ /// </summary>
+ public TokenType TokenType { internal get; set; } = TokenType.Bot;
+
+ /// <summary>
+ /// <para>Sets the minimum logging level for messages.</para>
+ /// <para>Typically, the default value of <see cref="Microsoft.Extensions.Logging.LogLevel.Information"/> is ok for most uses.</para>
+ /// </summary>
+ public LogLevel MinimumLogLevel { internal get; set; } = LogLevel.Information;
+
+ /// <summary>
+ /// Overwrites the api version.
+ /// Defaults to 10.
+ /// </summary>
+ public string ApiVersion { internal get; set; } = "10";
+
+ /// <summary>
+ /// <para>Sets whether to rely on Discord for NTP (Network Time Protocol) synchronization with the "X-Ratelimit-Reset-After" header.</para>
+ /// <para>If the system clock is unsynced, setting this to true will ensure ratelimits are synced with Discord and reduce the risk of hitting one.</para>
+ /// <para>This should only be set to false if the system clock is synced with NTP.</para>
+ /// <para>Defaults to true.</para>
+ /// </summary>
+ public bool UseRelativeRatelimit { internal get; set; } = true;
+
+ /// <summary>
+ /// <para>Allows you to overwrite the time format used by the internal debug logger.</para>
+ /// <para>Only applicable when <see cref="LoggerFactory"/> is set left at default value. Defaults to ISO 8601-like format.</para>
+ /// </summary>
+ public string LogTimestampFormat { internal get; set; } = "yyyy-MM-dd HH:mm:ss zzz";
+
+ /// <summary>
+ /// <para>Sets the member count threshold at which guilds are considered large.</para>
+ /// <para>Defaults to 250.</para>
+ /// </summary>
+ public int LargeThreshold { internal get; set; } = 250;
+
+ /// <summary>
+ /// <para>Sets whether to automatically reconnect in case a connection is lost.</para>
+ /// <para>Defaults to true.</para>
+ /// </summary>
+ public bool AutoReconnect { internal get; set; } = true;
+
+ /// <summary>
+ /// <para>Sets the ID of the shard to connect to.</para>
+ /// <para>If not sharding, or sharding automatically, this value should be left with the default value of 0.</para>
+ /// </summary>
+ public int ShardId { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets the total number of shards the bot is on. If not sharding, this value should be left with a default value of 1.</para>
+ /// <para>If sharding automatically, this value will indicate how many shards to boot. If left default for automatic sharding, the client will determine the shard count automatically.</para>
+ /// </summary>
+ public int ShardCount { internal get; set; } = 1;
+
+ /// <summary>
+ /// <para>Sets the level of compression for WebSocket traffic.</para>
+ /// <para>Disabling this option will increase the amount of traffic sent via WebSocket. Setting <see cref="GatewayCompressionLevel.Payload"/> will enable compression for READY and GUILD_CREATE payloads. Setting <see cref="GatewayCompressionLevel.Stream"/> will enable compression for the entire WebSocket stream, drastically reducing amount of traffic.</para>
+ /// <para>Defaults to <see cref="GatewayCompressionLevel.Stream"/>.</para>
+ /// </summary>
+ public GatewayCompressionLevel GatewayCompressionLevel { internal get; set; } = GatewayCompressionLevel.Stream;
+
+ /// <summary>
+ /// <para>Sets the size of the global message cache.</para>
+ /// <para>Setting this to 0 will disable message caching entirely. Defaults to 1024.</para>
+ /// </summary>
+ public int MessageCacheSize { internal get; set; } = 1024;
+
+ /// <summary>
+ /// <para>Sets the proxy to use for HTTP and WebSocket connections to Discord.</para>
+ /// <para>Defaults to null.</para>
+ /// </summary>
+ public IWebProxy Proxy { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets the timeout for HTTP requests.</para>
+ /// <para>Set to <see cref="System.Threading.Timeout.InfiniteTimeSpan"/> to disable timeouts.</para>
+ /// <para>Defaults to 20 seconds.</para>
+ /// </summary>
+ public TimeSpan HttpTimeout { internal get; set; } = TimeSpan.FromSeconds(20);
+
+ /// <summary>
+ /// <para>Defines that the client should attempt to reconnect indefinitely.</para>
+ /// <para>This is typically a very bad idea to set to <c>true</c>, as it will swallow all connection errors.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool ReconnectIndefinitely { internal get; set; }
+
+ /// <summary>
+ /// Sets whether the client should attempt to cache members if exclusively using unprivileged intents.
+ /// <para>
+ /// This will only take effect if there are no <see cref="DiscordIntents.GuildMembers"/> or <see cref="DiscordIntents.GuildPresences"/>
+ /// intents specified. Otherwise, this will always be overwritten to true.
+ /// </para>
+ /// <para>Defaults to true.</para>
+ /// </summary>
+ public bool AlwaysCacheMembers { internal get; set; } = true;
+
+ /// <summary>
+ /// <para>Sets the gateway intents for this client.</para>
+ /// <para>If set, the client will only receive events that they specify with intents.</para>
+ /// <para>Defaults to <see cref="DiscordIntents.AllUnprivileged"/>.</para>
+ /// </summary>
+ public DiscordIntents Intents { internal get; set; } = DiscordIntents.AllUnprivileged;
+
+ /// <summary>
+ /// <para>Sets the factory method used to create instances of WebSocket clients.</para>
+ /// <para>Use <see cref="DisCatSharp.Net.WebSocket.WebSocketClient.CreateNew(IWebProxy, IServiceProvider)"/> and equivalents on other implementations to switch out client implementations.</para>
+ /// <para>Defaults to <see cref="DisCatSharp.Net.WebSocket.WebSocketClient.CreateNew(IWebProxy, IServiceProvider)"/>.</para>
+ /// </summary>
+ public WebSocketClientFactoryDelegate WebSocketClientFactory
{
- if (value == null)
- throw new InvalidOperationException("You need to supply a valid WebSocket client factory method.");
-
- this._webSocketClientFactory = value;
+ internal get => this._webSocketClientFactory;
+ set
+ {
+ if (value == null)
+ throw new InvalidOperationException("You need to supply a valid WebSocket client factory method.");
+
+ this._webSocketClientFactory = value;
+ }
+ }
+ private WebSocketClientFactoryDelegate _webSocketClientFactory = WebSocketClient.CreateNew;
+
+ /// <summary>
+ /// <para>Sets the factory method used to create instances of UDP clients.</para>
+ /// <para>Use <see cref="DcsUdpClient.CreateNew"/> and equivalents on other implementations to switch out client implementations.</para>
+ /// <para>Defaults to <see cref="DcsUdpClient.CreateNew"/>.</para>
+ /// </summary>
+ public UdpClientFactoryDelegate UdpClientFactory
+ {
+ internal get => this._udpClientFactory;
+ set => this._udpClientFactory = value ?? throw new InvalidOperationException("You need to supply a valid UDP client factory method.");
+ }
+ private UdpClientFactoryDelegate _udpClientFactory = DcsUdpClient.CreateNew;
+
+ /// <summary>
+ /// <para>Sets the logger implementation to use.</para>
+ /// <para>To create your own logger, implement the <see cref="Microsoft.Extensions.Logging.ILoggerFactory"/> instance.</para>
+ /// <para>Defaults to built-in implementation.</para>
+ /// </summary>
+ public ILoggerFactory LoggerFactory { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets if the bot's status should show the mobile icon.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool MobileStatus { internal get; set; }
+
+ /// <summary>
+ /// <para>Whether to use canary. <see cref="UsePtb"/> has to be false.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool UseCanary { internal get; set; }
+
+ /// <summary>
+ /// <para>Whether to use ptb. <see cref="UseCanary"/> has to be false.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool UsePtb { internal get; set; }
+
+ /// <summary>
+ /// <para>Refresh full guild channel cache.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool AutoRefreshChannelCache { internal get; set; }
+
+ /// <summary>
+ /// <para>Do not use, this is meant for DisCatSharp Devs.</para>
+ /// <para>Defaults to null.</para>
+ /// </summary>
+ public string Override { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets the service provider.</para>
+ /// <para>This allows passing data around without resorting to static members.</para>
+ /// <para>Defaults to an empty service provider.</para>
+ /// </summary>
+ public IServiceProvider ServiceProvider { internal get; set; } = new ServiceCollection().BuildServiceProvider(true);
+
+ /// <summary>
+ /// Creates a new configuration with default values.
+ /// </summary>
+ public DiscordConfiguration()
+ { }
+
+ /// <summary>
+ /// Utilized via Dependency Injection Pipeline
+ /// </summary>
+ /// <param name="provider"></param>
+ [ActivatorUtilitiesConstructor]
+ public DiscordConfiguration(IServiceProvider provider)
+ {
+ this.ServiceProvider = provider;
}
- }
- private WebSocketClientFactoryDelegate _webSocketClientFactory = WebSocketClient.CreateNew;
-
- /// <summary>
- /// <para>Sets the factory method used to create instances of UDP clients.</para>
- /// <para>Use <see cref="DcsUdpClient.CreateNew"/> and equivalents on other implementations to switch out client implementations.</para>
- /// <para>Defaults to <see cref="DcsUdpClient.CreateNew"/>.</para>
- /// </summary>
- public UdpClientFactoryDelegate UdpClientFactory
- {
- internal get => this._udpClientFactory;
- set => this._udpClientFactory = value ?? throw new InvalidOperationException("You need to supply a valid UDP client factory method.");
- }
- private UdpClientFactoryDelegate _udpClientFactory = DcsUdpClient.CreateNew;
-
- /// <summary>
- /// <para>Sets the logger implementation to use.</para>
- /// <para>To create your own logger, implement the <see cref="Microsoft.Extensions.Logging.ILoggerFactory"/> instance.</para>
- /// <para>Defaults to built-in implementation.</para>
- /// </summary>
- public ILoggerFactory LoggerFactory { internal get; set; }
-
- /// <summary>
- /// <para>Sets if the bot's status should show the mobile icon.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool MobileStatus { internal get; set; }
-
- /// <summary>
- /// <para>Whether to use canary. <see cref="UsePtb"/> has to be false.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool UseCanary { internal get; set; }
-
- /// <summary>
- /// <para>Whether to use ptb. <see cref="UseCanary"/> has to be false.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool UsePtb { internal get; set; }
-
- /// <summary>
- /// <para>Refresh full guild channel cache.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool AutoRefreshChannelCache { internal get; set; }
-
- /// <summary>
- /// <para>Do not use, this is meant for DisCatSharp Devs.</para>
- /// <para>Defaults to null.</para>
- /// </summary>
- public string Override { internal get; set; }
-
- /// <summary>
- /// <para>Sets the service provider.</para>
- /// <para>This allows passing data around without resorting to static members.</para>
- /// <para>Defaults to an empty service provider.</para>
- /// </summary>
- public IServiceProvider ServiceProvider { internal get; set; } = new ServiceCollection().BuildServiceProvider(true);
-
- /// <summary>
- /// Creates a new configuration with default values.
- /// </summary>
- public DiscordConfiguration()
- { }
-
- /// <summary>
- /// Utilized via Dependency Injection Pipeline
- /// </summary>
- /// <param name="provider"></param>
- [ActivatorUtilitiesConstructor]
- public DiscordConfiguration(IServiceProvider provider)
- {
- this.ServiceProvider = provider;
- }
- /// <summary>
- /// Creates a clone of another discord configuration.
- /// </summary>
- /// <param name="other">Client configuration to clone.</param>
- public DiscordConfiguration(DiscordConfiguration other)
- {
- this.Token = other.Token;
- this.TokenType = other.TokenType;
- this.MinimumLogLevel = other.MinimumLogLevel;
- this.UseRelativeRatelimit = other.UseRelativeRatelimit;
- this.LogTimestampFormat = other.LogTimestampFormat;
- this.LargeThreshold = other.LargeThreshold;
- this.AutoReconnect = other.AutoReconnect;
- this.ShardId = other.ShardId;
- this.ShardCount = other.ShardCount;
- this.GatewayCompressionLevel = other.GatewayCompressionLevel;
- this.MessageCacheSize = other.MessageCacheSize;
- this.WebSocketClientFactory = other.WebSocketClientFactory;
- this.UdpClientFactory = other.UdpClientFactory;
- this.Proxy = other.Proxy;
- this.HttpTimeout = other.HttpTimeout;
- this.ReconnectIndefinitely = other.ReconnectIndefinitely;
- this.Intents = other.Intents;
- this.LoggerFactory = other.LoggerFactory;
- this.MobileStatus = other.MobileStatus;
- this.UseCanary = other.UseCanary;
- this.UsePtb = other.UsePtb;
- this.AutoRefreshChannelCache = other.AutoRefreshChannelCache;
- this.ApiVersion = other.ApiVersion;
- this.ServiceProvider = other.ServiceProvider;
- this.Override = other.Override;
+ /// <summary>
+ /// Creates a clone of another discord configuration.
+ /// </summary>
+ /// <param name="other">Client configuration to clone.</param>
+ public DiscordConfiguration(DiscordConfiguration other)
+ {
+ this.Token = other.Token;
+ this.TokenType = other.TokenType;
+ this.MinimumLogLevel = other.MinimumLogLevel;
+ this.UseRelativeRatelimit = other.UseRelativeRatelimit;
+ this.LogTimestampFormat = other.LogTimestampFormat;
+ this.LargeThreshold = other.LargeThreshold;
+ this.AutoReconnect = other.AutoReconnect;
+ this.ShardId = other.ShardId;
+ this.ShardCount = other.ShardCount;
+ this.GatewayCompressionLevel = other.GatewayCompressionLevel;
+ this.MessageCacheSize = other.MessageCacheSize;
+ this.WebSocketClientFactory = other.WebSocketClientFactory;
+ this.UdpClientFactory = other.UdpClientFactory;
+ this.Proxy = other.Proxy;
+ this.HttpTimeout = other.HttpTimeout;
+ this.ReconnectIndefinitely = other.ReconnectIndefinitely;
+ this.Intents = other.Intents;
+ this.LoggerFactory = other.LoggerFactory;
+ this.MobileStatus = other.MobileStatus;
+ this.UseCanary = other.UseCanary;
+ this.UsePtb = other.UsePtb;
+ this.AutoRefreshChannelCache = other.AutoRefreshChannelCache;
+ this.ApiVersion = other.ApiVersion;
+ this.ServiceProvider = other.ServiceProvider;
+ this.Override = other.Override;
+ }
}
}
diff --git a/DisCatSharp/DiscordEvent.cs b/DisCatSharp/DiscordEvent.cs
index fca5246a8..c895fefc4 100644
--- a/DisCatSharp/DiscordEvent.cs
+++ b/DisCatSharp/DiscordEvent.cs
@@ -1,142 +1,143 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
#nullable enable
using System;
-namespace DisCatSharp;
-
-/// <summary>
-/// Methods marked with this attribute will be registered as event handling methods
-/// if the associated type / an associated instance is being registered.
-/// </summary>
-[AttributeUsage(AttributeTargets.Method)]
-public class EventAttribute : Attribute
+namespace DisCatSharp
{
- internal readonly string? EventName;
+ /// <summary>
+ /// Methods marked with this attribute will be registered as event handling methods
+ /// if the associated type / an associated instance is being registered.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method)]
+ public class EventAttribute : Attribute
+ {
+ internal readonly string? EventName;
- public EventAttribute() { }
+ public EventAttribute() { }
- /// <param name="evtn"><para>The name of the event.</para>
- /// <para>The attributed method's name will be used if null.</para></param>
- public EventAttribute(DiscordEvent evtn)
- {
- this.EventName = evtn.ToString();
+ /// <param name="evtn"><para>The name of the event.</para>
+ /// <para>The attributed method's name will be used if null.</para></param>
+ public EventAttribute(DiscordEvent evtn)
+ {
+ this.EventName = evtn.ToString();
+ }
}
-}
-/// <summary>
-/// Classes marked with this attribute will be considered for event handler registration from an assembly.
-/// </summary>
-[AttributeUsage(AttributeTargets.Class, Inherited = false)]
-public class EventHandlerAttribute : Attribute { }
+ /// <summary>
+ /// Classes marked with this attribute will be considered for event handler registration from an assembly.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class, Inherited = false)]
+ public class EventHandlerAttribute : Attribute { }
-/// <summary>
-/// All events available in <see cref="DiscordClient"/> for use with <see cref="EventAttribute"/>.
-/// </summary>
-public enum DiscordEvent
-{
- ApplicationCommandCreated,
- ApplicationCommandDeleted,
- ApplicationCommandPermissionsUpdated,
- ApplicationCommandUpdated,
- ChannelCreated,
- ChannelDeleted,
- ChannelPinsUpdated,
- ChannelUpdated,
- ClientErrored,
- ComponentInteractionCreated,
- ContextMenuInteractionCreated,
- DmChannelDeleted,
- EmbeddedActivityUpdated,
- GuildApplicationCommandCountUpdated,
- GuildAvailable,
- GuildBanAdded,
- GuildBanRemoved,
- GuildCreated,
- GuildDeleted,
- GuildDownloadCompleted,
- GuildEmojisUpdated,
- GuildIntegrationCreated,
- GuildIntegrationDeleted,
- GuildIntegrationsUpdated,
- GuildIntegrationUpdated,
- GuildMemberAdded,
- GuildMemberRemoved,
- GuildMembersChunked,
- GuildMemberTimeoutAdded,
- GuildMemberTimeoutChanged,
- GuildMemberTimeoutRemoved,
- GuildMemberUpdated,
- GuildRoleCreated,
- GuildRoleDeleted,
- GuildRoleUpdated,
- GuildScheduledEventCreated,
- GuildScheduledEventDeleted,
- GuildScheduledEventUpdated,
- GuildScheduledEventUserAdded,
- GuildScheduledEventUserRemoved,
- GuildStickersUpdated,
- GuildUnavailable,
- GuildUpdated,
- Heartbeated,
- InteractionCreated,
- InviteCreated,
- InviteDeleted,
- MessageAcknowledged,
- MessageCreated,
- MessageDeleted,
- MessageReactionAdded,
- MessageReactionRemoved,
- MessageReactionRemovedEmoji,
- MessageReactionsCleared,
- MessagesBulkDeleted,
- MessageUpdated,
- PayloadReceived,
- PresenceUpdated,
- RateLimitHit,
- Ready,
- Resumed,
- SocketClosed,
- SocketErrored,
- SocketOpened,
- StageInstanceCreated,
- StageInstanceDeleted,
- StageInstanceUpdated,
- ThreadCreated,
- ThreadDeleted,
- ThreadListSynced,
- ThreadMembersUpdated,
- ThreadMemberUpdated,
- ThreadUpdated,
- TypingStarted,
- UnknownEvent,
- UserSettingsUpdated,
- UserUpdated,
- VoiceServerUpdated,
- VoiceStateUpdated,
- WebhooksUpdated,
- Zombied,
- GuildJoined = GuildCreated,
- JoinedGuild = GuildCreated,
+ /// <summary>
+ /// All events available in <see cref="DiscordClient"/> for use with <see cref="EventAttribute"/>.
+ /// </summary>
+ public enum DiscordEvent
+ {
+ ApplicationCommandCreated,
+ ApplicationCommandDeleted,
+ ApplicationCommandPermissionsUpdated,
+ ApplicationCommandUpdated,
+ ChannelCreated,
+ ChannelDeleted,
+ ChannelPinsUpdated,
+ ChannelUpdated,
+ ClientErrored,
+ ComponentInteractionCreated,
+ ContextMenuInteractionCreated,
+ DmChannelDeleted,
+ EmbeddedActivityUpdated,
+ GuildApplicationCommandCountUpdated,
+ GuildAvailable,
+ GuildBanAdded,
+ GuildBanRemoved,
+ GuildCreated,
+ GuildDeleted,
+ GuildDownloadCompleted,
+ GuildEmojisUpdated,
+ GuildIntegrationCreated,
+ GuildIntegrationDeleted,
+ GuildIntegrationsUpdated,
+ GuildIntegrationUpdated,
+ GuildMemberAdded,
+ GuildMemberRemoved,
+ GuildMembersChunked,
+ GuildMemberTimeoutAdded,
+ GuildMemberTimeoutChanged,
+ GuildMemberTimeoutRemoved,
+ GuildMemberUpdated,
+ GuildRoleCreated,
+ GuildRoleDeleted,
+ GuildRoleUpdated,
+ GuildScheduledEventCreated,
+ GuildScheduledEventDeleted,
+ GuildScheduledEventUpdated,
+ GuildScheduledEventUserAdded,
+ GuildScheduledEventUserRemoved,
+ GuildStickersUpdated,
+ GuildUnavailable,
+ GuildUpdated,
+ Heartbeated,
+ InteractionCreated,
+ InviteCreated,
+ InviteDeleted,
+ MessageAcknowledged,
+ MessageCreated,
+ MessageDeleted,
+ MessageReactionAdded,
+ MessageReactionRemoved,
+ MessageReactionRemovedEmoji,
+ MessageReactionsCleared,
+ MessagesBulkDeleted,
+ MessageUpdated,
+ PayloadReceived,
+ PresenceUpdated,
+ RateLimitHit,
+ Ready,
+ Resumed,
+ SocketClosed,
+ SocketErrored,
+ SocketOpened,
+ StageInstanceCreated,
+ StageInstanceDeleted,
+ StageInstanceUpdated,
+ ThreadCreated,
+ ThreadDeleted,
+ ThreadListSynced,
+ ThreadMembersUpdated,
+ ThreadMemberUpdated,
+ ThreadUpdated,
+ TypingStarted,
+ UnknownEvent,
+ UserSettingsUpdated,
+ UserUpdated,
+ VoiceServerUpdated,
+ VoiceStateUpdated,
+ WebhooksUpdated,
+ Zombied,
+ GuildJoined = GuildCreated,
+ JoinedGuild = GuildCreated,
+ }
}
diff --git a/DisCatSharp/DiscordIntents.cs b/DisCatSharp/DiscordIntents.cs
index ffead6a31..0b11bc251 100644
--- a/DisCatSharp/DiscordIntents.cs
+++ b/DisCatSharp/DiscordIntents.cs
@@ -1,220 +1,221 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents a discord intent extensions.
-/// </summary>
-public static class DiscordIntentExtensions
-{
- /// <summary>
- /// Calculates whether these intents have a certain intent.
- /// </summary>
- /// <param name="intents">The base intents.</param>
- /// <param name="search">The intents to search for.</param>
- /// <returns></returns>
- public static bool HasIntent(this DiscordIntents intents, DiscordIntents search)
- => (intents & search) == search;
-
- /// <summary>
- /// Adds an intent to these intents.
- /// </summary>
- /// <param name="intents">The base intents.</param>
- /// <param name="toAdd">The intents to add.</param>
- /// <returns></returns>
- public static DiscordIntents AddIntent(this DiscordIntents intents, DiscordIntents toAdd)
- => intents |= toAdd;
-
- /// <summary>
- /// Removes an intent from these intents.
- /// </summary>
- /// <param name="intents">The base intents.</param>
- /// <param name="toRemove">The intents to remove.</param>
- /// <returns></returns>
- public static DiscordIntents RemoveIntent(this DiscordIntents intents, DiscordIntents toRemove)
- => intents &= ~toRemove;
-
- /// <summary>
- /// Whether it has all privileged intents.
- /// </summary>
- /// <param name="intents">The intents.</param>
- internal static bool HasAllPrivilegedIntents(this DiscordIntents intents)
- => intents.HasIntent(DiscordIntents.GuildMembers | DiscordIntents.GuildPresences | DiscordIntents.MessageContent);
-
-
- /// <summary>
- /// Whether it has all v9 privileged intents.
- /// </summary>
- /// <param name="intents">The intents.</param>
- internal static bool HasAllV9PrivilegedIntents(this DiscordIntents intents)
- => intents.HasIntent(DiscordIntents.GuildMembers | DiscordIntents.GuildPresences);
-}
-
-/// <summary>
-/// Represents gateway intents to be specified for connecting to Discord.
-/// </summary>
-[Flags]
-public enum DiscordIntents
+namespace DisCatSharp
{
/// <summary>
- /// Whether to include general guild events. Note that you may receive empty message contents if you don't have the message content intent.
- /// <para>These include <see cref="DiscordClient.GuildCreated"/>, <see cref="DiscordClient.GuildDeleted"/>, <see cref="DiscordClient.GuildAvailable"/>, <see cref="DiscordClient.GuildDownloadCompleted"/>,</para>
- /// <para><see cref="DiscordClient.GuildRoleCreated"/>, <see cref="DiscordClient.GuildRoleUpdated"/>, <see cref="DiscordClient.GuildRoleDeleted"/>,</para>
- /// <para><see cref="DiscordClient.ChannelCreated"/>, <see cref="DiscordClient.ChannelUpdated"/>, <see cref="DiscordClient.ChannelDeleted"/>, <see cref="DiscordClient.ChannelPinsUpdated"/>,</para>
- /// <para><see cref="DiscordClient.StageInstanceCreated"/>, <see cref="DiscordClient.StageInstanceUpdated"/>, <see cref="DiscordClient.StageInstanceDeleted"/>,</para>
- /// <para><see cref="DiscordClient.ThreadCreated"/>, <see cref="DiscordClient.ThreadUpdated"/>, <see cref="DiscordClient.ThreadDeleted"/>,</para>
- /// <para><see cref="DiscordClient.ThreadListSynced"/>, <see cref="DiscordClient.ThreadMemberUpdated"/> and <see cref="DiscordClient.ThreadMembersUpdated"/>.</para>
- /// </summary>
- Guilds = 1 << 0,
-
- /// <summary>
- /// Whether to include guild member events.
- /// <para>These include <see cref="DiscordClient.GuildMemberAdded"/>, <see cref="DiscordClient.GuildMemberUpdated"/>, <see cref="DiscordClient.GuildMemberRemoved"/> and <see cref="DiscordClient.ThreadMembersUpdated"/>.</para>
- /// <para>This is a privileged intent, and must be enabled on the bot's developer page.</para>
- /// </summary>
- GuildMembers = 1 << 1,
-
- /// <summary>
- /// Whether to include guild ban events.
- /// <para>These include <see cref="DiscordClient.GuildBanAdded"/> and <see cref="DiscordClient.GuildBanRemoved"/>.</para>
- /// </summary>
- GuildBans = 1 << 2,
-
- /// <summary>
- /// Whether to include guild emoji and sticker events.
- /// <para>This includes <see cref="DiscordClient.GuildEmojisUpdated"/> and <see cref="DiscordClient.GuildStickersUpdated"/>.</para>
- /// </summary>
- GuildEmojisAndStickers = 1 << 3,
-
- /// <summary>
- /// Whether to include guild integration events.
- /// <para>This includes <see cref="DiscordClient.GuildIntegrationsUpdated"/>.</para>
- /// </summary>
- GuildIntegrations = 1 << 4,
-
- /// <summary>
- /// Whether to include guild webhook events.
- /// <para>This includes <see cref="DiscordClient.WebhooksUpdated"/>.</para>
- /// </summary>
- GuildWebhooks = 1 << 5,
-
- /// <summary>
- /// Whether to include guild invite events.
- /// <para>These include <see cref="DiscordClient.InviteCreated"/> and <see cref="DiscordClient.InviteDeleted"/>.</para>
- /// </summary>
- GuildInvites = 1 << 6,
-
- /// <summary>
- /// Whether to include guild voice state events.
- /// <para>This includes <see cref="DiscordClient.VoiceStateUpdated"/>.</para>
- /// </summary>
- GuildVoiceStates = 1 << 7,
-
- /// <summary>
- /// Whether to include guild presence events.
- /// <para>This includes <see cref="DiscordClient.PresenceUpdated"/>.</para>
- /// <para>This is a privileged intent, and must be enabled on the bot's developer page.</para>
- /// </summary>
- GuildPresences = 1 << 8,
-
- /// <summary>
- /// Whether to include guild message events. Note that you may receive empty contents if you don't have the message content intent.
- /// You can enable it in the developer portal. If you have a verified bot, you might need to apply for the intent.
- /// <para>These include <see cref="DiscordClient.MessageCreated"/>, <see cref="DiscordClient.MessageUpdated"/>, and <see cref="DiscordClient.MessageDeleted"/>.</para>
- /// </summary>
- GuildMessages = 1 << 9,
-
- /// <summary>
- /// Whether to include guild reaction events.
- /// <para>These include <see cref="DiscordClient.MessageReactionAdded"/>, <see cref="DiscordClient.MessageReactionRemoved"/>, <see cref="DiscordClient.MessageReactionsCleared"/></para>
- /// <para>and <see cref="DiscordClient.MessageReactionRemovedEmoji"/>.</para>
- /// </summary>
- GuildMessageReactions = 1 << 10,
-
- /// <summary>
- /// Whether to include guild typing events.
- /// <para>These include <see cref="DiscordClient.TypingStarted"/>.</para>
- /// </summary>
- GuildMessageTyping = 1 << 11,
-
- /// <summary>
- /// Whether to include general direct message events.
- /// <para>These include <see cref="DiscordClient.ChannelCreated"/>, <see cref="DiscordClient.MessageCreated"/>, <see cref="DiscordClient.MessageUpdated"/>,</para>
- /// <para><see cref="DiscordClient.MessageDeleted"/> and <see cref="DiscordClient.ChannelPinsUpdated"/>.</para>
- /// <para>These events only fire for DM channels.</para>
- /// </summary>
- DirectMessages = 1 << 12,
-
- /// <summary>
- /// Whether to include direct message reaction events.
- /// <para>These include <see cref="DiscordClient.MessageReactionAdded"/>, <see cref="DiscordClient.MessageReactionRemoved"/>,</para>
- /// <para><see cref="DiscordClient.MessageReactionsCleared"/> and <see cref="DiscordClient.MessageReactionRemovedEmoji"/>.</para>
- /// <para>These events only fire for DM channels.</para>
- /// </summary>
- DirectMessageReactions = 1 << 13,
-
- /// <summary>
- /// Whether to include direct message typing events.
- /// <para>This includes <see cref="DiscordClient.TypingStarted"/>.</para>
- /// <para>This event only fires for DM channels.</para>
- /// </summary>
- DirectMessageTyping = 1 << 14,
-
- /// <summary>
- /// Whether to include the content of guild messages.
- /// See https://support-dev.discord.com/hc/en-us/articles/4404772028055-Message-Content-Privileged-Intent-for-Verified-Bots for more informations.
- /// </summary>
- MessageContent = 1 << 15,
-
- /// <summary>
- /// Whether to include guild scheduled event events.
- /// <para>These include <see cref="DiscordClient.GuildScheduledEventCreated"/>, <see cref="DiscordClient.GuildScheduledEventUpdated"/>, <see cref="DiscordClient.GuildScheduledEventDeleted"/>,</para>
- /// <para><see cref="DiscordClient.GuildScheduledEventUserAdded"/> and <see cref="DiscordClient.GuildScheduledEventUserRemoved"/>.</para>
- /// The events <see cref="DiscordClient.GuildScheduledEventUserAdded"/> and <see cref="DiscordClient.GuildScheduledEventUserRemoved"/> are in experiment and not officially supported.
- /// </summary>
- GuildScheduledEvents = 1 << 16,
-
- /// <summary>
- /// Includes all unprivileged intents.
- /// <para>These are all intents excluding <see cref="GuildMembers"/> and <see cref="GuildPresences"/>.</para>
- /// <para>The <see cref="DiscordIntents.GuildMessages"/> will be excluded as of April 2022.</para>
- /// </summary>
- AllUnprivileged = Guilds | GuildBans | GuildEmojisAndStickers | GuildIntegrations | GuildWebhooks | GuildInvites | GuildVoiceStates | GuildMessages |
- GuildMessageReactions | GuildMessageTyping | DirectMessages | DirectMessageReactions | DirectMessageTyping | GuildScheduledEvents,
-
- /// <summary>
- /// Includes all intents.
- /// <para>The <see cref="GuildMembers"/>, <see cref="GuildPresences"/> and <see cref="MessageContent"/> intents are privileged, and must be enabled on the bot's developer page.</para>
- /// <para>The <see cref="MessageContent"/> exist only in v10.</para>
- /// </summary>
- All = AllUnprivileged | GuildMembers | GuildPresences | MessageContent,
-
- /// <summary>
- /// Includes all intents.
- /// <para>The <see cref="GuildMembers"/> and <see cref="GuildPresences"/> intents are privileged, and must be enabled on the bot's developer page.</para>
- /// <para>The <see cref="MessageContent"/> exist only in v10 and is removed here.</para>
- /// </summary>
- AllV9Less = AllUnprivileged | GuildMembers | GuildPresences
+ /// Represents a discord intent extensions.
+ /// </summary>
+ public static class DiscordIntentExtensions
+ {
+ /// <summary>
+ /// Calculates whether these intents have a certain intent.
+ /// </summary>
+ /// <param name="intents">The base intents.</param>
+ /// <param name="search">The intents to search for.</param>
+ /// <returns></returns>
+ public static bool HasIntent(this DiscordIntents intents, DiscordIntents search)
+ => (intents & search) == search;
+
+ /// <summary>
+ /// Adds an intent to these intents.
+ /// </summary>
+ /// <param name="intents">The base intents.</param>
+ /// <param name="toAdd">The intents to add.</param>
+ /// <returns></returns>
+ public static DiscordIntents AddIntent(this DiscordIntents intents, DiscordIntents toAdd)
+ => intents |= toAdd;
+
+ /// <summary>
+ /// Removes an intent from these intents.
+ /// </summary>
+ /// <param name="intents">The base intents.</param>
+ /// <param name="toRemove">The intents to remove.</param>
+ /// <returns></returns>
+ public static DiscordIntents RemoveIntent(this DiscordIntents intents, DiscordIntents toRemove)
+ => intents &= ~toRemove;
+
+ /// <summary>
+ /// Whether it has all privileged intents.
+ /// </summary>
+ /// <param name="intents">The intents.</param>
+ internal static bool HasAllPrivilegedIntents(this DiscordIntents intents)
+ => intents.HasIntent(DiscordIntents.GuildMembers | DiscordIntents.GuildPresences | DiscordIntents.MessageContent);
+
+
+ /// <summary>
+ /// Whether it has all v9 privileged intents.
+ /// </summary>
+ /// <param name="intents">The intents.</param>
+ internal static bool HasAllV9PrivilegedIntents(this DiscordIntents intents)
+ => intents.HasIntent(DiscordIntents.GuildMembers | DiscordIntents.GuildPresences);
+ }
+
+ /// <summary>
+ /// Represents gateway intents to be specified for connecting to Discord.
+ /// </summary>
+ [Flags]
+ public enum DiscordIntents
+ {
+ /// <summary>
+ /// Whether to include general guild events. Note that you may receive empty message contents if you don't have the message content intent.
+ /// <para>These include <see cref="DiscordClient.GuildCreated"/>, <see cref="DiscordClient.GuildDeleted"/>, <see cref="DiscordClient.GuildAvailable"/>, <see cref="DiscordClient.GuildDownloadCompleted"/>,</para>
+ /// <para><see cref="DiscordClient.GuildRoleCreated"/>, <see cref="DiscordClient.GuildRoleUpdated"/>, <see cref="DiscordClient.GuildRoleDeleted"/>,</para>
+ /// <para><see cref="DiscordClient.ChannelCreated"/>, <see cref="DiscordClient.ChannelUpdated"/>, <see cref="DiscordClient.ChannelDeleted"/>, <see cref="DiscordClient.ChannelPinsUpdated"/>,</para>
+ /// <para><see cref="DiscordClient.StageInstanceCreated"/>, <see cref="DiscordClient.StageInstanceUpdated"/>, <see cref="DiscordClient.StageInstanceDeleted"/>,</para>
+ /// <para><see cref="DiscordClient.ThreadCreated"/>, <see cref="DiscordClient.ThreadUpdated"/>, <see cref="DiscordClient.ThreadDeleted"/>,</para>
+ /// <para><see cref="DiscordClient.ThreadListSynced"/>, <see cref="DiscordClient.ThreadMemberUpdated"/> and <see cref="DiscordClient.ThreadMembersUpdated"/>.</para>
+ /// </summary>
+ Guilds = 1 << 0,
+
+ /// <summary>
+ /// Whether to include guild member events.
+ /// <para>These include <see cref="DiscordClient.GuildMemberAdded"/>, <see cref="DiscordClient.GuildMemberUpdated"/>, <see cref="DiscordClient.GuildMemberRemoved"/> and <see cref="DiscordClient.ThreadMembersUpdated"/>.</para>
+ /// <para>This is a privileged intent, and must be enabled on the bot's developer page.</para>
+ /// </summary>
+ GuildMembers = 1 << 1,
+
+ /// <summary>
+ /// Whether to include guild ban events.
+ /// <para>These include <see cref="DiscordClient.GuildBanAdded"/> and <see cref="DiscordClient.GuildBanRemoved"/>.</para>
+ /// </summary>
+ GuildBans = 1 << 2,
+
+ /// <summary>
+ /// Whether to include guild emoji and sticker events.
+ /// <para>This includes <see cref="DiscordClient.GuildEmojisUpdated"/> and <see cref="DiscordClient.GuildStickersUpdated"/>.</para>
+ /// </summary>
+ GuildEmojisAndStickers = 1 << 3,
+
+ /// <summary>
+ /// Whether to include guild integration events.
+ /// <para>This includes <see cref="DiscordClient.GuildIntegrationsUpdated"/>.</para>
+ /// </summary>
+ GuildIntegrations = 1 << 4,
+
+ /// <summary>
+ /// Whether to include guild webhook events.
+ /// <para>This includes <see cref="DiscordClient.WebhooksUpdated"/>.</para>
+ /// </summary>
+ GuildWebhooks = 1 << 5,
+
+ /// <summary>
+ /// Whether to include guild invite events.
+ /// <para>These include <see cref="DiscordClient.InviteCreated"/> and <see cref="DiscordClient.InviteDeleted"/>.</para>
+ /// </summary>
+ GuildInvites = 1 << 6,
+
+ /// <summary>
+ /// Whether to include guild voice state events.
+ /// <para>This includes <see cref="DiscordClient.VoiceStateUpdated"/>.</para>
+ /// </summary>
+ GuildVoiceStates = 1 << 7,
+
+ /// <summary>
+ /// Whether to include guild presence events.
+ /// <para>This includes <see cref="DiscordClient.PresenceUpdated"/>.</para>
+ /// <para>This is a privileged intent, and must be enabled on the bot's developer page.</para>
+ /// </summary>
+ GuildPresences = 1 << 8,
+
+ /// <summary>
+ /// Whether to include guild message events. Note that you may receive empty contents if you don't have the message content intent.
+ /// You can enable it in the developer portal. If you have a verified bot, you might need to apply for the intent.
+ /// <para>These include <see cref="DiscordClient.MessageCreated"/>, <see cref="DiscordClient.MessageUpdated"/>, and <see cref="DiscordClient.MessageDeleted"/>.</para>
+ /// </summary>
+ GuildMessages = 1 << 9,
+
+ /// <summary>
+ /// Whether to include guild reaction events.
+ /// <para>These include <see cref="DiscordClient.MessageReactionAdded"/>, <see cref="DiscordClient.MessageReactionRemoved"/>, <see cref="DiscordClient.MessageReactionsCleared"/></para>
+ /// <para>and <see cref="DiscordClient.MessageReactionRemovedEmoji"/>.</para>
+ /// </summary>
+ GuildMessageReactions = 1 << 10,
+
+ /// <summary>
+ /// Whether to include guild typing events.
+ /// <para>These include <see cref="DiscordClient.TypingStarted"/>.</para>
+ /// </summary>
+ GuildMessageTyping = 1 << 11,
+
+ /// <summary>
+ /// Whether to include general direct message events.
+ /// <para>These include <see cref="DiscordClient.ChannelCreated"/>, <see cref="DiscordClient.MessageCreated"/>, <see cref="DiscordClient.MessageUpdated"/>,</para>
+ /// <para><see cref="DiscordClient.MessageDeleted"/> and <see cref="DiscordClient.ChannelPinsUpdated"/>.</para>
+ /// <para>These events only fire for DM channels.</para>
+ /// </summary>
+ DirectMessages = 1 << 12,
+
+ /// <summary>
+ /// Whether to include direct message reaction events.
+ /// <para>These include <see cref="DiscordClient.MessageReactionAdded"/>, <see cref="DiscordClient.MessageReactionRemoved"/>,</para>
+ /// <para><see cref="DiscordClient.MessageReactionsCleared"/> and <see cref="DiscordClient.MessageReactionRemovedEmoji"/>.</para>
+ /// <para>These events only fire for DM channels.</para>
+ /// </summary>
+ DirectMessageReactions = 1 << 13,
+
+ /// <summary>
+ /// Whether to include direct message typing events.
+ /// <para>This includes <see cref="DiscordClient.TypingStarted"/>.</para>
+ /// <para>This event only fires for DM channels.</para>
+ /// </summary>
+ DirectMessageTyping = 1 << 14,
+
+ /// <summary>
+ /// Whether to include the content of guild messages.
+ /// See https://support-dev.discord.com/hc/en-us/articles/4404772028055-Message-Content-Privileged-Intent-for-Verified-Bots for more informations.
+ /// </summary>
+ MessageContent = 1 << 15,
+
+ /// <summary>
+ /// Whether to include guild scheduled event events.
+ /// <para>These include <see cref="DiscordClient.GuildScheduledEventCreated"/>, <see cref="DiscordClient.GuildScheduledEventUpdated"/>, <see cref="DiscordClient.GuildScheduledEventDeleted"/>,</para>
+ /// <para><see cref="DiscordClient.GuildScheduledEventUserAdded"/> and <see cref="DiscordClient.GuildScheduledEventUserRemoved"/>.</para>
+ /// The events <see cref="DiscordClient.GuildScheduledEventUserAdded"/> and <see cref="DiscordClient.GuildScheduledEventUserRemoved"/> are in experiment and not officially supported.
+ /// </summary>
+ GuildScheduledEvents = 1 << 16,
+
+ /// <summary>
+ /// Includes all unprivileged intents.
+ /// <para>These are all intents excluding <see cref="GuildMembers"/> and <see cref="GuildPresences"/>.</para>
+ /// <para>The <see cref="DiscordIntents.GuildMessages"/> will be excluded as of April 2022.</para>
+ /// </summary>
+ AllUnprivileged = Guilds | GuildBans | GuildEmojisAndStickers | GuildIntegrations | GuildWebhooks | GuildInvites | GuildVoiceStates | GuildMessages |
+ GuildMessageReactions | GuildMessageTyping | DirectMessages | DirectMessageReactions | DirectMessageTyping | GuildScheduledEvents,
+
+ /// <summary>
+ /// Includes all intents.
+ /// <para>The <see cref="GuildMembers"/>, <see cref="GuildPresences"/> and <see cref="MessageContent"/> intents are privileged, and must be enabled on the bot's developer page.</para>
+ /// <para>The <see cref="MessageContent"/> exist only in v10.</para>
+ /// </summary>
+ All = AllUnprivileged | GuildMembers | GuildPresences | MessageContent,
+
+ /// <summary>
+ /// Includes all intents.
+ /// <para>The <see cref="GuildMembers"/> and <see cref="GuildPresences"/> intents are privileged, and must be enabled on the bot's developer page.</para>
+ /// <para>The <see cref="MessageContent"/> exist only in v10 and is removed here.</para>
+ /// </summary>
+ AllV9Less = AllUnprivileged | GuildMembers | GuildPresences
+ }
}
diff --git a/DisCatSharp/Entities/Application/DiscordApplication.cs b/DisCatSharp/Entities/Application/DiscordApplication.cs
index 50663a4f6..3e4f677c5 100644
--- a/DisCatSharp/Entities/Application/DiscordApplication.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplication.cs
@@ -1,435 +1,436 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an OAuth2 application.
-/// </summary>
-public sealed class DiscordApplication : DiscordMessageApplication, IEquatable<DiscordApplication>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the application's summary.
- /// </summary>
- public string Summary { get; internal set; }
-
- /// <summary>
- /// Gets the application's icon.
- /// </summary>
- public override string Icon
- => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.APP_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets the application's icon hash.
- /// </summary>
- public string IconHash { get; internal set; }
-
- /// <summary>
- /// Gets the application's allowed RPC origins.
- /// </summary>
- public IReadOnlyList<string> RpcOrigins { get; internal set; }
-
- /// <summary>
- /// Gets the application's flags.
- /// </summary>
- public ApplicationFlags Flags { get; internal set; }
-
- /// <summary>
- /// Gets the application's owners.
- /// </summary>
- public IEnumerable<DiscordUser> Owners { get; internal set; }
-
- /// <summary>
- /// Gets whether this application's bot user requires code grant.
- /// </summary>
- public bool? RequiresCodeGrant { get; internal set; }
-
- /// <summary>
- /// Gets whether this bot application is public.
- /// </summary>
- public bool? IsPublic { get; internal set; }
-
- /// <summary>
- /// Gets the terms of service url of the application.
- /// </summary>
- public string TermsOfServiceUrl { get; internal set; }
-
- /// <summary>
- /// Gets the privacy policy url of the application.
- /// </summary>
- public string PrivacyPolicyUrl { get; internal set; }
-
- /// <summary>
- /// Gets the team name of the application.
- /// </summary>
- public string TeamName { get; internal set; }
-
- /// <summary>
- /// Gets the hash of the application's cover image.
- /// </summary>
- public string CoverImageHash { get; internal set; }
-
- /// <summary>
- /// Gets this application's cover image URL.
- /// </summary>
- public override string CoverImageUrl
- => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.APP_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.CoverImageHash}.png?size=1024";
-
- /// <summary>
- /// Gets the team which owns this application.
- /// </summary>
- public DiscordTeam Team { get; internal set; }
-
- /// <summary>
- /// Gets the hex encoded key for verification in interactions and the GameSDK's GetTicket
- /// </summary>
- public string VerifyKey { get; internal set; }
-
- /// <summary>
- /// If this application is a game sold on Discord, this field will be the guild to which it has been linked
- /// </summary>
- public ulong? GuildId { get; internal set; }
-
- /// <summary>
- /// If this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists
- /// </summary>
- public ulong? PrimarySkuId { get; internal set; }
-
- /// <summary>
- /// If this application is a game sold on Discord, this field will be the URL slug that links to the store page
- /// </summary>
- public string Slug { get; internal set; }
-
- /// <summary>
- /// Gets or sets a list of <see cref="DiscordApplicationAsset"/>.
- /// </summary>
- private IReadOnlyList<DiscordApplicationAsset> _assets;
-
- /// <summary>
- /// A custom url for the Add To Server button.
- /// </summary>
- public string CustomInstallUrl { get; internal set; }
-
- /// <summary>
- /// Install parameters for adding the application to a guild.
- /// </summary>
- public DiscordApplicationInstallParams InstallParams { get; internal set; }
-
- /// <summary>
- /// The application tags.
- /// Not used atm.
- /// </summary>
- public IReadOnlyList<string> Tags { get; internal set; }
-
- /// <summary>
- /// Whether the application is hooked.
+ /// Represents an OAuth2 application.
/// </summary>
- public bool IsHook { get; internal set; }
-
- /// <summary>
- /// Gets the application type.
- /// Mostly null.
- /// </summary>
- public string Type { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordApplication"/> class.
- /// </summary>
- internal DiscordApplication()
- { }
-
- /// <summary>
- /// Gets the application's cover image URL, in requested format and size.
- /// </summary>
- /// <param name="fmt">Format of the image to get.</param>
- /// <param name="size">Maximum size of the cover image. Must be a power of two, minimum 16, maximum 2048.</param>
- /// <returns>URL of the application's cover image.</returns>
- public string GetAvatarUrl(ImageFormat fmt, ushort size = 1024)
+ public sealed class DiscordApplication : DiscordMessageApplication, IEquatable<DiscordApplication>
{
- if (fmt == ImageFormat.Unknown)
- throw new ArgumentException("You must specify valid image format.", nameof(fmt));
-
- if (size < 16 || size > 2048)
- throw new ArgumentOutOfRangeException(nameof(size));
-
- var log = Math.Log(size, 2);
- if (log < 4 || log > 11 || log % 1 != 0)
- throw new ArgumentOutOfRangeException(nameof(size));
-
- var sfmt = "";
- sfmt = fmt switch
+ /// <summary>
+ /// Gets the application's summary.
+ /// </summary>
+ public string Summary { get; internal set; }
+
+ /// <summary>
+ /// Gets the application's icon.
+ /// </summary>
+ public override string Icon
+ => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.APP_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets the application's icon hash.
+ /// </summary>
+ public string IconHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the application's allowed RPC origins.
+ /// </summary>
+ public IReadOnlyList<string> RpcOrigins { get; internal set; }
+
+ /// <summary>
+ /// Gets the application's flags.
+ /// </summary>
+ public ApplicationFlags Flags { get; internal set; }
+
+ /// <summary>
+ /// Gets the application's owners.
+ /// </summary>
+ public IEnumerable<DiscordUser> Owners { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this application's bot user requires code grant.
+ /// </summary>
+ public bool? RequiresCodeGrant { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this bot application is public.
+ /// </summary>
+ public bool? IsPublic { get; internal set; }
+
+ /// <summary>
+ /// Gets the terms of service url of the application.
+ /// </summary>
+ public string TermsOfServiceUrl { get; internal set; }
+
+ /// <summary>
+ /// Gets the privacy policy url of the application.
+ /// </summary>
+ public string PrivacyPolicyUrl { get; internal set; }
+
+ /// <summary>
+ /// Gets the team name of the application.
+ /// </summary>
+ public string TeamName { get; internal set; }
+
+ /// <summary>
+ /// Gets the hash of the application's cover image.
+ /// </summary>
+ public string CoverImageHash { get; internal set; }
+
+ /// <summary>
+ /// Gets this application's cover image URL.
+ /// </summary>
+ public override string CoverImageUrl
+ => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.APP_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.CoverImageHash}.png?size=1024";
+
+ /// <summary>
+ /// Gets the team which owns this application.
+ /// </summary>
+ public DiscordTeam Team { get; internal set; }
+
+ /// <summary>
+ /// Gets the hex encoded key for verification in interactions and the GameSDK's GetTicket
+ /// </summary>
+ public string VerifyKey { get; internal set; }
+
+ /// <summary>
+ /// If this application is a game sold on Discord, this field will be the guild to which it has been linked
+ /// </summary>
+ public ulong? GuildId { get; internal set; }
+
+ /// <summary>
+ /// If this application is a game sold on Discord, this field will be the id of the "Game SKU" that is created, if exists
+ /// </summary>
+ public ulong? PrimarySkuId { get; internal set; }
+
+ /// <summary>
+ /// If this application is a game sold on Discord, this field will be the URL slug that links to the store page
+ /// </summary>
+ public string Slug { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets a list of <see cref="DiscordApplicationAsset"/>.
+ /// </summary>
+ private IReadOnlyList<DiscordApplicationAsset> _assets;
+
+ /// <summary>
+ /// A custom url for the Add To Server button.
+ /// </summary>
+ public string CustomInstallUrl { get; internal set; }
+
+ /// <summary>
+ /// Install parameters for adding the application to a guild.
+ /// </summary>
+ public DiscordApplicationInstallParams InstallParams { get; internal set; }
+
+ /// <summary>
+ /// The application tags.
+ /// Not used atm.
+ /// </summary>
+ public IReadOnlyList<string> Tags { get; internal set; }
+
+ /// <summary>
+ /// Whether the application is hooked.
+ /// </summary>
+ public bool IsHook { get; internal set; }
+
+ /// <summary>
+ /// Gets the application type.
+ /// Mostly null.
+ /// </summary>
+ public string Type { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordApplication"/> class.
+ /// </summary>
+ internal DiscordApplication()
+ { }
+
+ /// <summary>
+ /// Gets the application's cover image URL, in requested format and size.
+ /// </summary>
+ /// <param name="fmt">Format of the image to get.</param>
+ /// <param name="size">Maximum size of the cover image. Must be a power of two, minimum 16, maximum 2048.</param>
+ /// <returns>URL of the application's cover image.</returns>
+ public string GetAvatarUrl(ImageFormat fmt, ushort size = 1024)
{
- ImageFormat.Gif => "gif",
- ImageFormat.Jpeg => "jpg",
- ImageFormat.Auto or ImageFormat.Png => "png",
- ImageFormat.WebP => "webp",
- _ => throw new ArgumentOutOfRangeException(nameof(fmt)),
- };
- var ssize = size.ToString(CultureInfo.InvariantCulture);
- return !string.IsNullOrWhiteSpace(this.CoverImageHash)
- ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.{sfmt}?size={ssize}"
- : null;
- }
-
- /// <summary>
- /// Retrieves this application's assets.
- /// </summary>
- /// <returns>This application's assets.</returns>
- public async Task<IReadOnlyList<DiscordApplicationAsset>> GetAssetsAsync()
- {
- if (this._assets == null)
- this._assets = await this.Discord.ApiClient.GetApplicationAssetsAsync(this).ConfigureAwait(false);
-
- return this._assets;
+ if (fmt == ImageFormat.Unknown)
+ throw new ArgumentException("You must specify valid image format.", nameof(fmt));
+
+ if (size < 16 || size > 2048)
+ throw new ArgumentOutOfRangeException(nameof(size));
+
+ var log = Math.Log(size, 2);
+ if (log < 4 || log > 11 || log % 1 != 0)
+ throw new ArgumentOutOfRangeException(nameof(size));
+
+ var sfmt = "";
+ sfmt = fmt switch
+ {
+ ImageFormat.Gif => "gif",
+ ImageFormat.Jpeg => "jpg",
+ ImageFormat.Auto or ImageFormat.Png => "png",
+ ImageFormat.WebP => "webp",
+ _ => throw new ArgumentOutOfRangeException(nameof(fmt)),
+ };
+ var ssize = size.ToString(CultureInfo.InvariantCulture);
+ return !string.IsNullOrWhiteSpace(this.CoverImageHash)
+ ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.{sfmt}?size={ssize}"
+ : null;
+ }
+
+ /// <summary>
+ /// Retrieves this application's assets.
+ /// </summary>
+ /// <returns>This application's assets.</returns>
+ public async Task<IReadOnlyList<DiscordApplicationAsset>> GetAssetsAsync()
+ {
+ if (this._assets == null)
+ this._assets = await this.Discord.ApiClient.GetApplicationAssetsAsync(this).ConfigureAwait(false);
+
+ return this._assets;
+ }
+
+ /// <summary>
+ /// Generates an oauth url for the application.
+ /// </summary>
+ /// <param name="permissions">The permissions.</param>
+ /// <returns>OAuth Url</returns>
+ public string GenerateBotOAuth(Permissions permissions = Permissions.None)
+ {
+ permissions &= PermissionMethods.FullPerms;
+ // hey look, it's not all annoying and blue :P
+ return new QueryUriBuilder($"{DiscordDomain.GetDomain(CoreDomain.Discord).Url}{Endpoints.OAUTH2}{Endpoints.AUTHORIZE}")
+ .AddParameter("client_id", this.Id.ToString(CultureInfo.InvariantCulture))
+ .AddParameter("scope", "bot")
+ .AddParameter("permissions", ((long)permissions).ToString(CultureInfo.InvariantCulture))
+ .ToString();
+ }
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordApplication"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordApplication"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordApplication);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordApplication"/> is equal to another <see cref="DiscordApplication"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordApplication"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordApplication"/> is equal to this <see cref="DiscordApplication"/>.</returns>
+ public bool Equals(DiscordApplication e)
+ => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordApplication"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordApplication"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordApplication"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First application to compare.</param>
+ /// <param name="e2">Second application to compare.</param>
+ /// <returns>Whether the two applications are equal.</returns>
+ public static bool operator ==(DiscordApplication e1, DiscordApplication e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
+
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordApplication"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First application to compare.</param>
+ /// <param name="e2">Second application to compare.</param>
+ /// <returns>Whether the two applications are not equal.</returns>
+ public static bool operator !=(DiscordApplication e1, DiscordApplication e2)
+ => !(e1 == e2);
}
/// <summary>
- /// Generates an oauth url for the application.
+ /// Represents an discord asset.
/// </summary>
- /// <param name="permissions">The permissions.</param>
- /// <returns>OAuth Url</returns>
- public string GenerateBotOAuth(Permissions permissions = Permissions.None)
+ public abstract class DiscordAsset
{
- permissions &= PermissionMethods.FullPerms;
- // hey look, it's not all annoying and blue :P
- return new QueryUriBuilder($"{DiscordDomain.GetDomain(CoreDomain.Discord).Url}{Endpoints.OAUTH2}{Endpoints.AUTHORIZE}")
- .AddParameter("client_id", this.Id.ToString(CultureInfo.InvariantCulture))
- .AddParameter("scope", "bot")
- .AddParameter("permissions", ((long)permissions).ToString(CultureInfo.InvariantCulture))
- .ToString();
+ /// <summary>
+ /// Gets the ID of this asset.
+ /// </summary>
+ public virtual string Id { get; set; }
+
+ /// <summary>
+ /// Gets the URL of this asset.
+ /// </summary>
+ public abstract Uri Url { get; }
}
/// <summary>
- /// Checks whether this <see cref="DiscordApplication"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordApplication"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordApplication);
-
- /// <summary>
- /// Checks whether this <see cref="DiscordApplication"/> is equal to another <see cref="DiscordApplication"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordApplication"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordApplication"/> is equal to this <see cref="DiscordApplication"/>.</returns>
- public bool Equals(DiscordApplication e)
- => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
-
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordApplication"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordApplication"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordApplication"/> objects are equal.
- /// </summary>
- /// <param name="e1">First application to compare.</param>
- /// <param name="e2">Second application to compare.</param>
- /// <returns>Whether the two applications are equal.</returns>
- public static bool operator ==(DiscordApplication e1, DiscordApplication e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
-
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
- }
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordApplication"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First application to compare.</param>
- /// <param name="e2">Second application to compare.</param>
- /// <returns>Whether the two applications are not equal.</returns>
- public static bool operator !=(DiscordApplication e1, DiscordApplication e2)
- => !(e1 == e2);
-}
-
-/// <summary>
-/// Represents an discord asset.
-/// </summary>
-public abstract class DiscordAsset
-{
- /// <summary>
- /// Gets the ID of this asset.
- /// </summary>
- public virtual string Id { get; set; }
-
- /// <summary>
- /// Gets the URL of this asset.
- /// </summary>
- public abstract Uri Url { get; }
-}
-
-/// <summary>
-/// Represents an asset for an OAuth2 application.
-/// </summary>
-public sealed class DiscordApplicationAsset : DiscordAsset, IEquatable<DiscordApplicationAsset>
-{
- /// <summary>
- /// Gets the Discord client instance for this asset.
- /// </summary>
- internal BaseDiscordClient Discord { get; set; }
-
- /// <summary>
- /// Gets the asset's name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the asset's type.
- /// </summary>
- [JsonProperty("type")]
- public ApplicationAssetType Type { get; internal set; }
-
- /// <summary>
- /// Gets the application this asset belongs to.
- /// </summary>
- public DiscordApplication Application { get; internal set; }
-
- /// <summary>
- /// Gets the Url of this asset.
- /// </summary>
- public override Uri Url
- => new($"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.APP_ASSETS}/{this.Application.Id.ToString(CultureInfo.InvariantCulture)}/{this.Id}.png");
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordApplicationAsset"/> class.
+ /// Represents an asset for an OAuth2 application.
/// </summary>
- internal DiscordApplicationAsset()
- { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordApplicationAsset"/> class.
- /// </summary>
- /// <param name="app">The app.</param>
- internal DiscordApplicationAsset(DiscordApplication app)
+ public sealed class DiscordApplicationAsset : DiscordAsset, IEquatable<DiscordApplicationAsset>
{
- this.Discord = app.Discord;
+ /// <summary>
+ /// Gets the Discord client instance for this asset.
+ /// </summary>
+ internal BaseDiscordClient Discord { get; set; }
+
+ /// <summary>
+ /// Gets the asset's name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the asset's type.
+ /// </summary>
+ [JsonProperty("type")]
+ public ApplicationAssetType Type { get; internal set; }
+
+ /// <summary>
+ /// Gets the application this asset belongs to.
+ /// </summary>
+ public DiscordApplication Application { get; internal set; }
+
+ /// <summary>
+ /// Gets the Url of this asset.
+ /// </summary>
+ public override Uri Url
+ => new($"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.APP_ASSETS}/{this.Application.Id.ToString(CultureInfo.InvariantCulture)}/{this.Id}.png");
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordApplicationAsset"/> class.
+ /// </summary>
+ internal DiscordApplicationAsset()
+ { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordApplicationAsset"/> class.
+ /// </summary>
+ /// <param name="app">The app.</param>
+ internal DiscordApplicationAsset(DiscordApplication app)
+ {
+ this.Discord = app.Discord;
+ }
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordApplicationAsset"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordApplicationAsset"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordApplicationAsset);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordApplicationAsset"/> is equal to another <see cref="DiscordApplicationAsset"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordApplicationAsset"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordApplicationAsset"/> is equal to this <see cref="DiscordApplicationAsset"/>.</returns>
+ public bool Equals(DiscordApplicationAsset e)
+ => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordApplication"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordApplication"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordApplicationAsset"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First application asset to compare.</param>
+ /// <param name="e2">Second application asset to compare.</param>
+ /// <returns>Whether the two application assets not equal.</returns>
+ public static bool operator ==(DiscordApplicationAsset e1, DiscordApplicationAsset e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
+
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordApplicationAsset"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First application asset to compare.</param>
+ /// <param name="e2">Second application asset to compare.</param>
+ /// <returns>Whether the two application assets are not equal.</returns>
+ public static bool operator !=(DiscordApplicationAsset e1, DiscordApplicationAsset e2)
+ => !(e1 == e2);
}
/// <summary>
- /// Checks whether this <see cref="DiscordApplicationAsset"/> is equal to another object.
+ /// Represents an spotify asset.
/// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordApplicationAsset"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordApplicationAsset);
-
- /// <summary>
- /// Checks whether this <see cref="DiscordApplicationAsset"/> is equal to another <see cref="DiscordApplicationAsset"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordApplicationAsset"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordApplicationAsset"/> is equal to this <see cref="DiscordApplicationAsset"/>.</returns>
- public bool Equals(DiscordApplicationAsset e)
- => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
-
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordApplication"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordApplication"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordApplicationAsset"/> objects are equal.
- /// </summary>
- /// <param name="e1">First application asset to compare.</param>
- /// <param name="e2">Second application asset to compare.</param>
- /// <returns>Whether the two application assets not equal.</returns>
- public static bool operator ==(DiscordApplicationAsset e1, DiscordApplicationAsset e2)
+ public sealed class DiscordSpotifyAsset : DiscordAsset
{
- var o1 = e1 as object;
- var o2 = e2 as object;
-
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ /// <summary>
+ /// Gets the URL of this asset.
+ /// </summary>
+ public override Uri Url
+ => this._url.Value;
+
+ private readonly Lazy<Uri> _url;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordSpotifyAsset"/> class.
+ /// </summary>
+ public DiscordSpotifyAsset()
+ {
+ this._url = new Lazy<Uri>(() =>
+ {
+ var ids = this.Id.Split(':');
+ var id = ids[1];
+ return new Uri($"https://i.scdn.co/image/{id}");
+ });
+ }
}
/// <summary>
- /// Gets whether the two <see cref="DiscordApplicationAsset"/> objects are not equal.
+ /// Determines the type of the asset attached to the application.
/// </summary>
- /// <param name="e1">First application asset to compare.</param>
- /// <param name="e2">Second application asset to compare.</param>
- /// <returns>Whether the two application assets are not equal.</returns>
- public static bool operator !=(DiscordApplicationAsset e1, DiscordApplicationAsset e2)
- => !(e1 == e2);
-}
-
-/// <summary>
-/// Represents an spotify asset.
-/// </summary>
-public sealed class DiscordSpotifyAsset : DiscordAsset
-{
- /// <summary>
- /// Gets the URL of this asset.
- /// </summary>
- public override Uri Url
- => this._url.Value;
-
- private readonly Lazy<Uri> _url;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordSpotifyAsset"/> class.
- /// </summary>
- public DiscordSpotifyAsset()
+ public enum ApplicationAssetType : int
{
- this._url = new Lazy<Uri>(() =>
- {
- var ids = this.Id.Split(':');
- var id = ids[1];
- return new Uri($"https://i.scdn.co/image/{id}");
- });
+ /// <summary>
+ /// Unknown type. This indicates something went terribly wrong.
+ /// </summary>
+ Unknown = 0,
+
+ /// <summary>
+ /// This asset can be used as small image for rich presences.
+ /// </summary>
+ SmallImage = 1,
+
+ /// <summary>
+ /// This asset can be used as large image for rich presences.
+ /// </summary>
+ LargeImage = 2
}
}
-
-/// <summary>
-/// Determines the type of the asset attached to the application.
-/// </summary>
-public enum ApplicationAssetType : int
-{
- /// <summary>
- /// Unknown type. This indicates something went terribly wrong.
- /// </summary>
- Unknown = 0,
-
- /// <summary>
- /// This asset can be used as small image for rich presences.
- /// </summary>
- SmallImage = 1,
-
- /// <summary>
- /// This asset can be used as large image for rich presences.
- /// </summary>
- LargeImage = 2
-}
diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs
index a67b94d32..ec254a800 100644
--- a/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplicationCommand.cs
@@ -1,199 +1,200 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using DisCatSharp.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a command that is registered to an application.
-/// </summary>
-public sealed class DiscordApplicationCommand : SnowflakeObject, IEquatable<DiscordApplicationCommand>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the type of this application command.
- /// </summary>
- [JsonProperty("type")]
- public ApplicationCommandType Type { get; internal set; }
-
- /// <summary>
- /// Gets the unique ID of this command's application.
- /// </summary>
- [JsonProperty("application_id")]
- public ulong ApplicationId { get; internal set; }
-
- /// <summary>
- /// Gets the name of this command.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Sets the name localizations.
- /// </summary>
- [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
- internal Dictionary<string, string> RawNameLocalizations { get; set; }
-
- /// <summary>
- /// Gets the name localizations.
- /// </summary>
- [JsonIgnore]
- public DiscordApplicationCommandLocalization NameLocalizations
- => new(this.RawNameLocalizations);
-
- /// <summary>
- /// Gets the description of this command.
- /// </summary>
- [JsonProperty("description")]
- public string Description { get; internal set; }
-
- /// <summary>
- /// Sets the description localizations.
- /// </summary>
- [JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)]
- internal Dictionary<string, string> RawDescriptionLocalizations { get; set; }
-
- /// <summary>
- /// Gets the description localizations.
- /// </summary>
- [JsonIgnore]
- public DiscordApplicationCommandLocalization DescriptionLocalizations
- => new(this.RawDescriptionLocalizations);
-
- /// <summary>
- /// Gets the potential parameters for this command.
- /// </summary>
- [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<DiscordApplicationCommandOption> Options { get; internal set; }
-
- /// <summary>
- /// Gets the commands needed permissions.
+ /// Represents a command that is registered to an application.
/// </summary>
- [JsonProperty("default_member_permissions", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions? DefaultMemberPermissions { get; internal set; }
-
- /// <summary>
- /// Gets whether the command can be used in direct messages.
- /// </summary>
- [JsonProperty("dm_permission", NullValueHandling = NullValueHandling.Ignore)]
- public bool? DmPermission { get; internal set; }
-
- /// <summary>
- /// Gets the version number for this command.
- /// </summary>
- [JsonProperty("version")]
- public ulong Version { get; internal set; }
-
- /// <summary>
- /// Creates a new instance of a <see cref="DiscordApplicationCommand"/>.
- /// </summary>
- /// <param name="name">The name of the command.</param>
- /// <param name="description">The description of the command.</param>
- /// <param name="options">Optional parameters for this command.</param>
- /// <param name="type">The type of the command. Defaults to ChatInput.</param>
- /// <param name="nameLocalizations">The localizations of the command name.</param>
- /// <param name="descriptionLocalizations">The localizations of the command description.</param>
- /// <param name="defaultMemberPermissions">The default member permissions.</param>
- /// <param name="dmPermission">The dm permission.</param>
- public DiscordApplicationCommand(string name, string description, IEnumerable<DiscordApplicationCommandOption> options = null, ApplicationCommandType type = ApplicationCommandType.ChatInput, DiscordApplicationCommandLocalization nameLocalizations = null, DiscordApplicationCommandLocalization descriptionLocalizations = null, Permissions? defaultMemberPermissions = null, bool? dmPermission = null)
+ public sealed class DiscordApplicationCommand : SnowflakeObject, IEquatable<DiscordApplicationCommand>
{
- if (type is ApplicationCommandType.ChatInput)
+ /// <summary>
+ /// Gets the type of this application command.
+ /// </summary>
+ [JsonProperty("type")]
+ public ApplicationCommandType Type { get; internal set; }
+
+ /// <summary>
+ /// Gets the unique ID of this command's application.
+ /// </summary>
+ [JsonProperty("application_id")]
+ public ulong ApplicationId { get; internal set; }
+
+ /// <summary>
+ /// Gets the name of this command.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Sets the name localizations.
+ /// </summary>
+ [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
+ internal Dictionary<string, string> RawNameLocalizations { get; set; }
+
+ /// <summary>
+ /// Gets the name localizations.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization NameLocalizations
+ => new(this.RawNameLocalizations);
+
+ /// <summary>
+ /// Gets the description of this command.
+ /// </summary>
+ [JsonProperty("description")]
+ public string Description { get; internal set; }
+
+ /// <summary>
+ /// Sets the description localizations.
+ /// </summary>
+ [JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)]
+ internal Dictionary<string, string> RawDescriptionLocalizations { get; set; }
+
+ /// <summary>
+ /// Gets the description localizations.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization DescriptionLocalizations
+ => new(this.RawDescriptionLocalizations);
+
+ /// <summary>
+ /// Gets the potential parameters for this command.
+ /// </summary>
+ [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<DiscordApplicationCommandOption> Options { get; internal set; }
+
+ /// <summary>
+ /// Gets the commands needed permissions.
+ /// </summary>
+ [JsonProperty("default_member_permissions", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions? DefaultMemberPermissions { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the command can be used in direct messages.
+ /// </summary>
+ [JsonProperty("dm_permission", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? DmPermission { get; internal set; }
+
+ /// <summary>
+ /// Gets the version number for this command.
+ /// </summary>
+ [JsonProperty("version")]
+ public ulong Version { get; internal set; }
+
+ /// <summary>
+ /// Creates a new instance of a <see cref="DiscordApplicationCommand"/>.
+ /// </summary>
+ /// <param name="name">The name of the command.</param>
+ /// <param name="description">The description of the command.</param>
+ /// <param name="options">Optional parameters for this command.</param>
+ /// <param name="type">The type of the command. Defaults to ChatInput.</param>
+ /// <param name="nameLocalizations">The localizations of the command name.</param>
+ /// <param name="descriptionLocalizations">The localizations of the command description.</param>
+ /// <param name="defaultMemberPermissions">The default member permissions.</param>
+ /// <param name="dmPermission">The dm permission.</param>
+ public DiscordApplicationCommand(string name, string description, IEnumerable<DiscordApplicationCommandOption> options = null, ApplicationCommandType type = ApplicationCommandType.ChatInput, DiscordApplicationCommandLocalization nameLocalizations = null, DiscordApplicationCommandLocalization descriptionLocalizations = null, Permissions? defaultMemberPermissions = null, bool? dmPermission = null)
{
- if (!Utilities.IsValidSlashCommandName(name))
- throw new ArgumentException("Invalid slash command name specified. It must be below 32 characters and not contain any whitespace.", nameof(name));
- if (name.Any(ch => char.IsUpper(ch)))
- throw new ArgumentException("Slash command name cannot have any upper case characters.", nameof(name));
- if (description.Length > 100)
- throw new ArgumentException("Slash command description cannot exceed 100 characters.", nameof(description));
-
- this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
- this.RawDescriptionLocalizations = descriptionLocalizations?.GetKeyValuePairs();
+ if (type is ApplicationCommandType.ChatInput)
+ {
+ if (!Utilities.IsValidSlashCommandName(name))
+ throw new ArgumentException("Invalid slash command name specified. It must be below 32 characters and not contain any whitespace.", nameof(name));
+ if (name.Any(ch => char.IsUpper(ch)))
+ throw new ArgumentException("Slash command name cannot have any upper case characters.", nameof(name));
+ if (description.Length > 100)
+ throw new ArgumentException("Slash command description cannot exceed 100 characters.", nameof(description));
+
+ this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
+ this.RawDescriptionLocalizations = descriptionLocalizations?.GetKeyValuePairs();
+ }
+ else
+ {
+ if (!string.IsNullOrWhiteSpace(description))
+ throw new ArgumentException("Context menus do not support descriptions.");
+ if (options?.Any() ?? false)
+ throw new ArgumentException("Context menus do not support options.");
+ description = string.Empty;
+
+ this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
+ }
+
+ var optionsList = options != null ? new ReadOnlyCollection<DiscordApplicationCommandOption>(options.ToList()) : null;
+
+ this.Type = type;
+ this.Name = name;
+ this.Description = description;
+ this.Options = optionsList;
+ this.DefaultMemberPermissions = defaultMemberPermissions;
+ this.DmPermission = dmPermission;
}
- else
- {
- if (!string.IsNullOrWhiteSpace(description))
- throw new ArgumentException("Context menus do not support descriptions.");
- if (options?.Any() ?? false)
- throw new ArgumentException("Context menus do not support options.");
- description = string.Empty;
- this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
- }
-
- var optionsList = options != null ? new ReadOnlyCollection<DiscordApplicationCommandOption>(options.ToList()) : null;
-
- this.Type = type;
- this.Name = name;
- this.Description = description;
- this.Options = optionsList;
- this.DefaultMemberPermissions = defaultMemberPermissions;
- this.DmPermission = dmPermission;
+ /// <summary>
+ /// Checks whether this <see cref="DiscordApplicationCommand"/> object is equal to another object.
+ /// </summary>
+ /// <param name="other">The command to compare to.</param>
+ /// <returns>Whether the command is equal to this <see cref="DiscordApplicationCommand"/>.</returns>
+ public bool Equals(DiscordApplicationCommand other)
+ => this.Id == other.Id;
+
+ /// <summary>
+ /// Determines if two <see cref="DiscordApplicationCommand"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">The first command object.</param>
+ /// <param name="e2">The second command object.</param>
+ /// <returns>Whether the two <see cref="DiscordApplicationCommand"/> objects are equal.</returns>
+ public static bool operator ==(DiscordApplicationCommand e1, DiscordApplicationCommand e2)
+ => e1.Equals(e2);
+
+ /// <summary>
+ /// Determines if two <see cref="DiscordApplicationCommand"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">The first command object.</param>
+ /// <param name="e2">The second command object.</param>
+ /// <returns>Whether the two <see cref="DiscordApplicationCommand"/> objects are not equal.</returns>
+ public static bool operator !=(DiscordApplicationCommand e1, DiscordApplicationCommand e2)
+ => !(e1 == e2);
+
+ /// <summary>
+ /// Determines if a <see cref="object"/> is equal to the current <see cref="DiscordApplicationCommand"/>.
+ /// </summary>
+ /// <param name="other">The object to compare to.</param>
+ /// <returns>Whether the two <see cref="DiscordApplicationCommand"/> objects are not equal.</returns>
+ public override bool Equals(object other)
+ => other is DiscordApplicationCommand dac && this.Equals(dac);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordApplicationCommand"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordApplicationCommand"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
}
-
- /// <summary>
- /// Checks whether this <see cref="DiscordApplicationCommand"/> object is equal to another object.
- /// </summary>
- /// <param name="other">The command to compare to.</param>
- /// <returns>Whether the command is equal to this <see cref="DiscordApplicationCommand"/>.</returns>
- public bool Equals(DiscordApplicationCommand other)
- => this.Id == other.Id;
-
- /// <summary>
- /// Determines if two <see cref="DiscordApplicationCommand"/> objects are equal.
- /// </summary>
- /// <param name="e1">The first command object.</param>
- /// <param name="e2">The second command object.</param>
- /// <returns>Whether the two <see cref="DiscordApplicationCommand"/> objects are equal.</returns>
- public static bool operator ==(DiscordApplicationCommand e1, DiscordApplicationCommand e2)
- => e1.Equals(e2);
-
- /// <summary>
- /// Determines if two <see cref="DiscordApplicationCommand"/> objects are not equal.
- /// </summary>
- /// <param name="e1">The first command object.</param>
- /// <param name="e2">The second command object.</param>
- /// <returns>Whether the two <see cref="DiscordApplicationCommand"/> objects are not equal.</returns>
- public static bool operator !=(DiscordApplicationCommand e1, DiscordApplicationCommand e2)
- => !(e1 == e2);
-
- /// <summary>
- /// Determines if a <see cref="object"/> is equal to the current <see cref="DiscordApplicationCommand"/>.
- /// </summary>
- /// <param name="other">The object to compare to.</param>
- /// <returns>Whether the two <see cref="DiscordApplicationCommand"/> objects are not equal.</returns>
- public override bool Equals(object other)
- => other is DiscordApplicationCommand dac && this.Equals(dac);
-
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordApplicationCommand"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordApplicationCommand"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
}
diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommandAutocompleteChoice.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommandAutocompleteChoice.cs
index f68241355..8378614af 100644
--- a/DisCatSharp/Entities/Application/DiscordApplicationCommandAutocompleteChoice.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplicationCommandAutocompleteChoice.cs
@@ -1,79 +1,80 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an option for a user to select for auto-completion.
-/// </summary>
-public sealed class DiscordApplicationCommandAutocompleteChoice
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of this option which will be presented to the user.
+ /// Represents an option for a user to select for auto-completion.
/// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
+ public sealed class DiscordApplicationCommandAutocompleteChoice
+ {
+ /// <summary>
+ /// Gets the name of this option which will be presented to the user.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
- /// <summary>
- /// Sets the name localizations.
- /// </summary>
- [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
- internal Dictionary<string, string> RawNameLocalizations { get; set; }
+ /// <summary>
+ /// Sets the name localizations.
+ /// </summary>
+ [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
+ internal Dictionary<string, string> RawNameLocalizations { get; set; }
- /// <summary>
- /// Gets the name localizations.
- /// </summary>
- [JsonIgnore]
- public DiscordApplicationCommandLocalization NameLocalizations
- => new(this.RawNameLocalizations);
+ /// <summary>
+ /// Gets the name localizations.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization NameLocalizations
+ => new(this.RawNameLocalizations);
- /// <summary>
- /// Gets the value of this option.
- /// </summary>
- [JsonProperty("value")]
- public object Value { get; internal set; }
+ /// <summary>
+ /// Gets the value of this option.
+ /// </summary>
+ [JsonProperty("value")]
+ public object Value { get; internal set; }
- /// <summary>
- /// Creates a new instance of <see cref="DiscordApplicationCommandAutocompleteChoice"/>.
- /// </summary>
- /// <param name="name">The name of this option, which will be presented to the user.</param>
- /// <param name="nameLocalizations">The localizations of the option name.</param>
- /// <param name="value">The value of this option.</param>
- public DiscordApplicationCommandAutocompleteChoice(string name, object value, DiscordApplicationCommandLocalization nameLocalizations = null)
- {
- if (name.Length > 100)
- throw new ArgumentException("Application command choice name cannot exceed 100 characters.", nameof(name));
- if (value is string val && val.Length > 100)
- throw new ArgumentException("Application command choice value cannot exceed 100 characters.", nameof(value));
- if (!(value is string || value is long || value is int || value is double))
- throw new InvalidOperationException($"Only {typeof(string)}, {typeof(long)}, {typeof(double)} or {typeof(int)} types may be passed to a autocomplete choice.");
+ /// <summary>
+ /// Creates a new instance of <see cref="DiscordApplicationCommandAutocompleteChoice"/>.
+ /// </summary>
+ /// <param name="name">The name of this option, which will be presented to the user.</param>
+ /// <param name="nameLocalizations">The localizations of the option name.</param>
+ /// <param name="value">The value of this option.</param>
+ public DiscordApplicationCommandAutocompleteChoice(string name, object value, DiscordApplicationCommandLocalization nameLocalizations = null)
+ {
+ if (name.Length > 100)
+ throw new ArgumentException("Application command choice name cannot exceed 100 characters.", nameof(name));
+ if (value is string val && val.Length > 100)
+ throw new ArgumentException("Application command choice value cannot exceed 100 characters.", nameof(value));
+ if (!(value is string || value is long || value is int || value is double))
+ throw new InvalidOperationException($"Only {typeof(string)}, {typeof(long)}, {typeof(double)} or {typeof(int)} types may be passed to a autocomplete choice.");
- this.Name = name;
- this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
- this.Value = value;
+ this.Name = name;
+ this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
+ this.Value = value;
+ }
}
}
diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommandLocalization.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommandLocalization.cs
index 28d1022d2..ec561dd0a 100644
--- a/DisCatSharp/Entities/Application/DiscordApplicationCommandLocalization.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplicationCommandLocalization.cs
@@ -1,105 +1,106 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a application command localization.
-/// </summary>
-public sealed class DiscordApplicationCommandLocalization
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the localization dict.
+ /// Represents a application command localization.
/// </summary>
- public Dictionary<string, string> Localizations { get; internal set; }
+ public sealed class DiscordApplicationCommandLocalization
+ {
+ /// <summary>
+ /// Gets the localization dict.
+ /// </summary>
+ public Dictionary<string, string> Localizations { get; internal set; }
- /// <summary>
- /// Gets valid [locales](xref:application_commands_translations_reference#valid-locales) for Discord.
- /// </summary>
- internal List<string> ValidLocales = new() { "ru", "fi", "hr", "de", "hu", "sv-SE", "cs", "fr", "it", "en-GB", "pt-BR", "ja", "tr", "en-US", "es-ES", "uk", "hi", "th", "el", "no", "ro", "ko", "zh-TW", "vi", "zh-CN", "pl", "bg", "da", "nl", "lt" };
+ /// <summary>
+ /// Gets valid [locales](xref:application_commands_translations_reference#valid-locales) for Discord.
+ /// </summary>
+ internal List<string> ValidLocales = new() { "ru", "fi", "hr", "de", "hu", "sv-SE", "cs", "fr", "it", "en-GB", "pt-BR", "ja", "tr", "en-US", "es-ES", "uk", "hi", "th", "el", "no", "ro", "ko", "zh-TW", "vi", "zh-CN", "pl", "bg", "da", "nl", "lt" };
- /// <summary>
- /// Adds a localization.
- /// </summary>
- /// <param name="locale">The [locale](xref:application_commands_translations_reference#valid-locales) to add.</param>
- /// <param name="value">The translation to add.</param>
- public void AddLocalization(string locale, string value)
- {
- if (this.Validate(locale))
+ /// <summary>
+ /// Adds a localization.
+ /// </summary>
+ /// <param name="locale">The [locale](xref:application_commands_translations_reference#valid-locales) to add.</param>
+ /// <param name="value">The translation to add.</param>
+ public void AddLocalization(string locale, string value)
{
- this.Localizations.Add(locale, value);
- }
- else
- {
- throw new NotSupportedException($"The provided locale \"{locale}\" is not valid for Discord.\n" +
- $"Valid locales: {string.Join(", ", this.ValidLocales.ToArray())}");
+ if (this.Validate(locale))
+ {
+ this.Localizations.Add(locale, value);
+ }
+ else
+ {
+ throw new NotSupportedException($"The provided locale \"{locale}\" is not valid for Discord.\n" +
+ $"Valid locales: {string.Join(", ", this.ValidLocales.ToArray())}");
+ }
}
- }
- /// <summary>
- /// Removes a localization.
- /// </summary>
- /// <param name="locale">The [locale](xref:application_commands_translations_reference#valid-locales) to remove.</param>
- public void RemoveLocalization(string locale)
- => this.Localizations.Remove(locale);
+ /// <summary>
+ /// Removes a localization.
+ /// </summary>
+ /// <param name="locale">The [locale](xref:application_commands_translations_reference#valid-locales) to remove.</param>
+ public void RemoveLocalization(string locale)
+ => this.Localizations.Remove(locale);
- /// <summary>
- /// Initializes a new instance of <see cref="DiscordApplicationCommandLocalization"/>.
- /// </summary>
- public DiscordApplicationCommandLocalization() { }
+ /// <summary>
+ /// Initializes a new instance of <see cref="DiscordApplicationCommandLocalization"/>.
+ /// </summary>
+ public DiscordApplicationCommandLocalization() { }
- /// <summary>
- /// Initializes a new instance of <see cref="DiscordApplicationCommandLocalization"/>.
- /// </summary>
- /// <param name="localizations">Localizations.</param>
- public DiscordApplicationCommandLocalization(Dictionary<string, string> localizations)
- {
- if (localizations != null)
+ /// <summary>
+ /// Initializes a new instance of <see cref="DiscordApplicationCommandLocalization"/>.
+ /// </summary>
+ /// <param name="localizations">Localizations.</param>
+ public DiscordApplicationCommandLocalization(Dictionary<string, string> localizations)
{
- foreach (var locale in localizations.Keys)
+ if (localizations != null)
{
- if (!this.Validate(locale))
- throw new NotSupportedException($"The provided locale \"{locale}\" is not valid for Discord.\n" +
- $"Valid locales: {string.Join(", ", this.ValidLocales.ToArray())}");
+ foreach (var locale in localizations.Keys)
+ {
+ if (!this.Validate(locale))
+ throw new NotSupportedException($"The provided locale \"{locale}\" is not valid for Discord.\n" +
+ $"Valid locales: {string.Join(", ", this.ValidLocales.ToArray())}");
+ }
}
- }
- this.Localizations = localizations;
- }
+ this.Localizations = localizations;
+ }
- /// <summary>
- /// Gets the KVPs.
- /// </summary>
- /// <returns></returns>
- public Dictionary<string, string> GetKeyValuePairs()
- => this.Localizations;
+ /// <summary>
+ /// Gets the KVPs.
+ /// </summary>
+ /// <returns></returns>
+ public Dictionary<string, string> GetKeyValuePairs()
+ => this.Localizations;
- /// <summary>
- /// Whether the [locale](xref:application_commands_translations_reference#valid-locales) to be added is valid for Discord.
- /// </summary>
- /// <param name="lang">[Locale](xref:application_commands_translations_reference#valid-locales) string.</param>
- public bool Validate(string lang)
- => this.ValidLocales.Contains(lang);
+ /// <summary>
+ /// Whether the [locale](xref:application_commands_translations_reference#valid-locales) to be added is valid for Discord.
+ /// </summary>
+ /// <param name="lang">[Locale](xref:application_commands_translations_reference#valid-locales) string.</param>
+ public bool Validate(string lang)
+ => this.ValidLocales.Contains(lang);
+ }
}
diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommandOption.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommandOption.cs
index 0263ac96a..7dd726d9c 100644
--- a/DisCatSharp/Entities/Application/DiscordApplicationCommandOption.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplicationCommandOption.cs
@@ -1,163 +1,164 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a parameter for a <see cref="DiscordApplicationCommand"/>.
-/// </summary>
-public sealed class DiscordApplicationCommandOption
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the type of this command parameter.
- /// </summary>
- [JsonProperty("type")]
- public ApplicationCommandOptionType Type { get; internal set; }
-
- /// <summary>
- /// Gets the name of this command parameter.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Sets the name localizations.
- /// </summary>
- [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
- internal Dictionary<string, string> RawNameLocalizations { get; set; }
-
- /// <summary>
- /// Gets the name localizations.
- /// </summary>
- [JsonIgnore]
- public DiscordApplicationCommandLocalization NameLocalizations
- => new(this.RawNameLocalizations);
-
- /// <summary>
- /// Gets the description of this command parameter.
- /// </summary>
- [JsonProperty("description")]
- public string Description { get; internal set; }
-
- /// <summary>
- /// Sets the description localizations.
- /// </summary>
- [JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)]
- internal Dictionary<string, string> RawDescriptionLocalizations { get; set; }
-
- /// <summary>
- /// Gets the description localizations.
- /// </summary>
- [JsonIgnore]
- public DiscordApplicationCommandLocalization DescriptionLocalizations
- => new(this.RawDescriptionLocalizations);
-
- /// <summary>
- /// Gets whether this command parameter is required.
- /// </summary>
- [JsonProperty("required", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Required { get; internal set; }
-
- /// <summary>
- /// Gets the optional choices for this command parameter.
- /// Not applicable for auto-complete options.
- /// </summary>
- [JsonProperty("choices", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<DiscordApplicationCommandOptionChoice> Choices { get; internal set; }
-
- /// <summary>
- /// Gets the optional subcommand parameters for this parameter.
- /// </summary>
- [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<DiscordApplicationCommandOption> Options { get; internal set; }
-
- /// <summary>
- /// Gets the optional allowed channel types.
- /// </summary>
- [JsonProperty("channel_types", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<ChannelType> ChannelTypes { get; internal set; }
-
- /// <summary>
- /// Gets whether this option provides autocompletion.
- /// </summary>
- [JsonProperty("autocomplete", NullValueHandling = NullValueHandling.Ignore)]
- public bool? AutoComplete { get; internal set; }
-
- /// <summary>
- /// Gets the minimum value for this slash command parameter.
- /// </summary>
- [JsonProperty("min_value", NullValueHandling = NullValueHandling.Ignore)]
- public object MinimumValue { get; internal set; }
-
- /// <summary>
- /// Gets the maximum value for this slash command parameter.
- /// </summary>
- [JsonProperty("max_value", NullValueHandling = NullValueHandling.Ignore)]
- public object MaximumValue { get; internal set; }
-
- /// <summary>
- /// Creates a new instance of a <see cref="DiscordApplicationCommandOption"/>.
+ /// Represents a parameter for a <see cref="DiscordApplicationCommand"/>.
/// </summary>
- /// <param name="name">The name of this parameter.</param>
- /// <param name="description">The description of the parameter.</param>
- /// <param name="type">The type of this parameter.</param>
- /// <param name="required">Whether the parameter is required.</param>
- /// <param name="choices">The optional choice selection for this parameter.</param>
- /// <param name="options">The optional subcommands for this parameter.</param>
- /// <param name="channelTypes">If the option is a channel type, the channels shown will be restricted to these types.</param>
- /// <param name="autocomplete">Whether this option provides autocompletion.</param>
- /// <param name="minimumValue">The minimum value for this parameter. Only valid for types <see cref="ApplicationCommandOptionType.Integer"/> or <see cref="ApplicationCommandOptionType.Number"/>.</param>
- /// <param name="maximumValue">The maximum value for this parameter. Only valid for types <see cref="ApplicationCommandOptionType.Integer"/> or <see cref="ApplicationCommandOptionType.Number"/>.</param>
- /// <param name="nameLocalizations">The localizations of the parameter name.</param>
- /// <param name="descriptionLocalizations">The localizations of the parameter description.</param>
- public DiscordApplicationCommandOption(string name, string description, ApplicationCommandOptionType type, bool? required = null, IEnumerable<DiscordApplicationCommandOptionChoice> choices = null, IEnumerable<DiscordApplicationCommandOption> options = null, IEnumerable<ChannelType> channelTypes = null, bool? autocomplete = null, object minimumValue = null, object maximumValue = null, DiscordApplicationCommandLocalization nameLocalizations = null, DiscordApplicationCommandLocalization descriptionLocalizations = null)
+ public sealed class DiscordApplicationCommandOption
{
- if (!Utilities.IsValidSlashCommandName(name))
- throw new ArgumentException("Invalid application command option name specified. It must be below 32 characters and not contain any whitespace.", nameof(name));
- if (name.Any(char.IsUpper))
- throw new ArgumentException("Application command option name cannot have any upper case characters.", nameof(name));
- if (description.Length > 100)
- throw new ArgumentException("Application command option description cannot exceed 100 characters.", nameof(description));
- if ((autocomplete ?? false) && (choices?.Any() ?? false))
- throw new InvalidOperationException("Auto-complete slash command options cannot provide choices.");
-
- this.Name = name;
- this.Description = description;
- this.Type = type;
- this.Required = required;
- this.Choices = choices != null ? new ReadOnlyCollection<DiscordApplicationCommandOptionChoice>(choices.ToList()) : null;
- this.Options = options != null ? new ReadOnlyCollection<DiscordApplicationCommandOption>(options.ToList()) : null;
- this.ChannelTypes = channelTypes != null ? new ReadOnlyCollection<ChannelType>(channelTypes.ToList()) : null;
- this.AutoComplete = autocomplete;
- this.MinimumValue = minimumValue;
- this.MaximumValue = maximumValue;
- this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
- this.RawDescriptionLocalizations = descriptionLocalizations?.GetKeyValuePairs();
+ /// <summary>
+ /// Gets the type of this command parameter.
+ /// </summary>
+ [JsonProperty("type")]
+ public ApplicationCommandOptionType Type { get; internal set; }
+
+ /// <summary>
+ /// Gets the name of this command parameter.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Sets the name localizations.
+ /// </summary>
+ [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
+ internal Dictionary<string, string> RawNameLocalizations { get; set; }
+
+ /// <summary>
+ /// Gets the name localizations.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization NameLocalizations
+ => new(this.RawNameLocalizations);
+
+ /// <summary>
+ /// Gets the description of this command parameter.
+ /// </summary>
+ [JsonProperty("description")]
+ public string Description { get; internal set; }
+
+ /// <summary>
+ /// Sets the description localizations.
+ /// </summary>
+ [JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)]
+ internal Dictionary<string, string> RawDescriptionLocalizations { get; set; }
+
+ /// <summary>
+ /// Gets the description localizations.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization DescriptionLocalizations
+ => new(this.RawDescriptionLocalizations);
+
+ /// <summary>
+ /// Gets whether this command parameter is required.
+ /// </summary>
+ [JsonProperty("required", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Required { get; internal set; }
+
+ /// <summary>
+ /// Gets the optional choices for this command parameter.
+ /// Not applicable for auto-complete options.
+ /// </summary>
+ [JsonProperty("choices", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<DiscordApplicationCommandOptionChoice> Choices { get; internal set; }
+
+ /// <summary>
+ /// Gets the optional subcommand parameters for this parameter.
+ /// </summary>
+ [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<DiscordApplicationCommandOption> Options { get; internal set; }
+
+ /// <summary>
+ /// Gets the optional allowed channel types.
+ /// </summary>
+ [JsonProperty("channel_types", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<ChannelType> ChannelTypes { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this option provides autocompletion.
+ /// </summary>
+ [JsonProperty("autocomplete", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? AutoComplete { get; internal set; }
+
+ /// <summary>
+ /// Gets the minimum value for this slash command parameter.
+ /// </summary>
+ [JsonProperty("min_value", NullValueHandling = NullValueHandling.Ignore)]
+ public object MinimumValue { get; internal set; }
+
+ /// <summary>
+ /// Gets the maximum value for this slash command parameter.
+ /// </summary>
+ [JsonProperty("max_value", NullValueHandling = NullValueHandling.Ignore)]
+ public object MaximumValue { get; internal set; }
+
+ /// <summary>
+ /// Creates a new instance of a <see cref="DiscordApplicationCommandOption"/>.
+ /// </summary>
+ /// <param name="name">The name of this parameter.</param>
+ /// <param name="description">The description of the parameter.</param>
+ /// <param name="type">The type of this parameter.</param>
+ /// <param name="required">Whether the parameter is required.</param>
+ /// <param name="choices">The optional choice selection for this parameter.</param>
+ /// <param name="options">The optional subcommands for this parameter.</param>
+ /// <param name="channelTypes">If the option is a channel type, the channels shown will be restricted to these types.</param>
+ /// <param name="autocomplete">Whether this option provides autocompletion.</param>
+ /// <param name="minimumValue">The minimum value for this parameter. Only valid for types <see cref="ApplicationCommandOptionType.Integer"/> or <see cref="ApplicationCommandOptionType.Number"/>.</param>
+ /// <param name="maximumValue">The maximum value for this parameter. Only valid for types <see cref="ApplicationCommandOptionType.Integer"/> or <see cref="ApplicationCommandOptionType.Number"/>.</param>
+ /// <param name="nameLocalizations">The localizations of the parameter name.</param>
+ /// <param name="descriptionLocalizations">The localizations of the parameter description.</param>
+ public DiscordApplicationCommandOption(string name, string description, ApplicationCommandOptionType type, bool? required = null, IEnumerable<DiscordApplicationCommandOptionChoice> choices = null, IEnumerable<DiscordApplicationCommandOption> options = null, IEnumerable<ChannelType> channelTypes = null, bool? autocomplete = null, object minimumValue = null, object maximumValue = null, DiscordApplicationCommandLocalization nameLocalizations = null, DiscordApplicationCommandLocalization descriptionLocalizations = null)
+ {
+ if (!Utilities.IsValidSlashCommandName(name))
+ throw new ArgumentException("Invalid application command option name specified. It must be below 32 characters and not contain any whitespace.", nameof(name));
+ if (name.Any(char.IsUpper))
+ throw new ArgumentException("Application command option name cannot have any upper case characters.", nameof(name));
+ if (description.Length > 100)
+ throw new ArgumentException("Application command option description cannot exceed 100 characters.", nameof(description));
+ if ((autocomplete ?? false) && (choices?.Any() ?? false))
+ throw new InvalidOperationException("Auto-complete slash command options cannot provide choices.");
+
+ this.Name = name;
+ this.Description = description;
+ this.Type = type;
+ this.Required = required;
+ this.Choices = choices != null ? new ReadOnlyCollection<DiscordApplicationCommandOptionChoice>(choices.ToList()) : null;
+ this.Options = options != null ? new ReadOnlyCollection<DiscordApplicationCommandOption>(options.ToList()) : null;
+ this.ChannelTypes = channelTypes != null ? new ReadOnlyCollection<ChannelType>(channelTypes.ToList()) : null;
+ this.AutoComplete = autocomplete;
+ this.MinimumValue = minimumValue;
+ this.MaximumValue = maximumValue;
+ this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
+ this.RawDescriptionLocalizations = descriptionLocalizations?.GetKeyValuePairs();
+ }
}
}
diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommandOptionChoice.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommandOptionChoice.cs
index a4634965a..1e285bce4 100644
--- a/DisCatSharp/Entities/Application/DiscordApplicationCommandOptionChoice.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplicationCommandOptionChoice.cs
@@ -1,80 +1,81 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a command parameter choice for a <see cref="DiscordApplicationCommandOption"/>.
-/// </summary>
-public sealed class DiscordApplicationCommandOptionChoice
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of this choice parameter.
+ /// Represents a command parameter choice for a <see cref="DiscordApplicationCommandOption"/>.
/// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
+ public sealed class DiscordApplicationCommandOptionChoice
+ {
+ /// <summary>
+ /// Gets the name of this choice parameter.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
- /// <summary>
- /// Sets the name localizations.
- /// </summary>
- [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
- internal Dictionary<string, string> RawNameLocalizations { get; set; }
+ /// <summary>
+ /// Sets the name localizations.
+ /// </summary>
+ [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
+ internal Dictionary<string, string> RawNameLocalizations { get; set; }
- /// <summary>
- /// Gets the name localizations.
- /// </summary>
- [JsonIgnore]
- public DiscordApplicationCommandLocalization NameLocalizations
- => new(this.RawNameLocalizations);
+ /// <summary>
+ /// Gets the name localizations.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordApplicationCommandLocalization NameLocalizations
+ => new(this.RawNameLocalizations);
- /// <summary>
- /// Gets the value of this choice parameter. This will either be a type of <see cref="int"/>, <see cref="long"/>, <see cref="double"/> or <see cref="string"/>.
- /// </summary>
- [JsonProperty("value")]
- public object Value { get; set; }
+ /// <summary>
+ /// Gets the value of this choice parameter. This will either be a type of <see cref="int"/>, <see cref="long"/>, <see cref="double"/> or <see cref="string"/>.
+ /// </summary>
+ [JsonProperty("value")]
+ public object Value { get; set; }
- /// <summary>
- /// Creates a new instance of a <see cref="DiscordApplicationCommandOptionChoice"/>.
- /// </summary>
- /// <param name="name">The name of the parameter choice.</param>
- /// <param name="value">The value of the parameter choice.</param>
- /// <param name="nameLocalizations">The localizations of the parameter choice name.</param>
- public DiscordApplicationCommandOptionChoice(string name, object value, DiscordApplicationCommandLocalization nameLocalizations = null)
- {
- if (!(value is string || value is long || value is int || value is double))
- throw new InvalidOperationException($"Only {typeof(string)}, {typeof(long)}, {typeof(double)} or {typeof(int)} types may be passed to a command option choice.");
+ /// <summary>
+ /// Creates a new instance of a <see cref="DiscordApplicationCommandOptionChoice"/>.
+ /// </summary>
+ /// <param name="name">The name of the parameter choice.</param>
+ /// <param name="value">The value of the parameter choice.</param>
+ /// <param name="nameLocalizations">The localizations of the parameter choice name.</param>
+ public DiscordApplicationCommandOptionChoice(string name, object value, DiscordApplicationCommandLocalization nameLocalizations = null)
+ {
+ if (!(value is string || value is long || value is int || value is double))
+ throw new InvalidOperationException($"Only {typeof(string)}, {typeof(long)}, {typeof(double)} or {typeof(int)} types may be passed to a command option choice.");
- if (name.Length > 100)
- throw new ArgumentException("Application command choice name cannot exceed 100 characters.", nameof(name));
- if (value is string val && val.Length > 100)
- throw new ArgumentException("Application command choice value cannot exceed 100 characters.", nameof(value));
+ if (name.Length > 100)
+ throw new ArgumentException("Application command choice name cannot exceed 100 characters.", nameof(name));
+ if (value is string val && val.Length > 100)
+ throw new ArgumentException("Application command choice value cannot exceed 100 characters.", nameof(value));
- this.Name = name;
- this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
- this.Value = value;
+ this.Name = name;
+ this.RawNameLocalizations = nameLocalizations?.GetKeyValuePairs();
+ this.Value = value;
+ }
}
}
diff --git a/DisCatSharp/Entities/Application/DiscordApplicationCommandPermission.cs b/DisCatSharp/Entities/Application/DiscordApplicationCommandPermission.cs
index 302081065..ddf5a5d1b 100644
--- a/DisCatSharp/Entities/Application/DiscordApplicationCommandPermission.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplicationCommandPermission.cs
@@ -1,109 +1,110 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a application command permission.
-/// </summary>
-public sealed class DiscordApplicationCommandPermission : SnowflakeObject, IEquatable<DiscordApplicationCommandPermission>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the id of the role or user.
+ /// Represents a application command permission.
/// </summary>
- [JsonProperty("id")]
- public new ulong Id { get; set; }
+ public sealed class DiscordApplicationCommandPermission : SnowflakeObject, IEquatable<DiscordApplicationCommandPermission>
+ {
+ /// <summary>
+ /// Gets the id of the role or user.
+ /// </summary>
+ [JsonProperty("id")]
+ public new ulong Id { get; set; }
- /// <summary>
- /// Gets the application command permission type.
- /// </summary>
- [JsonProperty("type")]
- public ApplicationCommandPermissionType Type { get; set; }
+ /// <summary>
+ /// Gets the application command permission type.
+ /// </summary>
+ [JsonProperty("type")]
+ public ApplicationCommandPermissionType Type { get; set; }
- /// <summary>
- /// Gets the permission .
- /// </summary>
- [JsonProperty("permission")]
- public bool Permission { get; set; }
+ /// <summary>
+ /// Gets the permission .
+ /// </summary>
+ [JsonProperty("permission")]
+ public bool Permission { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordApplicationCommandPermission"/> class.
- /// </summary>
- internal DiscordApplicationCommandPermission() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordApplicationCommandPermission"/> class.
+ /// </summary>
+ internal DiscordApplicationCommandPermission() { }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordApplicationCommandPermission"/> class.
- /// </summary>
- /// <param name="id">The Id of the role or user for this permission.</param>
- /// <param name="type">Defines whether the permission effects a user or role.</param>
- /// <param name="permission">The permission for this command. True allows the subject to use the command, false does not allow the subject to use the command.</param>
- public DiscordApplicationCommandPermission(ulong id, ApplicationCommandPermissionType type, bool permission)
- {
- this.Id = id;
- this.Type = type;
- this.Permission = permission;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordApplicationCommandPermission"/> class.
+ /// </summary>
+ /// <param name="id">The Id of the role or user for this permission.</param>
+ /// <param name="type">Defines whether the permission effects a user or role.</param>
+ /// <param name="permission">The permission for this command. True allows the subject to use the command, false does not allow the subject to use the command.</param>
+ public DiscordApplicationCommandPermission(ulong id, ApplicationCommandPermissionType type, bool permission)
+ {
+ this.Id = id;
+ this.Type = type;
+ this.Permission = permission;
+ }
- /// <summary>
- /// Checks whether this <see cref="DiscordApplicationCommandPermission"/> object is equal to another object.
- /// </summary>
- /// <param name="other">The command to compare to.</param>
- /// <returns>Whether the command is equal to this <see cref="DiscordApplicationCommandPermission"/>.</returns>
- public bool Equals(DiscordApplicationCommandPermission other)
- => this.Id == other.Id;
+ /// <summary>
+ /// Checks whether this <see cref="DiscordApplicationCommandPermission"/> object is equal to another object.
+ /// </summary>
+ /// <param name="other">The command to compare to.</param>
+ /// <returns>Whether the command is equal to this <see cref="DiscordApplicationCommandPermission"/>.</returns>
+ public bool Equals(DiscordApplicationCommandPermission other)
+ => this.Id == other.Id;
- /// <summary>
- /// Determines if two <see cref="DiscordApplicationCommandPermission"/> objects are equal.
- /// </summary>
- /// <param name="e1">The first command object.</param>
- /// <param name="e2">The second command object.</param>
- /// <returns>Whether the two <see cref="DiscordApplicationCommandPermission"/> objects are equal.</returns>
- public static bool operator ==(DiscordApplicationCommandPermission e1, DiscordApplicationCommandPermission e2)
- => e1.Equals(e2);
+ /// <summary>
+ /// Determines if two <see cref="DiscordApplicationCommandPermission"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">The first command object.</param>
+ /// <param name="e2">The second command object.</param>
+ /// <returns>Whether the two <see cref="DiscordApplicationCommandPermission"/> objects are equal.</returns>
+ public static bool operator ==(DiscordApplicationCommandPermission e1, DiscordApplicationCommandPermission e2)
+ => e1.Equals(e2);
- /// <summary>
- /// Determines if two <see cref="DiscordApplicationCommandPermission"/> objects are not equal.
- /// </summary>
- /// <param name="e1">The first command object.</param>
- /// <param name="e2">The second command object.</param>
- /// <returns>Whether the two <see cref="DiscordApplicationCommandPermission"/> objects are not equal.</returns>
- public static bool operator !=(DiscordApplicationCommandPermission e1, DiscordApplicationCommandPermission e2)
- => !(e1 == e2);
+ /// <summary>
+ /// Determines if two <see cref="DiscordApplicationCommandPermission"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">The first command object.</param>
+ /// <param name="e2">The second command object.</param>
+ /// <returns>Whether the two <see cref="DiscordApplicationCommandPermission"/> objects are not equal.</returns>
+ public static bool operator !=(DiscordApplicationCommandPermission e1, DiscordApplicationCommandPermission e2)
+ => !(e1 == e2);
- /// <summary>
- /// Determines if a <see cref="object"/> is equal to the current <see cref="DiscordApplicationCommand"/>.
- /// </summary>
- /// <param name="other">The object to compare to.</param>
- /// <returns>Whether the two <see cref="DiscordApplicationCommandPermission"/> objects are not equal.</returns>
- public override bool Equals(object other) => other is DiscordApplicationCommandPermission dacp && this.Equals(dacp);
+ /// <summary>
+ /// Determines if a <see cref="object"/> is equal to the current <see cref="DiscordApplicationCommand"/>.
+ /// </summary>
+ /// <param name="other">The object to compare to.</param>
+ /// <returns>Whether the two <see cref="DiscordApplicationCommandPermission"/> objects are not equal.</returns>
+ public override bool Equals(object other) => other is DiscordApplicationCommandPermission dacp && this.Equals(dacp);
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordApplicationCommandPermission"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordApplicationCommandPermission"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordApplicationCommandPermission"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordApplicationCommandPermission"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
+ }
}
diff --git a/DisCatSharp/Entities/Application/DiscordApplicationInstallParams.cs b/DisCatSharp/Entities/Application/DiscordApplicationInstallParams.cs
index 31b835994..e0c8b790f 100644
--- a/DisCatSharp/Entities/Application/DiscordApplicationInstallParams.cs
+++ b/DisCatSharp/Entities/Application/DiscordApplicationInstallParams.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// The application install params.
-/// </summary>
-public sealed class DiscordApplicationInstallParams
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the scopes.
+ /// The application install params.
/// </summary>
- [JsonProperty("scopes", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<string> Scopes { get; internal set; }
+ public sealed class DiscordApplicationInstallParams
+ {
+ /// <summary>
+ /// Gets the scopes.
+ /// </summary>
+ [JsonProperty("scopes", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<string> Scopes { get; internal set; }
- /// <summary>
- /// Gets or sets the permissions.
- /// </summary>
- [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions? Permissions { get; internal set; }
+ /// <summary>
+ /// Gets or sets the permissions.
+ /// </summary>
+ [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions? Permissions { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordApplicationInstallParams"/> class.
- /// </summary>
- internal DiscordApplicationInstallParams() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordApplicationInstallParams"/> class.
+ /// </summary>
+ internal DiscordApplicationInstallParams() { }
+ }
}
diff --git a/DisCatSharp/Entities/Application/DiscordGuildApplicationCommandPermission.cs b/DisCatSharp/Entities/Application/DiscordGuildApplicationCommandPermission.cs
index 899bfecde..cb06b4ad1 100644
--- a/DisCatSharp/Entities/Application/DiscordGuildApplicationCommandPermission.cs
+++ b/DisCatSharp/Entities/Application/DiscordGuildApplicationCommandPermission.cs
@@ -1,110 +1,111 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a guild application command permission.
-/// </summary>
-public sealed class DiscordGuildApplicationCommandPermission : SnowflakeObject, IEquatable<DiscordGuildApplicationCommandPermission>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the id of the command.
+ /// Represents a guild application command permission.
/// </summary>
- [JsonProperty("id")]
- public new ulong Id { get; set; }
+ public sealed class DiscordGuildApplicationCommandPermission : SnowflakeObject, IEquatable<DiscordGuildApplicationCommandPermission>
+ {
+ /// <summary>
+ /// Gets the id of the command.
+ /// </summary>
+ [JsonProperty("id")]
+ public new ulong Id { get; set; }
- /// <summary>
- /// Gets the unique ID of this command's application.
- /// </summary>
- [JsonProperty("application_id")]
- public ulong ApplicationId { get; set; }
+ /// <summary>
+ /// Gets the unique ID of this command's application.
+ /// </summary>
+ [JsonProperty("application_id")]
+ public ulong ApplicationId { get; set; }
- /// <summary>
- /// Gets the guild id this permission applies to.
- /// </summary>
- [JsonProperty("guild_id")]
- public ulong GuildId { get; set; }
+ /// <summary>
+ /// Gets the guild id this permission applies to.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
- /// <summary>
- /// Gets the guild this permission applies to.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild
- => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) ? guild : null;
+ /// <summary>
+ /// Gets the guild this permission applies to.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild
+ => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) ? guild : null;
- /// <summary>
- /// Gets the permission array.
- /// </summary>
- [JsonProperty("permissions")]
- public IReadOnlyList<DiscordApplicationCommandPermission> Permissions { get; set; }
+ /// <summary>
+ /// Gets the permission array.
+ /// </summary>
+ [JsonProperty("permissions")]
+ public IReadOnlyList<DiscordApplicationCommandPermission> Permissions { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordGuildApplicationCommandPermission"/> class.
- /// </summary>
- internal DiscordGuildApplicationCommandPermission() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordGuildApplicationCommandPermission"/> class.
+ /// </summary>
+ internal DiscordGuildApplicationCommandPermission() { }
- /// <summary>
- /// Checks whether this <see cref="DiscordGuildApplicationCommandPermission"/> object is equal to another object.
- /// </summary>
- /// <param name="other">The command to compare to.</param>
- /// <returns>Whether the command is equal to this <see cref="DiscordGuildApplicationCommandPermission"/>.</returns>
- public bool Equals(DiscordGuildApplicationCommandPermission other)
- => this.Id == other.Id;
+ /// <summary>
+ /// Checks whether this <see cref="DiscordGuildApplicationCommandPermission"/> object is equal to another object.
+ /// </summary>
+ /// <param name="other">The command to compare to.</param>
+ /// <returns>Whether the command is equal to this <see cref="DiscordGuildApplicationCommandPermission"/>.</returns>
+ public bool Equals(DiscordGuildApplicationCommandPermission other)
+ => this.Id == other.Id;
- /// <summary>
- /// Determines if two <see cref="DiscordGuildApplicationCommandPermission"/> objects are equal.
- /// </summary>
- /// <param name="e1">The first command object.</param>
- /// <param name="e2">The second command object.</param>
- /// <returns>Whether the two <see cref="DiscordGuildApplicationCommandPermission"/> objects are equal.</returns>
- public static bool operator ==(DiscordGuildApplicationCommandPermission e1, DiscordGuildApplicationCommandPermission e2)
- => e1.Equals(e2);
+ /// <summary>
+ /// Determines if two <see cref="DiscordGuildApplicationCommandPermission"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">The first command object.</param>
+ /// <param name="e2">The second command object.</param>
+ /// <returns>Whether the two <see cref="DiscordGuildApplicationCommandPermission"/> objects are equal.</returns>
+ public static bool operator ==(DiscordGuildApplicationCommandPermission e1, DiscordGuildApplicationCommandPermission e2)
+ => e1.Equals(e2);
- /// <summary>
- /// Determines if two <see cref="DiscordGuildApplicationCommandPermission"/> objects are not equal.
- /// </summary>
- /// <param name="e1">The first command object.</param>
- /// <param name="e2">The second command object.</param>
- /// <returns>Whether the two <see cref="DiscordGuildApplicationCommandPermission"/> objects are not equal.</returns>
- public static bool operator !=(DiscordGuildApplicationCommandPermission e1, DiscordGuildApplicationCommandPermission e2)
- => !(e1 == e2);
+ /// <summary>
+ /// Determines if two <see cref="DiscordGuildApplicationCommandPermission"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">The first command object.</param>
+ /// <param name="e2">The second command object.</param>
+ /// <returns>Whether the two <see cref="DiscordGuildApplicationCommandPermission"/> objects are not equal.</returns>
+ public static bool operator !=(DiscordGuildApplicationCommandPermission e1, DiscordGuildApplicationCommandPermission e2)
+ => !(e1 == e2);
- /// <summary>
- /// Determines if a <see cref="object"/> is equal to the current <see cref="DiscordApplicationCommand"/>.
- /// </summary>
- /// <param name="other">The object to compare to.</param>
- /// <returns>Whether the two <see cref="DiscordGuildApplicationCommandPermission"/> objects are not equal.</returns>
- public override bool Equals(object other) => other is DiscordGuildApplicationCommandPermission dgacp && this.Equals(dgacp);
+ /// <summary>
+ /// Determines if a <see cref="object"/> is equal to the current <see cref="DiscordApplicationCommand"/>.
+ /// </summary>
+ /// <param name="other">The object to compare to.</param>
+ /// <returns>Whether the two <see cref="DiscordGuildApplicationCommandPermission"/> objects are not equal.</returns>
+ public override bool Equals(object other) => other is DiscordGuildApplicationCommandPermission dgacp && this.Equals(dgacp);
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordGuildApplicationCommandPermission"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordGuildApplicationCommandPermission"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordGuildApplicationCommandPermission"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordGuildApplicationCommandPermission"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
+ }
}
diff --git a/DisCatSharp/Entities/Channel/DiscordChannel.cs b/DisCatSharp/Entities/Channel/DiscordChannel.cs
index e5a968d6c..4209178e8 100644
--- a/DisCatSharp/Entities/Channel/DiscordChannel.cs
+++ b/DisCatSharp/Entities/Channel/DiscordChannel.cs
@@ -1,1356 +1,1357 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Models;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord channel.
-/// </summary>
-public class DiscordChannel : SnowflakeObject, IEquatable<DiscordChannel>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets ID of the guild to which this channel belongs.
- /// </summary>
- [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? GuildId { get; internal set; }
-
- /// <summary>
- /// Gets ID of the category that contains this channel.
- /// </summary>
- [JsonProperty("parent_id", NullValueHandling = NullValueHandling.Include)]
- public ulong? ParentId { get; internal set; }
-
- /// <summary>
- /// Gets the category that contains this channel.
- /// </summary>
- [JsonIgnore]
- public DiscordChannel Parent
- => this.ParentId.HasValue ? this.Guild.GetChannel(this.ParentId.Value) : null;
-
- /// <summary>
- /// Gets the name of this channel.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the type of this channel.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public ChannelType Type { get; internal set; }
-
- /// <summary>
- /// Gets this channel's banner hash, when applicable.
- /// </summary>
- [JsonProperty("banner")]
- public string BannerHash { get; internal set; }
-
- /// <summary>
- /// Gets this channel's banner in url form.
- /// </summary>
- [JsonIgnore]
- public string BannerUrl
- => !string.IsNullOrWhiteSpace(this.BannerHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Uri}{Endpoints.CHANNELS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.BANNERS}/{this.BannerHash}.{(this.BannerHash.StartsWith("a_") ? "gif" : "png")}" : null;
-
- /// <summary>
- /// Gets the position of this channel.
- /// </summary>
- [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
- public int Position { get; internal set; }
-
- /// <summary>
- /// Gets the flags of this channel.
+ /// Represents a discord channel.
/// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public ChannelFlags Flags { get; internal set; }
-
- /// <summary>
- /// Gets the maximum available position to move the channel to.
- /// This can contain outdated information.
- /// </summary>
- public int GetMaxPosition()
- {
- var channels = this.Guild.Channels.Values;
- return this.ParentId != null
- ? this.Type == ChannelType.Text || this.Type == ChannelType.News
- ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Text || xc.Type == ChannelType.News)).OrderBy(xc => xc.Position).ToArray().Last().Position
- : this.Type == ChannelType.Voice || this.Type == ChannelType.Stage
- ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Voice || xc.Type == ChannelType.Stage)).OrderBy(xc => xc.Position).ToArray().Last().Position
- : channels.Where(xc => xc.ParentId == this.ParentId && xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray().Last().Position
- : channels.Where(xc => xc.ParentId == null && xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray().Last().Position;
- }
-
- /// <summary>
- /// Gets the minimum available position to move the channel to.
- /// </summary>
- public int GetMinPosition()
+ public class DiscordChannel : SnowflakeObject, IEquatable<DiscordChannel>
{
- var channels = this.Guild.Channels.Values;
- return this.ParentId != null
- ? this.Type == ChannelType.Text || this.Type == ChannelType.News
- ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Text || xc.Type == ChannelType.News)).OrderBy(xc => xc.Position).ToArray().First().Position
- : this.Type == ChannelType.Voice || this.Type == ChannelType.Stage
- ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Voice || xc.Type == ChannelType.Stage)).OrderBy(xc => xc.Position).ToArray().First().Position
- : channels.Where(xc => xc.ParentId == this.ParentId && xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray().First().Position
- : channels.Where(xc => xc.ParentId == null && xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray().First().Position;
- }
-
- /// <summary>
- /// Gets whether this channel is a DM channel.
- /// </summary>
- [JsonIgnore]
- public bool IsPrivate
- => this.Type == ChannelType.Private || this.Type == ChannelType.Group;
-
- /// <summary>
- /// Gets whether this channel is a channel category.
- /// </summary>
- [JsonIgnore]
- public bool IsCategory
- => this.Type == ChannelType.Category;
-
- /// <summary>
- /// Gets whether this channel is a stage channel.
- /// </summary>
- [JsonIgnore]
- public bool IsStage
- => this.Type == ChannelType.Stage;
-
- /// <summary>
- /// Gets the guild to which this channel belongs.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild
- => this.GuildId.HasValue && this.Discord.Guilds.TryGetValue(this.GuildId.Value, out var guild) ? guild : null;
-
- /// <summary>
- /// Gets a collection of permission overwrites for this channel.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordOverwrite> PermissionOverwrites
- => this._permissionOverwritesLazy.Value;
-
- [JsonProperty("permission_overwrites", NullValueHandling = NullValueHandling.Ignore)]
- internal List<DiscordOverwrite> PermissionOverwritesInternal = new();
- [JsonIgnore]
- private readonly Lazy<IReadOnlyList<DiscordOverwrite>> _permissionOverwritesLazy;
-
- /// <summary>
- /// Gets the channel's topic. This is applicable to text channels only.
- /// </summary>
- [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
- public string Topic { get; internal set; }
-
- /// <summary>
- /// Gets the ID of the last message sent in this channel. This is applicable to text channels only.
- /// </summary>
- [JsonProperty("last_message_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? LastMessageId { get; internal set; }
-
- /// <summary>
- /// Gets this channel's bitrate. This is applicable to voice channels only.
- /// </summary>
- [JsonProperty("bitrate", NullValueHandling = NullValueHandling.Ignore)]
- public int? Bitrate { get; internal set; }
-
- /// <summary>
- /// Gets this channel's user limit. This is applicable to voice channels only.
- /// </summary>
- [JsonProperty("user_limit", NullValueHandling = NullValueHandling.Ignore)]
- public int? UserLimit { get; internal set; }
-
- /// <summary>
- /// <para>Gets the slow mode delay configured for this channel.</para>
- /// <para>All bots, as well as users with <see cref="Permissions.ManageChannels"/> or <see cref="Permissions.ManageMessages"/> permissions in the channel are exempt from slow mode.</para>
- /// </summary>
- [JsonProperty("rate_limit_per_user")]
- public int? PerUserRateLimit { get; internal set; }
-
- /// <summary>
- /// Gets this channel's video quality mode. This is applicable to voice channels only.
- /// </summary>
- [JsonProperty("video_quality_mode", NullValueHandling = NullValueHandling.Ignore)]
- public VideoQualityMode? QualityMode { get; internal set; }
-
- /// <summary>
- /// List of available tags for forum posts.
- /// </summary>
- [JsonProperty("available_tags", NullValueHandling = NullValueHandling.Ignore)]
- public List<ForumPostTag> AvailableTags { get; internal set; }
-
- /// <summary>
- /// Starter template for forum posts.
- /// </summary>
- [JsonProperty("template", NullValueHandling = NullValueHandling.Ignore)]
- public string Template { get; internal set; }
-
- /// <summary>
- /// Gets when the last pinned message was pinned.
- /// </summary>
- [JsonIgnore]
- public DateTimeOffset? LastPinTimestamp
- => !string.IsNullOrWhiteSpace(this.LastPinTimestampRaw) && DateTimeOffset.TryParse(this.LastPinTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
- dto : null;
-
- /// <summary>
- /// Gets when the last pinned message was pinned as raw string.
- /// </summary>
- [JsonProperty("last_pin_timestamp", NullValueHandling = NullValueHandling.Ignore)]
- internal string LastPinTimestampRaw { get; set; }
-
- /// <summary>
- /// Gets this channel's default duration for newly created threads, in minutes, to automatically archive the thread after recent activity.
- /// </summary>
- [JsonProperty("default_auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
- public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { get; internal set; }
-
- /// <summary>
- /// Gets this channel's mention string.
- /// </summary>
- [JsonIgnore]
- public string Mention
- => Formatter.Mention(this);
-
- /// <summary>
- /// Gets this channel's children. This applies only to channel categories.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordChannel> Children =>
- !this.IsCategory
- ? throw new ArgumentException("Only channel categories contain children.")
- : this.Guild.ChannelsInternal.Values.Where(e => e.ParentId == this.Id).ToList();
-
- /// <summary>
- /// Gets the list of members currently in the channel (if voice channel), or members who can see the channel (otherwise).
- /// </summary>
- [JsonIgnore]
- public virtual IReadOnlyList<DiscordMember> Users =>
- this.Guild == null
- ? throw new InvalidOperationException("Cannot query users outside of guild channels.")
- : this.IsVoiceJoinable()
- ? this.Guild.Members.Values.Where(x => x.VoiceState?.ChannelId == this.Id).ToList()
- : this.Guild.Members.Values.Where(x => (this.PermissionsFor(x) & Permissions.AccessChannels) == Permissions.AccessChannels).ToList();
-
- /// <summary>
- /// Gets whether this channel is an NSFW channel.
- /// </summary>
- [JsonProperty("nsfw")]
- public bool IsNsfw { get; internal set; }
-
- /// <summary>
- /// Gets this channel's region id (if voice channel).
- /// </summary>
- [JsonProperty("rtc_region", NullValueHandling = NullValueHandling.Ignore)]
- internal string RtcRegionId { get; set; }
-
- /// <summary>
- /// Gets this channel's region override (if voice channel).
- /// </summary>
- [JsonIgnore]
- public DiscordVoiceRegion RtcRegion
- => this.RtcRegionId != null ? this.Discord.VoiceRegions[this.RtcRegionId] : null;
-
-
- /// <summary>
- /// Only sent on the resolved channels of interaction responses for application commands.
- /// Gets the permissions of the user in this channel who invoked the command.
- /// </summary>
- [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions? UserPermissions { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordChannel"/> class.
- /// </summary>
- internal DiscordChannel()
- {
- this._permissionOverwritesLazy = new Lazy<IReadOnlyList<DiscordOverwrite>>(() => new ReadOnlyCollection<DiscordOverwrite>(this.PermissionOverwritesInternal));
- }
-
- #region Methods
-
- /// <summary>
- /// Sends a message to this channel.
- /// </summary>
- /// <param name="content">Content of the message to send.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission if TTS is true and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(string content) =>
- !this.IsWritable()
- ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
- : this.Discord.ApiClient.CreateMessageAsync(this.Id, content, null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+ /// <summary>
+ /// Gets ID of the guild to which this channel belongs.
+ /// </summary>
+ [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? GuildId { get; internal set; }
+
+ /// <summary>
+ /// Gets ID of the category that contains this channel.
+ /// </summary>
+ [JsonProperty("parent_id", NullValueHandling = NullValueHandling.Include)]
+ public ulong? ParentId { get; internal set; }
+
+ /// <summary>
+ /// Gets the category that contains this channel.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel Parent
+ => this.ParentId.HasValue ? this.Guild.GetChannel(this.ParentId.Value) : null;
+
+ /// <summary>
+ /// Gets the name of this channel.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the type of this channel.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public ChannelType Type { get; internal set; }
+
+ /// <summary>
+ /// Gets this channel's banner hash, when applicable.
+ /// </summary>
+ [JsonProperty("banner")]
+ public string BannerHash { get; internal set; }
+
+ /// <summary>
+ /// Gets this channel's banner in url form.
+ /// </summary>
+ [JsonIgnore]
+ public string BannerUrl
+ => !string.IsNullOrWhiteSpace(this.BannerHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Uri}{Endpoints.CHANNELS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.BANNERS}/{this.BannerHash}.{(this.BannerHash.StartsWith("a_") ? "gif" : "png")}" : null;
+
+ /// <summary>
+ /// Gets the position of this channel.
+ /// </summary>
+ [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
+ public int Position { get; internal set; }
+
+ /// <summary>
+ /// Gets the flags of this channel.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public ChannelFlags Flags { get; internal set; }
+
+ /// <summary>
+ /// Gets the maximum available position to move the channel to.
+ /// This can contain outdated information.
+ /// </summary>
+ public int GetMaxPosition()
+ {
+ var channels = this.Guild.Channels.Values;
+ return this.ParentId != null
+ ? this.Type == ChannelType.Text || this.Type == ChannelType.News
+ ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Text || xc.Type == ChannelType.News)).OrderBy(xc => xc.Position).ToArray().Last().Position
+ : this.Type == ChannelType.Voice || this.Type == ChannelType.Stage
+ ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Voice || xc.Type == ChannelType.Stage)).OrderBy(xc => xc.Position).ToArray().Last().Position
+ : channels.Where(xc => xc.ParentId == this.ParentId && xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray().Last().Position
+ : channels.Where(xc => xc.ParentId == null && xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray().Last().Position;
+ }
- /// <summary>
- /// Sends a message to this channel.
- /// </summary>
- /// <param name="embed">Embed to attach to the message.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(DiscordEmbed embed) =>
- !this.IsWritable()
- ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
- : this.Discord.ApiClient.CreateMessageAsync(this.Id, null, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+ /// <summary>
+ /// Gets the minimum available position to move the channel to.
+ /// </summary>
+ public int GetMinPosition()
+ {
+ var channels = this.Guild.Channels.Values;
+ return this.ParentId != null
+ ? this.Type == ChannelType.Text || this.Type == ChannelType.News
+ ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Text || xc.Type == ChannelType.News)).OrderBy(xc => xc.Position).ToArray().First().Position
+ : this.Type == ChannelType.Voice || this.Type == ChannelType.Stage
+ ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Voice || xc.Type == ChannelType.Stage)).OrderBy(xc => xc.Position).ToArray().First().Position
+ : channels.Where(xc => xc.ParentId == this.ParentId && xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray().First().Position
+ : channels.Where(xc => xc.ParentId == null && xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray().First().Position;
+ }
- /// <summary>
- /// Sends a message to this channel.
- /// </summary>
- /// <param name="embed">Embed to attach to the message.</param>
- /// <param name="content">Content of the message to send.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission if TTS is true and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(string content, DiscordEmbed embed) =>
- !this.IsWritable()
- ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
- : this.Discord.ApiClient.CreateMessageAsync(this.Id, content, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+ /// <summary>
+ /// Gets whether this channel is a DM channel.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsPrivate
+ => this.Type == ChannelType.Private || this.Type == ChannelType.Group;
+
+ /// <summary>
+ /// Gets whether this channel is a channel category.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsCategory
+ => this.Type == ChannelType.Category;
+
+ /// <summary>
+ /// Gets whether this channel is a stage channel.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsStage
+ => this.Type == ChannelType.Stage;
+
+ /// <summary>
+ /// Gets the guild to which this channel belongs.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild
+ => this.GuildId.HasValue && this.Discord.Guilds.TryGetValue(this.GuildId.Value, out var guild) ? guild : null;
+
+ /// <summary>
+ /// Gets a collection of permission overwrites for this channel.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordOverwrite> PermissionOverwrites
+ => this._permissionOverwritesLazy.Value;
+
+ [JsonProperty("permission_overwrites", NullValueHandling = NullValueHandling.Ignore)]
+ internal List<DiscordOverwrite> PermissionOverwritesInternal = new();
+ [JsonIgnore]
+ private readonly Lazy<IReadOnlyList<DiscordOverwrite>> _permissionOverwritesLazy;
+
+ /// <summary>
+ /// Gets the channel's topic. This is applicable to text channels only.
+ /// </summary>
+ [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
+ public string Topic { get; internal set; }
+
+ /// <summary>
+ /// Gets the ID of the last message sent in this channel. This is applicable to text channels only.
+ /// </summary>
+ [JsonProperty("last_message_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? LastMessageId { get; internal set; }
+
+ /// <summary>
+ /// Gets this channel's bitrate. This is applicable to voice channels only.
+ /// </summary>
+ [JsonProperty("bitrate", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Bitrate { get; internal set; }
+
+ /// <summary>
+ /// Gets this channel's user limit. This is applicable to voice channels only.
+ /// </summary>
+ [JsonProperty("user_limit", NullValueHandling = NullValueHandling.Ignore)]
+ public int? UserLimit { get; internal set; }
+
+ /// <summary>
+ /// <para>Gets the slow mode delay configured for this channel.</para>
+ /// <para>All bots, as well as users with <see cref="Permissions.ManageChannels"/> or <see cref="Permissions.ManageMessages"/> permissions in the channel are exempt from slow mode.</para>
+ /// </summary>
+ [JsonProperty("rate_limit_per_user")]
+ public int? PerUserRateLimit { get; internal set; }
+
+ /// <summary>
+ /// Gets this channel's video quality mode. This is applicable to voice channels only.
+ /// </summary>
+ [JsonProperty("video_quality_mode", NullValueHandling = NullValueHandling.Ignore)]
+ public VideoQualityMode? QualityMode { get; internal set; }
+
+ /// <summary>
+ /// List of available tags for forum posts.
+ /// </summary>
+ [JsonProperty("available_tags", NullValueHandling = NullValueHandling.Ignore)]
+ public List<ForumPostTag> AvailableTags { get; internal set; }
+
+ /// <summary>
+ /// Starter template for forum posts.
+ /// </summary>
+ [JsonProperty("template", NullValueHandling = NullValueHandling.Ignore)]
+ public string Template { get; internal set; }
+
+ /// <summary>
+ /// Gets when the last pinned message was pinned.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset? LastPinTimestamp
+ => !string.IsNullOrWhiteSpace(this.LastPinTimestampRaw) && DateTimeOffset.TryParse(this.LastPinTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
+ dto : null;
+
+ /// <summary>
+ /// Gets when the last pinned message was pinned as raw string.
+ /// </summary>
+ [JsonProperty("last_pin_timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ internal string LastPinTimestampRaw { get; set; }
+
+ /// <summary>
+ /// Gets this channel's default duration for newly created threads, in minutes, to automatically archive the thread after recent activity.
+ /// </summary>
+ [JsonProperty("default_auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
+ public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { get; internal set; }
+
+ /// <summary>
+ /// Gets this channel's mention string.
+ /// </summary>
+ [JsonIgnore]
+ public string Mention
+ => Formatter.Mention(this);
+
+ /// <summary>
+ /// Gets this channel's children. This applies only to channel categories.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordChannel> Children =>
+ !this.IsCategory
+ ? throw new ArgumentException("Only channel categories contain children.")
+ : this.Guild.ChannelsInternal.Values.Where(e => e.ParentId == this.Id).ToList();
+
+ /// <summary>
+ /// Gets the list of members currently in the channel (if voice channel), or members who can see the channel (otherwise).
+ /// </summary>
+ [JsonIgnore]
+ public virtual IReadOnlyList<DiscordMember> Users =>
+ this.Guild == null
+ ? throw new InvalidOperationException("Cannot query users outside of guild channels.")
+ : this.IsVoiceJoinable()
+ ? this.Guild.Members.Values.Where(x => x.VoiceState?.ChannelId == this.Id).ToList()
+ : this.Guild.Members.Values.Where(x => (this.PermissionsFor(x) & Permissions.AccessChannels) == Permissions.AccessChannels).ToList();
+
+ /// <summary>
+ /// Gets whether this channel is an NSFW channel.
+ /// </summary>
+ [JsonProperty("nsfw")]
+ public bool IsNsfw { get; internal set; }
+
+ /// <summary>
+ /// Gets this channel's region id (if voice channel).
+ /// </summary>
+ [JsonProperty("rtc_region", NullValueHandling = NullValueHandling.Ignore)]
+ internal string RtcRegionId { get; set; }
+
+ /// <summary>
+ /// Gets this channel's region override (if voice channel).
+ /// </summary>
+ [JsonIgnore]
+ public DiscordVoiceRegion RtcRegion
+ => this.RtcRegionId != null ? this.Discord.VoiceRegions[this.RtcRegionId] : null;
+
+
+ /// <summary>
+ /// Only sent on the resolved channels of interaction responses for application commands.
+ /// Gets the permissions of the user in this channel who invoked the command.
+ /// </summary>
+ [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions? UserPermissions { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordChannel"/> class.
+ /// </summary>
+ internal DiscordChannel()
+ {
+ this._permissionOverwritesLazy = new Lazy<IReadOnlyList<DiscordOverwrite>>(() => new ReadOnlyCollection<DiscordOverwrite>(this.PermissionOverwritesInternal));
+ }
- /// <summary>
- /// Sends a message to this channel.
- /// </summary>
- /// <param name="builder">The builder with all the items to send.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission TTS is true and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(DiscordMessageBuilder builder)
- => this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
+ #region Methods
+
+ /// <summary>
+ /// Sends a message to this channel.
+ /// </summary>
+ /// <param name="content">Content of the message to send.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission if TTS is true and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(string content) =>
+ !this.IsWritable()
+ ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
+ : this.Discord.ApiClient.CreateMessageAsync(this.Id, content, null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Sends a message to this channel.
+ /// </summary>
+ /// <param name="embed">Embed to attach to the message.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(DiscordEmbed embed) =>
+ !this.IsWritable()
+ ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
+ : this.Discord.ApiClient.CreateMessageAsync(this.Id, null, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Sends a message to this channel.
+ /// </summary>
+ /// <param name="embed">Embed to attach to the message.</param>
+ /// <param name="content">Content of the message to send.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission if TTS is true and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(string content, DiscordEmbed embed) =>
+ !this.IsWritable()
+ ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
+ : this.Discord.ApiClient.CreateMessageAsync(this.Id, content, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Sends a message to this channel.
+ /// </summary>
+ /// <param name="builder">The builder with all the items to send.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission TTS is true and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(DiscordMessageBuilder builder)
+ => this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
+
+ /// <summary>
+ /// Sends a message to this channel.
+ /// </summary>
+ /// <param name="action">The builder with all the items to send.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission TTS is true and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> SendMessageAsync(Action<DiscordMessageBuilder> action)
+ {
+ var builder = new DiscordMessageBuilder();
+ action(builder);
- /// <summary>
- /// Sends a message to this channel.
- /// </summary>
- /// <param name="action">The builder with all the items to send.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission TTS is true and <see cref="Permissions.SendTtsMessages"/> if TTS is true.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> SendMessageAsync(Action<DiscordMessageBuilder> action)
- {
- var builder = new DiscordMessageBuilder();
- action(builder);
+ return !this.IsWritable()
+ ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
+ : this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
+ }
- return !this.IsWritable()
- ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
- : this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
- }
+ /// <summary>
+ /// Deletes a guild channel
+ /// </summary>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteAsync(string reason = null)
+ => this.Discord.ApiClient.DeleteChannelAsync(this.Id, reason);
+
+ /// <summary>
+ /// Clones this channel. This operation will create a channel with identical settings to this one. Note that this will not copy messages.
+ /// </summary>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>Newly-created channel.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordChannel> CloneAsync(string reason = null)
+ {
+ if (this.Guild == null)
+ throw new InvalidOperationException("Non-guild channels cannot be cloned.");
- /// <summary>
- /// Deletes a guild channel
- /// </summary>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteAsync(string reason = null)
- => this.Discord.ApiClient.DeleteChannelAsync(this.Id, reason);
+ var ovrs = new List<DiscordOverwriteBuilder>();
+ foreach (var ovr in this.PermissionOverwritesInternal)
+ ovrs.Add(await new DiscordOverwriteBuilder().FromAsync(ovr).ConfigureAwait(false));
- /// <summary>
- /// Clones this channel. This operation will create a channel with identical settings to this one. Note that this will not copy messages.
- /// </summary>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>Newly-created channel.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordChannel> CloneAsync(string reason = null)
- {
- if (this.Guild == null)
- throw new InvalidOperationException("Non-guild channels cannot be cloned.");
+ var bitrate = this.Bitrate;
+ var userLimit = this.UserLimit;
+ Optional<int?> perUserRateLimit = this.PerUserRateLimit;
- var ovrs = new List<DiscordOverwriteBuilder>();
- foreach (var ovr in this.PermissionOverwritesInternal)
- ovrs.Add(await new DiscordOverwriteBuilder().FromAsync(ovr).ConfigureAwait(false));
+ if (!this.IsVoiceJoinable())
+ {
+ bitrate = null;
+ userLimit = null;
+ }
- var bitrate = this.Bitrate;
- var userLimit = this.UserLimit;
- Optional<int?> perUserRateLimit = this.PerUserRateLimit;
+ if (this.Type == ChannelType.Stage)
+ {
+ userLimit = null;
+ }
+ if (!this.IsWritable())
+ {
+ perUserRateLimit = Optional.None;
+ }
- if (!this.IsVoiceJoinable())
- {
- bitrate = null;
- userLimit = null;
+ return await this.Guild.CreateChannelAsync(this.Name, this.Type, this.Parent, this.Topic, bitrate, userLimit, ovrs, this.IsNsfw, perUserRateLimit, this.QualityMode, this.DefaultAutoArchiveDuration, reason).ConfigureAwait(false);
}
- if (this.Type == ChannelType.Stage)
+ /// <summary>
+ /// Returns a specific message
+ /// </summary>
+ /// <param name="id">The id of the message</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> GetMessageAsync(ulong id) =>
+ this.Discord.Configuration.MessageCacheSize > 0
+ && this.Discord is DiscordClient dc
+ && dc.MessageCache != null
+ && dc.MessageCache.TryGet(xm => xm.Id == id && xm.ChannelId == this.Id, out var msg)
+ ? msg
+ : await this.Discord.ApiClient.GetMessageAsync(this.Id, id).ConfigureAwait(false);
+
+ /// <summary>
+ /// Modifies the current channel.
+ /// </summary>
+ /// <param name="action">Action to perform on this channel</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/>.</exception>
+ /// <exception cref="System.NotSupportedException">Thrown when the client does not have the correct <see cref="PremiumTier"/> for modifying the <see cref="ThreadAutoArchiveDuration"/>.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ModifyAsync(Action<ChannelEditModel> action)
{
- userLimit = null;
- }
- if (!this.IsWritable())
- {
- perUserRateLimit = Optional.None;
- }
-
- return await this.Guild.CreateChannelAsync(this.Name, this.Type, this.Parent, this.Topic, bitrate, userLimit, ovrs, this.IsNsfw, perUserRateLimit, this.QualityMode, this.DefaultAutoArchiveDuration, reason).ConfigureAwait(false);
- }
+ var mdl = new ChannelEditModel();
+ action(mdl);
- /// <summary>
- /// Returns a specific message
- /// </summary>
- /// <param name="id">The id of the message</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> GetMessageAsync(ulong id) =>
- this.Discord.Configuration.MessageCacheSize > 0
- && this.Discord is DiscordClient dc
- && dc.MessageCache != null
- && dc.MessageCache.TryGet(xm => xm.Id == id && xm.ChannelId == this.Id, out var msg)
- ? msg
- : await this.Discord.ApiClient.GetMessageAsync(this.Id, id).ConfigureAwait(false);
+ if (mdl.DefaultAutoArchiveDuration.HasValue)
+ {
+ if (!Utilities.CheckThreadAutoArchiveDurationFeature(this.Guild, mdl.DefaultAutoArchiveDuration.Value))
+ throw new NotSupportedException($"Cannot modify DefaultAutoArchiveDuration. Guild needs boost tier {(mdl.DefaultAutoArchiveDuration.Value == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
+ }
+ if (mdl.Banner.HasValue)
+ {
+ if (!this.Guild.Features.CanSetChannelBanner)
+ throw new NotSupportedException($"Cannot modify Banner. Guild needs boost tier three.");
+ }
- /// <summary>
- /// Modifies the current channel.
- /// </summary>
- /// <param name="action">Action to perform on this channel</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/>.</exception>
- /// <exception cref="System.NotSupportedException">Thrown when the client does not have the correct <see cref="PremiumTier"/> for modifying the <see cref="ThreadAutoArchiveDuration"/>.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ModifyAsync(Action<ChannelEditModel> action)
- {
- var mdl = new ChannelEditModel();
- action(mdl);
+ var bannerb64 = ImageTool.Base64FromStream(mdl.Banner);
- if (mdl.DefaultAutoArchiveDuration.HasValue)
- {
- if (!Utilities.CheckThreadAutoArchiveDurationFeature(this.Guild, mdl.DefaultAutoArchiveDuration.Value))
- throw new NotSupportedException($"Cannot modify DefaultAutoArchiveDuration. Guild needs boost tier {(mdl.DefaultAutoArchiveDuration.Value == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
- }
- if (mdl.Banner.HasValue)
- {
- if (!this.Guild.Features.CanSetChannelBanner)
- throw new NotSupportedException($"Cannot modify Banner. Guild needs boost tier three.");
+ return this.Discord.ApiClient.ModifyChannelAsync(this.Id, mdl.Name, mdl.Position, mdl.Topic, mdl.Nsfw,
+ mdl.Parent.Map(p => p?.Id), mdl.Bitrate, mdl.UserLimit, mdl.PerUserRateLimit, mdl.RtcRegion.Map(r => r?.Id),
+ mdl.QualityMode, mdl.DefaultAutoArchiveDuration, mdl.Type, mdl.PermissionOverwrites, bannerb64, mdl.AuditLogReason);
}
- var bannerb64 = ImageTool.Base64FromStream(mdl.Banner);
-
- return this.Discord.ApiClient.ModifyChannelAsync(this.Id, mdl.Name, mdl.Position, mdl.Topic, mdl.Nsfw,
- mdl.Parent.Map(p => p?.Id), mdl.Bitrate, mdl.UserLimit, mdl.PerUserRateLimit, mdl.RtcRegion.Map(r => r?.Id),
- mdl.QualityMode, mdl.DefaultAutoArchiveDuration, mdl.Type, mdl.PermissionOverwrites, bannerb64, mdl.AuditLogReason);
- }
-
- /// <summary>
- /// Updates the channel position when it doesn't have a category.
- ///
- /// Use <see cref="ModifyParentAsync"/> for moving to other categories.
- /// Use <see cref="RemoveParentAsync"/> to move out of a category.
- /// Use <see cref="ModifyPositionInCategoryAsync"/> for moving within a category.
- /// </summary>
- /// <param name="position">Position the channel should be moved to.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ModifyPositionAsync(int position, string reason = null)
- {
- if (this.Guild == null)
- throw new ArgumentException("Cannot modify order of non-guild channels.");
- if (!this.IsMovable())
- throw new NotSupportedException("You can't move this type of channel in categories.");
- if (this.ParentId != null)
- throw new ArgumentException("Cannot modify order of channels within a category. Use ModifyPositionInCategoryAsync instead.");
-
- var chns = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray();
- var pmds = new RestGuildChannelReorderPayload[chns.Length];
- for (var i = 0; i < chns.Length; i++)
+ /// <summary>
+ /// Updates the channel position when it doesn't have a category.
+ ///
+ /// Use <see cref="ModifyParentAsync"/> for moving to other categories.
+ /// Use <see cref="RemoveParentAsync"/> to move out of a category.
+ /// Use <see cref="ModifyPositionInCategoryAsync"/> for moving within a category.
+ /// </summary>
+ /// <param name="position">Position the channel should be moved to.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ModifyPositionAsync(int position, string reason = null)
{
- pmds[i] = new RestGuildChannelReorderPayload
+ if (this.Guild == null)
+ throw new ArgumentException("Cannot modify order of non-guild channels.");
+ if (!this.IsMovable())
+ throw new NotSupportedException("You can't move this type of channel in categories.");
+ if (this.ParentId != null)
+ throw new ArgumentException("Cannot modify order of channels within a category. Use ModifyPositionInCategoryAsync instead.");
+
+ var chns = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type).OrderBy(xc => xc.Position).ToArray();
+ var pmds = new RestGuildChannelReorderPayload[chns.Length];
+ for (var i = 0; i < chns.Length; i++)
{
- ChannelId = chns[i].Id,
- Position = chns[i].Id == this.Id ? position : chns[i].Position >= position ? chns[i].Position + 1 : chns[i].Position
- };
- }
+ pmds[i] = new RestGuildChannelReorderPayload
+ {
+ ChannelId = chns[i].Id,
+ Position = chns[i].Id == this.Id ? position : chns[i].Position >= position ? chns[i].Position + 1 : chns[i].Position
+ };
+ }
- return this.Discord.ApiClient.ModifyGuildChannelPositionAsync(this.Guild.Id, pmds, reason);
- }
+ return this.Discord.ApiClient.ModifyGuildChannelPositionAsync(this.Guild.Id, pmds, reason);
+ }
- /// <summary>
- /// Updates the channel position within it's own category.
- ///
- /// Use <see cref="ModifyParentAsync"/> for moving to other categories.
- /// Use <see cref="RemoveParentAsync"/> to move out of a category.
- /// Use <see cref="ModifyPositionAsync"/> to move channels outside a category.
- /// </summary>
- /// <param name="position">The position.</param>
- /// <param name="reason">The reason.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.IndexOutOfRangeException">Thrown when <paramref name="position"/> is out of range.</exception>
- /// <exception cref="System.ArgumentException">Thrown when function is called on a channel without a parent channel.</exception>
- public async Task ModifyPositionInCategoryAsync(int position, string reason = null)
- {
- if (!this.IsMovableInParent())
- throw new NotSupportedException("You can't move this type of channel in categories.");
-
- var isUp = position > this.Position;
-
- var channels = await this.InternalRefreshChannelsAsync();
-
- var chns = this.ParentId != null
- ? this.Type == ChannelType.Text || this.Type == ChannelType.News
- ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Text || xc.Type == ChannelType.News))
- : this.Type == ChannelType.Voice || this.Type == ChannelType.Stage
- ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Voice || xc.Type == ChannelType.Stage))
- : channels.Where(xc => xc.ParentId == this.ParentId && xc.Type == this.Type)
- : this.Type == ChannelType.Text || this.Type == ChannelType.News
- ? channels.Where(xc => xc.ParentId == null && (xc.Type == ChannelType.Text || xc.Type == ChannelType.News))
- : this.Type == ChannelType.Voice || this.Type == ChannelType.Stage
- ? channels.Where(xc => xc.ParentId == null && (xc.Type == ChannelType.Voice || xc.Type == ChannelType.Stage))
- : channels.Where(xc => xc.ParentId == null && xc.Type == this.Type);
-
- var ochns = chns.OrderBy(xc => xc.Position).ToArray();
- var min = ochns.First().Position;
- var max = ochns.Last().Position;
-
- if (position > max || position < min)
- throw new IndexOutOfRangeException($"Position is not in range. {position} is {(position > max ? "greater then the maximal" : "lower then the minimal")} position.");
-
- var pmds = new RestGuildChannelReorderPayload[ochns.Length];
- for (var i = 0; i < ochns.Length; i++)
+ /// <summary>
+ /// Updates the channel position within it's own category.
+ ///
+ /// Use <see cref="ModifyParentAsync"/> for moving to other categories.
+ /// Use <see cref="RemoveParentAsync"/> to move out of a category.
+ /// Use <see cref="ModifyPositionAsync"/> to move channels outside a category.
+ /// </summary>
+ /// <param name="position">The position.</param>
+ /// <param name="reason">The reason.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.IndexOutOfRangeException">Thrown when <paramref name="position"/> is out of range.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when function is called on a channel without a parent channel.</exception>
+ public async Task ModifyPositionInCategoryAsync(int position, string reason = null)
{
- pmds[i] = new RestGuildChannelReorderPayload
+ if (!this.IsMovableInParent())
+ throw new NotSupportedException("You can't move this type of channel in categories.");
+
+ var isUp = position > this.Position;
+
+ var channels = await this.InternalRefreshChannelsAsync();
+
+ var chns = this.ParentId != null
+ ? this.Type == ChannelType.Text || this.Type == ChannelType.News
+ ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Text || xc.Type == ChannelType.News))
+ : this.Type == ChannelType.Voice || this.Type == ChannelType.Stage
+ ? channels.Where(xc => xc.ParentId == this.ParentId && (xc.Type == ChannelType.Voice || xc.Type == ChannelType.Stage))
+ : channels.Where(xc => xc.ParentId == this.ParentId && xc.Type == this.Type)
+ : this.Type == ChannelType.Text || this.Type == ChannelType.News
+ ? channels.Where(xc => xc.ParentId == null && (xc.Type == ChannelType.Text || xc.Type == ChannelType.News))
+ : this.Type == ChannelType.Voice || this.Type == ChannelType.Stage
+ ? channels.Where(xc => xc.ParentId == null && (xc.Type == ChannelType.Voice || xc.Type == ChannelType.Stage))
+ : channels.Where(xc => xc.ParentId == null && xc.Type == this.Type);
+
+ var ochns = chns.OrderBy(xc => xc.Position).ToArray();
+ var min = ochns.First().Position;
+ var max = ochns.Last().Position;
+
+ if (position > max || position < min)
+ throw new IndexOutOfRangeException($"Position is not in range. {position} is {(position > max ? "greater then the maximal" : "lower then the minimal")} position.");
+
+ var pmds = new RestGuildChannelReorderPayload[ochns.Length];
+ for (var i = 0; i < ochns.Length; i++)
{
- ChannelId = ochns[i].Id,
- };
+ pmds[i] = new RestGuildChannelReorderPayload
+ {
+ ChannelId = ochns[i].Id,
+ };
- if (ochns[i].Id == this.Id)
- {
- pmds[i].Position = position;
- }
- else
- {
- if (isUp)
+ if (ochns[i].Id == this.Id)
{
- if (ochns[i].Position <= position && ochns[i].Position > this.Position)
- {
- pmds[i].Position = ochns[i].Position - 1;
- }
- else if (ochns[i].Position < this.Position || ochns[i].Position > position)
- {
- pmds[i].Position = ochns[i].Position;
- }
+ pmds[i].Position = position;
}
else
{
- if (ochns[i].Position >= position && ochns[i].Position < this.Position)
+ if (isUp)
{
- pmds[i].Position = ochns[i].Position + 1;
+ if (ochns[i].Position <= position && ochns[i].Position > this.Position)
+ {
+ pmds[i].Position = ochns[i].Position - 1;
+ }
+ else if (ochns[i].Position < this.Position || ochns[i].Position > position)
+ {
+ pmds[i].Position = ochns[i].Position;
+ }
}
- else if (ochns[i].Position > this.Position || ochns[i].Position < position)
+ else
{
- pmds[i].Position = ochns[i].Position;
+ if (ochns[i].Position >= position && ochns[i].Position < this.Position)
+ {
+ pmds[i].Position = ochns[i].Position + 1;
+ }
+ else if (ochns[i].Position > this.Position || ochns[i].Position < position)
+ {
+ pmds[i].Position = ochns[i].Position;
+ }
}
}
}
- }
- await this.Discord.ApiClient.ModifyGuildChannelPositionAsync(this.Guild.Id, pmds, reason).ConfigureAwait(false);
- }
+ await this.Discord.ApiClient.ModifyGuildChannelPositionAsync(this.Guild.Id, pmds, reason).ConfigureAwait(false);
+ }
- /// <summary>
- /// Internally refreshes the channel list.
- /// </summary>
- private async Task<IReadOnlyList<DiscordChannel>> InternalRefreshChannelsAsync()
- {
- await this.RefreshPositionsAsync();
- return this.Guild.Channels.Values.ToList().AsReadOnly();
- }
+ /// <summary>
+ /// Internally refreshes the channel list.
+ /// </summary>
+ private async Task<IReadOnlyList<DiscordChannel>> InternalRefreshChannelsAsync()
+ {
+ await this.RefreshPositionsAsync();
+ return this.Guild.Channels.Values.ToList().AsReadOnly();
+ }
- /// <summary>
- /// Refreshes the positions.
- /// </summary>
- public async Task RefreshPositionsAsync()
- {
- var channels = await this.Discord.ApiClient.GetGuildChannelsAsync(this.Guild.Id);
- this.Guild.ChannelsInternal.Clear();
- foreach (var channel in channels.ToList())
+ /// <summary>
+ /// Refreshes the positions.
+ /// </summary>
+ public async Task RefreshPositionsAsync()
{
- channel.Discord = this.Discord;
- foreach (var xo in channel.PermissionOverwritesInternal)
+ var channels = await this.Discord.ApiClient.GetGuildChannelsAsync(this.Guild.Id);
+ this.Guild.ChannelsInternal.Clear();
+ foreach (var channel in channels.ToList())
{
- xo.Discord = this.Discord;
- xo.ChannelId = channel.Id;
+ channel.Discord = this.Discord;
+ foreach (var xo in channel.PermissionOverwritesInternal)
+ {
+ xo.Discord = this.Discord;
+ xo.ChannelId = channel.Id;
+ }
+ this.Guild.ChannelsInternal[channel.Id] = channel;
}
- this.Guild.ChannelsInternal[channel.Id] = channel;
}
- }
- /// <summary>
- /// Updates the channel position within it's own category.
- /// Valid modes: '+' or 'down' to move a channel down | '-' or 'up' to move a channel up.
- ///
- /// Use <see cref="ModifyParentAsync"/> for moving to other categories.
- /// Use <see cref="RemoveParentAsync"/> to move out of a category.
- /// Use <see cref="ModifyPositionAsync"/> to move channels outside a category.
- /// </summary>
- /// <param name="mode">The mode. Valid: '+' or 'down' to move a channel down | '-' or 'up' to move a channel up</param>
- /// <param name="position">The position.</param>
- /// <param name="reason">The reason.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.IndexOutOfRangeException">Thrown when <paramref name="position"/> is out of range.</exception>
- /// <exception cref="System.ArgumentException">Thrown when function is called on a channel without a parent channel, a wrong mode is givven or given position is zero.</exception>
- public Task ModifyPositionInCategorySmartAsync(string mode, int position, string reason = null)
- {
- if (!this.IsMovableInParent())
- throw new NotSupportedException("You can't move this type of channel in categories.");
-
- if (mode != "+" && mode != "-" && mode != "down" && mode != "up")
- throw new ArgumentException("Error with the selected mode: Valid is '+' or 'down' to move a channel down and '-' or 'up' to move a channel up");
-
- var positive = mode == "+" || mode == "positive" || mode == "down";
- var negative = mode == "-" || mode == "negative" || mode == "up";
- return positive
- ? position < this.GetMaxPosition()
- ? this.ModifyPositionInCategoryAsync(this.Position + position, reason)
- : throw new IndexOutOfRangeException($"Position is not in range of category.")
- : negative
- ? position > this.GetMinPosition()
- ? this.ModifyPositionInCategoryAsync(this.Position - position, reason)
+ /// <summary>
+ /// Updates the channel position within it's own category.
+ /// Valid modes: '+' or 'down' to move a channel down | '-' or 'up' to move a channel up.
+ ///
+ /// Use <see cref="ModifyParentAsync"/> for moving to other categories.
+ /// Use <see cref="RemoveParentAsync"/> to move out of a category.
+ /// Use <see cref="ModifyPositionAsync"/> to move channels outside a category.
+ /// </summary>
+ /// <param name="mode">The mode. Valid: '+' or 'down' to move a channel down | '-' or 'up' to move a channel up</param>
+ /// <param name="position">The position.</param>
+ /// <param name="reason">The reason.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.IndexOutOfRangeException">Thrown when <paramref name="position"/> is out of range.</exception>
+ /// <exception cref="System.ArgumentException">Thrown when function is called on a channel without a parent channel, a wrong mode is givven or given position is zero.</exception>
+ public Task ModifyPositionInCategorySmartAsync(string mode, int position, string reason = null)
+ {
+ if (!this.IsMovableInParent())
+ throw new NotSupportedException("You can't move this type of channel in categories.");
+
+ if (mode != "+" && mode != "-" && mode != "down" && mode != "up")
+ throw new ArgumentException("Error with the selected mode: Valid is '+' or 'down' to move a channel down and '-' or 'up' to move a channel up");
+
+ var positive = mode == "+" || mode == "positive" || mode == "down";
+ var negative = mode == "-" || mode == "negative" || mode == "up";
+ return positive
+ ? position < this.GetMaxPosition()
+ ? this.ModifyPositionInCategoryAsync(this.Position + position, reason)
: throw new IndexOutOfRangeException($"Position is not in range of category.")
- : throw new ArgumentException("You can only modify with +X or -X. 0 is not valid.");
- }
+ : negative
+ ? position > this.GetMinPosition()
+ ? this.ModifyPositionInCategoryAsync(this.Position - position, reason)
+ : throw new IndexOutOfRangeException($"Position is not in range of category.")
+ : throw new ArgumentException("You can only modify with +X or -X. 0 is not valid.");
+ }
- /// <summary>
- /// Updates the channel parent, moving the channel to the bottom of the new category.
- /// </summary>
- /// <param name="newParent">New parent for channel. Use <see cref="RemoveParentAsync(string)"/> to remove from parent.</param>
- /// <param name="lockPermissions">Sync permissions with parent. Defaults to null.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ModifyParentAsync(DiscordChannel newParent, bool? lockPermissions = null, string reason = null)
- {
- if (this.Guild == null)
- throw new ArgumentException("Cannot modify parent of non-guild channels.");
- if (!this.IsMovableInParent())
- throw new NotSupportedException("You can't move this type of channel in categories.");
- if (newParent.Type is not ChannelType.Category)
- throw new ArgumentException("Only category type channels can be parents.");
+ /// <summary>
+ /// Updates the channel parent, moving the channel to the bottom of the new category.
+ /// </summary>
+ /// <param name="newParent">New parent for channel. Use <see cref="RemoveParentAsync(string)"/> to remove from parent.</param>
+ /// <param name="lockPermissions">Sync permissions with parent. Defaults to null.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ModifyParentAsync(DiscordChannel newParent, bool? lockPermissions = null, string reason = null)
+ {
+ if (this.Guild == null)
+ throw new ArgumentException("Cannot modify parent of non-guild channels.");
+ if (!this.IsMovableInParent())
+ throw new NotSupportedException("You can't move this type of channel in categories.");
+ if (newParent.Type is not ChannelType.Category)
+ throw new ArgumentException("Only category type channels can be parents.");
- var position = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type && xc.ParentId == newParent.Id) // gets list same type channels in parent
+ var position = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type && xc.ParentId == newParent.Id) // gets list same type channels in parent
.Select(xc => xc.Position).DefaultIfEmpty(-1).Max() + 1; // returns highest position of list +1, default val: 0
- var chns = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type)
- .OrderBy(xc => xc.Position).ToArray();
+ var chns = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type)
+ .OrderBy(xc => xc.Position).ToArray();
- var pmds = new RestGuildChannelNewParentPayload[chns.Length];
+ var pmds = new RestGuildChannelNewParentPayload[chns.Length];
- for (var i = 0; i < chns.Length; i++)
- {
- pmds[i] = new RestGuildChannelNewParentPayload
- {
- ChannelId = chns[i].Id,
- Position = chns[i].Position >= position ? chns[i].Position + 1 : chns[i].Position,
- };
- if (chns[i].Id == this.Id)
+ for (var i = 0; i < chns.Length; i++)
{
- pmds[i].Position = position;
- pmds[i].ParentId = newParent is not null ? newParent.Id : null;
- pmds[i].LockPermissions = lockPermissions;
+ pmds[i] = new RestGuildChannelNewParentPayload
+ {
+ ChannelId = chns[i].Id,
+ Position = chns[i].Position >= position ? chns[i].Position + 1 : chns[i].Position,
+ };
+ if (chns[i].Id == this.Id)
+ {
+ pmds[i].Position = position;
+ pmds[i].ParentId = newParent is not null ? newParent.Id : null;
+ pmds[i].LockPermissions = lockPermissions;
+ }
}
- }
- return this.Discord.ApiClient.ModifyGuildChannelParentAsync(this.Guild.Id, pmds, reason);
- }
+ return this.Discord.ApiClient.ModifyGuildChannelParentAsync(this.Guild.Id, pmds, reason);
+ }
- /// <summary>
- /// Moves the channel out of a category.
- /// </summary>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task RemoveParentAsync(string reason = null)
- {
- if (this.Guild == null)
- throw new ArgumentException("Cannot modify parent of non-guild channels.");
- if (!this.IsMovableInParent())
- throw new NotSupportedException("You can't move this type of channel in categories.");
+ /// <summary>
+ /// Moves the channel out of a category.
+ /// </summary>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task RemoveParentAsync(string reason = null)
+ {
+ if (this.Guild == null)
+ throw new ArgumentException("Cannot modify parent of non-guild channels.");
+ if (!this.IsMovableInParent())
+ throw new NotSupportedException("You can't move this type of channel in categories.");
- var position = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type && xc.Parent is null) //gets list of same type channels with no parent
+ var position = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type && xc.Parent is null) //gets list of same type channels with no parent
.Select(xc => xc.Position).DefaultIfEmpty(-1).Max() + 1; // returns highest position of list +1, default val: 0
- var chns = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type)
- .OrderBy(xc => xc.Position).ToArray();
+ var chns = this.Guild.ChannelsInternal.Values.Where(xc => xc.Type == this.Type)
+ .OrderBy(xc => xc.Position).ToArray();
- var pmds = new RestGuildChannelNoParentPayload[chns.Length];
+ var pmds = new RestGuildChannelNoParentPayload[chns.Length];
- for (var i = 0; i < chns.Length; i++)
- {
- pmds[i] = new RestGuildChannelNoParentPayload
- {
- ChannelId = chns[i].Id,
- };
- if (chns[i].Id == this.Id)
+ for (var i = 0; i < chns.Length; i++)
{
- pmds[i].Position = 1;
- pmds[i].ParentId = null;
- }
- else
- {
- pmds[i].Position = chns[i].Position < this.Position ? chns[i].Position + 1 : chns[i].Position;
+ pmds[i] = new RestGuildChannelNoParentPayload
+ {
+ ChannelId = chns[i].Id,
+ };
+ if (chns[i].Id == this.Id)
+ {
+ pmds[i].Position = 1;
+ pmds[i].ParentId = null;
+ }
+ else
+ {
+ pmds[i].Position = chns[i].Position < this.Position ? chns[i].Position + 1 : chns[i].Position;
+ }
}
+
+ return this.Discord.ApiClient.DetachGuildChannelParentAsync(this.Guild.Id, pmds, reason);
}
- return this.Discord.ApiClient.DetachGuildChannelParentAsync(this.Guild.Id, pmds, reason);
- }
+ /// <summary>
+ /// Returns a list of messages before a certain message.
+ /// <param name="limit">The amount of messages to fetch.</param>
+ /// <param name="before">Message to fetch before from.</param>
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordMessage>> GetMessagesBeforeAsync(ulong before, int limit = 100)
+ => this.GetMessagesInternalAsync(limit, before, null, null);
+
+ /// <summary>
+ /// Returns a list of messages after a certain message.
+ /// <param name="limit">The amount of messages to fetch.</param>
+ /// <param name="after">Message to fetch after from.</param>
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordMessage>> GetMessagesAfterAsync(ulong after, int limit = 100)
+ => this.GetMessagesInternalAsync(limit, null, after, null);
+
+ /// <summary>
+ /// Returns a list of messages around a certain message.
+ /// <param name="limit">The amount of messages to fetch.</param>
+ /// <param name="around">Message to fetch around from.</param>
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordMessage>> GetMessagesAroundAsync(ulong around, int limit = 100)
+ => this.GetMessagesInternalAsync(limit, null, null, around);
+
+ /// <summary>
+ /// Returns a list of messages from the last message in the channel.
+ /// <param name="limit">The amount of messages to fetch.</param>
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordMessage>> GetMessagesAsync(int limit = 100) =>
+ this.GetMessagesInternalAsync(limit, null, null, null);
+
+ /// <summary>
+ /// Returns a list of messages
+ /// </summary>
+ /// <param name="limit">How many messages should be returned.</param>
+ /// <param name="before">Get messages before snowflake.</param>
+ /// <param name="after">Get messages after snowflake.</param>
+ /// <param name="around">Get messages around snowflake.</param>
+ private async Task<IReadOnlyList<DiscordMessage>> GetMessagesInternalAsync(int limit = 100, ulong? before = null, ulong? after = null, ulong? around = null)
+ {
+ if (!this.IsWritable())
+ throw new ArgumentException("Cannot get the messages of a non-text channel.");
- /// <summary>
- /// Returns a list of messages before a certain message.
- /// <param name="limit">The amount of messages to fetch.</param>
- /// <param name="before">Message to fetch before from.</param>
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordMessage>> GetMessagesBeforeAsync(ulong before, int limit = 100)
- => this.GetMessagesInternalAsync(limit, before, null, null);
+ if (limit < 0)
+ throw new ArgumentException("Cannot get a negative number of messages.");
- /// <summary>
- /// Returns a list of messages after a certain message.
- /// <param name="limit">The amount of messages to fetch.</param>
- /// <param name="after">Message to fetch after from.</param>
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordMessage>> GetMessagesAfterAsync(ulong after, int limit = 100)
- => this.GetMessagesInternalAsync(limit, null, after, null);
+ if (limit == 0)
+ return Array.Empty<DiscordMessage>();
- /// <summary>
- /// Returns a list of messages around a certain message.
- /// <param name="limit">The amount of messages to fetch.</param>
- /// <param name="around">Message to fetch around from.</param>
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordMessage>> GetMessagesAroundAsync(ulong around, int limit = 100)
- => this.GetMessagesInternalAsync(limit, null, null, around);
+ //return this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, limit, before, after, around);
+ if (limit > 100 && around != null)
+ throw new InvalidOperationException("Cannot get more than 100 messages around the specified ID.");
- /// <summary>
- /// Returns a list of messages from the last message in the channel.
- /// <param name="limit">The amount of messages to fetch.</param>
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordMessage>> GetMessagesAsync(int limit = 100) =>
- this.GetMessagesInternalAsync(limit, null, null, null);
-
- /// <summary>
- /// Returns a list of messages
- /// </summary>
- /// <param name="limit">How many messages should be returned.</param>
- /// <param name="before">Get messages before snowflake.</param>
- /// <param name="after">Get messages after snowflake.</param>
- /// <param name="around">Get messages around snowflake.</param>
- private async Task<IReadOnlyList<DiscordMessage>> GetMessagesInternalAsync(int limit = 100, ulong? before = null, ulong? after = null, ulong? around = null)
- {
- if (!this.IsWritable())
- throw new ArgumentException("Cannot get the messages of a non-text channel.");
+ var msgs = new List<DiscordMessage>(limit);
+ var remaining = limit;
+ ulong? last = null;
+ var isAfter = after != null;
- if (limit < 0)
- throw new ArgumentException("Cannot get a negative number of messages.");
+ int lastCount;
+ do
+ {
+ var fetchSize = remaining > 100 ? 100 : remaining;
+ var fetch = await this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, fetchSize, !isAfter ? last ?? before : null, isAfter ? last ?? after : null, around).ConfigureAwait(false);
- if (limit == 0)
- return Array.Empty<DiscordMessage>();
+ lastCount = fetch.Count;
+ remaining -= lastCount;
- //return this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, limit, before, after, around);
- if (limit > 100 && around != null)
- throw new InvalidOperationException("Cannot get more than 100 messages around the specified ID.");
+ if (!isAfter)
+ {
+ msgs.AddRange(fetch);
+ last = fetch.LastOrDefault()?.Id;
+ }
+ else
+ {
+ msgs.InsertRange(0, fetch);
+ last = fetch.FirstOrDefault()?.Id;
+ }
+ }
+ while (remaining > 0 && lastCount > 0);
- var msgs = new List<DiscordMessage>(limit);
- var remaining = limit;
- ulong? last = null;
- var isAfter = after != null;
+ return new ReadOnlyCollection<DiscordMessage>(msgs);
+ }
- int lastCount;
- do
+ /// <summary>
+ /// Deletes multiple messages if they are less than 14 days old. If they are older, none of the messages will be deleted and you will receive a <see cref="DisCatSharp.Exceptions.BadRequestException"/> error.
+ /// </summary>
+ /// <param name="messages">A collection of messages to delete.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task DeleteMessagesAsync(IEnumerable<DiscordMessage> messages, string reason = null)
{
- var fetchSize = remaining > 100 ? 100 : remaining;
- var fetch = await this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, fetchSize, !isAfter ? last ?? before : null, isAfter ? last ?? after : null, around).ConfigureAwait(false);
-
- lastCount = fetch.Count;
- remaining -= lastCount;
+ // don't enumerate more than once
+ var msgs = messages.Where(x => x.Channel.Id == this.Id).Select(x => x.Id).ToArray();
+ if (messages == null || !msgs.Any())
+ throw new ArgumentException("You need to specify at least one message to delete.");
- if (!isAfter)
- {
- msgs.AddRange(fetch);
- last = fetch.LastOrDefault()?.Id;
- }
- else
+ if (msgs.Length < 2)
{
- msgs.InsertRange(0, fetch);
- last = fetch.FirstOrDefault()?.Id;
+ await this.Discord.ApiClient.DeleteMessageAsync(this.Id, msgs.Single(), reason).ConfigureAwait(false);
+ return;
}
- }
- while (remaining > 0 && lastCount > 0);
-
- return new ReadOnlyCollection<DiscordMessage>(msgs);
- }
-
- /// <summary>
- /// Deletes multiple messages if they are less than 14 days old. If they are older, none of the messages will be deleted and you will receive a <see cref="DisCatSharp.Exceptions.BadRequestException"/> error.
- /// </summary>
- /// <param name="messages">A collection of messages to delete.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task DeleteMessagesAsync(IEnumerable<DiscordMessage> messages, string reason = null)
- {
- // don't enumerate more than once
- var msgs = messages.Where(x => x.Channel.Id == this.Id).Select(x => x.Id).ToArray();
- if (messages == null || !msgs.Any())
- throw new ArgumentException("You need to specify at least one message to delete.");
- if (msgs.Length < 2)
- {
- await this.Discord.ApiClient.DeleteMessageAsync(this.Id, msgs.Single(), reason).ConfigureAwait(false);
- return;
+ for (var i = 0; i < msgs.Length; i += 100)
+ await this.Discord.ApiClient.DeleteMessagesAsync(this.Id, msgs.Skip(i).Take(100), reason).ConfigureAwait(false);
}
- for (var i = 0; i < msgs.Length; i += 100)
- await this.Discord.ApiClient.DeleteMessagesAsync(this.Id, msgs.Skip(i).Take(100), reason).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Deletes a message
- /// </summary>
- /// <param name="message">The message to be deleted.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteMessageAsync(DiscordMessage message, string reason = null)
- => this.Discord.ApiClient.DeleteMessageAsync(this.Id, message.Id, reason);
-
- /// <summary>
- /// Returns a list of invite objects
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreateInstantInvite"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordInvite>> GetInvitesAsync() =>
- this.Guild == null
- ? throw new ArgumentException("Cannot get the invites of a channel that does not belong to a guild.")
- : this.Discord.ApiClient.GetChannelInvitesAsync(this.Id);
-
- /// <summary>
- /// Create a new invite object
- /// </summary>
- /// <param name="maxAge">Duration of invite in seconds before expiry, or 0 for never. Defaults to 86400.</param>
- /// <param name="maxUses">Max number of uses or 0 for unlimited. Defaults to 0</param>
- /// <param name="temporary">Whether this invite should be temporary. Defaults to false.</param>
- /// <param name="unique">Whether this invite should be unique. Defaults to false.</param>
- /// <param name="targetType">The target type. Defaults to null.</param>
- /// <param name="targetApplication">The target activity. Defaults to null.</param>
- /// <param name="targetUser">The target user id. Defaults to null.</param>
- /// <param name="reason">The audit log reason.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreateInstantInvite"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordInvite> CreateInviteAsync(int maxAge = 86400, int maxUses = 0, bool temporary = false, bool unique = false, TargetType? targetType = null, TargetActivity? targetApplication = null, ulong? targetUser = null, string reason = null)
- => this.Discord.ApiClient.CreateChannelInviteAsync(this.Id, maxAge, maxUses, targetType, targetApplication, targetUser, temporary, unique, reason);
-
- #region Stage
-
- /// <summary>
- /// Opens a stage.
- /// </summary>
- /// <param name="topic">Topic of the stage.</param>
- /// <param name="sendStartNotification">Whether @everyone should be notified.</param>
- /// <param name="privacyLevel">Privacy level of the stage (Defaults to <see cref="StagePrivacyLevel.GuildOnly"/>.</param>
- /// <param name="reason">Audit log reason.</param>
- /// <returns>Stage instance</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordStageInstance> OpenStageAsync(string topic, bool sendStartNotification = false, StagePrivacyLevel privacyLevel = StagePrivacyLevel.GuildOnly, string reason = null)
- => await this.Discord.ApiClient.CreateStageInstanceAsync(this.Id, topic, sendStartNotification, privacyLevel, reason);
-
- /// <summary>
- /// Modifies a stage topic.
- /// </summary>
- /// <param name="topic">New topic of the stage.</param>
- /// <param name="privacyLevel">New privacy level of the stage.</param>
- /// <param name="reason">Audit log reason.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task ModifyStageAsync(Optional<string> topic, Optional<StagePrivacyLevel> privacyLevel, string reason = null)
- => await this.Discord.ApiClient.ModifyStageInstanceAsync(this.Id, topic, privacyLevel, reason);
-
- /// <summary>
- /// Closes a stage.
- /// </summary>
- /// <param name="reason">Audit log reason.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task CloseStageAsync(string reason = null)
- => await this.Discord.ApiClient.DeleteStageInstanceAsync(this.Id, reason);
-
- /// <summary>
- /// Gets a stage.
- /// </summary>
- /// <returns>The requested stage.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or <see cref="Permissions.UseVoice"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordStageInstance> GetStageAsync()
- => await this.Discord.ApiClient.GetStageInstanceAsync(this.Id);
-
- #endregion
-
- #region Scheduled Events
-
- /// <summary>
- /// Creates a scheduled event based on the channel type.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="scheduledStartTime">The scheduled start time.</param>
- /// <param name="description">The description.</param>
- /// <param name="coverImage">The cover image.</param>
- /// <param name="reason">The reason.</param>
- /// <returns>A scheduled event.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the resource does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordScheduledEvent> CreateScheduledEventAsync(string name, DateTimeOffset scheduledStartTime, string description = null, Optional<Stream> coverImage = default, string reason = null)
- {
- if (!this.IsVoiceJoinable())
- throw new NotSupportedException("Cannot create a scheduled event for this type of channel. Channel type must be either voice or stage.");
-
- var type = this.Type == ChannelType.Voice ? ScheduledEventEntityType.Voice : ScheduledEventEntityType.StageInstance;
-
- return await this.Guild.CreateScheduledEventAsync(name, scheduledStartTime, null, this, null, description, type, coverImage, reason);
- }
-
- #endregion
-
- #region Threads
-
- /// <summary>
- /// Creates a thread.
- /// Depending on whether it is created inside an <see cref="ChannelType.News"/> or an <see cref="ChannelType.Text"/> it is either an <see cref="ChannelType.NewsThread"/> or an <see cref="ChannelType.PublicThread"/>.
- /// Depending on whether the <see cref="ChannelType"/> is set to <see cref="ChannelType.PrivateThread"/> it is either an <see cref="ChannelType.PrivateThread"/> or an <see cref="ChannelType.PublicThread"/> (default).
- /// </summary>
- /// <param name="name">The name of the thread.</param>
- /// <param name="autoArchiveDuration"><see cref="ThreadAutoArchiveDuration"/> till it gets archived. Defaults to <see cref="ThreadAutoArchiveDuration.OneHour"/>.</param>
- /// <param name="type">Can be either an <see cref="ChannelType.PrivateThread"/>, <see cref="ChannelType.NewsThread"/> or an <see cref="ChannelType.PublicThread"/>.</param>
- /// <param name="rateLimitPerUser">The per user ratelimit, aka slowdown.</param>
- /// <param name="reason">Audit log reason.</param>
- /// <returns>The created thread.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreatePublicThreads"/> or <see cref="Permissions.SendMessagesInThreads"/> or if creating a private thread the <see cref="Permissions.CreatePrivateThreads"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild hasn't enabled threads atm.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.NotSupportedException">Thrown when the <see cref="ThreadAutoArchiveDuration"/> cannot be modified. This happens, when the guild hasn't reached a certain boost <see cref="PremiumTier"/>. Or if <see cref="GuildFeatures.CanCreatePrivateThreads"/> is not enabled for guild. This happens, if the guild does not have <see cref="PremiumTier.TierTwo"/></exception>
- public async Task<DiscordThreadChannel> CreateThreadAsync(string name, ThreadAutoArchiveDuration autoArchiveDuration = ThreadAutoArchiveDuration.OneHour, ChannelType type = ChannelType.PublicThread, int? rateLimitPerUser = null, string reason = null) =>
- type != ChannelType.NewsThread && type != ChannelType.PublicThread && type != ChannelType.PrivateThread
- ? throw new NotSupportedException("Wrong thread type given.")
- : !this.IsThreadHolder()
- ? throw new NotSupportedException("Parent channel can't have threads.")
- : type == ChannelType.PrivateThread
- ? Utilities.CheckThreadPrivateFeature(this.Guild)
- ? Utilities.CheckThreadAutoArchiveDurationFeature(this.Guild, autoArchiveDuration)
- ? await this.Discord.ApiClient.CreateThreadAsync(this.Id, null, name, autoArchiveDuration, type, rateLimitPerUser, reason)
- : throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(autoArchiveDuration == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.")
- : throw new NotSupportedException($"Cannot create a private thread. Guild needs to be boost tier two.")
- : Utilities.CheckThreadAutoArchiveDurationFeature(this.Guild, autoArchiveDuration)
- ? await this.Discord.ApiClient.CreateThreadAsync(this.Id, null, name, autoArchiveDuration, this.Type == ChannelType.News ? ChannelType.NewsThread : ChannelType.PublicThread, rateLimitPerUser, reason)
- : throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(autoArchiveDuration == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
-
- /// <summary>
- /// Gets joined archived private threads. Can contain more threads.
- /// If the result's value 'HasMore' is true, you need to recall this function to get older threads.
- /// </summary>
- /// <param name="before">Get threads created before this thread id.</param>
- /// <param name="limit">Defines the limit of returned <see cref="DiscordThreadResult"/>.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordThreadResult> GetJoinedPrivateArchivedThreadsAsync(ulong? before, int? limit)
- => await this.Discord.ApiClient.GetJoinedPrivateArchivedThreadsAsync(this.Id, before, limit);
-
- /// <summary>
- /// Gets archived public threads. Can contain more threads.
- /// If the result's value 'HasMore' is true, you need to recall this function to get older threads.
- /// </summary>
- /// <param name="before">Get threads created before this thread id.</param>
- /// <param name="limit">Defines the limit of returned <see cref="DiscordThreadResult"/>.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordThreadResult> GetPublicArchivedThreadsAsync(ulong? before, int? limit)
- => await this.Discord.ApiClient.GetPublicArchivedThreadsAsync(this.Id, before, limit);
-
- /// <summary>
- /// Gets archived private threads. Can contain more threads.
- /// If the result's value 'HasMore' is true, you need to recall this function to get older threads.
- /// </summary>
- /// <param name="before">Get threads created before this thread id.</param>
- /// <param name="limit">Defines the limit of returned <see cref="DiscordThreadResult"/>.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageThreads"/> or <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordThreadResult> GetPrivateArchivedThreadsAsync(ulong? before, int? limit)
- => await this.Discord.ApiClient.GetPrivateArchivedThreadsAsync(this.Id, before, limit);
-
- #endregion
-
- /// <summary>
- /// Adds a channel permission overwrite for specified role.
- /// </summary>
- /// <param name="role">The role to have the permission added.</param>
- /// <param name="allow">The permissions to allow.</param>
- /// <param name="deny">The permissions to deny.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task AddOverwriteAsync(DiscordRole role, Permissions allow = Permissions.None, Permissions deny = Permissions.None, string reason = null)
- => this.Discord.ApiClient.EditChannelPermissionsAsync(this.Id, role.Id, allow, deny, "role", reason);
-
-
- /// <summary>
- /// Adds a channel permission overwrite for specified member.
- /// </summary>
- /// <param name="member">The member to have the permission added.</param>
- /// <param name="allow">The permissions to allow.</param>
- /// <param name="deny">The permissions to deny.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task AddOverwriteAsync(DiscordMember member, Permissions allow = Permissions.None, Permissions deny = Permissions.None, string reason = null)
- => this.Discord.ApiClient.EditChannelPermissionsAsync(this.Id, member.Id, allow, deny, "member", reason);
-
- /// <summary>
- /// Deletes a channel permission overwrite for specified member.
- /// </summary>
- /// <param name="member">The member to have the permission deleted.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteOverwriteAsync(DiscordMember member, string reason = null)
- => this.Discord.ApiClient.DeleteChannelPermissionAsync(this.Id, member.Id, reason);
+ /// <summary>
+ /// Deletes a message
+ /// </summary>
+ /// <param name="message">The message to be deleted.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteMessageAsync(DiscordMessage message, string reason = null)
+ => this.Discord.ApiClient.DeleteMessageAsync(this.Id, message.Id, reason);
+
+ /// <summary>
+ /// Returns a list of invite objects
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreateInstantInvite"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordInvite>> GetInvitesAsync() =>
+ this.Guild == null
+ ? throw new ArgumentException("Cannot get the invites of a channel that does not belong to a guild.")
+ : this.Discord.ApiClient.GetChannelInvitesAsync(this.Id);
+
+ /// <summary>
+ /// Create a new invite object
+ /// </summary>
+ /// <param name="maxAge">Duration of invite in seconds before expiry, or 0 for never. Defaults to 86400.</param>
+ /// <param name="maxUses">Max number of uses or 0 for unlimited. Defaults to 0</param>
+ /// <param name="temporary">Whether this invite should be temporary. Defaults to false.</param>
+ /// <param name="unique">Whether this invite should be unique. Defaults to false.</param>
+ /// <param name="targetType">The target type. Defaults to null.</param>
+ /// <param name="targetApplication">The target activity. Defaults to null.</param>
+ /// <param name="targetUser">The target user id. Defaults to null.</param>
+ /// <param name="reason">The audit log reason.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreateInstantInvite"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordInvite> CreateInviteAsync(int maxAge = 86400, int maxUses = 0, bool temporary = false, bool unique = false, TargetType? targetType = null, TargetActivity? targetApplication = null, ulong? targetUser = null, string reason = null)
+ => this.Discord.ApiClient.CreateChannelInviteAsync(this.Id, maxAge, maxUses, targetType, targetApplication, targetUser, temporary, unique, reason);
+
+ #region Stage
+
+ /// <summary>
+ /// Opens a stage.
+ /// </summary>
+ /// <param name="topic">Topic of the stage.</param>
+ /// <param name="sendStartNotification">Whether @everyone should be notified.</param>
+ /// <param name="privacyLevel">Privacy level of the stage (Defaults to <see cref="StagePrivacyLevel.GuildOnly"/>.</param>
+ /// <param name="reason">Audit log reason.</param>
+ /// <returns>Stage instance</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordStageInstance> OpenStageAsync(string topic, bool sendStartNotification = false, StagePrivacyLevel privacyLevel = StagePrivacyLevel.GuildOnly, string reason = null)
+ => await this.Discord.ApiClient.CreateStageInstanceAsync(this.Id, topic, sendStartNotification, privacyLevel, reason);
+
+ /// <summary>
+ /// Modifies a stage topic.
+ /// </summary>
+ /// <param name="topic">New topic of the stage.</param>
+ /// <param name="privacyLevel">New privacy level of the stage.</param>
+ /// <param name="reason">Audit log reason.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task ModifyStageAsync(Optional<string> topic, Optional<StagePrivacyLevel> privacyLevel, string reason = null)
+ => await this.Discord.ApiClient.ModifyStageInstanceAsync(this.Id, topic, privacyLevel, reason);
+
+ /// <summary>
+ /// Closes a stage.
+ /// </summary>
+ /// <param name="reason">Audit log reason.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task CloseStageAsync(string reason = null)
+ => await this.Discord.ApiClient.DeleteStageInstanceAsync(this.Id, reason);
+
+ /// <summary>
+ /// Gets a stage.
+ /// </summary>
+ /// <returns>The requested stage.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or <see cref="Permissions.UseVoice"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordStageInstance> GetStageAsync()
+ => await this.Discord.ApiClient.GetStageInstanceAsync(this.Id);
+
+ #endregion
+
+ #region Scheduled Events
+
+ /// <summary>
+ /// Creates a scheduled event based on the channel type.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="scheduledStartTime">The scheduled start time.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="coverImage">The cover image.</param>
+ /// <param name="reason">The reason.</param>
+ /// <returns>A scheduled event.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the resource does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordScheduledEvent> CreateScheduledEventAsync(string name, DateTimeOffset scheduledStartTime, string description = null, Optional<Stream> coverImage = default, string reason = null)
+ {
+ if (!this.IsVoiceJoinable())
+ throw new NotSupportedException("Cannot create a scheduled event for this type of channel. Channel type must be either voice or stage.");
- /// <summary>
- /// Deletes a channel permission overwrite for specified role.
- /// </summary>
- /// <param name="role">The role to have the permission deleted.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteOverwriteAsync(DiscordRole role, string reason = null)
- => this.Discord.ApiClient.DeleteChannelPermissionAsync(this.Id, role.Id, reason);
+ var type = this.Type == ChannelType.Voice ? ScheduledEventEntityType.Voice : ScheduledEventEntityType.StageInstance;
- /// <summary>
- /// Post a typing indicator.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task TriggerTypingAsync() =>
- !this.IsWritable()
- ? throw new ArgumentException("Cannot start typing in a non-text channel.")
- : this.Discord.ApiClient.TriggerTypingAsync(this.Id);
+ return await this.Guild.CreateScheduledEventAsync(name, scheduledStartTime, null, this, null, description, type, coverImage, reason);
+ }
- /// <summary>
- /// Returns all pinned messages.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordMessage>> GetPinnedMessagesAsync() =>
- !this.IsWritable()
- ? throw new ArgumentException("A non-text channel does not have pinned messages.")
- : this.Discord.ApiClient.GetPinnedMessagesAsync(this.Id);
+ #endregion
+
+ #region Threads
+
+ /// <summary>
+ /// Creates a thread.
+ /// Depending on whether it is created inside an <see cref="ChannelType.News"/> or an <see cref="ChannelType.Text"/> it is either an <see cref="ChannelType.NewsThread"/> or an <see cref="ChannelType.PublicThread"/>.
+ /// Depending on whether the <see cref="ChannelType"/> is set to <see cref="ChannelType.PrivateThread"/> it is either an <see cref="ChannelType.PrivateThread"/> or an <see cref="ChannelType.PublicThread"/> (default).
+ /// </summary>
+ /// <param name="name">The name of the thread.</param>
+ /// <param name="autoArchiveDuration"><see cref="ThreadAutoArchiveDuration"/> till it gets archived. Defaults to <see cref="ThreadAutoArchiveDuration.OneHour"/>.</param>
+ /// <param name="type">Can be either an <see cref="ChannelType.PrivateThread"/>, <see cref="ChannelType.NewsThread"/> or an <see cref="ChannelType.PublicThread"/>.</param>
+ /// <param name="rateLimitPerUser">The per user ratelimit, aka slowdown.</param>
+ /// <param name="reason">Audit log reason.</param>
+ /// <returns>The created thread.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreatePublicThreads"/> or <see cref="Permissions.SendMessagesInThreads"/> or if creating a private thread the <see cref="Permissions.CreatePrivateThreads"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild hasn't enabled threads atm.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.NotSupportedException">Thrown when the <see cref="ThreadAutoArchiveDuration"/> cannot be modified. This happens, when the guild hasn't reached a certain boost <see cref="PremiumTier"/>. Or if <see cref="GuildFeatures.CanCreatePrivateThreads"/> is not enabled for guild. This happens, if the guild does not have <see cref="PremiumTier.TierTwo"/></exception>
+ public async Task<DiscordThreadChannel> CreateThreadAsync(string name, ThreadAutoArchiveDuration autoArchiveDuration = ThreadAutoArchiveDuration.OneHour, ChannelType type = ChannelType.PublicThread, int? rateLimitPerUser = null, string reason = null) =>
+ type != ChannelType.NewsThread && type != ChannelType.PublicThread && type != ChannelType.PrivateThread
+ ? throw new NotSupportedException("Wrong thread type given.")
+ : !this.IsThreadHolder()
+ ? throw new NotSupportedException("Parent channel can't have threads.")
+ : type == ChannelType.PrivateThread
+ ? Utilities.CheckThreadPrivateFeature(this.Guild)
+ ? Utilities.CheckThreadAutoArchiveDurationFeature(this.Guild, autoArchiveDuration)
+ ? await this.Discord.ApiClient.CreateThreadAsync(this.Id, null, name, autoArchiveDuration, type, rateLimitPerUser, reason)
+ : throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(autoArchiveDuration == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.")
+ : throw new NotSupportedException($"Cannot create a private thread. Guild needs to be boost tier two.")
+ : Utilities.CheckThreadAutoArchiveDurationFeature(this.Guild, autoArchiveDuration)
+ ? await this.Discord.ApiClient.CreateThreadAsync(this.Id, null, name, autoArchiveDuration, this.Type == ChannelType.News ? ChannelType.NewsThread : ChannelType.PublicThread, rateLimitPerUser, reason)
+ : throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(autoArchiveDuration == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
+
+ /// <summary>
+ /// Gets joined archived private threads. Can contain more threads.
+ /// If the result's value 'HasMore' is true, you need to recall this function to get older threads.
+ /// </summary>
+ /// <param name="before">Get threads created before this thread id.</param>
+ /// <param name="limit">Defines the limit of returned <see cref="DiscordThreadResult"/>.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordThreadResult> GetJoinedPrivateArchivedThreadsAsync(ulong? before, int? limit)
+ => await this.Discord.ApiClient.GetJoinedPrivateArchivedThreadsAsync(this.Id, before, limit);
+
+ /// <summary>
+ /// Gets archived public threads. Can contain more threads.
+ /// If the result's value 'HasMore' is true, you need to recall this function to get older threads.
+ /// </summary>
+ /// <param name="before">Get threads created before this thread id.</param>
+ /// <param name="limit">Defines the limit of returned <see cref="DiscordThreadResult"/>.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordThreadResult> GetPublicArchivedThreadsAsync(ulong? before, int? limit)
+ => await this.Discord.ApiClient.GetPublicArchivedThreadsAsync(this.Id, before, limit);
+
+ /// <summary>
+ /// Gets archived private threads. Can contain more threads.
+ /// If the result's value 'HasMore' is true, you need to recall this function to get older threads.
+ /// </summary>
+ /// <param name="before">Get threads created before this thread id.</param>
+ /// <param name="limit">Defines the limit of returned <see cref="DiscordThreadResult"/>.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageThreads"/> or <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordThreadResult> GetPrivateArchivedThreadsAsync(ulong? before, int? limit)
+ => await this.Discord.ApiClient.GetPrivateArchivedThreadsAsync(this.Id, before, limit);
+
+ #endregion
+
+ /// <summary>
+ /// Adds a channel permission overwrite for specified role.
+ /// </summary>
+ /// <param name="role">The role to have the permission added.</param>
+ /// <param name="allow">The permissions to allow.</param>
+ /// <param name="deny">The permissions to deny.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task AddOverwriteAsync(DiscordRole role, Permissions allow = Permissions.None, Permissions deny = Permissions.None, string reason = null)
+ => this.Discord.ApiClient.EditChannelPermissionsAsync(this.Id, role.Id, allow, deny, "role", reason);
+
+
+ /// <summary>
+ /// Adds a channel permission overwrite for specified member.
+ /// </summary>
+ /// <param name="member">The member to have the permission added.</param>
+ /// <param name="allow">The permissions to allow.</param>
+ /// <param name="deny">The permissions to deny.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task AddOverwriteAsync(DiscordMember member, Permissions allow = Permissions.None, Permissions deny = Permissions.None, string reason = null)
+ => this.Discord.ApiClient.EditChannelPermissionsAsync(this.Id, member.Id, allow, deny, "member", reason);
+
+ /// <summary>
+ /// Deletes a channel permission overwrite for specified member.
+ /// </summary>
+ /// <param name="member">The member to have the permission deleted.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteOverwriteAsync(DiscordMember member, string reason = null)
+ => this.Discord.ApiClient.DeleteChannelPermissionAsync(this.Id, member.Id, reason);
+
+ /// <summary>
+ /// Deletes a channel permission overwrite for specified role.
+ /// </summary>
+ /// <param name="role">The role to have the permission deleted.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteOverwriteAsync(DiscordRole role, string reason = null)
+ => this.Discord.ApiClient.DeleteChannelPermissionAsync(this.Id, role.Id, reason);
+
+ /// <summary>
+ /// Post a typing indicator.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task TriggerTypingAsync() =>
+ !this.IsWritable()
+ ? throw new ArgumentException("Cannot start typing in a non-text channel.")
+ : this.Discord.ApiClient.TriggerTypingAsync(this.Id);
+
+ /// <summary>
+ /// Returns all pinned messages.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordMessage>> GetPinnedMessagesAsync() =>
+ !this.IsWritable()
+ ? throw new ArgumentException("A non-text channel does not have pinned messages.")
+ : this.Discord.ApiClient.GetPinnedMessagesAsync(this.Id);
+
+ /// <summary>
+ /// Create a new webhook.
+ /// </summary>
+ /// <param name="name">The name of the webhook.</param>
+ /// <param name="avatar">The image for the default webhook avatar.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordWebhook> CreateWebhookAsync(string name, Optional<Stream> avatar = default, string reason = null)
+ {
+ var av64 = ImageTool.Base64FromStream(avatar);
+ return await this.Discord.ApiClient.CreateWebhookAsync(this.Id, name, av64, reason).ConfigureAwait(false);
+ }
- /// <summary>
- /// Create a new webhook.
- /// </summary>
- /// <param name="name">The name of the webhook.</param>
- /// <param name="avatar">The image for the default webhook avatar.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordWebhook> CreateWebhookAsync(string name, Optional<Stream> avatar = default, string reason = null)
- {
- var av64 = ImageTool.Base64FromStream(avatar);
- return await this.Discord.ApiClient.CreateWebhookAsync(this.Id, name, av64, reason).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Returns a list of webhooks.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordWebhook>> GetWebhooksAsync()
+ => this.Discord.ApiClient.GetChannelWebhooksAsync(this.Id);
+
+ /// <summary>
+ /// Moves a member to this voice channel.
+ /// </summary>
+ /// <param name="member">The member to be moved.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MoveMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exists or if the Member does not exists.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task PlaceMemberAsync(DiscordMember member)
+ {
+ if (!this.IsVoiceJoinable())
+ throw new ArgumentException("Cannot place a member in a non-voice channel.");
- /// <summary>
- /// Returns a list of webhooks.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordWebhook>> GetWebhooksAsync()
- => this.Discord.ApiClient.GetChannelWebhooksAsync(this.Id);
+ await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, member.Id, default, default, default,
+ default, this.Id, null).ConfigureAwait(false);
+ }
- /// <summary>
- /// Moves a member to this voice channel.
- /// </summary>
- /// <param name="member">The member to be moved.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MoveMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the channel does not exists or if the Member does not exists.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task PlaceMemberAsync(DiscordMember member)
- {
- if (!this.IsVoiceJoinable())
- throw new ArgumentException("Cannot place a member in a non-voice channel.");
+ /// <summary>
+ /// Follows a news channel.
+ /// </summary>
+ /// <param name="targetChannel">Channel to crosspost messages to.</param>
+ /// <exception cref="System.ArgumentException">Thrown when trying to follow a non-news channel.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the current user doesn't have <see cref="Permissions.ManageWebhooks"/> on the target channel.</exception>
+ public Task<DiscordFollowedChannel> FollowAsync(DiscordChannel targetChannel) =>
+ this.Type != ChannelType.News
+ ? throw new ArgumentException("Cannot follow a non-news channel.")
+ : this.Discord.ApiClient.FollowChannelAsync(this.Id, targetChannel.Id);
+
+ /// <summary>
+ /// Publishes a message in a news channel to following channels.
+ /// </summary>
+ /// <param name="message">Message to publish.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the message has already been crossposted.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">
+ /// Thrown when the current user doesn't have <see cref="Permissions.ManageWebhooks"/> and/or <see cref="Permissions.SendMessages"/>
+ /// </exception>
+ public Task<DiscordMessage> CrosspostMessageAsync(DiscordMessage message) =>
+ (message.Flags & MessageFlags.Crossposted) == MessageFlags.Crossposted
+ ? throw new ArgumentException("Message is already crossposted.")
+ : this.Discord.ApiClient.CrosspostMessageAsync(this.Id, message.Id);
+
+ /// <summary>
+ /// Updates the current user's suppress state in this channel, if stage channel.
+ /// </summary>
+ /// <param name="suppress">Toggles the suppress state.</param>
+ /// <param name="requestToSpeakTimestamp">Sets the time the user requested to speak.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the channel is not a stage channel.</exception>
+ public async Task UpdateCurrentUserVoiceStateAsync(bool? suppress, DateTimeOffset? requestToSpeakTimestamp = null)
+ {
+ if (this.Type != ChannelType.Stage)
+ throw new ArgumentException("Voice state can only be updated in a stage channel.");
- await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, member.Id, default, default, default,
- default, this.Id, null).ConfigureAwait(false);
- }
+ await this.Discord.ApiClient.UpdateCurrentUserVoiceStateAsync(this.GuildId.Value, this.Id, suppress, requestToSpeakTimestamp).ConfigureAwait(false);
+ }
- /// <summary>
- /// Follows a news channel.
- /// </summary>
- /// <param name="targetChannel">Channel to crosspost messages to.</param>
- /// <exception cref="System.ArgumentException">Thrown when trying to follow a non-news channel.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the current user doesn't have <see cref="Permissions.ManageWebhooks"/> on the target channel.</exception>
- public Task<DiscordFollowedChannel> FollowAsync(DiscordChannel targetChannel) =>
- this.Type != ChannelType.News
- ? throw new ArgumentException("Cannot follow a non-news channel.")
- : this.Discord.ApiClient.FollowChannelAsync(this.Id, targetChannel.Id);
+ /// <summary>
+ /// Calculates permissions for a given member.
+ /// </summary>
+ /// <param name="mbr">Member to calculate permissions for.</param>
+ /// <returns>Calculated permissions for a given member.</returns>
+ public Permissions PermissionsFor(DiscordMember mbr)
+ {
+ // user > role > everyone
+ // allow > deny > undefined
+ // =>
+ // user allow > user deny > role allow > role deny > everyone allow > everyone deny
- /// <summary>
- /// Publishes a message in a news channel to following channels.
- /// </summary>
- /// <param name="message">Message to publish.</param>
- /// <exception cref="System.ArgumentException">Thrown when the message has already been crossposted.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">
- /// Thrown when the current user doesn't have <see cref="Permissions.ManageWebhooks"/> and/or <see cref="Permissions.SendMessages"/>
- /// </exception>
- public Task<DiscordMessage> CrosspostMessageAsync(DiscordMessage message) =>
- (message.Flags & MessageFlags.Crossposted) == MessageFlags.Crossposted
- ? throw new ArgumentException("Message is already crossposted.")
- : this.Discord.ApiClient.CrosspostMessageAsync(this.Id, message.Id);
+ if (this.IsPrivate || this.Guild == null)
+ return Permissions.None;
- /// <summary>
- /// Updates the current user's suppress state in this channel, if stage channel.
- /// </summary>
- /// <param name="suppress">Toggles the suppress state.</param>
- /// <param name="requestToSpeakTimestamp">Sets the time the user requested to speak.</param>
- /// <exception cref="System.ArgumentException">Thrown when the channel is not a stage channel.</exception>
- public async Task UpdateCurrentUserVoiceStateAsync(bool? suppress, DateTimeOffset? requestToSpeakTimestamp = null)
- {
- if (this.Type != ChannelType.Stage)
- throw new ArgumentException("Voice state can only be updated in a stage channel.");
+ if (this.Guild.OwnerId == mbr.Id)
+ return PermissionMethods.FullPerms;
- await this.Discord.ApiClient.UpdateCurrentUserVoiceStateAsync(this.GuildId.Value, this.Id, suppress, requestToSpeakTimestamp).ConfigureAwait(false);
- }
+ Permissions perms;
- /// <summary>
- /// Calculates permissions for a given member.
- /// </summary>
- /// <param name="mbr">Member to calculate permissions for.</param>
- /// <returns>Calculated permissions for a given member.</returns>
- public Permissions PermissionsFor(DiscordMember mbr)
- {
- // user > role > everyone
- // allow > deny > undefined
- // =>
- // user allow > user deny > role allow > role deny > everyone allow > everyone deny
+ // assign @everyone permissions
+ var everyoneRole = this.Guild.EveryoneRole;
+ perms = everyoneRole.Permissions;
- if (this.IsPrivate || this.Guild == null)
- return Permissions.None;
+ // roles that member is in
+ var mbRoles = mbr.Roles.Where(xr => xr.Id != everyoneRole.Id).ToArray();
- if (this.Guild.OwnerId == mbr.Id)
- return PermissionMethods.FullPerms;
+ // assign permissions from member's roles (in order)
+ perms |= mbRoles.Aggregate(Permissions.None, (c, role) => c | role.Permissions);
- Permissions perms;
+ // Administrator grants all permissions and cannot be overridden
+ if ((perms & Permissions.Administrator) == Permissions.Administrator)
+ return PermissionMethods.FullPerms;
- // assign @everyone permissions
- var everyoneRole = this.Guild.EveryoneRole;
- perms = everyoneRole.Permissions;
+ // channel overrides for roles that member is in
+ var mbRoleOverrides = mbRoles
+ .Select(xr => this.PermissionOverwritesInternal.FirstOrDefault(xo => xo.Id == xr.Id))
+ .Where(xo => xo != null)
+ .ToList();
- // roles that member is in
- var mbRoles = mbr.Roles.Where(xr => xr.Id != everyoneRole.Id).ToArray();
+ // assign channel permission overwrites for @everyone pseudo-role
+ var everyoneOverwrites = this.PermissionOverwritesInternal.FirstOrDefault(xo => xo.Id == everyoneRole.Id);
+ if (everyoneOverwrites != null)
+ {
+ perms &= ~everyoneOverwrites.Denied;
+ perms |= everyoneOverwrites.Allowed;
+ }
- // assign permissions from member's roles (in order)
- perms |= mbRoles.Aggregate(Permissions.None, (c, role) => c | role.Permissions);
+ // assign channel permission overwrites for member's roles (explicit deny)
+ perms &= ~mbRoleOverrides.Aggregate(Permissions.None, (c, overs) => c | overs.Denied);
+ // assign channel permission overwrites for member's roles (explicit allow)
+ perms |= mbRoleOverrides.Aggregate(Permissions.None, (c, overs) => c | overs.Allowed);
- // Administrator grants all permissions and cannot be overridden
- if ((perms & Permissions.Administrator) == Permissions.Administrator)
- return PermissionMethods.FullPerms;
+ // channel overrides for just this member
+ var mbOverrides = this.PermissionOverwritesInternal.FirstOrDefault(xo => xo.Id == mbr.Id);
+ if (mbOverrides == null) return perms;
- // channel overrides for roles that member is in
- var mbRoleOverrides = mbRoles
- .Select(xr => this.PermissionOverwritesInternal.FirstOrDefault(xo => xo.Id == xr.Id))
- .Where(xo => xo != null)
- .ToList();
+ // assign channel permission overwrites for just this member
+ perms &= ~mbOverrides.Denied;
+ perms |= mbOverrides.Allowed;
- // assign channel permission overwrites for @everyone pseudo-role
- var everyoneOverwrites = this.PermissionOverwritesInternal.FirstOrDefault(xo => xo.Id == everyoneRole.Id);
- if (everyoneOverwrites != null)
- {
- perms &= ~everyoneOverwrites.Denied;
- perms |= everyoneOverwrites.Allowed;
+ return perms;
}
- // assign channel permission overwrites for member's roles (explicit deny)
- perms &= ~mbRoleOverrides.Aggregate(Permissions.None, (c, overs) => c | overs.Denied);
- // assign channel permission overwrites for member's roles (explicit allow)
- perms |= mbRoleOverrides.Aggregate(Permissions.None, (c, overs) => c | overs.Allowed);
-
- // channel overrides for just this member
- var mbOverrides = this.PermissionOverwritesInternal.FirstOrDefault(xo => xo.Id == mbr.Id);
- if (mbOverrides == null) return perms;
-
- // assign channel permission overwrites for just this member
- perms &= ~mbOverrides.Denied;
- perms |= mbOverrides.Allowed;
-
- return perms;
- }
-
- /// <summary>
- /// Returns a string representation of this channel.
- /// </summary>
- /// <returns>String representation of this channel.</returns>
- public override string ToString() =>
- this.Type == ChannelType.Category
- ? $"Channel Category {this.Name} ({this.Id})"
- : this.Type == ChannelType.Text || this.Type == ChannelType.News || this.IsThread()
- ? $"Channel #{this.Name} ({this.Id})"
- : this.IsVoiceJoinable()
- ? $"Channel #!{this.Name} ({this.Id})"
- : !string.IsNullOrWhiteSpace(this.Name) ? $"Channel {this.Name} ({this.Id})" : $"Channel {this.Id}";
-
- #endregion
-
- /// <summary>
- /// Checks whether this <see cref="DiscordChannel"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordChannel"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordChannel);
-
- /// <summary>
- /// Checks whether this <see cref="DiscordChannel"/> is equal to another <see cref="DiscordChannel"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordChannel"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordChannel"/> is equal to this <see cref="DiscordChannel"/>.</returns>
- public bool Equals(DiscordChannel e)
- => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
-
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordChannel"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordChannel"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
+ /// <summary>
+ /// Returns a string representation of this channel.
+ /// </summary>
+ /// <returns>String representation of this channel.</returns>
+ public override string ToString() =>
+ this.Type == ChannelType.Category
+ ? $"Channel Category {this.Name} ({this.Id})"
+ : this.Type == ChannelType.Text || this.Type == ChannelType.News || this.IsThread()
+ ? $"Channel #{this.Name} ({this.Id})"
+ : this.IsVoiceJoinable()
+ ? $"Channel #!{this.Name} ({this.Id})"
+ : !string.IsNullOrWhiteSpace(this.Name) ? $"Channel {this.Name} ({this.Id})" : $"Channel {this.Id}";
+
+ #endregion
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordChannel"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordChannel"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordChannel);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordChannel"/> is equal to another <see cref="DiscordChannel"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordChannel"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordChannel"/> is equal to this <see cref="DiscordChannel"/>.</returns>
+ public bool Equals(DiscordChannel e)
+ => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordChannel"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordChannel"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordChannel"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are equal.</returns>
+ public static bool operator ==(DiscordChannel e1, DiscordChannel e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- /// <summary>
- /// Gets whether the two <see cref="DiscordChannel"/> objects are equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are equal.</returns>
- public static bool operator ==(DiscordChannel e1, DiscordChannel e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordChannel"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are not equal.</returns>
+ public static bool operator !=(DiscordChannel e1, DiscordChannel e2)
+ => !(e1 == e2);
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordChannel"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are not equal.</returns>
- public static bool operator !=(DiscordChannel e1, DiscordChannel e2)
- => !(e1 == e2);
}
diff --git a/DisCatSharp/Entities/Channel/DiscordDmChannel.cs b/DisCatSharp/Entities/Channel/DiscordDmChannel.cs
index 24e6b9e77..212eb62c2 100644
--- a/DisCatSharp/Entities/Channel/DiscordDmChannel.cs
+++ b/DisCatSharp/Entities/Channel/DiscordDmChannel.cs
@@ -1,92 +1,93 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a direct message channel.
-/// </summary>
-public class DiscordDmChannel : DiscordChannel
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the recipients of this direct message.
+ /// Represents a direct message channel.
/// </summary>
- [JsonProperty("recipients", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<DiscordUser> Recipients { get; internal set; }
+ public class DiscordDmChannel : DiscordChannel
+ {
+ /// <summary>
+ /// Gets the recipients of this direct message.
+ /// </summary>
+ [JsonProperty("recipients", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<DiscordUser> Recipients { get; internal set; }
- /// <summary>
- /// Gets the hash of this channel's icon.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
- public string IconHash { get; internal set; }
+ /// <summary>
+ /// Gets the hash of this channel's icon.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
+ public string IconHash { get; internal set; }
- /// <summary>
- /// Gets the id of this direct message's creator.
- /// </summary>
- [JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong OwnerId { get; internal set; }
+ /// <summary>
+ /// Gets the id of this direct message's creator.
+ /// </summary>
+ [JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong OwnerId { get; internal set; }
- /// <summary>
- /// Gets the application id of the direct message's creator if it a bot.
- /// </summary>
- [JsonProperty("application_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ApplicationId { get; internal set; }
+ /// <summary>
+ /// Gets the application id of the direct message's creator if it a bot.
+ /// </summary>
+ [JsonProperty("application_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ApplicationId { get; internal set; }
- /// <summary>
- /// Gets the URL of this channel's icon.
- /// </summary>
- [JsonIgnore]
- public string IconUrl
- => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.CHANNEL_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png" : null;
+ /// <summary>
+ /// Gets the URL of this channel's icon.
+ /// </summary>
+ [JsonIgnore]
+ public string IconUrl
+ => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.CHANNEL_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png" : null;
- /// <summary>
- /// Only use for Group DMs! Whitelisted bots only. Requires user's oauth2 access token.
- /// </summary>
- /// <param name="userId">The id of the user to add.</param>
- /// <param name="accessToken">The OAuth2 access token.</param>
- /// <param name="nickname">The nickname to give to the user.</param>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task AddDmRecipientAsync(ulong userId, string accessToken, string nickname)
- => this.Discord.ApiClient.AddGroupDmRecipientAsync(this.Id, userId, accessToken, nickname);
+ /// <summary>
+ /// Only use for Group DMs! Whitelisted bots only. Requires user's oauth2 access token.
+ /// </summary>
+ /// <param name="userId">The id of the user to add.</param>
+ /// <param name="accessToken">The OAuth2 access token.</param>
+ /// <param name="nickname">The nickname to give to the user.</param>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task AddDmRecipientAsync(ulong userId, string accessToken, string nickname)
+ => this.Discord.ApiClient.AddGroupDmRecipientAsync(this.Id, userId, accessToken, nickname);
- /// <summary>
- /// Only use for Group DMs! Whitelisted bots only. Requires user's oauth2 access token.
- /// </summary>
- /// <param name="userId">The id of the User to remove.</param>
- /// <param name="accessToken">The OAuth2 access token.</param>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task RemoveDmRecipientAsync(ulong userId, string accessToken)
- => this.Discord.ApiClient.RemoveGroupDmRecipientAsync(this.Id, userId);
+ /// <summary>
+ /// Only use for Group DMs! Whitelisted bots only. Requires user's oauth2 access token.
+ /// </summary>
+ /// <param name="userId">The id of the User to remove.</param>
+ /// <param name="accessToken">The OAuth2 access token.</param>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task RemoveDmRecipientAsync(ulong userId, string accessToken)
+ => this.Discord.ApiClient.RemoveGroupDmRecipientAsync(this.Id, userId);
+ }
}
diff --git a/DisCatSharp/Entities/Channel/DiscordFollowedChannel.cs b/DisCatSharp/Entities/Channel/DiscordFollowedChannel.cs
index 84d9f0d24..dfd248828 100644
--- a/DisCatSharp/Entities/Channel/DiscordFollowedChannel.cs
+++ b/DisCatSharp/Entities/Channel/DiscordFollowedChannel.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a followed channel.
-/// </summary>
-public class DiscordFollowedChannel
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the id of the channel following the announcement channel.
+ /// Represents a followed channel.
/// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; internal set; }
+ public class DiscordFollowedChannel
+ {
+ /// <summary>
+ /// Gets the id of the channel following the announcement channel.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; internal set; }
- /// <summary>
- /// Gets the id of the webhook that posts crossposted messages to the channel.
- /// </summary>
- [JsonProperty("webhook_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong WebhookId { get; internal set; }
+ /// <summary>
+ /// Gets the id of the webhook that posts crossposted messages to the channel.
+ /// </summary>
+ [JsonProperty("webhook_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong WebhookId { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Channel/DiscordGuildDirectoryChannel.cs b/DisCatSharp/Entities/Channel/DiscordGuildDirectoryChannel.cs
index ee6a064cd..535cd9762 100644
--- a/DisCatSharp/Entities/Channel/DiscordGuildDirectoryChannel.cs
+++ b/DisCatSharp/Entities/Channel/DiscordGuildDirectoryChannel.cs
@@ -1,95 +1,96 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord guild directory channel.
-/// </summary>
-public class DiscordGuildDirectoryChannel : DiscordChannel, IEquatable<DiscordGuildDirectoryChannel>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordGuildDirectoryChannel"/> class.
+ /// Represents a discord guild directory channel.
/// </summary>
- internal DiscordGuildDirectoryChannel()
- { }
+ public class DiscordGuildDirectoryChannel : DiscordChannel, IEquatable<DiscordGuildDirectoryChannel>
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordGuildDirectoryChannel"/> class.
+ /// </summary>
+ internal DiscordGuildDirectoryChannel()
+ { }
- [JsonIgnore]
- public IReadOnlyList<DiscordGuildDirectoryEntry> Entries =>
- this.Guild.ChannelsInternal.Values.Where(e => e.ParentId == this.Id).Select(x => x as DiscordGuildDirectoryEntry).ToList();
+ [JsonIgnore]
+ public IReadOnlyList<DiscordGuildDirectoryEntry> Entries =>
+ this.Guild.ChannelsInternal.Values.Where(e => e.ParentId == this.Id).Select(x => x as DiscordGuildDirectoryEntry).ToList();
- #region Methods
+ #region Methods
- #endregion
+ #endregion
- /// <summary>
- /// Checks whether this <see cref="DiscordGuildDirectoryChannel"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordGuildDirectoryChannel"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordGuildDirectoryChannel);
+ /// <summary>
+ /// Checks whether this <see cref="DiscordGuildDirectoryChannel"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordGuildDirectoryChannel"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordGuildDirectoryChannel);
- /// <summary>
- /// Checks whether this <see cref="DiscordGuildDirectoryChannel"/> is equal to another <see cref="DiscordGuildDirectoryChannel"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordGuildDirectoryChannel"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordGuildDirectoryChannel"/> is equal to this <see cref="DiscordGuildDirectoryChannel"/>.</returns>
- public bool Equals(DiscordGuildDirectoryChannel e)
- => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+ /// <summary>
+ /// Checks whether this <see cref="DiscordGuildDirectoryChannel"/> is equal to another <see cref="DiscordGuildDirectoryChannel"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordGuildDirectoryChannel"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordGuildDirectoryChannel"/> is equal to this <see cref="DiscordGuildDirectoryChannel"/>.</returns>
+ public bool Equals(DiscordGuildDirectoryChannel e)
+ => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordGuildDirectoryChannel"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordGuildDirectoryChannel"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordGuildDirectoryChannel"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordGuildDirectoryChannel"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
- /// <summary>
- /// Gets whether the two <see cref="DiscordGuildDirectoryChannel"/> objects are equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are equal.</returns>
- public static bool operator ==(DiscordGuildDirectoryChannel e1, DiscordGuildDirectoryChannel e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordGuildDirectoryChannel"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are equal.</returns>
+ public static bool operator ==(DiscordGuildDirectoryChannel e1, DiscordGuildDirectoryChannel e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
- }
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
- /// <summary>
- /// Gets whether the two <see cref="DiscordGuildDirectoryChannel"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are not equal.</returns>
- public static bool operator !=(DiscordGuildDirectoryChannel e1, DiscordGuildDirectoryChannel e2)
- => !(e1 == e2);
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordGuildDirectoryChannel"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are not equal.</returns>
+ public static bool operator !=(DiscordGuildDirectoryChannel e1, DiscordGuildDirectoryChannel e2)
+ => !(e1 == e2);
+ }
}
diff --git a/DisCatSharp/Entities/Channel/DiscordGuildDirectoryEntry.cs b/DisCatSharp/Entities/Channel/DiscordGuildDirectoryEntry.cs
index 37129df4b..a61a8aae1 100644
--- a/DisCatSharp/Entities/Channel/DiscordGuildDirectoryEntry.cs
+++ b/DisCatSharp/Entities/Channel/DiscordGuildDirectoryEntry.cs
@@ -1,102 +1,103 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord guild directory channel.
-/// </summary>
-public class DiscordGuildDirectoryEntry : DiscordChannel, IEquatable<DiscordGuildDirectoryEntry>
+namespace DisCatSharp.Entities
{
-
/// <summary>
- /// Gets the description of the directory entry.
+ /// Represents a discord guild directory channel.
/// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
+ public class DiscordGuildDirectoryEntry : DiscordChannel, IEquatable<DiscordGuildDirectoryEntry>
+ {
- /// <summary>
- /// Gets the primary category of the directory entry.
- /// </summary>
- [JsonProperty("primary_category_id", NullValueHandling = NullValueHandling.Ignore)]
- public DirectoryCategory PrimaryCategory { get; internal set; }
+ /// <summary>
+ /// Gets the description of the directory entry.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordGuildDirectoryEntry"/> class.
- /// </summary>
- internal DiscordGuildDirectoryEntry()
- { }
+ /// <summary>
+ /// Gets the primary category of the directory entry.
+ /// </summary>
+ [JsonProperty("primary_category_id", NullValueHandling = NullValueHandling.Ignore)]
+ public DirectoryCategory PrimaryCategory { get; internal set; }
- #region Methods
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordGuildDirectoryEntry"/> class.
+ /// </summary>
+ internal DiscordGuildDirectoryEntry()
+ { }
- #endregion
+ #region Methods
- /// <summary>
- /// Checks whether this <see cref="DiscordGuildDirectoryEntry"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordGuildDirectoryEntry"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordGuildDirectoryEntry);
+ #endregion
- /// <summary>
- /// Checks whether this <see cref="DiscordGuildDirectoryEntry"/> is equal to another <see cref="DiscordGuildDirectoryEntry"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordGuildDirectoryEntry"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordGuildDirectoryEntry"/> is equal to this <see cref="DiscordGuildDirectoryEntry"/>.</returns>
- public bool Equals(DiscordGuildDirectoryEntry e)
- => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+ /// <summary>
+ /// Checks whether this <see cref="DiscordGuildDirectoryEntry"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordGuildDirectoryEntry"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordGuildDirectoryEntry);
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordGuildDirectoryEntry"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordGuildDirectoryEntry"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
+ /// <summary>
+ /// Checks whether this <see cref="DiscordGuildDirectoryEntry"/> is equal to another <see cref="DiscordGuildDirectoryEntry"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordGuildDirectoryEntry"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordGuildDirectoryEntry"/> is equal to this <see cref="DiscordGuildDirectoryEntry"/>.</returns>
+ public bool Equals(DiscordGuildDirectoryEntry e)
+ => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
- /// <summary>
- /// Gets whether the two <see cref="DiscordGuildDirectoryEntry"/> objects are equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are equal.</returns>
- public static bool operator ==(DiscordGuildDirectoryEntry e1, DiscordGuildDirectoryEntry e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordGuildDirectoryEntry"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordGuildDirectoryEntry"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
- }
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordGuildDirectoryEntry"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are equal.</returns>
+ public static bool operator ==(DiscordGuildDirectoryEntry e1, DiscordGuildDirectoryEntry e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- /// <summary>
- /// Gets whether the two <see cref="DiscordGuildDirectoryEntry"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are not equal.</returns>
- public static bool operator !=(DiscordGuildDirectoryEntry e1, DiscordGuildDirectoryEntry e2)
- => !(e1 == e2);
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordGuildDirectoryEntry"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are not equal.</returns>
+ public static bool operator !=(DiscordGuildDirectoryEntry e1, DiscordGuildDirectoryEntry e2)
+ => !(e1 == e2);
+ }
}
diff --git a/DisCatSharp/Entities/Channel/ForumPostTag.cs b/DisCatSharp/Entities/Channel/ForumPostTag.cs
index bf23a06f3..2e1df06e2 100644
--- a/DisCatSharp/Entities/Channel/ForumPostTag.cs
+++ b/DisCatSharp/Entities/Channel/ForumPostTag.cs
@@ -1,103 +1,104 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord forum post tag.
-/// </summary>
-public class ForumPostTag : SnowflakeObject, IEquatable<ForumPostTag>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of this forum post tag.
+ /// Represents a discord forum post tag.
/// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
+ public class ForumPostTag : SnowflakeObject, IEquatable<ForumPostTag>
+ {
+ /// <summary>
+ /// Gets the name of this forum post tag.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the emoji id of the forum post tag.
- /// </summary>
- [JsonProperty("emoji_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? EmojiId { get; internal set; }
+ /// <summary>
+ /// Gets the emoji id of the forum post tag.
+ /// </summary>
+ [JsonProperty("emoji_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? EmojiId { get; internal set; }
- /// <summary>
- /// Gets the unicode emoji of the forum post tag.
- /// </summary>
- [JsonProperty("emoji_name", NullValueHandling = NullValueHandling.Ignore)]
- internal string UnicodeEmojiString;
+ /// <summary>
+ /// Gets the unicode emoji of the forum post tag.
+ /// </summary>
+ [JsonProperty("emoji_name", NullValueHandling = NullValueHandling.Ignore)]
+ internal string UnicodeEmojiString;
- /// <summary>
- /// Gets the unicode emoji.
- /// </summary>
- public DiscordEmoji UnicodeEmoji
- => this.UnicodeEmojiString != null ? DiscordEmoji.FromName(this.Discord, $":{this.UnicodeEmojiString}:", false) : null;
+ /// <summary>
+ /// Gets the unicode emoji.
+ /// </summary>
+ public DiscordEmoji UnicodeEmoji
+ => this.UnicodeEmojiString != null ? DiscordEmoji.FromName(this.Discord, $":{this.UnicodeEmojiString}:", false) : null;
- /// <summary>
- /// Checks whether this <see cref="ForumPostTag"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="ForumPostTag"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as ForumPostTag);
+ /// <summary>
+ /// Checks whether this <see cref="ForumPostTag"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="ForumPostTag"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as ForumPostTag);
- /// <summary>
- /// Checks whether this <see cref="ForumPostTag"/> is equal to another <see cref="ForumPostTag"/>.
- /// </summary>
- /// <param name="e"><see cref="ForumPostTag"/> to compare to.</param>
- /// <returns>Whether the <see cref="ForumPostTag"/> is equal to this <see cref="ForumPostTag"/>.</returns>
- public bool Equals(ForumPostTag e)
- => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.Name == e.Name));
+ /// <summary>
+ /// Checks whether this <see cref="ForumPostTag"/> is equal to another <see cref="ForumPostTag"/>.
+ /// </summary>
+ /// <param name="e"><see cref="ForumPostTag"/> to compare to.</param>
+ /// <returns>Whether the <see cref="ForumPostTag"/> is equal to this <see cref="ForumPostTag"/>.</returns>
+ public bool Equals(ForumPostTag e)
+ => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.Name == e.Name));
- /// <summary>
- /// Gets the hash code for this <see cref="ForumPostTag"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="ForumPostTag"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
+ /// <summary>
+ /// Gets the hash code for this <see cref="ForumPostTag"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="ForumPostTag"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
- /// <summary>
- /// Gets whether the two <see cref="ForumPostTag"/> objects are equal.
- /// </summary>
- /// <param name="e1">First forum post tag to compare.</param>
- /// <param name="e2">Second forum post tag to compare.</param>
- /// <returns>Whether the two forum post tags are equal.</returns>
- public static bool operator ==(ForumPostTag e1, ForumPostTag e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ /// <summary>
+ /// Gets whether the two <see cref="ForumPostTag"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First forum post tag to compare.</param>
+ /// <param name="e2">Second forum post tag to compare.</param>
+ /// <returns>Whether the two forum post tags are equal.</returns>
+ public static bool operator ==(ForumPostTag e1, ForumPostTag e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
- }
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
- /// <summary>
- /// Gets whether the two <see cref="DiscordEmoji"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First forum post tag to compare.</param>
- /// <param name="e2">Second forum post tag to compare.</param>
- /// <returns>Whether the two forum post tags are not equal.</returns>
- public static bool operator !=(ForumPostTag e1, ForumPostTag e2)
- => !(e1 == e2);
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordEmoji"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First forum post tag to compare.</param>
+ /// <param name="e2">Second forum post tag to compare.</param>
+ /// <returns>Whether the two forum post tags are not equal.</returns>
+ public static bool operator !=(ForumPostTag e1, ForumPostTag e2)
+ => !(e1 == e2);
+ }
}
diff --git a/DisCatSharp/Entities/Channel/Overwrite/DiscordOverwrite.cs b/DisCatSharp/Entities/Channel/Overwrite/DiscordOverwrite.cs
index d42d9a1ba..684ea0fd9 100644
--- a/DisCatSharp/Entities/Channel/Overwrite/DiscordOverwrite.cs
+++ b/DisCatSharp/Entities/Channel/Overwrite/DiscordOverwrite.cs
@@ -1,121 +1,122 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a permission overwrite for a channel.
-/// </summary>
-public class DiscordOverwrite : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the type of the overwrite. Either "role" or "member".
+ /// Represents a permission overwrite for a channel.
/// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public OverwriteType Type { get; internal set; }
+ public class DiscordOverwrite : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the type of the overwrite. Either "role" or "member".
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public OverwriteType Type { get; internal set; }
- /// <summary>
- /// Gets the allowed permission set.
- /// </summary>
- [JsonProperty("allow", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions Allowed { get; internal set; }
+ /// <summary>
+ /// Gets the allowed permission set.
+ /// </summary>
+ [JsonProperty("allow", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions Allowed { get; internal set; }
- /// <summary>
- /// Gets the denied permission set.
- /// </summary>
- [JsonProperty("deny", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions Denied { get; internal set; }
+ /// <summary>
+ /// Gets the denied permission set.
+ /// </summary>
+ [JsonProperty("deny", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions Denied { get; internal set; }
- [JsonIgnore]
- internal ulong ChannelId;
+ [JsonIgnore]
+ internal ulong ChannelId;
- #region Methods
- /// <summary>
- /// Deletes this channel overwrite.
- /// </summary>
- /// <param name="reason">Reason as to why this overwrite gets deleted.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the overwrite does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteAsync(string reason = null) => this.Discord.ApiClient.DeleteChannelPermissionAsync(this.ChannelId, this.Id, reason);
+ #region Methods
+ /// <summary>
+ /// Deletes this channel overwrite.
+ /// </summary>
+ /// <param name="reason">Reason as to why this overwrite gets deleted.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the overwrite does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteAsync(string reason = null) => this.Discord.ApiClient.DeleteChannelPermissionAsync(this.ChannelId, this.Id, reason);
- /// <summary>
- /// Updates this channel overwrite.
- /// </summary>
- /// <param name="allow">Permissions that are allowed.</param>
- /// <param name="deny">Permissions that are denied.</param>
- /// <param name="reason">Reason as to why you made this change.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the overwrite does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task UpdateAsync(Permissions? allow = null, Permissions? deny = null, string reason = null)
- => this.Discord.ApiClient.EditChannelPermissionsAsync(this.ChannelId, this.Id, allow ?? this.Allowed, deny ?? this.Denied, this.Type.ToString().ToLowerInvariant(), reason);
+ /// <summary>
+ /// Updates this channel overwrite.
+ /// </summary>
+ /// <param name="allow">Permissions that are allowed.</param>
+ /// <param name="deny">Permissions that are denied.</param>
+ /// <param name="reason">Reason as to why you made this change.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the overwrite does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task UpdateAsync(Permissions? allow = null, Permissions? deny = null, string reason = null)
+ => this.Discord.ApiClient.EditChannelPermissionsAsync(this.ChannelId, this.Id, allow ?? this.Allowed, deny ?? this.Denied, this.Type.ToString().ToLowerInvariant(), reason);
- /// <summary>
- /// Gets the DiscordMember that is affected by this overwrite.
- /// </summary>
- /// <returns>The DiscordMember that is affected by this overwrite</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the overwrite does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMember> GetMemberAsync() =>
- this.Type != OverwriteType.Member
- ? throw new ArgumentException(nameof(this.Type), "This overwrite is for a role, not a member.")
- : await (await this.Discord.ApiClient.GetChannelAsync(this.ChannelId).ConfigureAwait(false)).Guild.GetMemberAsync(this.Id).ConfigureAwait(false);
+ /// <summary>
+ /// Gets the DiscordMember that is affected by this overwrite.
+ /// </summary>
+ /// <returns>The DiscordMember that is affected by this overwrite</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the overwrite does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMember> GetMemberAsync() =>
+ this.Type != OverwriteType.Member
+ ? throw new ArgumentException(nameof(this.Type), "This overwrite is for a role, not a member.")
+ : await (await this.Discord.ApiClient.GetChannelAsync(this.ChannelId).ConfigureAwait(false)).Guild.GetMemberAsync(this.Id).ConfigureAwait(false);
- /// <summary>
- /// Gets the DiscordRole that is affected by this overwrite.
- /// </summary>
- /// <returns>The DiscordRole that is affected by this overwrite</returns>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordRole> GetRoleAsync() =>
- this.Type != OverwriteType.Role
- ? throw new ArgumentException(nameof(this.Type), "This overwrite is for a member, not a role.")
- : (await this.Discord.ApiClient.GetChannelAsync(this.ChannelId).ConfigureAwait(false)).Guild.GetRole(this.Id);
- #endregion
+ /// <summary>
+ /// Gets the DiscordRole that is affected by this overwrite.
+ /// </summary>
+ /// <returns>The DiscordRole that is affected by this overwrite</returns>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordRole> GetRoleAsync() =>
+ this.Type != OverwriteType.Role
+ ? throw new ArgumentException(nameof(this.Type), "This overwrite is for a member, not a role.")
+ : (await this.Discord.ApiClient.GetChannelAsync(this.ChannelId).ConfigureAwait(false)).Guild.GetRole(this.Id);
+ #endregion
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordOverwrite"/> class.
- /// </summary>
- internal DiscordOverwrite()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordOverwrite"/> class.
+ /// </summary>
+ internal DiscordOverwrite()
+ { }
- /// <summary>
- /// Checks whether given permissions are allowed, denied, or not set.
- /// </summary>
- /// <param name="permission">Permissions to check.</param>
- /// <returns>Whether given permissions are allowed, denied, or not set.</returns>
- public PermissionLevel CheckPermission(Permissions permission) =>
- (this.Allowed & permission) != 0
- ? PermissionLevel.Allowed
- : (this.Denied & permission) != 0 ? PermissionLevel.Denied : PermissionLevel.Unset;
+ /// <summary>
+ /// Checks whether given permissions are allowed, denied, or not set.
+ /// </summary>
+ /// <param name="permission">Permissions to check.</param>
+ /// <returns>Whether given permissions are allowed, denied, or not set.</returns>
+ public PermissionLevel CheckPermission(Permissions permission) =>
+ (this.Allowed & permission) != 0
+ ? PermissionLevel.Allowed
+ : (this.Denied & permission) != 0 ? PermissionLevel.Denied : PermissionLevel.Unset;
+ }
}
diff --git a/DisCatSharp/Entities/Channel/Overwrite/DiscordOverwriteBuilder.cs b/DisCatSharp/Entities/Channel/Overwrite/DiscordOverwriteBuilder.cs
index 163954b38..819344c1d 100644
--- a/DisCatSharp/Entities/Channel/Overwrite/DiscordOverwriteBuilder.cs
+++ b/DisCatSharp/Entities/Channel/Overwrite/DiscordOverwriteBuilder.cs
@@ -1,178 +1,179 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord permission overwrite builder.
-/// </summary>
-public sealed class DiscordOverwriteBuilder
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets or sets the allowed permissions for this overwrite.
- /// </summary>
- public Permissions Allowed { get; set; }
-
- /// <summary>
- /// Gets or sets the denied permissions for this overwrite.
- /// </summary>
- public Permissions Denied { get; set; }
-
- /// <summary>
- /// Gets the type of this overwrite's target.
- /// </summary>
- public OverwriteType Type { get; private set; }
-
- /// <summary>
- /// Gets the target for this overwrite.
- /// </summary>
- public SnowflakeObject Target { get; private set; }
-
- /// <summary>
- /// Creates a new Discord permission overwrite builder for a member. This class can be used to construct permission overwrites for guild channels, used when creating channels.
- /// </summary>
- public DiscordOverwriteBuilder(DiscordMember member)
- {
- this.Target = member;
- this.Type = OverwriteType.Member;
- }
-
- /// <summary>
- /// Creates a new Discord permission overwrite builder for a role. This class can be used to construct permission overwrites for guild channels, used when creating channels.
+ /// Represents a Discord permission overwrite builder.
/// </summary>
- public DiscordOverwriteBuilder(DiscordRole role)
+ public sealed class DiscordOverwriteBuilder
{
- this.Target = role;
- this.Type = OverwriteType.Role;
- }
-
- /// <summary>
- /// Creates a new Discord permission overwrite builder. This class can be used to construct permission overwrites for guild channels, used when creating channels.
- /// </summary>
- public DiscordOverwriteBuilder()
- { }
-
- /// <summary>
- /// Allows a permission for this overwrite.
- /// </summary>
- /// <param name="permission">Permission or permission set to allow for this overwrite.</param>
- /// <returns>This builder.</returns>
- public DiscordOverwriteBuilder Allow(Permissions permission)
- {
- this.Allowed |= permission;
- return this;
- }
-
- /// <summary>
- /// Denies a permission for this overwrite.
- /// </summary>
- /// <param name="permission">Permission or permission set to deny for this overwrite.</param>
- /// <returns>This builder.</returns>
- public DiscordOverwriteBuilder Deny(Permissions permission)
- {
- this.Denied |= permission;
- return this;
- }
-
- /// <summary>
- /// Sets the member to which this overwrite applies.
- /// </summary>
- /// <param name="member">Member to which apply this overwrite's permissions.</param>
- /// <returns>This builder.</returns>
- public DiscordOverwriteBuilder For(DiscordMember member)
- {
- this.Target = member;
- this.Type = OverwriteType.Member;
- return this;
- }
-
- /// <summary>
- /// Sets the role to which this overwrite applies.
- /// </summary>
- /// <param name="role">Role to which apply this overwrite's permissions.</param>
- /// <returns>This builder.</returns>
- public DiscordOverwriteBuilder For(DiscordRole role)
- {
- this.Target = role;
- this.Type = OverwriteType.Role;
- return this;
+ /// <summary>
+ /// Gets or sets the allowed permissions for this overwrite.
+ /// </summary>
+ public Permissions Allowed { get; set; }
+
+ /// <summary>
+ /// Gets or sets the denied permissions for this overwrite.
+ /// </summary>
+ public Permissions Denied { get; set; }
+
+ /// <summary>
+ /// Gets the type of this overwrite's target.
+ /// </summary>
+ public OverwriteType Type { get; private set; }
+
+ /// <summary>
+ /// Gets the target for this overwrite.
+ /// </summary>
+ public SnowflakeObject Target { get; private set; }
+
+ /// <summary>
+ /// Creates a new Discord permission overwrite builder for a member. This class can be used to construct permission overwrites for guild channels, used when creating channels.
+ /// </summary>
+ public DiscordOverwriteBuilder(DiscordMember member)
+ {
+ this.Target = member;
+ this.Type = OverwriteType.Member;
+ }
+
+ /// <summary>
+ /// Creates a new Discord permission overwrite builder for a role. This class can be used to construct permission overwrites for guild channels, used when creating channels.
+ /// </summary>
+ public DiscordOverwriteBuilder(DiscordRole role)
+ {
+ this.Target = role;
+ this.Type = OverwriteType.Role;
+ }
+
+ /// <summary>
+ /// Creates a new Discord permission overwrite builder. This class can be used to construct permission overwrites for guild channels, used when creating channels.
+ /// </summary>
+ public DiscordOverwriteBuilder()
+ { }
+
+ /// <summary>
+ /// Allows a permission for this overwrite.
+ /// </summary>
+ /// <param name="permission">Permission or permission set to allow for this overwrite.</param>
+ /// <returns>This builder.</returns>
+ public DiscordOverwriteBuilder Allow(Permissions permission)
+ {
+ this.Allowed |= permission;
+ return this;
+ }
+
+ /// <summary>
+ /// Denies a permission for this overwrite.
+ /// </summary>
+ /// <param name="permission">Permission or permission set to deny for this overwrite.</param>
+ /// <returns>This builder.</returns>
+ public DiscordOverwriteBuilder Deny(Permissions permission)
+ {
+ this.Denied |= permission;
+ return this;
+ }
+
+ /// <summary>
+ /// Sets the member to which this overwrite applies.
+ /// </summary>
+ /// <param name="member">Member to which apply this overwrite's permissions.</param>
+ /// <returns>This builder.</returns>
+ public DiscordOverwriteBuilder For(DiscordMember member)
+ {
+ this.Target = member;
+ this.Type = OverwriteType.Member;
+ return this;
+ }
+
+ /// <summary>
+ /// Sets the role to which this overwrite applies.
+ /// </summary>
+ /// <param name="role">Role to which apply this overwrite's permissions.</param>
+ /// <returns>This builder.</returns>
+ public DiscordOverwriteBuilder For(DiscordRole role)
+ {
+ this.Target = role;
+ this.Type = OverwriteType.Role;
+ return this;
+ }
+
+ /// <summary>
+ /// Populates this builder with data from another overwrite object.
+ /// </summary>
+ /// <param name="other">Overwrite from which data will be used.</param>
+ /// <returns>This builder.</returns>
+ public async Task<DiscordOverwriteBuilder> FromAsync(DiscordOverwrite other)
+ {
+ this.Allowed = other.Allowed;
+ this.Denied = other.Denied;
+ this.Type = other.Type;
+ this.Target = this.Type == OverwriteType.Member ? await other.GetMemberAsync().ConfigureAwait(false) as SnowflakeObject : await other.GetRoleAsync().ConfigureAwait(false) as SnowflakeObject;
+
+ return this;
+ }
+
+ /// <summary>
+ /// Builds this DiscordOverwrite.
+ /// </summary>
+ /// <returns>Use this object for creation of new overwrites.</returns>
+ internal DiscordRestOverwrite Build() =>
+ new()
+ {
+ Allow = this.Allowed,
+ Deny = this.Denied,
+ Id = this.Target.Id,
+ Type = this.Type,
+ };
}
- /// <summary>
- /// Populates this builder with data from another overwrite object.
- /// </summary>
- /// <param name="other">Overwrite from which data will be used.</param>
- /// <returns>This builder.</returns>
- public async Task<DiscordOverwriteBuilder> FromAsync(DiscordOverwrite other)
+ internal struct DiscordRestOverwrite
{
- this.Allowed = other.Allowed;
- this.Denied = other.Denied;
- this.Type = other.Type;
- this.Target = this.Type == OverwriteType.Member ? await other.GetMemberAsync().ConfigureAwait(false) as SnowflakeObject : await other.GetRoleAsync().ConfigureAwait(false) as SnowflakeObject;
-
- return this;
+ /// <summary>
+ /// Determines what is allowed.
+ /// </summary>
+ [JsonProperty("allow", NullValueHandling = NullValueHandling.Ignore)]
+ internal Permissions Allow { get; set; }
+
+ /// <summary>
+ /// Determines what is denied.
+ /// </summary>
+ [JsonProperty("deny", NullValueHandling = NullValueHandling.Ignore)]
+ internal Permissions Deny { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the overwrite type.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ internal OverwriteType Type { get; set; }
}
-
- /// <summary>
- /// Builds this DiscordOverwrite.
- /// </summary>
- /// <returns>Use this object for creation of new overwrites.</returns>
- internal DiscordRestOverwrite Build() =>
- new()
- {
- Allow = this.Allowed,
- Deny = this.Denied,
- Id = this.Target.Id,
- Type = this.Type,
- };
-}
-
-internal struct DiscordRestOverwrite
-{
- /// <summary>
- /// Determines what is allowed.
- /// </summary>
- [JsonProperty("allow", NullValueHandling = NullValueHandling.Ignore)]
- internal Permissions Allow { get; set; }
-
- /// <summary>
- /// Determines what is denied.
- /// </summary>
- [JsonProperty("deny", NullValueHandling = NullValueHandling.Ignore)]
- internal Permissions Deny { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong Id { get; set; }
-
- /// <summary>
- /// Gets or sets the overwrite type.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- internal OverwriteType Type { get; set; }
}
diff --git a/DisCatSharp/Entities/Color/DiscordColor.Colors.cs b/DisCatSharp/Entities/Color/DiscordColor.Colors.cs
index 54d16260f..5132862fd 100644
--- a/DisCatSharp/Entities/Color/DiscordColor.Colors.cs
+++ b/DisCatSharp/Entities/Color/DiscordColor.Colors.cs
@@ -1,263 +1,264 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Entities;
-
-public partial struct DiscordColor
+namespace DisCatSharp.Entities
{
- #region Black and White
- /// <summary>
- /// Represents no color, or integer 0;
- /// </summary>
- public static DiscordColor None { get; } = new(0);
-
- /// <summary>
- /// A near-black color. Due to API limitations, the color is #010101, rather than #000000, as the latter is treated as no color.
- /// </summary>
- public static DiscordColor Black { get; } = new(0x010101);
-
- /// <summary>
- /// White, or #FFFFFF.
- /// </summary>
- public static DiscordColor White { get; } = new(0xFFFFFF);
-
- /// <summary>
- /// Gray, or #808080.
- /// </summary>
- public static DiscordColor Gray { get; } = new(0x808080);
-
- /// <summary>
- /// Dark gray, or #A9A9A9.
- /// </summary>
- public static DiscordColor DarkGray { get; } = new(0xA9A9A9);
-
- /// <summary>
- /// Light gray, or #808080.
- /// </summary>
- public static DiscordColor LightGray { get; } = new(0xD3D3D3);
-
- // dev-approved
- /// <summary>
- /// Very dark gray, or #666666.
- /// </summary>
- public static DiscordColor VeryDarkGray { get; } = new(0x666666);
- #endregion
-
- #region Discord branding colors
- // https://discord.com/branding
-
- /// <summary>
- /// Discord Blurple, or #5865F2.
- /// </summary>
- public static DiscordColor Blurple { get; } = new(0x5865F2);
-
- /// <summary>
- /// Discord Fuchsia, or #EB459E.
- /// </summary>
- public static DiscordColor Fuchsia { get; } = new(0xEB459E);
-
- /// <summary>
- /// Discord Green, or #57F287.
- /// </summary>
- public static DiscordColor Green { get; } = new(0x57F287);
-
- /// <summary>
- /// Discord Yellow, or #FEE75C.
- /// </summary>
- public static DiscordColor Yellow { get; } = new(0xFEE75C);
-
- /// <summary>
- /// Discord Red, or #ED4245.
- /// </summary>
- public static DiscordColor Red { get; } = new(0xED4245);
- #endregion
-
- #region Other colors
- /// <summary>
- /// Dark red, or #7F0000.
- /// </summary>
- public static DiscordColor DarkRed { get; } = new(0x7F0000);
-
- /// <summary>
- /// Dark green, or #007F00.
- /// </summary>
- public static DiscordColor DarkGreen { get; } = new(0x007F00);
-
- /// <summary>
- /// Blue, or #0000FF.
- /// </summary>
- public static DiscordColor Blue { get; } = new(0x0000FF);
-
- /// <summary>
- /// Dark blue, or #00007F.
- /// </summary>
- public static DiscordColor DarkBlue { get; } = new(0x00007F);
-
- /// <summary>
- /// Cyan, or #00FFFF.
- /// </summary>
- public static DiscordColor Cyan { get; } = new(0x00FFFF);
-
- /// <summary>
- /// Magenta, or #FF00FF.
- /// </summary>
- public static DiscordColor Magenta { get; } = new(0xFF00FF);
-
- /// <summary>
- /// Teal, or #008080.
- /// </summary>
- public static DiscordColor Teal { get; } = new(0x008080);
-
- // meme
- /// <summary>
- /// Aquamarine, or #00FFBF.
- /// </summary>
- public static DiscordColor Aquamarine { get; } = new(0x00FFBF);
-
- /// <summary>
- /// Gold, or #FFD700.
- /// </summary>
- public static DiscordColor Gold { get; } = new(0xFFD700);
-
- // To be fair, you have to have a very high IQ to understand Goldenrod .
- // The tones are extremely subtle, and without a solid grasp of artistic
- // theory most of the beauty will go over a typical painter's head.
- // There's also the flower's nihilistic style, which is deftly woven
- // into its characterization - it's pollinated by the Bombus cryptarum
- // bumblebee, for instance. The fans understand this stuff; they have
- // the intellectual capacity to truly appreciate the depth of this
- // flower, to realize that it's not just a color - it says something
- // deep about LIFE. As a consequence people who dislike Goldenrod truly
- // ARE idiots - of course they wouldn't appreciate, for instance, the
- // beauty in the bumblebee species' complex presence in the British Isles,
- // which is cryptically explained by Turgenev's Russian epic Fathers and
- // Sons I'm blushing right now just imagining one of those addlepated
- // simpletons scratching their heads in confusion as nature's genius
- // unfolds itself on their computer screens. What fools... how I pity them.
- // 😂 And yes by the way, I DO have a goldenrod tattoo. And no, you cannot
- // see it. It's for the ladies' eyes only- And even they have to
- // demonstrate that they're within 5 IQ points of my own (preferably lower) beforehand.
- /// <summary>
- /// Goldenrod, or #DAA520.
- /// </summary>
- public static DiscordColor Goldenrod { get; } = new(0xDAA520);
-
- // emzi's favourite
- /// <summary>
- /// Azure, or #007FFF.
- /// </summary>
- public static DiscordColor Azure { get; } = new(0x007FFF);
-
- /// <summary>
- /// Rose, or #FF007F.
- /// </summary>
- public static DiscordColor Rose { get; } = new(0xFF007F);
-
- /// <summary>
- /// Spring green, or #00FF7F.
- /// </summary>
- public static DiscordColor SpringGreen { get; } = new(0x00FF7F);
-
- /// <summary>
- /// Chartreuse, or #7FFF00.
- /// </summary>
- public static DiscordColor Chartreuse { get; } = new(0x7FFF00);
-
- /// <summary>
- /// Orange, or #FFA500.
- /// </summary>
- public static DiscordColor Orange { get; } = new(0xFFA500);
-
- /// <summary>
- /// Purple, or #800080.
- /// </summary>
- public static DiscordColor Purple { get; } = new(0x800080);
-
- /// <summary>
- /// Violet, or #EE82EE.
- /// </summary>
- public static DiscordColor Violet { get; } = new(0xEE82EE);
-
- /// <summary>
- /// Brown, or #A52A2A.
- /// </summary>
- public static DiscordColor Brown { get; } = new(0xA52A2A);
-
- // meme
- /// <summary>
- /// Hot pink, or #FF69B4
- /// </summary>
- public static DiscordColor HotPink { get; } = new(0xFF69B4);
-
- /// <summary>
- /// Lilac, or #C8A2C8.
- /// </summary>
- public static DiscordColor Lilac { get; } = new(0xC8A2C8);
-
- /// <summary>
- /// Cornflower blue, or #6495ED.
- /// </summary>
- public static DiscordColor CornflowerBlue { get; } = new(0x6495ED);
-
- /// <summary>
- /// Midnight blue, or #191970.
- /// </summary>
- public static DiscordColor MidnightBlue { get; } = new(0x191970);
-
- /// <summary>
- /// Wheat, or #F5DEB3.
- /// </summary>
- public static DiscordColor Wheat { get; } = new(0xF5DEB3);
-
- /// <summary>
- /// Indian red, or #CD5C5C.
- /// </summary>
- public static DiscordColor IndianRed { get; } = new(0xCD5C5C);
-
- /// <summary>
- /// Turquoise, or #30D5C8.
- /// </summary>
- public static DiscordColor Turquoise { get; } = new(0x30D5C8);
-
- /// <summary>
- /// Sap green, or #507D2A.
- /// </summary>
- public static DiscordColor SapGreen { get; } = new(0x507D2A);
-
- // meme, specifically bob ross
- /// <summary>
- /// Phthalo blue, or #000F89.
- /// </summary>
- public static DiscordColor PhthaloBlue { get; } = new(0x000F89);
-
- // meme, specifically bob ross
- /// <summary>
- /// Phthalo green, or #123524.
- /// </summary>
- public static DiscordColor PhthaloGreen { get; } = new(0x123524);
-
- /// <summary>
- /// Sienna, or #882D17.
- /// </summary>
- public static DiscordColor Sienna { get; } = new(0x882D17);
- #endregion
+ public partial struct DiscordColor
+ {
+ #region Black and White
+ /// <summary>
+ /// Represents no color, or integer 0;
+ /// </summary>
+ public static DiscordColor None { get; } = new(0);
+
+ /// <summary>
+ /// A near-black color. Due to API limitations, the color is #010101, rather than #000000, as the latter is treated as no color.
+ /// </summary>
+ public static DiscordColor Black { get; } = new(0x010101);
+
+ /// <summary>
+ /// White, or #FFFFFF.
+ /// </summary>
+ public static DiscordColor White { get; } = new(0xFFFFFF);
+
+ /// <summary>
+ /// Gray, or #808080.
+ /// </summary>
+ public static DiscordColor Gray { get; } = new(0x808080);
+
+ /// <summary>
+ /// Dark gray, or #A9A9A9.
+ /// </summary>
+ public static DiscordColor DarkGray { get; } = new(0xA9A9A9);
+
+ /// <summary>
+ /// Light gray, or #808080.
+ /// </summary>
+ public static DiscordColor LightGray { get; } = new(0xD3D3D3);
+
+ // dev-approved
+ /// <summary>
+ /// Very dark gray, or #666666.
+ /// </summary>
+ public static DiscordColor VeryDarkGray { get; } = new(0x666666);
+ #endregion
+
+ #region Discord branding colors
+ // https://discord.com/branding
+
+ /// <summary>
+ /// Discord Blurple, or #5865F2.
+ /// </summary>
+ public static DiscordColor Blurple { get; } = new(0x5865F2);
+
+ /// <summary>
+ /// Discord Fuchsia, or #EB459E.
+ /// </summary>
+ public static DiscordColor Fuchsia { get; } = new(0xEB459E);
+
+ /// <summary>
+ /// Discord Green, or #57F287.
+ /// </summary>
+ public static DiscordColor Green { get; } = new(0x57F287);
+
+ /// <summary>
+ /// Discord Yellow, or #FEE75C.
+ /// </summary>
+ public static DiscordColor Yellow { get; } = new(0xFEE75C);
+
+ /// <summary>
+ /// Discord Red, or #ED4245.
+ /// </summary>
+ public static DiscordColor Red { get; } = new(0xED4245);
+ #endregion
+
+ #region Other colors
+ /// <summary>
+ /// Dark red, or #7F0000.
+ /// </summary>
+ public static DiscordColor DarkRed { get; } = new(0x7F0000);
+
+ /// <summary>
+ /// Dark green, or #007F00.
+ /// </summary>
+ public static DiscordColor DarkGreen { get; } = new(0x007F00);
+
+ /// <summary>
+ /// Blue, or #0000FF.
+ /// </summary>
+ public static DiscordColor Blue { get; } = new(0x0000FF);
+
+ /// <summary>
+ /// Dark blue, or #00007F.
+ /// </summary>
+ public static DiscordColor DarkBlue { get; } = new(0x00007F);
+
+ /// <summary>
+ /// Cyan, or #00FFFF.
+ /// </summary>
+ public static DiscordColor Cyan { get; } = new(0x00FFFF);
+
+ /// <summary>
+ /// Magenta, or #FF00FF.
+ /// </summary>
+ public static DiscordColor Magenta { get; } = new(0xFF00FF);
+
+ /// <summary>
+ /// Teal, or #008080.
+ /// </summary>
+ public static DiscordColor Teal { get; } = new(0x008080);
+
+ // meme
+ /// <summary>
+ /// Aquamarine, or #00FFBF.
+ /// </summary>
+ public static DiscordColor Aquamarine { get; } = new(0x00FFBF);
+
+ /// <summary>
+ /// Gold, or #FFD700.
+ /// </summary>
+ public static DiscordColor Gold { get; } = new(0xFFD700);
+
+ // To be fair, you have to have a very high IQ to understand Goldenrod .
+ // The tones are extremely subtle, and without a solid grasp of artistic
+ // theory most of the beauty will go over a typical painter's head.
+ // There's also the flower's nihilistic style, which is deftly woven
+ // into its characterization - it's pollinated by the Bombus cryptarum
+ // bumblebee, for instance. The fans understand this stuff; they have
+ // the intellectual capacity to truly appreciate the depth of this
+ // flower, to realize that it's not just a color - it says something
+ // deep about LIFE. As a consequence people who dislike Goldenrod truly
+ // ARE idiots - of course they wouldn't appreciate, for instance, the
+ // beauty in the bumblebee species' complex presence in the British Isles,
+ // which is cryptically explained by Turgenev's Russian epic Fathers and
+ // Sons I'm blushing right now just imagining one of those addlepated
+ // simpletons scratching their heads in confusion as nature's genius
+ // unfolds itself on their computer screens. What fools... how I pity them.
+ // 😂 And yes by the way, I DO have a goldenrod tattoo. And no, you cannot
+ // see it. It's for the ladies' eyes only- And even they have to
+ // demonstrate that they're within 5 IQ points of my own (preferably lower) beforehand.
+ /// <summary>
+ /// Goldenrod, or #DAA520.
+ /// </summary>
+ public static DiscordColor Goldenrod { get; } = new(0xDAA520);
+
+ // emzi's favourite
+ /// <summary>
+ /// Azure, or #007FFF.
+ /// </summary>
+ public static DiscordColor Azure { get; } = new(0x007FFF);
+
+ /// <summary>
+ /// Rose, or #FF007F.
+ /// </summary>
+ public static DiscordColor Rose { get; } = new(0xFF007F);
+
+ /// <summary>
+ /// Spring green, or #00FF7F.
+ /// </summary>
+ public static DiscordColor SpringGreen { get; } = new(0x00FF7F);
+
+ /// <summary>
+ /// Chartreuse, or #7FFF00.
+ /// </summary>
+ public static DiscordColor Chartreuse { get; } = new(0x7FFF00);
+
+ /// <summary>
+ /// Orange, or #FFA500.
+ /// </summary>
+ public static DiscordColor Orange { get; } = new(0xFFA500);
+
+ /// <summary>
+ /// Purple, or #800080.
+ /// </summary>
+ public static DiscordColor Purple { get; } = new(0x800080);
+
+ /// <summary>
+ /// Violet, or #EE82EE.
+ /// </summary>
+ public static DiscordColor Violet { get; } = new(0xEE82EE);
+
+ /// <summary>
+ /// Brown, or #A52A2A.
+ /// </summary>
+ public static DiscordColor Brown { get; } = new(0xA52A2A);
+
+ // meme
+ /// <summary>
+ /// Hot pink, or #FF69B4
+ /// </summary>
+ public static DiscordColor HotPink { get; } = new(0xFF69B4);
+
+ /// <summary>
+ /// Lilac, or #C8A2C8.
+ /// </summary>
+ public static DiscordColor Lilac { get; } = new(0xC8A2C8);
+
+ /// <summary>
+ /// Cornflower blue, or #6495ED.
+ /// </summary>
+ public static DiscordColor CornflowerBlue { get; } = new(0x6495ED);
+
+ /// <summary>
+ /// Midnight blue, or #191970.
+ /// </summary>
+ public static DiscordColor MidnightBlue { get; } = new(0x191970);
+
+ /// <summary>
+ /// Wheat, or #F5DEB3.
+ /// </summary>
+ public static DiscordColor Wheat { get; } = new(0xF5DEB3);
+
+ /// <summary>
+ /// Indian red, or #CD5C5C.
+ /// </summary>
+ public static DiscordColor IndianRed { get; } = new(0xCD5C5C);
+
+ /// <summary>
+ /// Turquoise, or #30D5C8.
+ /// </summary>
+ public static DiscordColor Turquoise { get; } = new(0x30D5C8);
+
+ /// <summary>
+ /// Sap green, or #507D2A.
+ /// </summary>
+ public static DiscordColor SapGreen { get; } = new(0x507D2A);
+
+ // meme, specifically bob ross
+ /// <summary>
+ /// Phthalo blue, or #000F89.
+ /// </summary>
+ public static DiscordColor PhthaloBlue { get; } = new(0x000F89);
+
+ // meme, specifically bob ross
+ /// <summary>
+ /// Phthalo green, or #123524.
+ /// </summary>
+ public static DiscordColor PhthaloGreen { get; } = new(0x123524);
+
+ /// <summary>
+ /// Sienna, or #882D17.
+ /// </summary>
+ public static DiscordColor Sienna { get; } = new(0x882D17);
+ #endregion
+ }
}
diff --git a/DisCatSharp/Entities/Color/DiscordColor.cs b/DisCatSharp/Entities/Color/DiscordColor.cs
index 8976d93b3..6db108788 100644
--- a/DisCatSharp/Entities/Color/DiscordColor.cs
+++ b/DisCatSharp/Entities/Color/DiscordColor.cs
@@ -1,129 +1,130 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a color used in Discord API.
-/// </summary>
-public partial struct DiscordColor
+namespace DisCatSharp.Entities
{
- private static readonly char[] s_hexAlphabet = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
-
- /// <summary>
- /// Gets the integer representation of this color.
- /// </summary>
- public int Value { get; }
-
- /// <summary>
- /// Gets the red component of this color as an 8-bit integer.
- /// </summary>
- public byte R
- => (byte)((this.Value >> 16) & 0xFF);
-
- /// <summary>
- /// Gets the green component of this color as an 8-bit integer.
- /// </summary>
- public byte G
- => (byte)((this.Value >> 8) & 0xFF);
-
- /// <summary>
- /// Gets the blue component of this color as an 8-bit integer.
- /// </summary>
- public byte B
- => (byte)(this.Value & 0xFF);
-
- /// <summary>
- /// Creates a new color with specified value.
- /// </summary>
- /// <param name="color">Value of the color.</param>
- public DiscordColor(int color)
- {
- this.Value = color;
- }
-
/// <summary>
- /// Creates a new color with specified values for red, green, and blue components.
+ /// Represents a color used in Discord API.
/// </summary>
- /// <param name="r">Value of the red component.</param>
- /// <param name="g">Value of the green component.</param>
- /// <param name="b">Value of the blue component.</param>
- public DiscordColor(byte r, byte g, byte b)
+ public partial struct DiscordColor
{
- this.Value = (r << 16) | (g << 8) | b;
+ private static readonly char[] s_hexAlphabet = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ /// <summary>
+ /// Gets the integer representation of this color.
+ /// </summary>
+ public int Value { get; }
+
+ /// <summary>
+ /// Gets the red component of this color as an 8-bit integer.
+ /// </summary>
+ public byte R
+ => (byte)((this.Value >> 16) & 0xFF);
+
+ /// <summary>
+ /// Gets the green component of this color as an 8-bit integer.
+ /// </summary>
+ public byte G
+ => (byte)((this.Value >> 8) & 0xFF);
+
+ /// <summary>
+ /// Gets the blue component of this color as an 8-bit integer.
+ /// </summary>
+ public byte B
+ => (byte)(this.Value & 0xFF);
+
+ /// <summary>
+ /// Creates a new color with specified value.
+ /// </summary>
+ /// <param name="color">Value of the color.</param>
+ public DiscordColor(int color)
+ {
+ this.Value = color;
+ }
+
+ /// <summary>
+ /// Creates a new color with specified values for red, green, and blue components.
+ /// </summary>
+ /// <param name="r">Value of the red component.</param>
+ /// <param name="g">Value of the green component.</param>
+ /// <param name="b">Value of the blue component.</param>
+ public DiscordColor(byte r, byte g, byte b)
+ {
+ this.Value = (r << 16) | (g << 8) | b;
+ }
+
+ /// <summary>
+ /// Creates a new color with specified values for red, green, and blue components.
+ /// </summary>
+ /// <param name="r">Value of the red component.</param>
+ /// <param name="g">Value of the green component.</param>
+ /// <param name="b">Value of the blue component.</param>
+ public DiscordColor(float r, float g, float b)
+ {
+ if (r < 0 || r > 1 || g < 0 || g > 1 || b < 0 || b > 1)
+ throw new ArgumentOutOfRangeException("Each component must be between 0.0 and 1.0 inclusive.");
+
+ var rb = (byte)(r * 255);
+ var gb = (byte)(g * 255);
+ var bb = (byte)(b * 255);
+
+ this.Value = (rb << 16) | (gb << 8) | bb;
+ }
+
+ /// <summary>
+ /// Creates a new color from specified string representation.
+ /// </summary>
+ /// <param name="color">String representation of the color. Must be 6 hexadecimal characters, optionally with # prefix.</param>
+ public DiscordColor(string color)
+ {
+ if (string.IsNullOrWhiteSpace(color))
+ throw new ArgumentNullException(nameof(color), "Null or empty values are not allowed!");
+
+ if (color.Length != 6 && color.Length != 7)
+ throw new ArgumentException(nameof(color), "Color must be 6 or 7 characters in length.");
+
+ color = color.ToUpper();
+ if (color.Length == 7 && color[0] != '#')
+ throw new ArgumentException(nameof(color), "7-character colors must begin with #.");
+ else if (color.Length == 7)
+ color = color[1..];
+
+ if (color.Any(xc => !s_hexAlphabet.Contains(xc)))
+ throw new ArgumentException(nameof(color), "Colors must consist of hexadecimal characters only.");
+
+ this.Value = int.Parse(color, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
+ }
+
+ /// <summary>
+ /// Gets a string representation of this color.
+ /// </summary>
+ /// <returns>String representation of this color.</returns>
+ public override string ToString() => $"#{this.Value:X6}";
+
+ public static implicit operator DiscordColor(int value)
+ => new(value);
}
-
- /// <summary>
- /// Creates a new color with specified values for red, green, and blue components.
- /// </summary>
- /// <param name="r">Value of the red component.</param>
- /// <param name="g">Value of the green component.</param>
- /// <param name="b">Value of the blue component.</param>
- public DiscordColor(float r, float g, float b)
- {
- if (r < 0 || r > 1 || g < 0 || g > 1 || b < 0 || b > 1)
- throw new ArgumentOutOfRangeException("Each component must be between 0.0 and 1.0 inclusive.");
-
- var rb = (byte)(r * 255);
- var gb = (byte)(g * 255);
- var bb = (byte)(b * 255);
-
- this.Value = (rb << 16) | (gb << 8) | bb;
- }
-
- /// <summary>
- /// Creates a new color from specified string representation.
- /// </summary>
- /// <param name="color">String representation of the color. Must be 6 hexadecimal characters, optionally with # prefix.</param>
- public DiscordColor(string color)
- {
- if (string.IsNullOrWhiteSpace(color))
- throw new ArgumentNullException(nameof(color), "Null or empty values are not allowed!");
-
- if (color.Length != 6 && color.Length != 7)
- throw new ArgumentException(nameof(color), "Color must be 6 or 7 characters in length.");
-
- color = color.ToUpper();
- if (color.Length == 7 && color[0] != '#')
- throw new ArgumentException(nameof(color), "7-character colors must begin with #.");
- else if (color.Length == 7)
- color = color[1..];
-
- if (color.Any(xc => !s_hexAlphabet.Contains(xc)))
- throw new ArgumentException(nameof(color), "Colors must consist of hexadecimal characters only.");
-
- this.Value = int.Parse(color, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
- }
-
- /// <summary>
- /// Gets a string representation of this color.
- /// </summary>
- /// <returns>String representation of this color.</returns>
- public override string ToString() => $"#{this.Value:X6}";
-
- public static implicit operator DiscordColor(int value)
- => new(value);
}
diff --git a/DisCatSharp/Entities/DCS/DisCatSharpTeam.cs b/DisCatSharp/Entities/DCS/DisCatSharpTeam.cs
index 01ec029cd..1384329d5 100644
--- a/DisCatSharp/Entities/DCS/DisCatSharpTeam.cs
+++ b/DisCatSharp/Entities/DCS/DisCatSharpTeam.cs
@@ -1,200 +1,201 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// The DisCatSharp team.
-/// </summary>
-public sealed class DisCatSharpTeam : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the team's name.
- /// </summary>
- public string TeamName { get; internal set; }
-
- /// <summary>
- /// Gets the team's icon.
- /// </summary>
- public string Icon
- => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.TEAM_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets the team's icon's hash.
- /// </summary>
- public string IconHash { get; internal set; }
-
- /// <summary>
- /// Gets the team's logo.
- /// </summary>
- public string Logo
- => !string.IsNullOrWhiteSpace(this.LogoHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.GuildId.ToString(CultureInfo.InvariantCulture)}/{this.LogoHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets the team's logo's hash.
- /// </summary>
- public string LogoHash { get; internal set; }
-
- /// <summary>
- /// Gets the team's banner.
- /// </summary>
- public string Banner
- => !string.IsNullOrWhiteSpace(this.BannerHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.BANNERS}/{this.GuildId.ToString(CultureInfo.InvariantCulture)}/{this.BannerHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets the team's banner's hash.
- /// </summary>
- public string BannerHash { get; internal set; }
-
- /// <summary>
- /// Gets the team's docs url.
- /// </summary>
- public string DocsUrl { get; internal set; }
-
- /// <summary>
- /// Gets the team's repo url.
- /// </summary>
- public string RepoUrl { get; internal set; }
-
- /// <summary>
- /// Gets the team's terms of service url.
- /// </summary>
- public string TermsOfServiceUrl { get; internal set; }
-
- /// <summary>
- /// Gets the team's privacy policy url.
+ /// The DisCatSharp team.
/// </summary>
- public string PrivacyPolicyUrl { get; internal set; }
-
- /// <summary>
- /// Get's the team's guild id
- /// </summary>
- public ulong GuildId { get; internal set; }
-
- /// <summary>
- /// Gets the team's developers.
- /// </summary>
- public IReadOnlyList<DisCatSharpTeamMember> Developers { get; internal set; }
-
- /// <summary>
- /// Gets the team's owner.
- /// </summary>
- public DisCatSharpTeamMember Owner { get; internal set; }
-
- /// <summary>
- /// Gets the team's guild.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Gets the team's support invite.
- /// </summary>
- public DiscordInvite SupportInvite { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DisCatSharpTeam"/> class.
- /// </summary>
- internal static async Task<DisCatSharpTeam> Get(HttpClient http, ILogger logger, DiscordApiClient apiClient)
+ public sealed class DisCatSharpTeam : SnowflakeObject
{
- try
+ /// <summary>
+ /// Gets the team's name.
+ /// </summary>
+ public string TeamName { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's icon.
+ /// </summary>
+ public string Icon
+ => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.TEAM_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets the team's icon's hash.
+ /// </summary>
+ public string IconHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's logo.
+ /// </summary>
+ public string Logo
+ => !string.IsNullOrWhiteSpace(this.LogoHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.GuildId.ToString(CultureInfo.InvariantCulture)}/{this.LogoHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets the team's logo's hash.
+ /// </summary>
+ public string LogoHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's banner.
+ /// </summary>
+ public string Banner
+ => !string.IsNullOrWhiteSpace(this.BannerHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.BANNERS}/{this.GuildId.ToString(CultureInfo.InvariantCulture)}/{this.BannerHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets the team's banner's hash.
+ /// </summary>
+ public string BannerHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's docs url.
+ /// </summary>
+ public string DocsUrl { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's repo url.
+ /// </summary>
+ public string RepoUrl { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's terms of service url.
+ /// </summary>
+ public string TermsOfServiceUrl { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's privacy policy url.
+ /// </summary>
+ public string PrivacyPolicyUrl { get; internal set; }
+
+ /// <summary>
+ /// Get's the team's guild id
+ /// </summary>
+ public ulong GuildId { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's developers.
+ /// </summary>
+ public IReadOnlyList<DisCatSharpTeamMember> Developers { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's owner.
+ /// </summary>
+ public DisCatSharpTeamMember Owner { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's guild.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's support invite.
+ /// </summary>
+ public DiscordInvite SupportInvite { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DisCatSharpTeam"/> class.
+ /// </summary>
+ internal static async Task<DisCatSharpTeam> Get(HttpClient http, ILogger logger, DiscordApiClient apiClient)
{
- var dcs = await http.GetStringAsync(new Uri("https://dcs.aitsys.dev/api/devs/"));
- var dcsGuild = await http.GetStringAsync(new Uri("https://dcs.aitsys.dev/api/guild/"));
+ try
+ {
+ var dcs = await http.GetStringAsync(new Uri("https://dcs.aitsys.dev/api/devs/"));
+ var dcsGuild = await http.GetStringAsync(new Uri("https://dcs.aitsys.dev/api/guild/"));
- var app = JsonConvert.DeserializeObject<TransportApplication>(dcs);
- var guild = JsonConvert.DeserializeObject<DiscordGuild>(dcsGuild);
+ var app = JsonConvert.DeserializeObject<TransportApplication>(dcs);
+ var guild = JsonConvert.DeserializeObject<DiscordGuild>(dcsGuild);
- var dcst = new DisCatSharpTeam
- {
- IconHash = app.Team.IconHash,
- TeamName = app.Team.Name,
- PrivacyPolicyUrl = app.PrivacyPolicyUrl,
- TermsOfServiceUrl = app.TermsOfServiceUrl,
- RepoUrl = "https://github.com/Aiko-IT-Systems/DisCatSharp",
- DocsUrl = "https://docs.dcs.aitsys.dev",
- Id = app.Team.Id,
- BannerHash = guild.BannerHash,
- LogoHash = guild.IconHash,
- GuildId = guild.Id,
- Guild = guild,
- SupportInvite = await apiClient.GetInviteAsync("GGYSywkxwN", true, true, null)
- };
- List<DisCatSharpTeamMember> team = new();
- DisCatSharpTeamMember owner = new();
- foreach (var mb in app.Team.Members.OrderBy(m => m.User.Username))
- {
- var tuser = await apiClient.GetUserAsync(mb.User.Id);
- var user = mb.User;
- if (mb.User.Id == 856780995629154305)
+ var dcst = new DisCatSharpTeam
{
- owner.Id = user.Id;
- owner.Username = user.Username;
- owner.Discriminator = user.Discriminator;
- owner.AvatarHash = user.AvatarHash;
- owner.BannerHash = tuser.BannerHash;
- owner.BannerColorInternal = tuser.BannerColorInternal;
- team.Add(owner);
- }
- else
+ IconHash = app.Team.IconHash,
+ TeamName = app.Team.Name,
+ PrivacyPolicyUrl = app.PrivacyPolicyUrl,
+ TermsOfServiceUrl = app.TermsOfServiceUrl,
+ RepoUrl = "https://github.com/Aiko-IT-Systems/DisCatSharp",
+ DocsUrl = "https://docs.dcs.aitsys.dev",
+ Id = app.Team.Id,
+ BannerHash = guild.BannerHash,
+ LogoHash = guild.IconHash,
+ GuildId = guild.Id,
+ Guild = guild,
+ SupportInvite = await apiClient.GetInviteAsync("GGYSywkxwN", true, true, null)
+ };
+ List<DisCatSharpTeamMember> team = new();
+ DisCatSharpTeamMember owner = new();
+ foreach (var mb in app.Team.Members.OrderBy(m => m.User.Username))
{
- team.Add(new DisCatSharpTeamMember
+ var tuser = await apiClient.GetUserAsync(mb.User.Id);
+ var user = mb.User;
+ if (mb.User.Id == 856780995629154305)
{
- Id = user.Id,
- Username = user.Username,
- Discriminator = user.Discriminator,
- AvatarHash = user.AvatarHash,
- BannerHash = tuser.BannerHash,
- BannerColorInternal = tuser.BannerColorInternal
- });
+ owner.Id = user.Id;
+ owner.Username = user.Username;
+ owner.Discriminator = user.Discriminator;
+ owner.AvatarHash = user.AvatarHash;
+ owner.BannerHash = tuser.BannerHash;
+ owner.BannerColorInternal = tuser.BannerColorInternal;
+ team.Add(owner);
+ }
+ else
+ {
+ team.Add(new DisCatSharpTeamMember
+ {
+ Id = user.Id,
+ Username = user.Username,
+ Discriminator = user.Discriminator,
+ AvatarHash = user.AvatarHash,
+ BannerHash = tuser.BannerHash,
+ BannerColorInternal = tuser.BannerColorInternal
+ });
+ }
}
- }
- dcst.Owner = owner;
- dcst.Developers = team;
+ dcst.Owner = owner;
+ dcst.Developers = team;
- return dcst;
- }
- catch (Exception ex)
- {
- logger.LogDebug(ex.Message);
- logger.LogDebug(ex.StackTrace);
- return null;
+ return dcst;
+ }
+ catch (Exception ex)
+ {
+ logger.LogDebug(ex.Message);
+ logger.LogDebug(ex.StackTrace);
+ return null;
+ }
}
- }
- private DisCatSharpTeam() { }
+ private DisCatSharpTeam() { }
+ }
}
diff --git a/DisCatSharp/Entities/DCS/DisCatSharpTeamMember.cs b/DisCatSharp/Entities/DCS/DisCatSharpTeamMember.cs
index fdecfd22b..5f9e3d338 100644
--- a/DisCatSharp/Entities/DCS/DisCatSharpTeamMember.cs
+++ b/DisCatSharp/Entities/DCS/DisCatSharpTeamMember.cs
@@ -1,92 +1,93 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using DisCatSharp.Enums;
using DisCatSharp.Net;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a DisCatSharp team member.
-/// </summary>
-public sealed class DisCatSharpTeamMember : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets this user's username.
+ /// Represents a DisCatSharp team member.
/// </summary>
- public string Username { get; internal set; }
+ public sealed class DisCatSharpTeamMember : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets this user's username.
+ /// </summary>
+ public string Username { get; internal set; }
- /// <summary>
- /// Gets the user's 4-digit discriminator.
- /// </summary>
- public string Discriminator { get; internal set; }
+ /// <summary>
+ /// Gets the user's 4-digit discriminator.
+ /// </summary>
+ public string Discriminator { get; internal set; }
- /// <summary>
- /// Gets the discriminator integer.
- /// </summary>
- internal int DiscriminatorInt
- => int.Parse(this.Discriminator, NumberStyles.Integer, CultureInfo.InvariantCulture);
+ /// <summary>
+ /// Gets the discriminator integer.
+ /// </summary>
+ internal int DiscriminatorInt
+ => int.Parse(this.Discriminator, NumberStyles.Integer, CultureInfo.InvariantCulture);
- /// <summary>
- /// Gets the user's banner color, if set. Mutually exclusive with <see cref="BannerHash"/>.
- /// </summary>
- public DiscordColor? BannerColor
- => !this.BannerColorInternal.HasValue ? null : new DiscordColor(this.BannerColorInternal.Value);
+ /// <summary>
+ /// Gets the user's banner color, if set. Mutually exclusive with <see cref="BannerHash"/>.
+ /// </summary>
+ public DiscordColor? BannerColor
+ => !this.BannerColorInternal.HasValue ? null : new DiscordColor(this.BannerColorInternal.Value);
- internal int? BannerColorInternal;
+ internal int? BannerColorInternal;
- /// <summary>
- /// Gets the user's banner url
- /// </summary>
- public string BannerUrl
- => string.IsNullOrWhiteSpace(this.BannerHash) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.BANNERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.BannerHash}.{(this.BannerHash.StartsWith("a_") ? "gif" : "png")}?size=4096";
+ /// <summary>
+ /// Gets the user's banner url
+ /// </summary>
+ public string BannerUrl
+ => string.IsNullOrWhiteSpace(this.BannerHash) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.BANNERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.BannerHash}.{(this.BannerHash.StartsWith("a_") ? "gif" : "png")}?size=4096";
- /// <summary>
- /// Gets the user's profile banner hash. Mutually exclusive with <see cref="BannerColor"/>.
- /// </summary>
- public string BannerHash { get; internal set; }
+ /// <summary>
+ /// Gets the user's profile banner hash. Mutually exclusive with <see cref="BannerColor"/>.
+ /// </summary>
+ public string BannerHash { get; internal set; }
- /// <summary>
- /// Gets the user's avatar hash.
- /// </summary>
- public string AvatarHash { get; internal set; }
+ /// <summary>
+ /// Gets the user's avatar hash.
+ /// </summary>
+ public string AvatarHash { get; internal set; }
- /// <summary>
- /// Gets the user's avatar URL.
- /// </summary>
- public string AvatarUrl
- => string.IsNullOrWhiteSpace(this.AvatarHash) ? this.DefaultAvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.AvatarHash}.{(this.AvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
+ /// <summary>
+ /// Gets the user's avatar URL.
+ /// </summary>
+ public string AvatarUrl
+ => string.IsNullOrWhiteSpace(this.AvatarHash) ? this.DefaultAvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.AvatarHash}.{(this.AvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
- /// <summary>
- /// Gets the URL of default avatar for this user.
- /// </summary>
- public string DefaultAvatarUrl
- => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMBED}{Endpoints.AVATARS}/{(this.DiscriminatorInt % 5).ToString(CultureInfo.InvariantCulture)}.png?size=1024";
+ /// <summary>
+ /// Gets the URL of default avatar for this user.
+ /// </summary>
+ public string DefaultAvatarUrl
+ => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMBED}{Endpoints.AVATARS}/{(this.DiscriminatorInt % 5).ToString(CultureInfo.InvariantCulture)}.png?size=1024";
- /// <summary>
- /// Initializes a new instance of the <see cref="DisCatSharpTeamMember"/> class.
- /// </summary>
- internal DisCatSharpTeamMember()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DisCatSharpTeamMember"/> class.
+ /// </summary>
+ internal DisCatSharpTeamMember()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/DiscordProtocol.cs b/DisCatSharp/Entities/DiscordProtocol.cs
index ca2c2db33..a24e90c28 100644
--- a/DisCatSharp/Entities/DiscordProtocol.cs
+++ b/DisCatSharp/Entities/DiscordProtocol.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents the discord protocol.
-/// </summary>
-public class DiscordProtocol
+namespace DisCatSharp.Entities
{
- #region Properties
-
- #endregion
-
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordProtocol"/> class.
+ /// Represents the discord protocol.
/// </summary>
- public DiscordProtocol()
- { }
+ public class DiscordProtocol
+ {
+ #region Properties
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordProtocol"/> class.
+ /// </summary>
+ public DiscordProtocol()
+ { }
- #region Methods
+ #region Methods
- #endregion
+ #endregion
+ }
}
diff --git a/DisCatSharp/Entities/DiscordUri.cs b/DisCatSharp/Entities/DiscordUri.cs
index 95eefc038..0b94fa242 100644
--- a/DisCatSharp/Entities/DiscordUri.cs
+++ b/DisCatSharp/Entities/DiscordUri.cs
@@ -1,161 +1,162 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Runtime.CompilerServices;
using Newtonsoft.Json;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// An URI in a Discord embed doesn't necessarily conform to the RFC 3986. If it uses the <c>attachment://</c>
-/// protocol, it mustn't contain a trailing slash to be interpreted correctly as an embed attachment reference by
-/// Discord.
-/// </summary>
-[JsonConverter(typeof(DiscordUriJsonConverter))]
-public class DiscordUri
+namespace DisCatSharp.Net
{
- private readonly object _value;
-
- /// <summary>
- /// The type of this URI.
- /// </summary>
- public DiscordUriType Type { get; }
-
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordUri"/> class.
+ /// An URI in a Discord embed doesn't necessarily conform to the RFC 3986. If it uses the <c>attachment://</c>
+ /// protocol, it mustn't contain a trailing slash to be interpreted correctly as an embed attachment reference by
+ /// Discord.
/// </summary>
- /// <param name="value">The value.</param>
- internal DiscordUri(Uri value)
+ [JsonConverter(typeof(DiscordUriJsonConverter))]
+ public class DiscordUri
{
- this._value = value ?? throw new ArgumentNullException(nameof(value));
- this.Type = DiscordUriType.Standard;
- }
+ private readonly object _value;
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordUri"/> class.
- /// </summary>
- /// <param name="value">The value.</param>
- internal DiscordUri(string value)
- {
- if (value == null)
- throw new ArgumentNullException(nameof(value));
+ /// <summary>
+ /// The type of this URI.
+ /// </summary>
+ public DiscordUriType Type { get; }
- if (IsStandard(value))
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordUri"/> class.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ internal DiscordUri(Uri value)
{
- this._value = new Uri(value);
+ this._value = value ?? throw new ArgumentNullException(nameof(value));
this.Type = DiscordUriType.Standard;
}
- else
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordUri"/> class.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ internal DiscordUri(string value)
{
- this._value = value;
- this.Type = DiscordUriType.NonStandard;
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+
+ if (IsStandard(value))
+ {
+ this._value = new Uri(value);
+ this.Type = DiscordUriType.Standard;
+ }
+ else
+ {
+ this._value = value;
+ this.Type = DiscordUriType.NonStandard;
+ }
}
- }
-
- /// <summary>
- /// Whether the uri is a standard uri
- /// </summary>
- /// <param name="value">Uri string</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool IsStandard(string value) => !value.StartsWith("attachment://");
- /// <summary>
- /// Returns a string representation of this DiscordUri.
- /// </summary>
- /// <returns>This DiscordUri, as a string.</returns>
- public override string ToString() => this._value.ToString();
-
- /// <summary>
- /// Converts this DiscordUri into a canonical representation of a <see cref="System.Uri"/> if it can be represented as
- /// such, throwing an exception otherwise.
- /// </summary>
- /// <returns>A canonical representation of this DiscordUri.</returns>
- /// <exception cref="System.UriFormatException">If <see cref="System.Type"/> is not <see cref="DiscordUriType.Standard"/>, as
- /// that would mean creating an invalid Uri, which would result in loss of data.</exception>
- public Uri ToUri()
- => this.Type == DiscordUriType.Standard
- ? this._value as Uri
- : throw new UriFormatException(
- $@"DiscordUri ""{this._value}"" would be invalid as a regular URI, please the {nameof(this.Type)} property first.");
+ /// <summary>
+ /// Whether the uri is a standard uri
+ /// </summary>
+ /// <param name="value">Uri string</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool IsStandard(string value) => !value.StartsWith("attachment://");
- /// <summary>
- /// Represents a uri json converter.
- /// </summary>
- internal sealed class DiscordUriJsonConverter : JsonConverter
- {
/// <summary>
- /// Writes the json.
+ /// Returns a string representation of this DiscordUri.
/// </summary>
- /// <param name="writer">The writer.</param>
- /// <param name="value">The value.</param>
- /// <param name="serializer">The serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => writer.WriteValue((value as DiscordUri)._value);
+ /// <returns>This DiscordUri, as a string.</returns>
+ public override string ToString() => this._value.ToString();
/// <summary>
- /// Reads the json.
+ /// Converts this DiscordUri into a canonical representation of a <see cref="System.Uri"/> if it can be represented as
+ /// such, throwing an exception otherwise.
/// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="objectType">The object type.</param>
- /// <param name="existingValue">The existing value.</param>
- /// <param name="serializer">The serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
- JsonSerializer serializer)
- {
- var val = reader.Value;
- return val == null
- ? null
- : val is not string s
- ? throw new JsonReaderException("DiscordUri value invalid format! This is a bug in DisCatSharp. " +
- $"Include the type in your bug report: [[{reader.TokenType}]]")
- : IsStandard(s)
- ? new DiscordUri(new Uri(s))
- : new DiscordUri(s);
- }
+ /// <returns>A canonical representation of this DiscordUri.</returns>
+ /// <exception cref="System.UriFormatException">If <see cref="System.Type"/> is not <see cref="DiscordUriType.Standard"/>, as
+ /// that would mean creating an invalid Uri, which would result in loss of data.</exception>
+ public Uri ToUri()
+ => this.Type == DiscordUriType.Standard
+ ? this._value as Uri
+ : throw new UriFormatException(
+ $@"DiscordUri ""{this._value}"" would be invalid as a regular URI, please the {nameof(this.Type)} property first.");
/// <summary>
- /// Whether it can be converted.
+ /// Represents a uri json converter.
/// </summary>
- /// <param name="objectType">The object type.</param>
- /// <returns>A bool.</returns>
- public override bool CanConvert(Type objectType) => objectType == typeof(DiscordUri);
+ internal sealed class DiscordUriJsonConverter : JsonConverter
+ {
+ /// <summary>
+ /// Writes the json.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="value">The value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => writer.WriteValue((value as DiscordUri)._value);
+
+ /// <summary>
+ /// Reads the json.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="objectType">The object type.</param>
+ /// <param name="existingValue">The existing value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
+ JsonSerializer serializer)
+ {
+ var val = reader.Value;
+ return val == null
+ ? null
+ : val is not string s
+ ? throw new JsonReaderException("DiscordUri value invalid format! This is a bug in DisCatSharp. " +
+ $"Include the type in your bug report: [[{reader.TokenType}]]")
+ : IsStandard(s)
+ ? new DiscordUri(new Uri(s))
+ : new DiscordUri(s);
+ }
+
+ /// <summary>
+ /// Whether it can be converted.
+ /// </summary>
+ /// <param name="objectType">The object type.</param>
+ /// <returns>A bool.</returns>
+ public override bool CanConvert(Type objectType) => objectType == typeof(DiscordUri);
+ }
}
-}
-/// <summary>
-/// Represents a uri type.
-/// </summary>
-public enum DiscordUriType : byte
-{
/// <summary>
- /// Represents a URI that conforms to RFC 3986, meaning it's stored internally as a <see cref="System.Uri"/> and will
- /// contain a trailing slash after the domain name.
+ /// Represents a uri type.
/// </summary>
- Standard,
+ public enum DiscordUriType : byte
+ {
+ /// <summary>
+ /// Represents a URI that conforms to RFC 3986, meaning it's stored internally as a <see cref="System.Uri"/> and will
+ /// contain a trailing slash after the domain name.
+ /// </summary>
+ Standard,
- /// <summary>
- /// Represents a URI that does not conform to RFC 3986, meaning it's stored internally as a plain string and
- /// should be treated as one.
- /// </summary>
- NonStandard
+ /// <summary>
+ /// Represents a URI that does not conform to RFC 3986, meaning it's stored internally as a plain string and
+ /// should be treated as one.
+ /// </summary>
+ NonStandard
+ }
}
diff --git a/DisCatSharp/Entities/Embed/DiscordEmbed.cs b/DisCatSharp/Entities/Embed/DiscordEmbed.cs
index 0fc1fd768..2c3b9cd25 100644
--- a/DisCatSharp/Entities/Embed/DiscordEmbed.cs
+++ b/DisCatSharp/Entities/Embed/DiscordEmbed.cs
@@ -1,126 +1,127 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord embed.
-/// </summary>
-public sealed class DiscordEmbed
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the embed's title.
- /// </summary>
- [JsonProperty("title", NullValueHandling = NullValueHandling.Ignore)]
- public string Title { get; internal set; }
-
- /// <summary>
- /// Gets the embed's type.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public string Type { get; internal set; }
-
- /// <summary>
- /// Gets the embed's description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
-
- /// <summary>
- /// Gets the embed's url.
- /// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public Uri Url { get; internal set; }
-
- /// <summary>
- /// Gets the embed's timestamp.
- /// </summary>
- [JsonProperty("timestamp", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset? Timestamp { get; internal set; }
-
- /// <summary>
- /// Gets the embed's color.
- /// </summary>
- [JsonIgnore]
- public Optional<DiscordColor> Color
- => this._colorLazy.Value;
-
- [JsonProperty("color", NullValueHandling = NullValueHandling.Include)]
- internal Optional<int> ColorInternal;
- [JsonIgnore]
- private readonly Lazy<Optional<DiscordColor>> _colorLazy;
-
- /// <summary>
- /// Gets the embed's footer.
- /// </summary>
- [JsonProperty("footer", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordEmbedFooter Footer { get; internal set; }
-
- /// <summary>
- /// Gets the embed's image.
- /// </summary>
- [JsonProperty("image", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordEmbedImage Image { get; internal set; }
-
- /// <summary>
- /// Gets the embed's thumbnail.
- /// </summary>
- [JsonProperty("thumbnail", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordEmbedThumbnail Thumbnail { get; internal set; }
-
- /// <summary>
- /// Gets the embed's video.
- /// </summary>
- [JsonProperty("video", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordEmbedVideo Video { get; internal set; }
-
- /// <summary>
- /// Gets the embed's provider.
- /// </summary>
- [JsonProperty("provider", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordEmbedProvider Provider { get; internal set; }
-
- /// <summary>
- /// Gets the embed's author.
- /// </summary>
- [JsonProperty("author", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordEmbedAuthor Author { get; internal set; }
-
- /// <summary>
- /// Gets the embed's fields.
- /// </summary>
- [JsonProperty("fields", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<DiscordEmbedField> Fields { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmbed"/> class.
+ /// Represents a discord embed.
/// </summary>
- internal DiscordEmbed()
+ public sealed class DiscordEmbed
{
- this._colorLazy = new Lazy<Optional<DiscordColor>>(() => this.ColorInternal.Map<DiscordColor>(c => c));
+ /// <summary>
+ /// Gets the embed's title.
+ /// </summary>
+ [JsonProperty("title", NullValueHandling = NullValueHandling.Ignore)]
+ public string Title { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's type.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public string Type { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's url.
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public Uri Url { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's timestamp.
+ /// </summary>
+ [JsonProperty("timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset? Timestamp { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's color.
+ /// </summary>
+ [JsonIgnore]
+ public Optional<DiscordColor> Color
+ => this._colorLazy.Value;
+
+ [JsonProperty("color", NullValueHandling = NullValueHandling.Include)]
+ internal Optional<int> ColorInternal;
+ [JsonIgnore]
+ private readonly Lazy<Optional<DiscordColor>> _colorLazy;
+
+ /// <summary>
+ /// Gets the embed's footer.
+ /// </summary>
+ [JsonProperty("footer", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordEmbedFooter Footer { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's image.
+ /// </summary>
+ [JsonProperty("image", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordEmbedImage Image { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's thumbnail.
+ /// </summary>
+ [JsonProperty("thumbnail", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordEmbedThumbnail Thumbnail { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's video.
+ /// </summary>
+ [JsonProperty("video", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordEmbedVideo Video { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's provider.
+ /// </summary>
+ [JsonProperty("provider", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordEmbedProvider Provider { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's author.
+ /// </summary>
+ [JsonProperty("author", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordEmbedAuthor Author { get; internal set; }
+
+ /// <summary>
+ /// Gets the embed's fields.
+ /// </summary>
+ [JsonProperty("fields", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<DiscordEmbedField> Fields { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmbed"/> class.
+ /// </summary>
+ internal DiscordEmbed()
+ {
+ this._colorLazy = new Lazy<Optional<DiscordColor>>(() => this.ColorInternal.Map<DiscordColor>(c => c));
+ }
}
}
diff --git a/DisCatSharp/Entities/Embed/DiscordEmbedAuthor.cs b/DisCatSharp/Entities/Embed/DiscordEmbedAuthor.cs
index 40bf28e82..06a84fdc6 100644
--- a/DisCatSharp/Entities/Embed/DiscordEmbedAuthor.cs
+++ b/DisCatSharp/Entities/Embed/DiscordEmbedAuthor.cs
@@ -1,65 +1,66 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Gets the author of a discord embed.
-/// </summary>
-public sealed class DiscordEmbedAuthor
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of the author.
+ /// Gets the author of a discord embed.
/// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; set; }
+ public sealed class DiscordEmbedAuthor
+ {
+ /// <summary>
+ /// Gets the name of the author.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
- /// <summary>
- /// Gets the url of the author.
- /// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public Uri Url { get; set; }
+ /// <summary>
+ /// Gets the url of the author.
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public Uri Url { get; set; }
- /// <summary>
- /// Gets the url of the author's icon.
- /// </summary>
- [JsonProperty("icon_url", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUri IconUrl { get; set; }
+ /// <summary>
+ /// Gets the url of the author's icon.
+ /// </summary>
+ [JsonProperty("icon_url", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUri IconUrl { get; set; }
- /// <summary>
- /// Gets the proxied url of the author's icon.
- /// </summary>
- [JsonProperty("proxy_icon_url", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUri ProxyIconUrl { get; internal set; }
+ /// <summary>
+ /// Gets the proxied url of the author's icon.
+ /// </summary>
+ [JsonProperty("proxy_icon_url", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUri ProxyIconUrl { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmbedAuthor"/> class.
- /// </summary>
- internal DiscordEmbedAuthor()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmbedAuthor"/> class.
+ /// </summary>
+ internal DiscordEmbedAuthor()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Embed/DiscordEmbedBuilder.cs b/DisCatSharp/Entities/Embed/DiscordEmbedBuilder.cs
index 6227e8c15..10150743c 100644
--- a/DisCatSharp/Entities/Embed/DiscordEmbedBuilder.cs
+++ b/DisCatSharp/Entities/Embed/DiscordEmbedBuilder.cs
@@ -1,652 +1,653 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using DisCatSharp.Net;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Constructs embeds.
-/// </summary>
-public sealed class DiscordEmbedBuilder
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets or sets the embed's title.
+ /// Constructs embeds.
/// </summary>
- public string Title
+ public sealed class DiscordEmbedBuilder
{
- get => this._title;
- set
+ /// <summary>
+ /// Gets or sets the embed's title.
+ /// </summary>
+ public string Title
{
- if (value != null && value.Length > 256)
- throw new ArgumentException("Title length cannot exceed 256 characters.", nameof(value));
- this._title = value;
+ get => this._title;
+ set
+ {
+ if (value != null && value.Length > 256)
+ throw new ArgumentException("Title length cannot exceed 256 characters.", nameof(value));
+ this._title = value;
+ }
}
- }
- private string _title;
+ private string _title;
- /// <summary>
- /// Gets or sets the embed's description.
- /// </summary>
- public string Description
- {
- get => this._description;
- set
+ /// <summary>
+ /// Gets or sets the embed's description.
+ /// </summary>
+ public string Description
{
- if (value != null && value.Length > 4096)
- throw new ArgumentException("Description length cannot exceed 4096 characters.", nameof(value));
- this._description = value;
+ get => this._description;
+ set
+ {
+ if (value != null && value.Length > 4096)
+ throw new ArgumentException("Description length cannot exceed 4096 characters.", nameof(value));
+ this._description = value;
+ }
}
- }
- private string _description;
-
- /// <summary>
- /// Gets or sets the url for the embed's title.
- /// </summary>
- public string Url
- {
- get => this._url?.ToString();
- set => this._url = string.IsNullOrEmpty(value) ? null : new Uri(value);
- }
- private Uri _url;
-
- /// <summary>
- /// Gets or sets the embed's color.
- /// </summary>
- public Optional<DiscordColor> Color { get; set; }
-
- /// <summary>
- /// Gets or sets the embed's timestamp.
- /// </summary>
- public DateTimeOffset? Timestamp { get; set; }
-
- /// <summary>
- /// Gets or sets the embed's image url.
- /// </summary>
- public string ImageUrl
- {
- get => this._imageUri?.ToString();
- set => this._imageUri = string.IsNullOrEmpty(value) ? null : new DiscordUri(value);
- }
- private DiscordUri _imageUri;
-
- /// <summary>
- /// Gets or sets the embed's author.
- /// </summary>
- public EmbedAuthor Author { get; set; }
-
- /// <summary>
- /// Gets or sets the embed's footer.
- /// </summary>
- public EmbedFooter Footer { get; set; }
-
- /// <summary>
- /// Gets or sets the embed's thumbnail.
- /// </summary>
- public EmbedThumbnail Thumbnail { get; set; }
-
- /// <summary>
- /// Gets the embed's fields.
- /// </summary>
- public IReadOnlyList<DiscordEmbedField> Fields { get; }
- private readonly List<DiscordEmbedField> _fields = new();
+ private string _description;
- /// <summary>
- /// Constructs a new empty embed builder.
- /// </summary>
- public DiscordEmbedBuilder()
- {
- this.Fields = new ReadOnlyCollection<DiscordEmbedField>(this._fields);
- }
+ /// <summary>
+ /// Gets or sets the url for the embed's title.
+ /// </summary>
+ public string Url
+ {
+ get => this._url?.ToString();
+ set => this._url = string.IsNullOrEmpty(value) ? null : new Uri(value);
+ }
+ private Uri _url;
- /// <summary>
- /// Constructs a new embed builder using another embed as prototype.
- /// </summary>
- /// <param name="original">Embed to use as prototype.</param>
- public DiscordEmbedBuilder(DiscordEmbed original)
- : this()
- {
- this.Title = original.Title;
- this.Description = original.Description;
- this.Url = original.Url?.ToString();
- this.ImageUrl = original.Image?.Url?.ToString();
- this.Color = original.Color;
- this.Timestamp = original.Timestamp;
-
- if (original.Thumbnail != null)
- this.Thumbnail = new EmbedThumbnail
- {
- Url = original.Thumbnail.Url?.ToString(),
- Height = original.Thumbnail.Height,
- Width = original.Thumbnail.Width
- };
+ /// <summary>
+ /// Gets or sets the embed's color.
+ /// </summary>
+ public Optional<DiscordColor> Color { get; set; }
- if (original.Author != null)
- this.Author = new EmbedAuthor
- {
- IconUrl = original.Author.IconUrl?.ToString(),
- Name = original.Author.Name,
- Url = original.Author.Url?.ToString()
- };
+ /// <summary>
+ /// Gets or sets the embed's timestamp.
+ /// </summary>
+ public DateTimeOffset? Timestamp { get; set; }
- if (original.Footer != null)
- this.Footer = new EmbedFooter
- {
- IconUrl = original.Footer.IconUrl?.ToString(),
- Text = original.Footer.Text
- };
+ /// <summary>
+ /// Gets or sets the embed's image url.
+ /// </summary>
+ public string ImageUrl
+ {
+ get => this._imageUri?.ToString();
+ set => this._imageUri = string.IsNullOrEmpty(value) ? null : new DiscordUri(value);
+ }
+ private DiscordUri _imageUri;
- if (original.Fields?.Any() == true)
- this._fields.AddRange(original.Fields);
+ /// <summary>
+ /// Gets or sets the embed's author.
+ /// </summary>
+ public EmbedAuthor Author { get; set; }
- while (this._fields.Count > 25)
- this._fields.RemoveAt(this._fields.Count - 1);
- }
+ /// <summary>
+ /// Gets or sets the embed's footer.
+ /// </summary>
+ public EmbedFooter Footer { get; set; }
- /// <summary>
- /// Sets the embed's title.
- /// </summary>
- /// <param name="title">Title to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithTitle(string title)
- {
- this.Title = title;
- return this;
- }
+ /// <summary>
+ /// Gets or sets the embed's thumbnail.
+ /// </summary>
+ public EmbedThumbnail Thumbnail { get; set; }
- /// <summary>
- /// Sets the embed's description.
- /// </summary>
- /// <param name="description">Description to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithDescription(string description)
- {
- this.Description = description;
- return this;
- }
+ /// <summary>
+ /// Gets the embed's fields.
+ /// </summary>
+ public IReadOnlyList<DiscordEmbedField> Fields { get; }
+ private readonly List<DiscordEmbedField> _fields = new();
- /// <summary>
- /// Sets the embed's title url.
- /// </summary>
- /// <param name="url">Title url to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithUrl(string url)
- {
- this.Url = url;
- return this;
- }
+ /// <summary>
+ /// Constructs a new empty embed builder.
+ /// </summary>
+ public DiscordEmbedBuilder()
+ {
+ this.Fields = new ReadOnlyCollection<DiscordEmbedField>(this._fields);
+ }
- /// <summary>
- /// Sets the embed's title url.
- /// </summary>
- /// <param name="url">Title url to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithUrl(Uri url)
- {
- this._url = url;
- return this;
- }
+ /// <summary>
+ /// Constructs a new embed builder using another embed as prototype.
+ /// </summary>
+ /// <param name="original">Embed to use as prototype.</param>
+ public DiscordEmbedBuilder(DiscordEmbed original)
+ : this()
+ {
+ this.Title = original.Title;
+ this.Description = original.Description;
+ this.Url = original.Url?.ToString();
+ this.ImageUrl = original.Image?.Url?.ToString();
+ this.Color = original.Color;
+ this.Timestamp = original.Timestamp;
+
+ if (original.Thumbnail != null)
+ this.Thumbnail = new EmbedThumbnail
+ {
+ Url = original.Thumbnail.Url?.ToString(),
+ Height = original.Thumbnail.Height,
+ Width = original.Thumbnail.Width
+ };
+
+ if (original.Author != null)
+ this.Author = new EmbedAuthor
+ {
+ IconUrl = original.Author.IconUrl?.ToString(),
+ Name = original.Author.Name,
+ Url = original.Author.Url?.ToString()
+ };
+
+ if (original.Footer != null)
+ this.Footer = new EmbedFooter
+ {
+ IconUrl = original.Footer.IconUrl?.ToString(),
+ Text = original.Footer.Text
+ };
+
+ if (original.Fields?.Any() == true)
+ this._fields.AddRange(original.Fields);
+
+ while (this._fields.Count > 25)
+ this._fields.RemoveAt(this._fields.Count - 1);
+ }
- /// <summary>
- /// Sets the embed's color.
- /// </summary>
- /// <param name="color">Embed color to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithColor(DiscordColor color)
- {
- this.Color = color;
- return this;
- }
+ /// <summary>
+ /// Sets the embed's title.
+ /// </summary>
+ /// <param name="title">Title to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithTitle(string title)
+ {
+ this.Title = title;
+ return this;
+ }
- /// <summary>
- /// Sets the embed's timestamp.
- /// </summary>
- /// <param name="timestamp">Timestamp to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithTimestamp(DateTimeOffset? timestamp)
- {
- this.Timestamp = timestamp;
- return this;
- }
+ /// <summary>
+ /// Sets the embed's description.
+ /// </summary>
+ /// <param name="description">Description to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithDescription(string description)
+ {
+ this.Description = description;
+ return this;
+ }
- /// <summary>
- /// Sets the embed's timestamp.
- /// </summary>
- /// <param name="timestamp">Timestamp to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithTimestamp(DateTime? timestamp)
- {
- this.Timestamp = timestamp == null ? null : (DateTimeOffset?)new DateTimeOffset(timestamp.Value);
- return this;
- }
+ /// <summary>
+ /// Sets the embed's title url.
+ /// </summary>
+ /// <param name="url">Title url to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithUrl(string url)
+ {
+ this.Url = url;
+ return this;
+ }
- /// <summary>
- /// Sets the embed's timestamp based on a snowflake.
- /// </summary>
- /// <param name="snowflake">Snowflake to calculate timestamp from.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithTimestamp(ulong snowflake)
- {
- this.Timestamp = new DateTimeOffset(2015, 1, 1, 0, 0, 0, TimeSpan.Zero).AddMilliseconds(snowflake >> 22);
- return this;
- }
+ /// <summary>
+ /// Sets the embed's title url.
+ /// </summary>
+ /// <param name="url">Title url to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithUrl(Uri url)
+ {
+ this._url = url;
+ return this;
+ }
- /// <summary>
- /// Sets the embed's image url.
- /// </summary>
- /// <param name="url">Image url to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithImageUrl(string url)
- {
- this.ImageUrl = url;
- return this;
- }
+ /// <summary>
+ /// Sets the embed's color.
+ /// </summary>
+ /// <param name="color">Embed color to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithColor(DiscordColor color)
+ {
+ this.Color = color;
+ return this;
+ }
- /// <summary>
- /// Sets the embed's image url.
- /// </summary>
- /// <param name="url">Image url to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithImageUrl(Uri url)
- {
- this._imageUri = new DiscordUri(url);
- return this;
- }
+ /// <summary>
+ /// Sets the embed's timestamp.
+ /// </summary>
+ /// <param name="timestamp">Timestamp to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithTimestamp(DateTimeOffset? timestamp)
+ {
+ this.Timestamp = timestamp;
+ return this;
+ }
- /// <summary>
- /// Sets the embed's thumbnail.
- /// </summary>
- /// <param name="url">Thumbnail url to set.</param>
- /// <param name="height">The height of the thumbnail to set.</param>
- /// <param name="width">The width of the thumbnail to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithThumbnail(string url, int height = 0, int width = 0)
- {
- this.Thumbnail = new EmbedThumbnail
+ /// <summary>
+ /// Sets the embed's timestamp.
+ /// </summary>
+ /// <param name="timestamp">Timestamp to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithTimestamp(DateTime? timestamp)
{
- Url = url,
- Height = height,
- Width = width
- };
+ this.Timestamp = timestamp == null ? null : (DateTimeOffset?)new DateTimeOffset(timestamp.Value);
+ return this;
+ }
- return this;
- }
+ /// <summary>
+ /// Sets the embed's timestamp based on a snowflake.
+ /// </summary>
+ /// <param name="snowflake">Snowflake to calculate timestamp from.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithTimestamp(ulong snowflake)
+ {
+ this.Timestamp = new DateTimeOffset(2015, 1, 1, 0, 0, 0, TimeSpan.Zero).AddMilliseconds(snowflake >> 22);
+ return this;
+ }
- /// <summary>
- /// Sets the embed's thumbnail.
- /// </summary>
- /// <param name="url">Thumbnail url to set.</param>
- /// <param name="height">The height of the thumbnail to set.</param>
- /// <param name="width">The width of the thumbnail to set.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithThumbnail(Uri url, int height = 0, int width = 0)
- {
- this.Thumbnail = new EmbedThumbnail
+ /// <summary>
+ /// Sets the embed's image url.
+ /// </summary>
+ /// <param name="url">Image url to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithImageUrl(string url)
{
- Uri = new DiscordUri(url),
- Height = height,
- Width = width
- };
+ this.ImageUrl = url;
+ return this;
+ }
- return this;
- }
+ /// <summary>
+ /// Sets the embed's image url.
+ /// </summary>
+ /// <param name="url">Image url to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithImageUrl(Uri url)
+ {
+ this._imageUri = new DiscordUri(url);
+ return this;
+ }
- /// <summary>
- /// Sets the embed's author.
- /// </summary>
- /// <param name="name">Author's name.</param>
- /// <param name="url">Author's url.</param>
- /// <param name="iconUrl">Author icon's url.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithAuthor(string name = null, string url = null, string iconUrl = null)
- {
- if (!string.IsNullOrEmpty(name) && name.Length > 256)
- throw new NotSupportedException("Embed author name can not exceed 256 chars. See https://discord.com/developers/docs/resources/channel#embed-limits.");
- this.Author = string.IsNullOrEmpty(name) && string.IsNullOrEmpty(url) && string.IsNullOrEmpty(iconUrl)
- ? null
- : new EmbedAuthor
+ /// <summary>
+ /// Sets the embed's thumbnail.
+ /// </summary>
+ /// <param name="url">Thumbnail url to set.</param>
+ /// <param name="height">The height of the thumbnail to set.</param>
+ /// <param name="width">The width of the thumbnail to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithThumbnail(string url, int height = 0, int width = 0)
+ {
+ this.Thumbnail = new EmbedThumbnail
{
- Name = name,
Url = url,
- IconUrl = iconUrl
+ Height = height,
+ Width = width
};
- return this;
- }
- /// <summary>
- /// Sets the embed's footer.
- /// </summary>
- /// <param name="text">Footer's text.</param>
- /// <param name="iconUrl">Footer icon's url.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder WithFooter(string text = null, string iconUrl = null)
- {
- if (text != null && text.Length > 2048)
- throw new ArgumentException("Footer text length cannot exceed 2048 characters.", nameof(text));
+ return this;
+ }
- this.Footer = string.IsNullOrEmpty(text) && string.IsNullOrEmpty(iconUrl)
- ? null
- : new EmbedFooter
+ /// <summary>
+ /// Sets the embed's thumbnail.
+ /// </summary>
+ /// <param name="url">Thumbnail url to set.</param>
+ /// <param name="height">The height of the thumbnail to set.</param>
+ /// <param name="width">The width of the thumbnail to set.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithThumbnail(Uri url, int height = 0, int width = 0)
+ {
+ this.Thumbnail = new EmbedThumbnail
{
- Text = text,
- IconUrl = iconUrl
+ Uri = new DiscordUri(url),
+ Height = height,
+ Width = width
};
- return this;
- }
-
- /// <summary>
- /// Adds a field to this embed.
- /// </summary>
- /// <param name="name">Name of the field to add.</param>
- /// <param name="value">Value of the field to add.</param>
- /// <param name="inline">Whether the field is to be inline or not.</param>
- /// <returns>This embed builder.</returns>
- [Obsolete("DiscordEmbedFields should be constructed manually.")]
- public DiscordEmbedBuilder AddField(string name, string value, bool inline = false)
- => this.AddField(new DiscordEmbedField(name, value, inline));
- /// <summary>
- /// Adds a field to this embed.
- /// </summary>
- /// <param name="field">The field to add.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder AddField(DiscordEmbedField field)
- {
- if (this._fields.Count >= 25)
- throw new InvalidOperationException("Cannot add more than 25 fields.");
-
- this._fields.Add(field);
-
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Adds multiple fields to this embed.
- /// </summary>
- /// <param name="fields">The fields to add.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder AddFields(params DiscordEmbedField[] fields)
- => this.AddFields((IEnumerable<DiscordEmbedField>)fields);
+ /// <summary>
+ /// Sets the embed's author.
+ /// </summary>
+ /// <param name="name">Author's name.</param>
+ /// <param name="url">Author's url.</param>
+ /// <param name="iconUrl">Author icon's url.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithAuthor(string name = null, string url = null, string iconUrl = null)
+ {
+ if (!string.IsNullOrEmpty(name) && name.Length > 256)
+ throw new NotSupportedException("Embed author name can not exceed 256 chars. See https://discord.com/developers/docs/resources/channel#embed-limits.");
+ this.Author = string.IsNullOrEmpty(name) && string.IsNullOrEmpty(url) && string.IsNullOrEmpty(iconUrl)
+ ? null
+ : new EmbedAuthor
+ {
+ Name = name,
+ Url = url,
+ IconUrl = iconUrl
+ };
+ return this;
+ }
- /// <summary>
- /// Adds multiple fields to this embed.
- /// </summary>
- /// <param name="fields">The fields to add.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder AddFields(IEnumerable<DiscordEmbedField> fields)
- => fields.Aggregate(this, (x, y) => x.AddField(y));
+ /// <summary>
+ /// Sets the embed's footer.
+ /// </summary>
+ /// <param name="text">Footer's text.</param>
+ /// <param name="iconUrl">Footer icon's url.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder WithFooter(string text = null, string iconUrl = null)
+ {
+ if (text != null && text.Length > 2048)
+ throw new ArgumentException("Footer text length cannot exceed 2048 characters.", nameof(text));
+
+ this.Footer = string.IsNullOrEmpty(text) && string.IsNullOrEmpty(iconUrl)
+ ? null
+ : new EmbedFooter
+ {
+ Text = text,
+ IconUrl = iconUrl
+ };
+ return this;
+ }
- /// <summary>
- /// Removes a field from this embed, if it is part of it.
- /// </summary>
- /// <param name="field">The field to remove.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder RemoveField(DiscordEmbedField field)
- {
- this._fields.Remove(field);
- return this;
- }
+ /// <summary>
+ /// Adds a field to this embed.
+ /// </summary>
+ /// <param name="name">Name of the field to add.</param>
+ /// <param name="value">Value of the field to add.</param>
+ /// <param name="inline">Whether the field is to be inline or not.</param>
+ /// <returns>This embed builder.</returns>
+ [Obsolete("DiscordEmbedFields should be constructed manually.")]
+ public DiscordEmbedBuilder AddField(string name, string value, bool inline = false)
+ => this.AddField(new DiscordEmbedField(name, value, inline));
- /// <summary>
- /// Removes multiple fields from this embed, if they are part of it.
- /// </summary>
- /// <param name="fields">The fields to remove.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder RemoveFields(params DiscordEmbedField[] fields)
- {
- this.RemoveFields((IEnumerable<DiscordEmbedField>)fields);
- return this;
- }
+ /// <summary>
+ /// Adds a field to this embed.
+ /// </summary>
+ /// <param name="field">The field to add.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder AddField(DiscordEmbedField field)
+ {
+ if (this._fields.Count >= 25)
+ throw new InvalidOperationException("Cannot add more than 25 fields.");
- /// <summary>
- /// Removes multiple fields from this embed, if they are part of it.
- /// </summary>
- /// <param name="fields">The fields to remove.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbed RemoveFields(IEnumerable<DiscordEmbedField> fields)
- {
- this._fields.RemoveAll(x => fields.Contains(x));
- return this;
- }
+ this._fields.Add(field);
- /// <summary>
- /// Removes a field of the specified index from this embed.
- /// </summary>
- /// <param name="index">Index of the field to remove.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder RemoveFieldAt(int index)
- {
- this._fields.RemoveAt(index);
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Removes fields of the specified range from this embed.
- /// </summary>
- /// <param name="index">Index of the first field to remove.</param>
- /// <param name="count">Number of fields to remove.</param>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder RemoveFieldRange(int index, int count)
- {
- this._fields.RemoveRange(index, count);
- return this;
- }
+ /// <summary>
+ /// Adds multiple fields to this embed.
+ /// </summary>
+ /// <param name="fields">The fields to add.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder AddFields(params DiscordEmbedField[] fields)
+ => this.AddFields((IEnumerable<DiscordEmbedField>)fields);
- /// <summary>
- /// Removes all fields from this embed.
- /// </summary>
- /// <returns>This embed builder.</returns>
- public DiscordEmbedBuilder ClearFields()
- {
- this._fields.Clear();
- return this;
- }
+ /// <summary>
+ /// Adds multiple fields to this embed.
+ /// </summary>
+ /// <param name="fields">The fields to add.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder AddFields(IEnumerable<DiscordEmbedField> fields)
+ => fields.Aggregate(this, (x, y) => x.AddField(y));
- /// <summary>
- /// Constructs a new embed from data supplied to this builder.
- /// </summary>
- /// <returns>New discord embed.</returns>
- public DiscordEmbed Build()
- {
- var embed = new DiscordEmbed
+ /// <summary>
+ /// Removes a field from this embed, if it is part of it.
+ /// </summary>
+ /// <param name="field">The field to remove.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder RemoveField(DiscordEmbedField field)
{
- Title = this._title,
- Description = this._description,
- Url = this._url,
- ColorInternal = this.Color.Map(e => e.Value),
- Timestamp = this.Timestamp
- };
-
- if (this.Footer != null)
- embed.Footer = new DiscordEmbedFooter
- {
- Text = this.Footer.Text,
- IconUrl = this.Footer.IconUri
- };
-
- if (this.Author != null)
- embed.Author = new DiscordEmbedAuthor
- {
- Name = this.Author.Name,
- Url = this.Author.Uri,
- IconUrl = this.Author.IconUri
- };
-
- if (this._imageUri != null)
- embed.Image = new DiscordEmbedImage { Url = this._imageUri };
- if (this.Thumbnail != null)
- embed.Thumbnail = new DiscordEmbedThumbnail
- {
- Url = this.Thumbnail.Uri,
- Height = this.Thumbnail.Height,
- Width = this.Thumbnail.Width
- };
-
- embed.Fields = new ReadOnlyCollection<DiscordEmbedField>(new List<DiscordEmbedField>(this._fields)); // copy the list, don't wrap it, prevents mutation
+ this._fields.Remove(field);
+ return this;
+ }
- var charCount = 0;
- if (embed.Fields.Any())
+ /// <summary>
+ /// Removes multiple fields from this embed, if they are part of it.
+ /// </summary>
+ /// <param name="fields">The fields to remove.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder RemoveFields(params DiscordEmbedField[] fields)
{
- foreach (var field in embed.Fields)
- {
- charCount += field.Name.Length;
- charCount += field.Value.Length;
- }
+ this.RemoveFields((IEnumerable<DiscordEmbedField>)fields);
+ return this;
}
- if (embed.Author != null && !string.IsNullOrEmpty(embed.Author.Name))
- charCount += embed.Author.Name.Length;
-
- if (embed.Footer != null && !string.IsNullOrEmpty(embed.Footer.Text))
- charCount += embed.Footer.Text.Length;
-
- if (!string.IsNullOrEmpty(embed.Title))
- charCount += embed.Title.Length;
-
- if (!string.IsNullOrEmpty(embed.Description))
- charCount += embed.Description.Length;
-
- return charCount >= 6000
- ? throw new NotSupportedException("Total char count can not exceed 6000 chars. See https://discord.com/developers/docs/resources/channel#embed-limits.")
- : embed;
- }
-
- /// <summary>
- /// Implicitly converts this builder to an embed.
- /// </summary>
- /// <param name="builder">Builder to convert.</param>
- public static implicit operator DiscordEmbed(DiscordEmbedBuilder builder)
- => builder?.Build();
+ /// <summary>
+ /// Removes multiple fields from this embed, if they are part of it.
+ /// </summary>
+ /// <param name="fields">The fields to remove.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbed RemoveFields(IEnumerable<DiscordEmbedField> fields)
+ {
+ this._fields.RemoveAll(x => fields.Contains(x));
+ return this;
+ }
- /// <summary>
- /// Represents an embed author.
- /// </summary>
- public class EmbedAuthor
- {
/// <summary>
- /// Gets or sets the name of the author.
+ /// Removes a field of the specified index from this embed.
/// </summary>
- public string Name
+ /// <param name="index">Index of the field to remove.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder RemoveFieldAt(int index)
{
- get => this._name;
- set
- {
- if (value != null && value.Length > 256)
- throw new ArgumentException("Author name length cannot exceed 256 characters.", nameof(value));
- this._name = value;
- }
+ this._fields.RemoveAt(index);
+ return this;
}
- private string _name;
/// <summary>
- /// Gets or sets the Url to which the author's link leads.
+ /// Removes fields of the specified range from this embed.
/// </summary>
- public string Url
+ /// <param name="index">Index of the first field to remove.</param>
+ /// <param name="count">Number of fields to remove.</param>
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder RemoveFieldRange(int index, int count)
{
- get => this.Uri?.ToString();
- set => this.Uri = string.IsNullOrEmpty(value) ? null : new Uri(value);
+ this._fields.RemoveRange(index, count);
+ return this;
}
- internal Uri Uri;
/// <summary>
- /// Gets or sets the Author's icon url.
+ /// Removes all fields from this embed.
/// </summary>
- public string IconUrl
+ /// <returns>This embed builder.</returns>
+ public DiscordEmbedBuilder ClearFields()
{
- get => this.IconUri?.ToString();
- set => this.IconUri = string.IsNullOrEmpty(value) ? null : new DiscordUri(value);
+ this._fields.Clear();
+ return this;
}
- internal DiscordUri IconUri;
- }
- /// <summary>
- /// Represents an embed footer.
- /// </summary>
- public class EmbedFooter
- {
/// <summary>
- /// Gets or sets the text of the footer.
+ /// Constructs a new embed from data supplied to this builder.
/// </summary>
- public string Text
+ /// <returns>New discord embed.</returns>
+ public DiscordEmbed Build()
{
- get => this._text;
- set
+ var embed = new DiscordEmbed
{
- if (value != null && value.Length > 2048)
- throw new ArgumentException("Footer text length cannot exceed 2048 characters.", nameof(value));
- this._text = value;
+ Title = this._title,
+ Description = this._description,
+ Url = this._url,
+ ColorInternal = this.Color.Map(e => e.Value),
+ Timestamp = this.Timestamp
+ };
+
+ if (this.Footer != null)
+ embed.Footer = new DiscordEmbedFooter
+ {
+ Text = this.Footer.Text,
+ IconUrl = this.Footer.IconUri
+ };
+
+ if (this.Author != null)
+ embed.Author = new DiscordEmbedAuthor
+ {
+ Name = this.Author.Name,
+ Url = this.Author.Uri,
+ IconUrl = this.Author.IconUri
+ };
+
+ if (this._imageUri != null)
+ embed.Image = new DiscordEmbedImage { Url = this._imageUri };
+ if (this.Thumbnail != null)
+ embed.Thumbnail = new DiscordEmbedThumbnail
+ {
+ Url = this.Thumbnail.Uri,
+ Height = this.Thumbnail.Height,
+ Width = this.Thumbnail.Width
+ };
+
+ embed.Fields = new ReadOnlyCollection<DiscordEmbedField>(new List<DiscordEmbedField>(this._fields)); // copy the list, don't wrap it, prevents mutation
+
+ var charCount = 0;
+ if (embed.Fields.Any())
+ {
+ foreach (var field in embed.Fields)
+ {
+ charCount += field.Name.Length;
+ charCount += field.Value.Length;
+ }
}
+
+ if (embed.Author != null && !string.IsNullOrEmpty(embed.Author.Name))
+ charCount += embed.Author.Name.Length;
+
+ if (embed.Footer != null && !string.IsNullOrEmpty(embed.Footer.Text))
+ charCount += embed.Footer.Text.Length;
+
+ if (!string.IsNullOrEmpty(embed.Title))
+ charCount += embed.Title.Length;
+
+ if (!string.IsNullOrEmpty(embed.Description))
+ charCount += embed.Description.Length;
+
+ return charCount >= 6000
+ ? throw new NotSupportedException("Total char count can not exceed 6000 chars. See https://discord.com/developers/docs/resources/channel#embed-limits.")
+ : embed;
}
- private string _text;
/// <summary>
- /// Gets or sets the Url
+ /// Implicitly converts this builder to an embed.
/// </summary>
- public string IconUrl
- {
- get => this.IconUri?.ToString();
- set => this.IconUri = string.IsNullOrEmpty(value) ? null : new DiscordUri(value);
- }
- internal DiscordUri IconUri;
- }
+ /// <param name="builder">Builder to convert.</param>
+ public static implicit operator DiscordEmbed(DiscordEmbedBuilder builder)
+ => builder?.Build();
- /// <summary>
- /// Represents an embed thumbnail.
- /// </summary>
- public class EmbedThumbnail
- {
/// <summary>
- /// Gets or sets the thumbnail's image url.
+ /// Represents an embed author.
/// </summary>
- public string Url
+ public class EmbedAuthor
{
- get => this.Uri?.ToString();
- set => this.Uri = string.IsNullOrEmpty(value) ? null : new DiscordUri(value);
+ /// <summary>
+ /// Gets or sets the name of the author.
+ /// </summary>
+ public string Name
+ {
+ get => this._name;
+ set
+ {
+ if (value != null && value.Length > 256)
+ throw new ArgumentException("Author name length cannot exceed 256 characters.", nameof(value));
+ this._name = value;
+ }
+ }
+ private string _name;
+
+ /// <summary>
+ /// Gets or sets the Url to which the author's link leads.
+ /// </summary>
+ public string Url
+ {
+ get => this.Uri?.ToString();
+ set => this.Uri = string.IsNullOrEmpty(value) ? null : new Uri(value);
+ }
+ internal Uri Uri;
+
+ /// <summary>
+ /// Gets or sets the Author's icon url.
+ /// </summary>
+ public string IconUrl
+ {
+ get => this.IconUri?.ToString();
+ set => this.IconUri = string.IsNullOrEmpty(value) ? null : new DiscordUri(value);
+ }
+ internal DiscordUri IconUri;
}
- internal DiscordUri Uri;
/// <summary>
- /// Gets or sets the thumbnail's height.
+ /// Represents an embed footer.
/// </summary>
- public int Height
+ public class EmbedFooter
{
- get => this._height;
- set => this._height = value >= 0 ? value : 0;
+ /// <summary>
+ /// Gets or sets the text of the footer.
+ /// </summary>
+ public string Text
+ {
+ get => this._text;
+ set
+ {
+ if (value != null && value.Length > 2048)
+ throw new ArgumentException("Footer text length cannot exceed 2048 characters.", nameof(value));
+ this._text = value;
+ }
+ }
+ private string _text;
+
+ /// <summary>
+ /// Gets or sets the Url
+ /// </summary>
+ public string IconUrl
+ {
+ get => this.IconUri?.ToString();
+ set => this.IconUri = string.IsNullOrEmpty(value) ? null : new DiscordUri(value);
+ }
+ internal DiscordUri IconUri;
}
- private int _height;
/// <summary>
- /// Gets or sets the thumbnail's width.
+ /// Represents an embed thumbnail.
/// </summary>
- public int Width
+ public class EmbedThumbnail
{
- get => this._width;
- set => this._width = value >= 0 ? value : 0;
+ /// <summary>
+ /// Gets or sets the thumbnail's image url.
+ /// </summary>
+ public string Url
+ {
+ get => this.Uri?.ToString();
+ set => this.Uri = string.IsNullOrEmpty(value) ? null : new DiscordUri(value);
+ }
+ internal DiscordUri Uri;
+
+ /// <summary>
+ /// Gets or sets the thumbnail's height.
+ /// </summary>
+ public int Height
+ {
+ get => this._height;
+ set => this._height = value >= 0 ? value : 0;
+ }
+ private int _height;
+
+ /// <summary>
+ /// Gets or sets the thumbnail's width.
+ /// </summary>
+ public int Width
+ {
+ get => this._width;
+ set => this._width = value >= 0 ? value : 0;
+ }
+ private int _width;
}
- private int _width;
}
}
diff --git a/DisCatSharp/Entities/Embed/DiscordEmbedField.cs b/DisCatSharp/Entities/Embed/DiscordEmbedField.cs
index 57f4d4957..c097603d5 100644
--- a/DisCatSharp/Entities/Embed/DiscordEmbedField.cs
+++ b/DisCatSharp/Entities/Embed/DiscordEmbedField.cs
@@ -1,100 +1,101 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a field inside a discord embed.
-/// </summary>
-public sealed class DiscordEmbedField
+namespace DisCatSharp.Entities
{
- private string _name;
-
/// <summary>
- /// The name of the field.
- /// Must be non-null, non-empty and &lt;= 256 characters.
+ /// Represents a field inside a discord embed.
/// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get => this._name;
- set
- {
- if (string.IsNullOrWhiteSpace(value))
+ public sealed class DiscordEmbedField
+ {
+ private string _name;
+
+ /// <summary>
+ /// The name of the field.
+ /// Must be non-null, non-empty and &lt;= 256 characters.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get => this._name;
+ set
{
- if (value == null)
- throw new ArgumentNullException(nameof(value));
- throw new ArgumentException("Name cannot be empty or whitespace.", nameof(value));
- }
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+ throw new ArgumentException("Name cannot be empty or whitespace.", nameof(value));
+ }
- if (value.Length > 256)
- throw new ArgumentException("Embed field name length cannot exceed 256 characters.");
+ if (value.Length > 256)
+ throw new ArgumentException("Embed field name length cannot exceed 256 characters.");
- this._name = value;
+ this._name = value;
+ }
}
- }
- private string _value;
+ private string _value;
- /// <summary>
- /// The value of the field.
- /// Must be non-null, non-empty and &lt;= 1024 characters.
- /// </summary>
- [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
- public string Value { get => this._value;
- set
- {
- if (string.IsNullOrWhiteSpace(value))
+ /// <summary>
+ /// The value of the field.
+ /// Must be non-null, non-empty and &lt;= 1024 characters.
+ /// </summary>
+ [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
+ public string Value { get => this._value;
+ set
{
- if (value == null)
- throw new ArgumentNullException(nameof(value));
- throw new ArgumentException("Value cannot be empty or whitespace.", nameof(value));
- }
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ if (value == null)
+ throw new ArgumentNullException(nameof(value));
+ throw new ArgumentException("Value cannot be empty or whitespace.", nameof(value));
+ }
- if (value.Length > 1024)
- throw new ArgumentException("Embed field value length cannot exceed 1024 characters.");
+ if (value.Length > 1024)
+ throw new ArgumentException("Embed field value length cannot exceed 1024 characters.");
- this._value = value;
+ this._value = value;
+ }
}
- }
- /// <summary>
- /// Whether or not this field should display inline.
- /// </summary>
- [JsonProperty("inline", NullValueHandling = NullValueHandling.Ignore)]
- public bool Inline { get; set; }
+ /// <summary>
+ /// Whether or not this field should display inline.
+ /// </summary>
+ [JsonProperty("inline", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Inline { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmbedField"/> class.
- /// </summary>
- /// <param name="name"><see cref="Name"/></param>
- /// <param name="value"><see cref="Value"/></param>
- /// <param name="inline"><see cref="Inline"/></param>
- public DiscordEmbedField(string name, string value, bool inline = false)
- {
- this.Name = name;
- this.Value = value;
- this.Inline = inline;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmbedField"/> class.
+ /// </summary>
+ /// <param name="name"><see cref="Name"/></param>
+ /// <param name="value"><see cref="Value"/></param>
+ /// <param name="inline"><see cref="Inline"/></param>
+ public DiscordEmbedField(string name, string value, bool inline = false)
+ {
+ this.Name = name;
+ this.Value = value;
+ this.Inline = inline;
+ }
}
}
diff --git a/DisCatSharp/Entities/Embed/DiscordEmbedFooter.cs b/DisCatSharp/Entities/Embed/DiscordEmbedFooter.cs
index 399a357f3..2b835845d 100644
--- a/DisCatSharp/Entities/Embed/DiscordEmbedFooter.cs
+++ b/DisCatSharp/Entities/Embed/DiscordEmbedFooter.cs
@@ -1,57 +1,58 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a footer in an embed.
-/// </summary>
-public sealed class DiscordEmbedFooter
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the footer's text.
+ /// Represents a footer in an embed.
/// </summary>
- [JsonProperty("text", NullValueHandling = NullValueHandling.Ignore)]
- public string Text { get; internal set; }
+ public sealed class DiscordEmbedFooter
+ {
+ /// <summary>
+ /// Gets the footer's text.
+ /// </summary>
+ [JsonProperty("text", NullValueHandling = NullValueHandling.Ignore)]
+ public string Text { get; internal set; }
- /// <summary>
- /// Gets the url of the footer's icon.
- /// </summary>
- [JsonProperty("icon_url", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUri IconUrl { get; internal set; }
+ /// <summary>
+ /// Gets the url of the footer's icon.
+ /// </summary>
+ [JsonProperty("icon_url", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUri IconUrl { get; internal set; }
- /// <summary>
- /// Gets the proxied url of the footer's icon.
- /// </summary>
- [JsonProperty("proxy_icon_url", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUri ProxyIconUrl { get; internal set; }
+ /// <summary>
+ /// Gets the proxied url of the footer's icon.
+ /// </summary>
+ [JsonProperty("proxy_icon_url", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUri ProxyIconUrl { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmbedFooter"/> class.
- /// </summary>
- internal DiscordEmbedFooter()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmbedFooter"/> class.
+ /// </summary>
+ internal DiscordEmbedFooter()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Embed/DiscordEmbedImage.cs b/DisCatSharp/Entities/Embed/DiscordEmbedImage.cs
index cbb0663cc..bff643da8 100644
--- a/DisCatSharp/Entities/Embed/DiscordEmbedImage.cs
+++ b/DisCatSharp/Entities/Embed/DiscordEmbedImage.cs
@@ -1,63 +1,64 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an image in an embed.
-/// </summary>
-public sealed class DiscordEmbedImage
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the source url of the image.
+ /// Represents an image in an embed.
/// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUri Url { get; internal set; }
+ public sealed class DiscordEmbedImage
+ {
+ /// <summary>
+ /// Gets the source url of the image.
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUri Url { get; internal set; }
- /// <summary>
- /// Gets a proxied url of the image.
- /// </summary>
- [JsonProperty("proxy_url", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUri ProxyUrl { get; internal set; }
+ /// <summary>
+ /// Gets a proxied url of the image.
+ /// </summary>
+ [JsonProperty("proxy_url", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUri ProxyUrl { get; internal set; }
- /// <summary>
- /// Gets the height of the image.
- /// </summary>
- [JsonProperty("height", NullValueHandling = NullValueHandling.Ignore)]
- public int Height { get; internal set; }
+ /// <summary>
+ /// Gets the height of the image.
+ /// </summary>
+ [JsonProperty("height", NullValueHandling = NullValueHandling.Ignore)]
+ public int Height { get; internal set; }
- /// <summary>
- /// Gets the width of the image.
- /// </summary>
- [JsonProperty("width", NullValueHandling = NullValueHandling.Ignore)]
- public int Width { get; internal set; }
+ /// <summary>
+ /// Gets the width of the image.
+ /// </summary>
+ [JsonProperty("width", NullValueHandling = NullValueHandling.Ignore)]
+ public int Width { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmbedImage"/> class.
- /// </summary>
- internal DiscordEmbedImage()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmbedImage"/> class.
+ /// </summary>
+ internal DiscordEmbedImage()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Embed/DiscordEmbedProvider.cs b/DisCatSharp/Entities/Embed/DiscordEmbedProvider.cs
index b99b2a06d..aa3005fa0 100644
--- a/DisCatSharp/Entities/Embed/DiscordEmbedProvider.cs
+++ b/DisCatSharp/Entities/Embed/DiscordEmbedProvider.cs
@@ -1,51 +1,52 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an embed provider.
-/// </summary>
-public sealed class DiscordEmbedProvider
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of the provider.
+ /// Represents an embed provider.
/// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
+ public sealed class DiscordEmbedProvider
+ {
+ /// <summary>
+ /// Gets the name of the provider.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the url of the provider.
- /// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public Uri Url { get; internal set; }
+ /// <summary>
+ /// Gets the url of the provider.
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public Uri Url { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmbedProvider"/> class.
- /// </summary>
- internal DiscordEmbedProvider()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmbedProvider"/> class.
+ /// </summary>
+ internal DiscordEmbedProvider()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Embed/DiscordEmbedThumbnail.cs b/DisCatSharp/Entities/Embed/DiscordEmbedThumbnail.cs
index eb350af29..e60d86458 100644
--- a/DisCatSharp/Entities/Embed/DiscordEmbedThumbnail.cs
+++ b/DisCatSharp/Entities/Embed/DiscordEmbedThumbnail.cs
@@ -1,63 +1,64 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a thumbnail in an embed.
-/// </summary>
-public sealed class DiscordEmbedThumbnail
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the source url of the thumbnail (only https).
+ /// Represents a thumbnail in an embed.
/// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUri Url { get; internal set; }
+ public sealed class DiscordEmbedThumbnail
+ {
+ /// <summary>
+ /// Gets the source url of the thumbnail (only https).
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUri Url { get; internal set; }
- /// <summary>
- /// Gets a proxied url of the thumbnail.
- /// </summary>
- [JsonProperty("proxy_url", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUri ProxyUrl { get; internal set; }
+ /// <summary>
+ /// Gets a proxied url of the thumbnail.
+ /// </summary>
+ [JsonProperty("proxy_url", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUri ProxyUrl { get; internal set; }
- /// <summary>
- /// Gets the height of the thumbnail.
- /// </summary>
- [JsonProperty("height", NullValueHandling = NullValueHandling.Ignore)]
- public int Height { get; internal set; }
+ /// <summary>
+ /// Gets the height of the thumbnail.
+ /// </summary>
+ [JsonProperty("height", NullValueHandling = NullValueHandling.Ignore)]
+ public int Height { get; internal set; }
- /// <summary>
- /// Gets the width of the thumbnail.
- /// </summary>
- [JsonProperty("width", NullValueHandling = NullValueHandling.Ignore)]
- public int Width { get; internal set; }
+ /// <summary>
+ /// Gets the width of the thumbnail.
+ /// </summary>
+ [JsonProperty("width", NullValueHandling = NullValueHandling.Ignore)]
+ public int Width { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmbedThumbnail"/> class.
- /// </summary>
- internal DiscordEmbedThumbnail()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmbedThumbnail"/> class.
+ /// </summary>
+ internal DiscordEmbedThumbnail()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Embed/DiscordEmbedVideo.cs b/DisCatSharp/Entities/Embed/DiscordEmbedVideo.cs
index c902fb1a3..215f7fb21 100644
--- a/DisCatSharp/Entities/Embed/DiscordEmbedVideo.cs
+++ b/DisCatSharp/Entities/Embed/DiscordEmbedVideo.cs
@@ -1,57 +1,58 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a video inside an embed.
-/// </summary>
-public sealed class DiscordEmbedVideo
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the source url of the video.
+ /// Represents a video inside an embed.
/// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public Uri Url { get; internal set; }
+ public sealed class DiscordEmbedVideo
+ {
+ /// <summary>
+ /// Gets the source url of the video.
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public Uri Url { get; internal set; }
- /// <summary>
- /// Gets the height of the video.
- /// </summary>
- [JsonProperty("height", NullValueHandling = NullValueHandling.Ignore)]
- public int Height { get; internal set; }
+ /// <summary>
+ /// Gets the height of the video.
+ /// </summary>
+ [JsonProperty("height", NullValueHandling = NullValueHandling.Ignore)]
+ public int Height { get; internal set; }
- /// <summary>
- /// Gets the width of the video.
- /// </summary>
- [JsonProperty("width", NullValueHandling = NullValueHandling.Ignore)]
- public int Width { get; internal set; }
+ /// <summary>
+ /// Gets the width of the video.
+ /// </summary>
+ [JsonProperty("width", NullValueHandling = NullValueHandling.Ignore)]
+ public int Width { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmbedVideo"/> class.
- /// </summary>
- internal DiscordEmbedVideo()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmbedVideo"/> class.
+ /// </summary>
+ internal DiscordEmbedVideo()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Emoji/DiscordEmoji.EmojiUtils.cs b/DisCatSharp/Entities/Emoji/DiscordEmoji.EmojiUtils.cs
index 2e30f76d0..65dce85b0 100644
--- a/DisCatSharp/Entities/Emoji/DiscordEmoji.EmojiUtils.cs
+++ b/DisCatSharp/Entities/Emoji/DiscordEmoji.EmojiUtils.cs
@@ -1,10148 +1,10149 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents discord emoji.
-/// </summary>
-public partial class DiscordEmoji
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets a mapping of :name: => unicode.
+ /// Represents discord emoji.
/// </summary>
- private static IReadOnlyDictionary<string, string> s_unicodeEmojis { get; }
+ public partial class DiscordEmoji
+ {
+ /// <summary>
+ /// Gets a mapping of :name: => unicode.
+ /// </summary>
+ private static IReadOnlyDictionary<string, string> s_unicodeEmojis { get; }
- /// <summary>
- /// Gets a mapping of unicode => :name:.
- /// </summary>
- private static IReadOnlyDictionary<string, string> s_discordNameLookup { get; }
+ /// <summary>
+ /// Gets a mapping of unicode => :name:.
+ /// </summary>
+ private static IReadOnlyDictionary<string, string> s_discordNameLookup { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmoji"/> class.
- /// </summary>
- static DiscordEmoji()
- {
- #region Generated Emoji Map
- s_unicodeEmojis = new Dictionary<string, string>
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmoji"/> class.
+ /// </summary>
+ static DiscordEmoji()
{
- [":100:"] = "\U0001f4af",
- [":1234:"] = "\U0001f522",
- [":8ball:"] = "\U0001f3b1",
- [":a:"] = "\U0001f170\ufe0f",
- [":ab:"] = "\U0001f18e",
- [":abacus:"] = "\U0001f9ee",
- [":abc:"] = "\U0001f524",
- [":abcd:"] = "\U0001f521",
- [":accept:"] = "\U0001f251",
- [":accordion:"] = "\U0001fa97",
- [":adhesive_bandage:"] = "\U0001fa79",
- [":adult:"] = "\U0001f9d1",
- [":adult_tone1:"] = "\U0001f9d1\U0001f3fb",
- [":adult_light_skin_tone:"] = "\U0001f9d1\U0001f3fb",
- [":adult::skin-tone-1:"] = "\U0001f9d1\U0001f3fb",
- [":adult_tone2:"] = "\U0001f9d1\U0001f3fc",
- [":adult_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc",
- [":adult::skin-tone-2:"] = "\U0001f9d1\U0001f3fc",
- [":adult_tone3:"] = "\U0001f9d1\U0001f3fd",
- [":adult_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd",
- [":adult::skin-tone-3:"] = "\U0001f9d1\U0001f3fd",
- [":adult_tone4:"] = "\U0001f9d1\U0001f3fe",
- [":adult_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe",
- [":adult::skin-tone-4:"] = "\U0001f9d1\U0001f3fe",
- [":adult_tone5:"] = "\U0001f9d1\U0001f3ff",
- [":adult_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff",
- [":adult::skin-tone-5:"] = "\U0001f9d1\U0001f3ff",
- [":aerial_tramway:"] = "\U0001f6a1",
- [":airplane:"] = "\u2708\ufe0f",
- [":airplane_arriving:"] = "\U0001f6ec",
- [":airplane_departure:"] = "\U0001f6eb",
- [":airplane_small:"] = "\U0001f6e9\ufe0f",
- [":small_airplane:"] = "\U0001f6e9\ufe0f",
- [":alarm_clock:"] = "\u23f0",
- [":alembic:"] = "\u2697\ufe0f",
- [":alien:"] = "\U0001f47d",
- [":ambulance:"] = "\U0001f691",
- [":amphora:"] = "\U0001f3fa",
- [":anatomical_heart:"] = "\U0001fac0",
- [":anchor:"] = "\u2693",
- [":angel:"] = "\U0001f47c",
- [":angel_tone1:"] = "\U0001f47c\U0001f3fb",
- [":angel::skin-tone-1:"] = "\U0001f47c\U0001f3fb",
- [":angel_tone2:"] = "\U0001f47c\U0001f3fc",
- [":angel::skin-tone-2:"] = "\U0001f47c\U0001f3fc",
- [":angel_tone3:"] = "\U0001f47c\U0001f3fd",
- [":angel::skin-tone-3:"] = "\U0001f47c\U0001f3fd",
- [":angel_tone4:"] = "\U0001f47c\U0001f3fe",
- [":angel::skin-tone-4:"] = "\U0001f47c\U0001f3fe",
- [":angel_tone5:"] = "\U0001f47c\U0001f3ff",
- [":angel::skin-tone-5:"] = "\U0001f47c\U0001f3ff",
- [":anger:"] = "\U0001f4a2",
- [":anger_right:"] = "\U0001f5ef\ufe0f",
- [":right_anger_bubble:"] = "\U0001f5ef\ufe0f",
- [":angry:"] = "\U0001f620",
- [">:("] = "\U0001f620",
- [">:-("] = "\U0001f620",
- [">=("] = "\U0001f620",
- [">=-("] = "\U0001f620",
- [":anguished:"] = "\U0001f627",
- [":ant:"] = "\U0001f41c",
- [":apple:"] = "\U0001f34e",
- [":aquarius:"] = "\u2652",
- [":aries:"] = "\u2648",
- [":arrow_backward:"] = "\u25c0\ufe0f",
- [":arrow_double_down:"] = "\u23ec",
- [":arrow_double_up:"] = "\u23eb",
- [":arrow_down:"] = "\u2b07\ufe0f",
- [":arrow_down_small:"] = "\U0001f53d",
- [":arrow_forward:"] = "\u25b6\ufe0f",
- [":arrow_heading_down:"] = "\u2935\ufe0f",
- [":arrow_heading_up:"] = "\u2934\ufe0f",
- [":arrow_left:"] = "\u2b05\ufe0f",
- [":arrow_lower_left:"] = "\u2199\ufe0f",
- [":arrow_lower_right:"] = "\u2198\ufe0f",
- [":arrow_right:"] = "\u27a1\ufe0f",
- [":arrow_right_hook:"] = "\u21aa\ufe0f",
- [":arrow_up:"] = "\u2b06\ufe0f",
- [":arrow_up_down:"] = "\u2195\ufe0f",
- [":arrow_up_small:"] = "\U0001f53c",
- [":arrow_upper_left:"] = "\u2196\ufe0f",
- [":arrow_upper_right:"] = "\u2197\ufe0f",
- [":arrows_clockwise:"] = "\U0001f503",
- [":arrows_counterclockwise:"] = "\U0001f504",
- [":art:"] = "\U0001f3a8",
- [":articulated_lorry:"] = "\U0001f69b",
- [":artist:"] = "\U0001f9d1\u200d\U0001f3a8",
- [":artist_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a8",
- [":artist_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a8",
- [":artist::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a8",
- [":artist_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a8",
- [":artist_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a8",
- [":artist::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a8",
- [":artist_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a8",
- [":artist_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a8",
- [":artist::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a8",
- [":artist_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a8",
- [":artist_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a8",
- [":artist::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a8",
- [":artist_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a8",
- [":artist_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a8",
- [":artist::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a8",
- [":asterisk:"] = "\u002a\ufe0f\u20e3",
- [":keycap_asterisk:"] = "\u002a\ufe0f\u20e3",
- [":astonished:"] = "\U0001f632",
- [":astronaut:"] = "\U0001f9d1\u200d\U0001f680",
- [":astronaut_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f680",
- [":astronaut_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f680",
- [":astronaut::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f680",
- [":astronaut_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f680",
- [":astronaut_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f680",
- [":astronaut::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f680",
- [":astronaut_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f680",
- [":astronaut_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f680",
- [":astronaut::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f680",
- [":astronaut_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f680",
- [":astronaut_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f680",
- [":astronaut::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f680",
- [":astronaut_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f680",
- [":astronaut_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f680",
- [":astronaut::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f680",
- [":athletic_shoe:"] = "\U0001f45f",
- [":atm:"] = "\U0001f3e7",
- [":atom:"] = "\u269b\ufe0f",
- [":atom_symbol:"] = "\u269b\ufe0f",
- [":auto_rickshaw:"] = "\U0001f6fa",
- [":avocado:"] = "\U0001f951",
- [":axe:"] = "\U0001fa93",
- [":b:"] = "\U0001f171\ufe0f",
- [":baby:"] = "\U0001f476",
- [":baby_bottle:"] = "\U0001f37c",
- [":baby_chick:"] = "\U0001f424",
- [":baby_symbol:"] = "\U0001f6bc",
- [":baby_tone1:"] = "\U0001f476\U0001f3fb",
- [":baby::skin-tone-1:"] = "\U0001f476\U0001f3fb",
- [":baby_tone2:"] = "\U0001f476\U0001f3fc",
- [":baby::skin-tone-2:"] = "\U0001f476\U0001f3fc",
- [":baby_tone3:"] = "\U0001f476\U0001f3fd",
- [":baby::skin-tone-3:"] = "\U0001f476\U0001f3fd",
- [":baby_tone4:"] = "\U0001f476\U0001f3fe",
- [":baby::skin-tone-4:"] = "\U0001f476\U0001f3fe",
- [":baby_tone5:"] = "\U0001f476\U0001f3ff",
- [":baby::skin-tone-5:"] = "\U0001f476\U0001f3ff",
- [":back:"] = "\U0001f519",
- [":bacon:"] = "\U0001f953",
- [":badger:"] = "\U0001f9a1",
- [":badminton:"] = "\U0001f3f8",
- [":bagel:"] = "\U0001f96f",
- [":baggage_claim:"] = "\U0001f6c4",
- [":ballet_shoes:"] = "\U0001fa70",
- [":balloon:"] = "\U0001f388",
- [":ballot_box:"] = "\U0001f5f3\ufe0f",
- [":ballot_box_with_ballot:"] = "\U0001f5f3\ufe0f",
- [":ballot_box_with_check:"] = "\u2611\ufe0f",
- [":bamboo:"] = "\U0001f38d",
- [":banana:"] = "\U0001f34c",
- [":bangbang:"] = "\u203c\ufe0f",
- [":banjo:"] = "\U0001fa95",
- [":bank:"] = "\U0001f3e6",
- [":bar_chart:"] = "\U0001f4ca",
- [":barber:"] = "\U0001f488",
- [":baseball:"] = "\u26be",
- [":basket:"] = "\U0001f9fa",
- [":basketball:"] = "\U0001f3c0",
- [":bat:"] = "\U0001f987",
- [":bath:"] = "\U0001f6c0",
- [":bath_tone1:"] = "\U0001f6c0\U0001f3fb",
- [":bath::skin-tone-1:"] = "\U0001f6c0\U0001f3fb",
- [":bath_tone2:"] = "\U0001f6c0\U0001f3fc",
- [":bath::skin-tone-2:"] = "\U0001f6c0\U0001f3fc",
- [":bath_tone3:"] = "\U0001f6c0\U0001f3fd",
- [":bath::skin-tone-3:"] = "\U0001f6c0\U0001f3fd",
- [":bath_tone4:"] = "\U0001f6c0\U0001f3fe",
- [":bath::skin-tone-4:"] = "\U0001f6c0\U0001f3fe",
- [":bath_tone5:"] = "\U0001f6c0\U0001f3ff",
- [":bath::skin-tone-5:"] = "\U0001f6c0\U0001f3ff",
- [":bathtub:"] = "\U0001f6c1",
- [":battery:"] = "\U0001f50b",
- [":beach:"] = "\U0001f3d6\ufe0f",
- [":beach_with_umbrella:"] = "\U0001f3d6\ufe0f",
- [":beach_umbrella:"] = "\u26f1\ufe0f",
- [":umbrella_on_ground:"] = "\u26f1\ufe0f",
- [":bear:"] = "\U0001f43b",
- [":bearded_person:"] = "\U0001f9d4",
- [":bearded_person_tone1:"] = "\U0001f9d4\U0001f3fb",
- [":bearded_person_light_skin_tone:"] = "\U0001f9d4\U0001f3fb",
- [":bearded_person::skin-tone-1:"] = "\U0001f9d4\U0001f3fb",
- [":bearded_person_tone2:"] = "\U0001f9d4\U0001f3fc",
- [":bearded_person_medium_light_skin_tone:"] = "\U0001f9d4\U0001f3fc",
- [":bearded_person::skin-tone-2:"] = "\U0001f9d4\U0001f3fc",
- [":bearded_person_tone3:"] = "\U0001f9d4\U0001f3fd",
- [":bearded_person_medium_skin_tone:"] = "\U0001f9d4\U0001f3fd",
- [":bearded_person::skin-tone-3:"] = "\U0001f9d4\U0001f3fd",
- [":bearded_person_tone4:"] = "\U0001f9d4\U0001f3fe",
- [":bearded_person_medium_dark_skin_tone:"] = "\U0001f9d4\U0001f3fe",
- [":bearded_person::skin-tone-4:"] = "\U0001f9d4\U0001f3fe",
- [":bearded_person_tone5:"] = "\U0001f9d4\U0001f3ff",
- [":bearded_person_dark_skin_tone:"] = "\U0001f9d4\U0001f3ff",
- [":bearded_person::skin-tone-5:"] = "\U0001f9d4\U0001f3ff",
- [":beaver:"] = "\U0001f9ab",
- [":bed:"] = "\U0001f6cf\ufe0f",
- [":bee:"] = "\U0001f41d",
- [":beer:"] = "\U0001f37a",
- [":beers:"] = "\U0001f37b",
- [":beetle:"] = "\U0001fab2",
- [":beginner:"] = "\U0001f530",
- [":bell:"] = "\U0001f514",
- [":bell_pepper:"] = "\U0001fad1",
- [":bellhop:"] = "\U0001f6ce\ufe0f",
- [":bellhop_bell:"] = "\U0001f6ce\ufe0f",
- [":bento:"] = "\U0001f371",
- [":beverage_box:"] = "\U0001f9c3",
- [":bike:"] = "\U0001f6b2",
- [":bikini:"] = "\U0001f459",
- [":billed_cap:"] = "\U0001f9e2",
- [":biohazard:"] = "\u2623\ufe0f",
- [":biohazard_sign:"] = "\u2623\ufe0f",
- [":bird:"] = "\U0001f426",
- [":birthday:"] = "\U0001f382",
- [":bison:"] = "\U0001f9ac",
- [":black_cat:"] = "\U0001f408\u200d\u2b1b",
- [":black_circle:"] = "\u26ab",
- [":black_heart:"] = "\U0001f5a4",
- [":black_joker:"] = "\U0001f0cf",
- [":black_large_square:"] = "\u2b1b",
- [":black_medium_small_square:"] = "\u25fe",
- [":black_medium_square:"] = "\u25fc\ufe0f",
- [":black_nib:"] = "\u2712\ufe0f",
- [":black_small_square:"] = "\u25aa\ufe0f",
- [":black_square_button:"] = "\U0001f532",
- [":blond_haired_man:"] = "\U0001f471\u200d\u2642\ufe0f",
- [":blond_haired_man_tone1:"] = "\U0001f471\U0001f3fb\u200d\u2642\ufe0f",
- [":blond_haired_man_light_skin_tone:"] = "\U0001f471\U0001f3fb\u200d\u2642\ufe0f",
- [":blond_haired_man::skin-tone-1:"] = "\U0001f471\U0001f3fb\u200d\u2642\ufe0f",
- [":blond_haired_man_tone2:"] = "\U0001f471\U0001f3fc\u200d\u2642\ufe0f",
- [":blond_haired_man_medium_light_skin_tone:"] = "\U0001f471\U0001f3fc\u200d\u2642\ufe0f",
- [":blond_haired_man::skin-tone-2:"] = "\U0001f471\U0001f3fc\u200d\u2642\ufe0f",
- [":blond_haired_man_tone3:"] = "\U0001f471\U0001f3fd\u200d\u2642\ufe0f",
- [":blond_haired_man_medium_skin_tone:"] = "\U0001f471\U0001f3fd\u200d\u2642\ufe0f",
- [":blond_haired_man::skin-tone-3:"] = "\U0001f471\U0001f3fd\u200d\u2642\ufe0f",
- [":blond_haired_man_tone4:"] = "\U0001f471\U0001f3fe\u200d\u2642\ufe0f",
- [":blond_haired_man_medium_dark_skin_tone:"] = "\U0001f471\U0001f3fe\u200d\u2642\ufe0f",
- [":blond_haired_man::skin-tone-4:"] = "\U0001f471\U0001f3fe\u200d\u2642\ufe0f",
- [":blond_haired_man_tone5:"] = "\U0001f471\U0001f3ff\u200d\u2642\ufe0f",
- [":blond_haired_man_dark_skin_tone:"] = "\U0001f471\U0001f3ff\u200d\u2642\ufe0f",
- [":blond_haired_man::skin-tone-5:"] = "\U0001f471\U0001f3ff\u200d\u2642\ufe0f",
- [":blond_haired_person:"] = "\U0001f471",
- [":person_with_blond_hair:"] = "\U0001f471",
- [":blond_haired_person_tone1:"] = "\U0001f471\U0001f3fb",
- [":person_with_blond_hair_tone1:"] = "\U0001f471\U0001f3fb",
- [":blond_haired_person::skin-tone-1:"] = "\U0001f471\U0001f3fb",
- [":person_with_blond_hair::skin-tone-1:"] = "\U0001f471\U0001f3fb",
- [":blond_haired_person_tone2:"] = "\U0001f471\U0001f3fc",
- [":person_with_blond_hair_tone2:"] = "\U0001f471\U0001f3fc",
- [":blond_haired_person::skin-tone-2:"] = "\U0001f471\U0001f3fc",
- [":person_with_blond_hair::skin-tone-2:"] = "\U0001f471\U0001f3fc",
- [":blond_haired_person_tone3:"] = "\U0001f471\U0001f3fd",
- [":person_with_blond_hair_tone3:"] = "\U0001f471\U0001f3fd",
- [":blond_haired_person::skin-tone-3:"] = "\U0001f471\U0001f3fd",
- [":person_with_blond_hair::skin-tone-3:"] = "\U0001f471\U0001f3fd",
- [":blond_haired_person_tone4:"] = "\U0001f471\U0001f3fe",
- [":person_with_blond_hair_tone4:"] = "\U0001f471\U0001f3fe",
- [":blond_haired_person::skin-tone-4:"] = "\U0001f471\U0001f3fe",
- [":person_with_blond_hair::skin-tone-4:"] = "\U0001f471\U0001f3fe",
- [":blond_haired_person_tone5:"] = "\U0001f471\U0001f3ff",
- [":person_with_blond_hair_tone5:"] = "\U0001f471\U0001f3ff",
- [":blond_haired_person::skin-tone-5:"] = "\U0001f471\U0001f3ff",
- [":person_with_blond_hair::skin-tone-5:"] = "\U0001f471\U0001f3ff",
- [":blond_haired_woman:"] = "\U0001f471\u200d\u2640\ufe0f",
- [":blond_haired_woman_tone1:"] = "\U0001f471\U0001f3fb\u200d\u2640\ufe0f",
- [":blond_haired_woman_light_skin_tone:"] = "\U0001f471\U0001f3fb\u200d\u2640\ufe0f",
- [":blond_haired_woman::skin-tone-1:"] = "\U0001f471\U0001f3fb\u200d\u2640\ufe0f",
- [":blond_haired_woman_tone2:"] = "\U0001f471\U0001f3fc\u200d\u2640\ufe0f",
- [":blond_haired_woman_medium_light_skin_tone:"] = "\U0001f471\U0001f3fc\u200d\u2640\ufe0f",
- [":blond_haired_woman::skin-tone-2:"] = "\U0001f471\U0001f3fc\u200d\u2640\ufe0f",
- [":blond_haired_woman_tone3:"] = "\U0001f471\U0001f3fd\u200d\u2640\ufe0f",
- [":blond_haired_woman_medium_skin_tone:"] = "\U0001f471\U0001f3fd\u200d\u2640\ufe0f",
- [":blond_haired_woman::skin-tone-3:"] = "\U0001f471\U0001f3fd\u200d\u2640\ufe0f",
- [":blond_haired_woman_tone4:"] = "\U0001f471\U0001f3fe\u200d\u2640\ufe0f",
- [":blond_haired_woman_medium_dark_skin_tone:"] = "\U0001f471\U0001f3fe\u200d\u2640\ufe0f",
- [":blond_haired_woman::skin-tone-4:"] = "\U0001f471\U0001f3fe\u200d\u2640\ufe0f",
- [":blond_haired_woman_tone5:"] = "\U0001f471\U0001f3ff\u200d\u2640\ufe0f",
- [":blond_haired_woman_dark_skin_tone:"] = "\U0001f471\U0001f3ff\u200d\u2640\ufe0f",
- [":blond_haired_woman::skin-tone-5:"] = "\U0001f471\U0001f3ff\u200d\u2640\ufe0f",
- [":blossom:"] = "\U0001f33c",
- [":blowfish:"] = "\U0001f421",
- [":blue_book:"] = "\U0001f4d8",
- [":blue_car:"] = "\U0001f699",
- [":blue_circle:"] = "\U0001f535",
- [":blue_heart:"] = "\U0001f499",
- [":blue_square:"] = "\U0001f7e6",
- [":blueberries:"] = "\U0001fad0",
- [":blush:"] = "\U0001f60a",
- [":\")"] = "\U0001f60a",
- [":-\")"] = "\U0001f60a",
- ["=\")"] = "\U0001f60a",
- ["=-\")"] = "\U0001f60a",
- [":boar:"] = "\U0001f417",
- [":bomb:"] = "\U0001f4a3",
- [":bone:"] = "\U0001f9b4",
- [":book:"] = "\U0001f4d6",
- [":bookmark:"] = "\U0001f516",
- [":bookmark_tabs:"] = "\U0001f4d1",
- [":books:"] = "\U0001f4da",
- [":boom:"] = "\U0001f4a5",
- [":boomerang:"] = "\U0001fa83",
- [":boot:"] = "\U0001f462",
- [":bouquet:"] = "\U0001f490",
- [":bow_and_arrow:"] = "\U0001f3f9",
- [":archery:"] = "\U0001f3f9",
- [":bowl_with_spoon:"] = "\U0001f963",
- [":bowling:"] = "\U0001f3b3",
- [":boxing_glove:"] = "\U0001f94a",
- [":boxing_gloves:"] = "\U0001f94a",
- [":boy:"] = "\U0001f466",
- [":boy_tone1:"] = "\U0001f466\U0001f3fb",
- [":boy::skin-tone-1:"] = "\U0001f466\U0001f3fb",
- [":boy_tone2:"] = "\U0001f466\U0001f3fc",
- [":boy::skin-tone-2:"] = "\U0001f466\U0001f3fc",
- [":boy_tone3:"] = "\U0001f466\U0001f3fd",
- [":boy::skin-tone-3:"] = "\U0001f466\U0001f3fd",
- [":boy_tone4:"] = "\U0001f466\U0001f3fe",
- [":boy::skin-tone-4:"] = "\U0001f466\U0001f3fe",
- [":boy_tone5:"] = "\U0001f466\U0001f3ff",
- [":boy::skin-tone-5:"] = "\U0001f466\U0001f3ff",
- [":brain:"] = "\U0001f9e0",
- [":bread:"] = "\U0001f35e",
- [":breast_feeding:"] = "\U0001f931",
- [":breast_feeding_tone1:"] = "\U0001f931\U0001f3fb",
- [":breast_feeding_light_skin_tone:"] = "\U0001f931\U0001f3fb",
- [":breast_feeding::skin-tone-1:"] = "\U0001f931\U0001f3fb",
- [":breast_feeding_tone2:"] = "\U0001f931\U0001f3fc",
- [":breast_feeding_medium_light_skin_tone:"] = "\U0001f931\U0001f3fc",
- [":breast_feeding::skin-tone-2:"] = "\U0001f931\U0001f3fc",
- [":breast_feeding_tone3:"] = "\U0001f931\U0001f3fd",
- [":breast_feeding_medium_skin_tone:"] = "\U0001f931\U0001f3fd",
- [":breast_feeding::skin-tone-3:"] = "\U0001f931\U0001f3fd",
- [":breast_feeding_tone4:"] = "\U0001f931\U0001f3fe",
- [":breast_feeding_medium_dark_skin_tone:"] = "\U0001f931\U0001f3fe",
- [":breast_feeding::skin-tone-4:"] = "\U0001f931\U0001f3fe",
- [":breast_feeding_tone5:"] = "\U0001f931\U0001f3ff",
- [":breast_feeding_dark_skin_tone:"] = "\U0001f931\U0001f3ff",
- [":breast_feeding::skin-tone-5:"] = "\U0001f931\U0001f3ff",
- [":bricks:"] = "\U0001f9f1",
- [":bridge_at_night:"] = "\U0001f309",
- [":briefcase:"] = "\U0001f4bc",
- [":briefs:"] = "\U0001fa72",
- [":broccoli:"] = "\U0001f966",
- [":broken_heart:"] = "\U0001f494",
- ["</3"] = "\U0001f494",
- ["<\\3"] = "\U0001f494",
- [":broom:"] = "\U0001f9f9",
- [":brown_circle:"] = "\U0001f7e4",
- [":brown_heart:"] = "\U0001f90e",
- [":brown_square:"] = "\U0001f7eb",
- [":bubble_tea:"] = "\U0001f9cb",
- [":bucket:"] = "\U0001faa3",
- [":bug:"] = "\U0001f41b",
- [":bulb:"] = "\U0001f4a1",
- [":bullettrain_front:"] = "\U0001f685",
- [":bullettrain_side:"] = "\U0001f684",
- [":burrito:"] = "\U0001f32f",
- [":bus:"] = "\U0001f68c",
- [":busstop:"] = "\U0001f68f",
- [":bust_in_silhouette:"] = "\U0001f464",
- [":busts_in_silhouette:"] = "\U0001f465",
- [":butter:"] = "\U0001f9c8",
- [":butterfly:"] = "\U0001f98b",
- [":cactus:"] = "\U0001f335",
- [":cake:"] = "\U0001f370",
- [":calendar:"] = "\U0001f4c6",
- [":calendar_spiral:"] = "\U0001f5d3\ufe0f",
- [":spiral_calendar_pad:"] = "\U0001f5d3\ufe0f",
- [":call_me:"] = "\U0001f919",
- [":call_me_hand:"] = "\U0001f919",
- [":call_me_tone1:"] = "\U0001f919\U0001f3fb",
- [":call_me_hand_tone1:"] = "\U0001f919\U0001f3fb",
- [":call_me::skin-tone-1:"] = "\U0001f919\U0001f3fb",
- [":call_me_hand::skin-tone-1:"] = "\U0001f919\U0001f3fb",
- [":call_me_tone2:"] = "\U0001f919\U0001f3fc",
- [":call_me_hand_tone2:"] = "\U0001f919\U0001f3fc",
- [":call_me::skin-tone-2:"] = "\U0001f919\U0001f3fc",
- [":call_me_hand::skin-tone-2:"] = "\U0001f919\U0001f3fc",
- [":call_me_tone3:"] = "\U0001f919\U0001f3fd",
- [":call_me_hand_tone3:"] = "\U0001f919\U0001f3fd",
- [":call_me::skin-tone-3:"] = "\U0001f919\U0001f3fd",
- [":call_me_hand::skin-tone-3:"] = "\U0001f919\U0001f3fd",
- [":call_me_tone4:"] = "\U0001f919\U0001f3fe",
- [":call_me_hand_tone4:"] = "\U0001f919\U0001f3fe",
- [":call_me::skin-tone-4:"] = "\U0001f919\U0001f3fe",
- [":call_me_hand::skin-tone-4:"] = "\U0001f919\U0001f3fe",
- [":call_me_tone5:"] = "\U0001f919\U0001f3ff",
- [":call_me_hand_tone5:"] = "\U0001f919\U0001f3ff",
- [":call_me::skin-tone-5:"] = "\U0001f919\U0001f3ff",
- [":call_me_hand::skin-tone-5:"] = "\U0001f919\U0001f3ff",
- [":calling:"] = "\U0001f4f2",
- [":camel:"] = "\U0001f42b",
- [":camera:"] = "\U0001f4f7",
- [":camera_with_flash:"] = "\U0001f4f8",
- [":camping:"] = "\U0001f3d5\ufe0f",
- [":cancer:"] = "\u264b",
- [":candle:"] = "\U0001f56f\ufe0f",
- [":candy:"] = "\U0001f36c",
- [":canned_food:"] = "\U0001f96b",
- [":canoe:"] = "\U0001f6f6",
- [":kayak:"] = "\U0001f6f6",
- [":capital_abcd:"] = "\U0001f520",
- [":capricorn:"] = "\u2651",
- [":card_box:"] = "\U0001f5c3\ufe0f",
- [":card_file_box:"] = "\U0001f5c3\ufe0f",
- [":card_index:"] = "\U0001f4c7",
- [":carousel_horse:"] = "\U0001f3a0",
- [":carpentry_saw:"] = "\U0001fa9a",
- [":carrot:"] = "\U0001f955",
- [":cat:"] = "\U0001f431",
- [":cat2:"] = "\U0001f408",
- [":cd:"] = "\U0001f4bf",
- [":chains:"] = "\u26d3\ufe0f",
- [":chair:"] = "\U0001fa91",
- [":champagne:"] = "\U0001f37e",
- [":bottle_with_popping_cork:"] = "\U0001f37e",
- [":champagne_glass:"] = "\U0001f942",
- [":clinking_glass:"] = "\U0001f942",
- [":chart:"] = "\U0001f4b9",
- [":chart_with_downwards_trend:"] = "\U0001f4c9",
- [":chart_with_upwards_trend:"] = "\U0001f4c8",
- [":checkered_flag:"] = "\U0001f3c1",
- [":cheese:"] = "\U0001f9c0",
- [":cheese_wedge:"] = "\U0001f9c0",
- [":cherries:"] = "\U0001f352",
- [":cherry_blossom:"] = "\U0001f338",
- [":chess_pawn:"] = "\u265f\ufe0f",
- [":chestnut:"] = "\U0001f330",
- [":chicken:"] = "\U0001f414",
- [":child:"] = "\U0001f9d2",
- [":child_tone1:"] = "\U0001f9d2\U0001f3fb",
- [":child_light_skin_tone:"] = "\U0001f9d2\U0001f3fb",
- [":child::skin-tone-1:"] = "\U0001f9d2\U0001f3fb",
- [":child_tone2:"] = "\U0001f9d2\U0001f3fc",
- [":child_medium_light_skin_tone:"] = "\U0001f9d2\U0001f3fc",
- [":child::skin-tone-2:"] = "\U0001f9d2\U0001f3fc",
- [":child_tone3:"] = "\U0001f9d2\U0001f3fd",
- [":child_medium_skin_tone:"] = "\U0001f9d2\U0001f3fd",
- [":child::skin-tone-3:"] = "\U0001f9d2\U0001f3fd",
- [":child_tone4:"] = "\U0001f9d2\U0001f3fe",
- [":child_medium_dark_skin_tone:"] = "\U0001f9d2\U0001f3fe",
- [":child::skin-tone-4:"] = "\U0001f9d2\U0001f3fe",
- [":child_tone5:"] = "\U0001f9d2\U0001f3ff",
- [":child_dark_skin_tone:"] = "\U0001f9d2\U0001f3ff",
- [":child::skin-tone-5:"] = "\U0001f9d2\U0001f3ff",
- [":children_crossing:"] = "\U0001f6b8",
- [":chipmunk:"] = "\U0001f43f\ufe0f",
- [":chocolate_bar:"] = "\U0001f36b",
- [":chopsticks:"] = "\U0001f962",
- [":christmas_tree:"] = "\U0001f384",
- [":church:"] = "\u26ea",
- [":cinema:"] = "\U0001f3a6",
- [":circus_tent:"] = "\U0001f3aa",
- [":city_dusk:"] = "\U0001f306",
- [":city_sunset:"] = "\U0001f307",
- [":city_sunrise:"] = "\U0001f307",
- [":cityscape:"] = "\U0001f3d9\ufe0f",
- [":cl:"] = "\U0001f191",
- [":clap:"] = "\U0001f44f",
- [":clap_tone1:"] = "\U0001f44f\U0001f3fb",
- [":clap::skin-tone-1:"] = "\U0001f44f\U0001f3fb",
- [":clap_tone2:"] = "\U0001f44f\U0001f3fc",
- [":clap::skin-tone-2:"] = "\U0001f44f\U0001f3fc",
- [":clap_tone3:"] = "\U0001f44f\U0001f3fd",
- [":clap::skin-tone-3:"] = "\U0001f44f\U0001f3fd",
- [":clap_tone4:"] = "\U0001f44f\U0001f3fe",
- [":clap::skin-tone-4:"] = "\U0001f44f\U0001f3fe",
- [":clap_tone5:"] = "\U0001f44f\U0001f3ff",
- [":clap::skin-tone-5:"] = "\U0001f44f\U0001f3ff",
- [":clapper:"] = "\U0001f3ac",
- [":classical_building:"] = "\U0001f3db\ufe0f",
- [":clipboard:"] = "\U0001f4cb",
- [":clock:"] = "\U0001f570\ufe0f",
- [":mantlepiece_clock:"] = "\U0001f570\ufe0f",
- [":clock1:"] = "\U0001f550",
- [":clock10:"] = "\U0001f559",
- [":clock1030:"] = "\U0001f565",
- [":clock11:"] = "\U0001f55a",
- [":clock1130:"] = "\U0001f566",
- [":clock12:"] = "\U0001f55b",
- [":clock1230:"] = "\U0001f567",
- [":clock130:"] = "\U0001f55c",
- [":clock2:"] = "\U0001f551",
- [":clock230:"] = "\U0001f55d",
- [":clock3:"] = "\U0001f552",
- [":clock330:"] = "\U0001f55e",
- [":clock4:"] = "\U0001f553",
- [":clock430:"] = "\U0001f55f",
- [":clock5:"] = "\U0001f554",
- [":clock530:"] = "\U0001f560",
- [":clock6:"] = "\U0001f555",
- [":clock630:"] = "\U0001f561",
- [":clock7:"] = "\U0001f556",
- [":clock730:"] = "\U0001f562",
- [":clock8:"] = "\U0001f557",
- [":clock830:"] = "\U0001f563",
- [":clock9:"] = "\U0001f558",
- [":clock930:"] = "\U0001f564",
- [":closed_book:"] = "\U0001f4d5",
- [":closed_lock_with_key:"] = "\U0001f510",
- [":closed_umbrella:"] = "\U0001f302",
- [":cloud:"] = "\u2601\ufe0f",
- [":cloud_lightning:"] = "\U0001f329\ufe0f",
- [":cloud_with_lightning:"] = "\U0001f329\ufe0f",
- [":cloud_rain:"] = "\U0001f327\ufe0f",
- [":cloud_with_rain:"] = "\U0001f327\ufe0f",
- [":cloud_snow:"] = "\U0001f328\ufe0f",
- [":cloud_with_snow:"] = "\U0001f328\ufe0f",
- [":cloud_tornado:"] = "\U0001f32a\ufe0f",
- [":cloud_with_tornado:"] = "\U0001f32a\ufe0f",
- [":clown:"] = "\U0001f921",
- [":clown_face:"] = "\U0001f921",
- [":clubs:"] = "\u2663\ufe0f",
- [":coat:"] = "\U0001f9e5",
- [":cockroach:"] = "\U0001fab3",
- [":cocktail:"] = "\U0001f378",
- [":coconut:"] = "\U0001f965",
- [":coffee:"] = "\u2615",
- [":coffin:"] = "\u26b0\ufe0f",
- [":coin:"] = "\U0001fa99",
- [":cold_face:"] = "\U0001f976",
- [":cold_sweat:"] = "\U0001f630",
- [":comet:"] = "\u2604\ufe0f",
- [":compass:"] = "\U0001f9ed",
- [":compression:"] = "\U0001f5dc\ufe0f",
- [":computer:"] = "\U0001f4bb",
- [":confetti_ball:"] = "\U0001f38a",
- [":confounded:"] = "\U0001f616",
- [":confused:"] = "\U0001f615",
- [":-\\"] = "\U0001f615",
- [":-/"] = "\U0001f615",
- ["=-\\"] = "\U0001f615",
- ["=-/"] = "\U0001f615",
- [":congratulations:"] = "\u3297\ufe0f",
- [":construction:"] = "\U0001f6a7",
- [":construction_site:"] = "\U0001f3d7\ufe0f",
- [":building_construction:"] = "\U0001f3d7\ufe0f",
- [":construction_worker:"] = "\U0001f477",
- [":construction_worker_tone1:"] = "\U0001f477\U0001f3fb",
- [":construction_worker::skin-tone-1:"] = "\U0001f477\U0001f3fb",
- [":construction_worker_tone2:"] = "\U0001f477\U0001f3fc",
- [":construction_worker::skin-tone-2:"] = "\U0001f477\U0001f3fc",
- [":construction_worker_tone3:"] = "\U0001f477\U0001f3fd",
- [":construction_worker::skin-tone-3:"] = "\U0001f477\U0001f3fd",
- [":construction_worker_tone4:"] = "\U0001f477\U0001f3fe",
- [":construction_worker::skin-tone-4:"] = "\U0001f477\U0001f3fe",
- [":construction_worker_tone5:"] = "\U0001f477\U0001f3ff",
- [":construction_worker::skin-tone-5:"] = "\U0001f477\U0001f3ff",
- [":control_knobs:"] = "\U0001f39b\ufe0f",
- [":convenience_store:"] = "\U0001f3ea",
- [":cook:"] = "\U0001f9d1\u200d\U0001f373",
- [":cook_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f373",
- [":cook_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f373",
- [":cook::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f373",
- [":cook_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f373",
- [":cook_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f373",
- [":cook::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f373",
- [":cook_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f373",
- [":cook_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f373",
- [":cook::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f373",
- [":cook_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f373",
- [":cook_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f373",
- [":cook::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f373",
- [":cook_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f373",
- [":cook_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f373",
- [":cook::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f373",
- [":cookie:"] = "\U0001f36a",
- [":cooking:"] = "\U0001f373",
- [":cool:"] = "\U0001f192",
- [":copyright:"] = "\u00a9\ufe0f",
- [":corn:"] = "\U0001f33d",
- [":couch:"] = "\U0001f6cb\ufe0f",
- [":couch_and_lamp:"] = "\U0001f6cb\ufe0f",
- [":couple:"] = "\U0001f46b",
- [":couple_mm:"] = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468",
- [":couple_with_heart_mm:"] = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468",
- [":couple_with_heart:"] = "\U0001f491",
- [":couple_with_heart_woman_man:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468",
- [":couple_ww:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469",
- [":couple_with_heart_ww:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469",
- [":couplekiss:"] = "\U0001f48f",
- [":cow:"] = "\U0001f42e",
- [":cow2:"] = "\U0001f404",
- [":cowboy:"] = "\U0001f920",
- [":face_with_cowboy_hat:"] = "\U0001f920",
- [":crab:"] = "\U0001f980",
- [":crayon:"] = "\U0001f58d\ufe0f",
- [":lower_left_crayon:"] = "\U0001f58d\ufe0f",
- [":credit_card:"] = "\U0001f4b3",
- [":crescent_moon:"] = "\U0001f319",
- [":cricket:"] = "\U0001f997",
- [":cricket_game:"] = "\U0001f3cf",
- [":cricket_bat_ball:"] = "\U0001f3cf",
- [":crocodile:"] = "\U0001f40a",
- [":croissant:"] = "\U0001f950",
- [":cross:"] = "\u271d\ufe0f",
- [":latin_cross:"] = "\u271d\ufe0f",
- [":crossed_flags:"] = "\U0001f38c",
- [":crossed_swords:"] = "\u2694\ufe0f",
- [":crown:"] = "\U0001f451",
- [":cruise_ship:"] = "\U0001f6f3\ufe0f",
- [":passenger_ship:"] = "\U0001f6f3\ufe0f",
- [":cry:"] = "\U0001f622",
- [":'("] = "\U0001f622",
- [":'-("] = "\U0001f622",
- [":,("] = "\U0001f622",
- [":,-("] = "\U0001f622",
- ["='("] = "\U0001f622",
- ["='-("] = "\U0001f622",
- ["=,("] = "\U0001f622",
- ["=,-("] = "\U0001f622",
- [":crying_cat_face:"] = "\U0001f63f",
- [":crystal_ball:"] = "\U0001f52e",
- [":cucumber:"] = "\U0001f952",
- [":cup_with_straw:"] = "\U0001f964",
- [":cupcake:"] = "\U0001f9c1",
- [":cupid:"] = "\U0001f498",
- [":curling_stone:"] = "\U0001f94c",
- [":curly_loop:"] = "\u27b0",
- [":currency_exchange:"] = "\U0001f4b1",
- [":curry:"] = "\U0001f35b",
- [":custard:"] = "\U0001f36e",
- [":pudding:"] = "\U0001f36e",
- [":flan:"] = "\U0001f36e",
- [":customs:"] = "\U0001f6c3",
- [":cut_of_meat:"] = "\U0001f969",
- [":cyclone:"] = "\U0001f300",
- [":dagger:"] = "\U0001f5e1\ufe0f",
- [":dagger_knife:"] = "\U0001f5e1\ufe0f",
- [":dancer:"] = "\U0001f483",
- [":dancer_tone1:"] = "\U0001f483\U0001f3fb",
- [":dancer::skin-tone-1:"] = "\U0001f483\U0001f3fb",
- [":dancer_tone2:"] = "\U0001f483\U0001f3fc",
- [":dancer::skin-tone-2:"] = "\U0001f483\U0001f3fc",
- [":dancer_tone3:"] = "\U0001f483\U0001f3fd",
- [":dancer::skin-tone-3:"] = "\U0001f483\U0001f3fd",
- [":dancer_tone4:"] = "\U0001f483\U0001f3fe",
- [":dancer::skin-tone-4:"] = "\U0001f483\U0001f3fe",
- [":dancer_tone5:"] = "\U0001f483\U0001f3ff",
- [":dancer::skin-tone-5:"] = "\U0001f483\U0001f3ff",
- [":dango:"] = "\U0001f361",
- [":dark_sunglasses:"] = "\U0001f576\ufe0f",
- [":dart:"] = "\U0001f3af",
- [":dash:"] = "\U0001f4a8",
- [":date:"] = "\U0001f4c5",
- [":deaf_man:"] = "\U0001f9cf\u200d\u2642\ufe0f",
- [":deaf_man_tone1:"] = "\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f",
- [":deaf_man_light_skin_tone:"] = "\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f",
- [":deaf_man::skin-tone-1:"] = "\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f",
- [":deaf_man_tone2:"] = "\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f",
- [":deaf_man_medium_light_skin_tone:"] = "\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f",
- [":deaf_man::skin-tone-2:"] = "\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f",
- [":deaf_man_tone3:"] = "\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f",
- [":deaf_man_medium_skin_tone:"] = "\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f",
- [":deaf_man::skin-tone-3:"] = "\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f",
- [":deaf_man_tone4:"] = "\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f",
- [":deaf_man_medium_dark_skin_tone:"] = "\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f",
- [":deaf_man::skin-tone-4:"] = "\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f",
- [":deaf_man_tone5:"] = "\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f",
- [":deaf_man_dark_skin_tone:"] = "\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f",
- [":deaf_man::skin-tone-5:"] = "\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f",
- [":deaf_person:"] = "\U0001f9cf",
- [":deaf_person_tone1:"] = "\U0001f9cf\U0001f3fb",
- [":deaf_person_light_skin_tone:"] = "\U0001f9cf\U0001f3fb",
- [":deaf_person::skin-tone-1:"] = "\U0001f9cf\U0001f3fb",
- [":deaf_person_tone2:"] = "\U0001f9cf\U0001f3fc",
- [":deaf_person_medium_light_skin_tone:"] = "\U0001f9cf\U0001f3fc",
- [":deaf_person::skin-tone-2:"] = "\U0001f9cf\U0001f3fc",
- [":deaf_person_tone3:"] = "\U0001f9cf\U0001f3fd",
- [":deaf_person_medium_skin_tone:"] = "\U0001f9cf\U0001f3fd",
- [":deaf_person::skin-tone-3:"] = "\U0001f9cf\U0001f3fd",
- [":deaf_person_tone4:"] = "\U0001f9cf\U0001f3fe",
- [":deaf_person_medium_dark_skin_tone:"] = "\U0001f9cf\U0001f3fe",
- [":deaf_person::skin-tone-4:"] = "\U0001f9cf\U0001f3fe",
- [":deaf_person_tone5:"] = "\U0001f9cf\U0001f3ff",
- [":deaf_person_dark_skin_tone:"] = "\U0001f9cf\U0001f3ff",
- [":deaf_person::skin-tone-5:"] = "\U0001f9cf\U0001f3ff",
- [":deaf_woman:"] = "\U0001f9cf\u200d\u2640\ufe0f",
- [":deaf_woman_tone1:"] = "\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f",
- [":deaf_woman_light_skin_tone:"] = "\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f",
- [":deaf_woman::skin-tone-1:"] = "\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f",
- [":deaf_woman_tone2:"] = "\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f",
- [":deaf_woman_medium_light_skin_tone:"] = "\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f",
- [":deaf_woman::skin-tone-2:"] = "\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f",
- [":deaf_woman_tone3:"] = "\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f",
- [":deaf_woman_medium_skin_tone:"] = "\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f",
- [":deaf_woman::skin-tone-3:"] = "\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f",
- [":deaf_woman_tone4:"] = "\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f",
- [":deaf_woman_medium_dark_skin_tone:"] = "\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f",
- [":deaf_woman::skin-tone-4:"] = "\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f",
- [":deaf_woman_tone5:"] = "\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f",
- [":deaf_woman_dark_skin_tone:"] = "\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f",
- [":deaf_woman::skin-tone-5:"] = "\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f",
- [":deciduous_tree:"] = "\U0001f333",
- [":deer:"] = "\U0001f98c",
- [":department_store:"] = "\U0001f3ec",
- [":desert:"] = "\U0001f3dc\ufe0f",
- [":desktop:"] = "\U0001f5a5\ufe0f",
- [":desktop_computer:"] = "\U0001f5a5\ufe0f",
- [":detective:"] = "\U0001f575\ufe0f",
- [":spy:"] = "\U0001f575\ufe0f",
- [":sleuth_or_spy:"] = "\U0001f575\ufe0f",
- [":detective_tone1:"] = "\U0001f575\U0001f3fb",
- [":spy_tone1:"] = "\U0001f575\U0001f3fb",
- [":sleuth_or_spy_tone1:"] = "\U0001f575\U0001f3fb",
- [":detective::skin-tone-1:"] = "\U0001f575\U0001f3fb",
- [":spy::skin-tone-1:"] = "\U0001f575\U0001f3fb",
- [":sleuth_or_spy::skin-tone-1:"] = "\U0001f575\U0001f3fb",
- [":detective_tone2:"] = "\U0001f575\U0001f3fc",
- [":spy_tone2:"] = "\U0001f575\U0001f3fc",
- [":sleuth_or_spy_tone2:"] = "\U0001f575\U0001f3fc",
- [":detective::skin-tone-2:"] = "\U0001f575\U0001f3fc",
- [":spy::skin-tone-2:"] = "\U0001f575\U0001f3fc",
- [":sleuth_or_spy::skin-tone-2:"] = "\U0001f575\U0001f3fc",
- [":detective_tone3:"] = "\U0001f575\U0001f3fd",
- [":spy_tone3:"] = "\U0001f575\U0001f3fd",
- [":sleuth_or_spy_tone3:"] = "\U0001f575\U0001f3fd",
- [":detective::skin-tone-3:"] = "\U0001f575\U0001f3fd",
- [":spy::skin-tone-3:"] = "\U0001f575\U0001f3fd",
- [":sleuth_or_spy::skin-tone-3:"] = "\U0001f575\U0001f3fd",
- [":detective_tone4:"] = "\U0001f575\U0001f3fe",
- [":spy_tone4:"] = "\U0001f575\U0001f3fe",
- [":sleuth_or_spy_tone4:"] = "\U0001f575\U0001f3fe",
- [":detective::skin-tone-4:"] = "\U0001f575\U0001f3fe",
- [":spy::skin-tone-4:"] = "\U0001f575\U0001f3fe",
- [":sleuth_or_spy::skin-tone-4:"] = "\U0001f575\U0001f3fe",
- [":detective_tone5:"] = "\U0001f575\U0001f3ff",
- [":spy_tone5:"] = "\U0001f575\U0001f3ff",
- [":sleuth_or_spy_tone5:"] = "\U0001f575\U0001f3ff",
- [":detective::skin-tone-5:"] = "\U0001f575\U0001f3ff",
- [":spy::skin-tone-5:"] = "\U0001f575\U0001f3ff",
- [":sleuth_or_spy::skin-tone-5:"] = "\U0001f575\U0001f3ff",
- [":diamond_shape_with_a_dot_inside:"] = "\U0001f4a0",
- [":diamonds:"] = "\u2666\ufe0f",
- [":disappointed:"] = "\U0001f61e",
- [":disappointed_relieved:"] = "\U0001f625",
- [":disguised_face:"] = "\U0001f978",
- [":dividers:"] = "\U0001f5c2\ufe0f",
- [":card_index_dividers:"] = "\U0001f5c2\ufe0f",
- [":diving_mask:"] = "\U0001f93f",
- [":diya_lamp:"] = "\U0001fa94",
- [":dizzy:"] = "\U0001f4ab",
- [":dizzy_face:"] = "\U0001f635",
- [":dna:"] = "\U0001f9ec",
- [":do_not_litter:"] = "\U0001f6af",
- [":dodo:"] = "\U0001f9a4",
- [":dog:"] = "\U0001f436",
- [":dog2:"] = "\U0001f415",
- [":dollar:"] = "\U0001f4b5",
- [":dolls:"] = "\U0001f38e",
- [":dolphin:"] = "\U0001f42c",
- [":door:"] = "\U0001f6aa",
- [":doughnut:"] = "\U0001f369",
- [":dove:"] = "\U0001f54a\ufe0f",
- [":dove_of_peace:"] = "\U0001f54a\ufe0f",
- [":dragon:"] = "\U0001f409",
- [":dragon_face:"] = "\U0001f432",
- [":dress:"] = "\U0001f457",
- [":dromedary_camel:"] = "\U0001f42a",
- [":drooling_face:"] = "\U0001f924",
- [":drool:"] = "\U0001f924",
- [":drop_of_blood:"] = "\U0001fa78",
- [":droplet:"] = "\U0001f4a7",
- [":drum:"] = "\U0001f941",
- [":drum_with_drumsticks:"] = "\U0001f941",
- [":duck:"] = "\U0001f986",
- [":dumpling:"] = "\U0001f95f",
- [":dvd:"] = "\U0001f4c0",
- [":e_mail:"] = "\U0001f4e7",
- [":email:"] = "\U0001f4e7",
- [":eagle:"] = "\U0001f985",
- [":ear:"] = "\U0001f442",
- [":ear_of_rice:"] = "\U0001f33e",
- [":ear_tone1:"] = "\U0001f442\U0001f3fb",
- [":ear::skin-tone-1:"] = "\U0001f442\U0001f3fb",
- [":ear_tone2:"] = "\U0001f442\U0001f3fc",
- [":ear::skin-tone-2:"] = "\U0001f442\U0001f3fc",
- [":ear_tone3:"] = "\U0001f442\U0001f3fd",
- [":ear::skin-tone-3:"] = "\U0001f442\U0001f3fd",
- [":ear_tone4:"] = "\U0001f442\U0001f3fe",
- [":ear::skin-tone-4:"] = "\U0001f442\U0001f3fe",
- [":ear_tone5:"] = "\U0001f442\U0001f3ff",
- [":ear::skin-tone-5:"] = "\U0001f442\U0001f3ff",
- [":ear_with_hearing_aid:"] = "\U0001f9bb",
- [":ear_with_hearing_aid_tone1:"] = "\U0001f9bb\U0001f3fb",
- [":ear_with_hearing_aid_light_skin_tone:"] = "\U0001f9bb\U0001f3fb",
- [":ear_with_hearing_aid::skin-tone-1:"] = "\U0001f9bb\U0001f3fb",
- [":ear_with_hearing_aid_tone2:"] = "\U0001f9bb\U0001f3fc",
- [":ear_with_hearing_aid_medium_light_skin_tone:"] = "\U0001f9bb\U0001f3fc",
- [":ear_with_hearing_aid::skin-tone-2:"] = "\U0001f9bb\U0001f3fc",
- [":ear_with_hearing_aid_tone3:"] = "\U0001f9bb\U0001f3fd",
- [":ear_with_hearing_aid_medium_skin_tone:"] = "\U0001f9bb\U0001f3fd",
- [":ear_with_hearing_aid::skin-tone-3:"] = "\U0001f9bb\U0001f3fd",
- [":ear_with_hearing_aid_tone4:"] = "\U0001f9bb\U0001f3fe",
- [":ear_with_hearing_aid_medium_dark_skin_tone:"] = "\U0001f9bb\U0001f3fe",
- [":ear_with_hearing_aid::skin-tone-4:"] = "\U0001f9bb\U0001f3fe",
- [":ear_with_hearing_aid_tone5:"] = "\U0001f9bb\U0001f3ff",
- [":ear_with_hearing_aid_dark_skin_tone:"] = "\U0001f9bb\U0001f3ff",
- [":ear_with_hearing_aid::skin-tone-5:"] = "\U0001f9bb\U0001f3ff",
- [":earth_africa:"] = "\U0001f30d",
- [":earth_americas:"] = "\U0001f30e",
- [":earth_asia:"] = "\U0001f30f",
- [":egg:"] = "\U0001f95a",
- [":eggplant:"] = "\U0001f346",
- [":eight:"] = "\u0038\ufe0f\u20e3",
- [":eight_pointed_black_star:"] = "\u2734\ufe0f",
- [":eight_spoked_asterisk:"] = "\u2733\ufe0f",
- [":eject:"] = "\u23cf\ufe0f",
- [":eject_symbol:"] = "\u23cf\ufe0f",
- [":electric_plug:"] = "\U0001f50c",
- [":elephant:"] = "\U0001f418",
- [":elevator:"] = "\U0001f6d7",
- [":elf:"] = "\U0001f9dd",
- [":elf_tone1:"] = "\U0001f9dd\U0001f3fb",
- [":elf_light_skin_tone:"] = "\U0001f9dd\U0001f3fb",
- [":elf::skin-tone-1:"] = "\U0001f9dd\U0001f3fb",
- [":elf_tone2:"] = "\U0001f9dd\U0001f3fc",
- [":elf_medium_light_skin_tone:"] = "\U0001f9dd\U0001f3fc",
- [":elf::skin-tone-2:"] = "\U0001f9dd\U0001f3fc",
- [":elf_tone3:"] = "\U0001f9dd\U0001f3fd",
- [":elf_medium_skin_tone:"] = "\U0001f9dd\U0001f3fd",
- [":elf::skin-tone-3:"] = "\U0001f9dd\U0001f3fd",
- [":elf_tone4:"] = "\U0001f9dd\U0001f3fe",
- [":elf_medium_dark_skin_tone:"] = "\U0001f9dd\U0001f3fe",
- [":elf::skin-tone-4:"] = "\U0001f9dd\U0001f3fe",
- [":elf_tone5:"] = "\U0001f9dd\U0001f3ff",
- [":elf_dark_skin_tone:"] = "\U0001f9dd\U0001f3ff",
- [":elf::skin-tone-5:"] = "\U0001f9dd\U0001f3ff",
- [":end:"] = "\U0001f51a",
- [":england:"] = "\U0001f3f4\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f",
- [":envelope:"] = "\u2709\ufe0f",
- [":envelope_with_arrow:"] = "\U0001f4e9",
- [":euro:"] = "\U0001f4b6",
- [":european_castle:"] = "\U0001f3f0",
- [":european_post_office:"] = "\U0001f3e4",
- [":evergreen_tree:"] = "\U0001f332",
- [":exclamation:"] = "\u2757",
- [":exploding_head:"] = "\U0001f92f",
- [":expressionless:"] = "\U0001f611",
- [":eye:"] = "\U0001f441\ufe0f",
- [":eye_in_speech_bubble:"] = "\U0001f441\u200d\U0001f5e8",
- [":eyeglasses:"] = "\U0001f453",
- [":eyes:"] = "\U0001f440",
- [":face_vomiting:"] = "\U0001f92e",
- [":face_with_hand_over_mouth:"] = "\U0001f92d",
- [":face_with_monocle:"] = "\U0001f9d0",
- [":face_with_raised_eyebrow:"] = "\U0001f928",
- [":face_with_symbols_over_mouth:"] = "\U0001f92c",
- [":factory:"] = "\U0001f3ed",
- [":factory_worker:"] = "\U0001f9d1\u200d\U0001f3ed",
- [":factory_worker_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3ed",
- [":factory_worker_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3ed",
- [":factory_worker::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3ed",
- [":factory_worker_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3ed",
- [":factory_worker_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3ed",
- [":factory_worker::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3ed",
- [":factory_worker_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3ed",
- [":factory_worker_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3ed",
- [":factory_worker::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3ed",
- [":factory_worker_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3ed",
- [":factory_worker_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3ed",
- [":factory_worker::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3ed",
- [":factory_worker_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3ed",
- [":factory_worker_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3ed",
- [":factory_worker::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3ed",
- [":fairy:"] = "\U0001f9da",
- [":fairy_tone1:"] = "\U0001f9da\U0001f3fb",
- [":fairy_light_skin_tone:"] = "\U0001f9da\U0001f3fb",
- [":fairy::skin-tone-1:"] = "\U0001f9da\U0001f3fb",
- [":fairy_tone2:"] = "\U0001f9da\U0001f3fc",
- [":fairy_medium_light_skin_tone:"] = "\U0001f9da\U0001f3fc",
- [":fairy::skin-tone-2:"] = "\U0001f9da\U0001f3fc",
- [":fairy_tone3:"] = "\U0001f9da\U0001f3fd",
- [":fairy_medium_skin_tone:"] = "\U0001f9da\U0001f3fd",
- [":fairy::skin-tone-3:"] = "\U0001f9da\U0001f3fd",
- [":fairy_tone4:"] = "\U0001f9da\U0001f3fe",
- [":fairy_medium_dark_skin_tone:"] = "\U0001f9da\U0001f3fe",
- [":fairy::skin-tone-4:"] = "\U0001f9da\U0001f3fe",
- [":fairy_tone5:"] = "\U0001f9da\U0001f3ff",
- [":fairy_dark_skin_tone:"] = "\U0001f9da\U0001f3ff",
- [":fairy::skin-tone-5:"] = "\U0001f9da\U0001f3ff",
- [":falafel:"] = "\U0001f9c6",
- [":fallen_leaf:"] = "\U0001f342",
- [":family:"] = "\U0001f46a",
- [":family_man_boy:"] = "\U0001f468\u200d\U0001f466",
- [":family_man_boy_boy:"] = "\U0001f468\u200d\U0001f466\u200d\U0001f466",
- [":family_man_girl:"] = "\U0001f468\u200d\U0001f467",
- [":family_man_girl_boy:"] = "\U0001f468\u200d\U0001f467\u200d\U0001f466",
- [":family_man_girl_girl:"] = "\U0001f468\u200d\U0001f467\u200d\U0001f467",
- [":family_man_woman_boy:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f466",
- [":family_mmb:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f466",
- [":family_mmbb:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f466\u200d\U0001f466",
- [":family_mmg:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f467",
- [":family_mmgb:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f466",
- [":family_mmgg:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f467",
- [":family_mwbb:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466",
- [":family_mwg:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f467",
- [":family_mwgb:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466",
- [":family_mwgg:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467",
- [":family_woman_boy:"] = "\U0001f469\u200d\U0001f466",
- [":family_woman_boy_boy:"] = "\U0001f469\u200d\U0001f466\u200d\U0001f466",
- [":family_woman_girl:"] = "\U0001f469\u200d\U0001f467",
- [":family_woman_girl_boy:"] = "\U0001f469\u200d\U0001f467\u200d\U0001f466",
- [":family_woman_girl_girl:"] = "\U0001f469\u200d\U0001f467\u200d\U0001f467",
- [":family_wwb:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f466",
- [":family_wwbb:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466",
- [":family_wwg:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f467",
- [":family_wwgb:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466",
- [":family_wwgg:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467",
- [":farmer:"] = "\U0001f9d1\u200d\U0001f33e",
- [":farmer_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f33e",
- [":farmer_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f33e",
- [":farmer::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f33e",
- [":farmer_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f33e",
- [":farmer_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f33e",
- [":farmer::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f33e",
- [":farmer_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f33e",
- [":farmer_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f33e",
- [":farmer::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f33e",
- [":farmer_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f33e",
- [":farmer_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f33e",
- [":farmer::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f33e",
- [":farmer_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f33e",
- [":farmer_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f33e",
- [":farmer::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f33e",
- [":fast_forward:"] = "\u23e9",
- [":fax:"] = "\U0001f4e0",
- [":fearful:"] = "\U0001f628",
- [":feather:"] = "\U0001fab6",
- [":feet:"] = "\U0001f43e",
- [":paw_prints:"] = "\U0001f43e",
- [":female_sign:"] = "\u2640\ufe0f",
- [":ferris_wheel:"] = "\U0001f3a1",
- [":ferry:"] = "\u26f4\ufe0f",
- [":field_hockey:"] = "\U0001f3d1",
- [":file_cabinet:"] = "\U0001f5c4\ufe0f",
- [":file_folder:"] = "\U0001f4c1",
- [":film_frames:"] = "\U0001f39e\ufe0f",
- [":fingers_crossed:"] = "\U0001f91e",
- [":hand_with_index_and_middle_finger_crossed:"] = "\U0001f91e",
- [":fingers_crossed_tone1:"] = "\U0001f91e\U0001f3fb",
- [":hand_with_index_and_middle_fingers_crossed_tone1:"] = "\U0001f91e\U0001f3fb",
- [":fingers_crossed::skin-tone-1:"] = "\U0001f91e\U0001f3fb",
- [":hand_with_index_and_middle_finger_crossed::skin-tone-1:"] = "\U0001f91e\U0001f3fb",
- [":fingers_crossed_tone2:"] = "\U0001f91e\U0001f3fc",
- [":hand_with_index_and_middle_fingers_crossed_tone2:"] = "\U0001f91e\U0001f3fc",
- [":fingers_crossed::skin-tone-2:"] = "\U0001f91e\U0001f3fc",
- [":hand_with_index_and_middle_finger_crossed::skin-tone-2:"] = "\U0001f91e\U0001f3fc",
- [":fingers_crossed_tone3:"] = "\U0001f91e\U0001f3fd",
- [":hand_with_index_and_middle_fingers_crossed_tone3:"] = "\U0001f91e\U0001f3fd",
- [":fingers_crossed::skin-tone-3:"] = "\U0001f91e\U0001f3fd",
- [":hand_with_index_and_middle_finger_crossed::skin-tone-3:"] = "\U0001f91e\U0001f3fd",
- [":fingers_crossed_tone4:"] = "\U0001f91e\U0001f3fe",
- [":hand_with_index_and_middle_fingers_crossed_tone4:"] = "\U0001f91e\U0001f3fe",
- [":fingers_crossed::skin-tone-4:"] = "\U0001f91e\U0001f3fe",
- [":hand_with_index_and_middle_finger_crossed::skin-tone-4:"] = "\U0001f91e\U0001f3fe",
- [":fingers_crossed_tone5:"] = "\U0001f91e\U0001f3ff",
- [":hand_with_index_and_middle_fingers_crossed_tone5:"] = "\U0001f91e\U0001f3ff",
- [":fingers_crossed::skin-tone-5:"] = "\U0001f91e\U0001f3ff",
- [":hand_with_index_and_middle_finger_crossed::skin-tone-5:"] = "\U0001f91e\U0001f3ff",
- [":fire:"] = "\U0001f525",
- [":flame:"] = "\U0001f525",
- [":fire_engine:"] = "\U0001f692",
- [":fire_extinguisher:"] = "\U0001f9ef",
- [":firecracker:"] = "\U0001f9e8",
- [":firefighter:"] = "\U0001f9d1\u200d\U0001f692",
- [":firefighter_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f692",
- [":firefighter_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f692",
- [":firefighter::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f692",
- [":firefighter_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f692",
- [":firefighter_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f692",
- [":firefighter::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f692",
- [":firefighter_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f692",
- [":firefighter_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f692",
- [":firefighter::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f692",
- [":firefighter_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f692",
- [":firefighter_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f692",
- [":firefighter::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f692",
- [":firefighter_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f692",
- [":firefighter_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f692",
- [":firefighter::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f692",
- [":fireworks:"] = "\U0001f386",
- [":first_place:"] = "\U0001f947",
- [":first_place_medal:"] = "\U0001f947",
- [":first_quarter_moon:"] = "\U0001f313",
- [":first_quarter_moon_with_face:"] = "\U0001f31b",
- [":fish:"] = "\U0001f41f",
- [":fish_cake:"] = "\U0001f365",
- [":fishing_pole_and_fish:"] = "\U0001f3a3",
- [":fist:"] = "\u270a",
- [":fist_tone1:"] = "\u270a\U0001f3fb",
- [":fist::skin-tone-1:"] = "\u270a\U0001f3fb",
- [":fist_tone2:"] = "\u270a\U0001f3fc",
- [":fist::skin-tone-2:"] = "\u270a\U0001f3fc",
- [":fist_tone3:"] = "\u270a\U0001f3fd",
- [":fist::skin-tone-3:"] = "\u270a\U0001f3fd",
- [":fist_tone4:"] = "\u270a\U0001f3fe",
- [":fist::skin-tone-4:"] = "\u270a\U0001f3fe",
- [":fist_tone5:"] = "\u270a\U0001f3ff",
- [":fist::skin-tone-5:"] = "\u270a\U0001f3ff",
- [":five:"] = "\u0035\ufe0f\u20e3",
- [":flag_ac:"] = "\U0001f1e6\U0001f1e8",
- [":flag_ad:"] = "\U0001f1e6\U0001f1e9",
- [":flag_ae:"] = "\U0001f1e6\U0001f1ea",
- [":flag_af:"] = "\U0001f1e6\U0001f1eb",
- [":flag_ag:"] = "\U0001f1e6\U0001f1ec",
- [":flag_ai:"] = "\U0001f1e6\U0001f1ee",
- [":flag_al:"] = "\U0001f1e6\U0001f1f1",
- [":flag_am:"] = "\U0001f1e6\U0001f1f2",
- [":flag_ao:"] = "\U0001f1e6\U0001f1f4",
- [":flag_aq:"] = "\U0001f1e6\U0001f1f6",
- [":flag_ar:"] = "\U0001f1e6\U0001f1f7",
- [":flag_as:"] = "\U0001f1e6\U0001f1f8",
- [":flag_at:"] = "\U0001f1e6\U0001f1f9",
- [":flag_au:"] = "\U0001f1e6\U0001f1fa",
- [":flag_aw:"] = "\U0001f1e6\U0001f1fc",
- [":flag_ax:"] = "\U0001f1e6\U0001f1fd",
- [":flag_az:"] = "\U0001f1e6\U0001f1ff",
- [":flag_ba:"] = "\U0001f1e7\U0001f1e6",
- [":flag_bb:"] = "\U0001f1e7\U0001f1e7",
- [":flag_bd:"] = "\U0001f1e7\U0001f1e9",
- [":flag_be:"] = "\U0001f1e7\U0001f1ea",
- [":flag_bf:"] = "\U0001f1e7\U0001f1eb",
- [":flag_bg:"] = "\U0001f1e7\U0001f1ec",
- [":flag_bh:"] = "\U0001f1e7\U0001f1ed",
- [":flag_bi:"] = "\U0001f1e7\U0001f1ee",
- [":flag_bj:"] = "\U0001f1e7\U0001f1ef",
- [":flag_bl:"] = "\U0001f1e7\U0001f1f1",
- [":flag_black:"] = "\U0001f3f4",
- [":flag_bm:"] = "\U0001f1e7\U0001f1f2",
- [":flag_bn:"] = "\U0001f1e7\U0001f1f3",
- [":flag_bo:"] = "\U0001f1e7\U0001f1f4",
- [":flag_bq:"] = "\U0001f1e7\U0001f1f6",
- [":flag_br:"] = "\U0001f1e7\U0001f1f7",
- [":flag_bs:"] = "\U0001f1e7\U0001f1f8",
- [":flag_bt:"] = "\U0001f1e7\U0001f1f9",
- [":flag_bv:"] = "\U0001f1e7\U0001f1fb",
- [":flag_bw:"] = "\U0001f1e7\U0001f1fc",
- [":flag_by:"] = "\U0001f1e7\U0001f1fe",
- [":flag_bz:"] = "\U0001f1e7\U0001f1ff",
- [":flag_ca:"] = "\U0001f1e8\U0001f1e6",
- [":flag_cc:"] = "\U0001f1e8\U0001f1e8",
- [":flag_cd:"] = "\U0001f1e8\U0001f1e9",
- [":flag_cf:"] = "\U0001f1e8\U0001f1eb",
- [":flag_cg:"] = "\U0001f1e8\U0001f1ec",
- [":flag_ch:"] = "\U0001f1e8\U0001f1ed",
- [":flag_ci:"] = "\U0001f1e8\U0001f1ee",
- [":flag_ck:"] = "\U0001f1e8\U0001f1f0",
- [":flag_cl:"] = "\U0001f1e8\U0001f1f1",
- [":flag_cm:"] = "\U0001f1e8\U0001f1f2",
- [":flag_cn:"] = "\U0001f1e8\U0001f1f3",
- [":flag_co:"] = "\U0001f1e8\U0001f1f4",
- [":flag_cp:"] = "\U0001f1e8\U0001f1f5",
- [":flag_cr:"] = "\U0001f1e8\U0001f1f7",
- [":flag_cu:"] = "\U0001f1e8\U0001f1fa",
- [":flag_cv:"] = "\U0001f1e8\U0001f1fb",
- [":flag_cw:"] = "\U0001f1e8\U0001f1fc",
- [":flag_cx:"] = "\U0001f1e8\U0001f1fd",
- [":flag_cy:"] = "\U0001f1e8\U0001f1fe",
- [":flag_cz:"] = "\U0001f1e8\U0001f1ff",
- [":flag_de:"] = "\U0001f1e9\U0001f1ea",
- [":flag_dg:"] = "\U0001f1e9\U0001f1ec",
- [":flag_dj:"] = "\U0001f1e9\U0001f1ef",
- [":flag_dk:"] = "\U0001f1e9\U0001f1f0",
- [":flag_dm:"] = "\U0001f1e9\U0001f1f2",
- [":flag_do:"] = "\U0001f1e9\U0001f1f4",
- [":flag_dz:"] = "\U0001f1e9\U0001f1ff",
- [":flag_ea:"] = "\U0001f1ea\U0001f1e6",
- [":flag_ec:"] = "\U0001f1ea\U0001f1e8",
- [":flag_ee:"] = "\U0001f1ea\U0001f1ea",
- [":flag_eg:"] = "\U0001f1ea\U0001f1ec",
- [":flag_eh:"] = "\U0001f1ea\U0001f1ed",
- [":flag_er:"] = "\U0001f1ea\U0001f1f7",
- [":flag_es:"] = "\U0001f1ea\U0001f1f8",
- [":flag_et:"] = "\U0001f1ea\U0001f1f9",
- [":flag_eu:"] = "\U0001f1ea\U0001f1fa",
- [":flag_fi:"] = "\U0001f1eb\U0001f1ee",
- [":flag_fj:"] = "\U0001f1eb\U0001f1ef",
- [":flag_fk:"] = "\U0001f1eb\U0001f1f0",
- [":flag_fm:"] = "\U0001f1eb\U0001f1f2",
- [":flag_fo:"] = "\U0001f1eb\U0001f1f4",
- [":flag_fr:"] = "\U0001f1eb\U0001f1f7",
- [":flag_ga:"] = "\U0001f1ec\U0001f1e6",
- [":flag_gb:"] = "\U0001f1ec\U0001f1e7",
- [":flag_gd:"] = "\U0001f1ec\U0001f1e9",
- [":flag_ge:"] = "\U0001f1ec\U0001f1ea",
- [":flag_gf:"] = "\U0001f1ec\U0001f1eb",
- [":flag_gg:"] = "\U0001f1ec\U0001f1ec",
- [":flag_gh:"] = "\U0001f1ec\U0001f1ed",
- [":flag_gi:"] = "\U0001f1ec\U0001f1ee",
- [":flag_gl:"] = "\U0001f1ec\U0001f1f1",
- [":flag_gm:"] = "\U0001f1ec\U0001f1f2",
- [":flag_gn:"] = "\U0001f1ec\U0001f1f3",
- [":flag_gp:"] = "\U0001f1ec\U0001f1f5",
- [":flag_gq:"] = "\U0001f1ec\U0001f1f6",
- [":flag_gr:"] = "\U0001f1ec\U0001f1f7",
- [":flag_gs:"] = "\U0001f1ec\U0001f1f8",
- [":flag_gt:"] = "\U0001f1ec\U0001f1f9",
- [":flag_gu:"] = "\U0001f1ec\U0001f1fa",
- [":flag_gw:"] = "\U0001f1ec\U0001f1fc",
- [":flag_gy:"] = "\U0001f1ec\U0001f1fe",
- [":flag_hk:"] = "\U0001f1ed\U0001f1f0",
- [":flag_hm:"] = "\U0001f1ed\U0001f1f2",
- [":flag_hn:"] = "\U0001f1ed\U0001f1f3",
- [":flag_hr:"] = "\U0001f1ed\U0001f1f7",
- [":flag_ht:"] = "\U0001f1ed\U0001f1f9",
- [":flag_hu:"] = "\U0001f1ed\U0001f1fa",
- [":flag_ic:"] = "\U0001f1ee\U0001f1e8",
- [":flag_id:"] = "\U0001f1ee\U0001f1e9",
- [":flag_ie:"] = "\U0001f1ee\U0001f1ea",
- [":flag_il:"] = "\U0001f1ee\U0001f1f1",
- [":flag_im:"] = "\U0001f1ee\U0001f1f2",
- [":flag_in:"] = "\U0001f1ee\U0001f1f3",
- [":flag_io:"] = "\U0001f1ee\U0001f1f4",
- [":flag_iq:"] = "\U0001f1ee\U0001f1f6",
- [":flag_ir:"] = "\U0001f1ee\U0001f1f7",
- [":flag_is:"] = "\U0001f1ee\U0001f1f8",
- [":flag_it:"] = "\U0001f1ee\U0001f1f9",
- [":flag_je:"] = "\U0001f1ef\U0001f1ea",
- [":flag_jm:"] = "\U0001f1ef\U0001f1f2",
- [":flag_jo:"] = "\U0001f1ef\U0001f1f4",
- [":flag_jp:"] = "\U0001f1ef\U0001f1f5",
- [":flag_ke:"] = "\U0001f1f0\U0001f1ea",
- [":flag_kg:"] = "\U0001f1f0\U0001f1ec",
- [":flag_kh:"] = "\U0001f1f0\U0001f1ed",
- [":flag_ki:"] = "\U0001f1f0\U0001f1ee",
- [":flag_km:"] = "\U0001f1f0\U0001f1f2",
- [":flag_kn:"] = "\U0001f1f0\U0001f1f3",
- [":flag_kp:"] = "\U0001f1f0\U0001f1f5",
- [":flag_kr:"] = "\U0001f1f0\U0001f1f7",
- [":flag_kw:"] = "\U0001f1f0\U0001f1fc",
- [":flag_ky:"] = "\U0001f1f0\U0001f1fe",
- [":flag_kz:"] = "\U0001f1f0\U0001f1ff",
- [":flag_la:"] = "\U0001f1f1\U0001f1e6",
- [":flag_lb:"] = "\U0001f1f1\U0001f1e7",
- [":flag_lc:"] = "\U0001f1f1\U0001f1e8",
- [":flag_li:"] = "\U0001f1f1\U0001f1ee",
- [":flag_lk:"] = "\U0001f1f1\U0001f1f0",
- [":flag_lr:"] = "\U0001f1f1\U0001f1f7",
- [":flag_ls:"] = "\U0001f1f1\U0001f1f8",
- [":flag_lt:"] = "\U0001f1f1\U0001f1f9",
- [":flag_lu:"] = "\U0001f1f1\U0001f1fa",
- [":flag_lv:"] = "\U0001f1f1\U0001f1fb",
- [":flag_ly:"] = "\U0001f1f1\U0001f1fe",
- [":flag_ma:"] = "\U0001f1f2\U0001f1e6",
- [":flag_mc:"] = "\U0001f1f2\U0001f1e8",
- [":flag_md:"] = "\U0001f1f2\U0001f1e9",
- [":flag_me:"] = "\U0001f1f2\U0001f1ea",
- [":flag_mf:"] = "\U0001f1f2\U0001f1eb",
- [":flag_mg:"] = "\U0001f1f2\U0001f1ec",
- [":flag_mh:"] = "\U0001f1f2\U0001f1ed",
- [":flag_mk:"] = "\U0001f1f2\U0001f1f0",
- [":flag_ml:"] = "\U0001f1f2\U0001f1f1",
- [":flag_mm:"] = "\U0001f1f2\U0001f1f2",
- [":flag_mn:"] = "\U0001f1f2\U0001f1f3",
- [":flag_mo:"] = "\U0001f1f2\U0001f1f4",
- [":flag_mp:"] = "\U0001f1f2\U0001f1f5",
- [":flag_mq:"] = "\U0001f1f2\U0001f1f6",
- [":flag_mr:"] = "\U0001f1f2\U0001f1f7",
- [":flag_ms:"] = "\U0001f1f2\U0001f1f8",
- [":flag_mt:"] = "\U0001f1f2\U0001f1f9",
- [":flag_mu:"] = "\U0001f1f2\U0001f1fa",
- [":flag_mv:"] = "\U0001f1f2\U0001f1fb",
- [":flag_mw:"] = "\U0001f1f2\U0001f1fc",
- [":flag_mx:"] = "\U0001f1f2\U0001f1fd",
- [":flag_my:"] = "\U0001f1f2\U0001f1fe",
- [":flag_mz:"] = "\U0001f1f2\U0001f1ff",
- [":flag_na:"] = "\U0001f1f3\U0001f1e6",
- [":flag_nc:"] = "\U0001f1f3\U0001f1e8",
- [":flag_ne:"] = "\U0001f1f3\U0001f1ea",
- [":flag_nf:"] = "\U0001f1f3\U0001f1eb",
- [":flag_ng:"] = "\U0001f1f3\U0001f1ec",
- [":flag_ni:"] = "\U0001f1f3\U0001f1ee",
- [":flag_nl:"] = "\U0001f1f3\U0001f1f1",
- [":flag_no:"] = "\U0001f1f3\U0001f1f4",
- [":flag_np:"] = "\U0001f1f3\U0001f1f5",
- [":flag_nr:"] = "\U0001f1f3\U0001f1f7",
- [":flag_nu:"] = "\U0001f1f3\U0001f1fa",
- [":flag_nz:"] = "\U0001f1f3\U0001f1ff",
- [":flag_om:"] = "\U0001f1f4\U0001f1f2",
- [":flag_pa:"] = "\U0001f1f5\U0001f1e6",
- [":flag_pe:"] = "\U0001f1f5\U0001f1ea",
- [":flag_pf:"] = "\U0001f1f5\U0001f1eb",
- [":flag_pg:"] = "\U0001f1f5\U0001f1ec",
- [":flag_ph:"] = "\U0001f1f5\U0001f1ed",
- [":flag_pk:"] = "\U0001f1f5\U0001f1f0",
- [":flag_pl:"] = "\U0001f1f5\U0001f1f1",
- [":flag_pm:"] = "\U0001f1f5\U0001f1f2",
- [":flag_pn:"] = "\U0001f1f5\U0001f1f3",
- [":flag_pr:"] = "\U0001f1f5\U0001f1f7",
- [":flag_ps:"] = "\U0001f1f5\U0001f1f8",
- [":flag_pt:"] = "\U0001f1f5\U0001f1f9",
- [":flag_pw:"] = "\U0001f1f5\U0001f1fc",
- [":flag_py:"] = "\U0001f1f5\U0001f1fe",
- [":flag_qa:"] = "\U0001f1f6\U0001f1e6",
- [":flag_re:"] = "\U0001f1f7\U0001f1ea",
- [":flag_ro:"] = "\U0001f1f7\U0001f1f4",
- [":flag_rs:"] = "\U0001f1f7\U0001f1f8",
- [":flag_ru:"] = "\U0001f1f7\U0001f1fa",
- [":flag_rw:"] = "\U0001f1f7\U0001f1fc",
- [":flag_sa:"] = "\U0001f1f8\U0001f1e6",
- [":flag_sb:"] = "\U0001f1f8\U0001f1e7",
- [":flag_sc:"] = "\U0001f1f8\U0001f1e8",
- [":flag_sd:"] = "\U0001f1f8\U0001f1e9",
- [":flag_se:"] = "\U0001f1f8\U0001f1ea",
- [":flag_sg:"] = "\U0001f1f8\U0001f1ec",
- [":flag_sh:"] = "\U0001f1f8\U0001f1ed",
- [":flag_si:"] = "\U0001f1f8\U0001f1ee",
- [":flag_sj:"] = "\U0001f1f8\U0001f1ef",
- [":flag_sk:"] = "\U0001f1f8\U0001f1f0",
- [":flag_sl:"] = "\U0001f1f8\U0001f1f1",
- [":flag_sm:"] = "\U0001f1f8\U0001f1f2",
- [":flag_sn:"] = "\U0001f1f8\U0001f1f3",
- [":flag_so:"] = "\U0001f1f8\U0001f1f4",
- [":flag_sr:"] = "\U0001f1f8\U0001f1f7",
- [":flag_ss:"] = "\U0001f1f8\U0001f1f8",
- [":flag_st:"] = "\U0001f1f8\U0001f1f9",
- [":flag_sv:"] = "\U0001f1f8\U0001f1fb",
- [":flag_sx:"] = "\U0001f1f8\U0001f1fd",
- [":flag_sy:"] = "\U0001f1f8\U0001f1fe",
- [":flag_sz:"] = "\U0001f1f8\U0001f1ff",
- [":flag_ta:"] = "\U0001f1f9\U0001f1e6",
- [":flag_tc:"] = "\U0001f1f9\U0001f1e8",
- [":flag_td:"] = "\U0001f1f9\U0001f1e9",
- [":flag_tf:"] = "\U0001f1f9\U0001f1eb",
- [":flag_tg:"] = "\U0001f1f9\U0001f1ec",
- [":flag_th:"] = "\U0001f1f9\U0001f1ed",
- [":flag_tj:"] = "\U0001f1f9\U0001f1ef",
- [":flag_tk:"] = "\U0001f1f9\U0001f1f0",
- [":flag_tl:"] = "\U0001f1f9\U0001f1f1",
- [":flag_tm:"] = "\U0001f1f9\U0001f1f2",
- [":flag_tn:"] = "\U0001f1f9\U0001f1f3",
- [":flag_to:"] = "\U0001f1f9\U0001f1f4",
- [":flag_tr:"] = "\U0001f1f9\U0001f1f7",
- [":flag_tt:"] = "\U0001f1f9\U0001f1f9",
- [":flag_tv:"] = "\U0001f1f9\U0001f1fb",
- [":flag_tw:"] = "\U0001f1f9\U0001f1fc",
- [":flag_tz:"] = "\U0001f1f9\U0001f1ff",
- [":flag_ua:"] = "\U0001f1fa\U0001f1e6",
- [":flag_ug:"] = "\U0001f1fa\U0001f1ec",
- [":flag_um:"] = "\U0001f1fa\U0001f1f2",
- [":flag_us:"] = "\U0001f1fa\U0001f1f8",
- [":flag_uy:"] = "\U0001f1fa\U0001f1fe",
- [":flag_uz:"] = "\U0001f1fa\U0001f1ff",
- [":flag_va:"] = "\U0001f1fb\U0001f1e6",
- [":flag_vc:"] = "\U0001f1fb\U0001f1e8",
- [":flag_ve:"] = "\U0001f1fb\U0001f1ea",
- [":flag_vg:"] = "\U0001f1fb\U0001f1ec",
- [":flag_vi:"] = "\U0001f1fb\U0001f1ee",
- [":flag_vn:"] = "\U0001f1fb\U0001f1f3",
- [":flag_vu:"] = "\U0001f1fb\U0001f1fa",
- [":flag_wf:"] = "\U0001f1fc\U0001f1eb",
- [":flag_white:"] = "\U0001f3f3\ufe0f",
- [":flag_ws:"] = "\U0001f1fc\U0001f1f8",
- [":flag_xk:"] = "\U0001f1fd\U0001f1f0",
- [":flag_ye:"] = "\U0001f1fe\U0001f1ea",
- [":flag_yt:"] = "\U0001f1fe\U0001f1f9",
- [":flag_za:"] = "\U0001f1ff\U0001f1e6",
- [":flag_zm:"] = "\U0001f1ff\U0001f1f2",
- [":flag_zw:"] = "\U0001f1ff\U0001f1fc",
- [":flags:"] = "\U0001f38f",
- [":flamingo:"] = "\U0001f9a9",
- [":flashlight:"] = "\U0001f526",
- [":flatbread:"] = "\U0001fad3",
- [":fleur_de_lis:"] = "\u269c\ufe0f",
- [":floppy_disk:"] = "\U0001f4be",
- [":flower_playing_cards:"] = "\U0001f3b4",
- [":flushed:"] = "\U0001f633",
- [":fly:"] = "\U0001fab0",
- [":flying_disc:"] = "\U0001f94f",
- [":flying_saucer:"] = "\U0001f6f8",
- [":fog:"] = "\U0001f32b\ufe0f",
- [":foggy:"] = "\U0001f301",
- [":fondue:"] = "\U0001fad5",
- [":foot:"] = "\U0001f9b6",
- [":foot_tone1:"] = "\U0001f9b6\U0001f3fb",
- [":foot_light_skin_tone:"] = "\U0001f9b6\U0001f3fb",
- [":foot::skin-tone-1:"] = "\U0001f9b6\U0001f3fb",
- [":foot_tone2:"] = "\U0001f9b6\U0001f3fc",
- [":foot_medium_light_skin_tone:"] = "\U0001f9b6\U0001f3fc",
- [":foot::skin-tone-2:"] = "\U0001f9b6\U0001f3fc",
- [":foot_tone3:"] = "\U0001f9b6\U0001f3fd",
- [":foot_medium_skin_tone:"] = "\U0001f9b6\U0001f3fd",
- [":foot::skin-tone-3:"] = "\U0001f9b6\U0001f3fd",
- [":foot_tone4:"] = "\U0001f9b6\U0001f3fe",
- [":foot_medium_dark_skin_tone:"] = "\U0001f9b6\U0001f3fe",
- [":foot::skin-tone-4:"] = "\U0001f9b6\U0001f3fe",
- [":foot_tone5:"] = "\U0001f9b6\U0001f3ff",
- [":foot_dark_skin_tone:"] = "\U0001f9b6\U0001f3ff",
- [":foot::skin-tone-5:"] = "\U0001f9b6\U0001f3ff",
- [":football:"] = "\U0001f3c8",
- [":footprints:"] = "\U0001f463",
- [":fork_and_knife:"] = "\U0001f374",
- [":fork_knife_plate:"] = "\U0001f37d\ufe0f",
- [":fork_and_knife_with_plate:"] = "\U0001f37d\ufe0f",
- [":fortune_cookie:"] = "\U0001f960",
- [":fountain:"] = "\u26f2",
- [":four:"] = "\u0034\ufe0f\u20e3",
- [":four_leaf_clover:"] = "\U0001f340",
- [":fox:"] = "\U0001f98a",
- [":fox_face:"] = "\U0001f98a",
- [":frame_photo:"] = "\U0001f5bc\ufe0f",
- [":frame_with_picture:"] = "\U0001f5bc\ufe0f",
- [":free:"] = "\U0001f193",
- [":french_bread:"] = "\U0001f956",
- [":baguette_bread:"] = "\U0001f956",
- [":fried_shrimp:"] = "\U0001f364",
- [":fries:"] = "\U0001f35f",
- [":frog:"] = "\U0001f438",
- [":frowning:"] = "\U0001f626",
- [":("] = "\U0001f626",
- [":-("] = "\U0001f626",
- ["=("] = "\U0001f626",
- ["=-("] = "\U0001f626",
- [":frowning2:"] = "\u2639\ufe0f",
- [":white_frowning_face:"] = "\u2639\ufe0f",
- [":fuelpump:"] = "\u26fd",
- [":full_moon:"] = "\U0001f315",
- [":full_moon_with_face:"] = "\U0001f31d",
- [":game_die:"] = "\U0001f3b2",
- [":garlic:"] = "\U0001f9c4",
- [":gear:"] = "\u2699\ufe0f",
- [":gem:"] = "\U0001f48e",
- [":gemini:"] = "\u264a",
- [":genie:"] = "\U0001f9de",
- [":ghost:"] = "\U0001f47b",
- [":gift:"] = "\U0001f381",
- [":gift_heart:"] = "\U0001f49d",
- [":giraffe:"] = "\U0001f992",
- [":girl:"] = "\U0001f467",
- [":girl_tone1:"] = "\U0001f467\U0001f3fb",
- [":girl::skin-tone-1:"] = "\U0001f467\U0001f3fb",
- [":girl_tone2:"] = "\U0001f467\U0001f3fc",
- [":girl::skin-tone-2:"] = "\U0001f467\U0001f3fc",
- [":girl_tone3:"] = "\U0001f467\U0001f3fd",
- [":girl::skin-tone-3:"] = "\U0001f467\U0001f3fd",
- [":girl_tone4:"] = "\U0001f467\U0001f3fe",
- [":girl::skin-tone-4:"] = "\U0001f467\U0001f3fe",
- [":girl_tone5:"] = "\U0001f467\U0001f3ff",
- [":girl::skin-tone-5:"] = "\U0001f467\U0001f3ff",
- [":globe_with_meridians:"] = "\U0001f310",
- [":gloves:"] = "\U0001f9e4",
- [":goal:"] = "\U0001f945",
- [":goal_net:"] = "\U0001f945",
- [":goat:"] = "\U0001f410",
- [":goggles:"] = "\U0001f97d",
- [":golf:"] = "\u26f3",
- [":gorilla:"] = "\U0001f98d",
- [":grapes:"] = "\U0001f347",
- [":green_apple:"] = "\U0001f34f",
- [":green_book:"] = "\U0001f4d7",
- [":green_circle:"] = "\U0001f7e2",
- [":green_heart:"] = "\U0001f49a",
- [":green_square:"] = "\U0001f7e9",
- [":grey_exclamation:"] = "\u2755",
- [":grey_question:"] = "\u2754",
- [":grimacing:"] = "\U0001f62c",
- [":grin:"] = "\U0001f601",
- [":grinning:"] = "\U0001f600",
- [":guard:"] = "\U0001f482",
- [":guardsman:"] = "\U0001f482",
- [":guard_tone1:"] = "\U0001f482\U0001f3fb",
- [":guardsman_tone1:"] = "\U0001f482\U0001f3fb",
- [":guard::skin-tone-1:"] = "\U0001f482\U0001f3fb",
- [":guardsman::skin-tone-1:"] = "\U0001f482\U0001f3fb",
- [":guard_tone2:"] = "\U0001f482\U0001f3fc",
- [":guardsman_tone2:"] = "\U0001f482\U0001f3fc",
- [":guard::skin-tone-2:"] = "\U0001f482\U0001f3fc",
- [":guardsman::skin-tone-2:"] = "\U0001f482\U0001f3fc",
- [":guard_tone3:"] = "\U0001f482\U0001f3fd",
- [":guardsman_tone3:"] = "\U0001f482\U0001f3fd",
- [":guard::skin-tone-3:"] = "\U0001f482\U0001f3fd",
- [":guardsman::skin-tone-3:"] = "\U0001f482\U0001f3fd",
- [":guard_tone4:"] = "\U0001f482\U0001f3fe",
- [":guardsman_tone4:"] = "\U0001f482\U0001f3fe",
- [":guard::skin-tone-4:"] = "\U0001f482\U0001f3fe",
- [":guardsman::skin-tone-4:"] = "\U0001f482\U0001f3fe",
- [":guard_tone5:"] = "\U0001f482\U0001f3ff",
- [":guardsman_tone5:"] = "\U0001f482\U0001f3ff",
- [":guard::skin-tone-5:"] = "\U0001f482\U0001f3ff",
- [":guardsman::skin-tone-5:"] = "\U0001f482\U0001f3ff",
- [":guide_dog:"] = "\U0001f9ae",
- [":guitar:"] = "\U0001f3b8",
- [":gun:"] = "\U0001f52b",
- [":hamburger:"] = "\U0001f354",
- [":hammer:"] = "\U0001f528",
- [":hammer_pick:"] = "\u2692\ufe0f",
- [":hammer_and_pick:"] = "\u2692\ufe0f",
- [":hamster:"] = "\U0001f439",
- [":hand_splayed:"] = "\U0001f590\ufe0f",
- [":raised_hand_with_fingers_splayed:"] = "\U0001f590\ufe0f",
- [":hand_splayed_tone1:"] = "\U0001f590\U0001f3fb",
- [":raised_hand_with_fingers_splayed_tone1:"] = "\U0001f590\U0001f3fb",
- [":hand_splayed::skin-tone-1:"] = "\U0001f590\U0001f3fb",
- [":raised_hand_with_fingers_splayed::skin-tone-1:"] = "\U0001f590\U0001f3fb",
- [":hand_splayed_tone2:"] = "\U0001f590\U0001f3fc",
- [":raised_hand_with_fingers_splayed_tone2:"] = "\U0001f590\U0001f3fc",
- [":hand_splayed::skin-tone-2:"] = "\U0001f590\U0001f3fc",
- [":raised_hand_with_fingers_splayed::skin-tone-2:"] = "\U0001f590\U0001f3fc",
- [":hand_splayed_tone3:"] = "\U0001f590\U0001f3fd",
- [":raised_hand_with_fingers_splayed_tone3:"] = "\U0001f590\U0001f3fd",
- [":hand_splayed::skin-tone-3:"] = "\U0001f590\U0001f3fd",
- [":raised_hand_with_fingers_splayed::skin-tone-3:"] = "\U0001f590\U0001f3fd",
- [":hand_splayed_tone4:"] = "\U0001f590\U0001f3fe",
- [":raised_hand_with_fingers_splayed_tone4:"] = "\U0001f590\U0001f3fe",
- [":hand_splayed::skin-tone-4:"] = "\U0001f590\U0001f3fe",
- [":raised_hand_with_fingers_splayed::skin-tone-4:"] = "\U0001f590\U0001f3fe",
- [":hand_splayed_tone5:"] = "\U0001f590\U0001f3ff",
- [":raised_hand_with_fingers_splayed_tone5:"] = "\U0001f590\U0001f3ff",
- [":hand_splayed::skin-tone-5:"] = "\U0001f590\U0001f3ff",
- [":raised_hand_with_fingers_splayed::skin-tone-5:"] = "\U0001f590\U0001f3ff",
- [":handbag:"] = "\U0001f45c",
- [":handshake:"] = "\U0001f91d",
- [":shaking_hands:"] = "\U0001f91d",
- [":hash:"] = "\u0023\ufe0f\u20e3",
- [":hatched_chick:"] = "\U0001f425",
- [":hatching_chick:"] = "\U0001f423",
- [":head_bandage:"] = "\U0001f915",
- [":face_with_head_bandage:"] = "\U0001f915",
- [":headphones:"] = "\U0001f3a7",
- [":headstone:"] = "\U0001faa6",
- [":health_worker:"] = "\U0001f9d1\u200d\u2695\ufe0f",
- [":health_worker_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f",
- [":health_worker_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f",
- [":health_worker::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f",
- [":health_worker_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f",
- [":health_worker_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f",
- [":health_worker::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f",
- [":health_worker_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f",
- [":health_worker_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f",
- [":health_worker::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f",
- [":health_worker_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f",
- [":health_worker_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f",
- [":health_worker::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f",
- [":health_worker_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f",
- [":health_worker_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f",
- [":health_worker::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f",
- [":hear_no_evil:"] = "\U0001f649",
- [":heart:"] = "\u2764\ufe0f",
- ["<3"] = "\u2764\ufe0f",
- ["♡"] = "\u2764\ufe0f",
- [":heart_decoration:"] = "\U0001f49f",
- [":heart_exclamation:"] = "\u2763\ufe0f",
- [":heavy_heart_exclamation_mark_ornament:"] = "\u2763\ufe0f",
- [":heart_eyes:"] = "\U0001f60d",
- [":heart_eyes_cat:"] = "\U0001f63b",
- [":heartbeat:"] = "\U0001f493",
- [":heartpulse:"] = "\U0001f497",
- [":hearts:"] = "\u2665\ufe0f",
- [":heavy_check_mark:"] = "\u2714\ufe0f",
- [":heavy_division_sign:"] = "\u2797",
- [":heavy_dollar_sign:"] = "\U0001f4b2",
- [":heavy_minus_sign:"] = "\u2796",
- [":heavy_multiplication_x:"] = "\u2716\ufe0f",
- [":heavy_plus_sign:"] = "\u2795",
- [":hedgehog:"] = "\U0001f994",
- [":helicopter:"] = "\U0001f681",
- [":helmet_with_cross:"] = "\u26d1\ufe0f",
- [":helmet_with_white_cross:"] = "\u26d1\ufe0f",
- [":herb:"] = "\U0001f33f",
- [":hibiscus:"] = "\U0001f33a",
- [":high_brightness:"] = "\U0001f506",
- [":high_heel:"] = "\U0001f460",
- [":hiking_boot:"] = "\U0001f97e",
- [":hindu_temple:"] = "\U0001f6d5",
- [":hippopotamus:"] = "\U0001f99b",
- [":hockey:"] = "\U0001f3d2",
- [":hole:"] = "\U0001f573\ufe0f",
- [":homes:"] = "\U0001f3d8\ufe0f",
- [":house_buildings:"] = "\U0001f3d8\ufe0f",
- [":honey_pot:"] = "\U0001f36f",
- [":hook:"] = "\U0001fa9d",
- [":horse:"] = "\U0001f434",
- [":horse_racing:"] = "\U0001f3c7",
- [":horse_racing_tone1:"] = "\U0001f3c7\U0001f3fb",
- [":horse_racing::skin-tone-1:"] = "\U0001f3c7\U0001f3fb",
- [":horse_racing_tone2:"] = "\U0001f3c7\U0001f3fc",
- [":horse_racing::skin-tone-2:"] = "\U0001f3c7\U0001f3fc",
- [":horse_racing_tone3:"] = "\U0001f3c7\U0001f3fd",
- [":horse_racing::skin-tone-3:"] = "\U0001f3c7\U0001f3fd",
- [":horse_racing_tone4:"] = "\U0001f3c7\U0001f3fe",
- [":horse_racing::skin-tone-4:"] = "\U0001f3c7\U0001f3fe",
- [":horse_racing_tone5:"] = "\U0001f3c7\U0001f3ff",
- [":horse_racing::skin-tone-5:"] = "\U0001f3c7\U0001f3ff",
- [":hospital:"] = "\U0001f3e5",
- [":hot_face:"] = "\U0001f975",
- [":hot_pepper:"] = "\U0001f336\ufe0f",
- [":hotdog:"] = "\U0001f32d",
- [":hot_dog:"] = "\U0001f32d",
- [":hotel:"] = "\U0001f3e8",
- [":hotsprings:"] = "\u2668\ufe0f",
- [":hourglass:"] = "\u231b",
- [":hourglass_flowing_sand:"] = "\u23f3",
- [":house:"] = "\U0001f3e0",
- [":house_abandoned:"] = "\U0001f3da\ufe0f",
- [":derelict_house_building:"] = "\U0001f3da\ufe0f",
- [":house_with_garden:"] = "\U0001f3e1",
- [":hugging:"] = "\U0001f917",
- [":hugging_face:"] = "\U0001f917",
- [":hushed:"] = "\U0001f62f",
- [":hut:"] = "\U0001f6d6",
- [":ice_cream:"] = "\U0001f368",
- [":ice_cube:"] = "\U0001f9ca",
- [":ice_skate:"] = "\u26f8\ufe0f",
- [":icecream:"] = "\U0001f366",
- [":id:"] = "\U0001f194",
- [":ideograph_advantage:"] = "\U0001f250",
- [":imp:"] = "\U0001f47f",
- ["]:("] = "\U0001f47f",
- ["]:-("] = "\U0001f47f",
- ["]=("] = "\U0001f47f",
- ["]=-("] = "\U0001f47f",
- [":inbox_tray:"] = "\U0001f4e5",
- [":incoming_envelope:"] = "\U0001f4e8",
- [":infinity:"] = "\u267e\ufe0f",
- [":information_source:"] = "\u2139\ufe0f",
- [":innocent:"] = "\U0001f607",
- ["o:)"] = "\U0001f607",
- ["O:)"] = "\U0001f607",
- ["o:-)"] = "\U0001f607",
- ["O:-)"] = "\U0001f607",
- ["0:)"] = "\U0001f607",
- ["0:-)"] = "\U0001f607",
- ["o=)"] = "\U0001f607",
- ["O=)"] = "\U0001f607",
- ["o=-)"] = "\U0001f607",
- ["O=-)"] = "\U0001f607",
- ["0=)"] = "\U0001f607",
- ["0=-)"] = "\U0001f607",
- [":interrobang:"] = "\u2049\ufe0f",
- [":island:"] = "\U0001f3dd\ufe0f",
- [":desert_island:"] = "\U0001f3dd\ufe0f",
- [":izakaya_lantern:"] = "\U0001f3ee",
- [":jack_o_lantern:"] = "\U0001f383",
- [":japan:"] = "\U0001f5fe",
- [":japanese_castle:"] = "\U0001f3ef",
- [":japanese_goblin:"] = "\U0001f47a",
- [":japanese_ogre:"] = "\U0001f479",
- [":jeans:"] = "\U0001f456",
- [":jigsaw:"] = "\U0001f9e9",
- [":joy:"] = "\U0001f602",
- [":')"] = "\U0001f602",
- [":'-)"] = "\U0001f602",
- [":,)"] = "\U0001f602",
- [":,-)"] = "\U0001f602",
- [":'D"] = "\U0001f602",
- [":'-D"] = "\U0001f602",
- [":,D"] = "\U0001f602",
- [":,-D"] = "\U0001f602",
- ["=')"] = "\U0001f602",
- ["='-)"] = "\U0001f602",
- ["=,)"] = "\U0001f602",
- ["=,-)"] = "\U0001f602",
- ["='D"] = "\U0001f602",
- ["='-D"] = "\U0001f602",
- ["=,D"] = "\U0001f602",
- ["=,-D"] = "\U0001f602",
- [":joy_cat:"] = "\U0001f639",
- [":joystick:"] = "\U0001f579\ufe0f",
- [":judge:"] = "\U0001f9d1\u200d\u2696\ufe0f",
- [":judge_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f",
- [":judge_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f",
- [":judge::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f",
- [":judge_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f",
- [":judge_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f",
- [":judge::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f",
- [":judge_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f",
- [":judge_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f",
- [":judge::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f",
- [":judge_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f",
- [":judge_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f",
- [":judge::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f",
- [":judge_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f",
- [":judge_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f",
- [":judge::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f",
- [":kaaba:"] = "\U0001f54b",
- [":kangaroo:"] = "\U0001f998",
- [":key:"] = "\U0001f511",
- [":key2:"] = "\U0001f5dd\ufe0f",
- [":old_key:"] = "\U0001f5dd\ufe0f",
- [":keyboard:"] = "\u2328\ufe0f",
- [":keycap_ten:"] = "\U0001f51f",
- [":kimono:"] = "\U0001f458",
- [":kiss:"] = "\U0001f48b",
- [":kiss_mm:"] = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468",
- [":couplekiss_mm:"] = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468",
- [":kiss_woman_man:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468",
- [":kiss_ww:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469",
- [":couplekiss_ww:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469",
- [":kissing:"] = "\U0001f617",
- [":*"] = "\U0001f617",
- [":-*"] = "\U0001f617",
- ["=*"] = "\U0001f617",
- ["=-*"] = "\U0001f617",
- [":kissing_cat:"] = "\U0001f63d",
- [":kissing_closed_eyes:"] = "\U0001f61a",
- [":kissing_heart:"] = "\U0001f618",
- [":kissing_smiling_eyes:"] = "\U0001f619",
- [":kite:"] = "\U0001fa81",
- [":kiwi:"] = "\U0001f95d",
- [":kiwifruit:"] = "\U0001f95d",
- [":knife:"] = "\U0001f52a",
- [":knot:"] = "\U0001faa2",
- [":koala:"] = "\U0001f428",
- [":koko:"] = "\U0001f201",
- [":lab_coat:"] = "\U0001f97c",
- [":label:"] = "\U0001f3f7\ufe0f",
- [":lacrosse:"] = "\U0001f94d",
- [":ladder:"] = "\U0001fa9c",
- [":lady_beetle:"] = "\U0001f41e",
- [":large_blue_diamond:"] = "\U0001f537",
- [":large_orange_diamond:"] = "\U0001f536",
- [":last_quarter_moon:"] = "\U0001f317",
- [":last_quarter_moon_with_face:"] = "\U0001f31c",
- [":laughing:"] = "\U0001f606",
- [":satisfied:"] = "\U0001f606",
- ["x-)"] = "\U0001f606",
- ["X-)"] = "\U0001f606",
- [":leafy_green:"] = "\U0001f96c",
- [":leaves:"] = "\U0001f343",
- [":ledger:"] = "\U0001f4d2",
- [":left_facing_fist:"] = "\U0001f91b",
- [":left_fist:"] = "\U0001f91b",
- [":left_facing_fist_tone1:"] = "\U0001f91b\U0001f3fb",
- [":left_fist_tone1:"] = "\U0001f91b\U0001f3fb",
- [":left_facing_fist::skin-tone-1:"] = "\U0001f91b\U0001f3fb",
- [":left_fist::skin-tone-1:"] = "\U0001f91b\U0001f3fb",
- [":left_facing_fist_tone2:"] = "\U0001f91b\U0001f3fc",
- [":left_fist_tone2:"] = "\U0001f91b\U0001f3fc",
- [":left_facing_fist::skin-tone-2:"] = "\U0001f91b\U0001f3fc",
- [":left_fist::skin-tone-2:"] = "\U0001f91b\U0001f3fc",
- [":left_facing_fist_tone3:"] = "\U0001f91b\U0001f3fd",
- [":left_fist_tone3:"] = "\U0001f91b\U0001f3fd",
- [":left_facing_fist::skin-tone-3:"] = "\U0001f91b\U0001f3fd",
- [":left_fist::skin-tone-3:"] = "\U0001f91b\U0001f3fd",
- [":left_facing_fist_tone4:"] = "\U0001f91b\U0001f3fe",
- [":left_fist_tone4:"] = "\U0001f91b\U0001f3fe",
- [":left_facing_fist::skin-tone-4:"] = "\U0001f91b\U0001f3fe",
- [":left_fist::skin-tone-4:"] = "\U0001f91b\U0001f3fe",
- [":left_facing_fist_tone5:"] = "\U0001f91b\U0001f3ff",
- [":left_fist_tone5:"] = "\U0001f91b\U0001f3ff",
- [":left_facing_fist::skin-tone-5:"] = "\U0001f91b\U0001f3ff",
- [":left_fist::skin-tone-5:"] = "\U0001f91b\U0001f3ff",
- [":left_luggage:"] = "\U0001f6c5",
- [":left_right_arrow:"] = "\u2194\ufe0f",
- [":leftwards_arrow_with_hook:"] = "\u21a9\ufe0f",
- [":leg:"] = "\U0001f9b5",
- [":leg_tone1:"] = "\U0001f9b5\U0001f3fb",
- [":leg_light_skin_tone:"] = "\U0001f9b5\U0001f3fb",
- [":leg::skin-tone-1:"] = "\U0001f9b5\U0001f3fb",
- [":leg_tone2:"] = "\U0001f9b5\U0001f3fc",
- [":leg_medium_light_skin_tone:"] = "\U0001f9b5\U0001f3fc",
- [":leg::skin-tone-2:"] = "\U0001f9b5\U0001f3fc",
- [":leg_tone3:"] = "\U0001f9b5\U0001f3fd",
- [":leg_medium_skin_tone:"] = "\U0001f9b5\U0001f3fd",
- [":leg::skin-tone-3:"] = "\U0001f9b5\U0001f3fd",
- [":leg_tone4:"] = "\U0001f9b5\U0001f3fe",
- [":leg_medium_dark_skin_tone:"] = "\U0001f9b5\U0001f3fe",
- [":leg::skin-tone-4:"] = "\U0001f9b5\U0001f3fe",
- [":leg_tone5:"] = "\U0001f9b5\U0001f3ff",
- [":leg_dark_skin_tone:"] = "\U0001f9b5\U0001f3ff",
- [":leg::skin-tone-5:"] = "\U0001f9b5\U0001f3ff",
- [":lemon:"] = "\U0001f34b",
- [":leo:"] = "\u264c",
- [":leopard:"] = "\U0001f406",
- [":level_slider:"] = "\U0001f39a\ufe0f",
- [":levitate:"] = "\U0001f574\ufe0f",
- [":man_in_business_suit_levitating:"] = "\U0001f574\ufe0f",
- [":levitate_tone1:"] = "\U0001f574\U0001f3fb",
- [":man_in_business_suit_levitating_tone1:"] = "\U0001f574\U0001f3fb",
- [":man_in_business_suit_levitating_light_skin_tone:"] = "\U0001f574\U0001f3fb",
- [":levitate::skin-tone-1:"] = "\U0001f574\U0001f3fb",
- [":man_in_business_suit_levitating::skin-tone-1:"] = "\U0001f574\U0001f3fb",
- [":levitate_tone2:"] = "\U0001f574\U0001f3fc",
- [":man_in_business_suit_levitating_tone2:"] = "\U0001f574\U0001f3fc",
- [":man_in_business_suit_levitating_medium_light_skin_tone:"] = "\U0001f574\U0001f3fc",
- [":levitate::skin-tone-2:"] = "\U0001f574\U0001f3fc",
- [":man_in_business_suit_levitating::skin-tone-2:"] = "\U0001f574\U0001f3fc",
- [":levitate_tone3:"] = "\U0001f574\U0001f3fd",
- [":man_in_business_suit_levitating_tone3:"] = "\U0001f574\U0001f3fd",
- [":man_in_business_suit_levitating_medium_skin_tone:"] = "\U0001f574\U0001f3fd",
- [":levitate::skin-tone-3:"] = "\U0001f574\U0001f3fd",
- [":man_in_business_suit_levitating::skin-tone-3:"] = "\U0001f574\U0001f3fd",
- [":levitate_tone4:"] = "\U0001f574\U0001f3fe",
- [":man_in_business_suit_levitating_tone4:"] = "\U0001f574\U0001f3fe",
- [":man_in_business_suit_levitating_medium_dark_skin_tone:"] = "\U0001f574\U0001f3fe",
- [":levitate::skin-tone-4:"] = "\U0001f574\U0001f3fe",
- [":man_in_business_suit_levitating::skin-tone-4:"] = "\U0001f574\U0001f3fe",
- [":levitate_tone5:"] = "\U0001f574\U0001f3ff",
- [":man_in_business_suit_levitating_tone5:"] = "\U0001f574\U0001f3ff",
- [":man_in_business_suit_levitating_dark_skin_tone:"] = "\U0001f574\U0001f3ff",
- [":levitate::skin-tone-5:"] = "\U0001f574\U0001f3ff",
- [":man_in_business_suit_levitating::skin-tone-5:"] = "\U0001f574\U0001f3ff",
- [":libra:"] = "\u264e",
- [":light_rail:"] = "\U0001f688",
- [":link:"] = "\U0001f517",
- [":lion_face:"] = "\U0001f981",
- [":lion:"] = "\U0001f981",
- [":lips:"] = "\U0001f444",
- [":lipstick:"] = "\U0001f484",
- [":lizard:"] = "\U0001f98e",
- [":llama:"] = "\U0001f999",
- [":lobster:"] = "\U0001f99e",
- [":lock:"] = "\U0001f512",
- [":lock_with_ink_pen:"] = "\U0001f50f",
- [":lollipop:"] = "\U0001f36d",
- [":long_drum:"] = "\U0001fa98",
- [":loop:"] = "\u27bf",
- [":loud_sound:"] = "\U0001f50a",
- [":loudspeaker:"] = "\U0001f4e2",
- [":love_hotel:"] = "\U0001f3e9",
- [":love_letter:"] = "\U0001f48c",
- [":love_you_gesture:"] = "\U0001f91f",
- [":love_you_gesture_tone1:"] = "\U0001f91f\U0001f3fb",
- [":love_you_gesture_light_skin_tone:"] = "\U0001f91f\U0001f3fb",
- [":love_you_gesture::skin-tone-1:"] = "\U0001f91f\U0001f3fb",
- [":love_you_gesture_tone2:"] = "\U0001f91f\U0001f3fc",
- [":love_you_gesture_medium_light_skin_tone:"] = "\U0001f91f\U0001f3fc",
- [":love_you_gesture::skin-tone-2:"] = "\U0001f91f\U0001f3fc",
- [":love_you_gesture_tone3:"] = "\U0001f91f\U0001f3fd",
- [":love_you_gesture_medium_skin_tone:"] = "\U0001f91f\U0001f3fd",
- [":love_you_gesture::skin-tone-3:"] = "\U0001f91f\U0001f3fd",
- [":love_you_gesture_tone4:"] = "\U0001f91f\U0001f3fe",
- [":love_you_gesture_medium_dark_skin_tone:"] = "\U0001f91f\U0001f3fe",
- [":love_you_gesture::skin-tone-4:"] = "\U0001f91f\U0001f3fe",
- [":love_you_gesture_tone5:"] = "\U0001f91f\U0001f3ff",
- [":love_you_gesture_dark_skin_tone:"] = "\U0001f91f\U0001f3ff",
- [":love_you_gesture::skin-tone-5:"] = "\U0001f91f\U0001f3ff",
- [":low_brightness:"] = "\U0001f505",
- [":luggage:"] = "\U0001f9f3",
- [":lungs:"] = "\U0001fac1",
- [":lying_face:"] = "\U0001f925",
- [":liar:"] = "\U0001f925",
- [":m:"] = "\u24c2\ufe0f",
- [":mag:"] = "\U0001f50d",
- [":mag_right:"] = "\U0001f50e",
- [":mage:"] = "\U0001f9d9",
- [":mage_tone1:"] = "\U0001f9d9\U0001f3fb",
- [":mage_light_skin_tone:"] = "\U0001f9d9\U0001f3fb",
- [":mage::skin-tone-1:"] = "\U0001f9d9\U0001f3fb",
- [":mage_tone2:"] = "\U0001f9d9\U0001f3fc",
- [":mage_medium_light_skin_tone:"] = "\U0001f9d9\U0001f3fc",
- [":mage::skin-tone-2:"] = "\U0001f9d9\U0001f3fc",
- [":mage_tone3:"] = "\U0001f9d9\U0001f3fd",
- [":mage_medium_skin_tone:"] = "\U0001f9d9\U0001f3fd",
- [":mage::skin-tone-3:"] = "\U0001f9d9\U0001f3fd",
- [":mage_tone4:"] = "\U0001f9d9\U0001f3fe",
- [":mage_medium_dark_skin_tone:"] = "\U0001f9d9\U0001f3fe",
- [":mage::skin-tone-4:"] = "\U0001f9d9\U0001f3fe",
- [":mage_tone5:"] = "\U0001f9d9\U0001f3ff",
- [":mage_dark_skin_tone:"] = "\U0001f9d9\U0001f3ff",
- [":mage::skin-tone-5:"] = "\U0001f9d9\U0001f3ff",
- [":magic_wand:"] = "\U0001fa84",
- [":magnet:"] = "\U0001f9f2",
- [":mahjong:"] = "\U0001f004",
- [":mailbox:"] = "\U0001f4eb",
- [":mailbox_closed:"] = "\U0001f4ea",
- [":mailbox_with_mail:"] = "\U0001f4ec",
- [":mailbox_with_no_mail:"] = "\U0001f4ed",
- [":male_sign:"] = "\u2642\ufe0f",
- [":mammoth:"] = "\U0001f9a3",
- [":man:"] = "\U0001f468",
- [":man_artist:"] = "\U0001f468\u200d\U0001f3a8",
- [":man_artist_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a8",
- [":man_artist_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a8",
- [":man_artist::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a8",
- [":man_artist_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a8",
- [":man_artist_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a8",
- [":man_artist::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a8",
- [":man_artist_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a8",
- [":man_artist_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a8",
- [":man_artist::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a8",
- [":man_artist_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a8",
- [":man_artist_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a8",
- [":man_artist::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a8",
- [":man_artist_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a8",
- [":man_artist_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a8",
- [":man_artist::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a8",
- [":man_astronaut:"] = "\U0001f468\u200d\U0001f680",
- [":man_astronaut_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f680",
- [":man_astronaut_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f680",
- [":man_astronaut::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f680",
- [":man_astronaut_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f680",
- [":man_astronaut_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f680",
- [":man_astronaut::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f680",
- [":man_astronaut_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f680",
- [":man_astronaut_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f680",
- [":man_astronaut::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f680",
- [":man_astronaut_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f680",
- [":man_astronaut_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f680",
- [":man_astronaut::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f680",
- [":man_astronaut_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f680",
- [":man_astronaut_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f680",
- [":man_astronaut::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f680",
- [":man_bald:"] = "\U0001f468\u200d\U0001f9b2",
- [":man_bald_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b2",
- [":man_bald_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b2",
- [":man_bald::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b2",
- [":man_bald_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b2",
- [":man_bald_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b2",
- [":man_bald::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b2",
- [":man_bald_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b2",
- [":man_bald_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b2",
- [":man_bald::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b2",
- [":man_bald_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b2",
- [":man_bald_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b2",
- [":man_bald::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b2",
- [":man_bald_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b2",
- [":man_bald_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b2",
- [":man_bald::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b2",
- [":man_biking:"] = "\U0001f6b4\u200d\u2642\ufe0f",
- [":man_biking_tone1:"] = "\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f",
- [":man_biking_light_skin_tone:"] = "\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f",
- [":man_biking::skin-tone-1:"] = "\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f",
- [":man_biking_tone2:"] = "\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f",
- [":man_biking_medium_light_skin_tone:"] = "\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f",
- [":man_biking::skin-tone-2:"] = "\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f",
- [":man_biking_tone3:"] = "\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f",
- [":man_biking_medium_skin_tone:"] = "\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f",
- [":man_biking::skin-tone-3:"] = "\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f",
- [":man_biking_tone4:"] = "\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f",
- [":man_biking_medium_dark_skin_tone:"] = "\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f",
- [":man_biking::skin-tone-4:"] = "\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f",
- [":man_biking_tone5:"] = "\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f",
- [":man_biking_dark_skin_tone:"] = "\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f",
- [":man_biking::skin-tone-5:"] = "\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f",
- [":man_bouncing_ball:"] = "\u26f9\ufe0f\u200d\u2642\ufe0f",
- [":man_bouncing_ball_tone1:"] = "\u26f9\U0001f3fb\u200d\u2642\ufe0f",
- [":man_bouncing_ball_light_skin_tone:"] = "\u26f9\U0001f3fb\u200d\u2642\ufe0f",
- [":man_bouncing_ball::skin-tone-1:"] = "\u26f9\U0001f3fb\u200d\u2642\ufe0f",
- [":man_bouncing_ball_tone2:"] = "\u26f9\U0001f3fc\u200d\u2642\ufe0f",
- [":man_bouncing_ball_medium_light_skin_tone:"] = "\u26f9\U0001f3fc\u200d\u2642\ufe0f",
- [":man_bouncing_ball::skin-tone-2:"] = "\u26f9\U0001f3fc\u200d\u2642\ufe0f",
- [":man_bouncing_ball_tone3:"] = "\u26f9\U0001f3fd\u200d\u2642\ufe0f",
- [":man_bouncing_ball_medium_skin_tone:"] = "\u26f9\U0001f3fd\u200d\u2642\ufe0f",
- [":man_bouncing_ball::skin-tone-3:"] = "\u26f9\U0001f3fd\u200d\u2642\ufe0f",
- [":man_bouncing_ball_tone4:"] = "\u26f9\U0001f3fe\u200d\u2642\ufe0f",
- [":man_bouncing_ball_medium_dark_skin_tone:"] = "\u26f9\U0001f3fe\u200d\u2642\ufe0f",
- [":man_bouncing_ball::skin-tone-4:"] = "\u26f9\U0001f3fe\u200d\u2642\ufe0f",
- [":man_bouncing_ball_tone5:"] = "\u26f9\U0001f3ff\u200d\u2642\ufe0f",
- [":man_bouncing_ball_dark_skin_tone:"] = "\u26f9\U0001f3ff\u200d\u2642\ufe0f",
- [":man_bouncing_ball::skin-tone-5:"] = "\u26f9\U0001f3ff\u200d\u2642\ufe0f",
- [":man_bowing:"] = "\U0001f647\u200d\u2642\ufe0f",
- [":man_bowing_tone1:"] = "\U0001f647\U0001f3fb\u200d\u2642\ufe0f",
- [":man_bowing_light_skin_tone:"] = "\U0001f647\U0001f3fb\u200d\u2642\ufe0f",
- [":man_bowing::skin-tone-1:"] = "\U0001f647\U0001f3fb\u200d\u2642\ufe0f",
- [":man_bowing_tone2:"] = "\U0001f647\U0001f3fc\u200d\u2642\ufe0f",
- [":man_bowing_medium_light_skin_tone:"] = "\U0001f647\U0001f3fc\u200d\u2642\ufe0f",
- [":man_bowing::skin-tone-2:"] = "\U0001f647\U0001f3fc\u200d\u2642\ufe0f",
- [":man_bowing_tone3:"] = "\U0001f647\U0001f3fd\u200d\u2642\ufe0f",
- [":man_bowing_medium_skin_tone:"] = "\U0001f647\U0001f3fd\u200d\u2642\ufe0f",
- [":man_bowing::skin-tone-3:"] = "\U0001f647\U0001f3fd\u200d\u2642\ufe0f",
- [":man_bowing_tone4:"] = "\U0001f647\U0001f3fe\u200d\u2642\ufe0f",
- [":man_bowing_medium_dark_skin_tone:"] = "\U0001f647\U0001f3fe\u200d\u2642\ufe0f",
- [":man_bowing::skin-tone-4:"] = "\U0001f647\U0001f3fe\u200d\u2642\ufe0f",
- [":man_bowing_tone5:"] = "\U0001f647\U0001f3ff\u200d\u2642\ufe0f",
- [":man_bowing_dark_skin_tone:"] = "\U0001f647\U0001f3ff\u200d\u2642\ufe0f",
- [":man_bowing::skin-tone-5:"] = "\U0001f647\U0001f3ff\u200d\u2642\ufe0f",
- [":man_cartwheeling:"] = "\U0001f938\u200d\u2642\ufe0f",
- [":man_cartwheeling_tone1:"] = "\U0001f938\U0001f3fb\u200d\u2642\ufe0f",
- [":man_cartwheeling_light_skin_tone:"] = "\U0001f938\U0001f3fb\u200d\u2642\ufe0f",
- [":man_cartwheeling::skin-tone-1:"] = "\U0001f938\U0001f3fb\u200d\u2642\ufe0f",
- [":man_cartwheeling_tone2:"] = "\U0001f938\U0001f3fc\u200d\u2642\ufe0f",
- [":man_cartwheeling_medium_light_skin_tone:"] = "\U0001f938\U0001f3fc\u200d\u2642\ufe0f",
- [":man_cartwheeling::skin-tone-2:"] = "\U0001f938\U0001f3fc\u200d\u2642\ufe0f",
- [":man_cartwheeling_tone3:"] = "\U0001f938\U0001f3fd\u200d\u2642\ufe0f",
- [":man_cartwheeling_medium_skin_tone:"] = "\U0001f938\U0001f3fd\u200d\u2642\ufe0f",
- [":man_cartwheeling::skin-tone-3:"] = "\U0001f938\U0001f3fd\u200d\u2642\ufe0f",
- [":man_cartwheeling_tone4:"] = "\U0001f938\U0001f3fe\u200d\u2642\ufe0f",
- [":man_cartwheeling_medium_dark_skin_tone:"] = "\U0001f938\U0001f3fe\u200d\u2642\ufe0f",
- [":man_cartwheeling::skin-tone-4:"] = "\U0001f938\U0001f3fe\u200d\u2642\ufe0f",
- [":man_cartwheeling_tone5:"] = "\U0001f938\U0001f3ff\u200d\u2642\ufe0f",
- [":man_cartwheeling_dark_skin_tone:"] = "\U0001f938\U0001f3ff\u200d\u2642\ufe0f",
- [":man_cartwheeling::skin-tone-5:"] = "\U0001f938\U0001f3ff\u200d\u2642\ufe0f",
- [":man_climbing:"] = "\U0001f9d7\u200d\u2642\ufe0f",
- [":man_climbing_tone1:"] = "\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f",
- [":man_climbing_light_skin_tone:"] = "\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f",
- [":man_climbing::skin-tone-1:"] = "\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f",
- [":man_climbing_tone2:"] = "\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f",
- [":man_climbing_medium_light_skin_tone:"] = "\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f",
- [":man_climbing::skin-tone-2:"] = "\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f",
- [":man_climbing_tone3:"] = "\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f",
- [":man_climbing_medium_skin_tone:"] = "\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f",
- [":man_climbing::skin-tone-3:"] = "\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f",
- [":man_climbing_tone4:"] = "\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f",
- [":man_climbing_medium_dark_skin_tone:"] = "\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f",
- [":man_climbing::skin-tone-4:"] = "\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f",
- [":man_climbing_tone5:"] = "\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f",
- [":man_climbing_dark_skin_tone:"] = "\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f",
- [":man_climbing::skin-tone-5:"] = "\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f",
- [":man_construction_worker:"] = "\U0001f477\u200d\u2642\ufe0f",
- [":man_construction_worker_tone1:"] = "\U0001f477\U0001f3fb\u200d\u2642\ufe0f",
- [":man_construction_worker_light_skin_tone:"] = "\U0001f477\U0001f3fb\u200d\u2642\ufe0f",
- [":man_construction_worker::skin-tone-1:"] = "\U0001f477\U0001f3fb\u200d\u2642\ufe0f",
- [":man_construction_worker_tone2:"] = "\U0001f477\U0001f3fc\u200d\u2642\ufe0f",
- [":man_construction_worker_medium_light_skin_tone:"] = "\U0001f477\U0001f3fc\u200d\u2642\ufe0f",
- [":man_construction_worker::skin-tone-2:"] = "\U0001f477\U0001f3fc\u200d\u2642\ufe0f",
- [":man_construction_worker_tone3:"] = "\U0001f477\U0001f3fd\u200d\u2642\ufe0f",
- [":man_construction_worker_medium_skin_tone:"] = "\U0001f477\U0001f3fd\u200d\u2642\ufe0f",
- [":man_construction_worker::skin-tone-3:"] = "\U0001f477\U0001f3fd\u200d\u2642\ufe0f",
- [":man_construction_worker_tone4:"] = "\U0001f477\U0001f3fe\u200d\u2642\ufe0f",
- [":man_construction_worker_medium_dark_skin_tone:"] = "\U0001f477\U0001f3fe\u200d\u2642\ufe0f",
- [":man_construction_worker::skin-tone-4:"] = "\U0001f477\U0001f3fe\u200d\u2642\ufe0f",
- [":man_construction_worker_tone5:"] = "\U0001f477\U0001f3ff\u200d\u2642\ufe0f",
- [":man_construction_worker_dark_skin_tone:"] = "\U0001f477\U0001f3ff\u200d\u2642\ufe0f",
- [":man_construction_worker::skin-tone-5:"] = "\U0001f477\U0001f3ff\u200d\u2642\ufe0f",
- [":man_cook:"] = "\U0001f468\u200d\U0001f373",
- [":man_cook_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f373",
- [":man_cook_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f373",
- [":man_cook::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f373",
- [":man_cook_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f373",
- [":man_cook_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f373",
- [":man_cook::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f373",
- [":man_cook_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f373",
- [":man_cook_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f373",
- [":man_cook::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f373",
- [":man_cook_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f373",
- [":man_cook_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f373",
- [":man_cook::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f373",
- [":man_cook_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f373",
- [":man_cook_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f373",
- [":man_cook::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f373",
- [":man_curly_haired:"] = "\U0001f468\u200d\U0001f9b1",
- [":man_curly_haired_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b1",
- [":man_curly_haired_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b1",
- [":man_curly_haired::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b1",
- [":man_curly_haired_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b1",
- [":man_curly_haired_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b1",
- [":man_curly_haired::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b1",
- [":man_curly_haired_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b1",
- [":man_curly_haired_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b1",
- [":man_curly_haired::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b1",
- [":man_curly_haired_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b1",
- [":man_curly_haired_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b1",
- [":man_curly_haired::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b1",
- [":man_curly_haired_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b1",
- [":man_curly_haired_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b1",
- [":man_curly_haired::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b1",
- [":man_dancing:"] = "\U0001f57a",
- [":male_dancer:"] = "\U0001f57a",
- [":man_dancing_tone1:"] = "\U0001f57a\U0001f3fb",
- [":male_dancer_tone1:"] = "\U0001f57a\U0001f3fb",
- [":man_dancing::skin-tone-1:"] = "\U0001f57a\U0001f3fb",
- [":male_dancer::skin-tone-1:"] = "\U0001f57a\U0001f3fb",
- [":man_dancing_tone2:"] = "\U0001f57a\U0001f3fc",
- [":male_dancer_tone2:"] = "\U0001f57a\U0001f3fc",
- [":man_dancing::skin-tone-2:"] = "\U0001f57a\U0001f3fc",
- [":male_dancer::skin-tone-2:"] = "\U0001f57a\U0001f3fc",
- [":man_dancing_tone3:"] = "\U0001f57a\U0001f3fd",
- [":male_dancer_tone3:"] = "\U0001f57a\U0001f3fd",
- [":man_dancing::skin-tone-3:"] = "\U0001f57a\U0001f3fd",
- [":male_dancer::skin-tone-3:"] = "\U0001f57a\U0001f3fd",
- [":man_dancing_tone4:"] = "\U0001f57a\U0001f3fe",
- [":male_dancer_tone4:"] = "\U0001f57a\U0001f3fe",
- [":man_dancing::skin-tone-4:"] = "\U0001f57a\U0001f3fe",
- [":male_dancer::skin-tone-4:"] = "\U0001f57a\U0001f3fe",
- [":man_dancing_tone5:"] = "\U0001f57a\U0001f3ff",
- [":male_dancer_tone5:"] = "\U0001f57a\U0001f3ff",
- [":man_dancing::skin-tone-5:"] = "\U0001f57a\U0001f3ff",
- [":male_dancer::skin-tone-5:"] = "\U0001f57a\U0001f3ff",
- [":man_detective:"] = "\U0001f575\ufe0f\u200d\u2642\ufe0f",
- [":man_detective_tone1:"] = "\U0001f575\U0001f3fb\u200d\u2642\ufe0f",
- [":man_detective_light_skin_tone:"] = "\U0001f575\U0001f3fb\u200d\u2642\ufe0f",
- [":man_detective::skin-tone-1:"] = "\U0001f575\U0001f3fb\u200d\u2642\ufe0f",
- [":man_detective_tone2:"] = "\U0001f575\U0001f3fc\u200d\u2642\ufe0f",
- [":man_detective_medium_light_skin_tone:"] = "\U0001f575\U0001f3fc\u200d\u2642\ufe0f",
- [":man_detective::skin-tone-2:"] = "\U0001f575\U0001f3fc\u200d\u2642\ufe0f",
- [":man_detective_tone3:"] = "\U0001f575\U0001f3fd\u200d\u2642\ufe0f",
- [":man_detective_medium_skin_tone:"] = "\U0001f575\U0001f3fd\u200d\u2642\ufe0f",
- [":man_detective::skin-tone-3:"] = "\U0001f575\U0001f3fd\u200d\u2642\ufe0f",
- [":man_detective_tone4:"] = "\U0001f575\U0001f3fe\u200d\u2642\ufe0f",
- [":man_detective_medium_dark_skin_tone:"] = "\U0001f575\U0001f3fe\u200d\u2642\ufe0f",
- [":man_detective::skin-tone-4:"] = "\U0001f575\U0001f3fe\u200d\u2642\ufe0f",
- [":man_detective_tone5:"] = "\U0001f575\U0001f3ff\u200d\u2642\ufe0f",
- [":man_detective_dark_skin_tone:"] = "\U0001f575\U0001f3ff\u200d\u2642\ufe0f",
- [":man_detective::skin-tone-5:"] = "\U0001f575\U0001f3ff\u200d\u2642\ufe0f",
- [":man_elf:"] = "\U0001f9dd\u200d\u2642\ufe0f",
- [":man_elf_tone1:"] = "\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f",
- [":man_elf_light_skin_tone:"] = "\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f",
- [":man_elf::skin-tone-1:"] = "\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f",
- [":man_elf_tone2:"] = "\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f",
- [":man_elf_medium_light_skin_tone:"] = "\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f",
- [":man_elf::skin-tone-2:"] = "\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f",
- [":man_elf_tone3:"] = "\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f",
- [":man_elf_medium_skin_tone:"] = "\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f",
- [":man_elf::skin-tone-3:"] = "\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f",
- [":man_elf_tone4:"] = "\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f",
- [":man_elf_medium_dark_skin_tone:"] = "\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f",
- [":man_elf::skin-tone-4:"] = "\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f",
- [":man_elf_tone5:"] = "\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f",
- [":man_elf_dark_skin_tone:"] = "\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f",
- [":man_elf::skin-tone-5:"] = "\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f",
- [":man_facepalming:"] = "\U0001f926\u200d\u2642\ufe0f",
- [":man_facepalming_tone1:"] = "\U0001f926\U0001f3fb\u200d\u2642\ufe0f",
- [":man_facepalming_light_skin_tone:"] = "\U0001f926\U0001f3fb\u200d\u2642\ufe0f",
- [":man_facepalming::skin-tone-1:"] = "\U0001f926\U0001f3fb\u200d\u2642\ufe0f",
- [":man_facepalming_tone2:"] = "\U0001f926\U0001f3fc\u200d\u2642\ufe0f",
- [":man_facepalming_medium_light_skin_tone:"] = "\U0001f926\U0001f3fc\u200d\u2642\ufe0f",
- [":man_facepalming::skin-tone-2:"] = "\U0001f926\U0001f3fc\u200d\u2642\ufe0f",
- [":man_facepalming_tone3:"] = "\U0001f926\U0001f3fd\u200d\u2642\ufe0f",
- [":man_facepalming_medium_skin_tone:"] = "\U0001f926\U0001f3fd\u200d\u2642\ufe0f",
- [":man_facepalming::skin-tone-3:"] = "\U0001f926\U0001f3fd\u200d\u2642\ufe0f",
- [":man_facepalming_tone4:"] = "\U0001f926\U0001f3fe\u200d\u2642\ufe0f",
- [":man_facepalming_medium_dark_skin_tone:"] = "\U0001f926\U0001f3fe\u200d\u2642\ufe0f",
- [":man_facepalming::skin-tone-4:"] = "\U0001f926\U0001f3fe\u200d\u2642\ufe0f",
- [":man_facepalming_tone5:"] = "\U0001f926\U0001f3ff\u200d\u2642\ufe0f",
- [":man_facepalming_dark_skin_tone:"] = "\U0001f926\U0001f3ff\u200d\u2642\ufe0f",
- [":man_facepalming::skin-tone-5:"] = "\U0001f926\U0001f3ff\u200d\u2642\ufe0f",
- [":man_factory_worker:"] = "\U0001f468\u200d\U0001f3ed",
- [":man_factory_worker_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3ed",
- [":man_factory_worker_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f3ed",
- [":man_factory_worker::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3ed",
- [":man_factory_worker_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3ed",
- [":man_factory_worker_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f3ed",
- [":man_factory_worker::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3ed",
- [":man_factory_worker_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3ed",
- [":man_factory_worker_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f3ed",
- [":man_factory_worker::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3ed",
- [":man_factory_worker_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3ed",
- [":man_factory_worker_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f3ed",
- [":man_factory_worker::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3ed",
- [":man_factory_worker_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3ed",
- [":man_factory_worker_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f3ed",
- [":man_factory_worker::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3ed",
- [":man_fairy:"] = "\U0001f9da\u200d\u2642\ufe0f",
- [":man_fairy_tone1:"] = "\U0001f9da\U0001f3fb\u200d\u2642\ufe0f",
- [":man_fairy_light_skin_tone:"] = "\U0001f9da\U0001f3fb\u200d\u2642\ufe0f",
- [":man_fairy::skin-tone-1:"] = "\U0001f9da\U0001f3fb\u200d\u2642\ufe0f",
- [":man_fairy_tone2:"] = "\U0001f9da\U0001f3fc\u200d\u2642\ufe0f",
- [":man_fairy_medium_light_skin_tone:"] = "\U0001f9da\U0001f3fc\u200d\u2642\ufe0f",
- [":man_fairy::skin-tone-2:"] = "\U0001f9da\U0001f3fc\u200d\u2642\ufe0f",
- [":man_fairy_tone3:"] = "\U0001f9da\U0001f3fd\u200d\u2642\ufe0f",
- [":man_fairy_medium_skin_tone:"] = "\U0001f9da\U0001f3fd\u200d\u2642\ufe0f",
- [":man_fairy::skin-tone-3:"] = "\U0001f9da\U0001f3fd\u200d\u2642\ufe0f",
- [":man_fairy_tone4:"] = "\U0001f9da\U0001f3fe\u200d\u2642\ufe0f",
- [":man_fairy_medium_dark_skin_tone:"] = "\U0001f9da\U0001f3fe\u200d\u2642\ufe0f",
- [":man_fairy::skin-tone-4:"] = "\U0001f9da\U0001f3fe\u200d\u2642\ufe0f",
- [":man_fairy_tone5:"] = "\U0001f9da\U0001f3ff\u200d\u2642\ufe0f",
- [":man_fairy_dark_skin_tone:"] = "\U0001f9da\U0001f3ff\u200d\u2642\ufe0f",
- [":man_fairy::skin-tone-5:"] = "\U0001f9da\U0001f3ff\u200d\u2642\ufe0f",
- [":man_farmer:"] = "\U0001f468\u200d\U0001f33e",
- [":man_farmer_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f33e",
- [":man_farmer_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f33e",
- [":man_farmer::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f33e",
- [":man_farmer_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f33e",
- [":man_farmer_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f33e",
- [":man_farmer::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f33e",
- [":man_farmer_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f33e",
- [":man_farmer_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f33e",
- [":man_farmer::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f33e",
- [":man_farmer_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f33e",
- [":man_farmer_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f33e",
- [":man_farmer::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f33e",
- [":man_farmer_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f33e",
- [":man_farmer_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f33e",
- [":man_farmer::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f33e",
- [":man_feeding_baby:"] = "\U0001f468\u200d\U0001f37c",
- [":man_feeding_baby_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f37c",
- [":man_feeding_baby_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f37c",
- [":man_feeding_baby::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f37c",
- [":man_feeding_baby_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f37c",
- [":man_feeding_baby_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f37c",
- [":man_feeding_baby::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f37c",
- [":man_feeding_baby_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f37c",
- [":man_feeding_baby_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f37c",
- [":man_feeding_baby::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f37c",
- [":man_feeding_baby_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f37c",
- [":man_feeding_baby_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f37c",
- [":man_feeding_baby::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f37c",
- [":man_feeding_baby_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f37c",
- [":man_feeding_baby_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f37c",
- [":man_feeding_baby::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f37c",
- [":man_firefighter:"] = "\U0001f468\u200d\U0001f692",
- [":man_firefighter_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f692",
- [":man_firefighter_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f692",
- [":man_firefighter::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f692",
- [":man_firefighter_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f692",
- [":man_firefighter_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f692",
- [":man_firefighter::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f692",
- [":man_firefighter_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f692",
- [":man_firefighter_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f692",
- [":man_firefighter::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f692",
- [":man_firefighter_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f692",
- [":man_firefighter_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f692",
- [":man_firefighter::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f692",
- [":man_firefighter_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f692",
- [":man_firefighter_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f692",
- [":man_firefighter::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f692",
- [":man_frowning:"] = "\U0001f64d\u200d\u2642\ufe0f",
- [":man_frowning_tone1:"] = "\U0001f64d\U0001f3fb\u200d\u2642\ufe0f",
- [":man_frowning_light_skin_tone:"] = "\U0001f64d\U0001f3fb\u200d\u2642\ufe0f",
- [":man_frowning::skin-tone-1:"] = "\U0001f64d\U0001f3fb\u200d\u2642\ufe0f",
- [":man_frowning_tone2:"] = "\U0001f64d\U0001f3fc\u200d\u2642\ufe0f",
- [":man_frowning_medium_light_skin_tone:"] = "\U0001f64d\U0001f3fc\u200d\u2642\ufe0f",
- [":man_frowning::skin-tone-2:"] = "\U0001f64d\U0001f3fc\u200d\u2642\ufe0f",
- [":man_frowning_tone3:"] = "\U0001f64d\U0001f3fd\u200d\u2642\ufe0f",
- [":man_frowning_medium_skin_tone:"] = "\U0001f64d\U0001f3fd\u200d\u2642\ufe0f",
- [":man_frowning::skin-tone-3:"] = "\U0001f64d\U0001f3fd\u200d\u2642\ufe0f",
- [":man_frowning_tone4:"] = "\U0001f64d\U0001f3fe\u200d\u2642\ufe0f",
- [":man_frowning_medium_dark_skin_tone:"] = "\U0001f64d\U0001f3fe\u200d\u2642\ufe0f",
- [":man_frowning::skin-tone-4:"] = "\U0001f64d\U0001f3fe\u200d\u2642\ufe0f",
- [":man_frowning_tone5:"] = "\U0001f64d\U0001f3ff\u200d\u2642\ufe0f",
- [":man_frowning_dark_skin_tone:"] = "\U0001f64d\U0001f3ff\u200d\u2642\ufe0f",
- [":man_frowning::skin-tone-5:"] = "\U0001f64d\U0001f3ff\u200d\u2642\ufe0f",
- [":man_genie:"] = "\U0001f9de\u200d\u2642\ufe0f",
- [":man_gesturing_no:"] = "\U0001f645\u200d\u2642\ufe0f",
- [":man_gesturing_no_tone1:"] = "\U0001f645\U0001f3fb\u200d\u2642\ufe0f",
- [":man_gesturing_no_light_skin_tone:"] = "\U0001f645\U0001f3fb\u200d\u2642\ufe0f",
- [":man_gesturing_no::skin-tone-1:"] = "\U0001f645\U0001f3fb\u200d\u2642\ufe0f",
- [":man_gesturing_no_tone2:"] = "\U0001f645\U0001f3fc\u200d\u2642\ufe0f",
- [":man_gesturing_no_medium_light_skin_tone:"] = "\U0001f645\U0001f3fc\u200d\u2642\ufe0f",
- [":man_gesturing_no::skin-tone-2:"] = "\U0001f645\U0001f3fc\u200d\u2642\ufe0f",
- [":man_gesturing_no_tone3:"] = "\U0001f645\U0001f3fd\u200d\u2642\ufe0f",
- [":man_gesturing_no_medium_skin_tone:"] = "\U0001f645\U0001f3fd\u200d\u2642\ufe0f",
- [":man_gesturing_no::skin-tone-3:"] = "\U0001f645\U0001f3fd\u200d\u2642\ufe0f",
- [":man_gesturing_no_tone4:"] = "\U0001f645\U0001f3fe\u200d\u2642\ufe0f",
- [":man_gesturing_no_medium_dark_skin_tone:"] = "\U0001f645\U0001f3fe\u200d\u2642\ufe0f",
- [":man_gesturing_no::skin-tone-4:"] = "\U0001f645\U0001f3fe\u200d\u2642\ufe0f",
- [":man_gesturing_no_tone5:"] = "\U0001f645\U0001f3ff\u200d\u2642\ufe0f",
- [":man_gesturing_no_dark_skin_tone:"] = "\U0001f645\U0001f3ff\u200d\u2642\ufe0f",
- [":man_gesturing_no::skin-tone-5:"] = "\U0001f645\U0001f3ff\u200d\u2642\ufe0f",
- [":man_gesturing_ok:"] = "\U0001f646\u200d\u2642\ufe0f",
- [":man_gesturing_ok_tone1:"] = "\U0001f646\U0001f3fb\u200d\u2642\ufe0f",
- [":man_gesturing_ok_light_skin_tone:"] = "\U0001f646\U0001f3fb\u200d\u2642\ufe0f",
- [":man_gesturing_ok::skin-tone-1:"] = "\U0001f646\U0001f3fb\u200d\u2642\ufe0f",
- [":man_gesturing_ok_tone2:"] = "\U0001f646\U0001f3fc\u200d\u2642\ufe0f",
- [":man_gesturing_ok_medium_light_skin_tone:"] = "\U0001f646\U0001f3fc\u200d\u2642\ufe0f",
- [":man_gesturing_ok::skin-tone-2:"] = "\U0001f646\U0001f3fc\u200d\u2642\ufe0f",
- [":man_gesturing_ok_tone3:"] = "\U0001f646\U0001f3fd\u200d\u2642\ufe0f",
- [":man_gesturing_ok_medium_skin_tone:"] = "\U0001f646\U0001f3fd\u200d\u2642\ufe0f",
- [":man_gesturing_ok::skin-tone-3:"] = "\U0001f646\U0001f3fd\u200d\u2642\ufe0f",
- [":man_gesturing_ok_tone4:"] = "\U0001f646\U0001f3fe\u200d\u2642\ufe0f",
- [":man_gesturing_ok_medium_dark_skin_tone:"] = "\U0001f646\U0001f3fe\u200d\u2642\ufe0f",
- [":man_gesturing_ok::skin-tone-4:"] = "\U0001f646\U0001f3fe\u200d\u2642\ufe0f",
- [":man_gesturing_ok_tone5:"] = "\U0001f646\U0001f3ff\u200d\u2642\ufe0f",
- [":man_gesturing_ok_dark_skin_tone:"] = "\U0001f646\U0001f3ff\u200d\u2642\ufe0f",
- [":man_gesturing_ok::skin-tone-5:"] = "\U0001f646\U0001f3ff\u200d\u2642\ufe0f",
- [":man_getting_face_massage:"] = "\U0001f486\u200d\u2642\ufe0f",
- [":man_getting_face_massage_tone1:"] = "\U0001f486\U0001f3fb\u200d\u2642\ufe0f",
- [":man_getting_face_massage_light_skin_tone:"] = "\U0001f486\U0001f3fb\u200d\u2642\ufe0f",
- [":man_getting_face_massage::skin-tone-1:"] = "\U0001f486\U0001f3fb\u200d\u2642\ufe0f",
- [":man_getting_face_massage_tone2:"] = "\U0001f486\U0001f3fc\u200d\u2642\ufe0f",
- [":man_getting_face_massage_medium_light_skin_tone:"] = "\U0001f486\U0001f3fc\u200d\u2642\ufe0f",
- [":man_getting_face_massage::skin-tone-2:"] = "\U0001f486\U0001f3fc\u200d\u2642\ufe0f",
- [":man_getting_face_massage_tone3:"] = "\U0001f486\U0001f3fd\u200d\u2642\ufe0f",
- [":man_getting_face_massage_medium_skin_tone:"] = "\U0001f486\U0001f3fd\u200d\u2642\ufe0f",
- [":man_getting_face_massage::skin-tone-3:"] = "\U0001f486\U0001f3fd\u200d\u2642\ufe0f",
- [":man_getting_face_massage_tone4:"] = "\U0001f486\U0001f3fe\u200d\u2642\ufe0f",
- [":man_getting_face_massage_medium_dark_skin_tone:"] = "\U0001f486\U0001f3fe\u200d\u2642\ufe0f",
- [":man_getting_face_massage::skin-tone-4:"] = "\U0001f486\U0001f3fe\u200d\u2642\ufe0f",
- [":man_getting_face_massage_tone5:"] = "\U0001f486\U0001f3ff\u200d\u2642\ufe0f",
- [":man_getting_face_massage_dark_skin_tone:"] = "\U0001f486\U0001f3ff\u200d\u2642\ufe0f",
- [":man_getting_face_massage::skin-tone-5:"] = "\U0001f486\U0001f3ff\u200d\u2642\ufe0f",
- [":man_getting_haircut:"] = "\U0001f487\u200d\u2642\ufe0f",
- [":man_getting_haircut_tone1:"] = "\U0001f487\U0001f3fb\u200d\u2642\ufe0f",
- [":man_getting_haircut_light_skin_tone:"] = "\U0001f487\U0001f3fb\u200d\u2642\ufe0f",
- [":man_getting_haircut::skin-tone-1:"] = "\U0001f487\U0001f3fb\u200d\u2642\ufe0f",
- [":man_getting_haircut_tone2:"] = "\U0001f487\U0001f3fc\u200d\u2642\ufe0f",
- [":man_getting_haircut_medium_light_skin_tone:"] = "\U0001f487\U0001f3fc\u200d\u2642\ufe0f",
- [":man_getting_haircut::skin-tone-2:"] = "\U0001f487\U0001f3fc\u200d\u2642\ufe0f",
- [":man_getting_haircut_tone3:"] = "\U0001f487\U0001f3fd\u200d\u2642\ufe0f",
- [":man_getting_haircut_medium_skin_tone:"] = "\U0001f487\U0001f3fd\u200d\u2642\ufe0f",
- [":man_getting_haircut::skin-tone-3:"] = "\U0001f487\U0001f3fd\u200d\u2642\ufe0f",
- [":man_getting_haircut_tone4:"] = "\U0001f487\U0001f3fe\u200d\u2642\ufe0f",
- [":man_getting_haircut_medium_dark_skin_tone:"] = "\U0001f487\U0001f3fe\u200d\u2642\ufe0f",
- [":man_getting_haircut::skin-tone-4:"] = "\U0001f487\U0001f3fe\u200d\u2642\ufe0f",
- [":man_getting_haircut_tone5:"] = "\U0001f487\U0001f3ff\u200d\u2642\ufe0f",
- [":man_getting_haircut_dark_skin_tone:"] = "\U0001f487\U0001f3ff\u200d\u2642\ufe0f",
- [":man_getting_haircut::skin-tone-5:"] = "\U0001f487\U0001f3ff\u200d\u2642\ufe0f",
- [":man_golfing:"] = "\U0001f3cc\ufe0f\u200d\u2642\ufe0f",
- [":man_golfing_tone1:"] = "\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f",
- [":man_golfing_light_skin_tone:"] = "\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f",
- [":man_golfing::skin-tone-1:"] = "\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f",
- [":man_golfing_tone2:"] = "\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f",
- [":man_golfing_medium_light_skin_tone:"] = "\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f",
- [":man_golfing::skin-tone-2:"] = "\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f",
- [":man_golfing_tone3:"] = "\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f",
- [":man_golfing_medium_skin_tone:"] = "\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f",
- [":man_golfing::skin-tone-3:"] = "\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f",
- [":man_golfing_tone4:"] = "\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f",
- [":man_golfing_medium_dark_skin_tone:"] = "\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f",
- [":man_golfing::skin-tone-4:"] = "\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f",
- [":man_golfing_tone5:"] = "\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f",
- [":man_golfing_dark_skin_tone:"] = "\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f",
- [":man_golfing::skin-tone-5:"] = "\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f",
- [":man_guard:"] = "\U0001f482\u200d\u2642\ufe0f",
- [":man_guard_tone1:"] = "\U0001f482\U0001f3fb\u200d\u2642\ufe0f",
- [":man_guard_light_skin_tone:"] = "\U0001f482\U0001f3fb\u200d\u2642\ufe0f",
- [":man_guard::skin-tone-1:"] = "\U0001f482\U0001f3fb\u200d\u2642\ufe0f",
- [":man_guard_tone2:"] = "\U0001f482\U0001f3fc\u200d\u2642\ufe0f",
- [":man_guard_medium_light_skin_tone:"] = "\U0001f482\U0001f3fc\u200d\u2642\ufe0f",
- [":man_guard::skin-tone-2:"] = "\U0001f482\U0001f3fc\u200d\u2642\ufe0f",
- [":man_guard_tone3:"] = "\U0001f482\U0001f3fd\u200d\u2642\ufe0f",
- [":man_guard_medium_skin_tone:"] = "\U0001f482\U0001f3fd\u200d\u2642\ufe0f",
- [":man_guard::skin-tone-3:"] = "\U0001f482\U0001f3fd\u200d\u2642\ufe0f",
- [":man_guard_tone4:"] = "\U0001f482\U0001f3fe\u200d\u2642\ufe0f",
- [":man_guard_medium_dark_skin_tone:"] = "\U0001f482\U0001f3fe\u200d\u2642\ufe0f",
- [":man_guard::skin-tone-4:"] = "\U0001f482\U0001f3fe\u200d\u2642\ufe0f",
- [":man_guard_tone5:"] = "\U0001f482\U0001f3ff\u200d\u2642\ufe0f",
- [":man_guard_dark_skin_tone:"] = "\U0001f482\U0001f3ff\u200d\u2642\ufe0f",
- [":man_guard::skin-tone-5:"] = "\U0001f482\U0001f3ff\u200d\u2642\ufe0f",
- [":man_health_worker:"] = "\U0001f468\u200d\u2695\ufe0f",
- [":man_health_worker_tone1:"] = "\U0001f468\U0001f3fb\u200d\u2695\ufe0f",
- [":man_health_worker_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\u2695\ufe0f",
- [":man_health_worker::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\u2695\ufe0f",
- [":man_health_worker_tone2:"] = "\U0001f468\U0001f3fc\u200d\u2695\ufe0f",
- [":man_health_worker_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\u2695\ufe0f",
- [":man_health_worker::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\u2695\ufe0f",
- [":man_health_worker_tone3:"] = "\U0001f468\U0001f3fd\u200d\u2695\ufe0f",
- [":man_health_worker_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\u2695\ufe0f",
- [":man_health_worker::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\u2695\ufe0f",
- [":man_health_worker_tone4:"] = "\U0001f468\U0001f3fe\u200d\u2695\ufe0f",
- [":man_health_worker_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\u2695\ufe0f",
- [":man_health_worker::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\u2695\ufe0f",
- [":man_health_worker_tone5:"] = "\U0001f468\U0001f3ff\u200d\u2695\ufe0f",
- [":man_health_worker_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\u2695\ufe0f",
- [":man_health_worker::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\u2695\ufe0f",
- [":man_in_lotus_position:"] = "\U0001f9d8\u200d\u2642\ufe0f",
- [":man_in_lotus_position_tone1:"] = "\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f",
- [":man_in_lotus_position_light_skin_tone:"] = "\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f",
- [":man_in_lotus_position::skin-tone-1:"] = "\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f",
- [":man_in_lotus_position_tone2:"] = "\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f",
- [":man_in_lotus_position_medium_light_skin_tone:"] = "\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f",
- [":man_in_lotus_position::skin-tone-2:"] = "\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f",
- [":man_in_lotus_position_tone3:"] = "\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f",
- [":man_in_lotus_position_medium_skin_tone:"] = "\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f",
- [":man_in_lotus_position::skin-tone-3:"] = "\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f",
- [":man_in_lotus_position_tone4:"] = "\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f",
- [":man_in_lotus_position_medium_dark_skin_tone:"] = "\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f",
- [":man_in_lotus_position::skin-tone-4:"] = "\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f",
- [":man_in_lotus_position_tone5:"] = "\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f",
- [":man_in_lotus_position_dark_skin_tone:"] = "\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f",
- [":man_in_lotus_position::skin-tone-5:"] = "\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f",
- [":man_in_manual_wheelchair:"] = "\U0001f468\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bd",
- [":man_in_manual_wheelchair::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bd",
- [":man_in_manual_wheelchair::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bd",
- [":man_in_manual_wheelchair::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bd",
- [":man_in_manual_wheelchair::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bd",
- [":man_in_manual_wheelchair_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bd",
- [":man_in_manual_wheelchair::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bd",
- [":man_in_motorized_wheelchair:"] = "\U0001f468\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bc",
- [":man_in_motorized_wheelchair::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bc",
- [":man_in_steamy_room:"] = "\U0001f9d6\u200d\u2642\ufe0f",
- [":man_in_steamy_room_tone1:"] = "\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f",
- [":man_in_steamy_room_light_skin_tone:"] = "\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f",
- [":man_in_steamy_room::skin-tone-1:"] = "\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f",
- [":man_in_steamy_room_tone2:"] = "\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f",
- [":man_in_steamy_room_medium_light_skin_tone:"] = "\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f",
- [":man_in_steamy_room::skin-tone-2:"] = "\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f",
- [":man_in_steamy_room_tone3:"] = "\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f",
- [":man_in_steamy_room_medium_skin_tone:"] = "\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f",
- [":man_in_steamy_room::skin-tone-3:"] = "\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f",
- [":man_in_steamy_room_tone4:"] = "\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f",
- [":man_in_steamy_room_medium_dark_skin_tone:"] = "\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f",
- [":man_in_steamy_room::skin-tone-4:"] = "\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f",
- [":man_in_steamy_room_tone5:"] = "\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f",
- [":man_in_steamy_room_dark_skin_tone:"] = "\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f",
- [":man_in_steamy_room::skin-tone-5:"] = "\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f",
- [":man_in_tuxedo:"] = "\U0001f935\u200d\u2642\ufe0f",
- [":man_in_tuxedo_tone1:"] = "\U0001f935\U0001f3fb\u200d\u2642\ufe0f",
- [":man_in_tuxedo_light_skin_tone:"] = "\U0001f935\U0001f3fb\u200d\u2642\ufe0f",
- [":man_in_tuxedo::skin-tone-1:"] = "\U0001f935\U0001f3fb\u200d\u2642\ufe0f",
- [":man_in_tuxedo_tone2:"] = "\U0001f935\U0001f3fc\u200d\u2642\ufe0f",
- [":man_in_tuxedo_medium_light_skin_tone:"] = "\U0001f935\U0001f3fc\u200d\u2642\ufe0f",
- [":man_in_tuxedo::skin-tone-2:"] = "\U0001f935\U0001f3fc\u200d\u2642\ufe0f",
- [":man_in_tuxedo_tone3:"] = "\U0001f935\U0001f3fd\u200d\u2642\ufe0f",
- [":man_in_tuxedo_medium_skin_tone:"] = "\U0001f935\U0001f3fd\u200d\u2642\ufe0f",
- [":man_in_tuxedo::skin-tone-3:"] = "\U0001f935\U0001f3fd\u200d\u2642\ufe0f",
- [":man_in_tuxedo_tone4:"] = "\U0001f935\U0001f3fe\u200d\u2642\ufe0f",
- [":man_in_tuxedo_medium_dark_skin_tone:"] = "\U0001f935\U0001f3fe\u200d\u2642\ufe0f",
- [":man_in_tuxedo::skin-tone-4:"] = "\U0001f935\U0001f3fe\u200d\u2642\ufe0f",
- [":man_in_tuxedo_tone5:"] = "\U0001f935\U0001f3ff\u200d\u2642\ufe0f",
- [":man_in_tuxedo_dark_skin_tone:"] = "\U0001f935\U0001f3ff\u200d\u2642\ufe0f",
- [":man_in_tuxedo::skin-tone-5:"] = "\U0001f935\U0001f3ff\u200d\u2642\ufe0f",
- [":man_judge:"] = "\U0001f468\u200d\u2696\ufe0f",
- [":man_judge_tone1:"] = "\U0001f468\U0001f3fb\u200d\u2696\ufe0f",
- [":man_judge_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\u2696\ufe0f",
- [":man_judge::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\u2696\ufe0f",
- [":man_judge_tone2:"] = "\U0001f468\U0001f3fc\u200d\u2696\ufe0f",
- [":man_judge_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\u2696\ufe0f",
- [":man_judge::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\u2696\ufe0f",
- [":man_judge_tone3:"] = "\U0001f468\U0001f3fd\u200d\u2696\ufe0f",
- [":man_judge_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\u2696\ufe0f",
- [":man_judge::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\u2696\ufe0f",
- [":man_judge_tone4:"] = "\U0001f468\U0001f3fe\u200d\u2696\ufe0f",
- [":man_judge_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\u2696\ufe0f",
- [":man_judge::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\u2696\ufe0f",
- [":man_judge_tone5:"] = "\U0001f468\U0001f3ff\u200d\u2696\ufe0f",
- [":man_judge_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\u2696\ufe0f",
- [":man_judge::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\u2696\ufe0f",
- [":man_juggling:"] = "\U0001f939\u200d\u2642\ufe0f",
- [":man_juggling_tone1:"] = "\U0001f939\U0001f3fb\u200d\u2642\ufe0f",
- [":man_juggling_light_skin_tone:"] = "\U0001f939\U0001f3fb\u200d\u2642\ufe0f",
- [":man_juggling::skin-tone-1:"] = "\U0001f939\U0001f3fb\u200d\u2642\ufe0f",
- [":man_juggling_tone2:"] = "\U0001f939\U0001f3fc\u200d\u2642\ufe0f",
- [":man_juggling_medium_light_skin_tone:"] = "\U0001f939\U0001f3fc\u200d\u2642\ufe0f",
- [":man_juggling::skin-tone-2:"] = "\U0001f939\U0001f3fc\u200d\u2642\ufe0f",
- [":man_juggling_tone3:"] = "\U0001f939\U0001f3fd\u200d\u2642\ufe0f",
- [":man_juggling_medium_skin_tone:"] = "\U0001f939\U0001f3fd\u200d\u2642\ufe0f",
- [":man_juggling::skin-tone-3:"] = "\U0001f939\U0001f3fd\u200d\u2642\ufe0f",
- [":man_juggling_tone4:"] = "\U0001f939\U0001f3fe\u200d\u2642\ufe0f",
- [":man_juggling_medium_dark_skin_tone:"] = "\U0001f939\U0001f3fe\u200d\u2642\ufe0f",
- [":man_juggling::skin-tone-4:"] = "\U0001f939\U0001f3fe\u200d\u2642\ufe0f",
- [":man_juggling_tone5:"] = "\U0001f939\U0001f3ff\u200d\u2642\ufe0f",
- [":man_juggling_dark_skin_tone:"] = "\U0001f939\U0001f3ff\u200d\u2642\ufe0f",
- [":man_juggling::skin-tone-5:"] = "\U0001f939\U0001f3ff\u200d\u2642\ufe0f",
- [":man_kneeling:"] = "\U0001f9ce\u200d\u2642\ufe0f",
- [":man_kneeling_tone1:"] = "\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f",
- [":man_kneeling_light_skin_tone:"] = "\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f",
- [":man_kneeling::skin-tone-1:"] = "\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f",
- [":man_kneeling_tone2:"] = "\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f",
- [":man_kneeling_medium_light_skin_tone:"] = "\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f",
- [":man_kneeling::skin-tone-2:"] = "\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f",
- [":man_kneeling_tone3:"] = "\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f",
- [":man_kneeling_medium_skin_tone:"] = "\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f",
- [":man_kneeling::skin-tone-3:"] = "\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f",
- [":man_kneeling_tone4:"] = "\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f",
- [":man_kneeling_medium_dark_skin_tone:"] = "\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f",
- [":man_kneeling::skin-tone-4:"] = "\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f",
- [":man_kneeling_tone5:"] = "\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f",
- [":man_kneeling_dark_skin_tone:"] = "\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f",
- [":man_kneeling::skin-tone-5:"] = "\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f",
- [":man_lifting_weights:"] = "\U0001f3cb\ufe0f\u200d\u2642\ufe0f",
- [":man_lifting_weights_tone1:"] = "\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f",
- [":man_lifting_weights_light_skin_tone:"] = "\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f",
- [":man_lifting_weights::skin-tone-1:"] = "\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f",
- [":man_lifting_weights_tone2:"] = "\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f",
- [":man_lifting_weights_medium_light_skin_tone:"] = "\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f",
- [":man_lifting_weights::skin-tone-2:"] = "\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f",
- [":man_lifting_weights_tone3:"] = "\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f",
- [":man_lifting_weights_medium_skin_tone:"] = "\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f",
- [":man_lifting_weights::skin-tone-3:"] = "\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f",
- [":man_lifting_weights_tone4:"] = "\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f",
- [":man_lifting_weights_medium_dark_skin_tone:"] = "\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f",
- [":man_lifting_weights::skin-tone-4:"] = "\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f",
- [":man_lifting_weights_tone5:"] = "\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f",
- [":man_lifting_weights_dark_skin_tone:"] = "\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f",
- [":man_lifting_weights::skin-tone-5:"] = "\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f",
- [":man_mage:"] = "\U0001f9d9\u200d\u2642\ufe0f",
- [":man_mage_tone1:"] = "\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f",
- [":man_mage_light_skin_tone:"] = "\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f",
- [":man_mage::skin-tone-1:"] = "\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f",
- [":man_mage_tone2:"] = "\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f",
- [":man_mage_medium_light_skin_tone:"] = "\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f",
- [":man_mage::skin-tone-2:"] = "\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f",
- [":man_mage_tone3:"] = "\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f",
- [":man_mage_medium_skin_tone:"] = "\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f",
- [":man_mage::skin-tone-3:"] = "\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f",
- [":man_mage_tone4:"] = "\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f",
- [":man_mage_medium_dark_skin_tone:"] = "\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f",
- [":man_mage::skin-tone-4:"] = "\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f",
- [":man_mage_tone5:"] = "\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f",
- [":man_mage_dark_skin_tone:"] = "\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f",
- [":man_mage::skin-tone-5:"] = "\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f",
- [":man_mechanic:"] = "\U0001f468\u200d\U0001f527",
- [":man_mechanic_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f527",
- [":man_mechanic_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f527",
- [":man_mechanic::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f527",
- [":man_mechanic_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f527",
- [":man_mechanic_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f527",
- [":man_mechanic::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f527",
- [":man_mechanic_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f527",
- [":man_mechanic_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f527",
- [":man_mechanic::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f527",
- [":man_mechanic_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f527",
- [":man_mechanic_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f527",
- [":man_mechanic::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f527",
- [":man_mechanic_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f527",
- [":man_mechanic_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f527",
- [":man_mechanic::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f527",
- [":man_mountain_biking:"] = "\U0001f6b5\u200d\u2642\ufe0f",
- [":man_mountain_biking_tone1:"] = "\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f",
- [":man_mountain_biking_light_skin_tone:"] = "\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f",
- [":man_mountain_biking::skin-tone-1:"] = "\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f",
- [":man_mountain_biking_tone2:"] = "\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f",
- [":man_mountain_biking_medium_light_skin_tone:"] = "\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f",
- [":man_mountain_biking::skin-tone-2:"] = "\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f",
- [":man_mountain_biking_tone3:"] = "\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f",
- [":man_mountain_biking_medium_skin_tone:"] = "\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f",
- [":man_mountain_biking::skin-tone-3:"] = "\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f",
- [":man_mountain_biking_tone4:"] = "\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f",
- [":man_mountain_biking_medium_dark_skin_tone:"] = "\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f",
- [":man_mountain_biking::skin-tone-4:"] = "\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f",
- [":man_mountain_biking_tone5:"] = "\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f",
- [":man_mountain_biking_dark_skin_tone:"] = "\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f",
- [":man_mountain_biking::skin-tone-5:"] = "\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f",
- [":man_office_worker:"] = "\U0001f468\u200d\U0001f4bc",
- [":man_office_worker_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bc",
- [":man_office_worker_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bc",
- [":man_office_worker::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bc",
- [":man_office_worker_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bc",
- [":man_office_worker_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bc",
- [":man_office_worker::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bc",
- [":man_office_worker_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bc",
- [":man_office_worker_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bc",
- [":man_office_worker::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bc",
- [":man_office_worker_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bc",
- [":man_office_worker_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bc",
- [":man_office_worker::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bc",
- [":man_office_worker_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bc",
- [":man_office_worker_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bc",
- [":man_office_worker::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bc",
- [":man_pilot:"] = "\U0001f468\u200d\u2708\ufe0f",
- [":man_pilot_tone1:"] = "\U0001f468\U0001f3fb\u200d\u2708\ufe0f",
- [":man_pilot_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\u2708\ufe0f",
- [":man_pilot::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\u2708\ufe0f",
- [":man_pilot_tone2:"] = "\U0001f468\U0001f3fc\u200d\u2708\ufe0f",
- [":man_pilot_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\u2708\ufe0f",
- [":man_pilot::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\u2708\ufe0f",
- [":man_pilot_tone3:"] = "\U0001f468\U0001f3fd\u200d\u2708\ufe0f",
- [":man_pilot_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\u2708\ufe0f",
- [":man_pilot::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\u2708\ufe0f",
- [":man_pilot_tone4:"] = "\U0001f468\U0001f3fe\u200d\u2708\ufe0f",
- [":man_pilot_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\u2708\ufe0f",
- [":man_pilot::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\u2708\ufe0f",
- [":man_pilot_tone5:"] = "\U0001f468\U0001f3ff\u200d\u2708\ufe0f",
- [":man_pilot_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\u2708\ufe0f",
- [":man_pilot::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\u2708\ufe0f",
- [":man_playing_handball:"] = "\U0001f93e\u200d\u2642\ufe0f",
- [":man_playing_handball_tone1:"] = "\U0001f93e\U0001f3fb\u200d\u2642\ufe0f",
- [":man_playing_handball_light_skin_tone:"] = "\U0001f93e\U0001f3fb\u200d\u2642\ufe0f",
- [":man_playing_handball::skin-tone-1:"] = "\U0001f93e\U0001f3fb\u200d\u2642\ufe0f",
- [":man_playing_handball_tone2:"] = "\U0001f93e\U0001f3fc\u200d\u2642\ufe0f",
- [":man_playing_handball_medium_light_skin_tone:"] = "\U0001f93e\U0001f3fc\u200d\u2642\ufe0f",
- [":man_playing_handball::skin-tone-2:"] = "\U0001f93e\U0001f3fc\u200d\u2642\ufe0f",
- [":man_playing_handball_tone3:"] = "\U0001f93e\U0001f3fd\u200d\u2642\ufe0f",
- [":man_playing_handball_medium_skin_tone:"] = "\U0001f93e\U0001f3fd\u200d\u2642\ufe0f",
- [":man_playing_handball::skin-tone-3:"] = "\U0001f93e\U0001f3fd\u200d\u2642\ufe0f",
- [":man_playing_handball_tone4:"] = "\U0001f93e\U0001f3fe\u200d\u2642\ufe0f",
- [":man_playing_handball_medium_dark_skin_tone:"] = "\U0001f93e\U0001f3fe\u200d\u2642\ufe0f",
- [":man_playing_handball::skin-tone-4:"] = "\U0001f93e\U0001f3fe\u200d\u2642\ufe0f",
- [":man_playing_handball_tone5:"] = "\U0001f93e\U0001f3ff\u200d\u2642\ufe0f",
- [":man_playing_handball_dark_skin_tone:"] = "\U0001f93e\U0001f3ff\u200d\u2642\ufe0f",
- [":man_playing_handball::skin-tone-5:"] = "\U0001f93e\U0001f3ff\u200d\u2642\ufe0f",
- [":man_playing_water_polo:"] = "\U0001f93d\u200d\u2642\ufe0f",
- [":man_playing_water_polo_tone1:"] = "\U0001f93d\U0001f3fb\u200d\u2642\ufe0f",
- [":man_playing_water_polo_light_skin_tone:"] = "\U0001f93d\U0001f3fb\u200d\u2642\ufe0f",
- [":man_playing_water_polo::skin-tone-1:"] = "\U0001f93d\U0001f3fb\u200d\u2642\ufe0f",
- [":man_playing_water_polo_tone2:"] = "\U0001f93d\U0001f3fc\u200d\u2642\ufe0f",
- [":man_playing_water_polo_medium_light_skin_tone:"] = "\U0001f93d\U0001f3fc\u200d\u2642\ufe0f",
- [":man_playing_water_polo::skin-tone-2:"] = "\U0001f93d\U0001f3fc\u200d\u2642\ufe0f",
- [":man_playing_water_polo_tone3:"] = "\U0001f93d\U0001f3fd\u200d\u2642\ufe0f",
- [":man_playing_water_polo_medium_skin_tone:"] = "\U0001f93d\U0001f3fd\u200d\u2642\ufe0f",
- [":man_playing_water_polo::skin-tone-3:"] = "\U0001f93d\U0001f3fd\u200d\u2642\ufe0f",
- [":man_playing_water_polo_tone4:"] = "\U0001f93d\U0001f3fe\u200d\u2642\ufe0f",
- [":man_playing_water_polo_medium_dark_skin_tone:"] = "\U0001f93d\U0001f3fe\u200d\u2642\ufe0f",
- [":man_playing_water_polo::skin-tone-4:"] = "\U0001f93d\U0001f3fe\u200d\u2642\ufe0f",
- [":man_playing_water_polo_tone5:"] = "\U0001f93d\U0001f3ff\u200d\u2642\ufe0f",
- [":man_playing_water_polo_dark_skin_tone:"] = "\U0001f93d\U0001f3ff\u200d\u2642\ufe0f",
- [":man_playing_water_polo::skin-tone-5:"] = "\U0001f93d\U0001f3ff\u200d\u2642\ufe0f",
- [":man_police_officer:"] = "\U0001f46e\u200d\u2642\ufe0f",
- [":man_police_officer_tone1:"] = "\U0001f46e\U0001f3fb\u200d\u2642\ufe0f",
- [":man_police_officer_light_skin_tone:"] = "\U0001f46e\U0001f3fb\u200d\u2642\ufe0f",
- [":man_police_officer::skin-tone-1:"] = "\U0001f46e\U0001f3fb\u200d\u2642\ufe0f",
- [":man_police_officer_tone2:"] = "\U0001f46e\U0001f3fc\u200d\u2642\ufe0f",
- [":man_police_officer_medium_light_skin_tone:"] = "\U0001f46e\U0001f3fc\u200d\u2642\ufe0f",
- [":man_police_officer::skin-tone-2:"] = "\U0001f46e\U0001f3fc\u200d\u2642\ufe0f",
- [":man_police_officer_tone3:"] = "\U0001f46e\U0001f3fd\u200d\u2642\ufe0f",
- [":man_police_officer_medium_skin_tone:"] = "\U0001f46e\U0001f3fd\u200d\u2642\ufe0f",
- [":man_police_officer::skin-tone-3:"] = "\U0001f46e\U0001f3fd\u200d\u2642\ufe0f",
- [":man_police_officer_tone4:"] = "\U0001f46e\U0001f3fe\u200d\u2642\ufe0f",
- [":man_police_officer_medium_dark_skin_tone:"] = "\U0001f46e\U0001f3fe\u200d\u2642\ufe0f",
- [":man_police_officer::skin-tone-4:"] = "\U0001f46e\U0001f3fe\u200d\u2642\ufe0f",
- [":man_police_officer_tone5:"] = "\U0001f46e\U0001f3ff\u200d\u2642\ufe0f",
- [":man_police_officer_dark_skin_tone:"] = "\U0001f46e\U0001f3ff\u200d\u2642\ufe0f",
- [":man_police_officer::skin-tone-5:"] = "\U0001f46e\U0001f3ff\u200d\u2642\ufe0f",
- [":man_pouting:"] = "\U0001f64e\u200d\u2642\ufe0f",
- [":man_pouting_tone1:"] = "\U0001f64e\U0001f3fb\u200d\u2642\ufe0f",
- [":man_pouting_light_skin_tone:"] = "\U0001f64e\U0001f3fb\u200d\u2642\ufe0f",
- [":man_pouting::skin-tone-1:"] = "\U0001f64e\U0001f3fb\u200d\u2642\ufe0f",
- [":man_pouting_tone2:"] = "\U0001f64e\U0001f3fc\u200d\u2642\ufe0f",
- [":man_pouting_medium_light_skin_tone:"] = "\U0001f64e\U0001f3fc\u200d\u2642\ufe0f",
- [":man_pouting::skin-tone-2:"] = "\U0001f64e\U0001f3fc\u200d\u2642\ufe0f",
- [":man_pouting_tone3:"] = "\U0001f64e\U0001f3fd\u200d\u2642\ufe0f",
- [":man_pouting_medium_skin_tone:"] = "\U0001f64e\U0001f3fd\u200d\u2642\ufe0f",
- [":man_pouting::skin-tone-3:"] = "\U0001f64e\U0001f3fd\u200d\u2642\ufe0f",
- [":man_pouting_tone4:"] = "\U0001f64e\U0001f3fe\u200d\u2642\ufe0f",
- [":man_pouting_medium_dark_skin_tone:"] = "\U0001f64e\U0001f3fe\u200d\u2642\ufe0f",
- [":man_pouting::skin-tone-4:"] = "\U0001f64e\U0001f3fe\u200d\u2642\ufe0f",
- [":man_pouting_tone5:"] = "\U0001f64e\U0001f3ff\u200d\u2642\ufe0f",
- [":man_pouting_dark_skin_tone:"] = "\U0001f64e\U0001f3ff\u200d\u2642\ufe0f",
- [":man_pouting::skin-tone-5:"] = "\U0001f64e\U0001f3ff\u200d\u2642\ufe0f",
- [":man_raising_hand:"] = "\U0001f64b\u200d\u2642\ufe0f",
- [":man_raising_hand_tone1:"] = "\U0001f64b\U0001f3fb\u200d\u2642\ufe0f",
- [":man_raising_hand_light_skin_tone:"] = "\U0001f64b\U0001f3fb\u200d\u2642\ufe0f",
- [":man_raising_hand::skin-tone-1:"] = "\U0001f64b\U0001f3fb\u200d\u2642\ufe0f",
- [":man_raising_hand_tone2:"] = "\U0001f64b\U0001f3fc\u200d\u2642\ufe0f",
- [":man_raising_hand_medium_light_skin_tone:"] = "\U0001f64b\U0001f3fc\u200d\u2642\ufe0f",
- [":man_raising_hand::skin-tone-2:"] = "\U0001f64b\U0001f3fc\u200d\u2642\ufe0f",
- [":man_raising_hand_tone3:"] = "\U0001f64b\U0001f3fd\u200d\u2642\ufe0f",
- [":man_raising_hand_medium_skin_tone:"] = "\U0001f64b\U0001f3fd\u200d\u2642\ufe0f",
- [":man_raising_hand::skin-tone-3:"] = "\U0001f64b\U0001f3fd\u200d\u2642\ufe0f",
- [":man_raising_hand_tone4:"] = "\U0001f64b\U0001f3fe\u200d\u2642\ufe0f",
- [":man_raising_hand_medium_dark_skin_tone:"] = "\U0001f64b\U0001f3fe\u200d\u2642\ufe0f",
- [":man_raising_hand::skin-tone-4:"] = "\U0001f64b\U0001f3fe\u200d\u2642\ufe0f",
- [":man_raising_hand_tone5:"] = "\U0001f64b\U0001f3ff\u200d\u2642\ufe0f",
- [":man_raising_hand_dark_skin_tone:"] = "\U0001f64b\U0001f3ff\u200d\u2642\ufe0f",
- [":man_raising_hand::skin-tone-5:"] = "\U0001f64b\U0001f3ff\u200d\u2642\ufe0f",
- [":man_red_haired:"] = "\U0001f468\u200d\U0001f9b0",
- [":man_red_haired_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b0",
- [":man_red_haired_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b0",
- [":man_red_haired::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b0",
- [":man_red_haired_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b0",
- [":man_red_haired_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b0",
- [":man_red_haired::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b0",
- [":man_red_haired_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b0",
- [":man_red_haired_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b0",
- [":man_red_haired::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b0",
- [":man_red_haired_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b0",
- [":man_red_haired_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b0",
- [":man_red_haired::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b0",
- [":man_red_haired_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b0",
- [":man_red_haired_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b0",
- [":man_red_haired::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b0",
- [":man_rowing_boat:"] = "\U0001f6a3\u200d\u2642\ufe0f",
- [":man_rowing_boat_tone1:"] = "\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f",
- [":man_rowing_boat_light_skin_tone:"] = "\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f",
- [":man_rowing_boat::skin-tone-1:"] = "\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f",
- [":man_rowing_boat_tone2:"] = "\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f",
- [":man_rowing_boat_medium_light_skin_tone:"] = "\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f",
- [":man_rowing_boat::skin-tone-2:"] = "\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f",
- [":man_rowing_boat_tone3:"] = "\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f",
- [":man_rowing_boat_medium_skin_tone:"] = "\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f",
- [":man_rowing_boat::skin-tone-3:"] = "\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f",
- [":man_rowing_boat_tone4:"] = "\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f",
- [":man_rowing_boat_medium_dark_skin_tone:"] = "\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f",
- [":man_rowing_boat::skin-tone-4:"] = "\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f",
- [":man_rowing_boat_tone5:"] = "\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f",
- [":man_rowing_boat_dark_skin_tone:"] = "\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f",
- [":man_rowing_boat::skin-tone-5:"] = "\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f",
- [":man_running:"] = "\U0001f3c3\u200d\u2642\ufe0f",
- [":man_running_tone1:"] = "\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f",
- [":man_running_light_skin_tone:"] = "\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f",
- [":man_running::skin-tone-1:"] = "\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f",
- [":man_running_tone2:"] = "\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f",
- [":man_running_medium_light_skin_tone:"] = "\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f",
- [":man_running::skin-tone-2:"] = "\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f",
- [":man_running_tone3:"] = "\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f",
- [":man_running_medium_skin_tone:"] = "\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f",
- [":man_running::skin-tone-3:"] = "\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f",
- [":man_running_tone4:"] = "\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f",
- [":man_running_medium_dark_skin_tone:"] = "\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f",
- [":man_running::skin-tone-4:"] = "\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f",
- [":man_running_tone5:"] = "\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f",
- [":man_running_dark_skin_tone:"] = "\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f",
- [":man_running::skin-tone-5:"] = "\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f",
- [":man_scientist:"] = "\U0001f468\u200d\U0001f52c",
- [":man_scientist_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f52c",
- [":man_scientist_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f52c",
- [":man_scientist::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f52c",
- [":man_scientist_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f52c",
- [":man_scientist_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f52c",
- [":man_scientist::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f52c",
- [":man_scientist_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f52c",
- [":man_scientist_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f52c",
- [":man_scientist::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f52c",
- [":man_scientist_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f52c",
- [":man_scientist_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f52c",
- [":man_scientist::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f52c",
- [":man_scientist_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f52c",
- [":man_scientist_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f52c",
- [":man_scientist::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f52c",
- [":man_shrugging:"] = "\U0001f937\u200d\u2642\ufe0f",
- [":man_shrugging_tone1:"] = "\U0001f937\U0001f3fb\u200d\u2642\ufe0f",
- [":man_shrugging_light_skin_tone:"] = "\U0001f937\U0001f3fb\u200d\u2642\ufe0f",
- [":man_shrugging::skin-tone-1:"] = "\U0001f937\U0001f3fb\u200d\u2642\ufe0f",
- [":man_shrugging_tone2:"] = "\U0001f937\U0001f3fc\u200d\u2642\ufe0f",
- [":man_shrugging_medium_light_skin_tone:"] = "\U0001f937\U0001f3fc\u200d\u2642\ufe0f",
- [":man_shrugging::skin-tone-2:"] = "\U0001f937\U0001f3fc\u200d\u2642\ufe0f",
- [":man_shrugging_tone3:"] = "\U0001f937\U0001f3fd\u200d\u2642\ufe0f",
- [":man_shrugging_medium_skin_tone:"] = "\U0001f937\U0001f3fd\u200d\u2642\ufe0f",
- [":man_shrugging::skin-tone-3:"] = "\U0001f937\U0001f3fd\u200d\u2642\ufe0f",
- [":man_shrugging_tone4:"] = "\U0001f937\U0001f3fe\u200d\u2642\ufe0f",
- [":man_shrugging_medium_dark_skin_tone:"] = "\U0001f937\U0001f3fe\u200d\u2642\ufe0f",
- [":man_shrugging::skin-tone-4:"] = "\U0001f937\U0001f3fe\u200d\u2642\ufe0f",
- [":man_shrugging_tone5:"] = "\U0001f937\U0001f3ff\u200d\u2642\ufe0f",
- [":man_shrugging_dark_skin_tone:"] = "\U0001f937\U0001f3ff\u200d\u2642\ufe0f",
- [":man_shrugging::skin-tone-5:"] = "\U0001f937\U0001f3ff\u200d\u2642\ufe0f",
- [":man_singer:"] = "\U0001f468\u200d\U0001f3a4",
- [":man_singer_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a4",
- [":man_singer_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a4",
- [":man_singer::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a4",
- [":man_singer_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a4",
- [":man_singer_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a4",
- [":man_singer::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a4",
- [":man_singer_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a4",
- [":man_singer_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a4",
- [":man_singer::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a4",
- [":man_singer_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a4",
- [":man_singer_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a4",
- [":man_singer::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a4",
- [":man_singer_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a4",
- [":man_singer_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a4",
- [":man_singer::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a4",
- [":man_standing:"] = "\U0001f9cd\u200d\u2642\ufe0f",
- [":man_standing_tone1:"] = "\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f",
- [":man_standing_light_skin_tone:"] = "\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f",
- [":man_standing::skin-tone-1:"] = "\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f",
- [":man_standing_tone2:"] = "\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f",
- [":man_standing_medium_light_skin_tone:"] = "\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f",
- [":man_standing::skin-tone-2:"] = "\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f",
- [":man_standing_tone3:"] = "\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f",
- [":man_standing_medium_skin_tone:"] = "\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f",
- [":man_standing::skin-tone-3:"] = "\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f",
- [":man_standing_tone4:"] = "\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f",
- [":man_standing_medium_dark_skin_tone:"] = "\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f",
- [":man_standing::skin-tone-4:"] = "\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f",
- [":man_standing_tone5:"] = "\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f",
- [":man_standing_dark_skin_tone:"] = "\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f",
- [":man_standing::skin-tone-5:"] = "\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f",
- [":man_student:"] = "\U0001f468\u200d\U0001f393",
- [":man_student_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f393",
- [":man_student_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f393",
- [":man_student::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f393",
- [":man_student_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f393",
- [":man_student_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f393",
- [":man_student::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f393",
- [":man_student_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f393",
- [":man_student_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f393",
- [":man_student::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f393",
- [":man_student_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f393",
- [":man_student_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f393",
- [":man_student::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f393",
- [":man_student_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f393",
- [":man_student_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f393",
- [":man_student::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f393",
- [":man_superhero:"] = "\U0001f9b8\u200d\u2642\ufe0f",
- [":man_superhero_tone1:"] = "\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f",
- [":man_superhero_light_skin_tone:"] = "\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f",
- [":man_superhero::skin-tone-1:"] = "\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f",
- [":man_superhero_tone2:"] = "\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f",
- [":man_superhero_medium_light_skin_tone:"] = "\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f",
- [":man_superhero::skin-tone-2:"] = "\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f",
- [":man_superhero_tone3:"] = "\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f",
- [":man_superhero_medium_skin_tone:"] = "\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f",
- [":man_superhero::skin-tone-3:"] = "\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f",
- [":man_superhero_tone4:"] = "\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f",
- [":man_superhero_medium_dark_skin_tone:"] = "\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f",
- [":man_superhero::skin-tone-4:"] = "\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f",
- [":man_superhero_tone5:"] = "\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f",
- [":man_superhero_dark_skin_tone:"] = "\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f",
- [":man_superhero::skin-tone-5:"] = "\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f",
- [":man_supervillain:"] = "\U0001f9b9\u200d\u2642\ufe0f",
- [":man_supervillain_tone1:"] = "\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f",
- [":man_supervillain_light_skin_tone:"] = "\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f",
- [":man_supervillain::skin-tone-1:"] = "\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f",
- [":man_supervillain_tone2:"] = "\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f",
- [":man_supervillain_medium_light_skin_tone:"] = "\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f",
- [":man_supervillain::skin-tone-2:"] = "\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f",
- [":man_supervillain_tone3:"] = "\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f",
- [":man_supervillain_medium_skin_tone:"] = "\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f",
- [":man_supervillain::skin-tone-3:"] = "\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f",
- [":man_supervillain_tone4:"] = "\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f",
- [":man_supervillain_medium_dark_skin_tone:"] = "\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f",
- [":man_supervillain::skin-tone-4:"] = "\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f",
- [":man_supervillain_tone5:"] = "\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f",
- [":man_supervillain_dark_skin_tone:"] = "\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f",
- [":man_supervillain::skin-tone-5:"] = "\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f",
- [":man_surfing:"] = "\U0001f3c4\u200d\u2642\ufe0f",
- [":man_surfing_tone1:"] = "\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f",
- [":man_surfing_light_skin_tone:"] = "\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f",
- [":man_surfing::skin-tone-1:"] = "\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f",
- [":man_surfing_tone2:"] = "\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f",
- [":man_surfing_medium_light_skin_tone:"] = "\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f",
- [":man_surfing::skin-tone-2:"] = "\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f",
- [":man_surfing_tone3:"] = "\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f",
- [":man_surfing_medium_skin_tone:"] = "\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f",
- [":man_surfing::skin-tone-3:"] = "\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f",
- [":man_surfing_tone4:"] = "\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f",
- [":man_surfing_medium_dark_skin_tone:"] = "\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f",
- [":man_surfing::skin-tone-4:"] = "\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f",
- [":man_surfing_tone5:"] = "\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f",
- [":man_surfing_dark_skin_tone:"] = "\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f",
- [":man_surfing::skin-tone-5:"] = "\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f",
- [":man_swimming:"] = "\U0001f3ca\u200d\u2642\ufe0f",
- [":man_swimming_tone1:"] = "\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f",
- [":man_swimming_light_skin_tone:"] = "\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f",
- [":man_swimming::skin-tone-1:"] = "\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f",
- [":man_swimming_tone2:"] = "\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f",
- [":man_swimming_medium_light_skin_tone:"] = "\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f",
- [":man_swimming::skin-tone-2:"] = "\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f",
- [":man_swimming_tone3:"] = "\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f",
- [":man_swimming_medium_skin_tone:"] = "\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f",
- [":man_swimming::skin-tone-3:"] = "\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f",
- [":man_swimming_tone4:"] = "\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f",
- [":man_swimming_medium_dark_skin_tone:"] = "\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f",
- [":man_swimming::skin-tone-4:"] = "\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f",
- [":man_swimming_tone5:"] = "\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f",
- [":man_swimming_dark_skin_tone:"] = "\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f",
- [":man_swimming::skin-tone-5:"] = "\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f",
- [":man_teacher:"] = "\U0001f468\u200d\U0001f3eb",
- [":man_teacher_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3eb",
- [":man_teacher_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f3eb",
- [":man_teacher::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3eb",
- [":man_teacher_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3eb",
- [":man_teacher_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f3eb",
- [":man_teacher::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3eb",
- [":man_teacher_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3eb",
- [":man_teacher_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f3eb",
- [":man_teacher::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3eb",
- [":man_teacher_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3eb",
- [":man_teacher_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f3eb",
- [":man_teacher::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3eb",
- [":man_teacher_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3eb",
- [":man_teacher_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f3eb",
- [":man_teacher::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3eb",
- [":man_technologist:"] = "\U0001f468\u200d\U0001f4bb",
- [":man_technologist_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bb",
- [":man_technologist_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bb",
- [":man_technologist::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bb",
- [":man_technologist_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bb",
- [":man_technologist_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bb",
- [":man_technologist::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bb",
- [":man_technologist_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bb",
- [":man_technologist_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bb",
- [":man_technologist::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bb",
- [":man_technologist_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bb",
- [":man_technologist_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bb",
- [":man_technologist::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bb",
- [":man_technologist_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bb",
- [":man_technologist_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bb",
- [":man_technologist::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bb",
- [":man_tipping_hand:"] = "\U0001f481\u200d\u2642\ufe0f",
- [":man_tipping_hand_tone1:"] = "\U0001f481\U0001f3fb\u200d\u2642\ufe0f",
- [":man_tipping_hand_light_skin_tone:"] = "\U0001f481\U0001f3fb\u200d\u2642\ufe0f",
- [":man_tipping_hand::skin-tone-1:"] = "\U0001f481\U0001f3fb\u200d\u2642\ufe0f",
- [":man_tipping_hand_tone2:"] = "\U0001f481\U0001f3fc\u200d\u2642\ufe0f",
- [":man_tipping_hand_medium_light_skin_tone:"] = "\U0001f481\U0001f3fc\u200d\u2642\ufe0f",
- [":man_tipping_hand::skin-tone-2:"] = "\U0001f481\U0001f3fc\u200d\u2642\ufe0f",
- [":man_tipping_hand_tone3:"] = "\U0001f481\U0001f3fd\u200d\u2642\ufe0f",
- [":man_tipping_hand_medium_skin_tone:"] = "\U0001f481\U0001f3fd\u200d\u2642\ufe0f",
- [":man_tipping_hand::skin-tone-3:"] = "\U0001f481\U0001f3fd\u200d\u2642\ufe0f",
- [":man_tipping_hand_tone4:"] = "\U0001f481\U0001f3fe\u200d\u2642\ufe0f",
- [":man_tipping_hand_medium_dark_skin_tone:"] = "\U0001f481\U0001f3fe\u200d\u2642\ufe0f",
- [":man_tipping_hand::skin-tone-4:"] = "\U0001f481\U0001f3fe\u200d\u2642\ufe0f",
- [":man_tipping_hand_tone5:"] = "\U0001f481\U0001f3ff\u200d\u2642\ufe0f",
- [":man_tipping_hand_dark_skin_tone:"] = "\U0001f481\U0001f3ff\u200d\u2642\ufe0f",
- [":man_tipping_hand::skin-tone-5:"] = "\U0001f481\U0001f3ff\u200d\u2642\ufe0f",
- [":man_tone1:"] = "\U0001f468\U0001f3fb",
- [":man::skin-tone-1:"] = "\U0001f468\U0001f3fb",
- [":man_tone2:"] = "\U0001f468\U0001f3fc",
- [":man::skin-tone-2:"] = "\U0001f468\U0001f3fc",
- [":man_tone3:"] = "\U0001f468\U0001f3fd",
- [":man::skin-tone-3:"] = "\U0001f468\U0001f3fd",
- [":man_tone4:"] = "\U0001f468\U0001f3fe",
- [":man::skin-tone-4:"] = "\U0001f468\U0001f3fe",
- [":man_tone5:"] = "\U0001f468\U0001f3ff",
- [":man::skin-tone-5:"] = "\U0001f468\U0001f3ff",
- [":man_vampire:"] = "\U0001f9db\u200d\u2642\ufe0f",
- [":man_vampire_tone1:"] = "\U0001f9db\U0001f3fb\u200d\u2642\ufe0f",
- [":man_vampire_light_skin_tone:"] = "\U0001f9db\U0001f3fb\u200d\u2642\ufe0f",
- [":man_vampire::skin-tone-1:"] = "\U0001f9db\U0001f3fb\u200d\u2642\ufe0f",
- [":man_vampire_tone2:"] = "\U0001f9db\U0001f3fc\u200d\u2642\ufe0f",
- [":man_vampire_medium_light_skin_tone:"] = "\U0001f9db\U0001f3fc\u200d\u2642\ufe0f",
- [":man_vampire::skin-tone-2:"] = "\U0001f9db\U0001f3fc\u200d\u2642\ufe0f",
- [":man_vampire_tone3:"] = "\U0001f9db\U0001f3fd\u200d\u2642\ufe0f",
- [":man_vampire_medium_skin_tone:"] = "\U0001f9db\U0001f3fd\u200d\u2642\ufe0f",
- [":man_vampire::skin-tone-3:"] = "\U0001f9db\U0001f3fd\u200d\u2642\ufe0f",
- [":man_vampire_tone4:"] = "\U0001f9db\U0001f3fe\u200d\u2642\ufe0f",
- [":man_vampire_medium_dark_skin_tone:"] = "\U0001f9db\U0001f3fe\u200d\u2642\ufe0f",
- [":man_vampire::skin-tone-4:"] = "\U0001f9db\U0001f3fe\u200d\u2642\ufe0f",
- [":man_vampire_tone5:"] = "\U0001f9db\U0001f3ff\u200d\u2642\ufe0f",
- [":man_vampire_dark_skin_tone:"] = "\U0001f9db\U0001f3ff\u200d\u2642\ufe0f",
- [":man_vampire::skin-tone-5:"] = "\U0001f9db\U0001f3ff\u200d\u2642\ufe0f",
- [":man_walking:"] = "\U0001f6b6\u200d\u2642\ufe0f",
- [":man_walking_tone1:"] = "\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f",
- [":man_walking_light_skin_tone:"] = "\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f",
- [":man_walking::skin-tone-1:"] = "\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f",
- [":man_walking_tone2:"] = "\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f",
- [":man_walking_medium_light_skin_tone:"] = "\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f",
- [":man_walking::skin-tone-2:"] = "\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f",
- [":man_walking_tone3:"] = "\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f",
- [":man_walking_medium_skin_tone:"] = "\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f",
- [":man_walking::skin-tone-3:"] = "\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f",
- [":man_walking_tone4:"] = "\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f",
- [":man_walking_medium_dark_skin_tone:"] = "\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f",
- [":man_walking::skin-tone-4:"] = "\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f",
- [":man_walking_tone5:"] = "\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f",
- [":man_walking_dark_skin_tone:"] = "\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f",
- [":man_walking::skin-tone-5:"] = "\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f",
- [":man_wearing_turban:"] = "\U0001f473\u200d\u2642\ufe0f",
- [":man_wearing_turban_tone1:"] = "\U0001f473\U0001f3fb\u200d\u2642\ufe0f",
- [":man_wearing_turban_light_skin_tone:"] = "\U0001f473\U0001f3fb\u200d\u2642\ufe0f",
- [":man_wearing_turban::skin-tone-1:"] = "\U0001f473\U0001f3fb\u200d\u2642\ufe0f",
- [":man_wearing_turban_tone2:"] = "\U0001f473\U0001f3fc\u200d\u2642\ufe0f",
- [":man_wearing_turban_medium_light_skin_tone:"] = "\U0001f473\U0001f3fc\u200d\u2642\ufe0f",
- [":man_wearing_turban::skin-tone-2:"] = "\U0001f473\U0001f3fc\u200d\u2642\ufe0f",
- [":man_wearing_turban_tone3:"] = "\U0001f473\U0001f3fd\u200d\u2642\ufe0f",
- [":man_wearing_turban_medium_skin_tone:"] = "\U0001f473\U0001f3fd\u200d\u2642\ufe0f",
- [":man_wearing_turban::skin-tone-3:"] = "\U0001f473\U0001f3fd\u200d\u2642\ufe0f",
- [":man_wearing_turban_tone4:"] = "\U0001f473\U0001f3fe\u200d\u2642\ufe0f",
- [":man_wearing_turban_medium_dark_skin_tone:"] = "\U0001f473\U0001f3fe\u200d\u2642\ufe0f",
- [":man_wearing_turban::skin-tone-4:"] = "\U0001f473\U0001f3fe\u200d\u2642\ufe0f",
- [":man_wearing_turban_tone5:"] = "\U0001f473\U0001f3ff\u200d\u2642\ufe0f",
- [":man_wearing_turban_dark_skin_tone:"] = "\U0001f473\U0001f3ff\u200d\u2642\ufe0f",
- [":man_wearing_turban::skin-tone-5:"] = "\U0001f473\U0001f3ff\u200d\u2642\ufe0f",
- [":man_white_haired:"] = "\U0001f468\u200d\U0001f9b3",
- [":man_white_haired_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b3",
- [":man_white_haired_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b3",
- [":man_white_haired::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b3",
- [":man_white_haired_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b3",
- [":man_white_haired_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b3",
- [":man_white_haired::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b3",
- [":man_white_haired_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b3",
- [":man_white_haired_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b3",
- [":man_white_haired::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b3",
- [":man_white_haired_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b3",
- [":man_white_haired_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b3",
- [":man_white_haired::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b3",
- [":man_white_haired_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b3",
- [":man_white_haired_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b3",
- [":man_white_haired::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b3",
- [":man_with_chinese_cap:"] = "\U0001f472",
- [":man_with_gua_pi_mao:"] = "\U0001f472",
- [":man_with_chinese_cap_tone1:"] = "\U0001f472\U0001f3fb",
- [":man_with_gua_pi_mao_tone1:"] = "\U0001f472\U0001f3fb",
- [":man_with_chinese_cap::skin-tone-1:"] = "\U0001f472\U0001f3fb",
- [":man_with_gua_pi_mao::skin-tone-1:"] = "\U0001f472\U0001f3fb",
- [":man_with_chinese_cap_tone2:"] = "\U0001f472\U0001f3fc",
- [":man_with_gua_pi_mao_tone2:"] = "\U0001f472\U0001f3fc",
- [":man_with_chinese_cap::skin-tone-2:"] = "\U0001f472\U0001f3fc",
- [":man_with_gua_pi_mao::skin-tone-2:"] = "\U0001f472\U0001f3fc",
- [":man_with_chinese_cap_tone3:"] = "\U0001f472\U0001f3fd",
- [":man_with_gua_pi_mao_tone3:"] = "\U0001f472\U0001f3fd",
- [":man_with_chinese_cap::skin-tone-3:"] = "\U0001f472\U0001f3fd",
- [":man_with_gua_pi_mao::skin-tone-3:"] = "\U0001f472\U0001f3fd",
- [":man_with_chinese_cap_tone4:"] = "\U0001f472\U0001f3fe",
- [":man_with_gua_pi_mao_tone4:"] = "\U0001f472\U0001f3fe",
- [":man_with_chinese_cap::skin-tone-4:"] = "\U0001f472\U0001f3fe",
- [":man_with_gua_pi_mao::skin-tone-4:"] = "\U0001f472\U0001f3fe",
- [":man_with_chinese_cap_tone5:"] = "\U0001f472\U0001f3ff",
- [":man_with_gua_pi_mao_tone5:"] = "\U0001f472\U0001f3ff",
- [":man_with_chinese_cap::skin-tone-5:"] = "\U0001f472\U0001f3ff",
- [":man_with_gua_pi_mao::skin-tone-5:"] = "\U0001f472\U0001f3ff",
- [":man_with_probing_cane:"] = "\U0001f468\u200d\U0001f9af",
- [":man_with_probing_cane_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9af",
- [":man_with_probing_cane_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9af",
- [":man_with_probing_cane::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9af",
- [":man_with_probing_cane_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9af",
- [":man_with_probing_cane_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9af",
- [":man_with_probing_cane::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9af",
- [":man_with_probing_cane_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9af",
- [":man_with_probing_cane_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9af",
- [":man_with_probing_cane::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9af",
- [":man_with_probing_cane_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9af",
- [":man_with_probing_cane_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9af",
- [":man_with_probing_cane::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9af",
- [":man_with_probing_cane_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9af",
- [":man_with_probing_cane_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9af",
- [":man_with_probing_cane::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9af",
- [":man_with_veil:"] = "\U0001f470\u200d\u2642\ufe0f",
- [":man_with_veil_tone1:"] = "\U0001f470\U0001f3fb\u200d\u2642\ufe0f",
- [":man_with_veil_light_skin_tone:"] = "\U0001f470\U0001f3fb\u200d\u2642\ufe0f",
- [":man_with_veil::skin-tone-1:"] = "\U0001f470\U0001f3fb\u200d\u2642\ufe0f",
- [":man_with_veil_tone2:"] = "\U0001f470\U0001f3fc\u200d\u2642\ufe0f",
- [":man_with_veil_medium_light_skin_tone:"] = "\U0001f470\U0001f3fc\u200d\u2642\ufe0f",
- [":man_with_veil::skin-tone-2:"] = "\U0001f470\U0001f3fc\u200d\u2642\ufe0f",
- [":man_with_veil_tone3:"] = "\U0001f470\U0001f3fd\u200d\u2642\ufe0f",
- [":man_with_veil_medium_skin_tone:"] = "\U0001f470\U0001f3fd\u200d\u2642\ufe0f",
- [":man_with_veil::skin-tone-3:"] = "\U0001f470\U0001f3fd\u200d\u2642\ufe0f",
- [":man_with_veil_tone4:"] = "\U0001f470\U0001f3fe\u200d\u2642\ufe0f",
- [":man_with_veil_medium_dark_skin_tone:"] = "\U0001f470\U0001f3fe\u200d\u2642\ufe0f",
- [":man_with_veil::skin-tone-4:"] = "\U0001f470\U0001f3fe\u200d\u2642\ufe0f",
- [":man_with_veil_tone5:"] = "\U0001f470\U0001f3ff\u200d\u2642\ufe0f",
- [":man_with_veil_dark_skin_tone:"] = "\U0001f470\U0001f3ff\u200d\u2642\ufe0f",
- [":man_with_veil::skin-tone-5:"] = "\U0001f470\U0001f3ff\u200d\u2642\ufe0f",
- [":man_zombie:"] = "\U0001f9df\u200d\u2642\ufe0f",
- [":mango:"] = "\U0001f96d",
- [":mans_shoe:"] = "\U0001f45e",
- [":manual_wheelchair:"] = "\U0001f9bd",
- [":map:"] = "\U0001f5fa\ufe0f",
- [":world_map:"] = "\U0001f5fa\ufe0f",
- [":maple_leaf:"] = "\U0001f341",
- [":martial_arts_uniform:"] = "\U0001f94b",
- [":karate_uniform:"] = "\U0001f94b",
- [":mask:"] = "\U0001f637",
- [":mate:"] = "\U0001f9c9",
- [":meat_on_bone:"] = "\U0001f356",
- [":mechanic:"] = "\U0001f9d1\u200d\U0001f527",
- [":mechanic_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f527",
- [":mechanic_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f527",
- [":mechanic::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f527",
- [":mechanic_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f527",
- [":mechanic_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f527",
- [":mechanic::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f527",
- [":mechanic_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f527",
- [":mechanic_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f527",
- [":mechanic::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f527",
- [":mechanic_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f527",
- [":mechanic_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f527",
- [":mechanic::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f527",
- [":mechanic_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f527",
- [":mechanic_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f527",
- [":mechanic::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f527",
- [":mechanical_arm:"] = "\U0001f9be",
- [":mechanical_leg:"] = "\U0001f9bf",
- [":medal:"] = "\U0001f3c5",
- [":sports_medal:"] = "\U0001f3c5",
- [":medical_symbol:"] = "\u2695\ufe0f",
- [":mega:"] = "\U0001f4e3",
- [":melon:"] = "\U0001f348",
- [":men_holding_hands_tone1:"] = "\U0001f46c",
- [":men_holding_hands_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone1_tone2:"] = "\U0001f46c",
- [":men_holding_hands_light_skin_tone_medium_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone1_tone3:"] = "\U0001f46c",
- [":men_holding_hands_light_skin_tone_medium_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone1_tone4:"] = "\U0001f46c",
- [":men_holding_hands_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone1_tone5:"] = "\U0001f46c",
- [":men_holding_hands_light_skin_tone_dark_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone2:"] = "\U0001f46c",
- [":men_holding_hands_medium_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone2_tone1:"] = "\U0001f46c",
- [":men_holding_hands_medium_light_skin_tone_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone2_tone3:"] = "\U0001f46c",
- [":men_holding_hands_medium_light_skin_tone_medium_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone2_tone4:"] = "\U0001f46c",
- [":men_holding_hands_medium_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone2_tone5:"] = "\U0001f46c",
- [":men_holding_hands_medium_light_skin_tone_dark_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone3:"] = "\U0001f46c",
- [":men_holding_hands_medium_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone3_tone1:"] = "\U0001f46c",
- [":men_holding_hands_medium_skin_tone_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone3_tone2:"] = "\U0001f46c",
- [":men_holding_hands_medium_skin_tone_medium_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone3_tone4:"] = "\U0001f46c",
- [":men_holding_hands_medium_skin_tone_medium_dark_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone3_tone5:"] = "\U0001f46c",
- [":men_holding_hands_medium_skin_tone_dark_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone4:"] = "\U0001f46c",
- [":men_holding_hands_medium_dark_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone4_tone1:"] = "\U0001f46c",
- [":men_holding_hands_medium_dark_skin_tone_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone4_tone2:"] = "\U0001f46c",
- [":men_holding_hands_medium_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone4_tone3:"] = "\U0001f46c",
- [":men_holding_hands_medium_dark_skin_tone_medium_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone4_tone5:"] = "\U0001f46c",
- [":men_holding_hands_medium_dark_skin_tone_dark_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone5:"] = "\U0001f46c",
- [":men_holding_hands_dark_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone5_tone1:"] = "\U0001f46c",
- [":men_holding_hands_dark_skin_tone_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone5_tone2:"] = "\U0001f46c",
- [":men_holding_hands_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone5_tone3:"] = "\U0001f46c",
- [":men_holding_hands_dark_skin_tone_medium_skin_tone:"] = "\U0001f46c",
- [":men_holding_hands_tone5_tone4:"] = "\U0001f46c",
- [":men_holding_hands_dark_skin_tone_medium_dark_skin_tone:"] = "\U0001f46c",
- [":men_with_bunny_ears_partying:"] = "\U0001f46f\u200d\u2642\ufe0f",
- [":men_wrestling:"] = "\U0001f93c\u200d\u2642\ufe0f",
- [":menorah:"] = "\U0001f54e",
- [":mens:"] = "\U0001f6b9",
- [":mermaid:"] = "\U0001f9dc\u200d\u2640\ufe0f",
- [":mermaid_tone1:"] = "\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f",
- [":mermaid_light_skin_tone:"] = "\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f",
- [":mermaid::skin-tone-1:"] = "\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f",
- [":mermaid_tone2:"] = "\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f",
- [":mermaid_medium_light_skin_tone:"] = "\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f",
- [":mermaid::skin-tone-2:"] = "\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f",
- [":mermaid_tone3:"] = "\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f",
- [":mermaid_medium_skin_tone:"] = "\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f",
- [":mermaid::skin-tone-3:"] = "\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f",
- [":mermaid_tone4:"] = "\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f",
- [":mermaid_medium_dark_skin_tone:"] = "\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f",
- [":mermaid::skin-tone-4:"] = "\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f",
- [":mermaid_tone5:"] = "\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f",
- [":mermaid_dark_skin_tone:"] = "\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f",
- [":mermaid::skin-tone-5:"] = "\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f",
- [":merman:"] = "\U0001f9dc\u200d\u2642\ufe0f",
- [":merman_tone1:"] = "\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f",
- [":merman_light_skin_tone:"] = "\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f",
- [":merman::skin-tone-1:"] = "\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f",
- [":merman_tone2:"] = "\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f",
- [":merman_medium_light_skin_tone:"] = "\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f",
- [":merman::skin-tone-2:"] = "\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f",
- [":merman_tone3:"] = "\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f",
- [":merman_medium_skin_tone:"] = "\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f",
- [":merman::skin-tone-3:"] = "\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f",
- [":merman_tone4:"] = "\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f",
- [":merman_medium_dark_skin_tone:"] = "\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f",
- [":merman::skin-tone-4:"] = "\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f",
- [":merman_tone5:"] = "\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f",
- [":merman_dark_skin_tone:"] = "\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f",
- [":merman::skin-tone-5:"] = "\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f",
- [":merperson:"] = "\U0001f9dc",
- [":merperson_tone1:"] = "\U0001f9dc\U0001f3fb",
- [":merperson_light_skin_tone:"] = "\U0001f9dc\U0001f3fb",
- [":merperson::skin-tone-1:"] = "\U0001f9dc\U0001f3fb",
- [":merperson_tone2:"] = "\U0001f9dc\U0001f3fc",
- [":merperson_medium_light_skin_tone:"] = "\U0001f9dc\U0001f3fc",
- [":merperson::skin-tone-2:"] = "\U0001f9dc\U0001f3fc",
- [":merperson_tone3:"] = "\U0001f9dc\U0001f3fd",
- [":merperson_medium_skin_tone:"] = "\U0001f9dc\U0001f3fd",
- [":merperson::skin-tone-3:"] = "\U0001f9dc\U0001f3fd",
- [":merperson_tone4:"] = "\U0001f9dc\U0001f3fe",
- [":merperson_medium_dark_skin_tone:"] = "\U0001f9dc\U0001f3fe",
- [":merperson::skin-tone-4:"] = "\U0001f9dc\U0001f3fe",
- [":merperson_tone5:"] = "\U0001f9dc\U0001f3ff",
- [":merperson_dark_skin_tone:"] = "\U0001f9dc\U0001f3ff",
- [":merperson::skin-tone-5:"] = "\U0001f9dc\U0001f3ff",
- [":metal:"] = "\U0001f918",
- [":sign_of_the_horns:"] = "\U0001f918",
- [":metal_tone1:"] = "\U0001f918\U0001f3fb",
- [":sign_of_the_horns_tone1:"] = "\U0001f918\U0001f3fb",
- [":metal::skin-tone-1:"] = "\U0001f918\U0001f3fb",
- [":sign_of_the_horns::skin-tone-1:"] = "\U0001f918\U0001f3fb",
- [":metal_tone2:"] = "\U0001f918\U0001f3fc",
- [":sign_of_the_horns_tone2:"] = "\U0001f918\U0001f3fc",
- [":metal::skin-tone-2:"] = "\U0001f918\U0001f3fc",
- [":sign_of_the_horns::skin-tone-2:"] = "\U0001f918\U0001f3fc",
- [":metal_tone3:"] = "\U0001f918\U0001f3fd",
- [":sign_of_the_horns_tone3:"] = "\U0001f918\U0001f3fd",
- [":metal::skin-tone-3:"] = "\U0001f918\U0001f3fd",
- [":sign_of_the_horns::skin-tone-3:"] = "\U0001f918\U0001f3fd",
- [":metal_tone4:"] = "\U0001f918\U0001f3fe",
- [":sign_of_the_horns_tone4:"] = "\U0001f918\U0001f3fe",
- [":metal::skin-tone-4:"] = "\U0001f918\U0001f3fe",
- [":sign_of_the_horns::skin-tone-4:"] = "\U0001f918\U0001f3fe",
- [":metal_tone5:"] = "\U0001f918\U0001f3ff",
- [":sign_of_the_horns_tone5:"] = "\U0001f918\U0001f3ff",
- [":metal::skin-tone-5:"] = "\U0001f918\U0001f3ff",
- [":sign_of_the_horns::skin-tone-5:"] = "\U0001f918\U0001f3ff",
- [":metro:"] = "\U0001f687",
- [":microbe:"] = "\U0001f9a0",
- [":microphone:"] = "\U0001f3a4",
- [":microphone2:"] = "\U0001f399\ufe0f",
- [":studio_microphone:"] = "\U0001f399\ufe0f",
- [":microscope:"] = "\U0001f52c",
- [":middle_finger:"] = "\U0001f595",
- [":reversed_hand_with_middle_finger_extended:"] = "\U0001f595",
- [":middle_finger_tone1:"] = "\U0001f595\U0001f3fb",
- [":reversed_hand_with_middle_finger_extended_tone1:"] = "\U0001f595\U0001f3fb",
- [":middle_finger::skin-tone-1:"] = "\U0001f595\U0001f3fb",
- [":reversed_hand_with_middle_finger_extended::skin-tone-1:"] = "\U0001f595\U0001f3fb",
- [":middle_finger_tone2:"] = "\U0001f595\U0001f3fc",
- [":reversed_hand_with_middle_finger_extended_tone2:"] = "\U0001f595\U0001f3fc",
- [":middle_finger::skin-tone-2:"] = "\U0001f595\U0001f3fc",
- [":reversed_hand_with_middle_finger_extended::skin-tone-2:"] = "\U0001f595\U0001f3fc",
- [":middle_finger_tone3:"] = "\U0001f595\U0001f3fd",
- [":reversed_hand_with_middle_finger_extended_tone3:"] = "\U0001f595\U0001f3fd",
- [":middle_finger::skin-tone-3:"] = "\U0001f595\U0001f3fd",
- [":reversed_hand_with_middle_finger_extended::skin-tone-3:"] = "\U0001f595\U0001f3fd",
- [":middle_finger_tone4:"] = "\U0001f595\U0001f3fe",
- [":reversed_hand_with_middle_finger_extended_tone4:"] = "\U0001f595\U0001f3fe",
- [":middle_finger::skin-tone-4:"] = "\U0001f595\U0001f3fe",
- [":reversed_hand_with_middle_finger_extended::skin-tone-4:"] = "\U0001f595\U0001f3fe",
- [":middle_finger_tone5:"] = "\U0001f595\U0001f3ff",
- [":reversed_hand_with_middle_finger_extended_tone5:"] = "\U0001f595\U0001f3ff",
- [":middle_finger::skin-tone-5:"] = "\U0001f595\U0001f3ff",
- [":reversed_hand_with_middle_finger_extended::skin-tone-5:"] = "\U0001f595\U0001f3ff",
- [":military_helmet:"] = "\U0001fa96",
- [":military_medal:"] = "\U0001f396\ufe0f",
- [":milk:"] = "\U0001f95b",
- [":glass_of_milk:"] = "\U0001f95b",
- [":milky_way:"] = "\U0001f30c",
- [":minibus:"] = "\U0001f690",
- [":minidisc:"] = "\U0001f4bd",
- [":mirror:"] = "\U0001fa9e",
- [":mobile_phone:"] = "\U0001f4f1",
- [":iphone:"] = "\U0001f4f1",
- [":mobile_phone_off:"] = "\U0001f4f4",
- [":money_mouth:"] = "\U0001f911",
- [":money_mouth_face:"] = "\U0001f911",
- [":money_with_wings:"] = "\U0001f4b8",
- [":moneybag:"] = "\U0001f4b0",
- [":monkey:"] = "\U0001f412",
- [":monkey_face:"] = "\U0001f435",
- [":monorail:"] = "\U0001f69d",
- [":moon_cake:"] = "\U0001f96e",
- [":mortar_board:"] = "\U0001f393",
- [":mosque:"] = "\U0001f54c",
- [":mosquito:"] = "\U0001f99f",
- [":motor_scooter:"] = "\U0001f6f5",
- [":motorbike:"] = "\U0001f6f5",
- [":motorboat:"] = "\U0001f6e5\ufe0f",
- [":motorcycle:"] = "\U0001f3cd\ufe0f",
- [":racing_motorcycle:"] = "\U0001f3cd\ufe0f",
- [":motorized_wheelchair:"] = "\U0001f9bc",
- [":motorway:"] = "\U0001f6e3\ufe0f",
- [":mount_fuji:"] = "\U0001f5fb",
- [":mountain:"] = "\u26f0\ufe0f",
- [":mountain_cableway:"] = "\U0001f6a0",
- [":mountain_railway:"] = "\U0001f69e",
- [":mountain_snow:"] = "\U0001f3d4\ufe0f",
- [":snow_capped_mountain:"] = "\U0001f3d4\ufe0f",
- [":mouse:"] = "\U0001f42d",
- [":mouse_three_button:"] = "\U0001f5b1\ufe0f",
- [":three_button_mouse:"] = "\U0001f5b1\ufe0f",
- [":mouse_trap:"] = "\U0001faa4",
- [":mouse2:"] = "\U0001f401",
- [":movie_camera:"] = "\U0001f3a5",
- [":moyai:"] = "\U0001f5ff",
- [":mrs_claus:"] = "\U0001f936",
- [":mother_christmas:"] = "\U0001f936",
- [":mrs_claus_tone1:"] = "\U0001f936\U0001f3fb",
- [":mother_christmas_tone1:"] = "\U0001f936\U0001f3fb",
- [":mrs_claus::skin-tone-1:"] = "\U0001f936\U0001f3fb",
- [":mother_christmas::skin-tone-1:"] = "\U0001f936\U0001f3fb",
- [":mrs_claus_tone2:"] = "\U0001f936\U0001f3fc",
- [":mother_christmas_tone2:"] = "\U0001f936\U0001f3fc",
- [":mrs_claus::skin-tone-2:"] = "\U0001f936\U0001f3fc",
- [":mother_christmas::skin-tone-2:"] = "\U0001f936\U0001f3fc",
- [":mrs_claus_tone3:"] = "\U0001f936\U0001f3fd",
- [":mother_christmas_tone3:"] = "\U0001f936\U0001f3fd",
- [":mrs_claus::skin-tone-3:"] = "\U0001f936\U0001f3fd",
- [":mother_christmas::skin-tone-3:"] = "\U0001f936\U0001f3fd",
- [":mrs_claus_tone4:"] = "\U0001f936\U0001f3fe",
- [":mother_christmas_tone4:"] = "\U0001f936\U0001f3fe",
- [":mrs_claus::skin-tone-4:"] = "\U0001f936\U0001f3fe",
- [":mother_christmas::skin-tone-4:"] = "\U0001f936\U0001f3fe",
- [":mrs_claus_tone5:"] = "\U0001f936\U0001f3ff",
- [":mother_christmas_tone5:"] = "\U0001f936\U0001f3ff",
- [":mrs_claus::skin-tone-5:"] = "\U0001f936\U0001f3ff",
- [":mother_christmas::skin-tone-5:"] = "\U0001f936\U0001f3ff",
- [":muscle:"] = "\U0001f4aa",
- [":muscle_tone1:"] = "\U0001f4aa\U0001f3fb",
- [":muscle::skin-tone-1:"] = "\U0001f4aa\U0001f3fb",
- [":muscle_tone2:"] = "\U0001f4aa\U0001f3fc",
- [":muscle::skin-tone-2:"] = "\U0001f4aa\U0001f3fc",
- [":muscle_tone3:"] = "\U0001f4aa\U0001f3fd",
- [":muscle::skin-tone-3:"] = "\U0001f4aa\U0001f3fd",
- [":muscle_tone4:"] = "\U0001f4aa\U0001f3fe",
- [":muscle::skin-tone-4:"] = "\U0001f4aa\U0001f3fe",
- [":muscle_tone5:"] = "\U0001f4aa\U0001f3ff",
- [":muscle::skin-tone-5:"] = "\U0001f4aa\U0001f3ff",
- [":mushroom:"] = "\U0001f344",
- [":musical_keyboard:"] = "\U0001f3b9",
- [":musical_note:"] = "\U0001f3b5",
- [":musical_score:"] = "\U0001f3bc",
- [":mute:"] = "\U0001f507",
- [":mx_claus:"] = "\U0001f9d1\u200d\U0001f384",
- [":mx_claus_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f384",
- [":mx_claus_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f384",
- [":mx_claus::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f384",
- [":mx_claus_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f384",
- [":mx_claus_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f384",
- [":mx_claus::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f384",
- [":mx_claus_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f384",
- [":mx_claus_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f384",
- [":mx_claus::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f384",
- [":mx_claus_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f384",
- [":mx_claus_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f384",
- [":mx_claus::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f384",
- [":mx_claus_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f384",
- [":mx_claus_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f384",
- [":mx_claus::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f384",
- [":nail_care:"] = "\U0001f485",
- [":nail_care_tone1:"] = "\U0001f485\U0001f3fb",
- [":nail_care::skin-tone-1:"] = "\U0001f485\U0001f3fb",
- [":nail_care_tone2:"] = "\U0001f485\U0001f3fc",
- [":nail_care::skin-tone-2:"] = "\U0001f485\U0001f3fc",
- [":nail_care_tone3:"] = "\U0001f485\U0001f3fd",
- [":nail_care::skin-tone-3:"] = "\U0001f485\U0001f3fd",
- [":nail_care_tone4:"] = "\U0001f485\U0001f3fe",
- [":nail_care::skin-tone-4:"] = "\U0001f485\U0001f3fe",
- [":nail_care_tone5:"] = "\U0001f485\U0001f3ff",
- [":nail_care::skin-tone-5:"] = "\U0001f485\U0001f3ff",
- [":name_badge:"] = "\U0001f4db",
- [":nauseated_face:"] = "\U0001f922",
- [":sick:"] = "\U0001f922",
- [":nazar_amulet:"] = "\U0001f9ff",
- [":necktie:"] = "\U0001f454",
- [":negative_squared_cross_mark:"] = "\u274e",
- [":nerd:"] = "\U0001f913",
- [":nerd_face:"] = "\U0001f913",
- [":nesting_dolls:"] = "\U0001fa86",
- [":neutral_face:"] = "\U0001f610",
- [":|"] = "\U0001f610",
- [":-|"] = "\U0001f610",
- ["=|"] = "\U0001f610",
- ["=-|"] = "\U0001f610",
- [":new:"] = "\U0001f195",
- [":new_moon:"] = "\U0001f311",
- [":new_moon_with_face:"] = "\U0001f31a",
- [":newspaper:"] = "\U0001f4f0",
- [":newspaper2:"] = "\U0001f5de\ufe0f",
- [":rolled_up_newspaper:"] = "\U0001f5de\ufe0f",
- [":ng:"] = "\U0001f196",
- [":night_with_stars:"] = "\U0001f303",
- [":nine:"] = "\u0039\ufe0f\u20e3",
- [":ninja:"] = "\U0001f977",
- [":ninja_tone1:"] = "\U0001f977\U0001f3fb",
- [":ninja_light_skin_tone:"] = "\U0001f977\U0001f3fb",
- [":ninja::skin-tone-1:"] = "\U0001f977\U0001f3fb",
- [":ninja_tone2:"] = "\U0001f977\U0001f3fc",
- [":ninja_medium_light_skin_tone:"] = "\U0001f977\U0001f3fc",
- [":ninja::skin-tone-2:"] = "\U0001f977\U0001f3fc",
- [":ninja_tone3:"] = "\U0001f977\U0001f3fd",
- [":ninja_medium_skin_tone:"] = "\U0001f977\U0001f3fd",
- [":ninja::skin-tone-3:"] = "\U0001f977\U0001f3fd",
- [":ninja_tone4:"] = "\U0001f977\U0001f3fe",
- [":ninja_medium_dark_skin_tone:"] = "\U0001f977\U0001f3fe",
- [":ninja::skin-tone-4:"] = "\U0001f977\U0001f3fe",
- [":ninja_tone5:"] = "\U0001f977\U0001f3ff",
- [":ninja_dark_skin_tone:"] = "\U0001f977\U0001f3ff",
- [":ninja::skin-tone-5:"] = "\U0001f977\U0001f3ff",
- [":no_bell:"] = "\U0001f515",
- [":no_bicycles:"] = "\U0001f6b3",
- [":no_entry:"] = "\u26d4",
- [":no_entry_sign:"] = "\U0001f6ab",
- [":no_mobile_phones:"] = "\U0001f4f5",
- [":no_mouth:"] = "\U0001f636",
- [":no_pedestrians:"] = "\U0001f6b7",
- [":no_smoking:"] = "\U0001f6ad",
- [":non_potable_water:"] = "\U0001f6b1",
- [":nose:"] = "\U0001f443",
- [":nose_tone1:"] = "\U0001f443\U0001f3fb",
- [":nose::skin-tone-1:"] = "\U0001f443\U0001f3fb",
- [":nose_tone2:"] = "\U0001f443\U0001f3fc",
- [":nose::skin-tone-2:"] = "\U0001f443\U0001f3fc",
- [":nose_tone3:"] = "\U0001f443\U0001f3fd",
- [":nose::skin-tone-3:"] = "\U0001f443\U0001f3fd",
- [":nose_tone4:"] = "\U0001f443\U0001f3fe",
- [":nose::skin-tone-4:"] = "\U0001f443\U0001f3fe",
- [":nose_tone5:"] = "\U0001f443\U0001f3ff",
- [":nose::skin-tone-5:"] = "\U0001f443\U0001f3ff",
- [":notebook:"] = "\U0001f4d3",
- [":notebook_with_decorative_cover:"] = "\U0001f4d4",
- [":notepad_spiral:"] = "\U0001f5d2\ufe0f",
- [":spiral_note_pad:"] = "\U0001f5d2\ufe0f",
- [":notes:"] = "\U0001f3b6",
- [":nut_and_bolt:"] = "\U0001f529",
- [":o:"] = "\u2b55",
- [":o2:"] = "\U0001f17e\ufe0f",
- [":ocean:"] = "\U0001f30a",
- [":octagonal_sign:"] = "\U0001f6d1",
- [":stop_sign:"] = "\U0001f6d1",
- [":octopus:"] = "\U0001f419",
- [":oden:"] = "\U0001f362",
- [":office:"] = "\U0001f3e2",
- [":office_worker:"] = "\U0001f9d1\u200d\U0001f4bc",
- [":office_worker_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bc",
- [":office_worker_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bc",
- [":office_worker::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bc",
- [":office_worker_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bc",
- [":office_worker_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bc",
- [":office_worker::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bc",
- [":office_worker_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bc",
- [":office_worker_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bc",
- [":office_worker::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bc",
- [":office_worker_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bc",
- [":office_worker_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bc",
- [":office_worker::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bc",
- [":office_worker_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bc",
- [":office_worker_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bc",
- [":office_worker::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bc",
- [":oil:"] = "\U0001f6e2\ufe0f",
- [":oil_drum:"] = "\U0001f6e2\ufe0f",
- [":ok:"] = "\U0001f197",
- [":ok_hand:"] = "\U0001f44c",
- [":ok_hand_tone1:"] = "\U0001f44c\U0001f3fb",
- [":ok_hand::skin-tone-1:"] = "\U0001f44c\U0001f3fb",
- [":ok_hand_tone2:"] = "\U0001f44c\U0001f3fc",
- [":ok_hand::skin-tone-2:"] = "\U0001f44c\U0001f3fc",
- [":ok_hand_tone3:"] = "\U0001f44c\U0001f3fd",
- [":ok_hand::skin-tone-3:"] = "\U0001f44c\U0001f3fd",
- [":ok_hand_tone4:"] = "\U0001f44c\U0001f3fe",
- [":ok_hand::skin-tone-4:"] = "\U0001f44c\U0001f3fe",
- [":ok_hand_tone5:"] = "\U0001f44c\U0001f3ff",
- [":ok_hand::skin-tone-5:"] = "\U0001f44c\U0001f3ff",
- [":older_adult:"] = "\U0001f9d3",
- [":older_adult_tone1:"] = "\U0001f9d3\U0001f3fb",
- [":older_adult_light_skin_tone:"] = "\U0001f9d3\U0001f3fb",
- [":older_adult::skin-tone-1:"] = "\U0001f9d3\U0001f3fb",
- [":older_adult_tone2:"] = "\U0001f9d3\U0001f3fc",
- [":older_adult_medium_light_skin_tone:"] = "\U0001f9d3\U0001f3fc",
- [":older_adult::skin-tone-2:"] = "\U0001f9d3\U0001f3fc",
- [":older_adult_tone3:"] = "\U0001f9d3\U0001f3fd",
- [":older_adult_medium_skin_tone:"] = "\U0001f9d3\U0001f3fd",
- [":older_adult::skin-tone-3:"] = "\U0001f9d3\U0001f3fd",
- [":older_adult_tone4:"] = "\U0001f9d3\U0001f3fe",
- [":older_adult_medium_dark_skin_tone:"] = "\U0001f9d3\U0001f3fe",
- [":older_adult::skin-tone-4:"] = "\U0001f9d3\U0001f3fe",
- [":older_adult_tone5:"] = "\U0001f9d3\U0001f3ff",
- [":older_adult_dark_skin_tone:"] = "\U0001f9d3\U0001f3ff",
- [":older_adult::skin-tone-5:"] = "\U0001f9d3\U0001f3ff",
- [":older_man:"] = "\U0001f474",
- [":older_man_tone1:"] = "\U0001f474\U0001f3fb",
- [":older_man::skin-tone-1:"] = "\U0001f474\U0001f3fb",
- [":older_man_tone2:"] = "\U0001f474\U0001f3fc",
- [":older_man::skin-tone-2:"] = "\U0001f474\U0001f3fc",
- [":older_man_tone3:"] = "\U0001f474\U0001f3fd",
- [":older_man::skin-tone-3:"] = "\U0001f474\U0001f3fd",
- [":older_man_tone4:"] = "\U0001f474\U0001f3fe",
- [":older_man::skin-tone-4:"] = "\U0001f474\U0001f3fe",
- [":older_man_tone5:"] = "\U0001f474\U0001f3ff",
- [":older_man::skin-tone-5:"] = "\U0001f474\U0001f3ff",
- [":older_woman:"] = "\U0001f475",
- [":grandma:"] = "\U0001f475",
- [":older_woman_tone1:"] = "\U0001f475\U0001f3fb",
- [":grandma_tone1:"] = "\U0001f475\U0001f3fb",
- [":older_woman::skin-tone-1:"] = "\U0001f475\U0001f3fb",
- [":grandma::skin-tone-1:"] = "\U0001f475\U0001f3fb",
- [":older_woman_tone2:"] = "\U0001f475\U0001f3fc",
- [":grandma_tone2:"] = "\U0001f475\U0001f3fc",
- [":older_woman::skin-tone-2:"] = "\U0001f475\U0001f3fc",
- [":grandma::skin-tone-2:"] = "\U0001f475\U0001f3fc",
- [":older_woman_tone3:"] = "\U0001f475\U0001f3fd",
- [":grandma_tone3:"] = "\U0001f475\U0001f3fd",
- [":older_woman::skin-tone-3:"] = "\U0001f475\U0001f3fd",
- [":grandma::skin-tone-3:"] = "\U0001f475\U0001f3fd",
- [":older_woman_tone4:"] = "\U0001f475\U0001f3fe",
- [":grandma_tone4:"] = "\U0001f475\U0001f3fe",
- [":older_woman::skin-tone-4:"] = "\U0001f475\U0001f3fe",
- [":grandma::skin-tone-4:"] = "\U0001f475\U0001f3fe",
- [":older_woman_tone5:"] = "\U0001f475\U0001f3ff",
- [":grandma_tone5:"] = "\U0001f475\U0001f3ff",
- [":older_woman::skin-tone-5:"] = "\U0001f475\U0001f3ff",
- [":grandma::skin-tone-5:"] = "\U0001f475\U0001f3ff",
- [":olive:"] = "\U0001fad2",
- [":om_symbol:"] = "\U0001f549\ufe0f",
- [":on:"] = "\U0001f51b",
- [":oncoming_automobile:"] = "\U0001f698",
- [":oncoming_bus:"] = "\U0001f68d",
- [":oncoming_police_car:"] = "\U0001f694",
- [":oncoming_taxi:"] = "\U0001f696",
- [":one:"] = "\u0031\ufe0f\u20e3",
- [":one_piece_swimsuit:"] = "\U0001fa71",
- [":onion:"] = "\U0001f9c5",
- [":open_file_folder:"] = "\U0001f4c2",
- [":open_hands:"] = "\U0001f450",
- [":open_hands_tone1:"] = "\U0001f450\U0001f3fb",
- [":open_hands::skin-tone-1:"] = "\U0001f450\U0001f3fb",
- [":open_hands_tone2:"] = "\U0001f450\U0001f3fc",
- [":open_hands::skin-tone-2:"] = "\U0001f450\U0001f3fc",
- [":open_hands_tone3:"] = "\U0001f450\U0001f3fd",
- [":open_hands::skin-tone-3:"] = "\U0001f450\U0001f3fd",
- [":open_hands_tone4:"] = "\U0001f450\U0001f3fe",
- [":open_hands::skin-tone-4:"] = "\U0001f450\U0001f3fe",
- [":open_hands_tone5:"] = "\U0001f450\U0001f3ff",
- [":open_hands::skin-tone-5:"] = "\U0001f450\U0001f3ff",
- [":open_mouth:"] = "\U0001f62e",
- [":o"] = "\U0001f62e",
- [":-o"] = "\U0001f62e",
- [":O"] = "\U0001f62e",
- [":-O"] = "\U0001f62e",
- ["=o"] = "\U0001f62e",
- ["=-o"] = "\U0001f62e",
- ["=O"] = "\U0001f62e",
- ["=-O"] = "\U0001f62e",
- [":ophiuchus:"] = "\u26ce",
- [":orange_book:"] = "\U0001f4d9",
- [":orange_circle:"] = "\U0001f7e0",
- [":orange_heart:"] = "\U0001f9e1",
- [":orange_square:"] = "\U0001f7e7",
- [":orangutan:"] = "\U0001f9a7",
- [":orthodox_cross:"] = "\u2626\ufe0f",
- [":otter:"] = "\U0001f9a6",
- [":outbox_tray:"] = "\U0001f4e4",
- [":owl:"] = "\U0001f989",
- [":ox:"] = "\U0001f402",
- [":oyster:"] = "\U0001f9aa",
- [":package:"] = "\U0001f4e6",
- [":page_facing_up:"] = "\U0001f4c4",
- [":page_with_curl:"] = "\U0001f4c3",
- [":pager:"] = "\U0001f4df",
- [":paintbrush:"] = "\U0001f58c\ufe0f",
- [":lower_left_paintbrush:"] = "\U0001f58c\ufe0f",
- [":palm_tree:"] = "\U0001f334",
- [":palms_up_together:"] = "\U0001f932",
- [":palms_up_together_tone1:"] = "\U0001f932\U0001f3fb",
- [":palms_up_together_light_skin_tone:"] = "\U0001f932\U0001f3fb",
- [":palms_up_together::skin-tone-1:"] = "\U0001f932\U0001f3fb",
- [":palms_up_together_tone2:"] = "\U0001f932\U0001f3fc",
- [":palms_up_together_medium_light_skin_tone:"] = "\U0001f932\U0001f3fc",
- [":palms_up_together::skin-tone-2:"] = "\U0001f932\U0001f3fc",
- [":palms_up_together_tone3:"] = "\U0001f932\U0001f3fd",
- [":palms_up_together_medium_skin_tone:"] = "\U0001f932\U0001f3fd",
- [":palms_up_together::skin-tone-3:"] = "\U0001f932\U0001f3fd",
- [":palms_up_together_tone4:"] = "\U0001f932\U0001f3fe",
- [":palms_up_together_medium_dark_skin_tone:"] = "\U0001f932\U0001f3fe",
- [":palms_up_together::skin-tone-4:"] = "\U0001f932\U0001f3fe",
- [":palms_up_together_tone5:"] = "\U0001f932\U0001f3ff",
- [":palms_up_together_dark_skin_tone:"] = "\U0001f932\U0001f3ff",
- [":palms_up_together::skin-tone-5:"] = "\U0001f932\U0001f3ff",
- [":pancakes:"] = "\U0001f95e",
- [":panda_face:"] = "\U0001f43c",
- [":paperclip:"] = "\U0001f4ce",
- [":paperclips:"] = "\U0001f587\ufe0f",
- [":linked_paperclips:"] = "\U0001f587\ufe0f",
- [":parachute:"] = "\U0001fa82",
- [":park:"] = "\U0001f3de\ufe0f",
- [":national_park:"] = "\U0001f3de\ufe0f",
- [":parking:"] = "\U0001f17f\ufe0f",
- [":parrot:"] = "\U0001f99c",
- [":part_alternation_mark:"] = "\u303d\ufe0f",
- [":partly_sunny:"] = "\u26c5",
- [":partying_face:"] = "\U0001f973",
- [":passport_control:"] = "\U0001f6c2",
- [":pause_button:"] = "\u23f8\ufe0f",
- [":double_vertical_bar:"] = "\u23f8\ufe0f",
- [":peace:"] = "\u262e\ufe0f",
- [":peace_symbol:"] = "\u262e\ufe0f",
- [":peach:"] = "\U0001f351",
- [":peacock:"] = "\U0001f99a",
- [":peanuts:"] = "\U0001f95c",
- [":shelled_peanut:"] = "\U0001f95c",
- [":pear:"] = "\U0001f350",
- [":pen_ballpoint:"] = "\U0001f58a\ufe0f",
- [":lower_left_ballpoint_pen:"] = "\U0001f58a\ufe0f",
- [":pen_fountain:"] = "\U0001f58b\ufe0f",
- [":lower_left_fountain_pen:"] = "\U0001f58b\ufe0f",
- [":pencil:"] = "\U0001f4dd",
- [":memo:"] = "\U0001f4dd",
- [":pencil2:"] = "\u270f\ufe0f",
- [":penguin:"] = "\U0001f427",
- [":pensive:"] = "\U0001f614",
- [":people_holding_hands:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone1_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_light_skin_tone_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone1_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_light_skin_tone_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone1_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone1_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_light_skin_tone_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone2_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_light_skin_tone_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone2_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_light_skin_tone_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone2_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone2_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_light_skin_tone_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone3_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_skin_tone_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone3_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_skin_tone_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone3_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_skin_tone_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone3_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_skin_tone_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone4_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_dark_skin_tone_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone4_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone4_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_dark_skin_tone_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone4_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_medium_dark_skin_tone_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone5_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_dark_skin_tone_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone5_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone5_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_dark_skin_tone_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_tone5_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_holding_hands_dark_skin_tone_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
- [":people_hugging:"] = "\U0001fac2",
- [":people_with_bunny_ears_partying:"] = "\U0001f46f",
- [":dancers:"] = "\U0001f46f",
- [":people_wrestling:"] = "\U0001f93c",
- [":wrestlers:"] = "\U0001f93c",
- [":wrestling:"] = "\U0001f93c",
- [":performing_arts:"] = "\U0001f3ad",
- [":persevere:"] = "\U0001f623",
- [":person_bald:"] = "\U0001f9d1\u200d\U0001f9b2",
- [":person_biking:"] = "\U0001f6b4",
- [":bicyclist:"] = "\U0001f6b4",
- [":person_biking_tone1:"] = "\U0001f6b4\U0001f3fb",
- [":bicyclist_tone1:"] = "\U0001f6b4\U0001f3fb",
- [":person_biking::skin-tone-1:"] = "\U0001f6b4\U0001f3fb",
- [":bicyclist::skin-tone-1:"] = "\U0001f6b4\U0001f3fb",
- [":person_biking_tone2:"] = "\U0001f6b4\U0001f3fc",
- [":bicyclist_tone2:"] = "\U0001f6b4\U0001f3fc",
- [":person_biking::skin-tone-2:"] = "\U0001f6b4\U0001f3fc",
- [":bicyclist::skin-tone-2:"] = "\U0001f6b4\U0001f3fc",
- [":person_biking_tone3:"] = "\U0001f6b4\U0001f3fd",
- [":bicyclist_tone3:"] = "\U0001f6b4\U0001f3fd",
- [":person_biking::skin-tone-3:"] = "\U0001f6b4\U0001f3fd",
- [":bicyclist::skin-tone-3:"] = "\U0001f6b4\U0001f3fd",
- [":person_biking_tone4:"] = "\U0001f6b4\U0001f3fe",
- [":bicyclist_tone4:"] = "\U0001f6b4\U0001f3fe",
- [":person_biking::skin-tone-4:"] = "\U0001f6b4\U0001f3fe",
- [":bicyclist::skin-tone-4:"] = "\U0001f6b4\U0001f3fe",
- [":person_biking_tone5:"] = "\U0001f6b4\U0001f3ff",
- [":bicyclist_tone5:"] = "\U0001f6b4\U0001f3ff",
- [":person_biking::skin-tone-5:"] = "\U0001f6b4\U0001f3ff",
- [":bicyclist::skin-tone-5:"] = "\U0001f6b4\U0001f3ff",
- [":person_bouncing_ball:"] = "\u26f9\ufe0f",
- [":basketball_player:"] = "\u26f9\ufe0f",
- [":person_with_ball:"] = "\u26f9\ufe0f",
- [":person_bouncing_ball_tone1:"] = "\u26f9\U0001f3fb",
- [":basketball_player_tone1:"] = "\u26f9\U0001f3fb",
- [":person_with_ball_tone1:"] = "\u26f9\U0001f3fb",
- [":person_bouncing_ball::skin-tone-1:"] = "\u26f9\U0001f3fb",
- [":basketball_player::skin-tone-1:"] = "\u26f9\U0001f3fb",
- [":person_with_ball::skin-tone-1:"] = "\u26f9\U0001f3fb",
- [":person_bouncing_ball_tone2:"] = "\u26f9\U0001f3fc",
- [":basketball_player_tone2:"] = "\u26f9\U0001f3fc",
- [":person_with_ball_tone2:"] = "\u26f9\U0001f3fc",
- [":person_bouncing_ball::skin-tone-2:"] = "\u26f9\U0001f3fc",
- [":basketball_player::skin-tone-2:"] = "\u26f9\U0001f3fc",
- [":person_with_ball::skin-tone-2:"] = "\u26f9\U0001f3fc",
- [":person_bouncing_ball_tone3:"] = "\u26f9\U0001f3fd",
- [":basketball_player_tone3:"] = "\u26f9\U0001f3fd",
- [":person_with_ball_tone3:"] = "\u26f9\U0001f3fd",
- [":person_bouncing_ball::skin-tone-3:"] = "\u26f9\U0001f3fd",
- [":basketball_player::skin-tone-3:"] = "\u26f9\U0001f3fd",
- [":person_with_ball::skin-tone-3:"] = "\u26f9\U0001f3fd",
- [":person_bouncing_ball_tone4:"] = "\u26f9\U0001f3fe",
- [":basketball_player_tone4:"] = "\u26f9\U0001f3fe",
- [":person_with_ball_tone4:"] = "\u26f9\U0001f3fe",
- [":person_bouncing_ball::skin-tone-4:"] = "\u26f9\U0001f3fe",
- [":basketball_player::skin-tone-4:"] = "\u26f9\U0001f3fe",
- [":person_with_ball::skin-tone-4:"] = "\u26f9\U0001f3fe",
- [":person_bouncing_ball_tone5:"] = "\u26f9\U0001f3ff",
- [":basketball_player_tone5:"] = "\u26f9\U0001f3ff",
- [":person_with_ball_tone5:"] = "\u26f9\U0001f3ff",
- [":person_bouncing_ball::skin-tone-5:"] = "\u26f9\U0001f3ff",
- [":basketball_player::skin-tone-5:"] = "\u26f9\U0001f3ff",
- [":person_with_ball::skin-tone-5:"] = "\u26f9\U0001f3ff",
- [":person_bowing:"] = "\U0001f647",
- [":bow:"] = "\U0001f647",
- [":person_bowing_tone1:"] = "\U0001f647\U0001f3fb",
- [":bow_tone1:"] = "\U0001f647\U0001f3fb",
- [":person_bowing::skin-tone-1:"] = "\U0001f647\U0001f3fb",
- [":bow::skin-tone-1:"] = "\U0001f647\U0001f3fb",
- [":person_bowing_tone2:"] = "\U0001f647\U0001f3fc",
- [":bow_tone2:"] = "\U0001f647\U0001f3fc",
- [":person_bowing::skin-tone-2:"] = "\U0001f647\U0001f3fc",
- [":bow::skin-tone-2:"] = "\U0001f647\U0001f3fc",
- [":person_bowing_tone3:"] = "\U0001f647\U0001f3fd",
- [":bow_tone3:"] = "\U0001f647\U0001f3fd",
- [":person_bowing::skin-tone-3:"] = "\U0001f647\U0001f3fd",
- [":bow::skin-tone-3:"] = "\U0001f647\U0001f3fd",
- [":person_bowing_tone4:"] = "\U0001f647\U0001f3fe",
- [":bow_tone4:"] = "\U0001f647\U0001f3fe",
- [":person_bowing::skin-tone-4:"] = "\U0001f647\U0001f3fe",
- [":bow::skin-tone-4:"] = "\U0001f647\U0001f3fe",
- [":person_bowing_tone5:"] = "\U0001f647\U0001f3ff",
- [":bow_tone5:"] = "\U0001f647\U0001f3ff",
- [":person_bowing::skin-tone-5:"] = "\U0001f647\U0001f3ff",
- [":bow::skin-tone-5:"] = "\U0001f647\U0001f3ff",
- [":person_climbing:"] = "\U0001f9d7",
- [":person_climbing_tone1:"] = "\U0001f9d7\U0001f3fb",
- [":person_climbing_light_skin_tone:"] = "\U0001f9d7\U0001f3fb",
- [":person_climbing::skin-tone-1:"] = "\U0001f9d7\U0001f3fb",
- [":person_climbing_tone2:"] = "\U0001f9d7\U0001f3fc",
- [":person_climbing_medium_light_skin_tone:"] = "\U0001f9d7\U0001f3fc",
- [":person_climbing::skin-tone-2:"] = "\U0001f9d7\U0001f3fc",
- [":person_climbing_tone3:"] = "\U0001f9d7\U0001f3fd",
- [":person_climbing_medium_skin_tone:"] = "\U0001f9d7\U0001f3fd",
- [":person_climbing::skin-tone-3:"] = "\U0001f9d7\U0001f3fd",
- [":person_climbing_tone4:"] = "\U0001f9d7\U0001f3fe",
- [":person_climbing_medium_dark_skin_tone:"] = "\U0001f9d7\U0001f3fe",
- [":person_climbing::skin-tone-4:"] = "\U0001f9d7\U0001f3fe",
- [":person_climbing_tone5:"] = "\U0001f9d7\U0001f3ff",
- [":person_climbing_dark_skin_tone:"] = "\U0001f9d7\U0001f3ff",
- [":person_climbing::skin-tone-5:"] = "\U0001f9d7\U0001f3ff",
- [":person_curly_hair:"] = "\U0001f9d1\u200d\U0001f9b1",
- [":person_doing_cartwheel:"] = "\U0001f938",
- [":cartwheel:"] = "\U0001f938",
- [":person_doing_cartwheel_tone1:"] = "\U0001f938\U0001f3fb",
- [":cartwheel_tone1:"] = "\U0001f938\U0001f3fb",
- [":person_doing_cartwheel::skin-tone-1:"] = "\U0001f938\U0001f3fb",
- [":cartwheel::skin-tone-1:"] = "\U0001f938\U0001f3fb",
- [":person_doing_cartwheel_tone2:"] = "\U0001f938\U0001f3fc",
- [":cartwheel_tone2:"] = "\U0001f938\U0001f3fc",
- [":person_doing_cartwheel::skin-tone-2:"] = "\U0001f938\U0001f3fc",
- [":cartwheel::skin-tone-2:"] = "\U0001f938\U0001f3fc",
- [":person_doing_cartwheel_tone3:"] = "\U0001f938\U0001f3fd",
- [":cartwheel_tone3:"] = "\U0001f938\U0001f3fd",
- [":person_doing_cartwheel::skin-tone-3:"] = "\U0001f938\U0001f3fd",
- [":cartwheel::skin-tone-3:"] = "\U0001f938\U0001f3fd",
- [":person_doing_cartwheel_tone4:"] = "\U0001f938\U0001f3fe",
- [":cartwheel_tone4:"] = "\U0001f938\U0001f3fe",
- [":person_doing_cartwheel::skin-tone-4:"] = "\U0001f938\U0001f3fe",
- [":cartwheel::skin-tone-4:"] = "\U0001f938\U0001f3fe",
- [":person_doing_cartwheel_tone5:"] = "\U0001f938\U0001f3ff",
- [":cartwheel_tone5:"] = "\U0001f938\U0001f3ff",
- [":person_doing_cartwheel::skin-tone-5:"] = "\U0001f938\U0001f3ff",
- [":cartwheel::skin-tone-5:"] = "\U0001f938\U0001f3ff",
- [":person_facepalming:"] = "\U0001f926",
- [":face_palm:"] = "\U0001f926",
- [":facepalm:"] = "\U0001f926",
- [":person_facepalming_tone1:"] = "\U0001f926\U0001f3fb",
- [":face_palm_tone1:"] = "\U0001f926\U0001f3fb",
- [":facepalm_tone1:"] = "\U0001f926\U0001f3fb",
- [":person_facepalming::skin-tone-1:"] = "\U0001f926\U0001f3fb",
- [":face_palm::skin-tone-1:"] = "\U0001f926\U0001f3fb",
- [":facepalm::skin-tone-1:"] = "\U0001f926\U0001f3fb",
- [":person_facepalming_tone2:"] = "\U0001f926\U0001f3fc",
- [":face_palm_tone2:"] = "\U0001f926\U0001f3fc",
- [":facepalm_tone2:"] = "\U0001f926\U0001f3fc",
- [":person_facepalming::skin-tone-2:"] = "\U0001f926\U0001f3fc",
- [":face_palm::skin-tone-2:"] = "\U0001f926\U0001f3fc",
- [":facepalm::skin-tone-2:"] = "\U0001f926\U0001f3fc",
- [":person_facepalming_tone3:"] = "\U0001f926\U0001f3fd",
- [":face_palm_tone3:"] = "\U0001f926\U0001f3fd",
- [":facepalm_tone3:"] = "\U0001f926\U0001f3fd",
- [":person_facepalming::skin-tone-3:"] = "\U0001f926\U0001f3fd",
- [":face_palm::skin-tone-3:"] = "\U0001f926\U0001f3fd",
- [":facepalm::skin-tone-3:"] = "\U0001f926\U0001f3fd",
- [":person_facepalming_tone4:"] = "\U0001f926\U0001f3fe",
- [":face_palm_tone4:"] = "\U0001f926\U0001f3fe",
- [":facepalm_tone4:"] = "\U0001f926\U0001f3fe",
- [":person_facepalming::skin-tone-4:"] = "\U0001f926\U0001f3fe",
- [":face_palm::skin-tone-4:"] = "\U0001f926\U0001f3fe",
- [":facepalm::skin-tone-4:"] = "\U0001f926\U0001f3fe",
- [":person_facepalming_tone5:"] = "\U0001f926\U0001f3ff",
- [":face_palm_tone5:"] = "\U0001f926\U0001f3ff",
- [":facepalm_tone5:"] = "\U0001f926\U0001f3ff",
- [":person_facepalming::skin-tone-5:"] = "\U0001f926\U0001f3ff",
- [":face_palm::skin-tone-5:"] = "\U0001f926\U0001f3ff",
- [":facepalm::skin-tone-5:"] = "\U0001f926\U0001f3ff",
- [":person_feeding_baby:"] = "\U0001f9d1\u200d\U0001f37c",
- [":person_feeding_baby_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f37c",
- [":person_feeding_baby_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f37c",
- [":person_feeding_baby::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f37c",
- [":person_feeding_baby_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f37c",
- [":person_feeding_baby_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f37c",
- [":person_feeding_baby::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f37c",
- [":person_feeding_baby_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f37c",
- [":person_feeding_baby_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f37c",
- [":person_feeding_baby::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f37c",
- [":person_feeding_baby_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f37c",
- [":person_feeding_baby_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f37c",
- [":person_feeding_baby::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f37c",
- [":person_feeding_baby_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f37c",
- [":person_feeding_baby_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f37c",
- [":person_feeding_baby::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f37c",
- [":person_fencing:"] = "\U0001f93a",
- [":fencer:"] = "\U0001f93a",
- [":fencing:"] = "\U0001f93a",
- [":person_frowning:"] = "\U0001f64d",
- [":person_frowning_tone1:"] = "\U0001f64d\U0001f3fb",
- [":person_frowning::skin-tone-1:"] = "\U0001f64d\U0001f3fb",
- [":person_frowning_tone2:"] = "\U0001f64d\U0001f3fc",
- [":person_frowning::skin-tone-2:"] = "\U0001f64d\U0001f3fc",
- [":person_frowning_tone3:"] = "\U0001f64d\U0001f3fd",
- [":person_frowning::skin-tone-3:"] = "\U0001f64d\U0001f3fd",
- [":person_frowning_tone4:"] = "\U0001f64d\U0001f3fe",
- [":person_frowning::skin-tone-4:"] = "\U0001f64d\U0001f3fe",
- [":person_frowning_tone5:"] = "\U0001f64d\U0001f3ff",
- [":person_frowning::skin-tone-5:"] = "\U0001f64d\U0001f3ff",
- [":person_gesturing_no:"] = "\U0001f645",
- [":no_good:"] = "\U0001f645",
- [":person_gesturing_no_tone1:"] = "\U0001f645\U0001f3fb",
- [":no_good_tone1:"] = "\U0001f645\U0001f3fb",
- [":person_gesturing_no::skin-tone-1:"] = "\U0001f645\U0001f3fb",
- [":no_good::skin-tone-1:"] = "\U0001f645\U0001f3fb",
- [":person_gesturing_no_tone2:"] = "\U0001f645\U0001f3fc",
- [":no_good_tone2:"] = "\U0001f645\U0001f3fc",
- [":person_gesturing_no::skin-tone-2:"] = "\U0001f645\U0001f3fc",
- [":no_good::skin-tone-2:"] = "\U0001f645\U0001f3fc",
- [":person_gesturing_no_tone3:"] = "\U0001f645\U0001f3fd",
- [":no_good_tone3:"] = "\U0001f645\U0001f3fd",
- [":person_gesturing_no::skin-tone-3:"] = "\U0001f645\U0001f3fd",
- [":no_good::skin-tone-3:"] = "\U0001f645\U0001f3fd",
- [":person_gesturing_no_tone4:"] = "\U0001f645\U0001f3fe",
- [":no_good_tone4:"] = "\U0001f645\U0001f3fe",
- [":person_gesturing_no::skin-tone-4:"] = "\U0001f645\U0001f3fe",
- [":no_good::skin-tone-4:"] = "\U0001f645\U0001f3fe",
- [":person_gesturing_no_tone5:"] = "\U0001f645\U0001f3ff",
- [":no_good_tone5:"] = "\U0001f645\U0001f3ff",
- [":person_gesturing_no::skin-tone-5:"] = "\U0001f645\U0001f3ff",
- [":no_good::skin-tone-5:"] = "\U0001f645\U0001f3ff",
- [":person_gesturing_ok:"] = "\U0001f646",
- [":ok_woman:"] = "\U0001f646",
- [":person_gesturing_ok_tone1:"] = "\U0001f646\U0001f3fb",
- [":ok_woman_tone1:"] = "\U0001f646\U0001f3fb",
- [":person_gesturing_ok::skin-tone-1:"] = "\U0001f646\U0001f3fb",
- [":ok_woman::skin-tone-1:"] = "\U0001f646\U0001f3fb",
- [":person_gesturing_ok_tone2:"] = "\U0001f646\U0001f3fc",
- [":ok_woman_tone2:"] = "\U0001f646\U0001f3fc",
- [":person_gesturing_ok::skin-tone-2:"] = "\U0001f646\U0001f3fc",
- [":ok_woman::skin-tone-2:"] = "\U0001f646\U0001f3fc",
- [":person_gesturing_ok_tone3:"] = "\U0001f646\U0001f3fd",
- [":ok_woman_tone3:"] = "\U0001f646\U0001f3fd",
- [":person_gesturing_ok::skin-tone-3:"] = "\U0001f646\U0001f3fd",
- [":ok_woman::skin-tone-3:"] = "\U0001f646\U0001f3fd",
- [":person_gesturing_ok_tone4:"] = "\U0001f646\U0001f3fe",
- [":ok_woman_tone4:"] = "\U0001f646\U0001f3fe",
- [":person_gesturing_ok::skin-tone-4:"] = "\U0001f646\U0001f3fe",
- [":ok_woman::skin-tone-4:"] = "\U0001f646\U0001f3fe",
- [":person_gesturing_ok_tone5:"] = "\U0001f646\U0001f3ff",
- [":ok_woman_tone5:"] = "\U0001f646\U0001f3ff",
- [":person_gesturing_ok::skin-tone-5:"] = "\U0001f646\U0001f3ff",
- [":ok_woman::skin-tone-5:"] = "\U0001f646\U0001f3ff",
- [":person_getting_haircut:"] = "\U0001f487",
- [":haircut:"] = "\U0001f487",
- [":person_getting_haircut_tone1:"] = "\U0001f487\U0001f3fb",
- [":haircut_tone1:"] = "\U0001f487\U0001f3fb",
- [":person_getting_haircut::skin-tone-1:"] = "\U0001f487\U0001f3fb",
- [":haircut::skin-tone-1:"] = "\U0001f487\U0001f3fb",
- [":person_getting_haircut_tone2:"] = "\U0001f487\U0001f3fc",
- [":haircut_tone2:"] = "\U0001f487\U0001f3fc",
- [":person_getting_haircut::skin-tone-2:"] = "\U0001f487\U0001f3fc",
- [":haircut::skin-tone-2:"] = "\U0001f487\U0001f3fc",
- [":person_getting_haircut_tone3:"] = "\U0001f487\U0001f3fd",
- [":haircut_tone3:"] = "\U0001f487\U0001f3fd",
- [":person_getting_haircut::skin-tone-3:"] = "\U0001f487\U0001f3fd",
- [":haircut::skin-tone-3:"] = "\U0001f487\U0001f3fd",
- [":person_getting_haircut_tone4:"] = "\U0001f487\U0001f3fe",
- [":haircut_tone4:"] = "\U0001f487\U0001f3fe",
- [":person_getting_haircut::skin-tone-4:"] = "\U0001f487\U0001f3fe",
- [":haircut::skin-tone-4:"] = "\U0001f487\U0001f3fe",
- [":person_getting_haircut_tone5:"] = "\U0001f487\U0001f3ff",
- [":haircut_tone5:"] = "\U0001f487\U0001f3ff",
- [":person_getting_haircut::skin-tone-5:"] = "\U0001f487\U0001f3ff",
- [":haircut::skin-tone-5:"] = "\U0001f487\U0001f3ff",
- [":person_getting_massage:"] = "\U0001f486",
- [":massage:"] = "\U0001f486",
- [":person_getting_massage_tone1:"] = "\U0001f486\U0001f3fb",
- [":massage_tone1:"] = "\U0001f486\U0001f3fb",
- [":person_getting_massage::skin-tone-1:"] = "\U0001f486\U0001f3fb",
- [":massage::skin-tone-1:"] = "\U0001f486\U0001f3fb",
- [":person_getting_massage_tone2:"] = "\U0001f486\U0001f3fc",
- [":massage_tone2:"] = "\U0001f486\U0001f3fc",
- [":person_getting_massage::skin-tone-2:"] = "\U0001f486\U0001f3fc",
- [":massage::skin-tone-2:"] = "\U0001f486\U0001f3fc",
- [":person_getting_massage_tone3:"] = "\U0001f486\U0001f3fd",
- [":massage_tone3:"] = "\U0001f486\U0001f3fd",
- [":person_getting_massage::skin-tone-3:"] = "\U0001f486\U0001f3fd",
- [":massage::skin-tone-3:"] = "\U0001f486\U0001f3fd",
- [":person_getting_massage_tone4:"] = "\U0001f486\U0001f3fe",
- [":massage_tone4:"] = "\U0001f486\U0001f3fe",
- [":person_getting_massage::skin-tone-4:"] = "\U0001f486\U0001f3fe",
- [":massage::skin-tone-4:"] = "\U0001f486\U0001f3fe",
- [":person_getting_massage_tone5:"] = "\U0001f486\U0001f3ff",
- [":massage_tone5:"] = "\U0001f486\U0001f3ff",
- [":person_getting_massage::skin-tone-5:"] = "\U0001f486\U0001f3ff",
- [":massage::skin-tone-5:"] = "\U0001f486\U0001f3ff",
- [":person_golfing:"] = "\U0001f3cc\ufe0f",
- [":golfer:"] = "\U0001f3cc\ufe0f",
- [":person_golfing_tone1:"] = "\U0001f3cc\U0001f3fb",
- [":person_golfing_light_skin_tone:"] = "\U0001f3cc\U0001f3fb",
- [":person_golfing::skin-tone-1:"] = "\U0001f3cc\U0001f3fb",
- [":golfer::skin-tone-1:"] = "\U0001f3cc\U0001f3fb",
- [":person_golfing_tone2:"] = "\U0001f3cc\U0001f3fc",
- [":person_golfing_medium_light_skin_tone:"] = "\U0001f3cc\U0001f3fc",
- [":person_golfing::skin-tone-2:"] = "\U0001f3cc\U0001f3fc",
- [":golfer::skin-tone-2:"] = "\U0001f3cc\U0001f3fc",
- [":person_golfing_tone3:"] = "\U0001f3cc\U0001f3fd",
- [":person_golfing_medium_skin_tone:"] = "\U0001f3cc\U0001f3fd",
- [":person_golfing::skin-tone-3:"] = "\U0001f3cc\U0001f3fd",
- [":golfer::skin-tone-3:"] = "\U0001f3cc\U0001f3fd",
- [":person_golfing_tone4:"] = "\U0001f3cc\U0001f3fe",
- [":person_golfing_medium_dark_skin_tone:"] = "\U0001f3cc\U0001f3fe",
- [":person_golfing::skin-tone-4:"] = "\U0001f3cc\U0001f3fe",
- [":golfer::skin-tone-4:"] = "\U0001f3cc\U0001f3fe",
- [":person_golfing_tone5:"] = "\U0001f3cc\U0001f3ff",
- [":person_golfing_dark_skin_tone:"] = "\U0001f3cc\U0001f3ff",
- [":person_golfing::skin-tone-5:"] = "\U0001f3cc\U0001f3ff",
- [":golfer::skin-tone-5:"] = "\U0001f3cc\U0001f3ff",
- [":person_in_bed_tone1:"] = "\U0001f6cc\U0001f3fb",
- [":person_in_bed_light_skin_tone:"] = "\U0001f6cc\U0001f3fb",
- [":sleeping_accommodation::skin-tone-1:"] = "\U0001f6cc\U0001f3fb",
- [":person_in_bed_tone2:"] = "\U0001f6cc\U0001f3fc",
- [":person_in_bed_medium_light_skin_tone:"] = "\U0001f6cc\U0001f3fc",
- [":sleeping_accommodation::skin-tone-2:"] = "\U0001f6cc\U0001f3fc",
- [":person_in_bed_tone3:"] = "\U0001f6cc\U0001f3fd",
- [":person_in_bed_medium_skin_tone:"] = "\U0001f6cc\U0001f3fd",
- [":sleeping_accommodation::skin-tone-3:"] = "\U0001f6cc\U0001f3fd",
- [":person_in_bed_tone4:"] = "\U0001f6cc\U0001f3fe",
- [":person_in_bed_medium_dark_skin_tone:"] = "\U0001f6cc\U0001f3fe",
- [":sleeping_accommodation::skin-tone-4:"] = "\U0001f6cc\U0001f3fe",
- [":person_in_bed_tone5:"] = "\U0001f6cc\U0001f3ff",
- [":person_in_bed_dark_skin_tone:"] = "\U0001f6cc\U0001f3ff",
- [":sleeping_accommodation::skin-tone-5:"] = "\U0001f6cc\U0001f3ff",
- [":person_in_lotus_position:"] = "\U0001f9d8",
- [":person_in_lotus_position_tone1:"] = "\U0001f9d8\U0001f3fb",
- [":person_in_lotus_position_light_skin_tone:"] = "\U0001f9d8\U0001f3fb",
- [":person_in_lotus_position::skin-tone-1:"] = "\U0001f9d8\U0001f3fb",
- [":person_in_lotus_position_tone2:"] = "\U0001f9d8\U0001f3fc",
- [":person_in_lotus_position_medium_light_skin_tone:"] = "\U0001f9d8\U0001f3fc",
- [":person_in_lotus_position::skin-tone-2:"] = "\U0001f9d8\U0001f3fc",
- [":person_in_lotus_position_tone3:"] = "\U0001f9d8\U0001f3fd",
- [":person_in_lotus_position_medium_skin_tone:"] = "\U0001f9d8\U0001f3fd",
- [":person_in_lotus_position::skin-tone-3:"] = "\U0001f9d8\U0001f3fd",
- [":person_in_lotus_position_tone4:"] = "\U0001f9d8\U0001f3fe",
- [":person_in_lotus_position_medium_dark_skin_tone:"] = "\U0001f9d8\U0001f3fe",
- [":person_in_lotus_position::skin-tone-4:"] = "\U0001f9d8\U0001f3fe",
- [":person_in_lotus_position_tone5:"] = "\U0001f9d8\U0001f3ff",
- [":person_in_lotus_position_dark_skin_tone:"] = "\U0001f9d8\U0001f3ff",
- [":person_in_lotus_position::skin-tone-5:"] = "\U0001f9d8\U0001f3ff",
- [":person_in_manual_wheelchair:"] = "\U0001f9d1\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bd",
- [":person_in_manual_wheelchair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bd",
- [":person_in_manual_wheelchair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bd",
- [":person_in_manual_wheelchair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bd",
- [":person_in_manual_wheelchair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bd",
- [":person_in_manual_wheelchair_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bd",
- [":person_in_manual_wheelchair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bd",
- [":person_in_motorized_wheelchair:"] = "\U0001f9d1\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bc",
- [":person_in_motorized_wheelchair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bc",
- [":person_in_steamy_room:"] = "\U0001f9d6",
- [":person_in_steamy_room_tone1:"] = "\U0001f9d6\U0001f3fb",
- [":person_in_steamy_room_light_skin_tone:"] = "\U0001f9d6\U0001f3fb",
- [":person_in_steamy_room::skin-tone-1:"] = "\U0001f9d6\U0001f3fb",
- [":person_in_steamy_room_tone2:"] = "\U0001f9d6\U0001f3fc",
- [":person_in_steamy_room_medium_light_skin_tone:"] = "\U0001f9d6\U0001f3fc",
- [":person_in_steamy_room::skin-tone-2:"] = "\U0001f9d6\U0001f3fc",
- [":person_in_steamy_room_tone3:"] = "\U0001f9d6\U0001f3fd",
- [":person_in_steamy_room_medium_skin_tone:"] = "\U0001f9d6\U0001f3fd",
- [":person_in_steamy_room::skin-tone-3:"] = "\U0001f9d6\U0001f3fd",
- [":person_in_steamy_room_tone4:"] = "\U0001f9d6\U0001f3fe",
- [":person_in_steamy_room_medium_dark_skin_tone:"] = "\U0001f9d6\U0001f3fe",
- [":person_in_steamy_room::skin-tone-4:"] = "\U0001f9d6\U0001f3fe",
- [":person_in_steamy_room_tone5:"] = "\U0001f9d6\U0001f3ff",
- [":person_in_steamy_room_dark_skin_tone:"] = "\U0001f9d6\U0001f3ff",
- [":person_in_steamy_room::skin-tone-5:"] = "\U0001f9d6\U0001f3ff",
- [":person_in_tuxedo:"] = "\U0001f935",
- [":person_in_tuxedo_tone1:"] = "\U0001f935\U0001f3fb",
- [":tuxedo_tone1:"] = "\U0001f935\U0001f3fb",
- [":person_in_tuxedo::skin-tone-1:"] = "\U0001f935\U0001f3fb",
- [":person_in_tuxedo_tone2:"] = "\U0001f935\U0001f3fc",
- [":tuxedo_tone2:"] = "\U0001f935\U0001f3fc",
- [":person_in_tuxedo::skin-tone-2:"] = "\U0001f935\U0001f3fc",
- [":person_in_tuxedo_tone3:"] = "\U0001f935\U0001f3fd",
- [":tuxedo_tone3:"] = "\U0001f935\U0001f3fd",
- [":person_in_tuxedo::skin-tone-3:"] = "\U0001f935\U0001f3fd",
- [":person_in_tuxedo_tone4:"] = "\U0001f935\U0001f3fe",
- [":tuxedo_tone4:"] = "\U0001f935\U0001f3fe",
- [":person_in_tuxedo::skin-tone-4:"] = "\U0001f935\U0001f3fe",
- [":person_in_tuxedo_tone5:"] = "\U0001f935\U0001f3ff",
- [":tuxedo_tone5:"] = "\U0001f935\U0001f3ff",
- [":person_in_tuxedo::skin-tone-5:"] = "\U0001f935\U0001f3ff",
- [":person_juggling:"] = "\U0001f939",
- [":juggling:"] = "\U0001f939",
- [":juggler:"] = "\U0001f939",
- [":person_juggling_tone1:"] = "\U0001f939\U0001f3fb",
- [":juggling_tone1:"] = "\U0001f939\U0001f3fb",
- [":juggler_tone1:"] = "\U0001f939\U0001f3fb",
- [":person_juggling::skin-tone-1:"] = "\U0001f939\U0001f3fb",
- [":juggling::skin-tone-1:"] = "\U0001f939\U0001f3fb",
- [":juggler::skin-tone-1:"] = "\U0001f939\U0001f3fb",
- [":person_juggling_tone2:"] = "\U0001f939\U0001f3fc",
- [":juggling_tone2:"] = "\U0001f939\U0001f3fc",
- [":juggler_tone2:"] = "\U0001f939\U0001f3fc",
- [":person_juggling::skin-tone-2:"] = "\U0001f939\U0001f3fc",
- [":juggling::skin-tone-2:"] = "\U0001f939\U0001f3fc",
- [":juggler::skin-tone-2:"] = "\U0001f939\U0001f3fc",
- [":person_juggling_tone3:"] = "\U0001f939\U0001f3fd",
- [":juggling_tone3:"] = "\U0001f939\U0001f3fd",
- [":juggler_tone3:"] = "\U0001f939\U0001f3fd",
- [":person_juggling::skin-tone-3:"] = "\U0001f939\U0001f3fd",
- [":juggling::skin-tone-3:"] = "\U0001f939\U0001f3fd",
- [":juggler::skin-tone-3:"] = "\U0001f939\U0001f3fd",
- [":person_juggling_tone4:"] = "\U0001f939\U0001f3fe",
- [":juggling_tone4:"] = "\U0001f939\U0001f3fe",
- [":juggler_tone4:"] = "\U0001f939\U0001f3fe",
- [":person_juggling::skin-tone-4:"] = "\U0001f939\U0001f3fe",
- [":juggling::skin-tone-4:"] = "\U0001f939\U0001f3fe",
- [":juggler::skin-tone-4:"] = "\U0001f939\U0001f3fe",
- [":person_juggling_tone5:"] = "\U0001f939\U0001f3ff",
- [":juggling_tone5:"] = "\U0001f939\U0001f3ff",
- [":juggler_tone5:"] = "\U0001f939\U0001f3ff",
- [":person_juggling::skin-tone-5:"] = "\U0001f939\U0001f3ff",
- [":juggling::skin-tone-5:"] = "\U0001f939\U0001f3ff",
- [":juggler::skin-tone-5:"] = "\U0001f939\U0001f3ff",
- [":person_kneeling:"] = "\U0001f9ce",
- [":person_kneeling_tone1:"] = "\U0001f9ce\U0001f3fb",
- [":person_kneeling_light_skin_tone:"] = "\U0001f9ce\U0001f3fb",
- [":person_kneeling::skin-tone-1:"] = "\U0001f9ce\U0001f3fb",
- [":person_kneeling_tone2:"] = "\U0001f9ce\U0001f3fc",
- [":person_kneeling_medium_light_skin_tone:"] = "\U0001f9ce\U0001f3fc",
- [":person_kneeling::skin-tone-2:"] = "\U0001f9ce\U0001f3fc",
- [":person_kneeling_tone3:"] = "\U0001f9ce\U0001f3fd",
- [":person_kneeling_medium_skin_tone:"] = "\U0001f9ce\U0001f3fd",
- [":person_kneeling::skin-tone-3:"] = "\U0001f9ce\U0001f3fd",
- [":person_kneeling_tone4:"] = "\U0001f9ce\U0001f3fe",
- [":person_kneeling_medium_dark_skin_tone:"] = "\U0001f9ce\U0001f3fe",
- [":person_kneeling::skin-tone-4:"] = "\U0001f9ce\U0001f3fe",
- [":person_kneeling_tone5:"] = "\U0001f9ce\U0001f3ff",
- [":person_kneeling_dark_skin_tone:"] = "\U0001f9ce\U0001f3ff",
- [":person_kneeling::skin-tone-5:"] = "\U0001f9ce\U0001f3ff",
- [":person_lifting_weights:"] = "\U0001f3cb\ufe0f",
- [":lifter:"] = "\U0001f3cb\ufe0f",
- [":weight_lifter:"] = "\U0001f3cb\ufe0f",
- [":person_lifting_weights_tone1:"] = "\U0001f3cb\U0001f3fb",
- [":lifter_tone1:"] = "\U0001f3cb\U0001f3fb",
- [":weight_lifter_tone1:"] = "\U0001f3cb\U0001f3fb",
- [":person_lifting_weights::skin-tone-1:"] = "\U0001f3cb\U0001f3fb",
- [":lifter::skin-tone-1:"] = "\U0001f3cb\U0001f3fb",
- [":weight_lifter::skin-tone-1:"] = "\U0001f3cb\U0001f3fb",
- [":person_lifting_weights_tone2:"] = "\U0001f3cb\U0001f3fc",
- [":lifter_tone2:"] = "\U0001f3cb\U0001f3fc",
- [":weight_lifter_tone2:"] = "\U0001f3cb\U0001f3fc",
- [":person_lifting_weights::skin-tone-2:"] = "\U0001f3cb\U0001f3fc",
- [":lifter::skin-tone-2:"] = "\U0001f3cb\U0001f3fc",
- [":weight_lifter::skin-tone-2:"] = "\U0001f3cb\U0001f3fc",
- [":person_lifting_weights_tone3:"] = "\U0001f3cb\U0001f3fd",
- [":lifter_tone3:"] = "\U0001f3cb\U0001f3fd",
- [":weight_lifter_tone3:"] = "\U0001f3cb\U0001f3fd",
- [":person_lifting_weights::skin-tone-3:"] = "\U0001f3cb\U0001f3fd",
- [":lifter::skin-tone-3:"] = "\U0001f3cb\U0001f3fd",
- [":weight_lifter::skin-tone-3:"] = "\U0001f3cb\U0001f3fd",
- [":person_lifting_weights_tone4:"] = "\U0001f3cb\U0001f3fe",
- [":lifter_tone4:"] = "\U0001f3cb\U0001f3fe",
- [":weight_lifter_tone4:"] = "\U0001f3cb\U0001f3fe",
- [":person_lifting_weights::skin-tone-4:"] = "\U0001f3cb\U0001f3fe",
- [":lifter::skin-tone-4:"] = "\U0001f3cb\U0001f3fe",
- [":weight_lifter::skin-tone-4:"] = "\U0001f3cb\U0001f3fe",
- [":person_lifting_weights_tone5:"] = "\U0001f3cb\U0001f3ff",
- [":lifter_tone5:"] = "\U0001f3cb\U0001f3ff",
- [":weight_lifter_tone5:"] = "\U0001f3cb\U0001f3ff",
- [":person_lifting_weights::skin-tone-5:"] = "\U0001f3cb\U0001f3ff",
- [":lifter::skin-tone-5:"] = "\U0001f3cb\U0001f3ff",
- [":weight_lifter::skin-tone-5:"] = "\U0001f3cb\U0001f3ff",
- [":person_mountain_biking:"] = "\U0001f6b5",
- [":mountain_bicyclist:"] = "\U0001f6b5",
- [":person_mountain_biking_tone1:"] = "\U0001f6b5\U0001f3fb",
- [":mountain_bicyclist_tone1:"] = "\U0001f6b5\U0001f3fb",
- [":person_mountain_biking::skin-tone-1:"] = "\U0001f6b5\U0001f3fb",
- [":mountain_bicyclist::skin-tone-1:"] = "\U0001f6b5\U0001f3fb",
- [":person_mountain_biking_tone2:"] = "\U0001f6b5\U0001f3fc",
- [":mountain_bicyclist_tone2:"] = "\U0001f6b5\U0001f3fc",
- [":person_mountain_biking::skin-tone-2:"] = "\U0001f6b5\U0001f3fc",
- [":mountain_bicyclist::skin-tone-2:"] = "\U0001f6b5\U0001f3fc",
- [":person_mountain_biking_tone3:"] = "\U0001f6b5\U0001f3fd",
- [":mountain_bicyclist_tone3:"] = "\U0001f6b5\U0001f3fd",
- [":person_mountain_biking::skin-tone-3:"] = "\U0001f6b5\U0001f3fd",
- [":mountain_bicyclist::skin-tone-3:"] = "\U0001f6b5\U0001f3fd",
- [":person_mountain_biking_tone4:"] = "\U0001f6b5\U0001f3fe",
- [":mountain_bicyclist_tone4:"] = "\U0001f6b5\U0001f3fe",
- [":person_mountain_biking::skin-tone-4:"] = "\U0001f6b5\U0001f3fe",
- [":mountain_bicyclist::skin-tone-4:"] = "\U0001f6b5\U0001f3fe",
- [":person_mountain_biking_tone5:"] = "\U0001f6b5\U0001f3ff",
- [":mountain_bicyclist_tone5:"] = "\U0001f6b5\U0001f3ff",
- [":person_mountain_biking::skin-tone-5:"] = "\U0001f6b5\U0001f3ff",
- [":mountain_bicyclist::skin-tone-5:"] = "\U0001f6b5\U0001f3ff",
- [":person_playing_handball:"] = "\U0001f93e",
- [":handball:"] = "\U0001f93e",
- [":person_playing_handball_tone1:"] = "\U0001f93e\U0001f3fb",
- [":handball_tone1:"] = "\U0001f93e\U0001f3fb",
- [":person_playing_handball::skin-tone-1:"] = "\U0001f93e\U0001f3fb",
- [":handball::skin-tone-1:"] = "\U0001f93e\U0001f3fb",
- [":person_playing_handball_tone2:"] = "\U0001f93e\U0001f3fc",
- [":handball_tone2:"] = "\U0001f93e\U0001f3fc",
- [":person_playing_handball::skin-tone-2:"] = "\U0001f93e\U0001f3fc",
- [":handball::skin-tone-2:"] = "\U0001f93e\U0001f3fc",
- [":person_playing_handball_tone3:"] = "\U0001f93e\U0001f3fd",
- [":handball_tone3:"] = "\U0001f93e\U0001f3fd",
- [":person_playing_handball::skin-tone-3:"] = "\U0001f93e\U0001f3fd",
- [":handball::skin-tone-3:"] = "\U0001f93e\U0001f3fd",
- [":person_playing_handball_tone4:"] = "\U0001f93e\U0001f3fe",
- [":handball_tone4:"] = "\U0001f93e\U0001f3fe",
- [":person_playing_handball::skin-tone-4:"] = "\U0001f93e\U0001f3fe",
- [":handball::skin-tone-4:"] = "\U0001f93e\U0001f3fe",
- [":person_playing_handball_tone5:"] = "\U0001f93e\U0001f3ff",
- [":handball_tone5:"] = "\U0001f93e\U0001f3ff",
- [":person_playing_handball::skin-tone-5:"] = "\U0001f93e\U0001f3ff",
- [":handball::skin-tone-5:"] = "\U0001f93e\U0001f3ff",
- [":person_playing_water_polo:"] = "\U0001f93d",
- [":water_polo:"] = "\U0001f93d",
- [":person_playing_water_polo_tone1:"] = "\U0001f93d\U0001f3fb",
- [":water_polo_tone1:"] = "\U0001f93d\U0001f3fb",
- [":person_playing_water_polo::skin-tone-1:"] = "\U0001f93d\U0001f3fb",
- [":water_polo::skin-tone-1:"] = "\U0001f93d\U0001f3fb",
- [":person_playing_water_polo_tone2:"] = "\U0001f93d\U0001f3fc",
- [":water_polo_tone2:"] = "\U0001f93d\U0001f3fc",
- [":person_playing_water_polo::skin-tone-2:"] = "\U0001f93d\U0001f3fc",
- [":water_polo::skin-tone-2:"] = "\U0001f93d\U0001f3fc",
- [":person_playing_water_polo_tone3:"] = "\U0001f93d\U0001f3fd",
- [":water_polo_tone3:"] = "\U0001f93d\U0001f3fd",
- [":person_playing_water_polo::skin-tone-3:"] = "\U0001f93d\U0001f3fd",
- [":water_polo::skin-tone-3:"] = "\U0001f93d\U0001f3fd",
- [":person_playing_water_polo_tone4:"] = "\U0001f93d\U0001f3fe",
- [":water_polo_tone4:"] = "\U0001f93d\U0001f3fe",
- [":person_playing_water_polo::skin-tone-4:"] = "\U0001f93d\U0001f3fe",
- [":water_polo::skin-tone-4:"] = "\U0001f93d\U0001f3fe",
- [":person_playing_water_polo_tone5:"] = "\U0001f93d\U0001f3ff",
- [":water_polo_tone5:"] = "\U0001f93d\U0001f3ff",
- [":person_playing_water_polo::skin-tone-5:"] = "\U0001f93d\U0001f3ff",
- [":water_polo::skin-tone-5:"] = "\U0001f93d\U0001f3ff",
- [":person_pouting:"] = "\U0001f64e",
- [":person_with_pouting_face:"] = "\U0001f64e",
- [":person_pouting_tone1:"] = "\U0001f64e\U0001f3fb",
- [":person_with_pouting_face_tone1:"] = "\U0001f64e\U0001f3fb",
- [":person_pouting::skin-tone-1:"] = "\U0001f64e\U0001f3fb",
- [":person_with_pouting_face::skin-tone-1:"] = "\U0001f64e\U0001f3fb",
- [":person_pouting_tone2:"] = "\U0001f64e\U0001f3fc",
- [":person_with_pouting_face_tone2:"] = "\U0001f64e\U0001f3fc",
- [":person_pouting::skin-tone-2:"] = "\U0001f64e\U0001f3fc",
- [":person_with_pouting_face::skin-tone-2:"] = "\U0001f64e\U0001f3fc",
- [":person_pouting_tone3:"] = "\U0001f64e\U0001f3fd",
- [":person_with_pouting_face_tone3:"] = "\U0001f64e\U0001f3fd",
- [":person_pouting::skin-tone-3:"] = "\U0001f64e\U0001f3fd",
- [":person_with_pouting_face::skin-tone-3:"] = "\U0001f64e\U0001f3fd",
- [":person_pouting_tone4:"] = "\U0001f64e\U0001f3fe",
- [":person_with_pouting_face_tone4:"] = "\U0001f64e\U0001f3fe",
- [":person_pouting::skin-tone-4:"] = "\U0001f64e\U0001f3fe",
- [":person_with_pouting_face::skin-tone-4:"] = "\U0001f64e\U0001f3fe",
- [":person_pouting_tone5:"] = "\U0001f64e\U0001f3ff",
- [":person_with_pouting_face_tone5:"] = "\U0001f64e\U0001f3ff",
- [":person_pouting::skin-tone-5:"] = "\U0001f64e\U0001f3ff",
- [":person_with_pouting_face::skin-tone-5:"] = "\U0001f64e\U0001f3ff",
- [":person_raising_hand:"] = "\U0001f64b",
- [":raising_hand:"] = "\U0001f64b",
- [":person_raising_hand_tone1:"] = "\U0001f64b\U0001f3fb",
- [":raising_hand_tone1:"] = "\U0001f64b\U0001f3fb",
- [":person_raising_hand::skin-tone-1:"] = "\U0001f64b\U0001f3fb",
- [":raising_hand::skin-tone-1:"] = "\U0001f64b\U0001f3fb",
- [":person_raising_hand_tone2:"] = "\U0001f64b\U0001f3fc",
- [":raising_hand_tone2:"] = "\U0001f64b\U0001f3fc",
- [":person_raising_hand::skin-tone-2:"] = "\U0001f64b\U0001f3fc",
- [":raising_hand::skin-tone-2:"] = "\U0001f64b\U0001f3fc",
- [":person_raising_hand_tone3:"] = "\U0001f64b\U0001f3fd",
- [":raising_hand_tone3:"] = "\U0001f64b\U0001f3fd",
- [":person_raising_hand::skin-tone-3:"] = "\U0001f64b\U0001f3fd",
- [":raising_hand::skin-tone-3:"] = "\U0001f64b\U0001f3fd",
- [":person_raising_hand_tone4:"] = "\U0001f64b\U0001f3fe",
- [":raising_hand_tone4:"] = "\U0001f64b\U0001f3fe",
- [":person_raising_hand::skin-tone-4:"] = "\U0001f64b\U0001f3fe",
- [":raising_hand::skin-tone-4:"] = "\U0001f64b\U0001f3fe",
- [":person_raising_hand_tone5:"] = "\U0001f64b\U0001f3ff",
- [":raising_hand_tone5:"] = "\U0001f64b\U0001f3ff",
- [":person_raising_hand::skin-tone-5:"] = "\U0001f64b\U0001f3ff",
- [":raising_hand::skin-tone-5:"] = "\U0001f64b\U0001f3ff",
- [":person_red_hair:"] = "\U0001f9d1\u200d\U0001f9b0",
- [":person_rowing_boat:"] = "\U0001f6a3",
- [":rowboat:"] = "\U0001f6a3",
- [":person_rowing_boat_tone1:"] = "\U0001f6a3\U0001f3fb",
- [":rowboat_tone1:"] = "\U0001f6a3\U0001f3fb",
- [":person_rowing_boat::skin-tone-1:"] = "\U0001f6a3\U0001f3fb",
- [":rowboat::skin-tone-1:"] = "\U0001f6a3\U0001f3fb",
- [":person_rowing_boat_tone2:"] = "\U0001f6a3\U0001f3fc",
- [":rowboat_tone2:"] = "\U0001f6a3\U0001f3fc",
- [":person_rowing_boat::skin-tone-2:"] = "\U0001f6a3\U0001f3fc",
- [":rowboat::skin-tone-2:"] = "\U0001f6a3\U0001f3fc",
- [":person_rowing_boat_tone3:"] = "\U0001f6a3\U0001f3fd",
- [":rowboat_tone3:"] = "\U0001f6a3\U0001f3fd",
- [":person_rowing_boat::skin-tone-3:"] = "\U0001f6a3\U0001f3fd",
- [":rowboat::skin-tone-3:"] = "\U0001f6a3\U0001f3fd",
- [":person_rowing_boat_tone4:"] = "\U0001f6a3\U0001f3fe",
- [":rowboat_tone4:"] = "\U0001f6a3\U0001f3fe",
- [":person_rowing_boat::skin-tone-4:"] = "\U0001f6a3\U0001f3fe",
- [":rowboat::skin-tone-4:"] = "\U0001f6a3\U0001f3fe",
- [":person_rowing_boat_tone5:"] = "\U0001f6a3\U0001f3ff",
- [":rowboat_tone5:"] = "\U0001f6a3\U0001f3ff",
- [":person_rowing_boat::skin-tone-5:"] = "\U0001f6a3\U0001f3ff",
- [":rowboat::skin-tone-5:"] = "\U0001f6a3\U0001f3ff",
- [":person_running:"] = "\U0001f3c3",
- [":runner:"] = "\U0001f3c3",
- [":person_running_tone1:"] = "\U0001f3c3\U0001f3fb",
- [":runner_tone1:"] = "\U0001f3c3\U0001f3fb",
- [":person_running::skin-tone-1:"] = "\U0001f3c3\U0001f3fb",
- [":runner::skin-tone-1:"] = "\U0001f3c3\U0001f3fb",
- [":person_running_tone2:"] = "\U0001f3c3\U0001f3fc",
- [":runner_tone2:"] = "\U0001f3c3\U0001f3fc",
- [":person_running::skin-tone-2:"] = "\U0001f3c3\U0001f3fc",
- [":runner::skin-tone-2:"] = "\U0001f3c3\U0001f3fc",
- [":person_running_tone3:"] = "\U0001f3c3\U0001f3fd",
- [":runner_tone3:"] = "\U0001f3c3\U0001f3fd",
- [":person_running::skin-tone-3:"] = "\U0001f3c3\U0001f3fd",
- [":runner::skin-tone-3:"] = "\U0001f3c3\U0001f3fd",
- [":person_running_tone4:"] = "\U0001f3c3\U0001f3fe",
- [":runner_tone4:"] = "\U0001f3c3\U0001f3fe",
- [":person_running::skin-tone-4:"] = "\U0001f3c3\U0001f3fe",
- [":runner::skin-tone-4:"] = "\U0001f3c3\U0001f3fe",
- [":person_running_tone5:"] = "\U0001f3c3\U0001f3ff",
- [":runner_tone5:"] = "\U0001f3c3\U0001f3ff",
- [":person_running::skin-tone-5:"] = "\U0001f3c3\U0001f3ff",
- [":runner::skin-tone-5:"] = "\U0001f3c3\U0001f3ff",
- [":person_shrugging:"] = "\U0001f937",
- [":shrug:"] = "\U0001f937",
- [":person_shrugging_tone1:"] = "\U0001f937\U0001f3fb",
- [":shrug_tone1:"] = "\U0001f937\U0001f3fb",
- [":person_shrugging::skin-tone-1:"] = "\U0001f937\U0001f3fb",
- [":shrug::skin-tone-1:"] = "\U0001f937\U0001f3fb",
- [":person_shrugging_tone2:"] = "\U0001f937\U0001f3fc",
- [":shrug_tone2:"] = "\U0001f937\U0001f3fc",
- [":person_shrugging::skin-tone-2:"] = "\U0001f937\U0001f3fc",
- [":shrug::skin-tone-2:"] = "\U0001f937\U0001f3fc",
- [":person_shrugging_tone3:"] = "\U0001f937\U0001f3fd",
- [":shrug_tone3:"] = "\U0001f937\U0001f3fd",
- [":person_shrugging::skin-tone-3:"] = "\U0001f937\U0001f3fd",
- [":shrug::skin-tone-3:"] = "\U0001f937\U0001f3fd",
- [":person_shrugging_tone4:"] = "\U0001f937\U0001f3fe",
- [":shrug_tone4:"] = "\U0001f937\U0001f3fe",
- [":person_shrugging::skin-tone-4:"] = "\U0001f937\U0001f3fe",
- [":shrug::skin-tone-4:"] = "\U0001f937\U0001f3fe",
- [":person_shrugging_tone5:"] = "\U0001f937\U0001f3ff",
- [":shrug_tone5:"] = "\U0001f937\U0001f3ff",
- [":person_shrugging::skin-tone-5:"] = "\U0001f937\U0001f3ff",
- [":shrug::skin-tone-5:"] = "\U0001f937\U0001f3ff",
- [":person_standing:"] = "\U0001f9cd",
- [":person_standing_tone1:"] = "\U0001f9cd\U0001f3fb",
- [":person_standing_light_skin_tone:"] = "\U0001f9cd\U0001f3fb",
- [":person_standing::skin-tone-1:"] = "\U0001f9cd\U0001f3fb",
- [":person_standing_tone2:"] = "\U0001f9cd\U0001f3fc",
- [":person_standing_medium_light_skin_tone:"] = "\U0001f9cd\U0001f3fc",
- [":person_standing::skin-tone-2:"] = "\U0001f9cd\U0001f3fc",
- [":person_standing_tone3:"] = "\U0001f9cd\U0001f3fd",
- [":person_standing_medium_skin_tone:"] = "\U0001f9cd\U0001f3fd",
- [":person_standing::skin-tone-3:"] = "\U0001f9cd\U0001f3fd",
- [":person_standing_tone4:"] = "\U0001f9cd\U0001f3fe",
- [":person_standing_medium_dark_skin_tone:"] = "\U0001f9cd\U0001f3fe",
- [":person_standing::skin-tone-4:"] = "\U0001f9cd\U0001f3fe",
- [":person_standing_tone5:"] = "\U0001f9cd\U0001f3ff",
- [":person_standing_dark_skin_tone:"] = "\U0001f9cd\U0001f3ff",
- [":person_standing::skin-tone-5:"] = "\U0001f9cd\U0001f3ff",
- [":person_surfing:"] = "\U0001f3c4",
- [":surfer:"] = "\U0001f3c4",
- [":person_surfing_tone1:"] = "\U0001f3c4\U0001f3fb",
- [":surfer_tone1:"] = "\U0001f3c4\U0001f3fb",
- [":person_surfing::skin-tone-1:"] = "\U0001f3c4\U0001f3fb",
- [":surfer::skin-tone-1:"] = "\U0001f3c4\U0001f3fb",
- [":person_surfing_tone2:"] = "\U0001f3c4\U0001f3fc",
- [":surfer_tone2:"] = "\U0001f3c4\U0001f3fc",
- [":person_surfing::skin-tone-2:"] = "\U0001f3c4\U0001f3fc",
- [":surfer::skin-tone-2:"] = "\U0001f3c4\U0001f3fc",
- [":person_surfing_tone3:"] = "\U0001f3c4\U0001f3fd",
- [":surfer_tone3:"] = "\U0001f3c4\U0001f3fd",
- [":person_surfing::skin-tone-3:"] = "\U0001f3c4\U0001f3fd",
- [":surfer::skin-tone-3:"] = "\U0001f3c4\U0001f3fd",
- [":person_surfing_tone4:"] = "\U0001f3c4\U0001f3fe",
- [":surfer_tone4:"] = "\U0001f3c4\U0001f3fe",
- [":person_surfing::skin-tone-4:"] = "\U0001f3c4\U0001f3fe",
- [":surfer::skin-tone-4:"] = "\U0001f3c4\U0001f3fe",
- [":person_surfing_tone5:"] = "\U0001f3c4\U0001f3ff",
- [":surfer_tone5:"] = "\U0001f3c4\U0001f3ff",
- [":person_surfing::skin-tone-5:"] = "\U0001f3c4\U0001f3ff",
- [":surfer::skin-tone-5:"] = "\U0001f3c4\U0001f3ff",
- [":person_swimming:"] = "\U0001f3ca",
- [":swimmer:"] = "\U0001f3ca",
- [":person_swimming_tone1:"] = "\U0001f3ca\U0001f3fb",
- [":swimmer_tone1:"] = "\U0001f3ca\U0001f3fb",
- [":person_swimming::skin-tone-1:"] = "\U0001f3ca\U0001f3fb",
- [":swimmer::skin-tone-1:"] = "\U0001f3ca\U0001f3fb",
- [":person_swimming_tone2:"] = "\U0001f3ca\U0001f3fc",
- [":swimmer_tone2:"] = "\U0001f3ca\U0001f3fc",
- [":person_swimming::skin-tone-2:"] = "\U0001f3ca\U0001f3fc",
- [":swimmer::skin-tone-2:"] = "\U0001f3ca\U0001f3fc",
- [":person_swimming_tone3:"] = "\U0001f3ca\U0001f3fd",
- [":swimmer_tone3:"] = "\U0001f3ca\U0001f3fd",
- [":person_swimming::skin-tone-3:"] = "\U0001f3ca\U0001f3fd",
- [":swimmer::skin-tone-3:"] = "\U0001f3ca\U0001f3fd",
- [":person_swimming_tone4:"] = "\U0001f3ca\U0001f3fe",
- [":swimmer_tone4:"] = "\U0001f3ca\U0001f3fe",
- [":person_swimming::skin-tone-4:"] = "\U0001f3ca\U0001f3fe",
- [":swimmer::skin-tone-4:"] = "\U0001f3ca\U0001f3fe",
- [":person_swimming_tone5:"] = "\U0001f3ca\U0001f3ff",
- [":swimmer_tone5:"] = "\U0001f3ca\U0001f3ff",
- [":person_swimming::skin-tone-5:"] = "\U0001f3ca\U0001f3ff",
- [":swimmer::skin-tone-5:"] = "\U0001f3ca\U0001f3ff",
- [":person_tipping_hand:"] = "\U0001f481",
- [":information_desk_person:"] = "\U0001f481",
- [":person_tipping_hand_tone1:"] = "\U0001f481\U0001f3fb",
- [":information_desk_person_tone1:"] = "\U0001f481\U0001f3fb",
- [":person_tipping_hand::skin-tone-1:"] = "\U0001f481\U0001f3fb",
- [":information_desk_person::skin-tone-1:"] = "\U0001f481\U0001f3fb",
- [":person_tipping_hand_tone2:"] = "\U0001f481\U0001f3fc",
- [":information_desk_person_tone2:"] = "\U0001f481\U0001f3fc",
- [":person_tipping_hand::skin-tone-2:"] = "\U0001f481\U0001f3fc",
- [":information_desk_person::skin-tone-2:"] = "\U0001f481\U0001f3fc",
- [":person_tipping_hand_tone3:"] = "\U0001f481\U0001f3fd",
- [":information_desk_person_tone3:"] = "\U0001f481\U0001f3fd",
- [":person_tipping_hand::skin-tone-3:"] = "\U0001f481\U0001f3fd",
- [":information_desk_person::skin-tone-3:"] = "\U0001f481\U0001f3fd",
- [":person_tipping_hand_tone4:"] = "\U0001f481\U0001f3fe",
- [":information_desk_person_tone4:"] = "\U0001f481\U0001f3fe",
- [":person_tipping_hand::skin-tone-4:"] = "\U0001f481\U0001f3fe",
- [":information_desk_person::skin-tone-4:"] = "\U0001f481\U0001f3fe",
- [":person_tipping_hand_tone5:"] = "\U0001f481\U0001f3ff",
- [":information_desk_person_tone5:"] = "\U0001f481\U0001f3ff",
- [":person_tipping_hand::skin-tone-5:"] = "\U0001f481\U0001f3ff",
- [":information_desk_person::skin-tone-5:"] = "\U0001f481\U0001f3ff",
- [":person_tone1_bald:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b2",
- [":person_light_skin_tone_bald:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b2",
- [":person_bald::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b2",
- [":person_tone1_curly_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b1",
- [":person_light_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b1",
- [":person_curly_hair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b1",
- [":person_tone1_red_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b0",
- [":person_light_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b0",
- [":person_red_hair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b0",
- [":person_tone1_white_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b3",
- [":person_light_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b3",
- [":person_white_hair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b3",
- [":person_tone2_bald:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b2",
- [":person_medium_light_skin_tone_bald:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b2",
- [":person_bald::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b2",
- [":person_tone2_curly_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b1",
- [":person_medium_light_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b1",
- [":person_curly_hair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b1",
- [":person_tone2_red_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b0",
- [":person_medium_light_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b0",
- [":person_red_hair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b0",
- [":person_tone2_white_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b3",
- [":person_medium_light_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b3",
- [":person_white_hair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b3",
- [":person_tone3_bald:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b2",
- [":person_medium_skin_tone_bald:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b2",
- [":person_bald::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b2",
- [":person_tone3_curly_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b1",
- [":person_medium_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b1",
- [":person_curly_hair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b1",
- [":person_tone3_red_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b0",
- [":person_medium_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b0",
- [":person_red_hair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b0",
- [":person_tone3_white_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b3",
- [":person_medium_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b3",
- [":person_white_hair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b3",
- [":person_tone4_bald:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b2",
- [":person_medium_dark_skin_tone_bald:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b2",
- [":person_bald::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b2",
- [":person_tone4_curly_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b1",
- [":person_medium_dark_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b1",
- [":person_curly_hair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b1",
- [":person_tone4_red_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b0",
- [":person_medium_dark_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b0",
- [":person_red_hair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b0",
- [":person_tone4_white_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b3",
- [":person_medium_dark_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b3",
- [":person_white_hair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b3",
- [":person_tone5_bald:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b2",
- [":person_dark_skin_tone_bald:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b2",
- [":person_bald::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b2",
- [":person_tone5_curly_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b1",
- [":person_dark_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b1",
- [":person_curly_hair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b1",
- [":person_tone5_red_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b0",
- [":person_dark_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b0",
- [":person_red_hair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b0",
- [":person_tone5_white_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b3",
- [":person_dark_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b3",
- [":person_white_hair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b3",
- [":person_walking:"] = "\U0001f6b6",
- [":walking:"] = "\U0001f6b6",
- [":person_walking_tone1:"] = "\U0001f6b6\U0001f3fb",
- [":walking_tone1:"] = "\U0001f6b6\U0001f3fb",
- [":person_walking::skin-tone-1:"] = "\U0001f6b6\U0001f3fb",
- [":walking::skin-tone-1:"] = "\U0001f6b6\U0001f3fb",
- [":person_walking_tone2:"] = "\U0001f6b6\U0001f3fc",
- [":walking_tone2:"] = "\U0001f6b6\U0001f3fc",
- [":person_walking::skin-tone-2:"] = "\U0001f6b6\U0001f3fc",
- [":walking::skin-tone-2:"] = "\U0001f6b6\U0001f3fc",
- [":person_walking_tone3:"] = "\U0001f6b6\U0001f3fd",
- [":walking_tone3:"] = "\U0001f6b6\U0001f3fd",
- [":person_walking::skin-tone-3:"] = "\U0001f6b6\U0001f3fd",
- [":walking::skin-tone-3:"] = "\U0001f6b6\U0001f3fd",
- [":person_walking_tone4:"] = "\U0001f6b6\U0001f3fe",
- [":walking_tone4:"] = "\U0001f6b6\U0001f3fe",
- [":person_walking::skin-tone-4:"] = "\U0001f6b6\U0001f3fe",
- [":walking::skin-tone-4:"] = "\U0001f6b6\U0001f3fe",
- [":person_walking_tone5:"] = "\U0001f6b6\U0001f3ff",
- [":walking_tone5:"] = "\U0001f6b6\U0001f3ff",
- [":person_walking::skin-tone-5:"] = "\U0001f6b6\U0001f3ff",
- [":walking::skin-tone-5:"] = "\U0001f6b6\U0001f3ff",
- [":person_wearing_turban:"] = "\U0001f473",
- [":man_with_turban:"] = "\U0001f473",
- [":person_wearing_turban_tone1:"] = "\U0001f473\U0001f3fb",
- [":man_with_turban_tone1:"] = "\U0001f473\U0001f3fb",
- [":person_wearing_turban::skin-tone-1:"] = "\U0001f473\U0001f3fb",
- [":man_with_turban::skin-tone-1:"] = "\U0001f473\U0001f3fb",
- [":person_wearing_turban_tone2:"] = "\U0001f473\U0001f3fc",
- [":man_with_turban_tone2:"] = "\U0001f473\U0001f3fc",
- [":person_wearing_turban::skin-tone-2:"] = "\U0001f473\U0001f3fc",
- [":man_with_turban::skin-tone-2:"] = "\U0001f473\U0001f3fc",
- [":person_wearing_turban_tone3:"] = "\U0001f473\U0001f3fd",
- [":man_with_turban_tone3:"] = "\U0001f473\U0001f3fd",
- [":person_wearing_turban::skin-tone-3:"] = "\U0001f473\U0001f3fd",
- [":man_with_turban::skin-tone-3:"] = "\U0001f473\U0001f3fd",
- [":person_wearing_turban_tone4:"] = "\U0001f473\U0001f3fe",
- [":man_with_turban_tone4:"] = "\U0001f473\U0001f3fe",
- [":person_wearing_turban::skin-tone-4:"] = "\U0001f473\U0001f3fe",
- [":man_with_turban::skin-tone-4:"] = "\U0001f473\U0001f3fe",
- [":person_wearing_turban_tone5:"] = "\U0001f473\U0001f3ff",
- [":man_with_turban_tone5:"] = "\U0001f473\U0001f3ff",
- [":person_wearing_turban::skin-tone-5:"] = "\U0001f473\U0001f3ff",
- [":man_with_turban::skin-tone-5:"] = "\U0001f473\U0001f3ff",
- [":person_white_hair:"] = "\U0001f9d1\u200d\U0001f9b3",
- [":person_with_probing_cane:"] = "\U0001f9d1\u200d\U0001f9af",
- [":person_with_probing_cane_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9af",
- [":person_with_probing_cane_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9af",
- [":person_with_probing_cane::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9af",
- [":person_with_probing_cane_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9af",
- [":person_with_probing_cane_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9af",
- [":person_with_probing_cane::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9af",
- [":person_with_probing_cane_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9af",
- [":person_with_probing_cane_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9af",
- [":person_with_probing_cane::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9af",
- [":person_with_probing_cane_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9af",
- [":person_with_probing_cane_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9af",
- [":person_with_probing_cane::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9af",
- [":person_with_probing_cane_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9af",
- [":person_with_probing_cane_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9af",
- [":person_with_probing_cane::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9af",
- [":person_with_veil:"] = "\U0001f470",
- [":person_with_veil_tone1:"] = "\U0001f470\U0001f3fb",
- [":person_with_veil::skin-tone-1:"] = "\U0001f470\U0001f3fb",
- [":person_with_veil_tone2:"] = "\U0001f470\U0001f3fc",
- [":person_with_veil::skin-tone-2:"] = "\U0001f470\U0001f3fc",
- [":person_with_veil_tone3:"] = "\U0001f470\U0001f3fd",
- [":person_with_veil::skin-tone-3:"] = "\U0001f470\U0001f3fd",
- [":person_with_veil_tone4:"] = "\U0001f470\U0001f3fe",
- [":person_with_veil::skin-tone-4:"] = "\U0001f470\U0001f3fe",
- [":person_with_veil_tone5:"] = "\U0001f470\U0001f3ff",
- [":person_with_veil::skin-tone-5:"] = "\U0001f470\U0001f3ff",
- [":petri_dish:"] = "\U0001f9eb",
- [":pick:"] = "\u26cf\ufe0f",
- [":pickup_truck:"] = "\U0001f6fb",
- [":pie:"] = "\U0001f967",
- [":pig:"] = "\U0001f437",
- [":pig_nose:"] = "\U0001f43d",
- [":pig2:"] = "\U0001f416",
- [":pill:"] = "\U0001f48a",
- [":pilot:"] = "\U0001f9d1\u200d\u2708\ufe0f",
- [":pilot_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f",
- [":pilot_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f",
- [":pilot::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f",
- [":pilot_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f",
- [":pilot_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f",
- [":pilot::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f",
- [":pilot_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f",
- [":pilot_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f",
- [":pilot::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f",
- [":pilot_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f",
- [":pilot_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f",
- [":pilot::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f",
- [":pilot_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f",
- [":pilot_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f",
- [":pilot::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f",
- [":piñata:"] = "\U0001fa85",
- [":pinched_fingers:"] = "\U0001f90c",
- [":pinched_fingers_tone1:"] = "\U0001f90c\U0001f3fb",
- [":pinched_fingers_light_skin_tone:"] = "\U0001f90c\U0001f3fb",
- [":pinched_fingers::skin-tone-1:"] = "\U0001f90c\U0001f3fb",
- [":pinched_fingers_tone2:"] = "\U0001f90c\U0001f3fc",
- [":pinched_fingers_medium_light_skin_tone:"] = "\U0001f90c\U0001f3fc",
- [":pinched_fingers::skin-tone-2:"] = "\U0001f90c\U0001f3fc",
- [":pinched_fingers_tone3:"] = "\U0001f90c\U0001f3fd",
- [":pinched_fingers_medium_skin_tone:"] = "\U0001f90c\U0001f3fd",
- [":pinched_fingers::skin-tone-3:"] = "\U0001f90c\U0001f3fd",
- [":pinched_fingers_tone4:"] = "\U0001f90c\U0001f3fe",
- [":pinched_fingers_medium_dark_skin_tone:"] = "\U0001f90c\U0001f3fe",
- [":pinched_fingers::skin-tone-4:"] = "\U0001f90c\U0001f3fe",
- [":pinched_fingers_tone5:"] = "\U0001f90c\U0001f3ff",
- [":pinched_fingers_dark_skin_tone:"] = "\U0001f90c\U0001f3ff",
- [":pinched_fingers::skin-tone-5:"] = "\U0001f90c\U0001f3ff",
- [":pinching_hand:"] = "\U0001f90f",
- [":pinching_hand_tone1:"] = "\U0001f90f\U0001f3fb",
- [":pinching_hand_light_skin_tone:"] = "\U0001f90f\U0001f3fb",
- [":pinching_hand::skin-tone-1:"] = "\U0001f90f\U0001f3fb",
- [":pinching_hand_tone2:"] = "\U0001f90f\U0001f3fc",
- [":pinching_hand_medium_light_skin_tone:"] = "\U0001f90f\U0001f3fc",
- [":pinching_hand::skin-tone-2:"] = "\U0001f90f\U0001f3fc",
- [":pinching_hand_tone3:"] = "\U0001f90f\U0001f3fd",
- [":pinching_hand_medium_skin_tone:"] = "\U0001f90f\U0001f3fd",
- [":pinching_hand::skin-tone-3:"] = "\U0001f90f\U0001f3fd",
- [":pinching_hand_tone4:"] = "\U0001f90f\U0001f3fe",
- [":pinching_hand_medium_dark_skin_tone:"] = "\U0001f90f\U0001f3fe",
- [":pinching_hand::skin-tone-4:"] = "\U0001f90f\U0001f3fe",
- [":pinching_hand_tone5:"] = "\U0001f90f\U0001f3ff",
- [":pinching_hand_dark_skin_tone:"] = "\U0001f90f\U0001f3ff",
- [":pinching_hand::skin-tone-5:"] = "\U0001f90f\U0001f3ff",
- [":pineapple:"] = "\U0001f34d",
- [":ping_pong:"] = "\U0001f3d3",
- [":table_tennis:"] = "\U0001f3d3",
- [":pirate_flag:"] = "\U0001f3f4\u200d\u2620\ufe0f",
- [":pisces:"] = "\u2653",
- [":pizza:"] = "\U0001f355",
- [":placard:"] = "\U0001faa7",
- [":place_of_worship:"] = "\U0001f6d0",
- [":worship_symbol:"] = "\U0001f6d0",
- [":play_pause:"] = "\u23ef\ufe0f",
- [":pleading_face:"] = "\U0001f97a",
- [":plunger:"] = "\U0001faa0",
- [":point_down:"] = "\U0001f447",
- [":point_down_tone1:"] = "\U0001f447\U0001f3fb",
- [":point_down::skin-tone-1:"] = "\U0001f447\U0001f3fb",
- [":point_down_tone2:"] = "\U0001f447\U0001f3fc",
- [":point_down::skin-tone-2:"] = "\U0001f447\U0001f3fc",
- [":point_down_tone3:"] = "\U0001f447\U0001f3fd",
- [":point_down::skin-tone-3:"] = "\U0001f447\U0001f3fd",
- [":point_down_tone4:"] = "\U0001f447\U0001f3fe",
- [":point_down::skin-tone-4:"] = "\U0001f447\U0001f3fe",
- [":point_down_tone5:"] = "\U0001f447\U0001f3ff",
- [":point_down::skin-tone-5:"] = "\U0001f447\U0001f3ff",
- [":point_left:"] = "\U0001f448",
- [":point_left_tone1:"] = "\U0001f448\U0001f3fb",
- [":point_left::skin-tone-1:"] = "\U0001f448\U0001f3fb",
- [":point_left_tone2:"] = "\U0001f448\U0001f3fc",
- [":point_left::skin-tone-2:"] = "\U0001f448\U0001f3fc",
- [":point_left_tone3:"] = "\U0001f448\U0001f3fd",
- [":point_left::skin-tone-3:"] = "\U0001f448\U0001f3fd",
- [":point_left_tone4:"] = "\U0001f448\U0001f3fe",
- [":point_left::skin-tone-4:"] = "\U0001f448\U0001f3fe",
- [":point_left_tone5:"] = "\U0001f448\U0001f3ff",
- [":point_left::skin-tone-5:"] = "\U0001f448\U0001f3ff",
- [":point_right:"] = "\U0001f449",
- [":point_right_tone1:"] = "\U0001f449\U0001f3fb",
- [":point_right::skin-tone-1:"] = "\U0001f449\U0001f3fb",
- [":point_right_tone2:"] = "\U0001f449\U0001f3fc",
- [":point_right::skin-tone-2:"] = "\U0001f449\U0001f3fc",
- [":point_right_tone3:"] = "\U0001f449\U0001f3fd",
- [":point_right::skin-tone-3:"] = "\U0001f449\U0001f3fd",
- [":point_right_tone4:"] = "\U0001f449\U0001f3fe",
- [":point_right::skin-tone-4:"] = "\U0001f449\U0001f3fe",
- [":point_right_tone5:"] = "\U0001f449\U0001f3ff",
- [":point_right::skin-tone-5:"] = "\U0001f449\U0001f3ff",
- [":point_up:"] = "\u261d\ufe0f",
- [":point_up_2:"] = "\U0001f446",
- [":point_up_2_tone1:"] = "\U0001f446\U0001f3fb",
- [":point_up_2::skin-tone-1:"] = "\U0001f446\U0001f3fb",
- [":point_up_2_tone2:"] = "\U0001f446\U0001f3fc",
- [":point_up_2::skin-tone-2:"] = "\U0001f446\U0001f3fc",
- [":point_up_2_tone3:"] = "\U0001f446\U0001f3fd",
- [":point_up_2::skin-tone-3:"] = "\U0001f446\U0001f3fd",
- [":point_up_2_tone4:"] = "\U0001f446\U0001f3fe",
- [":point_up_2::skin-tone-4:"] = "\U0001f446\U0001f3fe",
- [":point_up_2_tone5:"] = "\U0001f446\U0001f3ff",
- [":point_up_2::skin-tone-5:"] = "\U0001f446\U0001f3ff",
- [":point_up_tone1:"] = "\u261d\U0001f3fb",
- [":point_up::skin-tone-1:"] = "\u261d\U0001f3fb",
- [":point_up_tone2:"] = "\u261d\U0001f3fc",
- [":point_up::skin-tone-2:"] = "\u261d\U0001f3fc",
- [":point_up_tone3:"] = "\u261d\U0001f3fd",
- [":point_up::skin-tone-3:"] = "\u261d\U0001f3fd",
- [":point_up_tone4:"] = "\u261d\U0001f3fe",
- [":point_up::skin-tone-4:"] = "\u261d\U0001f3fe",
- [":point_up_tone5:"] = "\u261d\U0001f3ff",
- [":point_up::skin-tone-5:"] = "\u261d\U0001f3ff",
- [":polar_bear:"] = "\U0001f43b\u200d\u2744\ufe0f",
- [":police_car:"] = "\U0001f693",
- [":police_officer:"] = "\U0001f46e",
- [":cop:"] = "\U0001f46e",
- [":police_officer_tone1:"] = "\U0001f46e\U0001f3fb",
- [":cop_tone1:"] = "\U0001f46e\U0001f3fb",
- [":police_officer::skin-tone-1:"] = "\U0001f46e\U0001f3fb",
- [":cop::skin-tone-1:"] = "\U0001f46e\U0001f3fb",
- [":police_officer_tone2:"] = "\U0001f46e\U0001f3fc",
- [":cop_tone2:"] = "\U0001f46e\U0001f3fc",
- [":police_officer::skin-tone-2:"] = "\U0001f46e\U0001f3fc",
- [":cop::skin-tone-2:"] = "\U0001f46e\U0001f3fc",
- [":police_officer_tone3:"] = "\U0001f46e\U0001f3fd",
- [":cop_tone3:"] = "\U0001f46e\U0001f3fd",
- [":police_officer::skin-tone-3:"] = "\U0001f46e\U0001f3fd",
- [":cop::skin-tone-3:"] = "\U0001f46e\U0001f3fd",
- [":police_officer_tone4:"] = "\U0001f46e\U0001f3fe",
- [":cop_tone4:"] = "\U0001f46e\U0001f3fe",
- [":police_officer::skin-tone-4:"] = "\U0001f46e\U0001f3fe",
- [":cop::skin-tone-4:"] = "\U0001f46e\U0001f3fe",
- [":police_officer_tone5:"] = "\U0001f46e\U0001f3ff",
- [":cop_tone5:"] = "\U0001f46e\U0001f3ff",
- [":police_officer::skin-tone-5:"] = "\U0001f46e\U0001f3ff",
- [":cop::skin-tone-5:"] = "\U0001f46e\U0001f3ff",
- [":poodle:"] = "\U0001f429",
- [":poop:"] = "\U0001f4a9",
- [":shit:"] = "\U0001f4a9",
- [":hankey:"] = "\U0001f4a9",
- [":poo:"] = "\U0001f4a9",
- [":popcorn:"] = "\U0001f37f",
- [":post_office:"] = "\U0001f3e3",
- [":postal_horn:"] = "\U0001f4ef",
- [":postbox:"] = "\U0001f4ee",
- [":potable_water:"] = "\U0001f6b0",
- [":potato:"] = "\U0001f954",
- [":potted_plant:"] = "\U0001fab4",
- [":pouch:"] = "\U0001f45d",
- [":poultry_leg:"] = "\U0001f357",
- [":pound:"] = "\U0001f4b7",
- [":pouting_cat:"] = "\U0001f63e",
- [":pray:"] = "\U0001f64f",
- [":pray_tone1:"] = "\U0001f64f\U0001f3fb",
- [":pray::skin-tone-1:"] = "\U0001f64f\U0001f3fb",
- [":pray_tone2:"] = "\U0001f64f\U0001f3fc",
- [":pray::skin-tone-2:"] = "\U0001f64f\U0001f3fc",
- [":pray_tone3:"] = "\U0001f64f\U0001f3fd",
- [":pray::skin-tone-3:"] = "\U0001f64f\U0001f3fd",
- [":pray_tone4:"] = "\U0001f64f\U0001f3fe",
- [":pray::skin-tone-4:"] = "\U0001f64f\U0001f3fe",
- [":pray_tone5:"] = "\U0001f64f\U0001f3ff",
- [":pray::skin-tone-5:"] = "\U0001f64f\U0001f3ff",
- [":prayer_beads:"] = "\U0001f4ff",
- [":pregnant_woman:"] = "\U0001f930",
- [":expecting_woman:"] = "\U0001f930",
- [":pregnant_woman_tone1:"] = "\U0001f930\U0001f3fb",
- [":expecting_woman_tone1:"] = "\U0001f930\U0001f3fb",
- [":pregnant_woman::skin-tone-1:"] = "\U0001f930\U0001f3fb",
- [":expecting_woman::skin-tone-1:"] = "\U0001f930\U0001f3fb",
- [":pregnant_woman_tone2:"] = "\U0001f930\U0001f3fc",
- [":expecting_woman_tone2:"] = "\U0001f930\U0001f3fc",
- [":pregnant_woman::skin-tone-2:"] = "\U0001f930\U0001f3fc",
- [":expecting_woman::skin-tone-2:"] = "\U0001f930\U0001f3fc",
- [":pregnant_woman_tone3:"] = "\U0001f930\U0001f3fd",
- [":expecting_woman_tone3:"] = "\U0001f930\U0001f3fd",
- [":pregnant_woman::skin-tone-3:"] = "\U0001f930\U0001f3fd",
- [":expecting_woman::skin-tone-3:"] = "\U0001f930\U0001f3fd",
- [":pregnant_woman_tone4:"] = "\U0001f930\U0001f3fe",
- [":expecting_woman_tone4:"] = "\U0001f930\U0001f3fe",
- [":pregnant_woman::skin-tone-4:"] = "\U0001f930\U0001f3fe",
- [":expecting_woman::skin-tone-4:"] = "\U0001f930\U0001f3fe",
- [":pregnant_woman_tone5:"] = "\U0001f930\U0001f3ff",
- [":expecting_woman_tone5:"] = "\U0001f930\U0001f3ff",
- [":pregnant_woman::skin-tone-5:"] = "\U0001f930\U0001f3ff",
- [":expecting_woman::skin-tone-5:"] = "\U0001f930\U0001f3ff",
- [":pretzel:"] = "\U0001f968",
- [":prince:"] = "\U0001f934",
- [":prince_tone1:"] = "\U0001f934\U0001f3fb",
- [":prince::skin-tone-1:"] = "\U0001f934\U0001f3fb",
- [":prince_tone2:"] = "\U0001f934\U0001f3fc",
- [":prince::skin-tone-2:"] = "\U0001f934\U0001f3fc",
- [":prince_tone3:"] = "\U0001f934\U0001f3fd",
- [":prince::skin-tone-3:"] = "\U0001f934\U0001f3fd",
- [":prince_tone4:"] = "\U0001f934\U0001f3fe",
- [":prince::skin-tone-4:"] = "\U0001f934\U0001f3fe",
- [":prince_tone5:"] = "\U0001f934\U0001f3ff",
- [":prince::skin-tone-5:"] = "\U0001f934\U0001f3ff",
- [":princess:"] = "\U0001f478",
- [":princess_tone1:"] = "\U0001f478\U0001f3fb",
- [":princess::skin-tone-1:"] = "\U0001f478\U0001f3fb",
- [":princess_tone2:"] = "\U0001f478\U0001f3fc",
- [":princess::skin-tone-2:"] = "\U0001f478\U0001f3fc",
- [":princess_tone3:"] = "\U0001f478\U0001f3fd",
- [":princess::skin-tone-3:"] = "\U0001f478\U0001f3fd",
- [":princess_tone4:"] = "\U0001f478\U0001f3fe",
- [":princess::skin-tone-4:"] = "\U0001f478\U0001f3fe",
- [":princess_tone5:"] = "\U0001f478\U0001f3ff",
- [":princess::skin-tone-5:"] = "\U0001f478\U0001f3ff",
- [":printer:"] = "\U0001f5a8\ufe0f",
- [":probing_cane:"] = "\U0001f9af",
- [":projector:"] = "\U0001f4fd\ufe0f",
- [":film_projector:"] = "\U0001f4fd\ufe0f",
- [":punch:"] = "\U0001f44a",
- [":punch_tone1:"] = "\U0001f44a\U0001f3fb",
- [":punch::skin-tone-1:"] = "\U0001f44a\U0001f3fb",
- [":punch_tone2:"] = "\U0001f44a\U0001f3fc",
- [":punch::skin-tone-2:"] = "\U0001f44a\U0001f3fc",
- [":punch_tone3:"] = "\U0001f44a\U0001f3fd",
- [":punch::skin-tone-3:"] = "\U0001f44a\U0001f3fd",
- [":punch_tone4:"] = "\U0001f44a\U0001f3fe",
- [":punch::skin-tone-4:"] = "\U0001f44a\U0001f3fe",
- [":punch_tone5:"] = "\U0001f44a\U0001f3ff",
- [":punch::skin-tone-5:"] = "\U0001f44a\U0001f3ff",
- [":purple_circle:"] = "\U0001f7e3",
- [":purple_heart:"] = "\U0001f49c",
- [":purple_square:"] = "\U0001f7ea",
- [":purse:"] = "\U0001f45b",
- [":pushpin:"] = "\U0001f4cc",
- [":put_litter_in_its_place:"] = "\U0001f6ae",
- [":question:"] = "\u2753",
- [":rabbit:"] = "\U0001f430",
- [":rabbit2:"] = "\U0001f407",
- [":raccoon:"] = "\U0001f99d",
- [":race_car:"] = "\U0001f3ce\ufe0f",
- [":racing_car:"] = "\U0001f3ce\ufe0f",
- [":racehorse:"] = "\U0001f40e",
- [":radio:"] = "\U0001f4fb",
- [":radio_button:"] = "\U0001f518",
- [":radioactive:"] = "\u2622\ufe0f",
- [":radioactive_sign:"] = "\u2622\ufe0f",
- [":rage:"] = "\U0001f621",
- [":@"] = "\U0001f621",
- [":-@"] = "\U0001f621",
- ["=@"] = "\U0001f621",
- ["=-@"] = "\U0001f621",
- [":railway_car:"] = "\U0001f683",
- [":railway_track:"] = "\U0001f6e4\ufe0f",
- [":railroad_track:"] = "\U0001f6e4\ufe0f",
- [":rainbow:"] = "\U0001f308",
- [":rainbow_flag:"] = "\U0001f3f3\ufe0f\u200d\U0001f308",
- [":gay_pride_flag:"] = "\U0001f3f3\ufe0f\u200d\U0001f308",
- [":raised_back_of_hand:"] = "\U0001f91a",
- [":back_of_hand:"] = "\U0001f91a",
- [":raised_back_of_hand_tone1:"] = "\U0001f91a\U0001f3fb",
- [":back_of_hand_tone1:"] = "\U0001f91a\U0001f3fb",
- [":raised_back_of_hand::skin-tone-1:"] = "\U0001f91a\U0001f3fb",
- [":back_of_hand::skin-tone-1:"] = "\U0001f91a\U0001f3fb",
- [":raised_back_of_hand_tone2:"] = "\U0001f91a\U0001f3fc",
- [":back_of_hand_tone2:"] = "\U0001f91a\U0001f3fc",
- [":raised_back_of_hand::skin-tone-2:"] = "\U0001f91a\U0001f3fc",
- [":back_of_hand::skin-tone-2:"] = "\U0001f91a\U0001f3fc",
- [":raised_back_of_hand_tone3:"] = "\U0001f91a\U0001f3fd",
- [":back_of_hand_tone3:"] = "\U0001f91a\U0001f3fd",
- [":raised_back_of_hand::skin-tone-3:"] = "\U0001f91a\U0001f3fd",
- [":back_of_hand::skin-tone-3:"] = "\U0001f91a\U0001f3fd",
- [":raised_back_of_hand_tone4:"] = "\U0001f91a\U0001f3fe",
- [":back_of_hand_tone4:"] = "\U0001f91a\U0001f3fe",
- [":raised_back_of_hand::skin-tone-4:"] = "\U0001f91a\U0001f3fe",
- [":back_of_hand::skin-tone-4:"] = "\U0001f91a\U0001f3fe",
- [":raised_back_of_hand_tone5:"] = "\U0001f91a\U0001f3ff",
- [":back_of_hand_tone5:"] = "\U0001f91a\U0001f3ff",
- [":raised_back_of_hand::skin-tone-5:"] = "\U0001f91a\U0001f3ff",
- [":back_of_hand::skin-tone-5:"] = "\U0001f91a\U0001f3ff",
- [":raised_hand:"] = "\u270b",
- [":raised_hand_tone1:"] = "\u270b\U0001f3fb",
- [":raised_hand::skin-tone-1:"] = "\u270b\U0001f3fb",
- [":raised_hand_tone2:"] = "\u270b\U0001f3fc",
- [":raised_hand::skin-tone-2:"] = "\u270b\U0001f3fc",
- [":raised_hand_tone3:"] = "\u270b\U0001f3fd",
- [":raised_hand::skin-tone-3:"] = "\u270b\U0001f3fd",
- [":raised_hand_tone4:"] = "\u270b\U0001f3fe",
- [":raised_hand::skin-tone-4:"] = "\u270b\U0001f3fe",
- [":raised_hand_tone5:"] = "\u270b\U0001f3ff",
- [":raised_hand::skin-tone-5:"] = "\u270b\U0001f3ff",
- [":raised_hands:"] = "\U0001f64c",
- [":raised_hands_tone1:"] = "\U0001f64c\U0001f3fb",
- [":raised_hands::skin-tone-1:"] = "\U0001f64c\U0001f3fb",
- [":raised_hands_tone2:"] = "\U0001f64c\U0001f3fc",
- [":raised_hands::skin-tone-2:"] = "\U0001f64c\U0001f3fc",
- [":raised_hands_tone3:"] = "\U0001f64c\U0001f3fd",
- [":raised_hands::skin-tone-3:"] = "\U0001f64c\U0001f3fd",
- [":raised_hands_tone4:"] = "\U0001f64c\U0001f3fe",
- [":raised_hands::skin-tone-4:"] = "\U0001f64c\U0001f3fe",
- [":raised_hands_tone5:"] = "\U0001f64c\U0001f3ff",
- [":raised_hands::skin-tone-5:"] = "\U0001f64c\U0001f3ff",
- [":ram:"] = "\U0001f40f",
- [":ramen:"] = "\U0001f35c",
- [":rat:"] = "\U0001f400",
- [":razor:"] = "\U0001fa92",
- [":receipt:"] = "\U0001f9fe",
- [":record_button:"] = "\u23fa\ufe0f",
- [":recycle:"] = "\u267b\ufe0f",
- [":red_car:"] = "\U0001f697",
- [":red_circle:"] = "\U0001f534",
- [":red_envelope:"] = "\U0001f9e7",
- [":red_square:"] = "\U0001f7e5",
- [":regional_indicator_a:"] = "\U0001f1e6",
- [":regional_indicator_b:"] = "\U0001f1e7",
- [":regional_indicator_c:"] = "\U0001f1e8",
- [":regional_indicator_d:"] = "\U0001f1e9",
- [":regional_indicator_e:"] = "\U0001f1ea",
- [":regional_indicator_f:"] = "\U0001f1eb",
- [":regional_indicator_g:"] = "\U0001f1ec",
- [":regional_indicator_h:"] = "\U0001f1ed",
- [":regional_indicator_i:"] = "\U0001f1ee",
- [":regional_indicator_j:"] = "\U0001f1ef",
- [":regional_indicator_k:"] = "\U0001f1f0",
- [":regional_indicator_l:"] = "\U0001f1f1",
- [":regional_indicator_m:"] = "\U0001f1f2",
- [":regional_indicator_n:"] = "\U0001f1f3",
- [":regional_indicator_o:"] = "\U0001f1f4",
- [":regional_indicator_p:"] = "\U0001f1f5",
- [":regional_indicator_q:"] = "\U0001f1f6",
- [":regional_indicator_r:"] = "\U0001f1f7",
- [":regional_indicator_s:"] = "\U0001f1f8",
- [":regional_indicator_t:"] = "\U0001f1f9",
- [":regional_indicator_u:"] = "\U0001f1fa",
- [":regional_indicator_v:"] = "\U0001f1fb",
- [":regional_indicator_w:"] = "\U0001f1fc",
- [":regional_indicator_x:"] = "\U0001f1fd",
- [":regional_indicator_y:"] = "\U0001f1fe",
- [":regional_indicator_z:"] = "\U0001f1ff",
- [":registered:"] = "\u00ae\ufe0f",
- [":relaxed:"] = "\u263a\ufe0f",
- [":relieved:"] = "\U0001f60c",
- [":reminder_ribbon:"] = "\U0001f397\ufe0f",
- [":repeat:"] = "\U0001f501",
- [":repeat_one:"] = "\U0001f502",
- [":restroom:"] = "\U0001f6bb",
- [":revolving_hearts:"] = "\U0001f49e",
- [":rewind:"] = "\u23ea",
- [":rhino:"] = "\U0001f98f",
- [":rhinoceros:"] = "\U0001f98f",
- [":ribbon:"] = "\U0001f380",
- [":rice:"] = "\U0001f35a",
- [":rice_ball:"] = "\U0001f359",
- [":rice_cracker:"] = "\U0001f358",
- [":rice_scene:"] = "\U0001f391",
- [":right_facing_fist:"] = "\U0001f91c",
- [":right_fist:"] = "\U0001f91c",
- [":right_facing_fist_tone1:"] = "\U0001f91c\U0001f3fb",
- [":right_fist_tone1:"] = "\U0001f91c\U0001f3fb",
- [":right_facing_fist::skin-tone-1:"] = "\U0001f91c\U0001f3fb",
- [":right_fist::skin-tone-1:"] = "\U0001f91c\U0001f3fb",
- [":right_facing_fist_tone2:"] = "\U0001f91c\U0001f3fc",
- [":right_fist_tone2:"] = "\U0001f91c\U0001f3fc",
- [":right_facing_fist::skin-tone-2:"] = "\U0001f91c\U0001f3fc",
- [":right_fist::skin-tone-2:"] = "\U0001f91c\U0001f3fc",
- [":right_facing_fist_tone3:"] = "\U0001f91c\U0001f3fd",
- [":right_fist_tone3:"] = "\U0001f91c\U0001f3fd",
- [":right_facing_fist::skin-tone-3:"] = "\U0001f91c\U0001f3fd",
- [":right_fist::skin-tone-3:"] = "\U0001f91c\U0001f3fd",
- [":right_facing_fist_tone4:"] = "\U0001f91c\U0001f3fe",
- [":right_fist_tone4:"] = "\U0001f91c\U0001f3fe",
- [":right_facing_fist::skin-tone-4:"] = "\U0001f91c\U0001f3fe",
- [":right_fist::skin-tone-4:"] = "\U0001f91c\U0001f3fe",
- [":right_facing_fist_tone5:"] = "\U0001f91c\U0001f3ff",
- [":right_fist_tone5:"] = "\U0001f91c\U0001f3ff",
- [":right_facing_fist::skin-tone-5:"] = "\U0001f91c\U0001f3ff",
- [":right_fist::skin-tone-5:"] = "\U0001f91c\U0001f3ff",
- [":ring:"] = "\U0001f48d",
- [":ringed_planet:"] = "\U0001fa90",
- [":robot:"] = "\U0001f916",
- [":robot_face:"] = "\U0001f916",
- [":rock:"] = "\U0001faa8",
- [":rocket:"] = "\U0001f680",
- [":rofl:"] = "\U0001f923",
- [":rolling_on_the_floor_laughing:"] = "\U0001f923",
- [":roll_of_paper:"] = "\U0001f9fb",
- [":roller_coaster:"] = "\U0001f3a2",
- [":roller_skate:"] = "\U0001f6fc",
- [":rolling_eyes:"] = "\U0001f644",
- [":face_with_rolling_eyes:"] = "\U0001f644",
- [":rooster:"] = "\U0001f413",
- [":rose:"] = "\U0001f339",
- [":rosette:"] = "\U0001f3f5\ufe0f",
- [":rotating_light:"] = "\U0001f6a8",
- [":round_pushpin:"] = "\U0001f4cd",
- [":rugby_football:"] = "\U0001f3c9",
- [":running_shirt_with_sash:"] = "\U0001f3bd",
- [":sa:"] = "\U0001f202\ufe0f",
- [":safety_pin:"] = "\U0001f9f7",
- [":safety_vest:"] = "\U0001f9ba",
- [":sagittarius:"] = "\u2650",
- [":sailboat:"] = "\u26f5",
- [":sake:"] = "\U0001f376",
- [":salad:"] = "\U0001f957",
- [":green_salad:"] = "\U0001f957",
- [":salt:"] = "\U0001f9c2",
- [":sandal:"] = "\U0001f461",
- [":sandwich:"] = "\U0001f96a",
- [":santa:"] = "\U0001f385",
- [":santa_tone1:"] = "\U0001f385\U0001f3fb",
- [":santa::skin-tone-1:"] = "\U0001f385\U0001f3fb",
- [":santa_tone2:"] = "\U0001f385\U0001f3fc",
- [":santa::skin-tone-2:"] = "\U0001f385\U0001f3fc",
- [":santa_tone3:"] = "\U0001f385\U0001f3fd",
- [":santa::skin-tone-3:"] = "\U0001f385\U0001f3fd",
- [":santa_tone4:"] = "\U0001f385\U0001f3fe",
- [":santa::skin-tone-4:"] = "\U0001f385\U0001f3fe",
- [":santa_tone5:"] = "\U0001f385\U0001f3ff",
- [":santa::skin-tone-5:"] = "\U0001f385\U0001f3ff",
- [":sari:"] = "\U0001f97b",
- [":satellite:"] = "\U0001f4e1",
- [":satellite_orbital:"] = "\U0001f6f0\ufe0f",
- [":sauropod:"] = "\U0001f995",
- [":saxophone:"] = "\U0001f3b7",
- [":scales:"] = "\u2696\ufe0f",
- [":scarf:"] = "\U0001f9e3",
- [":school:"] = "\U0001f3eb",
- [":school_satchel:"] = "\U0001f392",
- [":scientist:"] = "\U0001f9d1\u200d\U0001f52c",
- [":scientist_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f52c",
- [":scientist_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f52c",
- [":scientist::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f52c",
- [":scientist_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f52c",
- [":scientist_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f52c",
- [":scientist::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f52c",
- [":scientist_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f52c",
- [":scientist_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f52c",
- [":scientist::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f52c",
- [":scientist_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f52c",
- [":scientist_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f52c",
- [":scientist::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f52c",
- [":scientist_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f52c",
- [":scientist_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f52c",
- [":scientist::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f52c",
- [":scissors:"] = "\u2702\ufe0f",
- [":scooter:"] = "\U0001f6f4",
- [":scorpion:"] = "\U0001f982",
- [":scorpius:"] = "\u264f",
- [":scotland:"] = "\U0001f3f4\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f",
- [":scream:"] = "\U0001f631",
- [":scream_cat:"] = "\U0001f640",
- [":screwdriver:"] = "\U0001fa9b",
- [":scroll:"] = "\U0001f4dc",
- [":seal:"] = "\U0001f9ad",
- [":seat:"] = "\U0001f4ba",
- [":second_place:"] = "\U0001f948",
- [":second_place_medal:"] = "\U0001f948",
- [":secret:"] = "\u3299\ufe0f",
- [":see_no_evil:"] = "\U0001f648",
- [":seedling:"] = "\U0001f331",
- [":selfie:"] = "\U0001f933",
- [":selfie_tone1:"] = "\U0001f933\U0001f3fb",
- [":selfie::skin-tone-1:"] = "\U0001f933\U0001f3fb",
- [":selfie_tone2:"] = "\U0001f933\U0001f3fc",
- [":selfie::skin-tone-2:"] = "\U0001f933\U0001f3fc",
- [":selfie_tone3:"] = "\U0001f933\U0001f3fd",
- [":selfie::skin-tone-3:"] = "\U0001f933\U0001f3fd",
- [":selfie_tone4:"] = "\U0001f933\U0001f3fe",
- [":selfie::skin-tone-4:"] = "\U0001f933\U0001f3fe",
- [":selfie_tone5:"] = "\U0001f933\U0001f3ff",
- [":selfie::skin-tone-5:"] = "\U0001f933\U0001f3ff",
- [":service_dog:"] = "\U0001f415\u200d\U0001f9ba",
- [":seven:"] = "\u0037\ufe0f\u20e3",
- [":sewing_needle:"] = "\U0001faa1",
- [":shallow_pan_of_food:"] = "\U0001f958",
- [":paella:"] = "\U0001f958",
- [":shamrock:"] = "\u2618\ufe0f",
- [":shark:"] = "\U0001f988",
- [":shaved_ice:"] = "\U0001f367",
- [":sheep:"] = "\U0001f411",
- [":shell:"] = "\U0001f41a",
- [":shield:"] = "\U0001f6e1\ufe0f",
- [":shinto_shrine:"] = "\u26e9\ufe0f",
- [":ship:"] = "\U0001f6a2",
- [":shirt:"] = "\U0001f455",
- [":shopping_bags:"] = "\U0001f6cd\ufe0f",
- [":shopping_cart:"] = "\U0001f6d2",
- [":shopping_trolley:"] = "\U0001f6d2",
- [":shorts:"] = "\U0001fa73",
- [":shower:"] = "\U0001f6bf",
- [":shrimp:"] = "\U0001f990",
- [":shushing_face:"] = "\U0001f92b",
- [":signal_strength:"] = "\U0001f4f6",
- [":singer:"] = "\U0001f9d1\u200d\U0001f3a4",
- [":singer_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a4",
- [":singer_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a4",
- [":singer::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a4",
- [":singer_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a4",
- [":singer_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a4",
- [":singer::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a4",
- [":singer_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a4",
- [":singer_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a4",
- [":singer::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a4",
- [":singer_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a4",
- [":singer_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a4",
- [":singer::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a4",
- [":singer_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a4",
- [":singer_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a4",
- [":singer::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a4",
- [":six:"] = "\u0036\ufe0f\u20e3",
- [":six_pointed_star:"] = "\U0001f52f",
- [":skateboard:"] = "\U0001f6f9",
- [":ski:"] = "\U0001f3bf",
- [":skier:"] = "\u26f7\ufe0f",
- [":skull:"] = "\U0001f480",
- [":skeleton:"] = "\U0001f480",
- [":skull_crossbones:"] = "\u2620\ufe0f",
- [":skull_and_crossbones:"] = "\u2620\ufe0f",
- [":skunk:"] = "\U0001f9a8",
- [":sled:"] = "\U0001f6f7",
- [":sleeping:"] = "\U0001f634",
- [":sleeping_accommodation:"] = "\U0001f6cc",
- [":sleepy:"] = "\U0001f62a",
- [":slight_frown:"] = "\U0001f641",
- [":slightly_frowning_face:"] = "\U0001f641",
- [":slight_smile:"] = "\U0001f642",
- [":slightly_smiling_face:"] = "\U0001f642",
- [":)"] = "\U0001f642",
- [":-)"] = "\U0001f642",
- ["=)"] = "\U0001f642",
- ["=-)"] = "\U0001f642",
- [":slot_machine:"] = "\U0001f3b0",
- [":sloth:"] = "\U0001f9a5",
- [":small_blue_diamond:"] = "\U0001f539",
- [":small_orange_diamond:"] = "\U0001f538",
- [":small_red_triangle:"] = "\U0001f53a",
- [":small_red_triangle_down:"] = "\U0001f53b",
- [":smile:"] = "\U0001f604",
- [":D"] = "\U0001f604",
- [":-D"] = "\U0001f604",
- ["=D"] = "\U0001f604",
- ["=-D"] = "\U0001f604",
- [":smile_cat:"] = "\U0001f638",
- [":smiley:"] = "\U0001f603",
- [":smiley_cat:"] = "\U0001f63a",
- [":smiling_face_with_3_hearts:"] = "\U0001f970",
- [":smiling_face_with_tear:"] = "\U0001f972",
- [":smiling_imp:"] = "\U0001f608",
- ["]:)"] = "\U0001f608",
- ["]:-)"] = "\U0001f608",
- ["]=)"] = "\U0001f608",
- ["]=-)"] = "\U0001f608",
- [":smirk:"] = "\U0001f60f",
- [":smirk_cat:"] = "\U0001f63c",
- [":smoking:"] = "\U0001f6ac",
- [":snail:"] = "\U0001f40c",
- [":snake:"] = "\U0001f40d",
- [":sneezing_face:"] = "\U0001f927",
- [":sneeze:"] = "\U0001f927",
- [":snowboarder:"] = "\U0001f3c2",
- [":snowboarder_tone1:"] = "\U0001f3c2\U0001f3fb",
- [":snowboarder_light_skin_tone:"] = "\U0001f3c2\U0001f3fb",
- [":snowboarder::skin-tone-1:"] = "\U0001f3c2\U0001f3fb",
- [":snowboarder_tone2:"] = "\U0001f3c2\U0001f3fc",
- [":snowboarder_medium_light_skin_tone:"] = "\U0001f3c2\U0001f3fc",
- [":snowboarder::skin-tone-2:"] = "\U0001f3c2\U0001f3fc",
- [":snowboarder_tone3:"] = "\U0001f3c2\U0001f3fd",
- [":snowboarder_medium_skin_tone:"] = "\U0001f3c2\U0001f3fd",
- [":snowboarder::skin-tone-3:"] = "\U0001f3c2\U0001f3fd",
- [":snowboarder_tone4:"] = "\U0001f3c2\U0001f3fe",
- [":snowboarder_medium_dark_skin_tone:"] = "\U0001f3c2\U0001f3fe",
- [":snowboarder::skin-tone-4:"] = "\U0001f3c2\U0001f3fe",
- [":snowboarder_tone5:"] = "\U0001f3c2\U0001f3ff",
- [":snowboarder_dark_skin_tone:"] = "\U0001f3c2\U0001f3ff",
- [":snowboarder::skin-tone-5:"] = "\U0001f3c2\U0001f3ff",
- [":snowflake:"] = "\u2744\ufe0f",
- [":snowman:"] = "\u26c4",
- [":snowman2:"] = "\u2603\ufe0f",
- [":soap:"] = "\U0001f9fc",
- [":sob:"] = "\U0001f62d",
- [":,'("] = "\U0001f62d",
- [":,'-("] = "\U0001f62d",
- [";("] = "\U0001f62d",
- [";-("] = "\U0001f62d",
- ["=,'("] = "\U0001f62d",
- ["=,'-("] = "\U0001f62d",
- [":soccer:"] = "\u26bd",
- [":socks:"] = "\U0001f9e6",
- [":softball:"] = "\U0001f94e",
- [":soon:"] = "\U0001f51c",
- [":sos:"] = "\U0001f198",
- [":sound:"] = "\U0001f509",
- [":space_invader:"] = "\U0001f47e",
- [":spades:"] = "\u2660\ufe0f",
- [":spaghetti:"] = "\U0001f35d",
- [":sparkle:"] = "\u2747\ufe0f",
- [":sparkler:"] = "\U0001f387",
- [":sparkles:"] = "\u2728",
- [":sparkling_heart:"] = "\U0001f496",
- [":speak_no_evil:"] = "\U0001f64a",
- [":speaker:"] = "\U0001f508",
- [":speaking_head:"] = "\U0001f5e3\ufe0f",
- [":speaking_head_in_silhouette:"] = "\U0001f5e3\ufe0f",
- [":speech_balloon:"] = "\U0001f4ac",
- [":speech_left:"] = "\U0001f5e8\ufe0f",
- [":left_speech_bubble:"] = "\U0001f5e8\ufe0f",
- [":speedboat:"] = "\U0001f6a4",
- [":spider:"] = "\U0001f577\ufe0f",
- [":spider_web:"] = "\U0001f578\ufe0f",
- [":sponge:"] = "\U0001f9fd",
- [":spoon:"] = "\U0001f944",
- [":squeeze_bottle:"] = "\U0001f9f4",
- [":squid:"] = "\U0001f991",
- [":stadium:"] = "\U0001f3df\ufe0f",
- [":star:"] = "\u2b50",
- [":star_and_crescent:"] = "\u262a\ufe0f",
- [":star_of_david:"] = "\u2721\ufe0f",
- [":star_struck:"] = "\U0001f929",
- [":star2:"] = "\U0001f31f",
- [":stars:"] = "\U0001f320",
- [":station:"] = "\U0001f689",
- [":statue_of_liberty:"] = "\U0001f5fd",
- [":steam_locomotive:"] = "\U0001f682",
- [":stethoscope:"] = "\U0001fa7a",
- [":stew:"] = "\U0001f372",
- [":stop_button:"] = "\u23f9\ufe0f",
- [":stopwatch:"] = "\u23f1\ufe0f",
- [":straight_ruler:"] = "\U0001f4cf",
- [":strawberry:"] = "\U0001f353",
- [":stuck_out_tongue:"] = "\U0001f61b",
- [":P"] = "\U0001f61b",
- [":-P"] = "\U0001f61b",
- ["=P"] = "\U0001f61b",
- ["=-P"] = "\U0001f61b",
- [":stuck_out_tongue_closed_eyes:"] = "\U0001f61d",
- [":stuck_out_tongue_winking_eye:"] = "\U0001f61c",
- [":student:"] = "\U0001f9d1\u200d\U0001f393",
- [":student_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f393",
- [":student_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f393",
- [":student::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f393",
- [":student_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f393",
- [":student_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f393",
- [":student::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f393",
- [":student_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f393",
- [":student_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f393",
- [":student::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f393",
- [":student_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f393",
- [":student_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f393",
- [":student::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f393",
- [":student_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f393",
- [":student_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f393",
- [":student::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f393",
- [":stuffed_flatbread:"] = "\U0001f959",
- [":stuffed_pita:"] = "\U0001f959",
- [":sun_with_face:"] = "\U0001f31e",
- [":sunflower:"] = "\U0001f33b",
- [":sunglasses:"] = "\U0001f60e",
- ["8-)"] = "\U0001f60e",
- ["B-)"] = "\U0001f60e",
- [":sunny:"] = "\u2600\ufe0f",
- [":sunrise:"] = "\U0001f305",
- [":sunrise_over_mountains:"] = "\U0001f304",
- [":superhero:"] = "\U0001f9b8",
- [":superhero_tone1:"] = "\U0001f9b8\U0001f3fb",
- [":superhero_light_skin_tone:"] = "\U0001f9b8\U0001f3fb",
- [":superhero::skin-tone-1:"] = "\U0001f9b8\U0001f3fb",
- [":superhero_tone2:"] = "\U0001f9b8\U0001f3fc",
- [":superhero_medium_light_skin_tone:"] = "\U0001f9b8\U0001f3fc",
- [":superhero::skin-tone-2:"] = "\U0001f9b8\U0001f3fc",
- [":superhero_tone3:"] = "\U0001f9b8\U0001f3fd",
- [":superhero_medium_skin_tone:"] = "\U0001f9b8\U0001f3fd",
- [":superhero::skin-tone-3:"] = "\U0001f9b8\U0001f3fd",
- [":superhero_tone4:"] = "\U0001f9b8\U0001f3fe",
- [":superhero_medium_dark_skin_tone:"] = "\U0001f9b8\U0001f3fe",
- [":superhero::skin-tone-4:"] = "\U0001f9b8\U0001f3fe",
- [":superhero_tone5:"] = "\U0001f9b8\U0001f3ff",
- [":superhero_dark_skin_tone:"] = "\U0001f9b8\U0001f3ff",
- [":superhero::skin-tone-5:"] = "\U0001f9b8\U0001f3ff",
- [":supervillain:"] = "\U0001f9b9",
- [":supervillain_tone1:"] = "\U0001f9b9\U0001f3fb",
- [":supervillain_light_skin_tone:"] = "\U0001f9b9\U0001f3fb",
- [":supervillain::skin-tone-1:"] = "\U0001f9b9\U0001f3fb",
- [":supervillain_tone2:"] = "\U0001f9b9\U0001f3fc",
- [":supervillain_medium_light_skin_tone:"] = "\U0001f9b9\U0001f3fc",
- [":supervillain::skin-tone-2:"] = "\U0001f9b9\U0001f3fc",
- [":supervillain_tone3:"] = "\U0001f9b9\U0001f3fd",
- [":supervillain_medium_skin_tone:"] = "\U0001f9b9\U0001f3fd",
- [":supervillain::skin-tone-3:"] = "\U0001f9b9\U0001f3fd",
- [":supervillain_tone4:"] = "\U0001f9b9\U0001f3fe",
- [":supervillain_medium_dark_skin_tone:"] = "\U0001f9b9\U0001f3fe",
- [":supervillain::skin-tone-4:"] = "\U0001f9b9\U0001f3fe",
- [":supervillain_tone5:"] = "\U0001f9b9\U0001f3ff",
- [":supervillain_dark_skin_tone:"] = "\U0001f9b9\U0001f3ff",
- [":supervillain::skin-tone-5:"] = "\U0001f9b9\U0001f3ff",
- [":sushi:"] = "\U0001f363",
- [":suspension_railway:"] = "\U0001f69f",
- [":swan:"] = "\U0001f9a2",
- [":sweat:"] = "\U0001f613",
- [",:("] = "\U0001f613",
- [",:-("] = "\U0001f613",
- [",=("] = "\U0001f613",
- [",=-("] = "\U0001f613",
- [":sweat_drops:"] = "\U0001f4a6",
- [":sweat_smile:"] = "\U0001f605",
- [",:)"] = "\U0001f605",
- [",:-)"] = "\U0001f605",
- [",=)"] = "\U0001f605",
- [",=-)"] = "\U0001f605",
- [":sweet_potato:"] = "\U0001f360",
- [":symbols:"] = "\U0001f523",
- [":synagogue:"] = "\U0001f54d",
- [":syringe:"] = "\U0001f489",
- [":t_rex:"] = "\U0001f996",
- [":taco:"] = "\U0001f32e",
- [":tada:"] = "\U0001f389",
- [":takeout_box:"] = "\U0001f961",
- [":tamale:"] = "\U0001fad4",
- [":tanabata_tree:"] = "\U0001f38b",
- [":tangerine:"] = "\U0001f34a",
- [":taurus:"] = "\u2649",
- [":taxi:"] = "\U0001f695",
- [":tea:"] = "\U0001f375",
- [":teacher:"] = "\U0001f9d1\u200d\U0001f3eb",
- [":teacher_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3eb",
- [":teacher_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3eb",
- [":teacher::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3eb",
- [":teacher_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3eb",
- [":teacher_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3eb",
- [":teacher::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3eb",
- [":teacher_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3eb",
- [":teacher_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3eb",
- [":teacher::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3eb",
- [":teacher_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3eb",
- [":teacher_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3eb",
- [":teacher::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3eb",
- [":teacher_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3eb",
- [":teacher_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3eb",
- [":teacher::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3eb",
- [":teapot:"] = "\U0001fad6",
- [":technologist:"] = "\U0001f9d1\u200d\U0001f4bb",
- [":technologist_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bb",
- [":technologist_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bb",
- [":technologist::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bb",
- [":technologist_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bb",
- [":technologist_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bb",
- [":technologist::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bb",
- [":technologist_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bb",
- [":technologist_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bb",
- [":technologist::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bb",
- [":technologist_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bb",
- [":technologist_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bb",
- [":technologist::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bb",
- [":technologist_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bb",
- [":technologist_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bb",
- [":technologist::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bb",
- [":teddy_bear:"] = "\U0001f9f8",
- [":telephone:"] = "\u260e\ufe0f",
- [":telephone_receiver:"] = "\U0001f4de",
- [":telescope:"] = "\U0001f52d",
- [":tennis:"] = "\U0001f3be",
- [":tent:"] = "\u26fa",
- [":test_tube:"] = "\U0001f9ea",
- [":thermometer:"] = "\U0001f321\ufe0f",
- [":thermometer_face:"] = "\U0001f912",
- [":face_with_thermometer:"] = "\U0001f912",
- [":thinking:"] = "\U0001f914",
- [":thinking_face:"] = "\U0001f914",
- [":third_place:"] = "\U0001f949",
- [":third_place_medal:"] = "\U0001f949",
- [":thong_sandal:"] = "\U0001fa74",
- [":thought_balloon:"] = "\U0001f4ad",
- [":thread:"] = "\U0001f9f5",
- [":three:"] = "\u0033\ufe0f\u20e3",
- [":thumbsdown:"] = "\U0001f44e",
- [":-1:"] = "\U0001f44e",
- [":thumbdown:"] = "\U0001f44e",
- [":thumbsdown_tone1:"] = "\U0001f44e\U0001f3fb",
- [":_1_tone1:"] = "\U0001f44e\U0001f3fb",
- [":thumbdown_tone1:"] = "\U0001f44e\U0001f3fb",
- [":thumbsdown::skin-tone-1:"] = "\U0001f44e\U0001f3fb",
- [":-1::skin-tone-1:"] = "\U0001f44e\U0001f3fb",
- [":thumbdown::skin-tone-1:"] = "\U0001f44e\U0001f3fb",
- [":thumbsdown_tone2:"] = "\U0001f44e\U0001f3fc",
- [":_1_tone2:"] = "\U0001f44e\U0001f3fc",
- [":thumbdown_tone2:"] = "\U0001f44e\U0001f3fc",
- [":thumbsdown::skin-tone-2:"] = "\U0001f44e\U0001f3fc",
- [":-1::skin-tone-2:"] = "\U0001f44e\U0001f3fc",
- [":thumbdown::skin-tone-2:"] = "\U0001f44e\U0001f3fc",
- [":thumbsdown_tone3:"] = "\U0001f44e\U0001f3fd",
- [":_1_tone3:"] = "\U0001f44e\U0001f3fd",
- [":thumbdown_tone3:"] = "\U0001f44e\U0001f3fd",
- [":thumbsdown::skin-tone-3:"] = "\U0001f44e\U0001f3fd",
- [":-1::skin-tone-3:"] = "\U0001f44e\U0001f3fd",
- [":thumbdown::skin-tone-3:"] = "\U0001f44e\U0001f3fd",
- [":thumbsdown_tone4:"] = "\U0001f44e\U0001f3fe",
- [":_1_tone4:"] = "\U0001f44e\U0001f3fe",
- [":thumbdown_tone4:"] = "\U0001f44e\U0001f3fe",
- [":thumbsdown::skin-tone-4:"] = "\U0001f44e\U0001f3fe",
- [":-1::skin-tone-4:"] = "\U0001f44e\U0001f3fe",
- [":thumbdown::skin-tone-4:"] = "\U0001f44e\U0001f3fe",
- [":thumbsdown_tone5:"] = "\U0001f44e\U0001f3ff",
- [":_1_tone5:"] = "\U0001f44e\U0001f3ff",
- [":thumbdown_tone5:"] = "\U0001f44e\U0001f3ff",
- [":thumbsdown::skin-tone-5:"] = "\U0001f44e\U0001f3ff",
- [":-1::skin-tone-5:"] = "\U0001f44e\U0001f3ff",
- [":thumbdown::skin-tone-5:"] = "\U0001f44e\U0001f3ff",
- [":thumbsup:"] = "\U0001f44d",
- [":+1:"] = "\U0001f44d",
- [":thumbup:"] = "\U0001f44d",
- [":thumbsup_tone1:"] = "\U0001f44d\U0001f3fb",
- [":+1_tone1:"] = "\U0001f44d\U0001f3fb",
- [":thumbup_tone1:"] = "\U0001f44d\U0001f3fb",
- [":thumbsup::skin-tone-1:"] = "\U0001f44d\U0001f3fb",
- [":+1::skin-tone-1:"] = "\U0001f44d\U0001f3fb",
- [":thumbup::skin-tone-1:"] = "\U0001f44d\U0001f3fb",
- [":thumbsup_tone2:"] = "\U0001f44d\U0001f3fc",
- [":+1_tone2:"] = "\U0001f44d\U0001f3fc",
- [":thumbup_tone2:"] = "\U0001f44d\U0001f3fc",
- [":thumbsup::skin-tone-2:"] = "\U0001f44d\U0001f3fc",
- [":+1::skin-tone-2:"] = "\U0001f44d\U0001f3fc",
- [":thumbup::skin-tone-2:"] = "\U0001f44d\U0001f3fc",
- [":thumbsup_tone3:"] = "\U0001f44d\U0001f3fd",
- [":+1_tone3:"] = "\U0001f44d\U0001f3fd",
- [":thumbup_tone3:"] = "\U0001f44d\U0001f3fd",
- [":thumbsup::skin-tone-3:"] = "\U0001f44d\U0001f3fd",
- [":+1::skin-tone-3:"] = "\U0001f44d\U0001f3fd",
- [":thumbup::skin-tone-3:"] = "\U0001f44d\U0001f3fd",
- [":thumbsup_tone4:"] = "\U0001f44d\U0001f3fe",
- [":+1_tone4:"] = "\U0001f44d\U0001f3fe",
- [":thumbup_tone4:"] = "\U0001f44d\U0001f3fe",
- [":thumbsup::skin-tone-4:"] = "\U0001f44d\U0001f3fe",
- [":+1::skin-tone-4:"] = "\U0001f44d\U0001f3fe",
- [":thumbup::skin-tone-4:"] = "\U0001f44d\U0001f3fe",
- [":thumbsup_tone5:"] = "\U0001f44d\U0001f3ff",
- [":+1_tone5:"] = "\U0001f44d\U0001f3ff",
- [":thumbup_tone5:"] = "\U0001f44d\U0001f3ff",
- [":thumbsup::skin-tone-5:"] = "\U0001f44d\U0001f3ff",
- [":+1::skin-tone-5:"] = "\U0001f44d\U0001f3ff",
- [":thumbup::skin-tone-5:"] = "\U0001f44d\U0001f3ff",
- [":thunder_cloud_rain:"] = "\u26c8\ufe0f",
- [":thunder_cloud_and_rain:"] = "\u26c8\ufe0f",
- [":ticket:"] = "\U0001f3ab",
- [":tickets:"] = "\U0001f39f\ufe0f",
- [":admission_tickets:"] = "\U0001f39f\ufe0f",
- [":tiger:"] = "\U0001f42f",
- [":tiger2:"] = "\U0001f405",
- [":timer:"] = "\u23f2\ufe0f",
- [":timer_clock:"] = "\u23f2\ufe0f",
- [":tired_face:"] = "\U0001f62b",
- [":tm:"] = "\u2122\ufe0f",
- [":toilet:"] = "\U0001f6bd",
- [":tokyo_tower:"] = "\U0001f5fc",
- [":tomato:"] = "\U0001f345",
- [":tongue:"] = "\U0001f445",
- [":toolbox:"] = "\U0001f9f0",
- [":tools:"] = "\U0001f6e0\ufe0f",
- [":hammer_and_wrench:"] = "\U0001f6e0\ufe0f",
- [":tooth:"] = "\U0001f9b7",
- [":toothbrush:"] = "\U0001faa5",
- [":top:"] = "\U0001f51d",
- [":tophat:"] = "\U0001f3a9",
- [":track_next:"] = "\u23ed\ufe0f",
- [":next_track:"] = "\u23ed\ufe0f",
- [":track_previous:"] = "\u23ee\ufe0f",
- [":previous_track:"] = "\u23ee\ufe0f",
- [":trackball:"] = "\U0001f5b2\ufe0f",
- [":tractor:"] = "\U0001f69c",
- [":traffic_light:"] = "\U0001f6a5",
- [":train:"] = "\U0001f68b",
- [":train2:"] = "\U0001f686",
- [":tram:"] = "\U0001f68a",
- [":transgender_flag:"] = "\U0001f3f3\ufe0f\u200d\u26a7\ufe0f",
- [":transgender_symbol:"] = "\u26a7",
- [":triangular_flag_on_post:"] = "\U0001f6a9",
- [":triangular_ruler:"] = "\U0001f4d0",
- [":trident:"] = "\U0001f531",
- [":triumph:"] = "\U0001f624",
- [":trolleybus:"] = "\U0001f68e",
- [":trophy:"] = "\U0001f3c6",
- [":tropical_drink:"] = "\U0001f379",
- [":tropical_fish:"] = "\U0001f420",
- [":truck:"] = "\U0001f69a",
- [":trumpet:"] = "\U0001f3ba",
- [":tulip:"] = "\U0001f337",
- [":tumbler_glass:"] = "\U0001f943",
- [":whisky:"] = "\U0001f943",
- [":turkey:"] = "\U0001f983",
- [":turtle:"] = "\U0001f422",
- [":tv:"] = "\U0001f4fa",
- [":twisted_rightwards_arrows:"] = "\U0001f500",
- [":two:"] = "\u0032\ufe0f\u20e3",
- [":two_hearts:"] = "\U0001f495",
- [":two_men_holding_hands:"] = "\U0001f46c",
- [":two_women_holding_hands:"] = "\U0001f46d",
- [":u5272:"] = "\U0001f239",
- [":u5408:"] = "\U0001f234",
- [":u55b6:"] = "\U0001f23a",
- [":u6307:"] = "\U0001f22f",
- [":u6708:"] = "\U0001f237\ufe0f",
- [":u6709:"] = "\U0001f236",
- [":u6e80:"] = "\U0001f235",
- [":u7121:"] = "\U0001f21a",
- [":u7533:"] = "\U0001f238",
- [":u7981:"] = "\U0001f232",
- [":u7a7a:"] = "\U0001f233",
- [":umbrella:"] = "\u2614",
- [":umbrella2:"] = "\u2602\ufe0f",
- [":unamused:"] = "\U0001f612",
- [":s"] = "\U0001f612",
- [":-S"] = "\U0001f612",
- [":z"] = "\U0001f612",
- [":-Z"] = "\U0001f612",
- [":$"] = "\U0001f612",
- [":-$"] = "\U0001f612",
- ["=s"] = "\U0001f612",
- ["=-S"] = "\U0001f612",
- ["=z"] = "\U0001f612",
- ["=-Z"] = "\U0001f612",
- ["=$"] = "\U0001f612",
- ["=-$"] = "\U0001f612",
- [":underage:"] = "\U0001f51e",
- [":unicorn:"] = "\U0001f984",
- [":unicorn_face:"] = "\U0001f984",
- [":united_nations:"] = "\U0001f1fa\U0001f1f3",
- [":unlock:"] = "\U0001f513",
- [":up:"] = "\U0001f199",
- [":upside_down:"] = "\U0001f643",
- [":upside_down_face:"] = "\U0001f643",
- [":urn:"] = "\u26b1\ufe0f",
- [":funeral_urn:"] = "\u26b1\ufe0f",
- [":v:"] = "\u270c\ufe0f",
- [":v_tone1:"] = "\u270c\U0001f3fb",
- [":v::skin-tone-1:"] = "\u270c\U0001f3fb",
- [":v_tone2:"] = "\u270c\U0001f3fc",
- [":v::skin-tone-2:"] = "\u270c\U0001f3fc",
- [":v_tone3:"] = "\u270c\U0001f3fd",
- [":v::skin-tone-3:"] = "\u270c\U0001f3fd",
- [":v_tone4:"] = "\u270c\U0001f3fe",
- [":v::skin-tone-4:"] = "\u270c\U0001f3fe",
- [":v_tone5:"] = "\u270c\U0001f3ff",
- [":v::skin-tone-5:"] = "\u270c\U0001f3ff",
- [":vampire:"] = "\U0001f9db",
- [":vampire_tone1:"] = "\U0001f9db\U0001f3fb",
- [":vampire_light_skin_tone:"] = "\U0001f9db\U0001f3fb",
- [":vampire::skin-tone-1:"] = "\U0001f9db\U0001f3fb",
- [":vampire_tone2:"] = "\U0001f9db\U0001f3fc",
- [":vampire_medium_light_skin_tone:"] = "\U0001f9db\U0001f3fc",
- [":vampire::skin-tone-2:"] = "\U0001f9db\U0001f3fc",
- [":vampire_tone3:"] = "\U0001f9db\U0001f3fd",
- [":vampire_medium_skin_tone:"] = "\U0001f9db\U0001f3fd",
- [":vampire::skin-tone-3:"] = "\U0001f9db\U0001f3fd",
- [":vampire_tone4:"] = "\U0001f9db\U0001f3fe",
- [":vampire_medium_dark_skin_tone:"] = "\U0001f9db\U0001f3fe",
- [":vampire::skin-tone-4:"] = "\U0001f9db\U0001f3fe",
- [":vampire_tone5:"] = "\U0001f9db\U0001f3ff",
- [":vampire_dark_skin_tone:"] = "\U0001f9db\U0001f3ff",
- [":vampire::skin-tone-5:"] = "\U0001f9db\U0001f3ff",
- [":vertical_traffic_light:"] = "\U0001f6a6",
- [":vhs:"] = "\U0001f4fc",
- [":vibration_mode:"] = "\U0001f4f3",
- [":video_camera:"] = "\U0001f4f9",
- [":video_game:"] = "\U0001f3ae",
- [":violin:"] = "\U0001f3bb",
- [":virgo:"] = "\u264d",
- [":volcano:"] = "\U0001f30b",
- [":volleyball:"] = "\U0001f3d0",
- [":vs:"] = "\U0001f19a",
- [":vulcan:"] = "\U0001f596",
- [":raised_hand_with_part_between_middle_and_ring_fingers:"] = "\U0001f596",
- [":vulcan_tone1:"] = "\U0001f596\U0001f3fb",
- [":raised_hand_with_part_between_middle_and_ring_fingers_tone1:"] = "\U0001f596\U0001f3fb",
- [":vulcan::skin-tone-1:"] = "\U0001f596\U0001f3fb",
- [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-1:"] = "\U0001f596\U0001f3fb",
- [":vulcan_tone2:"] = "\U0001f596\U0001f3fc",
- [":raised_hand_with_part_between_middle_and_ring_fingers_tone2:"] = "\U0001f596\U0001f3fc",
- [":vulcan::skin-tone-2:"] = "\U0001f596\U0001f3fc",
- [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-2:"] = "\U0001f596\U0001f3fc",
- [":vulcan_tone3:"] = "\U0001f596\U0001f3fd",
- [":raised_hand_with_part_between_middle_and_ring_fingers_tone3:"] = "\U0001f596\U0001f3fd",
- [":vulcan::skin-tone-3:"] = "\U0001f596\U0001f3fd",
- [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-3:"] = "\U0001f596\U0001f3fd",
- [":vulcan_tone4:"] = "\U0001f596\U0001f3fe",
- [":raised_hand_with_part_between_middle_and_ring_fingers_tone4:"] = "\U0001f596\U0001f3fe",
- [":vulcan::skin-tone-4:"] = "\U0001f596\U0001f3fe",
- [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-4:"] = "\U0001f596\U0001f3fe",
- [":vulcan_tone5:"] = "\U0001f596\U0001f3ff",
- [":raised_hand_with_part_between_middle_and_ring_fingers_tone5:"] = "\U0001f596\U0001f3ff",
- [":vulcan::skin-tone-5:"] = "\U0001f596\U0001f3ff",
- [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-5:"] = "\U0001f596\U0001f3ff",
- [":waffle:"] = "\U0001f9c7",
- [":wales:"] = "\U0001f3f4\U000e0067\U000e0062\U000e0077\U000e006c\U000e0073\U000e007f",
- [":waning_crescent_moon:"] = "\U0001f318",
- [":waning_gibbous_moon:"] = "\U0001f316",
- [":warning:"] = "\u26a0\ufe0f",
- [":wastebasket:"] = "\U0001f5d1\ufe0f",
- [":watch:"] = "\u231a",
- [":water_buffalo:"] = "\U0001f403",
- [":watermelon:"] = "\U0001f349",
- [":wave:"] = "\U0001f44b",
- [":wave_tone1:"] = "\U0001f44b\U0001f3fb",
- [":wave::skin-tone-1:"] = "\U0001f44b\U0001f3fb",
- [":wave_tone2:"] = "\U0001f44b\U0001f3fc",
- [":wave::skin-tone-2:"] = "\U0001f44b\U0001f3fc",
- [":wave_tone3:"] = "\U0001f44b\U0001f3fd",
- [":wave::skin-tone-3:"] = "\U0001f44b\U0001f3fd",
- [":wave_tone4:"] = "\U0001f44b\U0001f3fe",
- [":wave::skin-tone-4:"] = "\U0001f44b\U0001f3fe",
- [":wave_tone5:"] = "\U0001f44b\U0001f3ff",
- [":wave::skin-tone-5:"] = "\U0001f44b\U0001f3ff",
- [":wavy_dash:"] = "\u3030\ufe0f",
- [":waxing_crescent_moon:"] = "\U0001f312",
- [":waxing_gibbous_moon:"] = "\U0001f314",
- [":wc:"] = "\U0001f6be",
- [":weary:"] = "\U0001f629",
- [":wedding:"] = "\U0001f492",
- [":whale:"] = "\U0001f433",
- [":whale2:"] = "\U0001f40b",
- [":wheel_of_dharma:"] = "\u2638\ufe0f",
- [":wheelchair:"] = "\u267f",
- [":white_check_mark:"] = "\u2705",
- [":white_circle:"] = "\u26aa",
- [":white_flower:"] = "\U0001f4ae",
- [":white_heart:"] = "\U0001f90d",
- [":white_large_square:"] = "\u2b1c",
- [":white_medium_small_square:"] = "\u25fd",
- [":white_medium_square:"] = "\u25fb\ufe0f",
- [":white_small_square:"] = "\u25ab\ufe0f",
- [":white_square_button:"] = "\U0001f533",
- [":white_sun_cloud:"] = "\U0001f325\ufe0f",
- [":white_sun_behind_cloud:"] = "\U0001f325\ufe0f",
- [":white_sun_rain_cloud:"] = "\U0001f326\ufe0f",
- [":white_sun_behind_cloud_with_rain:"] = "\U0001f326\ufe0f",
- [":white_sun_small_cloud:"] = "\U0001f324\ufe0f",
- [":white_sun_with_small_cloud:"] = "\U0001f324\ufe0f",
- [":wilted_rose:"] = "\U0001f940",
- [":wilted_flower:"] = "\U0001f940",
- [":wind_blowing_face:"] = "\U0001f32c\ufe0f",
- [":wind_chime:"] = "\U0001f390",
- [":window:"] = "\U0001fa9f",
- [":wine_glass:"] = "\U0001f377",
- [":wink:"] = "\U0001f609",
- [";)"] = "\U0001f609",
- [";-)"] = "\U0001f609",
- [":wolf:"] = "\U0001f43a",
- [":woman:"] = "\U0001f469",
- [":woman_and_man_holding_hands_tone1:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone1_tone2:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_light_skin_tone_medium_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone1_tone3:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_light_skin_tone_medium_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone1_tone4:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone1_tone5:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_light_skin_tone_dark_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone2:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone2_tone1:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_light_skin_tone_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone2_tone3:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_light_skin_tone_medium_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone2_tone4:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone2_tone5:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_light_skin_tone_dark_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone3:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone3_tone1:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_skin_tone_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone3_tone2:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_skin_tone_medium_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone3_tone4:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_skin_tone_medium_dark_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone3_tone5:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_skin_tone_dark_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone4:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_dark_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone4_tone1:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_dark_skin_tone_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone4_tone2:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone4_tone3:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_dark_skin_tone_medium_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone4_tone5:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_medium_dark_skin_tone_dark_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone5:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_dark_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone5_tone1:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_dark_skin_tone_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone5_tone2:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone5_tone3:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_dark_skin_tone_medium_skin_tone:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_tone5_tone4:"] = "\U0001f46b",
- [":woman_and_man_holding_hands_dark_skin_tone_medium_dark_skin_tone:"] = "\U0001f46b",
- [":woman_artist:"] = "\U0001f469\u200d\U0001f3a8",
- [":woman_artist_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a8",
- [":woman_artist_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a8",
- [":woman_artist::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a8",
- [":woman_artist_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a8",
- [":woman_artist_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a8",
- [":woman_artist::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a8",
- [":woman_artist_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a8",
- [":woman_artist_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a8",
- [":woman_artist::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a8",
- [":woman_artist_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a8",
- [":woman_artist_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a8",
- [":woman_artist::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a8",
- [":woman_artist_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a8",
- [":woman_artist_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a8",
- [":woman_artist::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a8",
- [":woman_astronaut:"] = "\U0001f469\u200d\U0001f680",
- [":woman_astronaut_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f680",
- [":woman_astronaut_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f680",
- [":woman_astronaut::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f680",
- [":woman_astronaut_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f680",
- [":woman_astronaut_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f680",
- [":woman_astronaut::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f680",
- [":woman_astronaut_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f680",
- [":woman_astronaut_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f680",
- [":woman_astronaut::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f680",
- [":woman_astronaut_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f680",
- [":woman_astronaut_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f680",
- [":woman_astronaut::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f680",
- [":woman_astronaut_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f680",
- [":woman_astronaut_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f680",
- [":woman_astronaut::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f680",
- [":woman_bald:"] = "\U0001f469\u200d\U0001f9b2",
- [":woman_bald_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b2",
- [":woman_bald_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b2",
- [":woman_bald::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b2",
- [":woman_bald_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b2",
- [":woman_bald_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b2",
- [":woman_bald::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b2",
- [":woman_bald_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b2",
- [":woman_bald_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b2",
- [":woman_bald::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b2",
- [":woman_bald_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b2",
- [":woman_bald_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b2",
- [":woman_bald::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b2",
- [":woman_bald_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b2",
- [":woman_bald_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b2",
- [":woman_bald::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b2",
- [":woman_biking:"] = "\U0001f6b4\u200d\u2640\ufe0f",
- [":woman_biking_tone1:"] = "\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_biking_light_skin_tone:"] = "\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_biking::skin-tone-1:"] = "\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_biking_tone2:"] = "\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_biking_medium_light_skin_tone:"] = "\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_biking::skin-tone-2:"] = "\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_biking_tone3:"] = "\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_biking_medium_skin_tone:"] = "\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_biking::skin-tone-3:"] = "\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_biking_tone4:"] = "\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_biking_medium_dark_skin_tone:"] = "\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_biking::skin-tone-4:"] = "\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_biking_tone5:"] = "\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_biking_dark_skin_tone:"] = "\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_biking::skin-tone-5:"] = "\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_bouncing_ball:"] = "\u26f9\ufe0f\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_tone1:"] = "\u26f9\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_light_skin_tone:"] = "\u26f9\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_bouncing_ball::skin-tone-1:"] = "\u26f9\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_tone2:"] = "\u26f9\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_medium_light_skin_tone:"] = "\u26f9\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_bouncing_ball::skin-tone-2:"] = "\u26f9\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_tone3:"] = "\u26f9\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_medium_skin_tone:"] = "\u26f9\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_bouncing_ball::skin-tone-3:"] = "\u26f9\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_tone4:"] = "\u26f9\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_medium_dark_skin_tone:"] = "\u26f9\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_bouncing_ball::skin-tone-4:"] = "\u26f9\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_tone5:"] = "\u26f9\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_bouncing_ball_dark_skin_tone:"] = "\u26f9\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_bouncing_ball::skin-tone-5:"] = "\u26f9\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_bowing:"] = "\U0001f647\u200d\u2640\ufe0f",
- [":woman_bowing_tone1:"] = "\U0001f647\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_bowing_light_skin_tone:"] = "\U0001f647\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_bowing::skin-tone-1:"] = "\U0001f647\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_bowing_tone2:"] = "\U0001f647\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_bowing_medium_light_skin_tone:"] = "\U0001f647\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_bowing::skin-tone-2:"] = "\U0001f647\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_bowing_tone3:"] = "\U0001f647\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_bowing_medium_skin_tone:"] = "\U0001f647\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_bowing::skin-tone-3:"] = "\U0001f647\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_bowing_tone4:"] = "\U0001f647\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_bowing_medium_dark_skin_tone:"] = "\U0001f647\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_bowing::skin-tone-4:"] = "\U0001f647\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_bowing_tone5:"] = "\U0001f647\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_bowing_dark_skin_tone:"] = "\U0001f647\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_bowing::skin-tone-5:"] = "\U0001f647\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_cartwheeling:"] = "\U0001f938\u200d\u2640\ufe0f",
- [":woman_cartwheeling_tone1:"] = "\U0001f938\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_cartwheeling_light_skin_tone:"] = "\U0001f938\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_cartwheeling::skin-tone-1:"] = "\U0001f938\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_cartwheeling_tone2:"] = "\U0001f938\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_cartwheeling_medium_light_skin_tone:"] = "\U0001f938\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_cartwheeling::skin-tone-2:"] = "\U0001f938\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_cartwheeling_tone3:"] = "\U0001f938\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_cartwheeling_medium_skin_tone:"] = "\U0001f938\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_cartwheeling::skin-tone-3:"] = "\U0001f938\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_cartwheeling_tone4:"] = "\U0001f938\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_cartwheeling_medium_dark_skin_tone:"] = "\U0001f938\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_cartwheeling::skin-tone-4:"] = "\U0001f938\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_cartwheeling_tone5:"] = "\U0001f938\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_cartwheeling_dark_skin_tone:"] = "\U0001f938\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_cartwheeling::skin-tone-5:"] = "\U0001f938\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_climbing:"] = "\U0001f9d7\u200d\u2640\ufe0f",
- [":woman_climbing_tone1:"] = "\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_climbing_light_skin_tone:"] = "\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_climbing::skin-tone-1:"] = "\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_climbing_tone2:"] = "\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_climbing_medium_light_skin_tone:"] = "\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_climbing::skin-tone-2:"] = "\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_climbing_tone3:"] = "\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_climbing_medium_skin_tone:"] = "\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_climbing::skin-tone-3:"] = "\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_climbing_tone4:"] = "\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_climbing_medium_dark_skin_tone:"] = "\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_climbing::skin-tone-4:"] = "\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_climbing_tone5:"] = "\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_climbing_dark_skin_tone:"] = "\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_climbing::skin-tone-5:"] = "\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_construction_worker:"] = "\U0001f477\u200d\u2640\ufe0f",
- [":woman_construction_worker_tone1:"] = "\U0001f477\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_construction_worker_light_skin_tone:"] = "\U0001f477\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_construction_worker::skin-tone-1:"] = "\U0001f477\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_construction_worker_tone2:"] = "\U0001f477\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_construction_worker_medium_light_skin_tone:"] = "\U0001f477\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_construction_worker::skin-tone-2:"] = "\U0001f477\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_construction_worker_tone3:"] = "\U0001f477\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_construction_worker_medium_skin_tone:"] = "\U0001f477\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_construction_worker::skin-tone-3:"] = "\U0001f477\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_construction_worker_tone4:"] = "\U0001f477\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_construction_worker_medium_dark_skin_tone:"] = "\U0001f477\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_construction_worker::skin-tone-4:"] = "\U0001f477\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_construction_worker_tone5:"] = "\U0001f477\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_construction_worker_dark_skin_tone:"] = "\U0001f477\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_construction_worker::skin-tone-5:"] = "\U0001f477\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_cook:"] = "\U0001f469\u200d\U0001f373",
- [":woman_cook_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f373",
- [":woman_cook_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f373",
- [":woman_cook::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f373",
- [":woman_cook_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f373",
- [":woman_cook_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f373",
- [":woman_cook::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f373",
- [":woman_cook_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f373",
- [":woman_cook_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f373",
- [":woman_cook::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f373",
- [":woman_cook_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f373",
- [":woman_cook_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f373",
- [":woman_cook::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f373",
- [":woman_cook_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f373",
- [":woman_cook_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f373",
- [":woman_cook::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f373",
- [":woman_curly_haired:"] = "\U0001f469\u200d\U0001f9b1",
- [":woman_curly_haired_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b1",
- [":woman_curly_haired_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b1",
- [":woman_curly_haired::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b1",
- [":woman_curly_haired_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b1",
- [":woman_curly_haired_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b1",
- [":woman_curly_haired::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b1",
- [":woman_curly_haired_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b1",
- [":woman_curly_haired_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b1",
- [":woman_curly_haired::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b1",
- [":woman_curly_haired_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b1",
- [":woman_curly_haired_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b1",
- [":woman_curly_haired::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b1",
- [":woman_curly_haired_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b1",
- [":woman_curly_haired_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b1",
- [":woman_curly_haired::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b1",
- [":woman_detective:"] = "\U0001f575\ufe0f\u200d\u2640\ufe0f",
- [":woman_detective_tone1:"] = "\U0001f575\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_detective_light_skin_tone:"] = "\U0001f575\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_detective::skin-tone-1:"] = "\U0001f575\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_detective_tone2:"] = "\U0001f575\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_detective_medium_light_skin_tone:"] = "\U0001f575\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_detective::skin-tone-2:"] = "\U0001f575\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_detective_tone3:"] = "\U0001f575\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_detective_medium_skin_tone:"] = "\U0001f575\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_detective::skin-tone-3:"] = "\U0001f575\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_detective_tone4:"] = "\U0001f575\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_detective_medium_dark_skin_tone:"] = "\U0001f575\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_detective::skin-tone-4:"] = "\U0001f575\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_detective_tone5:"] = "\U0001f575\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_detective_dark_skin_tone:"] = "\U0001f575\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_detective::skin-tone-5:"] = "\U0001f575\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_elf:"] = "\U0001f9dd\u200d\u2640\ufe0f",
- [":woman_elf_tone1:"] = "\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_elf_light_skin_tone:"] = "\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_elf::skin-tone-1:"] = "\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_elf_tone2:"] = "\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_elf_medium_light_skin_tone:"] = "\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_elf::skin-tone-2:"] = "\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_elf_tone3:"] = "\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_elf_medium_skin_tone:"] = "\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_elf::skin-tone-3:"] = "\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_elf_tone4:"] = "\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_elf_medium_dark_skin_tone:"] = "\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_elf::skin-tone-4:"] = "\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_elf_tone5:"] = "\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_elf_dark_skin_tone:"] = "\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_elf::skin-tone-5:"] = "\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_facepalming:"] = "\U0001f926\u200d\u2640\ufe0f",
- [":woman_facepalming_tone1:"] = "\U0001f926\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_facepalming_light_skin_tone:"] = "\U0001f926\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_facepalming::skin-tone-1:"] = "\U0001f926\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_facepalming_tone2:"] = "\U0001f926\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_facepalming_medium_light_skin_tone:"] = "\U0001f926\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_facepalming::skin-tone-2:"] = "\U0001f926\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_facepalming_tone3:"] = "\U0001f926\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_facepalming_medium_skin_tone:"] = "\U0001f926\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_facepalming::skin-tone-3:"] = "\U0001f926\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_facepalming_tone4:"] = "\U0001f926\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_facepalming_medium_dark_skin_tone:"] = "\U0001f926\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_facepalming::skin-tone-4:"] = "\U0001f926\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_facepalming_tone5:"] = "\U0001f926\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_facepalming_dark_skin_tone:"] = "\U0001f926\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_facepalming::skin-tone-5:"] = "\U0001f926\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_factory_worker:"] = "\U0001f469\u200d\U0001f3ed",
- [":woman_factory_worker_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3ed",
- [":woman_factory_worker_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f3ed",
- [":woman_factory_worker::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3ed",
- [":woman_factory_worker_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3ed",
- [":woman_factory_worker_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f3ed",
- [":woman_factory_worker::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3ed",
- [":woman_factory_worker_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3ed",
- [":woman_factory_worker_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f3ed",
- [":woman_factory_worker::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3ed",
- [":woman_factory_worker_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3ed",
- [":woman_factory_worker_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f3ed",
- [":woman_factory_worker::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3ed",
- [":woman_factory_worker_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3ed",
- [":woman_factory_worker_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f3ed",
- [":woman_factory_worker::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3ed",
- [":woman_fairy:"] = "\U0001f9da\u200d\u2640\ufe0f",
- [":woman_fairy_tone1:"] = "\U0001f9da\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_fairy_light_skin_tone:"] = "\U0001f9da\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_fairy::skin-tone-1:"] = "\U0001f9da\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_fairy_tone2:"] = "\U0001f9da\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_fairy_medium_light_skin_tone:"] = "\U0001f9da\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_fairy::skin-tone-2:"] = "\U0001f9da\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_fairy_tone3:"] = "\U0001f9da\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_fairy_medium_skin_tone:"] = "\U0001f9da\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_fairy::skin-tone-3:"] = "\U0001f9da\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_fairy_tone4:"] = "\U0001f9da\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_fairy_medium_dark_skin_tone:"] = "\U0001f9da\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_fairy::skin-tone-4:"] = "\U0001f9da\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_fairy_tone5:"] = "\U0001f9da\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_fairy_dark_skin_tone:"] = "\U0001f9da\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_fairy::skin-tone-5:"] = "\U0001f9da\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_farmer:"] = "\U0001f469\u200d\U0001f33e",
- [":woman_farmer_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f33e",
- [":woman_farmer_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f33e",
- [":woman_farmer::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f33e",
- [":woman_farmer_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f33e",
- [":woman_farmer_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f33e",
- [":woman_farmer::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f33e",
- [":woman_farmer_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f33e",
- [":woman_farmer_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f33e",
- [":woman_farmer::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f33e",
- [":woman_farmer_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f33e",
- [":woman_farmer_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f33e",
- [":woman_farmer::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f33e",
- [":woman_farmer_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f33e",
- [":woman_farmer_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f33e",
- [":woman_farmer::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f33e",
- [":woman_feeding_baby:"] = "\U0001f469\u200d\U0001f37c",
- [":woman_feeding_baby_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f37c",
- [":woman_feeding_baby_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f37c",
- [":woman_feeding_baby::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f37c",
- [":woman_feeding_baby_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f37c",
- [":woman_feeding_baby_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f37c",
- [":woman_feeding_baby::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f37c",
- [":woman_feeding_baby_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f37c",
- [":woman_feeding_baby_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f37c",
- [":woman_feeding_baby::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f37c",
- [":woman_feeding_baby_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f37c",
- [":woman_feeding_baby_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f37c",
- [":woman_feeding_baby::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f37c",
- [":woman_feeding_baby_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f37c",
- [":woman_feeding_baby_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f37c",
- [":woman_feeding_baby::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f37c",
- [":woman_firefighter:"] = "\U0001f469\u200d\U0001f692",
- [":woman_firefighter_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f692",
- [":woman_firefighter_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f692",
- [":woman_firefighter::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f692",
- [":woman_firefighter_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f692",
- [":woman_firefighter_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f692",
- [":woman_firefighter::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f692",
- [":woman_firefighter_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f692",
- [":woman_firefighter_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f692",
- [":woman_firefighter::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f692",
- [":woman_firefighter_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f692",
- [":woman_firefighter_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f692",
- [":woman_firefighter::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f692",
- [":woman_firefighter_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f692",
- [":woman_firefighter_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f692",
- [":woman_firefighter::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f692",
- [":woman_frowning:"] = "\U0001f64d\u200d\u2640\ufe0f",
- [":woman_frowning_tone1:"] = "\U0001f64d\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_frowning_light_skin_tone:"] = "\U0001f64d\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_frowning::skin-tone-1:"] = "\U0001f64d\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_frowning_tone2:"] = "\U0001f64d\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_frowning_medium_light_skin_tone:"] = "\U0001f64d\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_frowning::skin-tone-2:"] = "\U0001f64d\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_frowning_tone3:"] = "\U0001f64d\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_frowning_medium_skin_tone:"] = "\U0001f64d\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_frowning::skin-tone-3:"] = "\U0001f64d\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_frowning_tone4:"] = "\U0001f64d\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_frowning_medium_dark_skin_tone:"] = "\U0001f64d\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_frowning::skin-tone-4:"] = "\U0001f64d\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_frowning_tone5:"] = "\U0001f64d\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_frowning_dark_skin_tone:"] = "\U0001f64d\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_frowning::skin-tone-5:"] = "\U0001f64d\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_genie:"] = "\U0001f9de\u200d\u2640\ufe0f",
- [":woman_gesturing_no:"] = "\U0001f645\u200d\u2640\ufe0f",
- [":woman_gesturing_no_tone1:"] = "\U0001f645\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_gesturing_no_light_skin_tone:"] = "\U0001f645\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_gesturing_no::skin-tone-1:"] = "\U0001f645\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_gesturing_no_tone2:"] = "\U0001f645\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_gesturing_no_medium_light_skin_tone:"] = "\U0001f645\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_gesturing_no::skin-tone-2:"] = "\U0001f645\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_gesturing_no_tone3:"] = "\U0001f645\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_gesturing_no_medium_skin_tone:"] = "\U0001f645\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_gesturing_no::skin-tone-3:"] = "\U0001f645\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_gesturing_no_tone4:"] = "\U0001f645\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_gesturing_no_medium_dark_skin_tone:"] = "\U0001f645\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_gesturing_no::skin-tone-4:"] = "\U0001f645\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_gesturing_no_tone5:"] = "\U0001f645\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_gesturing_no_dark_skin_tone:"] = "\U0001f645\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_gesturing_no::skin-tone-5:"] = "\U0001f645\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_gesturing_ok:"] = "\U0001f646\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_tone1:"] = "\U0001f646\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_light_skin_tone:"] = "\U0001f646\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_gesturing_ok::skin-tone-1:"] = "\U0001f646\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_tone2:"] = "\U0001f646\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_medium_light_skin_tone:"] = "\U0001f646\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_gesturing_ok::skin-tone-2:"] = "\U0001f646\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_tone3:"] = "\U0001f646\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_medium_skin_tone:"] = "\U0001f646\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_gesturing_ok::skin-tone-3:"] = "\U0001f646\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_tone4:"] = "\U0001f646\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_medium_dark_skin_tone:"] = "\U0001f646\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_gesturing_ok::skin-tone-4:"] = "\U0001f646\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_tone5:"] = "\U0001f646\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_gesturing_ok_dark_skin_tone:"] = "\U0001f646\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_gesturing_ok::skin-tone-5:"] = "\U0001f646\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_getting_face_massage:"] = "\U0001f486\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_tone1:"] = "\U0001f486\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_light_skin_tone:"] = "\U0001f486\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_getting_face_massage::skin-tone-1:"] = "\U0001f486\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_tone2:"] = "\U0001f486\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_medium_light_skin_tone:"] = "\U0001f486\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_getting_face_massage::skin-tone-2:"] = "\U0001f486\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_tone3:"] = "\U0001f486\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_medium_skin_tone:"] = "\U0001f486\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_getting_face_massage::skin-tone-3:"] = "\U0001f486\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_tone4:"] = "\U0001f486\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_medium_dark_skin_tone:"] = "\U0001f486\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_getting_face_massage::skin-tone-4:"] = "\U0001f486\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_tone5:"] = "\U0001f486\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_getting_face_massage_dark_skin_tone:"] = "\U0001f486\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_getting_face_massage::skin-tone-5:"] = "\U0001f486\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_getting_haircut:"] = "\U0001f487\u200d\u2640\ufe0f",
- [":woman_getting_haircut_tone1:"] = "\U0001f487\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_getting_haircut_light_skin_tone:"] = "\U0001f487\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_getting_haircut::skin-tone-1:"] = "\U0001f487\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_getting_haircut_tone2:"] = "\U0001f487\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_getting_haircut_medium_light_skin_tone:"] = "\U0001f487\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_getting_haircut::skin-tone-2:"] = "\U0001f487\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_getting_haircut_tone3:"] = "\U0001f487\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_getting_haircut_medium_skin_tone:"] = "\U0001f487\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_getting_haircut::skin-tone-3:"] = "\U0001f487\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_getting_haircut_tone4:"] = "\U0001f487\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_getting_haircut_medium_dark_skin_tone:"] = "\U0001f487\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_getting_haircut::skin-tone-4:"] = "\U0001f487\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_getting_haircut_tone5:"] = "\U0001f487\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_getting_haircut_dark_skin_tone:"] = "\U0001f487\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_getting_haircut::skin-tone-5:"] = "\U0001f487\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_golfing:"] = "\U0001f3cc\ufe0f\u200d\u2640\ufe0f",
- [":woman_golfing_tone1:"] = "\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_golfing_light_skin_tone:"] = "\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_golfing::skin-tone-1:"] = "\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_golfing_tone2:"] = "\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_golfing_medium_light_skin_tone:"] = "\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_golfing::skin-tone-2:"] = "\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_golfing_tone3:"] = "\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_golfing_medium_skin_tone:"] = "\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_golfing::skin-tone-3:"] = "\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_golfing_tone4:"] = "\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_golfing_medium_dark_skin_tone:"] = "\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_golfing::skin-tone-4:"] = "\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_golfing_tone5:"] = "\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_golfing_dark_skin_tone:"] = "\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_golfing::skin-tone-5:"] = "\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_guard:"] = "\U0001f482\u200d\u2640\ufe0f",
- [":woman_guard_tone1:"] = "\U0001f482\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_guard_light_skin_tone:"] = "\U0001f482\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_guard::skin-tone-1:"] = "\U0001f482\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_guard_tone2:"] = "\U0001f482\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_guard_medium_light_skin_tone:"] = "\U0001f482\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_guard::skin-tone-2:"] = "\U0001f482\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_guard_tone3:"] = "\U0001f482\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_guard_medium_skin_tone:"] = "\U0001f482\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_guard::skin-tone-3:"] = "\U0001f482\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_guard_tone4:"] = "\U0001f482\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_guard_medium_dark_skin_tone:"] = "\U0001f482\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_guard::skin-tone-4:"] = "\U0001f482\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_guard_tone5:"] = "\U0001f482\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_guard_dark_skin_tone:"] = "\U0001f482\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_guard::skin-tone-5:"] = "\U0001f482\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_health_worker:"] = "\U0001f469\u200d\u2695\ufe0f",
- [":woman_health_worker_tone1:"] = "\U0001f469\U0001f3fb\u200d\u2695\ufe0f",
- [":woman_health_worker_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\u2695\ufe0f",
- [":woman_health_worker::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\u2695\ufe0f",
- [":woman_health_worker_tone2:"] = "\U0001f469\U0001f3fc\u200d\u2695\ufe0f",
- [":woman_health_worker_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\u2695\ufe0f",
- [":woman_health_worker::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\u2695\ufe0f",
- [":woman_health_worker_tone3:"] = "\U0001f469\U0001f3fd\u200d\u2695\ufe0f",
- [":woman_health_worker_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\u2695\ufe0f",
- [":woman_health_worker::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\u2695\ufe0f",
- [":woman_health_worker_tone4:"] = "\U0001f469\U0001f3fe\u200d\u2695\ufe0f",
- [":woman_health_worker_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\u2695\ufe0f",
- [":woman_health_worker::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\u2695\ufe0f",
- [":woman_health_worker_tone5:"] = "\U0001f469\U0001f3ff\u200d\u2695\ufe0f",
- [":woman_health_worker_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\u2695\ufe0f",
- [":woman_health_worker::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\u2695\ufe0f",
- [":woman_in_lotus_position:"] = "\U0001f9d8\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_tone1:"] = "\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_light_skin_tone:"] = "\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_in_lotus_position::skin-tone-1:"] = "\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_tone2:"] = "\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_medium_light_skin_tone:"] = "\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_in_lotus_position::skin-tone-2:"] = "\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_tone3:"] = "\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_medium_skin_tone:"] = "\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_in_lotus_position::skin-tone-3:"] = "\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_tone4:"] = "\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_medium_dark_skin_tone:"] = "\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_in_lotus_position::skin-tone-4:"] = "\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_tone5:"] = "\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_in_lotus_position_dark_skin_tone:"] = "\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_in_lotus_position::skin-tone-5:"] = "\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_in_manual_wheelchair:"] = "\U0001f469\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bd",
- [":woman_in_manual_wheelchair::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bd",
- [":woman_in_motorized_wheelchair:"] = "\U0001f469\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bc",
- [":woman_in_motorized_wheelchair::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bc",
- [":woman_in_steamy_room:"] = "\U0001f9d6\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_tone1:"] = "\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_light_skin_tone:"] = "\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_in_steamy_room::skin-tone-1:"] = "\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_tone2:"] = "\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_medium_light_skin_tone:"] = "\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_in_steamy_room::skin-tone-2:"] = "\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_tone3:"] = "\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_medium_skin_tone:"] = "\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_in_steamy_room::skin-tone-3:"] = "\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_tone4:"] = "\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_medium_dark_skin_tone:"] = "\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_in_steamy_room::skin-tone-4:"] = "\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_tone5:"] = "\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_in_steamy_room_dark_skin_tone:"] = "\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_in_steamy_room::skin-tone-5:"] = "\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_in_tuxedo:"] = "\U0001f935\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_tone1:"] = "\U0001f935\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_light_skin_tone:"] = "\U0001f935\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_in_tuxedo::skin-tone-1:"] = "\U0001f935\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_tone2:"] = "\U0001f935\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_medium_light_skin_tone:"] = "\U0001f935\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_in_tuxedo::skin-tone-2:"] = "\U0001f935\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_tone3:"] = "\U0001f935\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_medium_skin_tone:"] = "\U0001f935\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_in_tuxedo::skin-tone-3:"] = "\U0001f935\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_tone4:"] = "\U0001f935\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_medium_dark_skin_tone:"] = "\U0001f935\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_in_tuxedo::skin-tone-4:"] = "\U0001f935\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_tone5:"] = "\U0001f935\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_in_tuxedo_dark_skin_tone:"] = "\U0001f935\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_in_tuxedo::skin-tone-5:"] = "\U0001f935\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_judge:"] = "\U0001f469\u200d\u2696\ufe0f",
- [":woman_judge_tone1:"] = "\U0001f469\U0001f3fb\u200d\u2696\ufe0f",
- [":woman_judge_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\u2696\ufe0f",
- [":woman_judge::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\u2696\ufe0f",
- [":woman_judge_tone2:"] = "\U0001f469\U0001f3fc\u200d\u2696\ufe0f",
- [":woman_judge_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\u2696\ufe0f",
- [":woman_judge::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\u2696\ufe0f",
- [":woman_judge_tone3:"] = "\U0001f469\U0001f3fd\u200d\u2696\ufe0f",
- [":woman_judge_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\u2696\ufe0f",
- [":woman_judge::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\u2696\ufe0f",
- [":woman_judge_tone4:"] = "\U0001f469\U0001f3fe\u200d\u2696\ufe0f",
- [":woman_judge_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\u2696\ufe0f",
- [":woman_judge::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\u2696\ufe0f",
- [":woman_judge_tone5:"] = "\U0001f469\U0001f3ff\u200d\u2696\ufe0f",
- [":woman_judge_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\u2696\ufe0f",
- [":woman_judge::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\u2696\ufe0f",
- [":woman_juggling:"] = "\U0001f939\u200d\u2640\ufe0f",
- [":woman_juggling_tone1:"] = "\U0001f939\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_juggling_light_skin_tone:"] = "\U0001f939\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_juggling::skin-tone-1:"] = "\U0001f939\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_juggling_tone2:"] = "\U0001f939\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_juggling_medium_light_skin_tone:"] = "\U0001f939\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_juggling::skin-tone-2:"] = "\U0001f939\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_juggling_tone3:"] = "\U0001f939\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_juggling_medium_skin_tone:"] = "\U0001f939\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_juggling::skin-tone-3:"] = "\U0001f939\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_juggling_tone4:"] = "\U0001f939\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_juggling_medium_dark_skin_tone:"] = "\U0001f939\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_juggling::skin-tone-4:"] = "\U0001f939\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_juggling_tone5:"] = "\U0001f939\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_juggling_dark_skin_tone:"] = "\U0001f939\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_juggling::skin-tone-5:"] = "\U0001f939\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_kneeling:"] = "\U0001f9ce\u200d\u2640\ufe0f",
- [":woman_kneeling_tone1:"] = "\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_kneeling_light_skin_tone:"] = "\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_kneeling::skin-tone-1:"] = "\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_kneeling_tone2:"] = "\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_kneeling_medium_light_skin_tone:"] = "\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_kneeling::skin-tone-2:"] = "\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_kneeling_tone3:"] = "\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_kneeling_medium_skin_tone:"] = "\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_kneeling::skin-tone-3:"] = "\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_kneeling_tone4:"] = "\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_kneeling_medium_dark_skin_tone:"] = "\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_kneeling::skin-tone-4:"] = "\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_kneeling_tone5:"] = "\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_kneeling_dark_skin_tone:"] = "\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_kneeling::skin-tone-5:"] = "\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_lifting_weights:"] = "\U0001f3cb\ufe0f\u200d\u2640\ufe0f",
- [":woman_lifting_weights_tone1:"] = "\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_lifting_weights_light_skin_tone:"] = "\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_lifting_weights::skin-tone-1:"] = "\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_lifting_weights_tone2:"] = "\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_lifting_weights_medium_light_skin_tone:"] = "\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_lifting_weights::skin-tone-2:"] = "\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_lifting_weights_tone3:"] = "\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_lifting_weights_medium_skin_tone:"] = "\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_lifting_weights::skin-tone-3:"] = "\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_lifting_weights_tone4:"] = "\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_lifting_weights_medium_dark_skin_tone:"] = "\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_lifting_weights::skin-tone-4:"] = "\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_lifting_weights_tone5:"] = "\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_lifting_weights_dark_skin_tone:"] = "\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_lifting_weights::skin-tone-5:"] = "\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_mage:"] = "\U0001f9d9\u200d\u2640\ufe0f",
- [":woman_mage_tone1:"] = "\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_mage_light_skin_tone:"] = "\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_mage::skin-tone-1:"] = "\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_mage_tone2:"] = "\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_mage_medium_light_skin_tone:"] = "\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_mage::skin-tone-2:"] = "\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_mage_tone3:"] = "\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_mage_medium_skin_tone:"] = "\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_mage::skin-tone-3:"] = "\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_mage_tone4:"] = "\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_mage_medium_dark_skin_tone:"] = "\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_mage::skin-tone-4:"] = "\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_mage_tone5:"] = "\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_mage_dark_skin_tone:"] = "\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_mage::skin-tone-5:"] = "\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_mechanic:"] = "\U0001f469\u200d\U0001f527",
- [":woman_mechanic_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f527",
- [":woman_mechanic_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f527",
- [":woman_mechanic::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f527",
- [":woman_mechanic_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f527",
- [":woman_mechanic_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f527",
- [":woman_mechanic::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f527",
- [":woman_mechanic_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f527",
- [":woman_mechanic_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f527",
- [":woman_mechanic::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f527",
- [":woman_mechanic_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f527",
- [":woman_mechanic_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f527",
- [":woman_mechanic::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f527",
- [":woman_mechanic_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f527",
- [":woman_mechanic_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f527",
- [":woman_mechanic::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f527",
- [":woman_mountain_biking:"] = "\U0001f6b5\u200d\u2640\ufe0f",
- [":woman_mountain_biking_tone1:"] = "\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_mountain_biking_light_skin_tone:"] = "\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_mountain_biking::skin-tone-1:"] = "\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_mountain_biking_tone2:"] = "\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_mountain_biking_medium_light_skin_tone:"] = "\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_mountain_biking::skin-tone-2:"] = "\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_mountain_biking_tone3:"] = "\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_mountain_biking_medium_skin_tone:"] = "\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_mountain_biking::skin-tone-3:"] = "\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_mountain_biking_tone4:"] = "\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_mountain_biking_medium_dark_skin_tone:"] = "\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_mountain_biking::skin-tone-4:"] = "\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_mountain_biking_tone5:"] = "\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_mountain_biking_dark_skin_tone:"] = "\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_mountain_biking::skin-tone-5:"] = "\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_office_worker:"] = "\U0001f469\u200d\U0001f4bc",
- [":woman_office_worker_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bc",
- [":woman_office_worker_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bc",
- [":woman_office_worker::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bc",
- [":woman_office_worker_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bc",
- [":woman_office_worker_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bc",
- [":woman_office_worker::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bc",
- [":woman_office_worker_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bc",
- [":woman_office_worker_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bc",
- [":woman_office_worker::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bc",
- [":woman_office_worker_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bc",
- [":woman_office_worker_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bc",
- [":woman_office_worker::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bc",
- [":woman_office_worker_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bc",
- [":woman_office_worker_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bc",
- [":woman_office_worker::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bc",
- [":woman_pilot:"] = "\U0001f469\u200d\u2708\ufe0f",
- [":woman_pilot_tone1:"] = "\U0001f469\U0001f3fb\u200d\u2708\ufe0f",
- [":woman_pilot_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\u2708\ufe0f",
- [":woman_pilot::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\u2708\ufe0f",
- [":woman_pilot_tone2:"] = "\U0001f469\U0001f3fc\u200d\u2708\ufe0f",
- [":woman_pilot_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\u2708\ufe0f",
- [":woman_pilot::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\u2708\ufe0f",
- [":woman_pilot_tone3:"] = "\U0001f469\U0001f3fd\u200d\u2708\ufe0f",
- [":woman_pilot_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\u2708\ufe0f",
- [":woman_pilot::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\u2708\ufe0f",
- [":woman_pilot_tone4:"] = "\U0001f469\U0001f3fe\u200d\u2708\ufe0f",
- [":woman_pilot_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\u2708\ufe0f",
- [":woman_pilot::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\u2708\ufe0f",
- [":woman_pilot_tone5:"] = "\U0001f469\U0001f3ff\u200d\u2708\ufe0f",
- [":woman_pilot_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\u2708\ufe0f",
- [":woman_pilot::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\u2708\ufe0f",
- [":woman_playing_handball:"] = "\U0001f93e\u200d\u2640\ufe0f",
- [":woman_playing_handball_tone1:"] = "\U0001f93e\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_playing_handball_light_skin_tone:"] = "\U0001f93e\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_playing_handball::skin-tone-1:"] = "\U0001f93e\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_playing_handball_tone2:"] = "\U0001f93e\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_playing_handball_medium_light_skin_tone:"] = "\U0001f93e\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_playing_handball::skin-tone-2:"] = "\U0001f93e\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_playing_handball_tone3:"] = "\U0001f93e\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_playing_handball_medium_skin_tone:"] = "\U0001f93e\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_playing_handball::skin-tone-3:"] = "\U0001f93e\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_playing_handball_tone4:"] = "\U0001f93e\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_playing_handball_medium_dark_skin_tone:"] = "\U0001f93e\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_playing_handball::skin-tone-4:"] = "\U0001f93e\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_playing_handball_tone5:"] = "\U0001f93e\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_playing_handball_dark_skin_tone:"] = "\U0001f93e\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_playing_handball::skin-tone-5:"] = "\U0001f93e\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_playing_water_polo:"] = "\U0001f93d\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_tone1:"] = "\U0001f93d\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_light_skin_tone:"] = "\U0001f93d\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_playing_water_polo::skin-tone-1:"] = "\U0001f93d\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_tone2:"] = "\U0001f93d\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_medium_light_skin_tone:"] = "\U0001f93d\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_playing_water_polo::skin-tone-2:"] = "\U0001f93d\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_tone3:"] = "\U0001f93d\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_medium_skin_tone:"] = "\U0001f93d\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_playing_water_polo::skin-tone-3:"] = "\U0001f93d\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_tone4:"] = "\U0001f93d\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_medium_dark_skin_tone:"] = "\U0001f93d\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_playing_water_polo::skin-tone-4:"] = "\U0001f93d\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_tone5:"] = "\U0001f93d\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_playing_water_polo_dark_skin_tone:"] = "\U0001f93d\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_playing_water_polo::skin-tone-5:"] = "\U0001f93d\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_police_officer:"] = "\U0001f46e\u200d\u2640\ufe0f",
- [":woman_police_officer_tone1:"] = "\U0001f46e\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_police_officer_light_skin_tone:"] = "\U0001f46e\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_police_officer::skin-tone-1:"] = "\U0001f46e\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_police_officer_tone2:"] = "\U0001f46e\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_police_officer_medium_light_skin_tone:"] = "\U0001f46e\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_police_officer::skin-tone-2:"] = "\U0001f46e\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_police_officer_tone3:"] = "\U0001f46e\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_police_officer_medium_skin_tone:"] = "\U0001f46e\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_police_officer::skin-tone-3:"] = "\U0001f46e\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_police_officer_tone4:"] = "\U0001f46e\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_police_officer_medium_dark_skin_tone:"] = "\U0001f46e\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_police_officer::skin-tone-4:"] = "\U0001f46e\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_police_officer_tone5:"] = "\U0001f46e\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_police_officer_dark_skin_tone:"] = "\U0001f46e\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_police_officer::skin-tone-5:"] = "\U0001f46e\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_pouting:"] = "\U0001f64e\u200d\u2640\ufe0f",
- [":woman_pouting_tone1:"] = "\U0001f64e\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_pouting_light_skin_tone:"] = "\U0001f64e\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_pouting::skin-tone-1:"] = "\U0001f64e\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_pouting_tone2:"] = "\U0001f64e\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_pouting_medium_light_skin_tone:"] = "\U0001f64e\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_pouting::skin-tone-2:"] = "\U0001f64e\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_pouting_tone3:"] = "\U0001f64e\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_pouting_medium_skin_tone:"] = "\U0001f64e\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_pouting::skin-tone-3:"] = "\U0001f64e\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_pouting_tone4:"] = "\U0001f64e\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_pouting_medium_dark_skin_tone:"] = "\U0001f64e\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_pouting::skin-tone-4:"] = "\U0001f64e\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_pouting_tone5:"] = "\U0001f64e\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_pouting_dark_skin_tone:"] = "\U0001f64e\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_pouting::skin-tone-5:"] = "\U0001f64e\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_raising_hand:"] = "\U0001f64b\u200d\u2640\ufe0f",
- [":woman_raising_hand_tone1:"] = "\U0001f64b\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_raising_hand_light_skin_tone:"] = "\U0001f64b\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_raising_hand::skin-tone-1:"] = "\U0001f64b\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_raising_hand_tone2:"] = "\U0001f64b\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_raising_hand_medium_light_skin_tone:"] = "\U0001f64b\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_raising_hand::skin-tone-2:"] = "\U0001f64b\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_raising_hand_tone3:"] = "\U0001f64b\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_raising_hand_medium_skin_tone:"] = "\U0001f64b\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_raising_hand::skin-tone-3:"] = "\U0001f64b\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_raising_hand_tone4:"] = "\U0001f64b\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_raising_hand_medium_dark_skin_tone:"] = "\U0001f64b\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_raising_hand::skin-tone-4:"] = "\U0001f64b\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_raising_hand_tone5:"] = "\U0001f64b\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_raising_hand_dark_skin_tone:"] = "\U0001f64b\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_raising_hand::skin-tone-5:"] = "\U0001f64b\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_red_haired:"] = "\U0001f469\u200d\U0001f9b0",
- [":woman_red_haired_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b0",
- [":woman_red_haired_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b0",
- [":woman_red_haired::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b0",
- [":woman_red_haired_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b0",
- [":woman_red_haired_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b0",
- [":woman_red_haired::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b0",
- [":woman_red_haired_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b0",
- [":woman_red_haired_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b0",
- [":woman_red_haired::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b0",
- [":woman_red_haired_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b0",
- [":woman_red_haired_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b0",
- [":woman_red_haired::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b0",
- [":woman_red_haired_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b0",
- [":woman_red_haired_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b0",
- [":woman_red_haired::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b0",
- [":woman_rowing_boat:"] = "\U0001f6a3\u200d\u2640\ufe0f",
- [":woman_rowing_boat_tone1:"] = "\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_rowing_boat_light_skin_tone:"] = "\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_rowing_boat::skin-tone-1:"] = "\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_rowing_boat_tone2:"] = "\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_rowing_boat_medium_light_skin_tone:"] = "\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_rowing_boat::skin-tone-2:"] = "\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_rowing_boat_tone3:"] = "\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_rowing_boat_medium_skin_tone:"] = "\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_rowing_boat::skin-tone-3:"] = "\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_rowing_boat_tone4:"] = "\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_rowing_boat_medium_dark_skin_tone:"] = "\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_rowing_boat::skin-tone-4:"] = "\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_rowing_boat_tone5:"] = "\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_rowing_boat_dark_skin_tone:"] = "\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_rowing_boat::skin-tone-5:"] = "\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_running:"] = "\U0001f3c3\u200d\u2640\ufe0f",
- [":woman_running_tone1:"] = "\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_running_light_skin_tone:"] = "\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_running::skin-tone-1:"] = "\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_running_tone2:"] = "\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_running_medium_light_skin_tone:"] = "\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_running::skin-tone-2:"] = "\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_running_tone3:"] = "\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_running_medium_skin_tone:"] = "\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_running::skin-tone-3:"] = "\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_running_tone4:"] = "\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_running_medium_dark_skin_tone:"] = "\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_running::skin-tone-4:"] = "\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_running_tone5:"] = "\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_running_dark_skin_tone:"] = "\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_running::skin-tone-5:"] = "\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_scientist:"] = "\U0001f469\u200d\U0001f52c",
- [":woman_scientist_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f52c",
- [":woman_scientist_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f52c",
- [":woman_scientist::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f52c",
- [":woman_scientist_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f52c",
- [":woman_scientist_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f52c",
- [":woman_scientist::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f52c",
- [":woman_scientist_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f52c",
- [":woman_scientist_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f52c",
- [":woman_scientist::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f52c",
- [":woman_scientist_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f52c",
- [":woman_scientist_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f52c",
- [":woman_scientist::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f52c",
- [":woman_scientist_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f52c",
- [":woman_scientist_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f52c",
- [":woman_scientist::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f52c",
- [":woman_shrugging:"] = "\U0001f937\u200d\u2640\ufe0f",
- [":woman_shrugging_tone1:"] = "\U0001f937\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_shrugging_light_skin_tone:"] = "\U0001f937\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_shrugging::skin-tone-1:"] = "\U0001f937\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_shrugging_tone2:"] = "\U0001f937\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_shrugging_medium_light_skin_tone:"] = "\U0001f937\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_shrugging::skin-tone-2:"] = "\U0001f937\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_shrugging_tone3:"] = "\U0001f937\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_shrugging_medium_skin_tone:"] = "\U0001f937\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_shrugging::skin-tone-3:"] = "\U0001f937\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_shrugging_tone4:"] = "\U0001f937\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_shrugging_medium_dark_skin_tone:"] = "\U0001f937\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_shrugging::skin-tone-4:"] = "\U0001f937\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_shrugging_tone5:"] = "\U0001f937\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_shrugging_dark_skin_tone:"] = "\U0001f937\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_shrugging::skin-tone-5:"] = "\U0001f937\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_singer:"] = "\U0001f469\u200d\U0001f3a4",
- [":woman_singer_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a4",
- [":woman_singer_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a4",
- [":woman_singer::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a4",
- [":woman_singer_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a4",
- [":woman_singer_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a4",
- [":woman_singer::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a4",
- [":woman_singer_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a4",
- [":woman_singer_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a4",
- [":woman_singer::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a4",
- [":woman_singer_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a4",
- [":woman_singer_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a4",
- [":woman_singer::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a4",
- [":woman_singer_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a4",
- [":woman_singer_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a4",
- [":woman_singer::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a4",
- [":woman_standing:"] = "\U0001f9cd\u200d\u2640\ufe0f",
- [":woman_standing_tone1:"] = "\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_standing_light_skin_tone:"] = "\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_standing::skin-tone-1:"] = "\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_standing_tone2:"] = "\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_standing_medium_light_skin_tone:"] = "\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_standing::skin-tone-2:"] = "\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_standing_tone3:"] = "\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_standing_medium_skin_tone:"] = "\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_standing::skin-tone-3:"] = "\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_standing_tone4:"] = "\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_standing_medium_dark_skin_tone:"] = "\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_standing::skin-tone-4:"] = "\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_standing_tone5:"] = "\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_standing_dark_skin_tone:"] = "\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_standing::skin-tone-5:"] = "\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_student:"] = "\U0001f469\u200d\U0001f393",
- [":woman_student_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f393",
- [":woman_student_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f393",
- [":woman_student::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f393",
- [":woman_student_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f393",
- [":woman_student_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f393",
- [":woman_student::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f393",
- [":woman_student_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f393",
- [":woman_student_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f393",
- [":woman_student::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f393",
- [":woman_student_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f393",
- [":woman_student_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f393",
- [":woman_student::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f393",
- [":woman_student_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f393",
- [":woman_student_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f393",
- [":woman_student::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f393",
- [":woman_superhero:"] = "\U0001f9b8\u200d\u2640\ufe0f",
- [":woman_superhero_tone1:"] = "\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_superhero_light_skin_tone:"] = "\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_superhero::skin-tone-1:"] = "\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_superhero_tone2:"] = "\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_superhero_medium_light_skin_tone:"] = "\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_superhero::skin-tone-2:"] = "\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_superhero_tone3:"] = "\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_superhero_medium_skin_tone:"] = "\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_superhero::skin-tone-3:"] = "\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_superhero_tone4:"] = "\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_superhero_medium_dark_skin_tone:"] = "\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_superhero::skin-tone-4:"] = "\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_superhero_tone5:"] = "\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_superhero_dark_skin_tone:"] = "\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_superhero::skin-tone-5:"] = "\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_supervillain:"] = "\U0001f9b9\u200d\u2640\ufe0f",
- [":woman_supervillain_tone1:"] = "\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_supervillain_light_skin_tone:"] = "\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_supervillain::skin-tone-1:"] = "\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_supervillain_tone2:"] = "\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_supervillain_medium_light_skin_tone:"] = "\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_supervillain::skin-tone-2:"] = "\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_supervillain_tone3:"] = "\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_supervillain_medium_skin_tone:"] = "\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_supervillain::skin-tone-3:"] = "\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_supervillain_tone4:"] = "\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_supervillain_medium_dark_skin_tone:"] = "\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_supervillain::skin-tone-4:"] = "\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_supervillain_tone5:"] = "\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_supervillain_dark_skin_tone:"] = "\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_supervillain::skin-tone-5:"] = "\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_surfing:"] = "\U0001f3c4\u200d\u2640\ufe0f",
- [":woman_surfing_tone1:"] = "\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_surfing_light_skin_tone:"] = "\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_surfing::skin-tone-1:"] = "\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_surfing_tone2:"] = "\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_surfing_medium_light_skin_tone:"] = "\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_surfing::skin-tone-2:"] = "\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_surfing_tone3:"] = "\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_surfing_medium_skin_tone:"] = "\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_surfing::skin-tone-3:"] = "\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_surfing_tone4:"] = "\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_surfing_medium_dark_skin_tone:"] = "\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_surfing::skin-tone-4:"] = "\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_surfing_tone5:"] = "\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_surfing_dark_skin_tone:"] = "\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_surfing::skin-tone-5:"] = "\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_swimming:"] = "\U0001f3ca\u200d\u2640\ufe0f",
- [":woman_swimming_tone1:"] = "\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_swimming_light_skin_tone:"] = "\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_swimming::skin-tone-1:"] = "\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_swimming_tone2:"] = "\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_swimming_medium_light_skin_tone:"] = "\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_swimming::skin-tone-2:"] = "\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_swimming_tone3:"] = "\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_swimming_medium_skin_tone:"] = "\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_swimming::skin-tone-3:"] = "\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_swimming_tone4:"] = "\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_swimming_medium_dark_skin_tone:"] = "\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_swimming::skin-tone-4:"] = "\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_swimming_tone5:"] = "\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_swimming_dark_skin_tone:"] = "\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_swimming::skin-tone-5:"] = "\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_teacher:"] = "\U0001f469\u200d\U0001f3eb",
- [":woman_teacher_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3eb",
- [":woman_teacher_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f3eb",
- [":woman_teacher::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3eb",
- [":woman_teacher_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3eb",
- [":woman_teacher_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f3eb",
- [":woman_teacher::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3eb",
- [":woman_teacher_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3eb",
- [":woman_teacher_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f3eb",
- [":woman_teacher::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3eb",
- [":woman_teacher_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3eb",
- [":woman_teacher_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f3eb",
- [":woman_teacher::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3eb",
- [":woman_teacher_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3eb",
- [":woman_teacher_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f3eb",
- [":woman_teacher::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3eb",
- [":woman_technologist:"] = "\U0001f469\u200d\U0001f4bb",
- [":woman_technologist_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bb",
- [":woman_technologist_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bb",
- [":woman_technologist::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bb",
- [":woman_technologist_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bb",
- [":woman_technologist_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bb",
- [":woman_technologist::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bb",
- [":woman_technologist_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bb",
- [":woman_technologist_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bb",
- [":woman_technologist::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bb",
- [":woman_technologist_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bb",
- [":woman_technologist_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bb",
- [":woman_technologist::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bb",
- [":woman_technologist_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bb",
- [":woman_technologist_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bb",
- [":woman_technologist::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bb",
- [":woman_tipping_hand:"] = "\U0001f481\u200d\u2640\ufe0f",
- [":woman_tipping_hand_tone1:"] = "\U0001f481\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_tipping_hand_light_skin_tone:"] = "\U0001f481\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_tipping_hand::skin-tone-1:"] = "\U0001f481\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_tipping_hand_tone2:"] = "\U0001f481\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_tipping_hand_medium_light_skin_tone:"] = "\U0001f481\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_tipping_hand::skin-tone-2:"] = "\U0001f481\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_tipping_hand_tone3:"] = "\U0001f481\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_tipping_hand_medium_skin_tone:"] = "\U0001f481\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_tipping_hand::skin-tone-3:"] = "\U0001f481\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_tipping_hand_tone4:"] = "\U0001f481\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_tipping_hand_medium_dark_skin_tone:"] = "\U0001f481\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_tipping_hand::skin-tone-4:"] = "\U0001f481\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_tipping_hand_tone5:"] = "\U0001f481\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_tipping_hand_dark_skin_tone:"] = "\U0001f481\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_tipping_hand::skin-tone-5:"] = "\U0001f481\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_tone1:"] = "\U0001f469\U0001f3fb",
- [":woman::skin-tone-1:"] = "\U0001f469\U0001f3fb",
- [":woman_tone2:"] = "\U0001f469\U0001f3fc",
- [":woman::skin-tone-2:"] = "\U0001f469\U0001f3fc",
- [":woman_tone3:"] = "\U0001f469\U0001f3fd",
- [":woman::skin-tone-3:"] = "\U0001f469\U0001f3fd",
- [":woman_tone4:"] = "\U0001f469\U0001f3fe",
- [":woman::skin-tone-4:"] = "\U0001f469\U0001f3fe",
- [":woman_tone5:"] = "\U0001f469\U0001f3ff",
- [":woman::skin-tone-5:"] = "\U0001f469\U0001f3ff",
- [":woman_vampire:"] = "\U0001f9db\u200d\u2640\ufe0f",
- [":woman_vampire_tone1:"] = "\U0001f9db\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_vampire_light_skin_tone:"] = "\U0001f9db\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_vampire::skin-tone-1:"] = "\U0001f9db\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_vampire_tone2:"] = "\U0001f9db\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_vampire_medium_light_skin_tone:"] = "\U0001f9db\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_vampire::skin-tone-2:"] = "\U0001f9db\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_vampire_tone3:"] = "\U0001f9db\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_vampire_medium_skin_tone:"] = "\U0001f9db\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_vampire::skin-tone-3:"] = "\U0001f9db\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_vampire_tone4:"] = "\U0001f9db\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_vampire_medium_dark_skin_tone:"] = "\U0001f9db\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_vampire::skin-tone-4:"] = "\U0001f9db\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_vampire_tone5:"] = "\U0001f9db\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_vampire_dark_skin_tone:"] = "\U0001f9db\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_vampire::skin-tone-5:"] = "\U0001f9db\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_walking:"] = "\U0001f6b6\u200d\u2640\ufe0f",
- [":woman_walking_tone1:"] = "\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_walking_light_skin_tone:"] = "\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_walking::skin-tone-1:"] = "\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_walking_tone2:"] = "\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_walking_medium_light_skin_tone:"] = "\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_walking::skin-tone-2:"] = "\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_walking_tone3:"] = "\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_walking_medium_skin_tone:"] = "\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_walking::skin-tone-3:"] = "\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_walking_tone4:"] = "\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_walking_medium_dark_skin_tone:"] = "\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_walking::skin-tone-4:"] = "\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_walking_tone5:"] = "\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_walking_dark_skin_tone:"] = "\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_walking::skin-tone-5:"] = "\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_wearing_turban:"] = "\U0001f473\u200d\u2640\ufe0f",
- [":woman_wearing_turban_tone1:"] = "\U0001f473\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_wearing_turban_light_skin_tone:"] = "\U0001f473\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_wearing_turban::skin-tone-1:"] = "\U0001f473\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_wearing_turban_tone2:"] = "\U0001f473\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_wearing_turban_medium_light_skin_tone:"] = "\U0001f473\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_wearing_turban::skin-tone-2:"] = "\U0001f473\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_wearing_turban_tone3:"] = "\U0001f473\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_wearing_turban_medium_skin_tone:"] = "\U0001f473\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_wearing_turban::skin-tone-3:"] = "\U0001f473\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_wearing_turban_tone4:"] = "\U0001f473\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_wearing_turban_medium_dark_skin_tone:"] = "\U0001f473\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_wearing_turban::skin-tone-4:"] = "\U0001f473\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_wearing_turban_tone5:"] = "\U0001f473\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_wearing_turban_dark_skin_tone:"] = "\U0001f473\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_wearing_turban::skin-tone-5:"] = "\U0001f473\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_white_haired:"] = "\U0001f469\u200d\U0001f9b3",
- [":woman_white_haired_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b3",
- [":woman_white_haired_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b3",
- [":woman_white_haired::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b3",
- [":woman_white_haired_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b3",
- [":woman_white_haired_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b3",
- [":woman_white_haired::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b3",
- [":woman_white_haired_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b3",
- [":woman_white_haired_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b3",
- [":woman_white_haired::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b3",
- [":woman_white_haired_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b3",
- [":woman_white_haired_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b3",
- [":woman_white_haired::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b3",
- [":woman_white_haired_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b3",
- [":woman_white_haired_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b3",
- [":woman_white_haired::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b3",
- [":woman_with_headscarf:"] = "\U0001f9d5",
- [":woman_with_headscarf_tone1:"] = "\U0001f9d5\U0001f3fb",
- [":woman_with_headscarf_light_skin_tone:"] = "\U0001f9d5\U0001f3fb",
- [":woman_with_headscarf::skin-tone-1:"] = "\U0001f9d5\U0001f3fb",
- [":woman_with_headscarf_tone2:"] = "\U0001f9d5\U0001f3fc",
- [":woman_with_headscarf_medium_light_skin_tone:"] = "\U0001f9d5\U0001f3fc",
- [":woman_with_headscarf::skin-tone-2:"] = "\U0001f9d5\U0001f3fc",
- [":woman_with_headscarf_tone3:"] = "\U0001f9d5\U0001f3fd",
- [":woman_with_headscarf_medium_skin_tone:"] = "\U0001f9d5\U0001f3fd",
- [":woman_with_headscarf::skin-tone-3:"] = "\U0001f9d5\U0001f3fd",
- [":woman_with_headscarf_tone4:"] = "\U0001f9d5\U0001f3fe",
- [":woman_with_headscarf_medium_dark_skin_tone:"] = "\U0001f9d5\U0001f3fe",
- [":woman_with_headscarf::skin-tone-4:"] = "\U0001f9d5\U0001f3fe",
- [":woman_with_headscarf_tone5:"] = "\U0001f9d5\U0001f3ff",
- [":woman_with_headscarf_dark_skin_tone:"] = "\U0001f9d5\U0001f3ff",
- [":woman_with_headscarf::skin-tone-5:"] = "\U0001f9d5\U0001f3ff",
- [":woman_with_probing_cane:"] = "\U0001f469\u200d\U0001f9af",
- [":woman_with_probing_cane_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9af",
- [":woman_with_probing_cane_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9af",
- [":woman_with_probing_cane::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9af",
- [":woman_with_probing_cane_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9af",
- [":woman_with_probing_cane_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9af",
- [":woman_with_probing_cane::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9af",
- [":woman_with_probing_cane_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9af",
- [":woman_with_probing_cane_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9af",
- [":woman_with_probing_cane::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9af",
- [":woman_with_probing_cane_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9af",
- [":woman_with_probing_cane_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9af",
- [":woman_with_probing_cane::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9af",
- [":woman_with_probing_cane_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9af",
- [":woman_with_probing_cane_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9af",
- [":woman_with_probing_cane::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9af",
- [":woman_with_veil:"] = "\U0001f470\u200d\u2640\ufe0f",
- [":bride_with_veil:"] = "\U0001f470\u200d\u2640\ufe0f",
- [":woman_with_veil_tone1:"] = "\U0001f470\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_with_veil_light_skin_tone:"] = "\U0001f470\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_with_veil::skin-tone-1:"] = "\U0001f470\U0001f3fb\u200d\u2640\ufe0f",
- [":bride_with_veil::skin-tone-1:"] = "\U0001f470\U0001f3fb\u200d\u2640\ufe0f",
- [":woman_with_veil_tone2:"] = "\U0001f470\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_with_veil_medium_light_skin_tone:"] = "\U0001f470\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_with_veil::skin-tone-2:"] = "\U0001f470\U0001f3fc\u200d\u2640\ufe0f",
- [":bride_with_veil::skin-tone-2:"] = "\U0001f470\U0001f3fc\u200d\u2640\ufe0f",
- [":woman_with_veil_tone3:"] = "\U0001f470\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_with_veil_medium_skin_tone:"] = "\U0001f470\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_with_veil::skin-tone-3:"] = "\U0001f470\U0001f3fd\u200d\u2640\ufe0f",
- [":bride_with_veil::skin-tone-3:"] = "\U0001f470\U0001f3fd\u200d\u2640\ufe0f",
- [":woman_with_veil_tone4:"] = "\U0001f470\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_with_veil_medium_dark_skin_tone:"] = "\U0001f470\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_with_veil::skin-tone-4:"] = "\U0001f470\U0001f3fe\u200d\u2640\ufe0f",
- [":bride_with_veil::skin-tone-4:"] = "\U0001f470\U0001f3fe\u200d\u2640\ufe0f",
- [":woman_with_veil_tone5:"] = "\U0001f470\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_with_veil_dark_skin_tone:"] = "\U0001f470\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_with_veil::skin-tone-5:"] = "\U0001f470\U0001f3ff\u200d\u2640\ufe0f",
- [":bride_with_veil::skin-tone-5:"] = "\U0001f470\U0001f3ff\u200d\u2640\ufe0f",
- [":woman_zombie:"] = "\U0001f9df\u200d\u2640\ufe0f",
- [":womans_clothes:"] = "\U0001f45a",
- [":womans_flat_shoe:"] = "\U0001f97f",
- [":womans_hat:"] = "\U0001f452",
- [":women_holding_hands_tone1:"] = "\U0001f46d",
- [":women_holding_hands_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone1_tone2:"] = "\U0001f46d",
- [":women_holding_hands_light_skin_tone_medium_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone1_tone3:"] = "\U0001f46d",
- [":women_holding_hands_light_skin_tone_medium_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone1_tone4:"] = "\U0001f46d",
- [":women_holding_hands_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone1_tone5:"] = "\U0001f46d",
- [":women_holding_hands_light_skin_tone_dark_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone2:"] = "\U0001f46d",
- [":women_holding_hands_medium_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone2_tone1:"] = "\U0001f46d",
- [":women_holding_hands_medium_light_skin_tone_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone2_tone3:"] = "\U0001f46d",
- [":women_holding_hands_medium_light_skin_tone_medium_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone2_tone4:"] = "\U0001f46d",
- [":women_holding_hands_medium_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone2_tone5:"] = "\U0001f46d",
- [":women_holding_hands_medium_light_skin_tone_dark_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone3:"] = "\U0001f46d",
- [":women_holding_hands_medium_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone3_tone1:"] = "\U0001f46d",
- [":women_holding_hands_medium_skin_tone_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone3_tone2:"] = "\U0001f46d",
- [":women_holding_hands_medium_skin_tone_medium_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone3_tone4:"] = "\U0001f46d",
- [":women_holding_hands_medium_skin_tone_medium_dark_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone3_tone5:"] = "\U0001f46d",
- [":women_holding_hands_medium_skin_tone_dark_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone4:"] = "\U0001f46d",
- [":women_holding_hands_medium_dark_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone4_tone1:"] = "\U0001f46d",
- [":women_holding_hands_medium_dark_skin_tone_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone4_tone2:"] = "\U0001f46d",
- [":women_holding_hands_medium_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone4_tone3:"] = "\U0001f46d",
- [":women_holding_hands_medium_dark_skin_tone_medium_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone4_tone5:"] = "\U0001f46d",
- [":women_holding_hands_medium_dark_skin_tone_dark_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone5:"] = "\U0001f46d",
- [":women_holding_hands_dark_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone5_tone1:"] = "\U0001f46d",
- [":women_holding_hands_dark_skin_tone_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone5_tone2:"] = "\U0001f46d",
- [":women_holding_hands_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone5_tone3:"] = "\U0001f46d",
- [":women_holding_hands_dark_skin_tone_medium_skin_tone:"] = "\U0001f46d",
- [":women_holding_hands_tone5_tone4:"] = "\U0001f46d",
- [":women_holding_hands_dark_skin_tone_medium_dark_skin_tone:"] = "\U0001f46d",
- [":women_with_bunny_ears_partying:"] = "\U0001f46f\u200d\u2640\ufe0f",
- [":women_wrestling:"] = "\U0001f93c\u200d\u2640\ufe0f",
- [":womens:"] = "\U0001f6ba",
- [":wood:"] = "\U0001fab5",
- [":woozy_face:"] = "\U0001f974",
- [":worm:"] = "\U0001fab1",
- [":worried:"] = "\U0001f61f",
- [":wrench:"] = "\U0001f527",
- [":writing_hand:"] = "\u270d\ufe0f",
- [":writing_hand_tone1:"] = "\u270d\U0001f3fb",
- [":writing_hand::skin-tone-1:"] = "\u270d\U0001f3fb",
- [":writing_hand_tone2:"] = "\u270d\U0001f3fc",
- [":writing_hand::skin-tone-2:"] = "\u270d\U0001f3fc",
- [":writing_hand_tone3:"] = "\u270d\U0001f3fd",
- [":writing_hand::skin-tone-3:"] = "\u270d\U0001f3fd",
- [":writing_hand_tone4:"] = "\u270d\U0001f3fe",
- [":writing_hand::skin-tone-4:"] = "\u270d\U0001f3fe",
- [":writing_hand_tone5:"] = "\u270d\U0001f3ff",
- [":writing_hand::skin-tone-5:"] = "\u270d\U0001f3ff",
- [":x:"] = "\u274c",
- [":yarn:"] = "\U0001f9f6",
- [":yawning_face:"] = "\U0001f971",
- [":yellow_circle:"] = "\U0001f7e1",
- [":yellow_heart:"] = "\U0001f49b",
- [":yellow_square:"] = "\U0001f7e8",
- [":yen:"] = "\U0001f4b4",
- [":yin_yang:"] = "\u262f\ufe0f",
- [":yo_yo:"] = "\U0001fa80",
- [":yum:"] = "\U0001f60b",
- [":zany_face:"] = "\U0001f92a",
- [":zap:"] = "\u26a1",
- [":zebra:"] = "\U0001f993",
- [":zero:"] = "\u0030\ufe0f\u20e3",
- [":zipper_mouth:"] = "\U0001f910",
- [":zipper_mouth_face:"] = "\U0001f910",
- [":zombie:"] = "\U0001f9df",
- [":zzz:"] = "\U0001f4a4",
- };
+ #region Generated Emoji Map
+ s_unicodeEmojis = new Dictionary<string, string>
+ {
+ [":100:"] = "\U0001f4af",
+ [":1234:"] = "\U0001f522",
+ [":8ball:"] = "\U0001f3b1",
+ [":a:"] = "\U0001f170\ufe0f",
+ [":ab:"] = "\U0001f18e",
+ [":abacus:"] = "\U0001f9ee",
+ [":abc:"] = "\U0001f524",
+ [":abcd:"] = "\U0001f521",
+ [":accept:"] = "\U0001f251",
+ [":accordion:"] = "\U0001fa97",
+ [":adhesive_bandage:"] = "\U0001fa79",
+ [":adult:"] = "\U0001f9d1",
+ [":adult_tone1:"] = "\U0001f9d1\U0001f3fb",
+ [":adult_light_skin_tone:"] = "\U0001f9d1\U0001f3fb",
+ [":adult::skin-tone-1:"] = "\U0001f9d1\U0001f3fb",
+ [":adult_tone2:"] = "\U0001f9d1\U0001f3fc",
+ [":adult_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc",
+ [":adult::skin-tone-2:"] = "\U0001f9d1\U0001f3fc",
+ [":adult_tone3:"] = "\U0001f9d1\U0001f3fd",
+ [":adult_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd",
+ [":adult::skin-tone-3:"] = "\U0001f9d1\U0001f3fd",
+ [":adult_tone4:"] = "\U0001f9d1\U0001f3fe",
+ [":adult_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe",
+ [":adult::skin-tone-4:"] = "\U0001f9d1\U0001f3fe",
+ [":adult_tone5:"] = "\U0001f9d1\U0001f3ff",
+ [":adult_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff",
+ [":adult::skin-tone-5:"] = "\U0001f9d1\U0001f3ff",
+ [":aerial_tramway:"] = "\U0001f6a1",
+ [":airplane:"] = "\u2708\ufe0f",
+ [":airplane_arriving:"] = "\U0001f6ec",
+ [":airplane_departure:"] = "\U0001f6eb",
+ [":airplane_small:"] = "\U0001f6e9\ufe0f",
+ [":small_airplane:"] = "\U0001f6e9\ufe0f",
+ [":alarm_clock:"] = "\u23f0",
+ [":alembic:"] = "\u2697\ufe0f",
+ [":alien:"] = "\U0001f47d",
+ [":ambulance:"] = "\U0001f691",
+ [":amphora:"] = "\U0001f3fa",
+ [":anatomical_heart:"] = "\U0001fac0",
+ [":anchor:"] = "\u2693",
+ [":angel:"] = "\U0001f47c",
+ [":angel_tone1:"] = "\U0001f47c\U0001f3fb",
+ [":angel::skin-tone-1:"] = "\U0001f47c\U0001f3fb",
+ [":angel_tone2:"] = "\U0001f47c\U0001f3fc",
+ [":angel::skin-tone-2:"] = "\U0001f47c\U0001f3fc",
+ [":angel_tone3:"] = "\U0001f47c\U0001f3fd",
+ [":angel::skin-tone-3:"] = "\U0001f47c\U0001f3fd",
+ [":angel_tone4:"] = "\U0001f47c\U0001f3fe",
+ [":angel::skin-tone-4:"] = "\U0001f47c\U0001f3fe",
+ [":angel_tone5:"] = "\U0001f47c\U0001f3ff",
+ [":angel::skin-tone-5:"] = "\U0001f47c\U0001f3ff",
+ [":anger:"] = "\U0001f4a2",
+ [":anger_right:"] = "\U0001f5ef\ufe0f",
+ [":right_anger_bubble:"] = "\U0001f5ef\ufe0f",
+ [":angry:"] = "\U0001f620",
+ [">:("] = "\U0001f620",
+ [">:-("] = "\U0001f620",
+ [">=("] = "\U0001f620",
+ [">=-("] = "\U0001f620",
+ [":anguished:"] = "\U0001f627",
+ [":ant:"] = "\U0001f41c",
+ [":apple:"] = "\U0001f34e",
+ [":aquarius:"] = "\u2652",
+ [":aries:"] = "\u2648",
+ [":arrow_backward:"] = "\u25c0\ufe0f",
+ [":arrow_double_down:"] = "\u23ec",
+ [":arrow_double_up:"] = "\u23eb",
+ [":arrow_down:"] = "\u2b07\ufe0f",
+ [":arrow_down_small:"] = "\U0001f53d",
+ [":arrow_forward:"] = "\u25b6\ufe0f",
+ [":arrow_heading_down:"] = "\u2935\ufe0f",
+ [":arrow_heading_up:"] = "\u2934\ufe0f",
+ [":arrow_left:"] = "\u2b05\ufe0f",
+ [":arrow_lower_left:"] = "\u2199\ufe0f",
+ [":arrow_lower_right:"] = "\u2198\ufe0f",
+ [":arrow_right:"] = "\u27a1\ufe0f",
+ [":arrow_right_hook:"] = "\u21aa\ufe0f",
+ [":arrow_up:"] = "\u2b06\ufe0f",
+ [":arrow_up_down:"] = "\u2195\ufe0f",
+ [":arrow_up_small:"] = "\U0001f53c",
+ [":arrow_upper_left:"] = "\u2196\ufe0f",
+ [":arrow_upper_right:"] = "\u2197\ufe0f",
+ [":arrows_clockwise:"] = "\U0001f503",
+ [":arrows_counterclockwise:"] = "\U0001f504",
+ [":art:"] = "\U0001f3a8",
+ [":articulated_lorry:"] = "\U0001f69b",
+ [":artist:"] = "\U0001f9d1\u200d\U0001f3a8",
+ [":artist_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a8",
+ [":artist_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a8",
+ [":artist::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a8",
+ [":artist_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a8",
+ [":artist_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a8",
+ [":artist::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a8",
+ [":artist_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a8",
+ [":artist_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a8",
+ [":artist::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a8",
+ [":artist_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a8",
+ [":artist_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a8",
+ [":artist::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a8",
+ [":artist_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a8",
+ [":artist_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a8",
+ [":artist::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a8",
+ [":asterisk:"] = "\u002a\ufe0f\u20e3",
+ [":keycap_asterisk:"] = "\u002a\ufe0f\u20e3",
+ [":astonished:"] = "\U0001f632",
+ [":astronaut:"] = "\U0001f9d1\u200d\U0001f680",
+ [":astronaut_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f680",
+ [":astronaut_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f680",
+ [":astronaut::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f680",
+ [":astronaut_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f680",
+ [":astronaut_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f680",
+ [":astronaut::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f680",
+ [":astronaut_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f680",
+ [":astronaut_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f680",
+ [":astronaut::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f680",
+ [":astronaut_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f680",
+ [":astronaut_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f680",
+ [":astronaut::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f680",
+ [":astronaut_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f680",
+ [":astronaut_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f680",
+ [":astronaut::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f680",
+ [":athletic_shoe:"] = "\U0001f45f",
+ [":atm:"] = "\U0001f3e7",
+ [":atom:"] = "\u269b\ufe0f",
+ [":atom_symbol:"] = "\u269b\ufe0f",
+ [":auto_rickshaw:"] = "\U0001f6fa",
+ [":avocado:"] = "\U0001f951",
+ [":axe:"] = "\U0001fa93",
+ [":b:"] = "\U0001f171\ufe0f",
+ [":baby:"] = "\U0001f476",
+ [":baby_bottle:"] = "\U0001f37c",
+ [":baby_chick:"] = "\U0001f424",
+ [":baby_symbol:"] = "\U0001f6bc",
+ [":baby_tone1:"] = "\U0001f476\U0001f3fb",
+ [":baby::skin-tone-1:"] = "\U0001f476\U0001f3fb",
+ [":baby_tone2:"] = "\U0001f476\U0001f3fc",
+ [":baby::skin-tone-2:"] = "\U0001f476\U0001f3fc",
+ [":baby_tone3:"] = "\U0001f476\U0001f3fd",
+ [":baby::skin-tone-3:"] = "\U0001f476\U0001f3fd",
+ [":baby_tone4:"] = "\U0001f476\U0001f3fe",
+ [":baby::skin-tone-4:"] = "\U0001f476\U0001f3fe",
+ [":baby_tone5:"] = "\U0001f476\U0001f3ff",
+ [":baby::skin-tone-5:"] = "\U0001f476\U0001f3ff",
+ [":back:"] = "\U0001f519",
+ [":bacon:"] = "\U0001f953",
+ [":badger:"] = "\U0001f9a1",
+ [":badminton:"] = "\U0001f3f8",
+ [":bagel:"] = "\U0001f96f",
+ [":baggage_claim:"] = "\U0001f6c4",
+ [":ballet_shoes:"] = "\U0001fa70",
+ [":balloon:"] = "\U0001f388",
+ [":ballot_box:"] = "\U0001f5f3\ufe0f",
+ [":ballot_box_with_ballot:"] = "\U0001f5f3\ufe0f",
+ [":ballot_box_with_check:"] = "\u2611\ufe0f",
+ [":bamboo:"] = "\U0001f38d",
+ [":banana:"] = "\U0001f34c",
+ [":bangbang:"] = "\u203c\ufe0f",
+ [":banjo:"] = "\U0001fa95",
+ [":bank:"] = "\U0001f3e6",
+ [":bar_chart:"] = "\U0001f4ca",
+ [":barber:"] = "\U0001f488",
+ [":baseball:"] = "\u26be",
+ [":basket:"] = "\U0001f9fa",
+ [":basketball:"] = "\U0001f3c0",
+ [":bat:"] = "\U0001f987",
+ [":bath:"] = "\U0001f6c0",
+ [":bath_tone1:"] = "\U0001f6c0\U0001f3fb",
+ [":bath::skin-tone-1:"] = "\U0001f6c0\U0001f3fb",
+ [":bath_tone2:"] = "\U0001f6c0\U0001f3fc",
+ [":bath::skin-tone-2:"] = "\U0001f6c0\U0001f3fc",
+ [":bath_tone3:"] = "\U0001f6c0\U0001f3fd",
+ [":bath::skin-tone-3:"] = "\U0001f6c0\U0001f3fd",
+ [":bath_tone4:"] = "\U0001f6c0\U0001f3fe",
+ [":bath::skin-tone-4:"] = "\U0001f6c0\U0001f3fe",
+ [":bath_tone5:"] = "\U0001f6c0\U0001f3ff",
+ [":bath::skin-tone-5:"] = "\U0001f6c0\U0001f3ff",
+ [":bathtub:"] = "\U0001f6c1",
+ [":battery:"] = "\U0001f50b",
+ [":beach:"] = "\U0001f3d6\ufe0f",
+ [":beach_with_umbrella:"] = "\U0001f3d6\ufe0f",
+ [":beach_umbrella:"] = "\u26f1\ufe0f",
+ [":umbrella_on_ground:"] = "\u26f1\ufe0f",
+ [":bear:"] = "\U0001f43b",
+ [":bearded_person:"] = "\U0001f9d4",
+ [":bearded_person_tone1:"] = "\U0001f9d4\U0001f3fb",
+ [":bearded_person_light_skin_tone:"] = "\U0001f9d4\U0001f3fb",
+ [":bearded_person::skin-tone-1:"] = "\U0001f9d4\U0001f3fb",
+ [":bearded_person_tone2:"] = "\U0001f9d4\U0001f3fc",
+ [":bearded_person_medium_light_skin_tone:"] = "\U0001f9d4\U0001f3fc",
+ [":bearded_person::skin-tone-2:"] = "\U0001f9d4\U0001f3fc",
+ [":bearded_person_tone3:"] = "\U0001f9d4\U0001f3fd",
+ [":bearded_person_medium_skin_tone:"] = "\U0001f9d4\U0001f3fd",
+ [":bearded_person::skin-tone-3:"] = "\U0001f9d4\U0001f3fd",
+ [":bearded_person_tone4:"] = "\U0001f9d4\U0001f3fe",
+ [":bearded_person_medium_dark_skin_tone:"] = "\U0001f9d4\U0001f3fe",
+ [":bearded_person::skin-tone-4:"] = "\U0001f9d4\U0001f3fe",
+ [":bearded_person_tone5:"] = "\U0001f9d4\U0001f3ff",
+ [":bearded_person_dark_skin_tone:"] = "\U0001f9d4\U0001f3ff",
+ [":bearded_person::skin-tone-5:"] = "\U0001f9d4\U0001f3ff",
+ [":beaver:"] = "\U0001f9ab",
+ [":bed:"] = "\U0001f6cf\ufe0f",
+ [":bee:"] = "\U0001f41d",
+ [":beer:"] = "\U0001f37a",
+ [":beers:"] = "\U0001f37b",
+ [":beetle:"] = "\U0001fab2",
+ [":beginner:"] = "\U0001f530",
+ [":bell:"] = "\U0001f514",
+ [":bell_pepper:"] = "\U0001fad1",
+ [":bellhop:"] = "\U0001f6ce\ufe0f",
+ [":bellhop_bell:"] = "\U0001f6ce\ufe0f",
+ [":bento:"] = "\U0001f371",
+ [":beverage_box:"] = "\U0001f9c3",
+ [":bike:"] = "\U0001f6b2",
+ [":bikini:"] = "\U0001f459",
+ [":billed_cap:"] = "\U0001f9e2",
+ [":biohazard:"] = "\u2623\ufe0f",
+ [":biohazard_sign:"] = "\u2623\ufe0f",
+ [":bird:"] = "\U0001f426",
+ [":birthday:"] = "\U0001f382",
+ [":bison:"] = "\U0001f9ac",
+ [":black_cat:"] = "\U0001f408\u200d\u2b1b",
+ [":black_circle:"] = "\u26ab",
+ [":black_heart:"] = "\U0001f5a4",
+ [":black_joker:"] = "\U0001f0cf",
+ [":black_large_square:"] = "\u2b1b",
+ [":black_medium_small_square:"] = "\u25fe",
+ [":black_medium_square:"] = "\u25fc\ufe0f",
+ [":black_nib:"] = "\u2712\ufe0f",
+ [":black_small_square:"] = "\u25aa\ufe0f",
+ [":black_square_button:"] = "\U0001f532",
+ [":blond_haired_man:"] = "\U0001f471\u200d\u2642\ufe0f",
+ [":blond_haired_man_tone1:"] = "\U0001f471\U0001f3fb\u200d\u2642\ufe0f",
+ [":blond_haired_man_light_skin_tone:"] = "\U0001f471\U0001f3fb\u200d\u2642\ufe0f",
+ [":blond_haired_man::skin-tone-1:"] = "\U0001f471\U0001f3fb\u200d\u2642\ufe0f",
+ [":blond_haired_man_tone2:"] = "\U0001f471\U0001f3fc\u200d\u2642\ufe0f",
+ [":blond_haired_man_medium_light_skin_tone:"] = "\U0001f471\U0001f3fc\u200d\u2642\ufe0f",
+ [":blond_haired_man::skin-tone-2:"] = "\U0001f471\U0001f3fc\u200d\u2642\ufe0f",
+ [":blond_haired_man_tone3:"] = "\U0001f471\U0001f3fd\u200d\u2642\ufe0f",
+ [":blond_haired_man_medium_skin_tone:"] = "\U0001f471\U0001f3fd\u200d\u2642\ufe0f",
+ [":blond_haired_man::skin-tone-3:"] = "\U0001f471\U0001f3fd\u200d\u2642\ufe0f",
+ [":blond_haired_man_tone4:"] = "\U0001f471\U0001f3fe\u200d\u2642\ufe0f",
+ [":blond_haired_man_medium_dark_skin_tone:"] = "\U0001f471\U0001f3fe\u200d\u2642\ufe0f",
+ [":blond_haired_man::skin-tone-4:"] = "\U0001f471\U0001f3fe\u200d\u2642\ufe0f",
+ [":blond_haired_man_tone5:"] = "\U0001f471\U0001f3ff\u200d\u2642\ufe0f",
+ [":blond_haired_man_dark_skin_tone:"] = "\U0001f471\U0001f3ff\u200d\u2642\ufe0f",
+ [":blond_haired_man::skin-tone-5:"] = "\U0001f471\U0001f3ff\u200d\u2642\ufe0f",
+ [":blond_haired_person:"] = "\U0001f471",
+ [":person_with_blond_hair:"] = "\U0001f471",
+ [":blond_haired_person_tone1:"] = "\U0001f471\U0001f3fb",
+ [":person_with_blond_hair_tone1:"] = "\U0001f471\U0001f3fb",
+ [":blond_haired_person::skin-tone-1:"] = "\U0001f471\U0001f3fb",
+ [":person_with_blond_hair::skin-tone-1:"] = "\U0001f471\U0001f3fb",
+ [":blond_haired_person_tone2:"] = "\U0001f471\U0001f3fc",
+ [":person_with_blond_hair_tone2:"] = "\U0001f471\U0001f3fc",
+ [":blond_haired_person::skin-tone-2:"] = "\U0001f471\U0001f3fc",
+ [":person_with_blond_hair::skin-tone-2:"] = "\U0001f471\U0001f3fc",
+ [":blond_haired_person_tone3:"] = "\U0001f471\U0001f3fd",
+ [":person_with_blond_hair_tone3:"] = "\U0001f471\U0001f3fd",
+ [":blond_haired_person::skin-tone-3:"] = "\U0001f471\U0001f3fd",
+ [":person_with_blond_hair::skin-tone-3:"] = "\U0001f471\U0001f3fd",
+ [":blond_haired_person_tone4:"] = "\U0001f471\U0001f3fe",
+ [":person_with_blond_hair_tone4:"] = "\U0001f471\U0001f3fe",
+ [":blond_haired_person::skin-tone-4:"] = "\U0001f471\U0001f3fe",
+ [":person_with_blond_hair::skin-tone-4:"] = "\U0001f471\U0001f3fe",
+ [":blond_haired_person_tone5:"] = "\U0001f471\U0001f3ff",
+ [":person_with_blond_hair_tone5:"] = "\U0001f471\U0001f3ff",
+ [":blond_haired_person::skin-tone-5:"] = "\U0001f471\U0001f3ff",
+ [":person_with_blond_hair::skin-tone-5:"] = "\U0001f471\U0001f3ff",
+ [":blond_haired_woman:"] = "\U0001f471\u200d\u2640\ufe0f",
+ [":blond_haired_woman_tone1:"] = "\U0001f471\U0001f3fb\u200d\u2640\ufe0f",
+ [":blond_haired_woman_light_skin_tone:"] = "\U0001f471\U0001f3fb\u200d\u2640\ufe0f",
+ [":blond_haired_woman::skin-tone-1:"] = "\U0001f471\U0001f3fb\u200d\u2640\ufe0f",
+ [":blond_haired_woman_tone2:"] = "\U0001f471\U0001f3fc\u200d\u2640\ufe0f",
+ [":blond_haired_woman_medium_light_skin_tone:"] = "\U0001f471\U0001f3fc\u200d\u2640\ufe0f",
+ [":blond_haired_woman::skin-tone-2:"] = "\U0001f471\U0001f3fc\u200d\u2640\ufe0f",
+ [":blond_haired_woman_tone3:"] = "\U0001f471\U0001f3fd\u200d\u2640\ufe0f",
+ [":blond_haired_woman_medium_skin_tone:"] = "\U0001f471\U0001f3fd\u200d\u2640\ufe0f",
+ [":blond_haired_woman::skin-tone-3:"] = "\U0001f471\U0001f3fd\u200d\u2640\ufe0f",
+ [":blond_haired_woman_tone4:"] = "\U0001f471\U0001f3fe\u200d\u2640\ufe0f",
+ [":blond_haired_woman_medium_dark_skin_tone:"] = "\U0001f471\U0001f3fe\u200d\u2640\ufe0f",
+ [":blond_haired_woman::skin-tone-4:"] = "\U0001f471\U0001f3fe\u200d\u2640\ufe0f",
+ [":blond_haired_woman_tone5:"] = "\U0001f471\U0001f3ff\u200d\u2640\ufe0f",
+ [":blond_haired_woman_dark_skin_tone:"] = "\U0001f471\U0001f3ff\u200d\u2640\ufe0f",
+ [":blond_haired_woman::skin-tone-5:"] = "\U0001f471\U0001f3ff\u200d\u2640\ufe0f",
+ [":blossom:"] = "\U0001f33c",
+ [":blowfish:"] = "\U0001f421",
+ [":blue_book:"] = "\U0001f4d8",
+ [":blue_car:"] = "\U0001f699",
+ [":blue_circle:"] = "\U0001f535",
+ [":blue_heart:"] = "\U0001f499",
+ [":blue_square:"] = "\U0001f7e6",
+ [":blueberries:"] = "\U0001fad0",
+ [":blush:"] = "\U0001f60a",
+ [":\")"] = "\U0001f60a",
+ [":-\")"] = "\U0001f60a",
+ ["=\")"] = "\U0001f60a",
+ ["=-\")"] = "\U0001f60a",
+ [":boar:"] = "\U0001f417",
+ [":bomb:"] = "\U0001f4a3",
+ [":bone:"] = "\U0001f9b4",
+ [":book:"] = "\U0001f4d6",
+ [":bookmark:"] = "\U0001f516",
+ [":bookmark_tabs:"] = "\U0001f4d1",
+ [":books:"] = "\U0001f4da",
+ [":boom:"] = "\U0001f4a5",
+ [":boomerang:"] = "\U0001fa83",
+ [":boot:"] = "\U0001f462",
+ [":bouquet:"] = "\U0001f490",
+ [":bow_and_arrow:"] = "\U0001f3f9",
+ [":archery:"] = "\U0001f3f9",
+ [":bowl_with_spoon:"] = "\U0001f963",
+ [":bowling:"] = "\U0001f3b3",
+ [":boxing_glove:"] = "\U0001f94a",
+ [":boxing_gloves:"] = "\U0001f94a",
+ [":boy:"] = "\U0001f466",
+ [":boy_tone1:"] = "\U0001f466\U0001f3fb",
+ [":boy::skin-tone-1:"] = "\U0001f466\U0001f3fb",
+ [":boy_tone2:"] = "\U0001f466\U0001f3fc",
+ [":boy::skin-tone-2:"] = "\U0001f466\U0001f3fc",
+ [":boy_tone3:"] = "\U0001f466\U0001f3fd",
+ [":boy::skin-tone-3:"] = "\U0001f466\U0001f3fd",
+ [":boy_tone4:"] = "\U0001f466\U0001f3fe",
+ [":boy::skin-tone-4:"] = "\U0001f466\U0001f3fe",
+ [":boy_tone5:"] = "\U0001f466\U0001f3ff",
+ [":boy::skin-tone-5:"] = "\U0001f466\U0001f3ff",
+ [":brain:"] = "\U0001f9e0",
+ [":bread:"] = "\U0001f35e",
+ [":breast_feeding:"] = "\U0001f931",
+ [":breast_feeding_tone1:"] = "\U0001f931\U0001f3fb",
+ [":breast_feeding_light_skin_tone:"] = "\U0001f931\U0001f3fb",
+ [":breast_feeding::skin-tone-1:"] = "\U0001f931\U0001f3fb",
+ [":breast_feeding_tone2:"] = "\U0001f931\U0001f3fc",
+ [":breast_feeding_medium_light_skin_tone:"] = "\U0001f931\U0001f3fc",
+ [":breast_feeding::skin-tone-2:"] = "\U0001f931\U0001f3fc",
+ [":breast_feeding_tone3:"] = "\U0001f931\U0001f3fd",
+ [":breast_feeding_medium_skin_tone:"] = "\U0001f931\U0001f3fd",
+ [":breast_feeding::skin-tone-3:"] = "\U0001f931\U0001f3fd",
+ [":breast_feeding_tone4:"] = "\U0001f931\U0001f3fe",
+ [":breast_feeding_medium_dark_skin_tone:"] = "\U0001f931\U0001f3fe",
+ [":breast_feeding::skin-tone-4:"] = "\U0001f931\U0001f3fe",
+ [":breast_feeding_tone5:"] = "\U0001f931\U0001f3ff",
+ [":breast_feeding_dark_skin_tone:"] = "\U0001f931\U0001f3ff",
+ [":breast_feeding::skin-tone-5:"] = "\U0001f931\U0001f3ff",
+ [":bricks:"] = "\U0001f9f1",
+ [":bridge_at_night:"] = "\U0001f309",
+ [":briefcase:"] = "\U0001f4bc",
+ [":briefs:"] = "\U0001fa72",
+ [":broccoli:"] = "\U0001f966",
+ [":broken_heart:"] = "\U0001f494",
+ ["</3"] = "\U0001f494",
+ ["<\\3"] = "\U0001f494",
+ [":broom:"] = "\U0001f9f9",
+ [":brown_circle:"] = "\U0001f7e4",
+ [":brown_heart:"] = "\U0001f90e",
+ [":brown_square:"] = "\U0001f7eb",
+ [":bubble_tea:"] = "\U0001f9cb",
+ [":bucket:"] = "\U0001faa3",
+ [":bug:"] = "\U0001f41b",
+ [":bulb:"] = "\U0001f4a1",
+ [":bullettrain_front:"] = "\U0001f685",
+ [":bullettrain_side:"] = "\U0001f684",
+ [":burrito:"] = "\U0001f32f",
+ [":bus:"] = "\U0001f68c",
+ [":busstop:"] = "\U0001f68f",
+ [":bust_in_silhouette:"] = "\U0001f464",
+ [":busts_in_silhouette:"] = "\U0001f465",
+ [":butter:"] = "\U0001f9c8",
+ [":butterfly:"] = "\U0001f98b",
+ [":cactus:"] = "\U0001f335",
+ [":cake:"] = "\U0001f370",
+ [":calendar:"] = "\U0001f4c6",
+ [":calendar_spiral:"] = "\U0001f5d3\ufe0f",
+ [":spiral_calendar_pad:"] = "\U0001f5d3\ufe0f",
+ [":call_me:"] = "\U0001f919",
+ [":call_me_hand:"] = "\U0001f919",
+ [":call_me_tone1:"] = "\U0001f919\U0001f3fb",
+ [":call_me_hand_tone1:"] = "\U0001f919\U0001f3fb",
+ [":call_me::skin-tone-1:"] = "\U0001f919\U0001f3fb",
+ [":call_me_hand::skin-tone-1:"] = "\U0001f919\U0001f3fb",
+ [":call_me_tone2:"] = "\U0001f919\U0001f3fc",
+ [":call_me_hand_tone2:"] = "\U0001f919\U0001f3fc",
+ [":call_me::skin-tone-2:"] = "\U0001f919\U0001f3fc",
+ [":call_me_hand::skin-tone-2:"] = "\U0001f919\U0001f3fc",
+ [":call_me_tone3:"] = "\U0001f919\U0001f3fd",
+ [":call_me_hand_tone3:"] = "\U0001f919\U0001f3fd",
+ [":call_me::skin-tone-3:"] = "\U0001f919\U0001f3fd",
+ [":call_me_hand::skin-tone-3:"] = "\U0001f919\U0001f3fd",
+ [":call_me_tone4:"] = "\U0001f919\U0001f3fe",
+ [":call_me_hand_tone4:"] = "\U0001f919\U0001f3fe",
+ [":call_me::skin-tone-4:"] = "\U0001f919\U0001f3fe",
+ [":call_me_hand::skin-tone-4:"] = "\U0001f919\U0001f3fe",
+ [":call_me_tone5:"] = "\U0001f919\U0001f3ff",
+ [":call_me_hand_tone5:"] = "\U0001f919\U0001f3ff",
+ [":call_me::skin-tone-5:"] = "\U0001f919\U0001f3ff",
+ [":call_me_hand::skin-tone-5:"] = "\U0001f919\U0001f3ff",
+ [":calling:"] = "\U0001f4f2",
+ [":camel:"] = "\U0001f42b",
+ [":camera:"] = "\U0001f4f7",
+ [":camera_with_flash:"] = "\U0001f4f8",
+ [":camping:"] = "\U0001f3d5\ufe0f",
+ [":cancer:"] = "\u264b",
+ [":candle:"] = "\U0001f56f\ufe0f",
+ [":candy:"] = "\U0001f36c",
+ [":canned_food:"] = "\U0001f96b",
+ [":canoe:"] = "\U0001f6f6",
+ [":kayak:"] = "\U0001f6f6",
+ [":capital_abcd:"] = "\U0001f520",
+ [":capricorn:"] = "\u2651",
+ [":card_box:"] = "\U0001f5c3\ufe0f",
+ [":card_file_box:"] = "\U0001f5c3\ufe0f",
+ [":card_index:"] = "\U0001f4c7",
+ [":carousel_horse:"] = "\U0001f3a0",
+ [":carpentry_saw:"] = "\U0001fa9a",
+ [":carrot:"] = "\U0001f955",
+ [":cat:"] = "\U0001f431",
+ [":cat2:"] = "\U0001f408",
+ [":cd:"] = "\U0001f4bf",
+ [":chains:"] = "\u26d3\ufe0f",
+ [":chair:"] = "\U0001fa91",
+ [":champagne:"] = "\U0001f37e",
+ [":bottle_with_popping_cork:"] = "\U0001f37e",
+ [":champagne_glass:"] = "\U0001f942",
+ [":clinking_glass:"] = "\U0001f942",
+ [":chart:"] = "\U0001f4b9",
+ [":chart_with_downwards_trend:"] = "\U0001f4c9",
+ [":chart_with_upwards_trend:"] = "\U0001f4c8",
+ [":checkered_flag:"] = "\U0001f3c1",
+ [":cheese:"] = "\U0001f9c0",
+ [":cheese_wedge:"] = "\U0001f9c0",
+ [":cherries:"] = "\U0001f352",
+ [":cherry_blossom:"] = "\U0001f338",
+ [":chess_pawn:"] = "\u265f\ufe0f",
+ [":chestnut:"] = "\U0001f330",
+ [":chicken:"] = "\U0001f414",
+ [":child:"] = "\U0001f9d2",
+ [":child_tone1:"] = "\U0001f9d2\U0001f3fb",
+ [":child_light_skin_tone:"] = "\U0001f9d2\U0001f3fb",
+ [":child::skin-tone-1:"] = "\U0001f9d2\U0001f3fb",
+ [":child_tone2:"] = "\U0001f9d2\U0001f3fc",
+ [":child_medium_light_skin_tone:"] = "\U0001f9d2\U0001f3fc",
+ [":child::skin-tone-2:"] = "\U0001f9d2\U0001f3fc",
+ [":child_tone3:"] = "\U0001f9d2\U0001f3fd",
+ [":child_medium_skin_tone:"] = "\U0001f9d2\U0001f3fd",
+ [":child::skin-tone-3:"] = "\U0001f9d2\U0001f3fd",
+ [":child_tone4:"] = "\U0001f9d2\U0001f3fe",
+ [":child_medium_dark_skin_tone:"] = "\U0001f9d2\U0001f3fe",
+ [":child::skin-tone-4:"] = "\U0001f9d2\U0001f3fe",
+ [":child_tone5:"] = "\U0001f9d2\U0001f3ff",
+ [":child_dark_skin_tone:"] = "\U0001f9d2\U0001f3ff",
+ [":child::skin-tone-5:"] = "\U0001f9d2\U0001f3ff",
+ [":children_crossing:"] = "\U0001f6b8",
+ [":chipmunk:"] = "\U0001f43f\ufe0f",
+ [":chocolate_bar:"] = "\U0001f36b",
+ [":chopsticks:"] = "\U0001f962",
+ [":christmas_tree:"] = "\U0001f384",
+ [":church:"] = "\u26ea",
+ [":cinema:"] = "\U0001f3a6",
+ [":circus_tent:"] = "\U0001f3aa",
+ [":city_dusk:"] = "\U0001f306",
+ [":city_sunset:"] = "\U0001f307",
+ [":city_sunrise:"] = "\U0001f307",
+ [":cityscape:"] = "\U0001f3d9\ufe0f",
+ [":cl:"] = "\U0001f191",
+ [":clap:"] = "\U0001f44f",
+ [":clap_tone1:"] = "\U0001f44f\U0001f3fb",
+ [":clap::skin-tone-1:"] = "\U0001f44f\U0001f3fb",
+ [":clap_tone2:"] = "\U0001f44f\U0001f3fc",
+ [":clap::skin-tone-2:"] = "\U0001f44f\U0001f3fc",
+ [":clap_tone3:"] = "\U0001f44f\U0001f3fd",
+ [":clap::skin-tone-3:"] = "\U0001f44f\U0001f3fd",
+ [":clap_tone4:"] = "\U0001f44f\U0001f3fe",
+ [":clap::skin-tone-4:"] = "\U0001f44f\U0001f3fe",
+ [":clap_tone5:"] = "\U0001f44f\U0001f3ff",
+ [":clap::skin-tone-5:"] = "\U0001f44f\U0001f3ff",
+ [":clapper:"] = "\U0001f3ac",
+ [":classical_building:"] = "\U0001f3db\ufe0f",
+ [":clipboard:"] = "\U0001f4cb",
+ [":clock:"] = "\U0001f570\ufe0f",
+ [":mantlepiece_clock:"] = "\U0001f570\ufe0f",
+ [":clock1:"] = "\U0001f550",
+ [":clock10:"] = "\U0001f559",
+ [":clock1030:"] = "\U0001f565",
+ [":clock11:"] = "\U0001f55a",
+ [":clock1130:"] = "\U0001f566",
+ [":clock12:"] = "\U0001f55b",
+ [":clock1230:"] = "\U0001f567",
+ [":clock130:"] = "\U0001f55c",
+ [":clock2:"] = "\U0001f551",
+ [":clock230:"] = "\U0001f55d",
+ [":clock3:"] = "\U0001f552",
+ [":clock330:"] = "\U0001f55e",
+ [":clock4:"] = "\U0001f553",
+ [":clock430:"] = "\U0001f55f",
+ [":clock5:"] = "\U0001f554",
+ [":clock530:"] = "\U0001f560",
+ [":clock6:"] = "\U0001f555",
+ [":clock630:"] = "\U0001f561",
+ [":clock7:"] = "\U0001f556",
+ [":clock730:"] = "\U0001f562",
+ [":clock8:"] = "\U0001f557",
+ [":clock830:"] = "\U0001f563",
+ [":clock9:"] = "\U0001f558",
+ [":clock930:"] = "\U0001f564",
+ [":closed_book:"] = "\U0001f4d5",
+ [":closed_lock_with_key:"] = "\U0001f510",
+ [":closed_umbrella:"] = "\U0001f302",
+ [":cloud:"] = "\u2601\ufe0f",
+ [":cloud_lightning:"] = "\U0001f329\ufe0f",
+ [":cloud_with_lightning:"] = "\U0001f329\ufe0f",
+ [":cloud_rain:"] = "\U0001f327\ufe0f",
+ [":cloud_with_rain:"] = "\U0001f327\ufe0f",
+ [":cloud_snow:"] = "\U0001f328\ufe0f",
+ [":cloud_with_snow:"] = "\U0001f328\ufe0f",
+ [":cloud_tornado:"] = "\U0001f32a\ufe0f",
+ [":cloud_with_tornado:"] = "\U0001f32a\ufe0f",
+ [":clown:"] = "\U0001f921",
+ [":clown_face:"] = "\U0001f921",
+ [":clubs:"] = "\u2663\ufe0f",
+ [":coat:"] = "\U0001f9e5",
+ [":cockroach:"] = "\U0001fab3",
+ [":cocktail:"] = "\U0001f378",
+ [":coconut:"] = "\U0001f965",
+ [":coffee:"] = "\u2615",
+ [":coffin:"] = "\u26b0\ufe0f",
+ [":coin:"] = "\U0001fa99",
+ [":cold_face:"] = "\U0001f976",
+ [":cold_sweat:"] = "\U0001f630",
+ [":comet:"] = "\u2604\ufe0f",
+ [":compass:"] = "\U0001f9ed",
+ [":compression:"] = "\U0001f5dc\ufe0f",
+ [":computer:"] = "\U0001f4bb",
+ [":confetti_ball:"] = "\U0001f38a",
+ [":confounded:"] = "\U0001f616",
+ [":confused:"] = "\U0001f615",
+ [":-\\"] = "\U0001f615",
+ [":-/"] = "\U0001f615",
+ ["=-\\"] = "\U0001f615",
+ ["=-/"] = "\U0001f615",
+ [":congratulations:"] = "\u3297\ufe0f",
+ [":construction:"] = "\U0001f6a7",
+ [":construction_site:"] = "\U0001f3d7\ufe0f",
+ [":building_construction:"] = "\U0001f3d7\ufe0f",
+ [":construction_worker:"] = "\U0001f477",
+ [":construction_worker_tone1:"] = "\U0001f477\U0001f3fb",
+ [":construction_worker::skin-tone-1:"] = "\U0001f477\U0001f3fb",
+ [":construction_worker_tone2:"] = "\U0001f477\U0001f3fc",
+ [":construction_worker::skin-tone-2:"] = "\U0001f477\U0001f3fc",
+ [":construction_worker_tone3:"] = "\U0001f477\U0001f3fd",
+ [":construction_worker::skin-tone-3:"] = "\U0001f477\U0001f3fd",
+ [":construction_worker_tone4:"] = "\U0001f477\U0001f3fe",
+ [":construction_worker::skin-tone-4:"] = "\U0001f477\U0001f3fe",
+ [":construction_worker_tone5:"] = "\U0001f477\U0001f3ff",
+ [":construction_worker::skin-tone-5:"] = "\U0001f477\U0001f3ff",
+ [":control_knobs:"] = "\U0001f39b\ufe0f",
+ [":convenience_store:"] = "\U0001f3ea",
+ [":cook:"] = "\U0001f9d1\u200d\U0001f373",
+ [":cook_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f373",
+ [":cook_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f373",
+ [":cook::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f373",
+ [":cook_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f373",
+ [":cook_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f373",
+ [":cook::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f373",
+ [":cook_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f373",
+ [":cook_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f373",
+ [":cook::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f373",
+ [":cook_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f373",
+ [":cook_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f373",
+ [":cook::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f373",
+ [":cook_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f373",
+ [":cook_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f373",
+ [":cook::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f373",
+ [":cookie:"] = "\U0001f36a",
+ [":cooking:"] = "\U0001f373",
+ [":cool:"] = "\U0001f192",
+ [":copyright:"] = "\u00a9\ufe0f",
+ [":corn:"] = "\U0001f33d",
+ [":couch:"] = "\U0001f6cb\ufe0f",
+ [":couch_and_lamp:"] = "\U0001f6cb\ufe0f",
+ [":couple:"] = "\U0001f46b",
+ [":couple_mm:"] = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468",
+ [":couple_with_heart_mm:"] = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468",
+ [":couple_with_heart:"] = "\U0001f491",
+ [":couple_with_heart_woman_man:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468",
+ [":couple_ww:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469",
+ [":couple_with_heart_ww:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469",
+ [":couplekiss:"] = "\U0001f48f",
+ [":cow:"] = "\U0001f42e",
+ [":cow2:"] = "\U0001f404",
+ [":cowboy:"] = "\U0001f920",
+ [":face_with_cowboy_hat:"] = "\U0001f920",
+ [":crab:"] = "\U0001f980",
+ [":crayon:"] = "\U0001f58d\ufe0f",
+ [":lower_left_crayon:"] = "\U0001f58d\ufe0f",
+ [":credit_card:"] = "\U0001f4b3",
+ [":crescent_moon:"] = "\U0001f319",
+ [":cricket:"] = "\U0001f997",
+ [":cricket_game:"] = "\U0001f3cf",
+ [":cricket_bat_ball:"] = "\U0001f3cf",
+ [":crocodile:"] = "\U0001f40a",
+ [":croissant:"] = "\U0001f950",
+ [":cross:"] = "\u271d\ufe0f",
+ [":latin_cross:"] = "\u271d\ufe0f",
+ [":crossed_flags:"] = "\U0001f38c",
+ [":crossed_swords:"] = "\u2694\ufe0f",
+ [":crown:"] = "\U0001f451",
+ [":cruise_ship:"] = "\U0001f6f3\ufe0f",
+ [":passenger_ship:"] = "\U0001f6f3\ufe0f",
+ [":cry:"] = "\U0001f622",
+ [":'("] = "\U0001f622",
+ [":'-("] = "\U0001f622",
+ [":,("] = "\U0001f622",
+ [":,-("] = "\U0001f622",
+ ["='("] = "\U0001f622",
+ ["='-("] = "\U0001f622",
+ ["=,("] = "\U0001f622",
+ ["=,-("] = "\U0001f622",
+ [":crying_cat_face:"] = "\U0001f63f",
+ [":crystal_ball:"] = "\U0001f52e",
+ [":cucumber:"] = "\U0001f952",
+ [":cup_with_straw:"] = "\U0001f964",
+ [":cupcake:"] = "\U0001f9c1",
+ [":cupid:"] = "\U0001f498",
+ [":curling_stone:"] = "\U0001f94c",
+ [":curly_loop:"] = "\u27b0",
+ [":currency_exchange:"] = "\U0001f4b1",
+ [":curry:"] = "\U0001f35b",
+ [":custard:"] = "\U0001f36e",
+ [":pudding:"] = "\U0001f36e",
+ [":flan:"] = "\U0001f36e",
+ [":customs:"] = "\U0001f6c3",
+ [":cut_of_meat:"] = "\U0001f969",
+ [":cyclone:"] = "\U0001f300",
+ [":dagger:"] = "\U0001f5e1\ufe0f",
+ [":dagger_knife:"] = "\U0001f5e1\ufe0f",
+ [":dancer:"] = "\U0001f483",
+ [":dancer_tone1:"] = "\U0001f483\U0001f3fb",
+ [":dancer::skin-tone-1:"] = "\U0001f483\U0001f3fb",
+ [":dancer_tone2:"] = "\U0001f483\U0001f3fc",
+ [":dancer::skin-tone-2:"] = "\U0001f483\U0001f3fc",
+ [":dancer_tone3:"] = "\U0001f483\U0001f3fd",
+ [":dancer::skin-tone-3:"] = "\U0001f483\U0001f3fd",
+ [":dancer_tone4:"] = "\U0001f483\U0001f3fe",
+ [":dancer::skin-tone-4:"] = "\U0001f483\U0001f3fe",
+ [":dancer_tone5:"] = "\U0001f483\U0001f3ff",
+ [":dancer::skin-tone-5:"] = "\U0001f483\U0001f3ff",
+ [":dango:"] = "\U0001f361",
+ [":dark_sunglasses:"] = "\U0001f576\ufe0f",
+ [":dart:"] = "\U0001f3af",
+ [":dash:"] = "\U0001f4a8",
+ [":date:"] = "\U0001f4c5",
+ [":deaf_man:"] = "\U0001f9cf\u200d\u2642\ufe0f",
+ [":deaf_man_tone1:"] = "\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f",
+ [":deaf_man_light_skin_tone:"] = "\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f",
+ [":deaf_man::skin-tone-1:"] = "\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f",
+ [":deaf_man_tone2:"] = "\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f",
+ [":deaf_man_medium_light_skin_tone:"] = "\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f",
+ [":deaf_man::skin-tone-2:"] = "\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f",
+ [":deaf_man_tone3:"] = "\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f",
+ [":deaf_man_medium_skin_tone:"] = "\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f",
+ [":deaf_man::skin-tone-3:"] = "\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f",
+ [":deaf_man_tone4:"] = "\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f",
+ [":deaf_man_medium_dark_skin_tone:"] = "\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f",
+ [":deaf_man::skin-tone-4:"] = "\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f",
+ [":deaf_man_tone5:"] = "\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f",
+ [":deaf_man_dark_skin_tone:"] = "\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f",
+ [":deaf_man::skin-tone-5:"] = "\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f",
+ [":deaf_person:"] = "\U0001f9cf",
+ [":deaf_person_tone1:"] = "\U0001f9cf\U0001f3fb",
+ [":deaf_person_light_skin_tone:"] = "\U0001f9cf\U0001f3fb",
+ [":deaf_person::skin-tone-1:"] = "\U0001f9cf\U0001f3fb",
+ [":deaf_person_tone2:"] = "\U0001f9cf\U0001f3fc",
+ [":deaf_person_medium_light_skin_tone:"] = "\U0001f9cf\U0001f3fc",
+ [":deaf_person::skin-tone-2:"] = "\U0001f9cf\U0001f3fc",
+ [":deaf_person_tone3:"] = "\U0001f9cf\U0001f3fd",
+ [":deaf_person_medium_skin_tone:"] = "\U0001f9cf\U0001f3fd",
+ [":deaf_person::skin-tone-3:"] = "\U0001f9cf\U0001f3fd",
+ [":deaf_person_tone4:"] = "\U0001f9cf\U0001f3fe",
+ [":deaf_person_medium_dark_skin_tone:"] = "\U0001f9cf\U0001f3fe",
+ [":deaf_person::skin-tone-4:"] = "\U0001f9cf\U0001f3fe",
+ [":deaf_person_tone5:"] = "\U0001f9cf\U0001f3ff",
+ [":deaf_person_dark_skin_tone:"] = "\U0001f9cf\U0001f3ff",
+ [":deaf_person::skin-tone-5:"] = "\U0001f9cf\U0001f3ff",
+ [":deaf_woman:"] = "\U0001f9cf\u200d\u2640\ufe0f",
+ [":deaf_woman_tone1:"] = "\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f",
+ [":deaf_woman_light_skin_tone:"] = "\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f",
+ [":deaf_woman::skin-tone-1:"] = "\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f",
+ [":deaf_woman_tone2:"] = "\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f",
+ [":deaf_woman_medium_light_skin_tone:"] = "\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f",
+ [":deaf_woman::skin-tone-2:"] = "\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f",
+ [":deaf_woman_tone3:"] = "\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f",
+ [":deaf_woman_medium_skin_tone:"] = "\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f",
+ [":deaf_woman::skin-tone-3:"] = "\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f",
+ [":deaf_woman_tone4:"] = "\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f",
+ [":deaf_woman_medium_dark_skin_tone:"] = "\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f",
+ [":deaf_woman::skin-tone-4:"] = "\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f",
+ [":deaf_woman_tone5:"] = "\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f",
+ [":deaf_woman_dark_skin_tone:"] = "\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f",
+ [":deaf_woman::skin-tone-5:"] = "\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f",
+ [":deciduous_tree:"] = "\U0001f333",
+ [":deer:"] = "\U0001f98c",
+ [":department_store:"] = "\U0001f3ec",
+ [":desert:"] = "\U0001f3dc\ufe0f",
+ [":desktop:"] = "\U0001f5a5\ufe0f",
+ [":desktop_computer:"] = "\U0001f5a5\ufe0f",
+ [":detective:"] = "\U0001f575\ufe0f",
+ [":spy:"] = "\U0001f575\ufe0f",
+ [":sleuth_or_spy:"] = "\U0001f575\ufe0f",
+ [":detective_tone1:"] = "\U0001f575\U0001f3fb",
+ [":spy_tone1:"] = "\U0001f575\U0001f3fb",
+ [":sleuth_or_spy_tone1:"] = "\U0001f575\U0001f3fb",
+ [":detective::skin-tone-1:"] = "\U0001f575\U0001f3fb",
+ [":spy::skin-tone-1:"] = "\U0001f575\U0001f3fb",
+ [":sleuth_or_spy::skin-tone-1:"] = "\U0001f575\U0001f3fb",
+ [":detective_tone2:"] = "\U0001f575\U0001f3fc",
+ [":spy_tone2:"] = "\U0001f575\U0001f3fc",
+ [":sleuth_or_spy_tone2:"] = "\U0001f575\U0001f3fc",
+ [":detective::skin-tone-2:"] = "\U0001f575\U0001f3fc",
+ [":spy::skin-tone-2:"] = "\U0001f575\U0001f3fc",
+ [":sleuth_or_spy::skin-tone-2:"] = "\U0001f575\U0001f3fc",
+ [":detective_tone3:"] = "\U0001f575\U0001f3fd",
+ [":spy_tone3:"] = "\U0001f575\U0001f3fd",
+ [":sleuth_or_spy_tone3:"] = "\U0001f575\U0001f3fd",
+ [":detective::skin-tone-3:"] = "\U0001f575\U0001f3fd",
+ [":spy::skin-tone-3:"] = "\U0001f575\U0001f3fd",
+ [":sleuth_or_spy::skin-tone-3:"] = "\U0001f575\U0001f3fd",
+ [":detective_tone4:"] = "\U0001f575\U0001f3fe",
+ [":spy_tone4:"] = "\U0001f575\U0001f3fe",
+ [":sleuth_or_spy_tone4:"] = "\U0001f575\U0001f3fe",
+ [":detective::skin-tone-4:"] = "\U0001f575\U0001f3fe",
+ [":spy::skin-tone-4:"] = "\U0001f575\U0001f3fe",
+ [":sleuth_or_spy::skin-tone-4:"] = "\U0001f575\U0001f3fe",
+ [":detective_tone5:"] = "\U0001f575\U0001f3ff",
+ [":spy_tone5:"] = "\U0001f575\U0001f3ff",
+ [":sleuth_or_spy_tone5:"] = "\U0001f575\U0001f3ff",
+ [":detective::skin-tone-5:"] = "\U0001f575\U0001f3ff",
+ [":spy::skin-tone-5:"] = "\U0001f575\U0001f3ff",
+ [":sleuth_or_spy::skin-tone-5:"] = "\U0001f575\U0001f3ff",
+ [":diamond_shape_with_a_dot_inside:"] = "\U0001f4a0",
+ [":diamonds:"] = "\u2666\ufe0f",
+ [":disappointed:"] = "\U0001f61e",
+ [":disappointed_relieved:"] = "\U0001f625",
+ [":disguised_face:"] = "\U0001f978",
+ [":dividers:"] = "\U0001f5c2\ufe0f",
+ [":card_index_dividers:"] = "\U0001f5c2\ufe0f",
+ [":diving_mask:"] = "\U0001f93f",
+ [":diya_lamp:"] = "\U0001fa94",
+ [":dizzy:"] = "\U0001f4ab",
+ [":dizzy_face:"] = "\U0001f635",
+ [":dna:"] = "\U0001f9ec",
+ [":do_not_litter:"] = "\U0001f6af",
+ [":dodo:"] = "\U0001f9a4",
+ [":dog:"] = "\U0001f436",
+ [":dog2:"] = "\U0001f415",
+ [":dollar:"] = "\U0001f4b5",
+ [":dolls:"] = "\U0001f38e",
+ [":dolphin:"] = "\U0001f42c",
+ [":door:"] = "\U0001f6aa",
+ [":doughnut:"] = "\U0001f369",
+ [":dove:"] = "\U0001f54a\ufe0f",
+ [":dove_of_peace:"] = "\U0001f54a\ufe0f",
+ [":dragon:"] = "\U0001f409",
+ [":dragon_face:"] = "\U0001f432",
+ [":dress:"] = "\U0001f457",
+ [":dromedary_camel:"] = "\U0001f42a",
+ [":drooling_face:"] = "\U0001f924",
+ [":drool:"] = "\U0001f924",
+ [":drop_of_blood:"] = "\U0001fa78",
+ [":droplet:"] = "\U0001f4a7",
+ [":drum:"] = "\U0001f941",
+ [":drum_with_drumsticks:"] = "\U0001f941",
+ [":duck:"] = "\U0001f986",
+ [":dumpling:"] = "\U0001f95f",
+ [":dvd:"] = "\U0001f4c0",
+ [":e_mail:"] = "\U0001f4e7",
+ [":email:"] = "\U0001f4e7",
+ [":eagle:"] = "\U0001f985",
+ [":ear:"] = "\U0001f442",
+ [":ear_of_rice:"] = "\U0001f33e",
+ [":ear_tone1:"] = "\U0001f442\U0001f3fb",
+ [":ear::skin-tone-1:"] = "\U0001f442\U0001f3fb",
+ [":ear_tone2:"] = "\U0001f442\U0001f3fc",
+ [":ear::skin-tone-2:"] = "\U0001f442\U0001f3fc",
+ [":ear_tone3:"] = "\U0001f442\U0001f3fd",
+ [":ear::skin-tone-3:"] = "\U0001f442\U0001f3fd",
+ [":ear_tone4:"] = "\U0001f442\U0001f3fe",
+ [":ear::skin-tone-4:"] = "\U0001f442\U0001f3fe",
+ [":ear_tone5:"] = "\U0001f442\U0001f3ff",
+ [":ear::skin-tone-5:"] = "\U0001f442\U0001f3ff",
+ [":ear_with_hearing_aid:"] = "\U0001f9bb",
+ [":ear_with_hearing_aid_tone1:"] = "\U0001f9bb\U0001f3fb",
+ [":ear_with_hearing_aid_light_skin_tone:"] = "\U0001f9bb\U0001f3fb",
+ [":ear_with_hearing_aid::skin-tone-1:"] = "\U0001f9bb\U0001f3fb",
+ [":ear_with_hearing_aid_tone2:"] = "\U0001f9bb\U0001f3fc",
+ [":ear_with_hearing_aid_medium_light_skin_tone:"] = "\U0001f9bb\U0001f3fc",
+ [":ear_with_hearing_aid::skin-tone-2:"] = "\U0001f9bb\U0001f3fc",
+ [":ear_with_hearing_aid_tone3:"] = "\U0001f9bb\U0001f3fd",
+ [":ear_with_hearing_aid_medium_skin_tone:"] = "\U0001f9bb\U0001f3fd",
+ [":ear_with_hearing_aid::skin-tone-3:"] = "\U0001f9bb\U0001f3fd",
+ [":ear_with_hearing_aid_tone4:"] = "\U0001f9bb\U0001f3fe",
+ [":ear_with_hearing_aid_medium_dark_skin_tone:"] = "\U0001f9bb\U0001f3fe",
+ [":ear_with_hearing_aid::skin-tone-4:"] = "\U0001f9bb\U0001f3fe",
+ [":ear_with_hearing_aid_tone5:"] = "\U0001f9bb\U0001f3ff",
+ [":ear_with_hearing_aid_dark_skin_tone:"] = "\U0001f9bb\U0001f3ff",
+ [":ear_with_hearing_aid::skin-tone-5:"] = "\U0001f9bb\U0001f3ff",
+ [":earth_africa:"] = "\U0001f30d",
+ [":earth_americas:"] = "\U0001f30e",
+ [":earth_asia:"] = "\U0001f30f",
+ [":egg:"] = "\U0001f95a",
+ [":eggplant:"] = "\U0001f346",
+ [":eight:"] = "\u0038\ufe0f\u20e3",
+ [":eight_pointed_black_star:"] = "\u2734\ufe0f",
+ [":eight_spoked_asterisk:"] = "\u2733\ufe0f",
+ [":eject:"] = "\u23cf\ufe0f",
+ [":eject_symbol:"] = "\u23cf\ufe0f",
+ [":electric_plug:"] = "\U0001f50c",
+ [":elephant:"] = "\U0001f418",
+ [":elevator:"] = "\U0001f6d7",
+ [":elf:"] = "\U0001f9dd",
+ [":elf_tone1:"] = "\U0001f9dd\U0001f3fb",
+ [":elf_light_skin_tone:"] = "\U0001f9dd\U0001f3fb",
+ [":elf::skin-tone-1:"] = "\U0001f9dd\U0001f3fb",
+ [":elf_tone2:"] = "\U0001f9dd\U0001f3fc",
+ [":elf_medium_light_skin_tone:"] = "\U0001f9dd\U0001f3fc",
+ [":elf::skin-tone-2:"] = "\U0001f9dd\U0001f3fc",
+ [":elf_tone3:"] = "\U0001f9dd\U0001f3fd",
+ [":elf_medium_skin_tone:"] = "\U0001f9dd\U0001f3fd",
+ [":elf::skin-tone-3:"] = "\U0001f9dd\U0001f3fd",
+ [":elf_tone4:"] = "\U0001f9dd\U0001f3fe",
+ [":elf_medium_dark_skin_tone:"] = "\U0001f9dd\U0001f3fe",
+ [":elf::skin-tone-4:"] = "\U0001f9dd\U0001f3fe",
+ [":elf_tone5:"] = "\U0001f9dd\U0001f3ff",
+ [":elf_dark_skin_tone:"] = "\U0001f9dd\U0001f3ff",
+ [":elf::skin-tone-5:"] = "\U0001f9dd\U0001f3ff",
+ [":end:"] = "\U0001f51a",
+ [":england:"] = "\U0001f3f4\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f",
+ [":envelope:"] = "\u2709\ufe0f",
+ [":envelope_with_arrow:"] = "\U0001f4e9",
+ [":euro:"] = "\U0001f4b6",
+ [":european_castle:"] = "\U0001f3f0",
+ [":european_post_office:"] = "\U0001f3e4",
+ [":evergreen_tree:"] = "\U0001f332",
+ [":exclamation:"] = "\u2757",
+ [":exploding_head:"] = "\U0001f92f",
+ [":expressionless:"] = "\U0001f611",
+ [":eye:"] = "\U0001f441\ufe0f",
+ [":eye_in_speech_bubble:"] = "\U0001f441\u200d\U0001f5e8",
+ [":eyeglasses:"] = "\U0001f453",
+ [":eyes:"] = "\U0001f440",
+ [":face_vomiting:"] = "\U0001f92e",
+ [":face_with_hand_over_mouth:"] = "\U0001f92d",
+ [":face_with_monocle:"] = "\U0001f9d0",
+ [":face_with_raised_eyebrow:"] = "\U0001f928",
+ [":face_with_symbols_over_mouth:"] = "\U0001f92c",
+ [":factory:"] = "\U0001f3ed",
+ [":factory_worker:"] = "\U0001f9d1\u200d\U0001f3ed",
+ [":factory_worker_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3ed",
+ [":factory_worker_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3ed",
+ [":factory_worker::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3ed",
+ [":factory_worker_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3ed",
+ [":factory_worker_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3ed",
+ [":factory_worker::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3ed",
+ [":factory_worker_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3ed",
+ [":factory_worker_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3ed",
+ [":factory_worker::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3ed",
+ [":factory_worker_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3ed",
+ [":factory_worker_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3ed",
+ [":factory_worker::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3ed",
+ [":factory_worker_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3ed",
+ [":factory_worker_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3ed",
+ [":factory_worker::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3ed",
+ [":fairy:"] = "\U0001f9da",
+ [":fairy_tone1:"] = "\U0001f9da\U0001f3fb",
+ [":fairy_light_skin_tone:"] = "\U0001f9da\U0001f3fb",
+ [":fairy::skin-tone-1:"] = "\U0001f9da\U0001f3fb",
+ [":fairy_tone2:"] = "\U0001f9da\U0001f3fc",
+ [":fairy_medium_light_skin_tone:"] = "\U0001f9da\U0001f3fc",
+ [":fairy::skin-tone-2:"] = "\U0001f9da\U0001f3fc",
+ [":fairy_tone3:"] = "\U0001f9da\U0001f3fd",
+ [":fairy_medium_skin_tone:"] = "\U0001f9da\U0001f3fd",
+ [":fairy::skin-tone-3:"] = "\U0001f9da\U0001f3fd",
+ [":fairy_tone4:"] = "\U0001f9da\U0001f3fe",
+ [":fairy_medium_dark_skin_tone:"] = "\U0001f9da\U0001f3fe",
+ [":fairy::skin-tone-4:"] = "\U0001f9da\U0001f3fe",
+ [":fairy_tone5:"] = "\U0001f9da\U0001f3ff",
+ [":fairy_dark_skin_tone:"] = "\U0001f9da\U0001f3ff",
+ [":fairy::skin-tone-5:"] = "\U0001f9da\U0001f3ff",
+ [":falafel:"] = "\U0001f9c6",
+ [":fallen_leaf:"] = "\U0001f342",
+ [":family:"] = "\U0001f46a",
+ [":family_man_boy:"] = "\U0001f468\u200d\U0001f466",
+ [":family_man_boy_boy:"] = "\U0001f468\u200d\U0001f466\u200d\U0001f466",
+ [":family_man_girl:"] = "\U0001f468\u200d\U0001f467",
+ [":family_man_girl_boy:"] = "\U0001f468\u200d\U0001f467\u200d\U0001f466",
+ [":family_man_girl_girl:"] = "\U0001f468\u200d\U0001f467\u200d\U0001f467",
+ [":family_man_woman_boy:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f466",
+ [":family_mmb:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f466",
+ [":family_mmbb:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f466\u200d\U0001f466",
+ [":family_mmg:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f467",
+ [":family_mmgb:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f466",
+ [":family_mmgg:"] = "\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f467",
+ [":family_mwbb:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466",
+ [":family_mwg:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f467",
+ [":family_mwgb:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466",
+ [":family_mwgg:"] = "\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467",
+ [":family_woman_boy:"] = "\U0001f469\u200d\U0001f466",
+ [":family_woman_boy_boy:"] = "\U0001f469\u200d\U0001f466\u200d\U0001f466",
+ [":family_woman_girl:"] = "\U0001f469\u200d\U0001f467",
+ [":family_woman_girl_boy:"] = "\U0001f469\u200d\U0001f467\u200d\U0001f466",
+ [":family_woman_girl_girl:"] = "\U0001f469\u200d\U0001f467\u200d\U0001f467",
+ [":family_wwb:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f466",
+ [":family_wwbb:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466",
+ [":family_wwg:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f467",
+ [":family_wwgb:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466",
+ [":family_wwgg:"] = "\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467",
+ [":farmer:"] = "\U0001f9d1\u200d\U0001f33e",
+ [":farmer_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f33e",
+ [":farmer_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f33e",
+ [":farmer::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f33e",
+ [":farmer_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f33e",
+ [":farmer_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f33e",
+ [":farmer::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f33e",
+ [":farmer_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f33e",
+ [":farmer_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f33e",
+ [":farmer::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f33e",
+ [":farmer_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f33e",
+ [":farmer_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f33e",
+ [":farmer::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f33e",
+ [":farmer_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f33e",
+ [":farmer_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f33e",
+ [":farmer::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f33e",
+ [":fast_forward:"] = "\u23e9",
+ [":fax:"] = "\U0001f4e0",
+ [":fearful:"] = "\U0001f628",
+ [":feather:"] = "\U0001fab6",
+ [":feet:"] = "\U0001f43e",
+ [":paw_prints:"] = "\U0001f43e",
+ [":female_sign:"] = "\u2640\ufe0f",
+ [":ferris_wheel:"] = "\U0001f3a1",
+ [":ferry:"] = "\u26f4\ufe0f",
+ [":field_hockey:"] = "\U0001f3d1",
+ [":file_cabinet:"] = "\U0001f5c4\ufe0f",
+ [":file_folder:"] = "\U0001f4c1",
+ [":film_frames:"] = "\U0001f39e\ufe0f",
+ [":fingers_crossed:"] = "\U0001f91e",
+ [":hand_with_index_and_middle_finger_crossed:"] = "\U0001f91e",
+ [":fingers_crossed_tone1:"] = "\U0001f91e\U0001f3fb",
+ [":hand_with_index_and_middle_fingers_crossed_tone1:"] = "\U0001f91e\U0001f3fb",
+ [":fingers_crossed::skin-tone-1:"] = "\U0001f91e\U0001f3fb",
+ [":hand_with_index_and_middle_finger_crossed::skin-tone-1:"] = "\U0001f91e\U0001f3fb",
+ [":fingers_crossed_tone2:"] = "\U0001f91e\U0001f3fc",
+ [":hand_with_index_and_middle_fingers_crossed_tone2:"] = "\U0001f91e\U0001f3fc",
+ [":fingers_crossed::skin-tone-2:"] = "\U0001f91e\U0001f3fc",
+ [":hand_with_index_and_middle_finger_crossed::skin-tone-2:"] = "\U0001f91e\U0001f3fc",
+ [":fingers_crossed_tone3:"] = "\U0001f91e\U0001f3fd",
+ [":hand_with_index_and_middle_fingers_crossed_tone3:"] = "\U0001f91e\U0001f3fd",
+ [":fingers_crossed::skin-tone-3:"] = "\U0001f91e\U0001f3fd",
+ [":hand_with_index_and_middle_finger_crossed::skin-tone-3:"] = "\U0001f91e\U0001f3fd",
+ [":fingers_crossed_tone4:"] = "\U0001f91e\U0001f3fe",
+ [":hand_with_index_and_middle_fingers_crossed_tone4:"] = "\U0001f91e\U0001f3fe",
+ [":fingers_crossed::skin-tone-4:"] = "\U0001f91e\U0001f3fe",
+ [":hand_with_index_and_middle_finger_crossed::skin-tone-4:"] = "\U0001f91e\U0001f3fe",
+ [":fingers_crossed_tone5:"] = "\U0001f91e\U0001f3ff",
+ [":hand_with_index_and_middle_fingers_crossed_tone5:"] = "\U0001f91e\U0001f3ff",
+ [":fingers_crossed::skin-tone-5:"] = "\U0001f91e\U0001f3ff",
+ [":hand_with_index_and_middle_finger_crossed::skin-tone-5:"] = "\U0001f91e\U0001f3ff",
+ [":fire:"] = "\U0001f525",
+ [":flame:"] = "\U0001f525",
+ [":fire_engine:"] = "\U0001f692",
+ [":fire_extinguisher:"] = "\U0001f9ef",
+ [":firecracker:"] = "\U0001f9e8",
+ [":firefighter:"] = "\U0001f9d1\u200d\U0001f692",
+ [":firefighter_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f692",
+ [":firefighter_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f692",
+ [":firefighter::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f692",
+ [":firefighter_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f692",
+ [":firefighter_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f692",
+ [":firefighter::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f692",
+ [":firefighter_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f692",
+ [":firefighter_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f692",
+ [":firefighter::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f692",
+ [":firefighter_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f692",
+ [":firefighter_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f692",
+ [":firefighter::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f692",
+ [":firefighter_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f692",
+ [":firefighter_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f692",
+ [":firefighter::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f692",
+ [":fireworks:"] = "\U0001f386",
+ [":first_place:"] = "\U0001f947",
+ [":first_place_medal:"] = "\U0001f947",
+ [":first_quarter_moon:"] = "\U0001f313",
+ [":first_quarter_moon_with_face:"] = "\U0001f31b",
+ [":fish:"] = "\U0001f41f",
+ [":fish_cake:"] = "\U0001f365",
+ [":fishing_pole_and_fish:"] = "\U0001f3a3",
+ [":fist:"] = "\u270a",
+ [":fist_tone1:"] = "\u270a\U0001f3fb",
+ [":fist::skin-tone-1:"] = "\u270a\U0001f3fb",
+ [":fist_tone2:"] = "\u270a\U0001f3fc",
+ [":fist::skin-tone-2:"] = "\u270a\U0001f3fc",
+ [":fist_tone3:"] = "\u270a\U0001f3fd",
+ [":fist::skin-tone-3:"] = "\u270a\U0001f3fd",
+ [":fist_tone4:"] = "\u270a\U0001f3fe",
+ [":fist::skin-tone-4:"] = "\u270a\U0001f3fe",
+ [":fist_tone5:"] = "\u270a\U0001f3ff",
+ [":fist::skin-tone-5:"] = "\u270a\U0001f3ff",
+ [":five:"] = "\u0035\ufe0f\u20e3",
+ [":flag_ac:"] = "\U0001f1e6\U0001f1e8",
+ [":flag_ad:"] = "\U0001f1e6\U0001f1e9",
+ [":flag_ae:"] = "\U0001f1e6\U0001f1ea",
+ [":flag_af:"] = "\U0001f1e6\U0001f1eb",
+ [":flag_ag:"] = "\U0001f1e6\U0001f1ec",
+ [":flag_ai:"] = "\U0001f1e6\U0001f1ee",
+ [":flag_al:"] = "\U0001f1e6\U0001f1f1",
+ [":flag_am:"] = "\U0001f1e6\U0001f1f2",
+ [":flag_ao:"] = "\U0001f1e6\U0001f1f4",
+ [":flag_aq:"] = "\U0001f1e6\U0001f1f6",
+ [":flag_ar:"] = "\U0001f1e6\U0001f1f7",
+ [":flag_as:"] = "\U0001f1e6\U0001f1f8",
+ [":flag_at:"] = "\U0001f1e6\U0001f1f9",
+ [":flag_au:"] = "\U0001f1e6\U0001f1fa",
+ [":flag_aw:"] = "\U0001f1e6\U0001f1fc",
+ [":flag_ax:"] = "\U0001f1e6\U0001f1fd",
+ [":flag_az:"] = "\U0001f1e6\U0001f1ff",
+ [":flag_ba:"] = "\U0001f1e7\U0001f1e6",
+ [":flag_bb:"] = "\U0001f1e7\U0001f1e7",
+ [":flag_bd:"] = "\U0001f1e7\U0001f1e9",
+ [":flag_be:"] = "\U0001f1e7\U0001f1ea",
+ [":flag_bf:"] = "\U0001f1e7\U0001f1eb",
+ [":flag_bg:"] = "\U0001f1e7\U0001f1ec",
+ [":flag_bh:"] = "\U0001f1e7\U0001f1ed",
+ [":flag_bi:"] = "\U0001f1e7\U0001f1ee",
+ [":flag_bj:"] = "\U0001f1e7\U0001f1ef",
+ [":flag_bl:"] = "\U0001f1e7\U0001f1f1",
+ [":flag_black:"] = "\U0001f3f4",
+ [":flag_bm:"] = "\U0001f1e7\U0001f1f2",
+ [":flag_bn:"] = "\U0001f1e7\U0001f1f3",
+ [":flag_bo:"] = "\U0001f1e7\U0001f1f4",
+ [":flag_bq:"] = "\U0001f1e7\U0001f1f6",
+ [":flag_br:"] = "\U0001f1e7\U0001f1f7",
+ [":flag_bs:"] = "\U0001f1e7\U0001f1f8",
+ [":flag_bt:"] = "\U0001f1e7\U0001f1f9",
+ [":flag_bv:"] = "\U0001f1e7\U0001f1fb",
+ [":flag_bw:"] = "\U0001f1e7\U0001f1fc",
+ [":flag_by:"] = "\U0001f1e7\U0001f1fe",
+ [":flag_bz:"] = "\U0001f1e7\U0001f1ff",
+ [":flag_ca:"] = "\U0001f1e8\U0001f1e6",
+ [":flag_cc:"] = "\U0001f1e8\U0001f1e8",
+ [":flag_cd:"] = "\U0001f1e8\U0001f1e9",
+ [":flag_cf:"] = "\U0001f1e8\U0001f1eb",
+ [":flag_cg:"] = "\U0001f1e8\U0001f1ec",
+ [":flag_ch:"] = "\U0001f1e8\U0001f1ed",
+ [":flag_ci:"] = "\U0001f1e8\U0001f1ee",
+ [":flag_ck:"] = "\U0001f1e8\U0001f1f0",
+ [":flag_cl:"] = "\U0001f1e8\U0001f1f1",
+ [":flag_cm:"] = "\U0001f1e8\U0001f1f2",
+ [":flag_cn:"] = "\U0001f1e8\U0001f1f3",
+ [":flag_co:"] = "\U0001f1e8\U0001f1f4",
+ [":flag_cp:"] = "\U0001f1e8\U0001f1f5",
+ [":flag_cr:"] = "\U0001f1e8\U0001f1f7",
+ [":flag_cu:"] = "\U0001f1e8\U0001f1fa",
+ [":flag_cv:"] = "\U0001f1e8\U0001f1fb",
+ [":flag_cw:"] = "\U0001f1e8\U0001f1fc",
+ [":flag_cx:"] = "\U0001f1e8\U0001f1fd",
+ [":flag_cy:"] = "\U0001f1e8\U0001f1fe",
+ [":flag_cz:"] = "\U0001f1e8\U0001f1ff",
+ [":flag_de:"] = "\U0001f1e9\U0001f1ea",
+ [":flag_dg:"] = "\U0001f1e9\U0001f1ec",
+ [":flag_dj:"] = "\U0001f1e9\U0001f1ef",
+ [":flag_dk:"] = "\U0001f1e9\U0001f1f0",
+ [":flag_dm:"] = "\U0001f1e9\U0001f1f2",
+ [":flag_do:"] = "\U0001f1e9\U0001f1f4",
+ [":flag_dz:"] = "\U0001f1e9\U0001f1ff",
+ [":flag_ea:"] = "\U0001f1ea\U0001f1e6",
+ [":flag_ec:"] = "\U0001f1ea\U0001f1e8",
+ [":flag_ee:"] = "\U0001f1ea\U0001f1ea",
+ [":flag_eg:"] = "\U0001f1ea\U0001f1ec",
+ [":flag_eh:"] = "\U0001f1ea\U0001f1ed",
+ [":flag_er:"] = "\U0001f1ea\U0001f1f7",
+ [":flag_es:"] = "\U0001f1ea\U0001f1f8",
+ [":flag_et:"] = "\U0001f1ea\U0001f1f9",
+ [":flag_eu:"] = "\U0001f1ea\U0001f1fa",
+ [":flag_fi:"] = "\U0001f1eb\U0001f1ee",
+ [":flag_fj:"] = "\U0001f1eb\U0001f1ef",
+ [":flag_fk:"] = "\U0001f1eb\U0001f1f0",
+ [":flag_fm:"] = "\U0001f1eb\U0001f1f2",
+ [":flag_fo:"] = "\U0001f1eb\U0001f1f4",
+ [":flag_fr:"] = "\U0001f1eb\U0001f1f7",
+ [":flag_ga:"] = "\U0001f1ec\U0001f1e6",
+ [":flag_gb:"] = "\U0001f1ec\U0001f1e7",
+ [":flag_gd:"] = "\U0001f1ec\U0001f1e9",
+ [":flag_ge:"] = "\U0001f1ec\U0001f1ea",
+ [":flag_gf:"] = "\U0001f1ec\U0001f1eb",
+ [":flag_gg:"] = "\U0001f1ec\U0001f1ec",
+ [":flag_gh:"] = "\U0001f1ec\U0001f1ed",
+ [":flag_gi:"] = "\U0001f1ec\U0001f1ee",
+ [":flag_gl:"] = "\U0001f1ec\U0001f1f1",
+ [":flag_gm:"] = "\U0001f1ec\U0001f1f2",
+ [":flag_gn:"] = "\U0001f1ec\U0001f1f3",
+ [":flag_gp:"] = "\U0001f1ec\U0001f1f5",
+ [":flag_gq:"] = "\U0001f1ec\U0001f1f6",
+ [":flag_gr:"] = "\U0001f1ec\U0001f1f7",
+ [":flag_gs:"] = "\U0001f1ec\U0001f1f8",
+ [":flag_gt:"] = "\U0001f1ec\U0001f1f9",
+ [":flag_gu:"] = "\U0001f1ec\U0001f1fa",
+ [":flag_gw:"] = "\U0001f1ec\U0001f1fc",
+ [":flag_gy:"] = "\U0001f1ec\U0001f1fe",
+ [":flag_hk:"] = "\U0001f1ed\U0001f1f0",
+ [":flag_hm:"] = "\U0001f1ed\U0001f1f2",
+ [":flag_hn:"] = "\U0001f1ed\U0001f1f3",
+ [":flag_hr:"] = "\U0001f1ed\U0001f1f7",
+ [":flag_ht:"] = "\U0001f1ed\U0001f1f9",
+ [":flag_hu:"] = "\U0001f1ed\U0001f1fa",
+ [":flag_ic:"] = "\U0001f1ee\U0001f1e8",
+ [":flag_id:"] = "\U0001f1ee\U0001f1e9",
+ [":flag_ie:"] = "\U0001f1ee\U0001f1ea",
+ [":flag_il:"] = "\U0001f1ee\U0001f1f1",
+ [":flag_im:"] = "\U0001f1ee\U0001f1f2",
+ [":flag_in:"] = "\U0001f1ee\U0001f1f3",
+ [":flag_io:"] = "\U0001f1ee\U0001f1f4",
+ [":flag_iq:"] = "\U0001f1ee\U0001f1f6",
+ [":flag_ir:"] = "\U0001f1ee\U0001f1f7",
+ [":flag_is:"] = "\U0001f1ee\U0001f1f8",
+ [":flag_it:"] = "\U0001f1ee\U0001f1f9",
+ [":flag_je:"] = "\U0001f1ef\U0001f1ea",
+ [":flag_jm:"] = "\U0001f1ef\U0001f1f2",
+ [":flag_jo:"] = "\U0001f1ef\U0001f1f4",
+ [":flag_jp:"] = "\U0001f1ef\U0001f1f5",
+ [":flag_ke:"] = "\U0001f1f0\U0001f1ea",
+ [":flag_kg:"] = "\U0001f1f0\U0001f1ec",
+ [":flag_kh:"] = "\U0001f1f0\U0001f1ed",
+ [":flag_ki:"] = "\U0001f1f0\U0001f1ee",
+ [":flag_km:"] = "\U0001f1f0\U0001f1f2",
+ [":flag_kn:"] = "\U0001f1f0\U0001f1f3",
+ [":flag_kp:"] = "\U0001f1f0\U0001f1f5",
+ [":flag_kr:"] = "\U0001f1f0\U0001f1f7",
+ [":flag_kw:"] = "\U0001f1f0\U0001f1fc",
+ [":flag_ky:"] = "\U0001f1f0\U0001f1fe",
+ [":flag_kz:"] = "\U0001f1f0\U0001f1ff",
+ [":flag_la:"] = "\U0001f1f1\U0001f1e6",
+ [":flag_lb:"] = "\U0001f1f1\U0001f1e7",
+ [":flag_lc:"] = "\U0001f1f1\U0001f1e8",
+ [":flag_li:"] = "\U0001f1f1\U0001f1ee",
+ [":flag_lk:"] = "\U0001f1f1\U0001f1f0",
+ [":flag_lr:"] = "\U0001f1f1\U0001f1f7",
+ [":flag_ls:"] = "\U0001f1f1\U0001f1f8",
+ [":flag_lt:"] = "\U0001f1f1\U0001f1f9",
+ [":flag_lu:"] = "\U0001f1f1\U0001f1fa",
+ [":flag_lv:"] = "\U0001f1f1\U0001f1fb",
+ [":flag_ly:"] = "\U0001f1f1\U0001f1fe",
+ [":flag_ma:"] = "\U0001f1f2\U0001f1e6",
+ [":flag_mc:"] = "\U0001f1f2\U0001f1e8",
+ [":flag_md:"] = "\U0001f1f2\U0001f1e9",
+ [":flag_me:"] = "\U0001f1f2\U0001f1ea",
+ [":flag_mf:"] = "\U0001f1f2\U0001f1eb",
+ [":flag_mg:"] = "\U0001f1f2\U0001f1ec",
+ [":flag_mh:"] = "\U0001f1f2\U0001f1ed",
+ [":flag_mk:"] = "\U0001f1f2\U0001f1f0",
+ [":flag_ml:"] = "\U0001f1f2\U0001f1f1",
+ [":flag_mm:"] = "\U0001f1f2\U0001f1f2",
+ [":flag_mn:"] = "\U0001f1f2\U0001f1f3",
+ [":flag_mo:"] = "\U0001f1f2\U0001f1f4",
+ [":flag_mp:"] = "\U0001f1f2\U0001f1f5",
+ [":flag_mq:"] = "\U0001f1f2\U0001f1f6",
+ [":flag_mr:"] = "\U0001f1f2\U0001f1f7",
+ [":flag_ms:"] = "\U0001f1f2\U0001f1f8",
+ [":flag_mt:"] = "\U0001f1f2\U0001f1f9",
+ [":flag_mu:"] = "\U0001f1f2\U0001f1fa",
+ [":flag_mv:"] = "\U0001f1f2\U0001f1fb",
+ [":flag_mw:"] = "\U0001f1f2\U0001f1fc",
+ [":flag_mx:"] = "\U0001f1f2\U0001f1fd",
+ [":flag_my:"] = "\U0001f1f2\U0001f1fe",
+ [":flag_mz:"] = "\U0001f1f2\U0001f1ff",
+ [":flag_na:"] = "\U0001f1f3\U0001f1e6",
+ [":flag_nc:"] = "\U0001f1f3\U0001f1e8",
+ [":flag_ne:"] = "\U0001f1f3\U0001f1ea",
+ [":flag_nf:"] = "\U0001f1f3\U0001f1eb",
+ [":flag_ng:"] = "\U0001f1f3\U0001f1ec",
+ [":flag_ni:"] = "\U0001f1f3\U0001f1ee",
+ [":flag_nl:"] = "\U0001f1f3\U0001f1f1",
+ [":flag_no:"] = "\U0001f1f3\U0001f1f4",
+ [":flag_np:"] = "\U0001f1f3\U0001f1f5",
+ [":flag_nr:"] = "\U0001f1f3\U0001f1f7",
+ [":flag_nu:"] = "\U0001f1f3\U0001f1fa",
+ [":flag_nz:"] = "\U0001f1f3\U0001f1ff",
+ [":flag_om:"] = "\U0001f1f4\U0001f1f2",
+ [":flag_pa:"] = "\U0001f1f5\U0001f1e6",
+ [":flag_pe:"] = "\U0001f1f5\U0001f1ea",
+ [":flag_pf:"] = "\U0001f1f5\U0001f1eb",
+ [":flag_pg:"] = "\U0001f1f5\U0001f1ec",
+ [":flag_ph:"] = "\U0001f1f5\U0001f1ed",
+ [":flag_pk:"] = "\U0001f1f5\U0001f1f0",
+ [":flag_pl:"] = "\U0001f1f5\U0001f1f1",
+ [":flag_pm:"] = "\U0001f1f5\U0001f1f2",
+ [":flag_pn:"] = "\U0001f1f5\U0001f1f3",
+ [":flag_pr:"] = "\U0001f1f5\U0001f1f7",
+ [":flag_ps:"] = "\U0001f1f5\U0001f1f8",
+ [":flag_pt:"] = "\U0001f1f5\U0001f1f9",
+ [":flag_pw:"] = "\U0001f1f5\U0001f1fc",
+ [":flag_py:"] = "\U0001f1f5\U0001f1fe",
+ [":flag_qa:"] = "\U0001f1f6\U0001f1e6",
+ [":flag_re:"] = "\U0001f1f7\U0001f1ea",
+ [":flag_ro:"] = "\U0001f1f7\U0001f1f4",
+ [":flag_rs:"] = "\U0001f1f7\U0001f1f8",
+ [":flag_ru:"] = "\U0001f1f7\U0001f1fa",
+ [":flag_rw:"] = "\U0001f1f7\U0001f1fc",
+ [":flag_sa:"] = "\U0001f1f8\U0001f1e6",
+ [":flag_sb:"] = "\U0001f1f8\U0001f1e7",
+ [":flag_sc:"] = "\U0001f1f8\U0001f1e8",
+ [":flag_sd:"] = "\U0001f1f8\U0001f1e9",
+ [":flag_se:"] = "\U0001f1f8\U0001f1ea",
+ [":flag_sg:"] = "\U0001f1f8\U0001f1ec",
+ [":flag_sh:"] = "\U0001f1f8\U0001f1ed",
+ [":flag_si:"] = "\U0001f1f8\U0001f1ee",
+ [":flag_sj:"] = "\U0001f1f8\U0001f1ef",
+ [":flag_sk:"] = "\U0001f1f8\U0001f1f0",
+ [":flag_sl:"] = "\U0001f1f8\U0001f1f1",
+ [":flag_sm:"] = "\U0001f1f8\U0001f1f2",
+ [":flag_sn:"] = "\U0001f1f8\U0001f1f3",
+ [":flag_so:"] = "\U0001f1f8\U0001f1f4",
+ [":flag_sr:"] = "\U0001f1f8\U0001f1f7",
+ [":flag_ss:"] = "\U0001f1f8\U0001f1f8",
+ [":flag_st:"] = "\U0001f1f8\U0001f1f9",
+ [":flag_sv:"] = "\U0001f1f8\U0001f1fb",
+ [":flag_sx:"] = "\U0001f1f8\U0001f1fd",
+ [":flag_sy:"] = "\U0001f1f8\U0001f1fe",
+ [":flag_sz:"] = "\U0001f1f8\U0001f1ff",
+ [":flag_ta:"] = "\U0001f1f9\U0001f1e6",
+ [":flag_tc:"] = "\U0001f1f9\U0001f1e8",
+ [":flag_td:"] = "\U0001f1f9\U0001f1e9",
+ [":flag_tf:"] = "\U0001f1f9\U0001f1eb",
+ [":flag_tg:"] = "\U0001f1f9\U0001f1ec",
+ [":flag_th:"] = "\U0001f1f9\U0001f1ed",
+ [":flag_tj:"] = "\U0001f1f9\U0001f1ef",
+ [":flag_tk:"] = "\U0001f1f9\U0001f1f0",
+ [":flag_tl:"] = "\U0001f1f9\U0001f1f1",
+ [":flag_tm:"] = "\U0001f1f9\U0001f1f2",
+ [":flag_tn:"] = "\U0001f1f9\U0001f1f3",
+ [":flag_to:"] = "\U0001f1f9\U0001f1f4",
+ [":flag_tr:"] = "\U0001f1f9\U0001f1f7",
+ [":flag_tt:"] = "\U0001f1f9\U0001f1f9",
+ [":flag_tv:"] = "\U0001f1f9\U0001f1fb",
+ [":flag_tw:"] = "\U0001f1f9\U0001f1fc",
+ [":flag_tz:"] = "\U0001f1f9\U0001f1ff",
+ [":flag_ua:"] = "\U0001f1fa\U0001f1e6",
+ [":flag_ug:"] = "\U0001f1fa\U0001f1ec",
+ [":flag_um:"] = "\U0001f1fa\U0001f1f2",
+ [":flag_us:"] = "\U0001f1fa\U0001f1f8",
+ [":flag_uy:"] = "\U0001f1fa\U0001f1fe",
+ [":flag_uz:"] = "\U0001f1fa\U0001f1ff",
+ [":flag_va:"] = "\U0001f1fb\U0001f1e6",
+ [":flag_vc:"] = "\U0001f1fb\U0001f1e8",
+ [":flag_ve:"] = "\U0001f1fb\U0001f1ea",
+ [":flag_vg:"] = "\U0001f1fb\U0001f1ec",
+ [":flag_vi:"] = "\U0001f1fb\U0001f1ee",
+ [":flag_vn:"] = "\U0001f1fb\U0001f1f3",
+ [":flag_vu:"] = "\U0001f1fb\U0001f1fa",
+ [":flag_wf:"] = "\U0001f1fc\U0001f1eb",
+ [":flag_white:"] = "\U0001f3f3\ufe0f",
+ [":flag_ws:"] = "\U0001f1fc\U0001f1f8",
+ [":flag_xk:"] = "\U0001f1fd\U0001f1f0",
+ [":flag_ye:"] = "\U0001f1fe\U0001f1ea",
+ [":flag_yt:"] = "\U0001f1fe\U0001f1f9",
+ [":flag_za:"] = "\U0001f1ff\U0001f1e6",
+ [":flag_zm:"] = "\U0001f1ff\U0001f1f2",
+ [":flag_zw:"] = "\U0001f1ff\U0001f1fc",
+ [":flags:"] = "\U0001f38f",
+ [":flamingo:"] = "\U0001f9a9",
+ [":flashlight:"] = "\U0001f526",
+ [":flatbread:"] = "\U0001fad3",
+ [":fleur_de_lis:"] = "\u269c\ufe0f",
+ [":floppy_disk:"] = "\U0001f4be",
+ [":flower_playing_cards:"] = "\U0001f3b4",
+ [":flushed:"] = "\U0001f633",
+ [":fly:"] = "\U0001fab0",
+ [":flying_disc:"] = "\U0001f94f",
+ [":flying_saucer:"] = "\U0001f6f8",
+ [":fog:"] = "\U0001f32b\ufe0f",
+ [":foggy:"] = "\U0001f301",
+ [":fondue:"] = "\U0001fad5",
+ [":foot:"] = "\U0001f9b6",
+ [":foot_tone1:"] = "\U0001f9b6\U0001f3fb",
+ [":foot_light_skin_tone:"] = "\U0001f9b6\U0001f3fb",
+ [":foot::skin-tone-1:"] = "\U0001f9b6\U0001f3fb",
+ [":foot_tone2:"] = "\U0001f9b6\U0001f3fc",
+ [":foot_medium_light_skin_tone:"] = "\U0001f9b6\U0001f3fc",
+ [":foot::skin-tone-2:"] = "\U0001f9b6\U0001f3fc",
+ [":foot_tone3:"] = "\U0001f9b6\U0001f3fd",
+ [":foot_medium_skin_tone:"] = "\U0001f9b6\U0001f3fd",
+ [":foot::skin-tone-3:"] = "\U0001f9b6\U0001f3fd",
+ [":foot_tone4:"] = "\U0001f9b6\U0001f3fe",
+ [":foot_medium_dark_skin_tone:"] = "\U0001f9b6\U0001f3fe",
+ [":foot::skin-tone-4:"] = "\U0001f9b6\U0001f3fe",
+ [":foot_tone5:"] = "\U0001f9b6\U0001f3ff",
+ [":foot_dark_skin_tone:"] = "\U0001f9b6\U0001f3ff",
+ [":foot::skin-tone-5:"] = "\U0001f9b6\U0001f3ff",
+ [":football:"] = "\U0001f3c8",
+ [":footprints:"] = "\U0001f463",
+ [":fork_and_knife:"] = "\U0001f374",
+ [":fork_knife_plate:"] = "\U0001f37d\ufe0f",
+ [":fork_and_knife_with_plate:"] = "\U0001f37d\ufe0f",
+ [":fortune_cookie:"] = "\U0001f960",
+ [":fountain:"] = "\u26f2",
+ [":four:"] = "\u0034\ufe0f\u20e3",
+ [":four_leaf_clover:"] = "\U0001f340",
+ [":fox:"] = "\U0001f98a",
+ [":fox_face:"] = "\U0001f98a",
+ [":frame_photo:"] = "\U0001f5bc\ufe0f",
+ [":frame_with_picture:"] = "\U0001f5bc\ufe0f",
+ [":free:"] = "\U0001f193",
+ [":french_bread:"] = "\U0001f956",
+ [":baguette_bread:"] = "\U0001f956",
+ [":fried_shrimp:"] = "\U0001f364",
+ [":fries:"] = "\U0001f35f",
+ [":frog:"] = "\U0001f438",
+ [":frowning:"] = "\U0001f626",
+ [":("] = "\U0001f626",
+ [":-("] = "\U0001f626",
+ ["=("] = "\U0001f626",
+ ["=-("] = "\U0001f626",
+ [":frowning2:"] = "\u2639\ufe0f",
+ [":white_frowning_face:"] = "\u2639\ufe0f",
+ [":fuelpump:"] = "\u26fd",
+ [":full_moon:"] = "\U0001f315",
+ [":full_moon_with_face:"] = "\U0001f31d",
+ [":game_die:"] = "\U0001f3b2",
+ [":garlic:"] = "\U0001f9c4",
+ [":gear:"] = "\u2699\ufe0f",
+ [":gem:"] = "\U0001f48e",
+ [":gemini:"] = "\u264a",
+ [":genie:"] = "\U0001f9de",
+ [":ghost:"] = "\U0001f47b",
+ [":gift:"] = "\U0001f381",
+ [":gift_heart:"] = "\U0001f49d",
+ [":giraffe:"] = "\U0001f992",
+ [":girl:"] = "\U0001f467",
+ [":girl_tone1:"] = "\U0001f467\U0001f3fb",
+ [":girl::skin-tone-1:"] = "\U0001f467\U0001f3fb",
+ [":girl_tone2:"] = "\U0001f467\U0001f3fc",
+ [":girl::skin-tone-2:"] = "\U0001f467\U0001f3fc",
+ [":girl_tone3:"] = "\U0001f467\U0001f3fd",
+ [":girl::skin-tone-3:"] = "\U0001f467\U0001f3fd",
+ [":girl_tone4:"] = "\U0001f467\U0001f3fe",
+ [":girl::skin-tone-4:"] = "\U0001f467\U0001f3fe",
+ [":girl_tone5:"] = "\U0001f467\U0001f3ff",
+ [":girl::skin-tone-5:"] = "\U0001f467\U0001f3ff",
+ [":globe_with_meridians:"] = "\U0001f310",
+ [":gloves:"] = "\U0001f9e4",
+ [":goal:"] = "\U0001f945",
+ [":goal_net:"] = "\U0001f945",
+ [":goat:"] = "\U0001f410",
+ [":goggles:"] = "\U0001f97d",
+ [":golf:"] = "\u26f3",
+ [":gorilla:"] = "\U0001f98d",
+ [":grapes:"] = "\U0001f347",
+ [":green_apple:"] = "\U0001f34f",
+ [":green_book:"] = "\U0001f4d7",
+ [":green_circle:"] = "\U0001f7e2",
+ [":green_heart:"] = "\U0001f49a",
+ [":green_square:"] = "\U0001f7e9",
+ [":grey_exclamation:"] = "\u2755",
+ [":grey_question:"] = "\u2754",
+ [":grimacing:"] = "\U0001f62c",
+ [":grin:"] = "\U0001f601",
+ [":grinning:"] = "\U0001f600",
+ [":guard:"] = "\U0001f482",
+ [":guardsman:"] = "\U0001f482",
+ [":guard_tone1:"] = "\U0001f482\U0001f3fb",
+ [":guardsman_tone1:"] = "\U0001f482\U0001f3fb",
+ [":guard::skin-tone-1:"] = "\U0001f482\U0001f3fb",
+ [":guardsman::skin-tone-1:"] = "\U0001f482\U0001f3fb",
+ [":guard_tone2:"] = "\U0001f482\U0001f3fc",
+ [":guardsman_tone2:"] = "\U0001f482\U0001f3fc",
+ [":guard::skin-tone-2:"] = "\U0001f482\U0001f3fc",
+ [":guardsman::skin-tone-2:"] = "\U0001f482\U0001f3fc",
+ [":guard_tone3:"] = "\U0001f482\U0001f3fd",
+ [":guardsman_tone3:"] = "\U0001f482\U0001f3fd",
+ [":guard::skin-tone-3:"] = "\U0001f482\U0001f3fd",
+ [":guardsman::skin-tone-3:"] = "\U0001f482\U0001f3fd",
+ [":guard_tone4:"] = "\U0001f482\U0001f3fe",
+ [":guardsman_tone4:"] = "\U0001f482\U0001f3fe",
+ [":guard::skin-tone-4:"] = "\U0001f482\U0001f3fe",
+ [":guardsman::skin-tone-4:"] = "\U0001f482\U0001f3fe",
+ [":guard_tone5:"] = "\U0001f482\U0001f3ff",
+ [":guardsman_tone5:"] = "\U0001f482\U0001f3ff",
+ [":guard::skin-tone-5:"] = "\U0001f482\U0001f3ff",
+ [":guardsman::skin-tone-5:"] = "\U0001f482\U0001f3ff",
+ [":guide_dog:"] = "\U0001f9ae",
+ [":guitar:"] = "\U0001f3b8",
+ [":gun:"] = "\U0001f52b",
+ [":hamburger:"] = "\U0001f354",
+ [":hammer:"] = "\U0001f528",
+ [":hammer_pick:"] = "\u2692\ufe0f",
+ [":hammer_and_pick:"] = "\u2692\ufe0f",
+ [":hamster:"] = "\U0001f439",
+ [":hand_splayed:"] = "\U0001f590\ufe0f",
+ [":raised_hand_with_fingers_splayed:"] = "\U0001f590\ufe0f",
+ [":hand_splayed_tone1:"] = "\U0001f590\U0001f3fb",
+ [":raised_hand_with_fingers_splayed_tone1:"] = "\U0001f590\U0001f3fb",
+ [":hand_splayed::skin-tone-1:"] = "\U0001f590\U0001f3fb",
+ [":raised_hand_with_fingers_splayed::skin-tone-1:"] = "\U0001f590\U0001f3fb",
+ [":hand_splayed_tone2:"] = "\U0001f590\U0001f3fc",
+ [":raised_hand_with_fingers_splayed_tone2:"] = "\U0001f590\U0001f3fc",
+ [":hand_splayed::skin-tone-2:"] = "\U0001f590\U0001f3fc",
+ [":raised_hand_with_fingers_splayed::skin-tone-2:"] = "\U0001f590\U0001f3fc",
+ [":hand_splayed_tone3:"] = "\U0001f590\U0001f3fd",
+ [":raised_hand_with_fingers_splayed_tone3:"] = "\U0001f590\U0001f3fd",
+ [":hand_splayed::skin-tone-3:"] = "\U0001f590\U0001f3fd",
+ [":raised_hand_with_fingers_splayed::skin-tone-3:"] = "\U0001f590\U0001f3fd",
+ [":hand_splayed_tone4:"] = "\U0001f590\U0001f3fe",
+ [":raised_hand_with_fingers_splayed_tone4:"] = "\U0001f590\U0001f3fe",
+ [":hand_splayed::skin-tone-4:"] = "\U0001f590\U0001f3fe",
+ [":raised_hand_with_fingers_splayed::skin-tone-4:"] = "\U0001f590\U0001f3fe",
+ [":hand_splayed_tone5:"] = "\U0001f590\U0001f3ff",
+ [":raised_hand_with_fingers_splayed_tone5:"] = "\U0001f590\U0001f3ff",
+ [":hand_splayed::skin-tone-5:"] = "\U0001f590\U0001f3ff",
+ [":raised_hand_with_fingers_splayed::skin-tone-5:"] = "\U0001f590\U0001f3ff",
+ [":handbag:"] = "\U0001f45c",
+ [":handshake:"] = "\U0001f91d",
+ [":shaking_hands:"] = "\U0001f91d",
+ [":hash:"] = "\u0023\ufe0f\u20e3",
+ [":hatched_chick:"] = "\U0001f425",
+ [":hatching_chick:"] = "\U0001f423",
+ [":head_bandage:"] = "\U0001f915",
+ [":face_with_head_bandage:"] = "\U0001f915",
+ [":headphones:"] = "\U0001f3a7",
+ [":headstone:"] = "\U0001faa6",
+ [":health_worker:"] = "\U0001f9d1\u200d\u2695\ufe0f",
+ [":health_worker_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f",
+ [":health_worker_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f",
+ [":health_worker::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f",
+ [":health_worker_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f",
+ [":health_worker_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f",
+ [":health_worker::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f",
+ [":health_worker_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f",
+ [":health_worker_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f",
+ [":health_worker::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f",
+ [":health_worker_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f",
+ [":health_worker_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f",
+ [":health_worker::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f",
+ [":health_worker_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f",
+ [":health_worker_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f",
+ [":health_worker::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f",
+ [":hear_no_evil:"] = "\U0001f649",
+ [":heart:"] = "\u2764\ufe0f",
+ ["<3"] = "\u2764\ufe0f",
+ ["♡"] = "\u2764\ufe0f",
+ [":heart_decoration:"] = "\U0001f49f",
+ [":heart_exclamation:"] = "\u2763\ufe0f",
+ [":heavy_heart_exclamation_mark_ornament:"] = "\u2763\ufe0f",
+ [":heart_eyes:"] = "\U0001f60d",
+ [":heart_eyes_cat:"] = "\U0001f63b",
+ [":heartbeat:"] = "\U0001f493",
+ [":heartpulse:"] = "\U0001f497",
+ [":hearts:"] = "\u2665\ufe0f",
+ [":heavy_check_mark:"] = "\u2714\ufe0f",
+ [":heavy_division_sign:"] = "\u2797",
+ [":heavy_dollar_sign:"] = "\U0001f4b2",
+ [":heavy_minus_sign:"] = "\u2796",
+ [":heavy_multiplication_x:"] = "\u2716\ufe0f",
+ [":heavy_plus_sign:"] = "\u2795",
+ [":hedgehog:"] = "\U0001f994",
+ [":helicopter:"] = "\U0001f681",
+ [":helmet_with_cross:"] = "\u26d1\ufe0f",
+ [":helmet_with_white_cross:"] = "\u26d1\ufe0f",
+ [":herb:"] = "\U0001f33f",
+ [":hibiscus:"] = "\U0001f33a",
+ [":high_brightness:"] = "\U0001f506",
+ [":high_heel:"] = "\U0001f460",
+ [":hiking_boot:"] = "\U0001f97e",
+ [":hindu_temple:"] = "\U0001f6d5",
+ [":hippopotamus:"] = "\U0001f99b",
+ [":hockey:"] = "\U0001f3d2",
+ [":hole:"] = "\U0001f573\ufe0f",
+ [":homes:"] = "\U0001f3d8\ufe0f",
+ [":house_buildings:"] = "\U0001f3d8\ufe0f",
+ [":honey_pot:"] = "\U0001f36f",
+ [":hook:"] = "\U0001fa9d",
+ [":horse:"] = "\U0001f434",
+ [":horse_racing:"] = "\U0001f3c7",
+ [":horse_racing_tone1:"] = "\U0001f3c7\U0001f3fb",
+ [":horse_racing::skin-tone-1:"] = "\U0001f3c7\U0001f3fb",
+ [":horse_racing_tone2:"] = "\U0001f3c7\U0001f3fc",
+ [":horse_racing::skin-tone-2:"] = "\U0001f3c7\U0001f3fc",
+ [":horse_racing_tone3:"] = "\U0001f3c7\U0001f3fd",
+ [":horse_racing::skin-tone-3:"] = "\U0001f3c7\U0001f3fd",
+ [":horse_racing_tone4:"] = "\U0001f3c7\U0001f3fe",
+ [":horse_racing::skin-tone-4:"] = "\U0001f3c7\U0001f3fe",
+ [":horse_racing_tone5:"] = "\U0001f3c7\U0001f3ff",
+ [":horse_racing::skin-tone-5:"] = "\U0001f3c7\U0001f3ff",
+ [":hospital:"] = "\U0001f3e5",
+ [":hot_face:"] = "\U0001f975",
+ [":hot_pepper:"] = "\U0001f336\ufe0f",
+ [":hotdog:"] = "\U0001f32d",
+ [":hot_dog:"] = "\U0001f32d",
+ [":hotel:"] = "\U0001f3e8",
+ [":hotsprings:"] = "\u2668\ufe0f",
+ [":hourglass:"] = "\u231b",
+ [":hourglass_flowing_sand:"] = "\u23f3",
+ [":house:"] = "\U0001f3e0",
+ [":house_abandoned:"] = "\U0001f3da\ufe0f",
+ [":derelict_house_building:"] = "\U0001f3da\ufe0f",
+ [":house_with_garden:"] = "\U0001f3e1",
+ [":hugging:"] = "\U0001f917",
+ [":hugging_face:"] = "\U0001f917",
+ [":hushed:"] = "\U0001f62f",
+ [":hut:"] = "\U0001f6d6",
+ [":ice_cream:"] = "\U0001f368",
+ [":ice_cube:"] = "\U0001f9ca",
+ [":ice_skate:"] = "\u26f8\ufe0f",
+ [":icecream:"] = "\U0001f366",
+ [":id:"] = "\U0001f194",
+ [":ideograph_advantage:"] = "\U0001f250",
+ [":imp:"] = "\U0001f47f",
+ ["]:("] = "\U0001f47f",
+ ["]:-("] = "\U0001f47f",
+ ["]=("] = "\U0001f47f",
+ ["]=-("] = "\U0001f47f",
+ [":inbox_tray:"] = "\U0001f4e5",
+ [":incoming_envelope:"] = "\U0001f4e8",
+ [":infinity:"] = "\u267e\ufe0f",
+ [":information_source:"] = "\u2139\ufe0f",
+ [":innocent:"] = "\U0001f607",
+ ["o:)"] = "\U0001f607",
+ ["O:)"] = "\U0001f607",
+ ["o:-)"] = "\U0001f607",
+ ["O:-)"] = "\U0001f607",
+ ["0:)"] = "\U0001f607",
+ ["0:-)"] = "\U0001f607",
+ ["o=)"] = "\U0001f607",
+ ["O=)"] = "\U0001f607",
+ ["o=-)"] = "\U0001f607",
+ ["O=-)"] = "\U0001f607",
+ ["0=)"] = "\U0001f607",
+ ["0=-)"] = "\U0001f607",
+ [":interrobang:"] = "\u2049\ufe0f",
+ [":island:"] = "\U0001f3dd\ufe0f",
+ [":desert_island:"] = "\U0001f3dd\ufe0f",
+ [":izakaya_lantern:"] = "\U0001f3ee",
+ [":jack_o_lantern:"] = "\U0001f383",
+ [":japan:"] = "\U0001f5fe",
+ [":japanese_castle:"] = "\U0001f3ef",
+ [":japanese_goblin:"] = "\U0001f47a",
+ [":japanese_ogre:"] = "\U0001f479",
+ [":jeans:"] = "\U0001f456",
+ [":jigsaw:"] = "\U0001f9e9",
+ [":joy:"] = "\U0001f602",
+ [":')"] = "\U0001f602",
+ [":'-)"] = "\U0001f602",
+ [":,)"] = "\U0001f602",
+ [":,-)"] = "\U0001f602",
+ [":'D"] = "\U0001f602",
+ [":'-D"] = "\U0001f602",
+ [":,D"] = "\U0001f602",
+ [":,-D"] = "\U0001f602",
+ ["=')"] = "\U0001f602",
+ ["='-)"] = "\U0001f602",
+ ["=,)"] = "\U0001f602",
+ ["=,-)"] = "\U0001f602",
+ ["='D"] = "\U0001f602",
+ ["='-D"] = "\U0001f602",
+ ["=,D"] = "\U0001f602",
+ ["=,-D"] = "\U0001f602",
+ [":joy_cat:"] = "\U0001f639",
+ [":joystick:"] = "\U0001f579\ufe0f",
+ [":judge:"] = "\U0001f9d1\u200d\u2696\ufe0f",
+ [":judge_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f",
+ [":judge_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f",
+ [":judge::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f",
+ [":judge_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f",
+ [":judge_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f",
+ [":judge::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f",
+ [":judge_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f",
+ [":judge_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f",
+ [":judge::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f",
+ [":judge_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f",
+ [":judge_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f",
+ [":judge::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f",
+ [":judge_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f",
+ [":judge_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f",
+ [":judge::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f",
+ [":kaaba:"] = "\U0001f54b",
+ [":kangaroo:"] = "\U0001f998",
+ [":key:"] = "\U0001f511",
+ [":key2:"] = "\U0001f5dd\ufe0f",
+ [":old_key:"] = "\U0001f5dd\ufe0f",
+ [":keyboard:"] = "\u2328\ufe0f",
+ [":keycap_ten:"] = "\U0001f51f",
+ [":kimono:"] = "\U0001f458",
+ [":kiss:"] = "\U0001f48b",
+ [":kiss_mm:"] = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468",
+ [":couplekiss_mm:"] = "\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468",
+ [":kiss_woman_man:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468",
+ [":kiss_ww:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469",
+ [":couplekiss_ww:"] = "\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469",
+ [":kissing:"] = "\U0001f617",
+ [":*"] = "\U0001f617",
+ [":-*"] = "\U0001f617",
+ ["=*"] = "\U0001f617",
+ ["=-*"] = "\U0001f617",
+ [":kissing_cat:"] = "\U0001f63d",
+ [":kissing_closed_eyes:"] = "\U0001f61a",
+ [":kissing_heart:"] = "\U0001f618",
+ [":kissing_smiling_eyes:"] = "\U0001f619",
+ [":kite:"] = "\U0001fa81",
+ [":kiwi:"] = "\U0001f95d",
+ [":kiwifruit:"] = "\U0001f95d",
+ [":knife:"] = "\U0001f52a",
+ [":knot:"] = "\U0001faa2",
+ [":koala:"] = "\U0001f428",
+ [":koko:"] = "\U0001f201",
+ [":lab_coat:"] = "\U0001f97c",
+ [":label:"] = "\U0001f3f7\ufe0f",
+ [":lacrosse:"] = "\U0001f94d",
+ [":ladder:"] = "\U0001fa9c",
+ [":lady_beetle:"] = "\U0001f41e",
+ [":large_blue_diamond:"] = "\U0001f537",
+ [":large_orange_diamond:"] = "\U0001f536",
+ [":last_quarter_moon:"] = "\U0001f317",
+ [":last_quarter_moon_with_face:"] = "\U0001f31c",
+ [":laughing:"] = "\U0001f606",
+ [":satisfied:"] = "\U0001f606",
+ ["x-)"] = "\U0001f606",
+ ["X-)"] = "\U0001f606",
+ [":leafy_green:"] = "\U0001f96c",
+ [":leaves:"] = "\U0001f343",
+ [":ledger:"] = "\U0001f4d2",
+ [":left_facing_fist:"] = "\U0001f91b",
+ [":left_fist:"] = "\U0001f91b",
+ [":left_facing_fist_tone1:"] = "\U0001f91b\U0001f3fb",
+ [":left_fist_tone1:"] = "\U0001f91b\U0001f3fb",
+ [":left_facing_fist::skin-tone-1:"] = "\U0001f91b\U0001f3fb",
+ [":left_fist::skin-tone-1:"] = "\U0001f91b\U0001f3fb",
+ [":left_facing_fist_tone2:"] = "\U0001f91b\U0001f3fc",
+ [":left_fist_tone2:"] = "\U0001f91b\U0001f3fc",
+ [":left_facing_fist::skin-tone-2:"] = "\U0001f91b\U0001f3fc",
+ [":left_fist::skin-tone-2:"] = "\U0001f91b\U0001f3fc",
+ [":left_facing_fist_tone3:"] = "\U0001f91b\U0001f3fd",
+ [":left_fist_tone3:"] = "\U0001f91b\U0001f3fd",
+ [":left_facing_fist::skin-tone-3:"] = "\U0001f91b\U0001f3fd",
+ [":left_fist::skin-tone-3:"] = "\U0001f91b\U0001f3fd",
+ [":left_facing_fist_tone4:"] = "\U0001f91b\U0001f3fe",
+ [":left_fist_tone4:"] = "\U0001f91b\U0001f3fe",
+ [":left_facing_fist::skin-tone-4:"] = "\U0001f91b\U0001f3fe",
+ [":left_fist::skin-tone-4:"] = "\U0001f91b\U0001f3fe",
+ [":left_facing_fist_tone5:"] = "\U0001f91b\U0001f3ff",
+ [":left_fist_tone5:"] = "\U0001f91b\U0001f3ff",
+ [":left_facing_fist::skin-tone-5:"] = "\U0001f91b\U0001f3ff",
+ [":left_fist::skin-tone-5:"] = "\U0001f91b\U0001f3ff",
+ [":left_luggage:"] = "\U0001f6c5",
+ [":left_right_arrow:"] = "\u2194\ufe0f",
+ [":leftwards_arrow_with_hook:"] = "\u21a9\ufe0f",
+ [":leg:"] = "\U0001f9b5",
+ [":leg_tone1:"] = "\U0001f9b5\U0001f3fb",
+ [":leg_light_skin_tone:"] = "\U0001f9b5\U0001f3fb",
+ [":leg::skin-tone-1:"] = "\U0001f9b5\U0001f3fb",
+ [":leg_tone2:"] = "\U0001f9b5\U0001f3fc",
+ [":leg_medium_light_skin_tone:"] = "\U0001f9b5\U0001f3fc",
+ [":leg::skin-tone-2:"] = "\U0001f9b5\U0001f3fc",
+ [":leg_tone3:"] = "\U0001f9b5\U0001f3fd",
+ [":leg_medium_skin_tone:"] = "\U0001f9b5\U0001f3fd",
+ [":leg::skin-tone-3:"] = "\U0001f9b5\U0001f3fd",
+ [":leg_tone4:"] = "\U0001f9b5\U0001f3fe",
+ [":leg_medium_dark_skin_tone:"] = "\U0001f9b5\U0001f3fe",
+ [":leg::skin-tone-4:"] = "\U0001f9b5\U0001f3fe",
+ [":leg_tone5:"] = "\U0001f9b5\U0001f3ff",
+ [":leg_dark_skin_tone:"] = "\U0001f9b5\U0001f3ff",
+ [":leg::skin-tone-5:"] = "\U0001f9b5\U0001f3ff",
+ [":lemon:"] = "\U0001f34b",
+ [":leo:"] = "\u264c",
+ [":leopard:"] = "\U0001f406",
+ [":level_slider:"] = "\U0001f39a\ufe0f",
+ [":levitate:"] = "\U0001f574\ufe0f",
+ [":man_in_business_suit_levitating:"] = "\U0001f574\ufe0f",
+ [":levitate_tone1:"] = "\U0001f574\U0001f3fb",
+ [":man_in_business_suit_levitating_tone1:"] = "\U0001f574\U0001f3fb",
+ [":man_in_business_suit_levitating_light_skin_tone:"] = "\U0001f574\U0001f3fb",
+ [":levitate::skin-tone-1:"] = "\U0001f574\U0001f3fb",
+ [":man_in_business_suit_levitating::skin-tone-1:"] = "\U0001f574\U0001f3fb",
+ [":levitate_tone2:"] = "\U0001f574\U0001f3fc",
+ [":man_in_business_suit_levitating_tone2:"] = "\U0001f574\U0001f3fc",
+ [":man_in_business_suit_levitating_medium_light_skin_tone:"] = "\U0001f574\U0001f3fc",
+ [":levitate::skin-tone-2:"] = "\U0001f574\U0001f3fc",
+ [":man_in_business_suit_levitating::skin-tone-2:"] = "\U0001f574\U0001f3fc",
+ [":levitate_tone3:"] = "\U0001f574\U0001f3fd",
+ [":man_in_business_suit_levitating_tone3:"] = "\U0001f574\U0001f3fd",
+ [":man_in_business_suit_levitating_medium_skin_tone:"] = "\U0001f574\U0001f3fd",
+ [":levitate::skin-tone-3:"] = "\U0001f574\U0001f3fd",
+ [":man_in_business_suit_levitating::skin-tone-3:"] = "\U0001f574\U0001f3fd",
+ [":levitate_tone4:"] = "\U0001f574\U0001f3fe",
+ [":man_in_business_suit_levitating_tone4:"] = "\U0001f574\U0001f3fe",
+ [":man_in_business_suit_levitating_medium_dark_skin_tone:"] = "\U0001f574\U0001f3fe",
+ [":levitate::skin-tone-4:"] = "\U0001f574\U0001f3fe",
+ [":man_in_business_suit_levitating::skin-tone-4:"] = "\U0001f574\U0001f3fe",
+ [":levitate_tone5:"] = "\U0001f574\U0001f3ff",
+ [":man_in_business_suit_levitating_tone5:"] = "\U0001f574\U0001f3ff",
+ [":man_in_business_suit_levitating_dark_skin_tone:"] = "\U0001f574\U0001f3ff",
+ [":levitate::skin-tone-5:"] = "\U0001f574\U0001f3ff",
+ [":man_in_business_suit_levitating::skin-tone-5:"] = "\U0001f574\U0001f3ff",
+ [":libra:"] = "\u264e",
+ [":light_rail:"] = "\U0001f688",
+ [":link:"] = "\U0001f517",
+ [":lion_face:"] = "\U0001f981",
+ [":lion:"] = "\U0001f981",
+ [":lips:"] = "\U0001f444",
+ [":lipstick:"] = "\U0001f484",
+ [":lizard:"] = "\U0001f98e",
+ [":llama:"] = "\U0001f999",
+ [":lobster:"] = "\U0001f99e",
+ [":lock:"] = "\U0001f512",
+ [":lock_with_ink_pen:"] = "\U0001f50f",
+ [":lollipop:"] = "\U0001f36d",
+ [":long_drum:"] = "\U0001fa98",
+ [":loop:"] = "\u27bf",
+ [":loud_sound:"] = "\U0001f50a",
+ [":loudspeaker:"] = "\U0001f4e2",
+ [":love_hotel:"] = "\U0001f3e9",
+ [":love_letter:"] = "\U0001f48c",
+ [":love_you_gesture:"] = "\U0001f91f",
+ [":love_you_gesture_tone1:"] = "\U0001f91f\U0001f3fb",
+ [":love_you_gesture_light_skin_tone:"] = "\U0001f91f\U0001f3fb",
+ [":love_you_gesture::skin-tone-1:"] = "\U0001f91f\U0001f3fb",
+ [":love_you_gesture_tone2:"] = "\U0001f91f\U0001f3fc",
+ [":love_you_gesture_medium_light_skin_tone:"] = "\U0001f91f\U0001f3fc",
+ [":love_you_gesture::skin-tone-2:"] = "\U0001f91f\U0001f3fc",
+ [":love_you_gesture_tone3:"] = "\U0001f91f\U0001f3fd",
+ [":love_you_gesture_medium_skin_tone:"] = "\U0001f91f\U0001f3fd",
+ [":love_you_gesture::skin-tone-3:"] = "\U0001f91f\U0001f3fd",
+ [":love_you_gesture_tone4:"] = "\U0001f91f\U0001f3fe",
+ [":love_you_gesture_medium_dark_skin_tone:"] = "\U0001f91f\U0001f3fe",
+ [":love_you_gesture::skin-tone-4:"] = "\U0001f91f\U0001f3fe",
+ [":love_you_gesture_tone5:"] = "\U0001f91f\U0001f3ff",
+ [":love_you_gesture_dark_skin_tone:"] = "\U0001f91f\U0001f3ff",
+ [":love_you_gesture::skin-tone-5:"] = "\U0001f91f\U0001f3ff",
+ [":low_brightness:"] = "\U0001f505",
+ [":luggage:"] = "\U0001f9f3",
+ [":lungs:"] = "\U0001fac1",
+ [":lying_face:"] = "\U0001f925",
+ [":liar:"] = "\U0001f925",
+ [":m:"] = "\u24c2\ufe0f",
+ [":mag:"] = "\U0001f50d",
+ [":mag_right:"] = "\U0001f50e",
+ [":mage:"] = "\U0001f9d9",
+ [":mage_tone1:"] = "\U0001f9d9\U0001f3fb",
+ [":mage_light_skin_tone:"] = "\U0001f9d9\U0001f3fb",
+ [":mage::skin-tone-1:"] = "\U0001f9d9\U0001f3fb",
+ [":mage_tone2:"] = "\U0001f9d9\U0001f3fc",
+ [":mage_medium_light_skin_tone:"] = "\U0001f9d9\U0001f3fc",
+ [":mage::skin-tone-2:"] = "\U0001f9d9\U0001f3fc",
+ [":mage_tone3:"] = "\U0001f9d9\U0001f3fd",
+ [":mage_medium_skin_tone:"] = "\U0001f9d9\U0001f3fd",
+ [":mage::skin-tone-3:"] = "\U0001f9d9\U0001f3fd",
+ [":mage_tone4:"] = "\U0001f9d9\U0001f3fe",
+ [":mage_medium_dark_skin_tone:"] = "\U0001f9d9\U0001f3fe",
+ [":mage::skin-tone-4:"] = "\U0001f9d9\U0001f3fe",
+ [":mage_tone5:"] = "\U0001f9d9\U0001f3ff",
+ [":mage_dark_skin_tone:"] = "\U0001f9d9\U0001f3ff",
+ [":mage::skin-tone-5:"] = "\U0001f9d9\U0001f3ff",
+ [":magic_wand:"] = "\U0001fa84",
+ [":magnet:"] = "\U0001f9f2",
+ [":mahjong:"] = "\U0001f004",
+ [":mailbox:"] = "\U0001f4eb",
+ [":mailbox_closed:"] = "\U0001f4ea",
+ [":mailbox_with_mail:"] = "\U0001f4ec",
+ [":mailbox_with_no_mail:"] = "\U0001f4ed",
+ [":male_sign:"] = "\u2642\ufe0f",
+ [":mammoth:"] = "\U0001f9a3",
+ [":man:"] = "\U0001f468",
+ [":man_artist:"] = "\U0001f468\u200d\U0001f3a8",
+ [":man_artist_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a8",
+ [":man_artist_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a8",
+ [":man_artist::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a8",
+ [":man_artist_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a8",
+ [":man_artist_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a8",
+ [":man_artist::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a8",
+ [":man_artist_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a8",
+ [":man_artist_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a8",
+ [":man_artist::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a8",
+ [":man_artist_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a8",
+ [":man_artist_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a8",
+ [":man_artist::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a8",
+ [":man_artist_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a8",
+ [":man_artist_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a8",
+ [":man_artist::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a8",
+ [":man_astronaut:"] = "\U0001f468\u200d\U0001f680",
+ [":man_astronaut_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f680",
+ [":man_astronaut_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f680",
+ [":man_astronaut::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f680",
+ [":man_astronaut_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f680",
+ [":man_astronaut_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f680",
+ [":man_astronaut::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f680",
+ [":man_astronaut_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f680",
+ [":man_astronaut_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f680",
+ [":man_astronaut::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f680",
+ [":man_astronaut_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f680",
+ [":man_astronaut_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f680",
+ [":man_astronaut::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f680",
+ [":man_astronaut_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f680",
+ [":man_astronaut_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f680",
+ [":man_astronaut::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f680",
+ [":man_bald:"] = "\U0001f468\u200d\U0001f9b2",
+ [":man_bald_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b2",
+ [":man_bald_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b2",
+ [":man_bald::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b2",
+ [":man_bald_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b2",
+ [":man_bald_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b2",
+ [":man_bald::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b2",
+ [":man_bald_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b2",
+ [":man_bald_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b2",
+ [":man_bald::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b2",
+ [":man_bald_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b2",
+ [":man_bald_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b2",
+ [":man_bald::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b2",
+ [":man_bald_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b2",
+ [":man_bald_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b2",
+ [":man_bald::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b2",
+ [":man_biking:"] = "\U0001f6b4\u200d\u2642\ufe0f",
+ [":man_biking_tone1:"] = "\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_biking_light_skin_tone:"] = "\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_biking::skin-tone-1:"] = "\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_biking_tone2:"] = "\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_biking_medium_light_skin_tone:"] = "\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_biking::skin-tone-2:"] = "\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_biking_tone3:"] = "\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_biking_medium_skin_tone:"] = "\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_biking::skin-tone-3:"] = "\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_biking_tone4:"] = "\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_biking_medium_dark_skin_tone:"] = "\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_biking::skin-tone-4:"] = "\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_biking_tone5:"] = "\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_biking_dark_skin_tone:"] = "\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_biking::skin-tone-5:"] = "\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_bouncing_ball:"] = "\u26f9\ufe0f\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_tone1:"] = "\u26f9\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_light_skin_tone:"] = "\u26f9\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_bouncing_ball::skin-tone-1:"] = "\u26f9\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_tone2:"] = "\u26f9\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_medium_light_skin_tone:"] = "\u26f9\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_bouncing_ball::skin-tone-2:"] = "\u26f9\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_tone3:"] = "\u26f9\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_medium_skin_tone:"] = "\u26f9\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_bouncing_ball::skin-tone-3:"] = "\u26f9\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_tone4:"] = "\u26f9\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_medium_dark_skin_tone:"] = "\u26f9\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_bouncing_ball::skin-tone-4:"] = "\u26f9\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_tone5:"] = "\u26f9\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_bouncing_ball_dark_skin_tone:"] = "\u26f9\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_bouncing_ball::skin-tone-5:"] = "\u26f9\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_bowing:"] = "\U0001f647\u200d\u2642\ufe0f",
+ [":man_bowing_tone1:"] = "\U0001f647\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_bowing_light_skin_tone:"] = "\U0001f647\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_bowing::skin-tone-1:"] = "\U0001f647\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_bowing_tone2:"] = "\U0001f647\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_bowing_medium_light_skin_tone:"] = "\U0001f647\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_bowing::skin-tone-2:"] = "\U0001f647\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_bowing_tone3:"] = "\U0001f647\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_bowing_medium_skin_tone:"] = "\U0001f647\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_bowing::skin-tone-3:"] = "\U0001f647\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_bowing_tone4:"] = "\U0001f647\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_bowing_medium_dark_skin_tone:"] = "\U0001f647\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_bowing::skin-tone-4:"] = "\U0001f647\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_bowing_tone5:"] = "\U0001f647\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_bowing_dark_skin_tone:"] = "\U0001f647\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_bowing::skin-tone-5:"] = "\U0001f647\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_cartwheeling:"] = "\U0001f938\u200d\u2642\ufe0f",
+ [":man_cartwheeling_tone1:"] = "\U0001f938\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_cartwheeling_light_skin_tone:"] = "\U0001f938\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_cartwheeling::skin-tone-1:"] = "\U0001f938\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_cartwheeling_tone2:"] = "\U0001f938\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_cartwheeling_medium_light_skin_tone:"] = "\U0001f938\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_cartwheeling::skin-tone-2:"] = "\U0001f938\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_cartwheeling_tone3:"] = "\U0001f938\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_cartwheeling_medium_skin_tone:"] = "\U0001f938\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_cartwheeling::skin-tone-3:"] = "\U0001f938\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_cartwheeling_tone4:"] = "\U0001f938\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_cartwheeling_medium_dark_skin_tone:"] = "\U0001f938\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_cartwheeling::skin-tone-4:"] = "\U0001f938\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_cartwheeling_tone5:"] = "\U0001f938\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_cartwheeling_dark_skin_tone:"] = "\U0001f938\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_cartwheeling::skin-tone-5:"] = "\U0001f938\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_climbing:"] = "\U0001f9d7\u200d\u2642\ufe0f",
+ [":man_climbing_tone1:"] = "\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_climbing_light_skin_tone:"] = "\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_climbing::skin-tone-1:"] = "\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_climbing_tone2:"] = "\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_climbing_medium_light_skin_tone:"] = "\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_climbing::skin-tone-2:"] = "\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_climbing_tone3:"] = "\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_climbing_medium_skin_tone:"] = "\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_climbing::skin-tone-3:"] = "\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_climbing_tone4:"] = "\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_climbing_medium_dark_skin_tone:"] = "\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_climbing::skin-tone-4:"] = "\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_climbing_tone5:"] = "\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_climbing_dark_skin_tone:"] = "\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_climbing::skin-tone-5:"] = "\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_construction_worker:"] = "\U0001f477\u200d\u2642\ufe0f",
+ [":man_construction_worker_tone1:"] = "\U0001f477\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_construction_worker_light_skin_tone:"] = "\U0001f477\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_construction_worker::skin-tone-1:"] = "\U0001f477\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_construction_worker_tone2:"] = "\U0001f477\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_construction_worker_medium_light_skin_tone:"] = "\U0001f477\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_construction_worker::skin-tone-2:"] = "\U0001f477\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_construction_worker_tone3:"] = "\U0001f477\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_construction_worker_medium_skin_tone:"] = "\U0001f477\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_construction_worker::skin-tone-3:"] = "\U0001f477\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_construction_worker_tone4:"] = "\U0001f477\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_construction_worker_medium_dark_skin_tone:"] = "\U0001f477\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_construction_worker::skin-tone-4:"] = "\U0001f477\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_construction_worker_tone5:"] = "\U0001f477\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_construction_worker_dark_skin_tone:"] = "\U0001f477\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_construction_worker::skin-tone-5:"] = "\U0001f477\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_cook:"] = "\U0001f468\u200d\U0001f373",
+ [":man_cook_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f373",
+ [":man_cook_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f373",
+ [":man_cook::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f373",
+ [":man_cook_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f373",
+ [":man_cook_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f373",
+ [":man_cook::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f373",
+ [":man_cook_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f373",
+ [":man_cook_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f373",
+ [":man_cook::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f373",
+ [":man_cook_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f373",
+ [":man_cook_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f373",
+ [":man_cook::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f373",
+ [":man_cook_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f373",
+ [":man_cook_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f373",
+ [":man_cook::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f373",
+ [":man_curly_haired:"] = "\U0001f468\u200d\U0001f9b1",
+ [":man_curly_haired_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b1",
+ [":man_curly_haired_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b1",
+ [":man_curly_haired::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b1",
+ [":man_curly_haired_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b1",
+ [":man_curly_haired_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b1",
+ [":man_curly_haired::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b1",
+ [":man_curly_haired_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b1",
+ [":man_curly_haired_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b1",
+ [":man_curly_haired::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b1",
+ [":man_curly_haired_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b1",
+ [":man_curly_haired_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b1",
+ [":man_curly_haired::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b1",
+ [":man_curly_haired_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b1",
+ [":man_curly_haired_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b1",
+ [":man_curly_haired::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b1",
+ [":man_dancing:"] = "\U0001f57a",
+ [":male_dancer:"] = "\U0001f57a",
+ [":man_dancing_tone1:"] = "\U0001f57a\U0001f3fb",
+ [":male_dancer_tone1:"] = "\U0001f57a\U0001f3fb",
+ [":man_dancing::skin-tone-1:"] = "\U0001f57a\U0001f3fb",
+ [":male_dancer::skin-tone-1:"] = "\U0001f57a\U0001f3fb",
+ [":man_dancing_tone2:"] = "\U0001f57a\U0001f3fc",
+ [":male_dancer_tone2:"] = "\U0001f57a\U0001f3fc",
+ [":man_dancing::skin-tone-2:"] = "\U0001f57a\U0001f3fc",
+ [":male_dancer::skin-tone-2:"] = "\U0001f57a\U0001f3fc",
+ [":man_dancing_tone3:"] = "\U0001f57a\U0001f3fd",
+ [":male_dancer_tone3:"] = "\U0001f57a\U0001f3fd",
+ [":man_dancing::skin-tone-3:"] = "\U0001f57a\U0001f3fd",
+ [":male_dancer::skin-tone-3:"] = "\U0001f57a\U0001f3fd",
+ [":man_dancing_tone4:"] = "\U0001f57a\U0001f3fe",
+ [":male_dancer_tone4:"] = "\U0001f57a\U0001f3fe",
+ [":man_dancing::skin-tone-4:"] = "\U0001f57a\U0001f3fe",
+ [":male_dancer::skin-tone-4:"] = "\U0001f57a\U0001f3fe",
+ [":man_dancing_tone5:"] = "\U0001f57a\U0001f3ff",
+ [":male_dancer_tone5:"] = "\U0001f57a\U0001f3ff",
+ [":man_dancing::skin-tone-5:"] = "\U0001f57a\U0001f3ff",
+ [":male_dancer::skin-tone-5:"] = "\U0001f57a\U0001f3ff",
+ [":man_detective:"] = "\U0001f575\ufe0f\u200d\u2642\ufe0f",
+ [":man_detective_tone1:"] = "\U0001f575\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_detective_light_skin_tone:"] = "\U0001f575\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_detective::skin-tone-1:"] = "\U0001f575\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_detective_tone2:"] = "\U0001f575\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_detective_medium_light_skin_tone:"] = "\U0001f575\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_detective::skin-tone-2:"] = "\U0001f575\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_detective_tone3:"] = "\U0001f575\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_detective_medium_skin_tone:"] = "\U0001f575\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_detective::skin-tone-3:"] = "\U0001f575\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_detective_tone4:"] = "\U0001f575\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_detective_medium_dark_skin_tone:"] = "\U0001f575\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_detective::skin-tone-4:"] = "\U0001f575\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_detective_tone5:"] = "\U0001f575\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_detective_dark_skin_tone:"] = "\U0001f575\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_detective::skin-tone-5:"] = "\U0001f575\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_elf:"] = "\U0001f9dd\u200d\u2642\ufe0f",
+ [":man_elf_tone1:"] = "\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_elf_light_skin_tone:"] = "\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_elf::skin-tone-1:"] = "\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_elf_tone2:"] = "\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_elf_medium_light_skin_tone:"] = "\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_elf::skin-tone-2:"] = "\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_elf_tone3:"] = "\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_elf_medium_skin_tone:"] = "\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_elf::skin-tone-3:"] = "\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_elf_tone4:"] = "\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_elf_medium_dark_skin_tone:"] = "\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_elf::skin-tone-4:"] = "\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_elf_tone5:"] = "\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_elf_dark_skin_tone:"] = "\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_elf::skin-tone-5:"] = "\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_facepalming:"] = "\U0001f926\u200d\u2642\ufe0f",
+ [":man_facepalming_tone1:"] = "\U0001f926\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_facepalming_light_skin_tone:"] = "\U0001f926\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_facepalming::skin-tone-1:"] = "\U0001f926\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_facepalming_tone2:"] = "\U0001f926\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_facepalming_medium_light_skin_tone:"] = "\U0001f926\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_facepalming::skin-tone-2:"] = "\U0001f926\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_facepalming_tone3:"] = "\U0001f926\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_facepalming_medium_skin_tone:"] = "\U0001f926\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_facepalming::skin-tone-3:"] = "\U0001f926\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_facepalming_tone4:"] = "\U0001f926\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_facepalming_medium_dark_skin_tone:"] = "\U0001f926\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_facepalming::skin-tone-4:"] = "\U0001f926\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_facepalming_tone5:"] = "\U0001f926\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_facepalming_dark_skin_tone:"] = "\U0001f926\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_facepalming::skin-tone-5:"] = "\U0001f926\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_factory_worker:"] = "\U0001f468\u200d\U0001f3ed",
+ [":man_factory_worker_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3ed",
+ [":man_factory_worker_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f3ed",
+ [":man_factory_worker::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3ed",
+ [":man_factory_worker_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3ed",
+ [":man_factory_worker_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f3ed",
+ [":man_factory_worker::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3ed",
+ [":man_factory_worker_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3ed",
+ [":man_factory_worker_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f3ed",
+ [":man_factory_worker::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3ed",
+ [":man_factory_worker_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3ed",
+ [":man_factory_worker_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f3ed",
+ [":man_factory_worker::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3ed",
+ [":man_factory_worker_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3ed",
+ [":man_factory_worker_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f3ed",
+ [":man_factory_worker::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3ed",
+ [":man_fairy:"] = "\U0001f9da\u200d\u2642\ufe0f",
+ [":man_fairy_tone1:"] = "\U0001f9da\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_fairy_light_skin_tone:"] = "\U0001f9da\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_fairy::skin-tone-1:"] = "\U0001f9da\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_fairy_tone2:"] = "\U0001f9da\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_fairy_medium_light_skin_tone:"] = "\U0001f9da\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_fairy::skin-tone-2:"] = "\U0001f9da\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_fairy_tone3:"] = "\U0001f9da\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_fairy_medium_skin_tone:"] = "\U0001f9da\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_fairy::skin-tone-3:"] = "\U0001f9da\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_fairy_tone4:"] = "\U0001f9da\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_fairy_medium_dark_skin_tone:"] = "\U0001f9da\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_fairy::skin-tone-4:"] = "\U0001f9da\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_fairy_tone5:"] = "\U0001f9da\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_fairy_dark_skin_tone:"] = "\U0001f9da\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_fairy::skin-tone-5:"] = "\U0001f9da\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_farmer:"] = "\U0001f468\u200d\U0001f33e",
+ [":man_farmer_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f33e",
+ [":man_farmer_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f33e",
+ [":man_farmer::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f33e",
+ [":man_farmer_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f33e",
+ [":man_farmer_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f33e",
+ [":man_farmer::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f33e",
+ [":man_farmer_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f33e",
+ [":man_farmer_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f33e",
+ [":man_farmer::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f33e",
+ [":man_farmer_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f33e",
+ [":man_farmer_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f33e",
+ [":man_farmer::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f33e",
+ [":man_farmer_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f33e",
+ [":man_farmer_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f33e",
+ [":man_farmer::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f33e",
+ [":man_feeding_baby:"] = "\U0001f468\u200d\U0001f37c",
+ [":man_feeding_baby_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f37c",
+ [":man_feeding_baby_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f37c",
+ [":man_feeding_baby::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f37c",
+ [":man_feeding_baby_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f37c",
+ [":man_feeding_baby_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f37c",
+ [":man_feeding_baby::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f37c",
+ [":man_feeding_baby_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f37c",
+ [":man_feeding_baby_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f37c",
+ [":man_feeding_baby::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f37c",
+ [":man_feeding_baby_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f37c",
+ [":man_feeding_baby_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f37c",
+ [":man_feeding_baby::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f37c",
+ [":man_feeding_baby_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f37c",
+ [":man_feeding_baby_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f37c",
+ [":man_feeding_baby::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f37c",
+ [":man_firefighter:"] = "\U0001f468\u200d\U0001f692",
+ [":man_firefighter_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f692",
+ [":man_firefighter_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f692",
+ [":man_firefighter::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f692",
+ [":man_firefighter_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f692",
+ [":man_firefighter_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f692",
+ [":man_firefighter::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f692",
+ [":man_firefighter_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f692",
+ [":man_firefighter_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f692",
+ [":man_firefighter::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f692",
+ [":man_firefighter_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f692",
+ [":man_firefighter_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f692",
+ [":man_firefighter::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f692",
+ [":man_firefighter_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f692",
+ [":man_firefighter_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f692",
+ [":man_firefighter::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f692",
+ [":man_frowning:"] = "\U0001f64d\u200d\u2642\ufe0f",
+ [":man_frowning_tone1:"] = "\U0001f64d\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_frowning_light_skin_tone:"] = "\U0001f64d\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_frowning::skin-tone-1:"] = "\U0001f64d\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_frowning_tone2:"] = "\U0001f64d\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_frowning_medium_light_skin_tone:"] = "\U0001f64d\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_frowning::skin-tone-2:"] = "\U0001f64d\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_frowning_tone3:"] = "\U0001f64d\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_frowning_medium_skin_tone:"] = "\U0001f64d\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_frowning::skin-tone-3:"] = "\U0001f64d\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_frowning_tone4:"] = "\U0001f64d\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_frowning_medium_dark_skin_tone:"] = "\U0001f64d\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_frowning::skin-tone-4:"] = "\U0001f64d\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_frowning_tone5:"] = "\U0001f64d\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_frowning_dark_skin_tone:"] = "\U0001f64d\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_frowning::skin-tone-5:"] = "\U0001f64d\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_genie:"] = "\U0001f9de\u200d\u2642\ufe0f",
+ [":man_gesturing_no:"] = "\U0001f645\u200d\u2642\ufe0f",
+ [":man_gesturing_no_tone1:"] = "\U0001f645\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_gesturing_no_light_skin_tone:"] = "\U0001f645\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_gesturing_no::skin-tone-1:"] = "\U0001f645\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_gesturing_no_tone2:"] = "\U0001f645\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_gesturing_no_medium_light_skin_tone:"] = "\U0001f645\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_gesturing_no::skin-tone-2:"] = "\U0001f645\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_gesturing_no_tone3:"] = "\U0001f645\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_gesturing_no_medium_skin_tone:"] = "\U0001f645\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_gesturing_no::skin-tone-3:"] = "\U0001f645\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_gesturing_no_tone4:"] = "\U0001f645\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_gesturing_no_medium_dark_skin_tone:"] = "\U0001f645\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_gesturing_no::skin-tone-4:"] = "\U0001f645\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_gesturing_no_tone5:"] = "\U0001f645\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_gesturing_no_dark_skin_tone:"] = "\U0001f645\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_gesturing_no::skin-tone-5:"] = "\U0001f645\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_gesturing_ok:"] = "\U0001f646\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_tone1:"] = "\U0001f646\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_light_skin_tone:"] = "\U0001f646\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_gesturing_ok::skin-tone-1:"] = "\U0001f646\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_tone2:"] = "\U0001f646\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_medium_light_skin_tone:"] = "\U0001f646\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_gesturing_ok::skin-tone-2:"] = "\U0001f646\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_tone3:"] = "\U0001f646\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_medium_skin_tone:"] = "\U0001f646\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_gesturing_ok::skin-tone-3:"] = "\U0001f646\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_tone4:"] = "\U0001f646\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_medium_dark_skin_tone:"] = "\U0001f646\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_gesturing_ok::skin-tone-4:"] = "\U0001f646\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_tone5:"] = "\U0001f646\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_gesturing_ok_dark_skin_tone:"] = "\U0001f646\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_gesturing_ok::skin-tone-5:"] = "\U0001f646\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_getting_face_massage:"] = "\U0001f486\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_tone1:"] = "\U0001f486\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_light_skin_tone:"] = "\U0001f486\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_getting_face_massage::skin-tone-1:"] = "\U0001f486\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_tone2:"] = "\U0001f486\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_medium_light_skin_tone:"] = "\U0001f486\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_getting_face_massage::skin-tone-2:"] = "\U0001f486\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_tone3:"] = "\U0001f486\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_medium_skin_tone:"] = "\U0001f486\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_getting_face_massage::skin-tone-3:"] = "\U0001f486\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_tone4:"] = "\U0001f486\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_medium_dark_skin_tone:"] = "\U0001f486\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_getting_face_massage::skin-tone-4:"] = "\U0001f486\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_tone5:"] = "\U0001f486\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_getting_face_massage_dark_skin_tone:"] = "\U0001f486\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_getting_face_massage::skin-tone-5:"] = "\U0001f486\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_getting_haircut:"] = "\U0001f487\u200d\u2642\ufe0f",
+ [":man_getting_haircut_tone1:"] = "\U0001f487\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_getting_haircut_light_skin_tone:"] = "\U0001f487\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_getting_haircut::skin-tone-1:"] = "\U0001f487\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_getting_haircut_tone2:"] = "\U0001f487\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_getting_haircut_medium_light_skin_tone:"] = "\U0001f487\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_getting_haircut::skin-tone-2:"] = "\U0001f487\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_getting_haircut_tone3:"] = "\U0001f487\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_getting_haircut_medium_skin_tone:"] = "\U0001f487\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_getting_haircut::skin-tone-3:"] = "\U0001f487\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_getting_haircut_tone4:"] = "\U0001f487\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_getting_haircut_medium_dark_skin_tone:"] = "\U0001f487\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_getting_haircut::skin-tone-4:"] = "\U0001f487\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_getting_haircut_tone5:"] = "\U0001f487\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_getting_haircut_dark_skin_tone:"] = "\U0001f487\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_getting_haircut::skin-tone-5:"] = "\U0001f487\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_golfing:"] = "\U0001f3cc\ufe0f\u200d\u2642\ufe0f",
+ [":man_golfing_tone1:"] = "\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_golfing_light_skin_tone:"] = "\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_golfing::skin-tone-1:"] = "\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_golfing_tone2:"] = "\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_golfing_medium_light_skin_tone:"] = "\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_golfing::skin-tone-2:"] = "\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_golfing_tone3:"] = "\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_golfing_medium_skin_tone:"] = "\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_golfing::skin-tone-3:"] = "\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_golfing_tone4:"] = "\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_golfing_medium_dark_skin_tone:"] = "\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_golfing::skin-tone-4:"] = "\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_golfing_tone5:"] = "\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_golfing_dark_skin_tone:"] = "\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_golfing::skin-tone-5:"] = "\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_guard:"] = "\U0001f482\u200d\u2642\ufe0f",
+ [":man_guard_tone1:"] = "\U0001f482\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_guard_light_skin_tone:"] = "\U0001f482\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_guard::skin-tone-1:"] = "\U0001f482\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_guard_tone2:"] = "\U0001f482\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_guard_medium_light_skin_tone:"] = "\U0001f482\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_guard::skin-tone-2:"] = "\U0001f482\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_guard_tone3:"] = "\U0001f482\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_guard_medium_skin_tone:"] = "\U0001f482\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_guard::skin-tone-3:"] = "\U0001f482\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_guard_tone4:"] = "\U0001f482\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_guard_medium_dark_skin_tone:"] = "\U0001f482\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_guard::skin-tone-4:"] = "\U0001f482\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_guard_tone5:"] = "\U0001f482\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_guard_dark_skin_tone:"] = "\U0001f482\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_guard::skin-tone-5:"] = "\U0001f482\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_health_worker:"] = "\U0001f468\u200d\u2695\ufe0f",
+ [":man_health_worker_tone1:"] = "\U0001f468\U0001f3fb\u200d\u2695\ufe0f",
+ [":man_health_worker_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\u2695\ufe0f",
+ [":man_health_worker::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\u2695\ufe0f",
+ [":man_health_worker_tone2:"] = "\U0001f468\U0001f3fc\u200d\u2695\ufe0f",
+ [":man_health_worker_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\u2695\ufe0f",
+ [":man_health_worker::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\u2695\ufe0f",
+ [":man_health_worker_tone3:"] = "\U0001f468\U0001f3fd\u200d\u2695\ufe0f",
+ [":man_health_worker_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\u2695\ufe0f",
+ [":man_health_worker::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\u2695\ufe0f",
+ [":man_health_worker_tone4:"] = "\U0001f468\U0001f3fe\u200d\u2695\ufe0f",
+ [":man_health_worker_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\u2695\ufe0f",
+ [":man_health_worker::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\u2695\ufe0f",
+ [":man_health_worker_tone5:"] = "\U0001f468\U0001f3ff\u200d\u2695\ufe0f",
+ [":man_health_worker_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\u2695\ufe0f",
+ [":man_health_worker::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\u2695\ufe0f",
+ [":man_in_lotus_position:"] = "\U0001f9d8\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_tone1:"] = "\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_light_skin_tone:"] = "\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_in_lotus_position::skin-tone-1:"] = "\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_tone2:"] = "\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_medium_light_skin_tone:"] = "\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_in_lotus_position::skin-tone-2:"] = "\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_tone3:"] = "\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_medium_skin_tone:"] = "\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_in_lotus_position::skin-tone-3:"] = "\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_tone4:"] = "\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_medium_dark_skin_tone:"] = "\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_in_lotus_position::skin-tone-4:"] = "\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_tone5:"] = "\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_in_lotus_position_dark_skin_tone:"] = "\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_in_lotus_position::skin-tone-5:"] = "\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_in_manual_wheelchair:"] = "\U0001f468\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bd",
+ [":man_in_manual_wheelchair::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bd",
+ [":man_in_motorized_wheelchair:"] = "\U0001f468\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bc",
+ [":man_in_motorized_wheelchair::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9bc",
+ [":man_in_steamy_room:"] = "\U0001f9d6\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_tone1:"] = "\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_light_skin_tone:"] = "\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_in_steamy_room::skin-tone-1:"] = "\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_tone2:"] = "\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_medium_light_skin_tone:"] = "\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_in_steamy_room::skin-tone-2:"] = "\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_tone3:"] = "\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_medium_skin_tone:"] = "\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_in_steamy_room::skin-tone-3:"] = "\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_tone4:"] = "\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_medium_dark_skin_tone:"] = "\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_in_steamy_room::skin-tone-4:"] = "\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_tone5:"] = "\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_in_steamy_room_dark_skin_tone:"] = "\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_in_steamy_room::skin-tone-5:"] = "\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_in_tuxedo:"] = "\U0001f935\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_tone1:"] = "\U0001f935\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_light_skin_tone:"] = "\U0001f935\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_in_tuxedo::skin-tone-1:"] = "\U0001f935\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_tone2:"] = "\U0001f935\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_medium_light_skin_tone:"] = "\U0001f935\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_in_tuxedo::skin-tone-2:"] = "\U0001f935\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_tone3:"] = "\U0001f935\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_medium_skin_tone:"] = "\U0001f935\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_in_tuxedo::skin-tone-3:"] = "\U0001f935\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_tone4:"] = "\U0001f935\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_medium_dark_skin_tone:"] = "\U0001f935\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_in_tuxedo::skin-tone-4:"] = "\U0001f935\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_tone5:"] = "\U0001f935\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_in_tuxedo_dark_skin_tone:"] = "\U0001f935\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_in_tuxedo::skin-tone-5:"] = "\U0001f935\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_judge:"] = "\U0001f468\u200d\u2696\ufe0f",
+ [":man_judge_tone1:"] = "\U0001f468\U0001f3fb\u200d\u2696\ufe0f",
+ [":man_judge_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\u2696\ufe0f",
+ [":man_judge::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\u2696\ufe0f",
+ [":man_judge_tone2:"] = "\U0001f468\U0001f3fc\u200d\u2696\ufe0f",
+ [":man_judge_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\u2696\ufe0f",
+ [":man_judge::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\u2696\ufe0f",
+ [":man_judge_tone3:"] = "\U0001f468\U0001f3fd\u200d\u2696\ufe0f",
+ [":man_judge_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\u2696\ufe0f",
+ [":man_judge::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\u2696\ufe0f",
+ [":man_judge_tone4:"] = "\U0001f468\U0001f3fe\u200d\u2696\ufe0f",
+ [":man_judge_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\u2696\ufe0f",
+ [":man_judge::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\u2696\ufe0f",
+ [":man_judge_tone5:"] = "\U0001f468\U0001f3ff\u200d\u2696\ufe0f",
+ [":man_judge_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\u2696\ufe0f",
+ [":man_judge::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\u2696\ufe0f",
+ [":man_juggling:"] = "\U0001f939\u200d\u2642\ufe0f",
+ [":man_juggling_tone1:"] = "\U0001f939\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_juggling_light_skin_tone:"] = "\U0001f939\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_juggling::skin-tone-1:"] = "\U0001f939\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_juggling_tone2:"] = "\U0001f939\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_juggling_medium_light_skin_tone:"] = "\U0001f939\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_juggling::skin-tone-2:"] = "\U0001f939\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_juggling_tone3:"] = "\U0001f939\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_juggling_medium_skin_tone:"] = "\U0001f939\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_juggling::skin-tone-3:"] = "\U0001f939\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_juggling_tone4:"] = "\U0001f939\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_juggling_medium_dark_skin_tone:"] = "\U0001f939\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_juggling::skin-tone-4:"] = "\U0001f939\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_juggling_tone5:"] = "\U0001f939\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_juggling_dark_skin_tone:"] = "\U0001f939\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_juggling::skin-tone-5:"] = "\U0001f939\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_kneeling:"] = "\U0001f9ce\u200d\u2642\ufe0f",
+ [":man_kneeling_tone1:"] = "\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_kneeling_light_skin_tone:"] = "\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_kneeling::skin-tone-1:"] = "\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_kneeling_tone2:"] = "\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_kneeling_medium_light_skin_tone:"] = "\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_kneeling::skin-tone-2:"] = "\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_kneeling_tone3:"] = "\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_kneeling_medium_skin_tone:"] = "\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_kneeling::skin-tone-3:"] = "\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_kneeling_tone4:"] = "\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_kneeling_medium_dark_skin_tone:"] = "\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_kneeling::skin-tone-4:"] = "\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_kneeling_tone5:"] = "\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_kneeling_dark_skin_tone:"] = "\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_kneeling::skin-tone-5:"] = "\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_lifting_weights:"] = "\U0001f3cb\ufe0f\u200d\u2642\ufe0f",
+ [":man_lifting_weights_tone1:"] = "\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_lifting_weights_light_skin_tone:"] = "\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_lifting_weights::skin-tone-1:"] = "\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_lifting_weights_tone2:"] = "\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_lifting_weights_medium_light_skin_tone:"] = "\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_lifting_weights::skin-tone-2:"] = "\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_lifting_weights_tone3:"] = "\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_lifting_weights_medium_skin_tone:"] = "\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_lifting_weights::skin-tone-3:"] = "\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_lifting_weights_tone4:"] = "\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_lifting_weights_medium_dark_skin_tone:"] = "\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_lifting_weights::skin-tone-4:"] = "\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_lifting_weights_tone5:"] = "\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_lifting_weights_dark_skin_tone:"] = "\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_lifting_weights::skin-tone-5:"] = "\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_mage:"] = "\U0001f9d9\u200d\u2642\ufe0f",
+ [":man_mage_tone1:"] = "\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_mage_light_skin_tone:"] = "\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_mage::skin-tone-1:"] = "\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_mage_tone2:"] = "\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_mage_medium_light_skin_tone:"] = "\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_mage::skin-tone-2:"] = "\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_mage_tone3:"] = "\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_mage_medium_skin_tone:"] = "\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_mage::skin-tone-3:"] = "\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_mage_tone4:"] = "\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_mage_medium_dark_skin_tone:"] = "\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_mage::skin-tone-4:"] = "\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_mage_tone5:"] = "\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_mage_dark_skin_tone:"] = "\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_mage::skin-tone-5:"] = "\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_mechanic:"] = "\U0001f468\u200d\U0001f527",
+ [":man_mechanic_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f527",
+ [":man_mechanic_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f527",
+ [":man_mechanic::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f527",
+ [":man_mechanic_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f527",
+ [":man_mechanic_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f527",
+ [":man_mechanic::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f527",
+ [":man_mechanic_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f527",
+ [":man_mechanic_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f527",
+ [":man_mechanic::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f527",
+ [":man_mechanic_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f527",
+ [":man_mechanic_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f527",
+ [":man_mechanic::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f527",
+ [":man_mechanic_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f527",
+ [":man_mechanic_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f527",
+ [":man_mechanic::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f527",
+ [":man_mountain_biking:"] = "\U0001f6b5\u200d\u2642\ufe0f",
+ [":man_mountain_biking_tone1:"] = "\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_mountain_biking_light_skin_tone:"] = "\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_mountain_biking::skin-tone-1:"] = "\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_mountain_biking_tone2:"] = "\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_mountain_biking_medium_light_skin_tone:"] = "\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_mountain_biking::skin-tone-2:"] = "\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_mountain_biking_tone3:"] = "\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_mountain_biking_medium_skin_tone:"] = "\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_mountain_biking::skin-tone-3:"] = "\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_mountain_biking_tone4:"] = "\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_mountain_biking_medium_dark_skin_tone:"] = "\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_mountain_biking::skin-tone-4:"] = "\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_mountain_biking_tone5:"] = "\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_mountain_biking_dark_skin_tone:"] = "\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_mountain_biking::skin-tone-5:"] = "\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_office_worker:"] = "\U0001f468\u200d\U0001f4bc",
+ [":man_office_worker_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bc",
+ [":man_office_worker_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bc",
+ [":man_office_worker::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bc",
+ [":man_office_worker_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bc",
+ [":man_office_worker_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bc",
+ [":man_office_worker::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bc",
+ [":man_office_worker_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bc",
+ [":man_office_worker_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bc",
+ [":man_office_worker::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bc",
+ [":man_office_worker_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bc",
+ [":man_office_worker_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bc",
+ [":man_office_worker::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bc",
+ [":man_office_worker_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bc",
+ [":man_office_worker_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bc",
+ [":man_office_worker::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bc",
+ [":man_pilot:"] = "\U0001f468\u200d\u2708\ufe0f",
+ [":man_pilot_tone1:"] = "\U0001f468\U0001f3fb\u200d\u2708\ufe0f",
+ [":man_pilot_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\u2708\ufe0f",
+ [":man_pilot::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\u2708\ufe0f",
+ [":man_pilot_tone2:"] = "\U0001f468\U0001f3fc\u200d\u2708\ufe0f",
+ [":man_pilot_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\u2708\ufe0f",
+ [":man_pilot::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\u2708\ufe0f",
+ [":man_pilot_tone3:"] = "\U0001f468\U0001f3fd\u200d\u2708\ufe0f",
+ [":man_pilot_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\u2708\ufe0f",
+ [":man_pilot::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\u2708\ufe0f",
+ [":man_pilot_tone4:"] = "\U0001f468\U0001f3fe\u200d\u2708\ufe0f",
+ [":man_pilot_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\u2708\ufe0f",
+ [":man_pilot::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\u2708\ufe0f",
+ [":man_pilot_tone5:"] = "\U0001f468\U0001f3ff\u200d\u2708\ufe0f",
+ [":man_pilot_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\u2708\ufe0f",
+ [":man_pilot::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\u2708\ufe0f",
+ [":man_playing_handball:"] = "\U0001f93e\u200d\u2642\ufe0f",
+ [":man_playing_handball_tone1:"] = "\U0001f93e\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_playing_handball_light_skin_tone:"] = "\U0001f93e\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_playing_handball::skin-tone-1:"] = "\U0001f93e\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_playing_handball_tone2:"] = "\U0001f93e\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_playing_handball_medium_light_skin_tone:"] = "\U0001f93e\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_playing_handball::skin-tone-2:"] = "\U0001f93e\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_playing_handball_tone3:"] = "\U0001f93e\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_playing_handball_medium_skin_tone:"] = "\U0001f93e\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_playing_handball::skin-tone-3:"] = "\U0001f93e\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_playing_handball_tone4:"] = "\U0001f93e\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_playing_handball_medium_dark_skin_tone:"] = "\U0001f93e\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_playing_handball::skin-tone-4:"] = "\U0001f93e\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_playing_handball_tone5:"] = "\U0001f93e\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_playing_handball_dark_skin_tone:"] = "\U0001f93e\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_playing_handball::skin-tone-5:"] = "\U0001f93e\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_playing_water_polo:"] = "\U0001f93d\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_tone1:"] = "\U0001f93d\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_light_skin_tone:"] = "\U0001f93d\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_playing_water_polo::skin-tone-1:"] = "\U0001f93d\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_tone2:"] = "\U0001f93d\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_medium_light_skin_tone:"] = "\U0001f93d\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_playing_water_polo::skin-tone-2:"] = "\U0001f93d\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_tone3:"] = "\U0001f93d\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_medium_skin_tone:"] = "\U0001f93d\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_playing_water_polo::skin-tone-3:"] = "\U0001f93d\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_tone4:"] = "\U0001f93d\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_medium_dark_skin_tone:"] = "\U0001f93d\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_playing_water_polo::skin-tone-4:"] = "\U0001f93d\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_tone5:"] = "\U0001f93d\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_playing_water_polo_dark_skin_tone:"] = "\U0001f93d\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_playing_water_polo::skin-tone-5:"] = "\U0001f93d\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_police_officer:"] = "\U0001f46e\u200d\u2642\ufe0f",
+ [":man_police_officer_tone1:"] = "\U0001f46e\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_police_officer_light_skin_tone:"] = "\U0001f46e\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_police_officer::skin-tone-1:"] = "\U0001f46e\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_police_officer_tone2:"] = "\U0001f46e\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_police_officer_medium_light_skin_tone:"] = "\U0001f46e\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_police_officer::skin-tone-2:"] = "\U0001f46e\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_police_officer_tone3:"] = "\U0001f46e\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_police_officer_medium_skin_tone:"] = "\U0001f46e\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_police_officer::skin-tone-3:"] = "\U0001f46e\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_police_officer_tone4:"] = "\U0001f46e\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_police_officer_medium_dark_skin_tone:"] = "\U0001f46e\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_police_officer::skin-tone-4:"] = "\U0001f46e\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_police_officer_tone5:"] = "\U0001f46e\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_police_officer_dark_skin_tone:"] = "\U0001f46e\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_police_officer::skin-tone-5:"] = "\U0001f46e\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_pouting:"] = "\U0001f64e\u200d\u2642\ufe0f",
+ [":man_pouting_tone1:"] = "\U0001f64e\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_pouting_light_skin_tone:"] = "\U0001f64e\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_pouting::skin-tone-1:"] = "\U0001f64e\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_pouting_tone2:"] = "\U0001f64e\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_pouting_medium_light_skin_tone:"] = "\U0001f64e\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_pouting::skin-tone-2:"] = "\U0001f64e\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_pouting_tone3:"] = "\U0001f64e\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_pouting_medium_skin_tone:"] = "\U0001f64e\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_pouting::skin-tone-3:"] = "\U0001f64e\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_pouting_tone4:"] = "\U0001f64e\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_pouting_medium_dark_skin_tone:"] = "\U0001f64e\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_pouting::skin-tone-4:"] = "\U0001f64e\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_pouting_tone5:"] = "\U0001f64e\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_pouting_dark_skin_tone:"] = "\U0001f64e\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_pouting::skin-tone-5:"] = "\U0001f64e\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_raising_hand:"] = "\U0001f64b\u200d\u2642\ufe0f",
+ [":man_raising_hand_tone1:"] = "\U0001f64b\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_raising_hand_light_skin_tone:"] = "\U0001f64b\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_raising_hand::skin-tone-1:"] = "\U0001f64b\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_raising_hand_tone2:"] = "\U0001f64b\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_raising_hand_medium_light_skin_tone:"] = "\U0001f64b\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_raising_hand::skin-tone-2:"] = "\U0001f64b\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_raising_hand_tone3:"] = "\U0001f64b\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_raising_hand_medium_skin_tone:"] = "\U0001f64b\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_raising_hand::skin-tone-3:"] = "\U0001f64b\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_raising_hand_tone4:"] = "\U0001f64b\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_raising_hand_medium_dark_skin_tone:"] = "\U0001f64b\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_raising_hand::skin-tone-4:"] = "\U0001f64b\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_raising_hand_tone5:"] = "\U0001f64b\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_raising_hand_dark_skin_tone:"] = "\U0001f64b\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_raising_hand::skin-tone-5:"] = "\U0001f64b\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_red_haired:"] = "\U0001f468\u200d\U0001f9b0",
+ [":man_red_haired_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b0",
+ [":man_red_haired_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b0",
+ [":man_red_haired::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b0",
+ [":man_red_haired_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b0",
+ [":man_red_haired_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b0",
+ [":man_red_haired::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b0",
+ [":man_red_haired_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b0",
+ [":man_red_haired_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b0",
+ [":man_red_haired::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b0",
+ [":man_red_haired_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b0",
+ [":man_red_haired_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b0",
+ [":man_red_haired::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b0",
+ [":man_red_haired_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b0",
+ [":man_red_haired_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b0",
+ [":man_red_haired::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b0",
+ [":man_rowing_boat:"] = "\U0001f6a3\u200d\u2642\ufe0f",
+ [":man_rowing_boat_tone1:"] = "\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_rowing_boat_light_skin_tone:"] = "\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_rowing_boat::skin-tone-1:"] = "\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_rowing_boat_tone2:"] = "\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_rowing_boat_medium_light_skin_tone:"] = "\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_rowing_boat::skin-tone-2:"] = "\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_rowing_boat_tone3:"] = "\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_rowing_boat_medium_skin_tone:"] = "\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_rowing_boat::skin-tone-3:"] = "\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_rowing_boat_tone4:"] = "\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_rowing_boat_medium_dark_skin_tone:"] = "\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_rowing_boat::skin-tone-4:"] = "\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_rowing_boat_tone5:"] = "\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_rowing_boat_dark_skin_tone:"] = "\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_rowing_boat::skin-tone-5:"] = "\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_running:"] = "\U0001f3c3\u200d\u2642\ufe0f",
+ [":man_running_tone1:"] = "\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_running_light_skin_tone:"] = "\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_running::skin-tone-1:"] = "\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_running_tone2:"] = "\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_running_medium_light_skin_tone:"] = "\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_running::skin-tone-2:"] = "\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_running_tone3:"] = "\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_running_medium_skin_tone:"] = "\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_running::skin-tone-3:"] = "\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_running_tone4:"] = "\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_running_medium_dark_skin_tone:"] = "\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_running::skin-tone-4:"] = "\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_running_tone5:"] = "\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_running_dark_skin_tone:"] = "\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_running::skin-tone-5:"] = "\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_scientist:"] = "\U0001f468\u200d\U0001f52c",
+ [":man_scientist_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f52c",
+ [":man_scientist_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f52c",
+ [":man_scientist::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f52c",
+ [":man_scientist_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f52c",
+ [":man_scientist_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f52c",
+ [":man_scientist::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f52c",
+ [":man_scientist_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f52c",
+ [":man_scientist_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f52c",
+ [":man_scientist::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f52c",
+ [":man_scientist_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f52c",
+ [":man_scientist_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f52c",
+ [":man_scientist::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f52c",
+ [":man_scientist_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f52c",
+ [":man_scientist_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f52c",
+ [":man_scientist::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f52c",
+ [":man_shrugging:"] = "\U0001f937\u200d\u2642\ufe0f",
+ [":man_shrugging_tone1:"] = "\U0001f937\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_shrugging_light_skin_tone:"] = "\U0001f937\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_shrugging::skin-tone-1:"] = "\U0001f937\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_shrugging_tone2:"] = "\U0001f937\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_shrugging_medium_light_skin_tone:"] = "\U0001f937\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_shrugging::skin-tone-2:"] = "\U0001f937\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_shrugging_tone3:"] = "\U0001f937\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_shrugging_medium_skin_tone:"] = "\U0001f937\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_shrugging::skin-tone-3:"] = "\U0001f937\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_shrugging_tone4:"] = "\U0001f937\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_shrugging_medium_dark_skin_tone:"] = "\U0001f937\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_shrugging::skin-tone-4:"] = "\U0001f937\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_shrugging_tone5:"] = "\U0001f937\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_shrugging_dark_skin_tone:"] = "\U0001f937\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_shrugging::skin-tone-5:"] = "\U0001f937\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_singer:"] = "\U0001f468\u200d\U0001f3a4",
+ [":man_singer_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a4",
+ [":man_singer_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a4",
+ [":man_singer::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3a4",
+ [":man_singer_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a4",
+ [":man_singer_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a4",
+ [":man_singer::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3a4",
+ [":man_singer_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a4",
+ [":man_singer_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a4",
+ [":man_singer::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3a4",
+ [":man_singer_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a4",
+ [":man_singer_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a4",
+ [":man_singer::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3a4",
+ [":man_singer_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a4",
+ [":man_singer_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a4",
+ [":man_singer::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3a4",
+ [":man_standing:"] = "\U0001f9cd\u200d\u2642\ufe0f",
+ [":man_standing_tone1:"] = "\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_standing_light_skin_tone:"] = "\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_standing::skin-tone-1:"] = "\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_standing_tone2:"] = "\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_standing_medium_light_skin_tone:"] = "\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_standing::skin-tone-2:"] = "\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_standing_tone3:"] = "\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_standing_medium_skin_tone:"] = "\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_standing::skin-tone-3:"] = "\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_standing_tone4:"] = "\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_standing_medium_dark_skin_tone:"] = "\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_standing::skin-tone-4:"] = "\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_standing_tone5:"] = "\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_standing_dark_skin_tone:"] = "\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_standing::skin-tone-5:"] = "\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_student:"] = "\U0001f468\u200d\U0001f393",
+ [":man_student_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f393",
+ [":man_student_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f393",
+ [":man_student::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f393",
+ [":man_student_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f393",
+ [":man_student_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f393",
+ [":man_student::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f393",
+ [":man_student_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f393",
+ [":man_student_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f393",
+ [":man_student::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f393",
+ [":man_student_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f393",
+ [":man_student_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f393",
+ [":man_student::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f393",
+ [":man_student_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f393",
+ [":man_student_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f393",
+ [":man_student::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f393",
+ [":man_superhero:"] = "\U0001f9b8\u200d\u2642\ufe0f",
+ [":man_superhero_tone1:"] = "\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_superhero_light_skin_tone:"] = "\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_superhero::skin-tone-1:"] = "\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_superhero_tone2:"] = "\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_superhero_medium_light_skin_tone:"] = "\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_superhero::skin-tone-2:"] = "\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_superhero_tone3:"] = "\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_superhero_medium_skin_tone:"] = "\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_superhero::skin-tone-3:"] = "\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_superhero_tone4:"] = "\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_superhero_medium_dark_skin_tone:"] = "\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_superhero::skin-tone-4:"] = "\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_superhero_tone5:"] = "\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_superhero_dark_skin_tone:"] = "\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_superhero::skin-tone-5:"] = "\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_supervillain:"] = "\U0001f9b9\u200d\u2642\ufe0f",
+ [":man_supervillain_tone1:"] = "\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_supervillain_light_skin_tone:"] = "\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_supervillain::skin-tone-1:"] = "\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_supervillain_tone2:"] = "\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_supervillain_medium_light_skin_tone:"] = "\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_supervillain::skin-tone-2:"] = "\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_supervillain_tone3:"] = "\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_supervillain_medium_skin_tone:"] = "\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_supervillain::skin-tone-3:"] = "\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_supervillain_tone4:"] = "\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_supervillain_medium_dark_skin_tone:"] = "\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_supervillain::skin-tone-4:"] = "\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_supervillain_tone5:"] = "\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_supervillain_dark_skin_tone:"] = "\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_supervillain::skin-tone-5:"] = "\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_surfing:"] = "\U0001f3c4\u200d\u2642\ufe0f",
+ [":man_surfing_tone1:"] = "\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_surfing_light_skin_tone:"] = "\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_surfing::skin-tone-1:"] = "\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_surfing_tone2:"] = "\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_surfing_medium_light_skin_tone:"] = "\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_surfing::skin-tone-2:"] = "\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_surfing_tone3:"] = "\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_surfing_medium_skin_tone:"] = "\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_surfing::skin-tone-3:"] = "\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_surfing_tone4:"] = "\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_surfing_medium_dark_skin_tone:"] = "\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_surfing::skin-tone-4:"] = "\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_surfing_tone5:"] = "\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_surfing_dark_skin_tone:"] = "\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_surfing::skin-tone-5:"] = "\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_swimming:"] = "\U0001f3ca\u200d\u2642\ufe0f",
+ [":man_swimming_tone1:"] = "\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_swimming_light_skin_tone:"] = "\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_swimming::skin-tone-1:"] = "\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_swimming_tone2:"] = "\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_swimming_medium_light_skin_tone:"] = "\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_swimming::skin-tone-2:"] = "\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_swimming_tone3:"] = "\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_swimming_medium_skin_tone:"] = "\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_swimming::skin-tone-3:"] = "\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_swimming_tone4:"] = "\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_swimming_medium_dark_skin_tone:"] = "\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_swimming::skin-tone-4:"] = "\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_swimming_tone5:"] = "\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_swimming_dark_skin_tone:"] = "\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_swimming::skin-tone-5:"] = "\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_teacher:"] = "\U0001f468\u200d\U0001f3eb",
+ [":man_teacher_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3eb",
+ [":man_teacher_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f3eb",
+ [":man_teacher::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f3eb",
+ [":man_teacher_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3eb",
+ [":man_teacher_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f3eb",
+ [":man_teacher::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f3eb",
+ [":man_teacher_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3eb",
+ [":man_teacher_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f3eb",
+ [":man_teacher::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f3eb",
+ [":man_teacher_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3eb",
+ [":man_teacher_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f3eb",
+ [":man_teacher::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f3eb",
+ [":man_teacher_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3eb",
+ [":man_teacher_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f3eb",
+ [":man_teacher::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f3eb",
+ [":man_technologist:"] = "\U0001f468\u200d\U0001f4bb",
+ [":man_technologist_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bb",
+ [":man_technologist_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bb",
+ [":man_technologist::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f4bb",
+ [":man_technologist_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bb",
+ [":man_technologist_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bb",
+ [":man_technologist::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f4bb",
+ [":man_technologist_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bb",
+ [":man_technologist_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bb",
+ [":man_technologist::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f4bb",
+ [":man_technologist_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bb",
+ [":man_technologist_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bb",
+ [":man_technologist::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f4bb",
+ [":man_technologist_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bb",
+ [":man_technologist_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bb",
+ [":man_technologist::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f4bb",
+ [":man_tipping_hand:"] = "\U0001f481\u200d\u2642\ufe0f",
+ [":man_tipping_hand_tone1:"] = "\U0001f481\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_tipping_hand_light_skin_tone:"] = "\U0001f481\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_tipping_hand::skin-tone-1:"] = "\U0001f481\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_tipping_hand_tone2:"] = "\U0001f481\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_tipping_hand_medium_light_skin_tone:"] = "\U0001f481\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_tipping_hand::skin-tone-2:"] = "\U0001f481\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_tipping_hand_tone3:"] = "\U0001f481\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_tipping_hand_medium_skin_tone:"] = "\U0001f481\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_tipping_hand::skin-tone-3:"] = "\U0001f481\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_tipping_hand_tone4:"] = "\U0001f481\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_tipping_hand_medium_dark_skin_tone:"] = "\U0001f481\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_tipping_hand::skin-tone-4:"] = "\U0001f481\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_tipping_hand_tone5:"] = "\U0001f481\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_tipping_hand_dark_skin_tone:"] = "\U0001f481\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_tipping_hand::skin-tone-5:"] = "\U0001f481\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_tone1:"] = "\U0001f468\U0001f3fb",
+ [":man::skin-tone-1:"] = "\U0001f468\U0001f3fb",
+ [":man_tone2:"] = "\U0001f468\U0001f3fc",
+ [":man::skin-tone-2:"] = "\U0001f468\U0001f3fc",
+ [":man_tone3:"] = "\U0001f468\U0001f3fd",
+ [":man::skin-tone-3:"] = "\U0001f468\U0001f3fd",
+ [":man_tone4:"] = "\U0001f468\U0001f3fe",
+ [":man::skin-tone-4:"] = "\U0001f468\U0001f3fe",
+ [":man_tone5:"] = "\U0001f468\U0001f3ff",
+ [":man::skin-tone-5:"] = "\U0001f468\U0001f3ff",
+ [":man_vampire:"] = "\U0001f9db\u200d\u2642\ufe0f",
+ [":man_vampire_tone1:"] = "\U0001f9db\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_vampire_light_skin_tone:"] = "\U0001f9db\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_vampire::skin-tone-1:"] = "\U0001f9db\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_vampire_tone2:"] = "\U0001f9db\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_vampire_medium_light_skin_tone:"] = "\U0001f9db\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_vampire::skin-tone-2:"] = "\U0001f9db\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_vampire_tone3:"] = "\U0001f9db\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_vampire_medium_skin_tone:"] = "\U0001f9db\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_vampire::skin-tone-3:"] = "\U0001f9db\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_vampire_tone4:"] = "\U0001f9db\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_vampire_medium_dark_skin_tone:"] = "\U0001f9db\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_vampire::skin-tone-4:"] = "\U0001f9db\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_vampire_tone5:"] = "\U0001f9db\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_vampire_dark_skin_tone:"] = "\U0001f9db\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_vampire::skin-tone-5:"] = "\U0001f9db\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_walking:"] = "\U0001f6b6\u200d\u2642\ufe0f",
+ [":man_walking_tone1:"] = "\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_walking_light_skin_tone:"] = "\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_walking::skin-tone-1:"] = "\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_walking_tone2:"] = "\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_walking_medium_light_skin_tone:"] = "\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_walking::skin-tone-2:"] = "\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_walking_tone3:"] = "\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_walking_medium_skin_tone:"] = "\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_walking::skin-tone-3:"] = "\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_walking_tone4:"] = "\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_walking_medium_dark_skin_tone:"] = "\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_walking::skin-tone-4:"] = "\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_walking_tone5:"] = "\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_walking_dark_skin_tone:"] = "\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_walking::skin-tone-5:"] = "\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_wearing_turban:"] = "\U0001f473\u200d\u2642\ufe0f",
+ [":man_wearing_turban_tone1:"] = "\U0001f473\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_wearing_turban_light_skin_tone:"] = "\U0001f473\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_wearing_turban::skin-tone-1:"] = "\U0001f473\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_wearing_turban_tone2:"] = "\U0001f473\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_wearing_turban_medium_light_skin_tone:"] = "\U0001f473\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_wearing_turban::skin-tone-2:"] = "\U0001f473\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_wearing_turban_tone3:"] = "\U0001f473\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_wearing_turban_medium_skin_tone:"] = "\U0001f473\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_wearing_turban::skin-tone-3:"] = "\U0001f473\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_wearing_turban_tone4:"] = "\U0001f473\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_wearing_turban_medium_dark_skin_tone:"] = "\U0001f473\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_wearing_turban::skin-tone-4:"] = "\U0001f473\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_wearing_turban_tone5:"] = "\U0001f473\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_wearing_turban_dark_skin_tone:"] = "\U0001f473\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_wearing_turban::skin-tone-5:"] = "\U0001f473\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_white_haired:"] = "\U0001f468\u200d\U0001f9b3",
+ [":man_white_haired_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b3",
+ [":man_white_haired_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b3",
+ [":man_white_haired::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9b3",
+ [":man_white_haired_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b3",
+ [":man_white_haired_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b3",
+ [":man_white_haired::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9b3",
+ [":man_white_haired_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b3",
+ [":man_white_haired_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b3",
+ [":man_white_haired::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9b3",
+ [":man_white_haired_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b3",
+ [":man_white_haired_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b3",
+ [":man_white_haired::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9b3",
+ [":man_white_haired_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b3",
+ [":man_white_haired_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b3",
+ [":man_white_haired::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9b3",
+ [":man_with_chinese_cap:"] = "\U0001f472",
+ [":man_with_gua_pi_mao:"] = "\U0001f472",
+ [":man_with_chinese_cap_tone1:"] = "\U0001f472\U0001f3fb",
+ [":man_with_gua_pi_mao_tone1:"] = "\U0001f472\U0001f3fb",
+ [":man_with_chinese_cap::skin-tone-1:"] = "\U0001f472\U0001f3fb",
+ [":man_with_gua_pi_mao::skin-tone-1:"] = "\U0001f472\U0001f3fb",
+ [":man_with_chinese_cap_tone2:"] = "\U0001f472\U0001f3fc",
+ [":man_with_gua_pi_mao_tone2:"] = "\U0001f472\U0001f3fc",
+ [":man_with_chinese_cap::skin-tone-2:"] = "\U0001f472\U0001f3fc",
+ [":man_with_gua_pi_mao::skin-tone-2:"] = "\U0001f472\U0001f3fc",
+ [":man_with_chinese_cap_tone3:"] = "\U0001f472\U0001f3fd",
+ [":man_with_gua_pi_mao_tone3:"] = "\U0001f472\U0001f3fd",
+ [":man_with_chinese_cap::skin-tone-3:"] = "\U0001f472\U0001f3fd",
+ [":man_with_gua_pi_mao::skin-tone-3:"] = "\U0001f472\U0001f3fd",
+ [":man_with_chinese_cap_tone4:"] = "\U0001f472\U0001f3fe",
+ [":man_with_gua_pi_mao_tone4:"] = "\U0001f472\U0001f3fe",
+ [":man_with_chinese_cap::skin-tone-4:"] = "\U0001f472\U0001f3fe",
+ [":man_with_gua_pi_mao::skin-tone-4:"] = "\U0001f472\U0001f3fe",
+ [":man_with_chinese_cap_tone5:"] = "\U0001f472\U0001f3ff",
+ [":man_with_gua_pi_mao_tone5:"] = "\U0001f472\U0001f3ff",
+ [":man_with_chinese_cap::skin-tone-5:"] = "\U0001f472\U0001f3ff",
+ [":man_with_gua_pi_mao::skin-tone-5:"] = "\U0001f472\U0001f3ff",
+ [":man_with_probing_cane:"] = "\U0001f468\u200d\U0001f9af",
+ [":man_with_probing_cane_tone1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9af",
+ [":man_with_probing_cane_light_skin_tone:"] = "\U0001f468\U0001f3fb\u200d\U0001f9af",
+ [":man_with_probing_cane::skin-tone-1:"] = "\U0001f468\U0001f3fb\u200d\U0001f9af",
+ [":man_with_probing_cane_tone2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9af",
+ [":man_with_probing_cane_medium_light_skin_tone:"] = "\U0001f468\U0001f3fc\u200d\U0001f9af",
+ [":man_with_probing_cane::skin-tone-2:"] = "\U0001f468\U0001f3fc\u200d\U0001f9af",
+ [":man_with_probing_cane_tone3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9af",
+ [":man_with_probing_cane_medium_skin_tone:"] = "\U0001f468\U0001f3fd\u200d\U0001f9af",
+ [":man_with_probing_cane::skin-tone-3:"] = "\U0001f468\U0001f3fd\u200d\U0001f9af",
+ [":man_with_probing_cane_tone4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9af",
+ [":man_with_probing_cane_medium_dark_skin_tone:"] = "\U0001f468\U0001f3fe\u200d\U0001f9af",
+ [":man_with_probing_cane::skin-tone-4:"] = "\U0001f468\U0001f3fe\u200d\U0001f9af",
+ [":man_with_probing_cane_tone5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9af",
+ [":man_with_probing_cane_dark_skin_tone:"] = "\U0001f468\U0001f3ff\u200d\U0001f9af",
+ [":man_with_probing_cane::skin-tone-5:"] = "\U0001f468\U0001f3ff\u200d\U0001f9af",
+ [":man_with_veil:"] = "\U0001f470\u200d\u2642\ufe0f",
+ [":man_with_veil_tone1:"] = "\U0001f470\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_with_veil_light_skin_tone:"] = "\U0001f470\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_with_veil::skin-tone-1:"] = "\U0001f470\U0001f3fb\u200d\u2642\ufe0f",
+ [":man_with_veil_tone2:"] = "\U0001f470\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_with_veil_medium_light_skin_tone:"] = "\U0001f470\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_with_veil::skin-tone-2:"] = "\U0001f470\U0001f3fc\u200d\u2642\ufe0f",
+ [":man_with_veil_tone3:"] = "\U0001f470\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_with_veil_medium_skin_tone:"] = "\U0001f470\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_with_veil::skin-tone-3:"] = "\U0001f470\U0001f3fd\u200d\u2642\ufe0f",
+ [":man_with_veil_tone4:"] = "\U0001f470\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_with_veil_medium_dark_skin_tone:"] = "\U0001f470\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_with_veil::skin-tone-4:"] = "\U0001f470\U0001f3fe\u200d\u2642\ufe0f",
+ [":man_with_veil_tone5:"] = "\U0001f470\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_with_veil_dark_skin_tone:"] = "\U0001f470\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_with_veil::skin-tone-5:"] = "\U0001f470\U0001f3ff\u200d\u2642\ufe0f",
+ [":man_zombie:"] = "\U0001f9df\u200d\u2642\ufe0f",
+ [":mango:"] = "\U0001f96d",
+ [":mans_shoe:"] = "\U0001f45e",
+ [":manual_wheelchair:"] = "\U0001f9bd",
+ [":map:"] = "\U0001f5fa\ufe0f",
+ [":world_map:"] = "\U0001f5fa\ufe0f",
+ [":maple_leaf:"] = "\U0001f341",
+ [":martial_arts_uniform:"] = "\U0001f94b",
+ [":karate_uniform:"] = "\U0001f94b",
+ [":mask:"] = "\U0001f637",
+ [":mate:"] = "\U0001f9c9",
+ [":meat_on_bone:"] = "\U0001f356",
+ [":mechanic:"] = "\U0001f9d1\u200d\U0001f527",
+ [":mechanic_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f527",
+ [":mechanic_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f527",
+ [":mechanic::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f527",
+ [":mechanic_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f527",
+ [":mechanic_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f527",
+ [":mechanic::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f527",
+ [":mechanic_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f527",
+ [":mechanic_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f527",
+ [":mechanic::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f527",
+ [":mechanic_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f527",
+ [":mechanic_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f527",
+ [":mechanic::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f527",
+ [":mechanic_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f527",
+ [":mechanic_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f527",
+ [":mechanic::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f527",
+ [":mechanical_arm:"] = "\U0001f9be",
+ [":mechanical_leg:"] = "\U0001f9bf",
+ [":medal:"] = "\U0001f3c5",
+ [":sports_medal:"] = "\U0001f3c5",
+ [":medical_symbol:"] = "\u2695\ufe0f",
+ [":mega:"] = "\U0001f4e3",
+ [":melon:"] = "\U0001f348",
+ [":men_holding_hands_tone1:"] = "\U0001f46c",
+ [":men_holding_hands_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone1_tone2:"] = "\U0001f46c",
+ [":men_holding_hands_light_skin_tone_medium_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone1_tone3:"] = "\U0001f46c",
+ [":men_holding_hands_light_skin_tone_medium_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone1_tone4:"] = "\U0001f46c",
+ [":men_holding_hands_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone1_tone5:"] = "\U0001f46c",
+ [":men_holding_hands_light_skin_tone_dark_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone2:"] = "\U0001f46c",
+ [":men_holding_hands_medium_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone2_tone1:"] = "\U0001f46c",
+ [":men_holding_hands_medium_light_skin_tone_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone2_tone3:"] = "\U0001f46c",
+ [":men_holding_hands_medium_light_skin_tone_medium_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone2_tone4:"] = "\U0001f46c",
+ [":men_holding_hands_medium_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone2_tone5:"] = "\U0001f46c",
+ [":men_holding_hands_medium_light_skin_tone_dark_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone3:"] = "\U0001f46c",
+ [":men_holding_hands_medium_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone3_tone1:"] = "\U0001f46c",
+ [":men_holding_hands_medium_skin_tone_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone3_tone2:"] = "\U0001f46c",
+ [":men_holding_hands_medium_skin_tone_medium_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone3_tone4:"] = "\U0001f46c",
+ [":men_holding_hands_medium_skin_tone_medium_dark_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone3_tone5:"] = "\U0001f46c",
+ [":men_holding_hands_medium_skin_tone_dark_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone4:"] = "\U0001f46c",
+ [":men_holding_hands_medium_dark_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone4_tone1:"] = "\U0001f46c",
+ [":men_holding_hands_medium_dark_skin_tone_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone4_tone2:"] = "\U0001f46c",
+ [":men_holding_hands_medium_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone4_tone3:"] = "\U0001f46c",
+ [":men_holding_hands_medium_dark_skin_tone_medium_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone4_tone5:"] = "\U0001f46c",
+ [":men_holding_hands_medium_dark_skin_tone_dark_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone5:"] = "\U0001f46c",
+ [":men_holding_hands_dark_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone5_tone1:"] = "\U0001f46c",
+ [":men_holding_hands_dark_skin_tone_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone5_tone2:"] = "\U0001f46c",
+ [":men_holding_hands_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone5_tone3:"] = "\U0001f46c",
+ [":men_holding_hands_dark_skin_tone_medium_skin_tone:"] = "\U0001f46c",
+ [":men_holding_hands_tone5_tone4:"] = "\U0001f46c",
+ [":men_holding_hands_dark_skin_tone_medium_dark_skin_tone:"] = "\U0001f46c",
+ [":men_with_bunny_ears_partying:"] = "\U0001f46f\u200d\u2642\ufe0f",
+ [":men_wrestling:"] = "\U0001f93c\u200d\u2642\ufe0f",
+ [":menorah:"] = "\U0001f54e",
+ [":mens:"] = "\U0001f6b9",
+ [":mermaid:"] = "\U0001f9dc\u200d\u2640\ufe0f",
+ [":mermaid_tone1:"] = "\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f",
+ [":mermaid_light_skin_tone:"] = "\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f",
+ [":mermaid::skin-tone-1:"] = "\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f",
+ [":mermaid_tone2:"] = "\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f",
+ [":mermaid_medium_light_skin_tone:"] = "\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f",
+ [":mermaid::skin-tone-2:"] = "\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f",
+ [":mermaid_tone3:"] = "\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f",
+ [":mermaid_medium_skin_tone:"] = "\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f",
+ [":mermaid::skin-tone-3:"] = "\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f",
+ [":mermaid_tone4:"] = "\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f",
+ [":mermaid_medium_dark_skin_tone:"] = "\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f",
+ [":mermaid::skin-tone-4:"] = "\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f",
+ [":mermaid_tone5:"] = "\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f",
+ [":mermaid_dark_skin_tone:"] = "\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f",
+ [":mermaid::skin-tone-5:"] = "\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f",
+ [":merman:"] = "\U0001f9dc\u200d\u2642\ufe0f",
+ [":merman_tone1:"] = "\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f",
+ [":merman_light_skin_tone:"] = "\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f",
+ [":merman::skin-tone-1:"] = "\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f",
+ [":merman_tone2:"] = "\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f",
+ [":merman_medium_light_skin_tone:"] = "\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f",
+ [":merman::skin-tone-2:"] = "\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f",
+ [":merman_tone3:"] = "\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f",
+ [":merman_medium_skin_tone:"] = "\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f",
+ [":merman::skin-tone-3:"] = "\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f",
+ [":merman_tone4:"] = "\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f",
+ [":merman_medium_dark_skin_tone:"] = "\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f",
+ [":merman::skin-tone-4:"] = "\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f",
+ [":merman_tone5:"] = "\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f",
+ [":merman_dark_skin_tone:"] = "\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f",
+ [":merman::skin-tone-5:"] = "\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f",
+ [":merperson:"] = "\U0001f9dc",
+ [":merperson_tone1:"] = "\U0001f9dc\U0001f3fb",
+ [":merperson_light_skin_tone:"] = "\U0001f9dc\U0001f3fb",
+ [":merperson::skin-tone-1:"] = "\U0001f9dc\U0001f3fb",
+ [":merperson_tone2:"] = "\U0001f9dc\U0001f3fc",
+ [":merperson_medium_light_skin_tone:"] = "\U0001f9dc\U0001f3fc",
+ [":merperson::skin-tone-2:"] = "\U0001f9dc\U0001f3fc",
+ [":merperson_tone3:"] = "\U0001f9dc\U0001f3fd",
+ [":merperson_medium_skin_tone:"] = "\U0001f9dc\U0001f3fd",
+ [":merperson::skin-tone-3:"] = "\U0001f9dc\U0001f3fd",
+ [":merperson_tone4:"] = "\U0001f9dc\U0001f3fe",
+ [":merperson_medium_dark_skin_tone:"] = "\U0001f9dc\U0001f3fe",
+ [":merperson::skin-tone-4:"] = "\U0001f9dc\U0001f3fe",
+ [":merperson_tone5:"] = "\U0001f9dc\U0001f3ff",
+ [":merperson_dark_skin_tone:"] = "\U0001f9dc\U0001f3ff",
+ [":merperson::skin-tone-5:"] = "\U0001f9dc\U0001f3ff",
+ [":metal:"] = "\U0001f918",
+ [":sign_of_the_horns:"] = "\U0001f918",
+ [":metal_tone1:"] = "\U0001f918\U0001f3fb",
+ [":sign_of_the_horns_tone1:"] = "\U0001f918\U0001f3fb",
+ [":metal::skin-tone-1:"] = "\U0001f918\U0001f3fb",
+ [":sign_of_the_horns::skin-tone-1:"] = "\U0001f918\U0001f3fb",
+ [":metal_tone2:"] = "\U0001f918\U0001f3fc",
+ [":sign_of_the_horns_tone2:"] = "\U0001f918\U0001f3fc",
+ [":metal::skin-tone-2:"] = "\U0001f918\U0001f3fc",
+ [":sign_of_the_horns::skin-tone-2:"] = "\U0001f918\U0001f3fc",
+ [":metal_tone3:"] = "\U0001f918\U0001f3fd",
+ [":sign_of_the_horns_tone3:"] = "\U0001f918\U0001f3fd",
+ [":metal::skin-tone-3:"] = "\U0001f918\U0001f3fd",
+ [":sign_of_the_horns::skin-tone-3:"] = "\U0001f918\U0001f3fd",
+ [":metal_tone4:"] = "\U0001f918\U0001f3fe",
+ [":sign_of_the_horns_tone4:"] = "\U0001f918\U0001f3fe",
+ [":metal::skin-tone-4:"] = "\U0001f918\U0001f3fe",
+ [":sign_of_the_horns::skin-tone-4:"] = "\U0001f918\U0001f3fe",
+ [":metal_tone5:"] = "\U0001f918\U0001f3ff",
+ [":sign_of_the_horns_tone5:"] = "\U0001f918\U0001f3ff",
+ [":metal::skin-tone-5:"] = "\U0001f918\U0001f3ff",
+ [":sign_of_the_horns::skin-tone-5:"] = "\U0001f918\U0001f3ff",
+ [":metro:"] = "\U0001f687",
+ [":microbe:"] = "\U0001f9a0",
+ [":microphone:"] = "\U0001f3a4",
+ [":microphone2:"] = "\U0001f399\ufe0f",
+ [":studio_microphone:"] = "\U0001f399\ufe0f",
+ [":microscope:"] = "\U0001f52c",
+ [":middle_finger:"] = "\U0001f595",
+ [":reversed_hand_with_middle_finger_extended:"] = "\U0001f595",
+ [":middle_finger_tone1:"] = "\U0001f595\U0001f3fb",
+ [":reversed_hand_with_middle_finger_extended_tone1:"] = "\U0001f595\U0001f3fb",
+ [":middle_finger::skin-tone-1:"] = "\U0001f595\U0001f3fb",
+ [":reversed_hand_with_middle_finger_extended::skin-tone-1:"] = "\U0001f595\U0001f3fb",
+ [":middle_finger_tone2:"] = "\U0001f595\U0001f3fc",
+ [":reversed_hand_with_middle_finger_extended_tone2:"] = "\U0001f595\U0001f3fc",
+ [":middle_finger::skin-tone-2:"] = "\U0001f595\U0001f3fc",
+ [":reversed_hand_with_middle_finger_extended::skin-tone-2:"] = "\U0001f595\U0001f3fc",
+ [":middle_finger_tone3:"] = "\U0001f595\U0001f3fd",
+ [":reversed_hand_with_middle_finger_extended_tone3:"] = "\U0001f595\U0001f3fd",
+ [":middle_finger::skin-tone-3:"] = "\U0001f595\U0001f3fd",
+ [":reversed_hand_with_middle_finger_extended::skin-tone-3:"] = "\U0001f595\U0001f3fd",
+ [":middle_finger_tone4:"] = "\U0001f595\U0001f3fe",
+ [":reversed_hand_with_middle_finger_extended_tone4:"] = "\U0001f595\U0001f3fe",
+ [":middle_finger::skin-tone-4:"] = "\U0001f595\U0001f3fe",
+ [":reversed_hand_with_middle_finger_extended::skin-tone-4:"] = "\U0001f595\U0001f3fe",
+ [":middle_finger_tone5:"] = "\U0001f595\U0001f3ff",
+ [":reversed_hand_with_middle_finger_extended_tone5:"] = "\U0001f595\U0001f3ff",
+ [":middle_finger::skin-tone-5:"] = "\U0001f595\U0001f3ff",
+ [":reversed_hand_with_middle_finger_extended::skin-tone-5:"] = "\U0001f595\U0001f3ff",
+ [":military_helmet:"] = "\U0001fa96",
+ [":military_medal:"] = "\U0001f396\ufe0f",
+ [":milk:"] = "\U0001f95b",
+ [":glass_of_milk:"] = "\U0001f95b",
+ [":milky_way:"] = "\U0001f30c",
+ [":minibus:"] = "\U0001f690",
+ [":minidisc:"] = "\U0001f4bd",
+ [":mirror:"] = "\U0001fa9e",
+ [":mobile_phone:"] = "\U0001f4f1",
+ [":iphone:"] = "\U0001f4f1",
+ [":mobile_phone_off:"] = "\U0001f4f4",
+ [":money_mouth:"] = "\U0001f911",
+ [":money_mouth_face:"] = "\U0001f911",
+ [":money_with_wings:"] = "\U0001f4b8",
+ [":moneybag:"] = "\U0001f4b0",
+ [":monkey:"] = "\U0001f412",
+ [":monkey_face:"] = "\U0001f435",
+ [":monorail:"] = "\U0001f69d",
+ [":moon_cake:"] = "\U0001f96e",
+ [":mortar_board:"] = "\U0001f393",
+ [":mosque:"] = "\U0001f54c",
+ [":mosquito:"] = "\U0001f99f",
+ [":motor_scooter:"] = "\U0001f6f5",
+ [":motorbike:"] = "\U0001f6f5",
+ [":motorboat:"] = "\U0001f6e5\ufe0f",
+ [":motorcycle:"] = "\U0001f3cd\ufe0f",
+ [":racing_motorcycle:"] = "\U0001f3cd\ufe0f",
+ [":motorized_wheelchair:"] = "\U0001f9bc",
+ [":motorway:"] = "\U0001f6e3\ufe0f",
+ [":mount_fuji:"] = "\U0001f5fb",
+ [":mountain:"] = "\u26f0\ufe0f",
+ [":mountain_cableway:"] = "\U0001f6a0",
+ [":mountain_railway:"] = "\U0001f69e",
+ [":mountain_snow:"] = "\U0001f3d4\ufe0f",
+ [":snow_capped_mountain:"] = "\U0001f3d4\ufe0f",
+ [":mouse:"] = "\U0001f42d",
+ [":mouse_three_button:"] = "\U0001f5b1\ufe0f",
+ [":three_button_mouse:"] = "\U0001f5b1\ufe0f",
+ [":mouse_trap:"] = "\U0001faa4",
+ [":mouse2:"] = "\U0001f401",
+ [":movie_camera:"] = "\U0001f3a5",
+ [":moyai:"] = "\U0001f5ff",
+ [":mrs_claus:"] = "\U0001f936",
+ [":mother_christmas:"] = "\U0001f936",
+ [":mrs_claus_tone1:"] = "\U0001f936\U0001f3fb",
+ [":mother_christmas_tone1:"] = "\U0001f936\U0001f3fb",
+ [":mrs_claus::skin-tone-1:"] = "\U0001f936\U0001f3fb",
+ [":mother_christmas::skin-tone-1:"] = "\U0001f936\U0001f3fb",
+ [":mrs_claus_tone2:"] = "\U0001f936\U0001f3fc",
+ [":mother_christmas_tone2:"] = "\U0001f936\U0001f3fc",
+ [":mrs_claus::skin-tone-2:"] = "\U0001f936\U0001f3fc",
+ [":mother_christmas::skin-tone-2:"] = "\U0001f936\U0001f3fc",
+ [":mrs_claus_tone3:"] = "\U0001f936\U0001f3fd",
+ [":mother_christmas_tone3:"] = "\U0001f936\U0001f3fd",
+ [":mrs_claus::skin-tone-3:"] = "\U0001f936\U0001f3fd",
+ [":mother_christmas::skin-tone-3:"] = "\U0001f936\U0001f3fd",
+ [":mrs_claus_tone4:"] = "\U0001f936\U0001f3fe",
+ [":mother_christmas_tone4:"] = "\U0001f936\U0001f3fe",
+ [":mrs_claus::skin-tone-4:"] = "\U0001f936\U0001f3fe",
+ [":mother_christmas::skin-tone-4:"] = "\U0001f936\U0001f3fe",
+ [":mrs_claus_tone5:"] = "\U0001f936\U0001f3ff",
+ [":mother_christmas_tone5:"] = "\U0001f936\U0001f3ff",
+ [":mrs_claus::skin-tone-5:"] = "\U0001f936\U0001f3ff",
+ [":mother_christmas::skin-tone-5:"] = "\U0001f936\U0001f3ff",
+ [":muscle:"] = "\U0001f4aa",
+ [":muscle_tone1:"] = "\U0001f4aa\U0001f3fb",
+ [":muscle::skin-tone-1:"] = "\U0001f4aa\U0001f3fb",
+ [":muscle_tone2:"] = "\U0001f4aa\U0001f3fc",
+ [":muscle::skin-tone-2:"] = "\U0001f4aa\U0001f3fc",
+ [":muscle_tone3:"] = "\U0001f4aa\U0001f3fd",
+ [":muscle::skin-tone-3:"] = "\U0001f4aa\U0001f3fd",
+ [":muscle_tone4:"] = "\U0001f4aa\U0001f3fe",
+ [":muscle::skin-tone-4:"] = "\U0001f4aa\U0001f3fe",
+ [":muscle_tone5:"] = "\U0001f4aa\U0001f3ff",
+ [":muscle::skin-tone-5:"] = "\U0001f4aa\U0001f3ff",
+ [":mushroom:"] = "\U0001f344",
+ [":musical_keyboard:"] = "\U0001f3b9",
+ [":musical_note:"] = "\U0001f3b5",
+ [":musical_score:"] = "\U0001f3bc",
+ [":mute:"] = "\U0001f507",
+ [":mx_claus:"] = "\U0001f9d1\u200d\U0001f384",
+ [":mx_claus_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f384",
+ [":mx_claus_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f384",
+ [":mx_claus::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f384",
+ [":mx_claus_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f384",
+ [":mx_claus_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f384",
+ [":mx_claus::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f384",
+ [":mx_claus_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f384",
+ [":mx_claus_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f384",
+ [":mx_claus::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f384",
+ [":mx_claus_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f384",
+ [":mx_claus_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f384",
+ [":mx_claus::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f384",
+ [":mx_claus_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f384",
+ [":mx_claus_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f384",
+ [":mx_claus::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f384",
+ [":nail_care:"] = "\U0001f485",
+ [":nail_care_tone1:"] = "\U0001f485\U0001f3fb",
+ [":nail_care::skin-tone-1:"] = "\U0001f485\U0001f3fb",
+ [":nail_care_tone2:"] = "\U0001f485\U0001f3fc",
+ [":nail_care::skin-tone-2:"] = "\U0001f485\U0001f3fc",
+ [":nail_care_tone3:"] = "\U0001f485\U0001f3fd",
+ [":nail_care::skin-tone-3:"] = "\U0001f485\U0001f3fd",
+ [":nail_care_tone4:"] = "\U0001f485\U0001f3fe",
+ [":nail_care::skin-tone-4:"] = "\U0001f485\U0001f3fe",
+ [":nail_care_tone5:"] = "\U0001f485\U0001f3ff",
+ [":nail_care::skin-tone-5:"] = "\U0001f485\U0001f3ff",
+ [":name_badge:"] = "\U0001f4db",
+ [":nauseated_face:"] = "\U0001f922",
+ [":sick:"] = "\U0001f922",
+ [":nazar_amulet:"] = "\U0001f9ff",
+ [":necktie:"] = "\U0001f454",
+ [":negative_squared_cross_mark:"] = "\u274e",
+ [":nerd:"] = "\U0001f913",
+ [":nerd_face:"] = "\U0001f913",
+ [":nesting_dolls:"] = "\U0001fa86",
+ [":neutral_face:"] = "\U0001f610",
+ [":|"] = "\U0001f610",
+ [":-|"] = "\U0001f610",
+ ["=|"] = "\U0001f610",
+ ["=-|"] = "\U0001f610",
+ [":new:"] = "\U0001f195",
+ [":new_moon:"] = "\U0001f311",
+ [":new_moon_with_face:"] = "\U0001f31a",
+ [":newspaper:"] = "\U0001f4f0",
+ [":newspaper2:"] = "\U0001f5de\ufe0f",
+ [":rolled_up_newspaper:"] = "\U0001f5de\ufe0f",
+ [":ng:"] = "\U0001f196",
+ [":night_with_stars:"] = "\U0001f303",
+ [":nine:"] = "\u0039\ufe0f\u20e3",
+ [":ninja:"] = "\U0001f977",
+ [":ninja_tone1:"] = "\U0001f977\U0001f3fb",
+ [":ninja_light_skin_tone:"] = "\U0001f977\U0001f3fb",
+ [":ninja::skin-tone-1:"] = "\U0001f977\U0001f3fb",
+ [":ninja_tone2:"] = "\U0001f977\U0001f3fc",
+ [":ninja_medium_light_skin_tone:"] = "\U0001f977\U0001f3fc",
+ [":ninja::skin-tone-2:"] = "\U0001f977\U0001f3fc",
+ [":ninja_tone3:"] = "\U0001f977\U0001f3fd",
+ [":ninja_medium_skin_tone:"] = "\U0001f977\U0001f3fd",
+ [":ninja::skin-tone-3:"] = "\U0001f977\U0001f3fd",
+ [":ninja_tone4:"] = "\U0001f977\U0001f3fe",
+ [":ninja_medium_dark_skin_tone:"] = "\U0001f977\U0001f3fe",
+ [":ninja::skin-tone-4:"] = "\U0001f977\U0001f3fe",
+ [":ninja_tone5:"] = "\U0001f977\U0001f3ff",
+ [":ninja_dark_skin_tone:"] = "\U0001f977\U0001f3ff",
+ [":ninja::skin-tone-5:"] = "\U0001f977\U0001f3ff",
+ [":no_bell:"] = "\U0001f515",
+ [":no_bicycles:"] = "\U0001f6b3",
+ [":no_entry:"] = "\u26d4",
+ [":no_entry_sign:"] = "\U0001f6ab",
+ [":no_mobile_phones:"] = "\U0001f4f5",
+ [":no_mouth:"] = "\U0001f636",
+ [":no_pedestrians:"] = "\U0001f6b7",
+ [":no_smoking:"] = "\U0001f6ad",
+ [":non_potable_water:"] = "\U0001f6b1",
+ [":nose:"] = "\U0001f443",
+ [":nose_tone1:"] = "\U0001f443\U0001f3fb",
+ [":nose::skin-tone-1:"] = "\U0001f443\U0001f3fb",
+ [":nose_tone2:"] = "\U0001f443\U0001f3fc",
+ [":nose::skin-tone-2:"] = "\U0001f443\U0001f3fc",
+ [":nose_tone3:"] = "\U0001f443\U0001f3fd",
+ [":nose::skin-tone-3:"] = "\U0001f443\U0001f3fd",
+ [":nose_tone4:"] = "\U0001f443\U0001f3fe",
+ [":nose::skin-tone-4:"] = "\U0001f443\U0001f3fe",
+ [":nose_tone5:"] = "\U0001f443\U0001f3ff",
+ [":nose::skin-tone-5:"] = "\U0001f443\U0001f3ff",
+ [":notebook:"] = "\U0001f4d3",
+ [":notebook_with_decorative_cover:"] = "\U0001f4d4",
+ [":notepad_spiral:"] = "\U0001f5d2\ufe0f",
+ [":spiral_note_pad:"] = "\U0001f5d2\ufe0f",
+ [":notes:"] = "\U0001f3b6",
+ [":nut_and_bolt:"] = "\U0001f529",
+ [":o:"] = "\u2b55",
+ [":o2:"] = "\U0001f17e\ufe0f",
+ [":ocean:"] = "\U0001f30a",
+ [":octagonal_sign:"] = "\U0001f6d1",
+ [":stop_sign:"] = "\U0001f6d1",
+ [":octopus:"] = "\U0001f419",
+ [":oden:"] = "\U0001f362",
+ [":office:"] = "\U0001f3e2",
+ [":office_worker:"] = "\U0001f9d1\u200d\U0001f4bc",
+ [":office_worker_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bc",
+ [":office_worker_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bc",
+ [":office_worker::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bc",
+ [":office_worker_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bc",
+ [":office_worker_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bc",
+ [":office_worker::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bc",
+ [":office_worker_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bc",
+ [":office_worker_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bc",
+ [":office_worker::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bc",
+ [":office_worker_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bc",
+ [":office_worker_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bc",
+ [":office_worker::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bc",
+ [":office_worker_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bc",
+ [":office_worker_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bc",
+ [":office_worker::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bc",
+ [":oil:"] = "\U0001f6e2\ufe0f",
+ [":oil_drum:"] = "\U0001f6e2\ufe0f",
+ [":ok:"] = "\U0001f197",
+ [":ok_hand:"] = "\U0001f44c",
+ [":ok_hand_tone1:"] = "\U0001f44c\U0001f3fb",
+ [":ok_hand::skin-tone-1:"] = "\U0001f44c\U0001f3fb",
+ [":ok_hand_tone2:"] = "\U0001f44c\U0001f3fc",
+ [":ok_hand::skin-tone-2:"] = "\U0001f44c\U0001f3fc",
+ [":ok_hand_tone3:"] = "\U0001f44c\U0001f3fd",
+ [":ok_hand::skin-tone-3:"] = "\U0001f44c\U0001f3fd",
+ [":ok_hand_tone4:"] = "\U0001f44c\U0001f3fe",
+ [":ok_hand::skin-tone-4:"] = "\U0001f44c\U0001f3fe",
+ [":ok_hand_tone5:"] = "\U0001f44c\U0001f3ff",
+ [":ok_hand::skin-tone-5:"] = "\U0001f44c\U0001f3ff",
+ [":older_adult:"] = "\U0001f9d3",
+ [":older_adult_tone1:"] = "\U0001f9d3\U0001f3fb",
+ [":older_adult_light_skin_tone:"] = "\U0001f9d3\U0001f3fb",
+ [":older_adult::skin-tone-1:"] = "\U0001f9d3\U0001f3fb",
+ [":older_adult_tone2:"] = "\U0001f9d3\U0001f3fc",
+ [":older_adult_medium_light_skin_tone:"] = "\U0001f9d3\U0001f3fc",
+ [":older_adult::skin-tone-2:"] = "\U0001f9d3\U0001f3fc",
+ [":older_adult_tone3:"] = "\U0001f9d3\U0001f3fd",
+ [":older_adult_medium_skin_tone:"] = "\U0001f9d3\U0001f3fd",
+ [":older_adult::skin-tone-3:"] = "\U0001f9d3\U0001f3fd",
+ [":older_adult_tone4:"] = "\U0001f9d3\U0001f3fe",
+ [":older_adult_medium_dark_skin_tone:"] = "\U0001f9d3\U0001f3fe",
+ [":older_adult::skin-tone-4:"] = "\U0001f9d3\U0001f3fe",
+ [":older_adult_tone5:"] = "\U0001f9d3\U0001f3ff",
+ [":older_adult_dark_skin_tone:"] = "\U0001f9d3\U0001f3ff",
+ [":older_adult::skin-tone-5:"] = "\U0001f9d3\U0001f3ff",
+ [":older_man:"] = "\U0001f474",
+ [":older_man_tone1:"] = "\U0001f474\U0001f3fb",
+ [":older_man::skin-tone-1:"] = "\U0001f474\U0001f3fb",
+ [":older_man_tone2:"] = "\U0001f474\U0001f3fc",
+ [":older_man::skin-tone-2:"] = "\U0001f474\U0001f3fc",
+ [":older_man_tone3:"] = "\U0001f474\U0001f3fd",
+ [":older_man::skin-tone-3:"] = "\U0001f474\U0001f3fd",
+ [":older_man_tone4:"] = "\U0001f474\U0001f3fe",
+ [":older_man::skin-tone-4:"] = "\U0001f474\U0001f3fe",
+ [":older_man_tone5:"] = "\U0001f474\U0001f3ff",
+ [":older_man::skin-tone-5:"] = "\U0001f474\U0001f3ff",
+ [":older_woman:"] = "\U0001f475",
+ [":grandma:"] = "\U0001f475",
+ [":older_woman_tone1:"] = "\U0001f475\U0001f3fb",
+ [":grandma_tone1:"] = "\U0001f475\U0001f3fb",
+ [":older_woman::skin-tone-1:"] = "\U0001f475\U0001f3fb",
+ [":grandma::skin-tone-1:"] = "\U0001f475\U0001f3fb",
+ [":older_woman_tone2:"] = "\U0001f475\U0001f3fc",
+ [":grandma_tone2:"] = "\U0001f475\U0001f3fc",
+ [":older_woman::skin-tone-2:"] = "\U0001f475\U0001f3fc",
+ [":grandma::skin-tone-2:"] = "\U0001f475\U0001f3fc",
+ [":older_woman_tone3:"] = "\U0001f475\U0001f3fd",
+ [":grandma_tone3:"] = "\U0001f475\U0001f3fd",
+ [":older_woman::skin-tone-3:"] = "\U0001f475\U0001f3fd",
+ [":grandma::skin-tone-3:"] = "\U0001f475\U0001f3fd",
+ [":older_woman_tone4:"] = "\U0001f475\U0001f3fe",
+ [":grandma_tone4:"] = "\U0001f475\U0001f3fe",
+ [":older_woman::skin-tone-4:"] = "\U0001f475\U0001f3fe",
+ [":grandma::skin-tone-4:"] = "\U0001f475\U0001f3fe",
+ [":older_woman_tone5:"] = "\U0001f475\U0001f3ff",
+ [":grandma_tone5:"] = "\U0001f475\U0001f3ff",
+ [":older_woman::skin-tone-5:"] = "\U0001f475\U0001f3ff",
+ [":grandma::skin-tone-5:"] = "\U0001f475\U0001f3ff",
+ [":olive:"] = "\U0001fad2",
+ [":om_symbol:"] = "\U0001f549\ufe0f",
+ [":on:"] = "\U0001f51b",
+ [":oncoming_automobile:"] = "\U0001f698",
+ [":oncoming_bus:"] = "\U0001f68d",
+ [":oncoming_police_car:"] = "\U0001f694",
+ [":oncoming_taxi:"] = "\U0001f696",
+ [":one:"] = "\u0031\ufe0f\u20e3",
+ [":one_piece_swimsuit:"] = "\U0001fa71",
+ [":onion:"] = "\U0001f9c5",
+ [":open_file_folder:"] = "\U0001f4c2",
+ [":open_hands:"] = "\U0001f450",
+ [":open_hands_tone1:"] = "\U0001f450\U0001f3fb",
+ [":open_hands::skin-tone-1:"] = "\U0001f450\U0001f3fb",
+ [":open_hands_tone2:"] = "\U0001f450\U0001f3fc",
+ [":open_hands::skin-tone-2:"] = "\U0001f450\U0001f3fc",
+ [":open_hands_tone3:"] = "\U0001f450\U0001f3fd",
+ [":open_hands::skin-tone-3:"] = "\U0001f450\U0001f3fd",
+ [":open_hands_tone4:"] = "\U0001f450\U0001f3fe",
+ [":open_hands::skin-tone-4:"] = "\U0001f450\U0001f3fe",
+ [":open_hands_tone5:"] = "\U0001f450\U0001f3ff",
+ [":open_hands::skin-tone-5:"] = "\U0001f450\U0001f3ff",
+ [":open_mouth:"] = "\U0001f62e",
+ [":o"] = "\U0001f62e",
+ [":-o"] = "\U0001f62e",
+ [":O"] = "\U0001f62e",
+ [":-O"] = "\U0001f62e",
+ ["=o"] = "\U0001f62e",
+ ["=-o"] = "\U0001f62e",
+ ["=O"] = "\U0001f62e",
+ ["=-O"] = "\U0001f62e",
+ [":ophiuchus:"] = "\u26ce",
+ [":orange_book:"] = "\U0001f4d9",
+ [":orange_circle:"] = "\U0001f7e0",
+ [":orange_heart:"] = "\U0001f9e1",
+ [":orange_square:"] = "\U0001f7e7",
+ [":orangutan:"] = "\U0001f9a7",
+ [":orthodox_cross:"] = "\u2626\ufe0f",
+ [":otter:"] = "\U0001f9a6",
+ [":outbox_tray:"] = "\U0001f4e4",
+ [":owl:"] = "\U0001f989",
+ [":ox:"] = "\U0001f402",
+ [":oyster:"] = "\U0001f9aa",
+ [":package:"] = "\U0001f4e6",
+ [":page_facing_up:"] = "\U0001f4c4",
+ [":page_with_curl:"] = "\U0001f4c3",
+ [":pager:"] = "\U0001f4df",
+ [":paintbrush:"] = "\U0001f58c\ufe0f",
+ [":lower_left_paintbrush:"] = "\U0001f58c\ufe0f",
+ [":palm_tree:"] = "\U0001f334",
+ [":palms_up_together:"] = "\U0001f932",
+ [":palms_up_together_tone1:"] = "\U0001f932\U0001f3fb",
+ [":palms_up_together_light_skin_tone:"] = "\U0001f932\U0001f3fb",
+ [":palms_up_together::skin-tone-1:"] = "\U0001f932\U0001f3fb",
+ [":palms_up_together_tone2:"] = "\U0001f932\U0001f3fc",
+ [":palms_up_together_medium_light_skin_tone:"] = "\U0001f932\U0001f3fc",
+ [":palms_up_together::skin-tone-2:"] = "\U0001f932\U0001f3fc",
+ [":palms_up_together_tone3:"] = "\U0001f932\U0001f3fd",
+ [":palms_up_together_medium_skin_tone:"] = "\U0001f932\U0001f3fd",
+ [":palms_up_together::skin-tone-3:"] = "\U0001f932\U0001f3fd",
+ [":palms_up_together_tone4:"] = "\U0001f932\U0001f3fe",
+ [":palms_up_together_medium_dark_skin_tone:"] = "\U0001f932\U0001f3fe",
+ [":palms_up_together::skin-tone-4:"] = "\U0001f932\U0001f3fe",
+ [":palms_up_together_tone5:"] = "\U0001f932\U0001f3ff",
+ [":palms_up_together_dark_skin_tone:"] = "\U0001f932\U0001f3ff",
+ [":palms_up_together::skin-tone-5:"] = "\U0001f932\U0001f3ff",
+ [":pancakes:"] = "\U0001f95e",
+ [":panda_face:"] = "\U0001f43c",
+ [":paperclip:"] = "\U0001f4ce",
+ [":paperclips:"] = "\U0001f587\ufe0f",
+ [":linked_paperclips:"] = "\U0001f587\ufe0f",
+ [":parachute:"] = "\U0001fa82",
+ [":park:"] = "\U0001f3de\ufe0f",
+ [":national_park:"] = "\U0001f3de\ufe0f",
+ [":parking:"] = "\U0001f17f\ufe0f",
+ [":parrot:"] = "\U0001f99c",
+ [":part_alternation_mark:"] = "\u303d\ufe0f",
+ [":partly_sunny:"] = "\u26c5",
+ [":partying_face:"] = "\U0001f973",
+ [":passport_control:"] = "\U0001f6c2",
+ [":pause_button:"] = "\u23f8\ufe0f",
+ [":double_vertical_bar:"] = "\u23f8\ufe0f",
+ [":peace:"] = "\u262e\ufe0f",
+ [":peace_symbol:"] = "\u262e\ufe0f",
+ [":peach:"] = "\U0001f351",
+ [":peacock:"] = "\U0001f99a",
+ [":peanuts:"] = "\U0001f95c",
+ [":shelled_peanut:"] = "\U0001f95c",
+ [":pear:"] = "\U0001f350",
+ [":pen_ballpoint:"] = "\U0001f58a\ufe0f",
+ [":lower_left_ballpoint_pen:"] = "\U0001f58a\ufe0f",
+ [":pen_fountain:"] = "\U0001f58b\ufe0f",
+ [":lower_left_fountain_pen:"] = "\U0001f58b\ufe0f",
+ [":pencil:"] = "\U0001f4dd",
+ [":memo:"] = "\U0001f4dd",
+ [":pencil2:"] = "\u270f\ufe0f",
+ [":penguin:"] = "\U0001f427",
+ [":pensive:"] = "\U0001f614",
+ [":people_holding_hands:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone1_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_light_skin_tone_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone1_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_light_skin_tone_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone1_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone1_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_light_skin_tone_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone2_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_light_skin_tone_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone2_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_light_skin_tone_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone2_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone2_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_light_skin_tone_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone3_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_skin_tone_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone3_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_skin_tone_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone3_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_skin_tone_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone3_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_skin_tone_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone4_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_dark_skin_tone_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone4_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone4_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_dark_skin_tone_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone4_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_medium_dark_skin_tone_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone5:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone5_tone1:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_dark_skin_tone_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone5_tone2:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone5_tone3:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_dark_skin_tone_medium_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_tone5_tone4:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_holding_hands_dark_skin_tone_medium_dark_skin_tone:"] = "\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1",
+ [":people_hugging:"] = "\U0001fac2",
+ [":people_with_bunny_ears_partying:"] = "\U0001f46f",
+ [":dancers:"] = "\U0001f46f",
+ [":people_wrestling:"] = "\U0001f93c",
+ [":wrestlers:"] = "\U0001f93c",
+ [":wrestling:"] = "\U0001f93c",
+ [":performing_arts:"] = "\U0001f3ad",
+ [":persevere:"] = "\U0001f623",
+ [":person_bald:"] = "\U0001f9d1\u200d\U0001f9b2",
+ [":person_biking:"] = "\U0001f6b4",
+ [":bicyclist:"] = "\U0001f6b4",
+ [":person_biking_tone1:"] = "\U0001f6b4\U0001f3fb",
+ [":bicyclist_tone1:"] = "\U0001f6b4\U0001f3fb",
+ [":person_biking::skin-tone-1:"] = "\U0001f6b4\U0001f3fb",
+ [":bicyclist::skin-tone-1:"] = "\U0001f6b4\U0001f3fb",
+ [":person_biking_tone2:"] = "\U0001f6b4\U0001f3fc",
+ [":bicyclist_tone2:"] = "\U0001f6b4\U0001f3fc",
+ [":person_biking::skin-tone-2:"] = "\U0001f6b4\U0001f3fc",
+ [":bicyclist::skin-tone-2:"] = "\U0001f6b4\U0001f3fc",
+ [":person_biking_tone3:"] = "\U0001f6b4\U0001f3fd",
+ [":bicyclist_tone3:"] = "\U0001f6b4\U0001f3fd",
+ [":person_biking::skin-tone-3:"] = "\U0001f6b4\U0001f3fd",
+ [":bicyclist::skin-tone-3:"] = "\U0001f6b4\U0001f3fd",
+ [":person_biking_tone4:"] = "\U0001f6b4\U0001f3fe",
+ [":bicyclist_tone4:"] = "\U0001f6b4\U0001f3fe",
+ [":person_biking::skin-tone-4:"] = "\U0001f6b4\U0001f3fe",
+ [":bicyclist::skin-tone-4:"] = "\U0001f6b4\U0001f3fe",
+ [":person_biking_tone5:"] = "\U0001f6b4\U0001f3ff",
+ [":bicyclist_tone5:"] = "\U0001f6b4\U0001f3ff",
+ [":person_biking::skin-tone-5:"] = "\U0001f6b4\U0001f3ff",
+ [":bicyclist::skin-tone-5:"] = "\U0001f6b4\U0001f3ff",
+ [":person_bouncing_ball:"] = "\u26f9\ufe0f",
+ [":basketball_player:"] = "\u26f9\ufe0f",
+ [":person_with_ball:"] = "\u26f9\ufe0f",
+ [":person_bouncing_ball_tone1:"] = "\u26f9\U0001f3fb",
+ [":basketball_player_tone1:"] = "\u26f9\U0001f3fb",
+ [":person_with_ball_tone1:"] = "\u26f9\U0001f3fb",
+ [":person_bouncing_ball::skin-tone-1:"] = "\u26f9\U0001f3fb",
+ [":basketball_player::skin-tone-1:"] = "\u26f9\U0001f3fb",
+ [":person_with_ball::skin-tone-1:"] = "\u26f9\U0001f3fb",
+ [":person_bouncing_ball_tone2:"] = "\u26f9\U0001f3fc",
+ [":basketball_player_tone2:"] = "\u26f9\U0001f3fc",
+ [":person_with_ball_tone2:"] = "\u26f9\U0001f3fc",
+ [":person_bouncing_ball::skin-tone-2:"] = "\u26f9\U0001f3fc",
+ [":basketball_player::skin-tone-2:"] = "\u26f9\U0001f3fc",
+ [":person_with_ball::skin-tone-2:"] = "\u26f9\U0001f3fc",
+ [":person_bouncing_ball_tone3:"] = "\u26f9\U0001f3fd",
+ [":basketball_player_tone3:"] = "\u26f9\U0001f3fd",
+ [":person_with_ball_tone3:"] = "\u26f9\U0001f3fd",
+ [":person_bouncing_ball::skin-tone-3:"] = "\u26f9\U0001f3fd",
+ [":basketball_player::skin-tone-3:"] = "\u26f9\U0001f3fd",
+ [":person_with_ball::skin-tone-3:"] = "\u26f9\U0001f3fd",
+ [":person_bouncing_ball_tone4:"] = "\u26f9\U0001f3fe",
+ [":basketball_player_tone4:"] = "\u26f9\U0001f3fe",
+ [":person_with_ball_tone4:"] = "\u26f9\U0001f3fe",
+ [":person_bouncing_ball::skin-tone-4:"] = "\u26f9\U0001f3fe",
+ [":basketball_player::skin-tone-4:"] = "\u26f9\U0001f3fe",
+ [":person_with_ball::skin-tone-4:"] = "\u26f9\U0001f3fe",
+ [":person_bouncing_ball_tone5:"] = "\u26f9\U0001f3ff",
+ [":basketball_player_tone5:"] = "\u26f9\U0001f3ff",
+ [":person_with_ball_tone5:"] = "\u26f9\U0001f3ff",
+ [":person_bouncing_ball::skin-tone-5:"] = "\u26f9\U0001f3ff",
+ [":basketball_player::skin-tone-5:"] = "\u26f9\U0001f3ff",
+ [":person_with_ball::skin-tone-5:"] = "\u26f9\U0001f3ff",
+ [":person_bowing:"] = "\U0001f647",
+ [":bow:"] = "\U0001f647",
+ [":person_bowing_tone1:"] = "\U0001f647\U0001f3fb",
+ [":bow_tone1:"] = "\U0001f647\U0001f3fb",
+ [":person_bowing::skin-tone-1:"] = "\U0001f647\U0001f3fb",
+ [":bow::skin-tone-1:"] = "\U0001f647\U0001f3fb",
+ [":person_bowing_tone2:"] = "\U0001f647\U0001f3fc",
+ [":bow_tone2:"] = "\U0001f647\U0001f3fc",
+ [":person_bowing::skin-tone-2:"] = "\U0001f647\U0001f3fc",
+ [":bow::skin-tone-2:"] = "\U0001f647\U0001f3fc",
+ [":person_bowing_tone3:"] = "\U0001f647\U0001f3fd",
+ [":bow_tone3:"] = "\U0001f647\U0001f3fd",
+ [":person_bowing::skin-tone-3:"] = "\U0001f647\U0001f3fd",
+ [":bow::skin-tone-3:"] = "\U0001f647\U0001f3fd",
+ [":person_bowing_tone4:"] = "\U0001f647\U0001f3fe",
+ [":bow_tone4:"] = "\U0001f647\U0001f3fe",
+ [":person_bowing::skin-tone-4:"] = "\U0001f647\U0001f3fe",
+ [":bow::skin-tone-4:"] = "\U0001f647\U0001f3fe",
+ [":person_bowing_tone5:"] = "\U0001f647\U0001f3ff",
+ [":bow_tone5:"] = "\U0001f647\U0001f3ff",
+ [":person_bowing::skin-tone-5:"] = "\U0001f647\U0001f3ff",
+ [":bow::skin-tone-5:"] = "\U0001f647\U0001f3ff",
+ [":person_climbing:"] = "\U0001f9d7",
+ [":person_climbing_tone1:"] = "\U0001f9d7\U0001f3fb",
+ [":person_climbing_light_skin_tone:"] = "\U0001f9d7\U0001f3fb",
+ [":person_climbing::skin-tone-1:"] = "\U0001f9d7\U0001f3fb",
+ [":person_climbing_tone2:"] = "\U0001f9d7\U0001f3fc",
+ [":person_climbing_medium_light_skin_tone:"] = "\U0001f9d7\U0001f3fc",
+ [":person_climbing::skin-tone-2:"] = "\U0001f9d7\U0001f3fc",
+ [":person_climbing_tone3:"] = "\U0001f9d7\U0001f3fd",
+ [":person_climbing_medium_skin_tone:"] = "\U0001f9d7\U0001f3fd",
+ [":person_climbing::skin-tone-3:"] = "\U0001f9d7\U0001f3fd",
+ [":person_climbing_tone4:"] = "\U0001f9d7\U0001f3fe",
+ [":person_climbing_medium_dark_skin_tone:"] = "\U0001f9d7\U0001f3fe",
+ [":person_climbing::skin-tone-4:"] = "\U0001f9d7\U0001f3fe",
+ [":person_climbing_tone5:"] = "\U0001f9d7\U0001f3ff",
+ [":person_climbing_dark_skin_tone:"] = "\U0001f9d7\U0001f3ff",
+ [":person_climbing::skin-tone-5:"] = "\U0001f9d7\U0001f3ff",
+ [":person_curly_hair:"] = "\U0001f9d1\u200d\U0001f9b1",
+ [":person_doing_cartwheel:"] = "\U0001f938",
+ [":cartwheel:"] = "\U0001f938",
+ [":person_doing_cartwheel_tone1:"] = "\U0001f938\U0001f3fb",
+ [":cartwheel_tone1:"] = "\U0001f938\U0001f3fb",
+ [":person_doing_cartwheel::skin-tone-1:"] = "\U0001f938\U0001f3fb",
+ [":cartwheel::skin-tone-1:"] = "\U0001f938\U0001f3fb",
+ [":person_doing_cartwheel_tone2:"] = "\U0001f938\U0001f3fc",
+ [":cartwheel_tone2:"] = "\U0001f938\U0001f3fc",
+ [":person_doing_cartwheel::skin-tone-2:"] = "\U0001f938\U0001f3fc",
+ [":cartwheel::skin-tone-2:"] = "\U0001f938\U0001f3fc",
+ [":person_doing_cartwheel_tone3:"] = "\U0001f938\U0001f3fd",
+ [":cartwheel_tone3:"] = "\U0001f938\U0001f3fd",
+ [":person_doing_cartwheel::skin-tone-3:"] = "\U0001f938\U0001f3fd",
+ [":cartwheel::skin-tone-3:"] = "\U0001f938\U0001f3fd",
+ [":person_doing_cartwheel_tone4:"] = "\U0001f938\U0001f3fe",
+ [":cartwheel_tone4:"] = "\U0001f938\U0001f3fe",
+ [":person_doing_cartwheel::skin-tone-4:"] = "\U0001f938\U0001f3fe",
+ [":cartwheel::skin-tone-4:"] = "\U0001f938\U0001f3fe",
+ [":person_doing_cartwheel_tone5:"] = "\U0001f938\U0001f3ff",
+ [":cartwheel_tone5:"] = "\U0001f938\U0001f3ff",
+ [":person_doing_cartwheel::skin-tone-5:"] = "\U0001f938\U0001f3ff",
+ [":cartwheel::skin-tone-5:"] = "\U0001f938\U0001f3ff",
+ [":person_facepalming:"] = "\U0001f926",
+ [":face_palm:"] = "\U0001f926",
+ [":facepalm:"] = "\U0001f926",
+ [":person_facepalming_tone1:"] = "\U0001f926\U0001f3fb",
+ [":face_palm_tone1:"] = "\U0001f926\U0001f3fb",
+ [":facepalm_tone1:"] = "\U0001f926\U0001f3fb",
+ [":person_facepalming::skin-tone-1:"] = "\U0001f926\U0001f3fb",
+ [":face_palm::skin-tone-1:"] = "\U0001f926\U0001f3fb",
+ [":facepalm::skin-tone-1:"] = "\U0001f926\U0001f3fb",
+ [":person_facepalming_tone2:"] = "\U0001f926\U0001f3fc",
+ [":face_palm_tone2:"] = "\U0001f926\U0001f3fc",
+ [":facepalm_tone2:"] = "\U0001f926\U0001f3fc",
+ [":person_facepalming::skin-tone-2:"] = "\U0001f926\U0001f3fc",
+ [":face_palm::skin-tone-2:"] = "\U0001f926\U0001f3fc",
+ [":facepalm::skin-tone-2:"] = "\U0001f926\U0001f3fc",
+ [":person_facepalming_tone3:"] = "\U0001f926\U0001f3fd",
+ [":face_palm_tone3:"] = "\U0001f926\U0001f3fd",
+ [":facepalm_tone3:"] = "\U0001f926\U0001f3fd",
+ [":person_facepalming::skin-tone-3:"] = "\U0001f926\U0001f3fd",
+ [":face_palm::skin-tone-3:"] = "\U0001f926\U0001f3fd",
+ [":facepalm::skin-tone-3:"] = "\U0001f926\U0001f3fd",
+ [":person_facepalming_tone4:"] = "\U0001f926\U0001f3fe",
+ [":face_palm_tone4:"] = "\U0001f926\U0001f3fe",
+ [":facepalm_tone4:"] = "\U0001f926\U0001f3fe",
+ [":person_facepalming::skin-tone-4:"] = "\U0001f926\U0001f3fe",
+ [":face_palm::skin-tone-4:"] = "\U0001f926\U0001f3fe",
+ [":facepalm::skin-tone-4:"] = "\U0001f926\U0001f3fe",
+ [":person_facepalming_tone5:"] = "\U0001f926\U0001f3ff",
+ [":face_palm_tone5:"] = "\U0001f926\U0001f3ff",
+ [":facepalm_tone5:"] = "\U0001f926\U0001f3ff",
+ [":person_facepalming::skin-tone-5:"] = "\U0001f926\U0001f3ff",
+ [":face_palm::skin-tone-5:"] = "\U0001f926\U0001f3ff",
+ [":facepalm::skin-tone-5:"] = "\U0001f926\U0001f3ff",
+ [":person_feeding_baby:"] = "\U0001f9d1\u200d\U0001f37c",
+ [":person_feeding_baby_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f37c",
+ [":person_feeding_baby_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f37c",
+ [":person_feeding_baby::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f37c",
+ [":person_feeding_baby_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f37c",
+ [":person_feeding_baby_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f37c",
+ [":person_feeding_baby::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f37c",
+ [":person_feeding_baby_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f37c",
+ [":person_feeding_baby_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f37c",
+ [":person_feeding_baby::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f37c",
+ [":person_feeding_baby_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f37c",
+ [":person_feeding_baby_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f37c",
+ [":person_feeding_baby::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f37c",
+ [":person_feeding_baby_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f37c",
+ [":person_feeding_baby_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f37c",
+ [":person_feeding_baby::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f37c",
+ [":person_fencing:"] = "\U0001f93a",
+ [":fencer:"] = "\U0001f93a",
+ [":fencing:"] = "\U0001f93a",
+ [":person_frowning:"] = "\U0001f64d",
+ [":person_frowning_tone1:"] = "\U0001f64d\U0001f3fb",
+ [":person_frowning::skin-tone-1:"] = "\U0001f64d\U0001f3fb",
+ [":person_frowning_tone2:"] = "\U0001f64d\U0001f3fc",
+ [":person_frowning::skin-tone-2:"] = "\U0001f64d\U0001f3fc",
+ [":person_frowning_tone3:"] = "\U0001f64d\U0001f3fd",
+ [":person_frowning::skin-tone-3:"] = "\U0001f64d\U0001f3fd",
+ [":person_frowning_tone4:"] = "\U0001f64d\U0001f3fe",
+ [":person_frowning::skin-tone-4:"] = "\U0001f64d\U0001f3fe",
+ [":person_frowning_tone5:"] = "\U0001f64d\U0001f3ff",
+ [":person_frowning::skin-tone-5:"] = "\U0001f64d\U0001f3ff",
+ [":person_gesturing_no:"] = "\U0001f645",
+ [":no_good:"] = "\U0001f645",
+ [":person_gesturing_no_tone1:"] = "\U0001f645\U0001f3fb",
+ [":no_good_tone1:"] = "\U0001f645\U0001f3fb",
+ [":person_gesturing_no::skin-tone-1:"] = "\U0001f645\U0001f3fb",
+ [":no_good::skin-tone-1:"] = "\U0001f645\U0001f3fb",
+ [":person_gesturing_no_tone2:"] = "\U0001f645\U0001f3fc",
+ [":no_good_tone2:"] = "\U0001f645\U0001f3fc",
+ [":person_gesturing_no::skin-tone-2:"] = "\U0001f645\U0001f3fc",
+ [":no_good::skin-tone-2:"] = "\U0001f645\U0001f3fc",
+ [":person_gesturing_no_tone3:"] = "\U0001f645\U0001f3fd",
+ [":no_good_tone3:"] = "\U0001f645\U0001f3fd",
+ [":person_gesturing_no::skin-tone-3:"] = "\U0001f645\U0001f3fd",
+ [":no_good::skin-tone-3:"] = "\U0001f645\U0001f3fd",
+ [":person_gesturing_no_tone4:"] = "\U0001f645\U0001f3fe",
+ [":no_good_tone4:"] = "\U0001f645\U0001f3fe",
+ [":person_gesturing_no::skin-tone-4:"] = "\U0001f645\U0001f3fe",
+ [":no_good::skin-tone-4:"] = "\U0001f645\U0001f3fe",
+ [":person_gesturing_no_tone5:"] = "\U0001f645\U0001f3ff",
+ [":no_good_tone5:"] = "\U0001f645\U0001f3ff",
+ [":person_gesturing_no::skin-tone-5:"] = "\U0001f645\U0001f3ff",
+ [":no_good::skin-tone-5:"] = "\U0001f645\U0001f3ff",
+ [":person_gesturing_ok:"] = "\U0001f646",
+ [":ok_woman:"] = "\U0001f646",
+ [":person_gesturing_ok_tone1:"] = "\U0001f646\U0001f3fb",
+ [":ok_woman_tone1:"] = "\U0001f646\U0001f3fb",
+ [":person_gesturing_ok::skin-tone-1:"] = "\U0001f646\U0001f3fb",
+ [":ok_woman::skin-tone-1:"] = "\U0001f646\U0001f3fb",
+ [":person_gesturing_ok_tone2:"] = "\U0001f646\U0001f3fc",
+ [":ok_woman_tone2:"] = "\U0001f646\U0001f3fc",
+ [":person_gesturing_ok::skin-tone-2:"] = "\U0001f646\U0001f3fc",
+ [":ok_woman::skin-tone-2:"] = "\U0001f646\U0001f3fc",
+ [":person_gesturing_ok_tone3:"] = "\U0001f646\U0001f3fd",
+ [":ok_woman_tone3:"] = "\U0001f646\U0001f3fd",
+ [":person_gesturing_ok::skin-tone-3:"] = "\U0001f646\U0001f3fd",
+ [":ok_woman::skin-tone-3:"] = "\U0001f646\U0001f3fd",
+ [":person_gesturing_ok_tone4:"] = "\U0001f646\U0001f3fe",
+ [":ok_woman_tone4:"] = "\U0001f646\U0001f3fe",
+ [":person_gesturing_ok::skin-tone-4:"] = "\U0001f646\U0001f3fe",
+ [":ok_woman::skin-tone-4:"] = "\U0001f646\U0001f3fe",
+ [":person_gesturing_ok_tone5:"] = "\U0001f646\U0001f3ff",
+ [":ok_woman_tone5:"] = "\U0001f646\U0001f3ff",
+ [":person_gesturing_ok::skin-tone-5:"] = "\U0001f646\U0001f3ff",
+ [":ok_woman::skin-tone-5:"] = "\U0001f646\U0001f3ff",
+ [":person_getting_haircut:"] = "\U0001f487",
+ [":haircut:"] = "\U0001f487",
+ [":person_getting_haircut_tone1:"] = "\U0001f487\U0001f3fb",
+ [":haircut_tone1:"] = "\U0001f487\U0001f3fb",
+ [":person_getting_haircut::skin-tone-1:"] = "\U0001f487\U0001f3fb",
+ [":haircut::skin-tone-1:"] = "\U0001f487\U0001f3fb",
+ [":person_getting_haircut_tone2:"] = "\U0001f487\U0001f3fc",
+ [":haircut_tone2:"] = "\U0001f487\U0001f3fc",
+ [":person_getting_haircut::skin-tone-2:"] = "\U0001f487\U0001f3fc",
+ [":haircut::skin-tone-2:"] = "\U0001f487\U0001f3fc",
+ [":person_getting_haircut_tone3:"] = "\U0001f487\U0001f3fd",
+ [":haircut_tone3:"] = "\U0001f487\U0001f3fd",
+ [":person_getting_haircut::skin-tone-3:"] = "\U0001f487\U0001f3fd",
+ [":haircut::skin-tone-3:"] = "\U0001f487\U0001f3fd",
+ [":person_getting_haircut_tone4:"] = "\U0001f487\U0001f3fe",
+ [":haircut_tone4:"] = "\U0001f487\U0001f3fe",
+ [":person_getting_haircut::skin-tone-4:"] = "\U0001f487\U0001f3fe",
+ [":haircut::skin-tone-4:"] = "\U0001f487\U0001f3fe",
+ [":person_getting_haircut_tone5:"] = "\U0001f487\U0001f3ff",
+ [":haircut_tone5:"] = "\U0001f487\U0001f3ff",
+ [":person_getting_haircut::skin-tone-5:"] = "\U0001f487\U0001f3ff",
+ [":haircut::skin-tone-5:"] = "\U0001f487\U0001f3ff",
+ [":person_getting_massage:"] = "\U0001f486",
+ [":massage:"] = "\U0001f486",
+ [":person_getting_massage_tone1:"] = "\U0001f486\U0001f3fb",
+ [":massage_tone1:"] = "\U0001f486\U0001f3fb",
+ [":person_getting_massage::skin-tone-1:"] = "\U0001f486\U0001f3fb",
+ [":massage::skin-tone-1:"] = "\U0001f486\U0001f3fb",
+ [":person_getting_massage_tone2:"] = "\U0001f486\U0001f3fc",
+ [":massage_tone2:"] = "\U0001f486\U0001f3fc",
+ [":person_getting_massage::skin-tone-2:"] = "\U0001f486\U0001f3fc",
+ [":massage::skin-tone-2:"] = "\U0001f486\U0001f3fc",
+ [":person_getting_massage_tone3:"] = "\U0001f486\U0001f3fd",
+ [":massage_tone3:"] = "\U0001f486\U0001f3fd",
+ [":person_getting_massage::skin-tone-3:"] = "\U0001f486\U0001f3fd",
+ [":massage::skin-tone-3:"] = "\U0001f486\U0001f3fd",
+ [":person_getting_massage_tone4:"] = "\U0001f486\U0001f3fe",
+ [":massage_tone4:"] = "\U0001f486\U0001f3fe",
+ [":person_getting_massage::skin-tone-4:"] = "\U0001f486\U0001f3fe",
+ [":massage::skin-tone-4:"] = "\U0001f486\U0001f3fe",
+ [":person_getting_massage_tone5:"] = "\U0001f486\U0001f3ff",
+ [":massage_tone5:"] = "\U0001f486\U0001f3ff",
+ [":person_getting_massage::skin-tone-5:"] = "\U0001f486\U0001f3ff",
+ [":massage::skin-tone-5:"] = "\U0001f486\U0001f3ff",
+ [":person_golfing:"] = "\U0001f3cc\ufe0f",
+ [":golfer:"] = "\U0001f3cc\ufe0f",
+ [":person_golfing_tone1:"] = "\U0001f3cc\U0001f3fb",
+ [":person_golfing_light_skin_tone:"] = "\U0001f3cc\U0001f3fb",
+ [":person_golfing::skin-tone-1:"] = "\U0001f3cc\U0001f3fb",
+ [":golfer::skin-tone-1:"] = "\U0001f3cc\U0001f3fb",
+ [":person_golfing_tone2:"] = "\U0001f3cc\U0001f3fc",
+ [":person_golfing_medium_light_skin_tone:"] = "\U0001f3cc\U0001f3fc",
+ [":person_golfing::skin-tone-2:"] = "\U0001f3cc\U0001f3fc",
+ [":golfer::skin-tone-2:"] = "\U0001f3cc\U0001f3fc",
+ [":person_golfing_tone3:"] = "\U0001f3cc\U0001f3fd",
+ [":person_golfing_medium_skin_tone:"] = "\U0001f3cc\U0001f3fd",
+ [":person_golfing::skin-tone-3:"] = "\U0001f3cc\U0001f3fd",
+ [":golfer::skin-tone-3:"] = "\U0001f3cc\U0001f3fd",
+ [":person_golfing_tone4:"] = "\U0001f3cc\U0001f3fe",
+ [":person_golfing_medium_dark_skin_tone:"] = "\U0001f3cc\U0001f3fe",
+ [":person_golfing::skin-tone-4:"] = "\U0001f3cc\U0001f3fe",
+ [":golfer::skin-tone-4:"] = "\U0001f3cc\U0001f3fe",
+ [":person_golfing_tone5:"] = "\U0001f3cc\U0001f3ff",
+ [":person_golfing_dark_skin_tone:"] = "\U0001f3cc\U0001f3ff",
+ [":person_golfing::skin-tone-5:"] = "\U0001f3cc\U0001f3ff",
+ [":golfer::skin-tone-5:"] = "\U0001f3cc\U0001f3ff",
+ [":person_in_bed_tone1:"] = "\U0001f6cc\U0001f3fb",
+ [":person_in_bed_light_skin_tone:"] = "\U0001f6cc\U0001f3fb",
+ [":sleeping_accommodation::skin-tone-1:"] = "\U0001f6cc\U0001f3fb",
+ [":person_in_bed_tone2:"] = "\U0001f6cc\U0001f3fc",
+ [":person_in_bed_medium_light_skin_tone:"] = "\U0001f6cc\U0001f3fc",
+ [":sleeping_accommodation::skin-tone-2:"] = "\U0001f6cc\U0001f3fc",
+ [":person_in_bed_tone3:"] = "\U0001f6cc\U0001f3fd",
+ [":person_in_bed_medium_skin_tone:"] = "\U0001f6cc\U0001f3fd",
+ [":sleeping_accommodation::skin-tone-3:"] = "\U0001f6cc\U0001f3fd",
+ [":person_in_bed_tone4:"] = "\U0001f6cc\U0001f3fe",
+ [":person_in_bed_medium_dark_skin_tone:"] = "\U0001f6cc\U0001f3fe",
+ [":sleeping_accommodation::skin-tone-4:"] = "\U0001f6cc\U0001f3fe",
+ [":person_in_bed_tone5:"] = "\U0001f6cc\U0001f3ff",
+ [":person_in_bed_dark_skin_tone:"] = "\U0001f6cc\U0001f3ff",
+ [":sleeping_accommodation::skin-tone-5:"] = "\U0001f6cc\U0001f3ff",
+ [":person_in_lotus_position:"] = "\U0001f9d8",
+ [":person_in_lotus_position_tone1:"] = "\U0001f9d8\U0001f3fb",
+ [":person_in_lotus_position_light_skin_tone:"] = "\U0001f9d8\U0001f3fb",
+ [":person_in_lotus_position::skin-tone-1:"] = "\U0001f9d8\U0001f3fb",
+ [":person_in_lotus_position_tone2:"] = "\U0001f9d8\U0001f3fc",
+ [":person_in_lotus_position_medium_light_skin_tone:"] = "\U0001f9d8\U0001f3fc",
+ [":person_in_lotus_position::skin-tone-2:"] = "\U0001f9d8\U0001f3fc",
+ [":person_in_lotus_position_tone3:"] = "\U0001f9d8\U0001f3fd",
+ [":person_in_lotus_position_medium_skin_tone:"] = "\U0001f9d8\U0001f3fd",
+ [":person_in_lotus_position::skin-tone-3:"] = "\U0001f9d8\U0001f3fd",
+ [":person_in_lotus_position_tone4:"] = "\U0001f9d8\U0001f3fe",
+ [":person_in_lotus_position_medium_dark_skin_tone:"] = "\U0001f9d8\U0001f3fe",
+ [":person_in_lotus_position::skin-tone-4:"] = "\U0001f9d8\U0001f3fe",
+ [":person_in_lotus_position_tone5:"] = "\U0001f9d8\U0001f3ff",
+ [":person_in_lotus_position_dark_skin_tone:"] = "\U0001f9d8\U0001f3ff",
+ [":person_in_lotus_position::skin-tone-5:"] = "\U0001f9d8\U0001f3ff",
+ [":person_in_manual_wheelchair:"] = "\U0001f9d1\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bd",
+ [":person_in_manual_wheelchair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bd",
+ [":person_in_motorized_wheelchair:"] = "\U0001f9d1\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bc",
+ [":person_in_motorized_wheelchair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9bc",
+ [":person_in_steamy_room:"] = "\U0001f9d6",
+ [":person_in_steamy_room_tone1:"] = "\U0001f9d6\U0001f3fb",
+ [":person_in_steamy_room_light_skin_tone:"] = "\U0001f9d6\U0001f3fb",
+ [":person_in_steamy_room::skin-tone-1:"] = "\U0001f9d6\U0001f3fb",
+ [":person_in_steamy_room_tone2:"] = "\U0001f9d6\U0001f3fc",
+ [":person_in_steamy_room_medium_light_skin_tone:"] = "\U0001f9d6\U0001f3fc",
+ [":person_in_steamy_room::skin-tone-2:"] = "\U0001f9d6\U0001f3fc",
+ [":person_in_steamy_room_tone3:"] = "\U0001f9d6\U0001f3fd",
+ [":person_in_steamy_room_medium_skin_tone:"] = "\U0001f9d6\U0001f3fd",
+ [":person_in_steamy_room::skin-tone-3:"] = "\U0001f9d6\U0001f3fd",
+ [":person_in_steamy_room_tone4:"] = "\U0001f9d6\U0001f3fe",
+ [":person_in_steamy_room_medium_dark_skin_tone:"] = "\U0001f9d6\U0001f3fe",
+ [":person_in_steamy_room::skin-tone-4:"] = "\U0001f9d6\U0001f3fe",
+ [":person_in_steamy_room_tone5:"] = "\U0001f9d6\U0001f3ff",
+ [":person_in_steamy_room_dark_skin_tone:"] = "\U0001f9d6\U0001f3ff",
+ [":person_in_steamy_room::skin-tone-5:"] = "\U0001f9d6\U0001f3ff",
+ [":person_in_tuxedo:"] = "\U0001f935",
+ [":person_in_tuxedo_tone1:"] = "\U0001f935\U0001f3fb",
+ [":tuxedo_tone1:"] = "\U0001f935\U0001f3fb",
+ [":person_in_tuxedo::skin-tone-1:"] = "\U0001f935\U0001f3fb",
+ [":person_in_tuxedo_tone2:"] = "\U0001f935\U0001f3fc",
+ [":tuxedo_tone2:"] = "\U0001f935\U0001f3fc",
+ [":person_in_tuxedo::skin-tone-2:"] = "\U0001f935\U0001f3fc",
+ [":person_in_tuxedo_tone3:"] = "\U0001f935\U0001f3fd",
+ [":tuxedo_tone3:"] = "\U0001f935\U0001f3fd",
+ [":person_in_tuxedo::skin-tone-3:"] = "\U0001f935\U0001f3fd",
+ [":person_in_tuxedo_tone4:"] = "\U0001f935\U0001f3fe",
+ [":tuxedo_tone4:"] = "\U0001f935\U0001f3fe",
+ [":person_in_tuxedo::skin-tone-4:"] = "\U0001f935\U0001f3fe",
+ [":person_in_tuxedo_tone5:"] = "\U0001f935\U0001f3ff",
+ [":tuxedo_tone5:"] = "\U0001f935\U0001f3ff",
+ [":person_in_tuxedo::skin-tone-5:"] = "\U0001f935\U0001f3ff",
+ [":person_juggling:"] = "\U0001f939",
+ [":juggling:"] = "\U0001f939",
+ [":juggler:"] = "\U0001f939",
+ [":person_juggling_tone1:"] = "\U0001f939\U0001f3fb",
+ [":juggling_tone1:"] = "\U0001f939\U0001f3fb",
+ [":juggler_tone1:"] = "\U0001f939\U0001f3fb",
+ [":person_juggling::skin-tone-1:"] = "\U0001f939\U0001f3fb",
+ [":juggling::skin-tone-1:"] = "\U0001f939\U0001f3fb",
+ [":juggler::skin-tone-1:"] = "\U0001f939\U0001f3fb",
+ [":person_juggling_tone2:"] = "\U0001f939\U0001f3fc",
+ [":juggling_tone2:"] = "\U0001f939\U0001f3fc",
+ [":juggler_tone2:"] = "\U0001f939\U0001f3fc",
+ [":person_juggling::skin-tone-2:"] = "\U0001f939\U0001f3fc",
+ [":juggling::skin-tone-2:"] = "\U0001f939\U0001f3fc",
+ [":juggler::skin-tone-2:"] = "\U0001f939\U0001f3fc",
+ [":person_juggling_tone3:"] = "\U0001f939\U0001f3fd",
+ [":juggling_tone3:"] = "\U0001f939\U0001f3fd",
+ [":juggler_tone3:"] = "\U0001f939\U0001f3fd",
+ [":person_juggling::skin-tone-3:"] = "\U0001f939\U0001f3fd",
+ [":juggling::skin-tone-3:"] = "\U0001f939\U0001f3fd",
+ [":juggler::skin-tone-3:"] = "\U0001f939\U0001f3fd",
+ [":person_juggling_tone4:"] = "\U0001f939\U0001f3fe",
+ [":juggling_tone4:"] = "\U0001f939\U0001f3fe",
+ [":juggler_tone4:"] = "\U0001f939\U0001f3fe",
+ [":person_juggling::skin-tone-4:"] = "\U0001f939\U0001f3fe",
+ [":juggling::skin-tone-4:"] = "\U0001f939\U0001f3fe",
+ [":juggler::skin-tone-4:"] = "\U0001f939\U0001f3fe",
+ [":person_juggling_tone5:"] = "\U0001f939\U0001f3ff",
+ [":juggling_tone5:"] = "\U0001f939\U0001f3ff",
+ [":juggler_tone5:"] = "\U0001f939\U0001f3ff",
+ [":person_juggling::skin-tone-5:"] = "\U0001f939\U0001f3ff",
+ [":juggling::skin-tone-5:"] = "\U0001f939\U0001f3ff",
+ [":juggler::skin-tone-5:"] = "\U0001f939\U0001f3ff",
+ [":person_kneeling:"] = "\U0001f9ce",
+ [":person_kneeling_tone1:"] = "\U0001f9ce\U0001f3fb",
+ [":person_kneeling_light_skin_tone:"] = "\U0001f9ce\U0001f3fb",
+ [":person_kneeling::skin-tone-1:"] = "\U0001f9ce\U0001f3fb",
+ [":person_kneeling_tone2:"] = "\U0001f9ce\U0001f3fc",
+ [":person_kneeling_medium_light_skin_tone:"] = "\U0001f9ce\U0001f3fc",
+ [":person_kneeling::skin-tone-2:"] = "\U0001f9ce\U0001f3fc",
+ [":person_kneeling_tone3:"] = "\U0001f9ce\U0001f3fd",
+ [":person_kneeling_medium_skin_tone:"] = "\U0001f9ce\U0001f3fd",
+ [":person_kneeling::skin-tone-3:"] = "\U0001f9ce\U0001f3fd",
+ [":person_kneeling_tone4:"] = "\U0001f9ce\U0001f3fe",
+ [":person_kneeling_medium_dark_skin_tone:"] = "\U0001f9ce\U0001f3fe",
+ [":person_kneeling::skin-tone-4:"] = "\U0001f9ce\U0001f3fe",
+ [":person_kneeling_tone5:"] = "\U0001f9ce\U0001f3ff",
+ [":person_kneeling_dark_skin_tone:"] = "\U0001f9ce\U0001f3ff",
+ [":person_kneeling::skin-tone-5:"] = "\U0001f9ce\U0001f3ff",
+ [":person_lifting_weights:"] = "\U0001f3cb\ufe0f",
+ [":lifter:"] = "\U0001f3cb\ufe0f",
+ [":weight_lifter:"] = "\U0001f3cb\ufe0f",
+ [":person_lifting_weights_tone1:"] = "\U0001f3cb\U0001f3fb",
+ [":lifter_tone1:"] = "\U0001f3cb\U0001f3fb",
+ [":weight_lifter_tone1:"] = "\U0001f3cb\U0001f3fb",
+ [":person_lifting_weights::skin-tone-1:"] = "\U0001f3cb\U0001f3fb",
+ [":lifter::skin-tone-1:"] = "\U0001f3cb\U0001f3fb",
+ [":weight_lifter::skin-tone-1:"] = "\U0001f3cb\U0001f3fb",
+ [":person_lifting_weights_tone2:"] = "\U0001f3cb\U0001f3fc",
+ [":lifter_tone2:"] = "\U0001f3cb\U0001f3fc",
+ [":weight_lifter_tone2:"] = "\U0001f3cb\U0001f3fc",
+ [":person_lifting_weights::skin-tone-2:"] = "\U0001f3cb\U0001f3fc",
+ [":lifter::skin-tone-2:"] = "\U0001f3cb\U0001f3fc",
+ [":weight_lifter::skin-tone-2:"] = "\U0001f3cb\U0001f3fc",
+ [":person_lifting_weights_tone3:"] = "\U0001f3cb\U0001f3fd",
+ [":lifter_tone3:"] = "\U0001f3cb\U0001f3fd",
+ [":weight_lifter_tone3:"] = "\U0001f3cb\U0001f3fd",
+ [":person_lifting_weights::skin-tone-3:"] = "\U0001f3cb\U0001f3fd",
+ [":lifter::skin-tone-3:"] = "\U0001f3cb\U0001f3fd",
+ [":weight_lifter::skin-tone-3:"] = "\U0001f3cb\U0001f3fd",
+ [":person_lifting_weights_tone4:"] = "\U0001f3cb\U0001f3fe",
+ [":lifter_tone4:"] = "\U0001f3cb\U0001f3fe",
+ [":weight_lifter_tone4:"] = "\U0001f3cb\U0001f3fe",
+ [":person_lifting_weights::skin-tone-4:"] = "\U0001f3cb\U0001f3fe",
+ [":lifter::skin-tone-4:"] = "\U0001f3cb\U0001f3fe",
+ [":weight_lifter::skin-tone-4:"] = "\U0001f3cb\U0001f3fe",
+ [":person_lifting_weights_tone5:"] = "\U0001f3cb\U0001f3ff",
+ [":lifter_tone5:"] = "\U0001f3cb\U0001f3ff",
+ [":weight_lifter_tone5:"] = "\U0001f3cb\U0001f3ff",
+ [":person_lifting_weights::skin-tone-5:"] = "\U0001f3cb\U0001f3ff",
+ [":lifter::skin-tone-5:"] = "\U0001f3cb\U0001f3ff",
+ [":weight_lifter::skin-tone-5:"] = "\U0001f3cb\U0001f3ff",
+ [":person_mountain_biking:"] = "\U0001f6b5",
+ [":mountain_bicyclist:"] = "\U0001f6b5",
+ [":person_mountain_biking_tone1:"] = "\U0001f6b5\U0001f3fb",
+ [":mountain_bicyclist_tone1:"] = "\U0001f6b5\U0001f3fb",
+ [":person_mountain_biking::skin-tone-1:"] = "\U0001f6b5\U0001f3fb",
+ [":mountain_bicyclist::skin-tone-1:"] = "\U0001f6b5\U0001f3fb",
+ [":person_mountain_biking_tone2:"] = "\U0001f6b5\U0001f3fc",
+ [":mountain_bicyclist_tone2:"] = "\U0001f6b5\U0001f3fc",
+ [":person_mountain_biking::skin-tone-2:"] = "\U0001f6b5\U0001f3fc",
+ [":mountain_bicyclist::skin-tone-2:"] = "\U0001f6b5\U0001f3fc",
+ [":person_mountain_biking_tone3:"] = "\U0001f6b5\U0001f3fd",
+ [":mountain_bicyclist_tone3:"] = "\U0001f6b5\U0001f3fd",
+ [":person_mountain_biking::skin-tone-3:"] = "\U0001f6b5\U0001f3fd",
+ [":mountain_bicyclist::skin-tone-3:"] = "\U0001f6b5\U0001f3fd",
+ [":person_mountain_biking_tone4:"] = "\U0001f6b5\U0001f3fe",
+ [":mountain_bicyclist_tone4:"] = "\U0001f6b5\U0001f3fe",
+ [":person_mountain_biking::skin-tone-4:"] = "\U0001f6b5\U0001f3fe",
+ [":mountain_bicyclist::skin-tone-4:"] = "\U0001f6b5\U0001f3fe",
+ [":person_mountain_biking_tone5:"] = "\U0001f6b5\U0001f3ff",
+ [":mountain_bicyclist_tone5:"] = "\U0001f6b5\U0001f3ff",
+ [":person_mountain_biking::skin-tone-5:"] = "\U0001f6b5\U0001f3ff",
+ [":mountain_bicyclist::skin-tone-5:"] = "\U0001f6b5\U0001f3ff",
+ [":person_playing_handball:"] = "\U0001f93e",
+ [":handball:"] = "\U0001f93e",
+ [":person_playing_handball_tone1:"] = "\U0001f93e\U0001f3fb",
+ [":handball_tone1:"] = "\U0001f93e\U0001f3fb",
+ [":person_playing_handball::skin-tone-1:"] = "\U0001f93e\U0001f3fb",
+ [":handball::skin-tone-1:"] = "\U0001f93e\U0001f3fb",
+ [":person_playing_handball_tone2:"] = "\U0001f93e\U0001f3fc",
+ [":handball_tone2:"] = "\U0001f93e\U0001f3fc",
+ [":person_playing_handball::skin-tone-2:"] = "\U0001f93e\U0001f3fc",
+ [":handball::skin-tone-2:"] = "\U0001f93e\U0001f3fc",
+ [":person_playing_handball_tone3:"] = "\U0001f93e\U0001f3fd",
+ [":handball_tone3:"] = "\U0001f93e\U0001f3fd",
+ [":person_playing_handball::skin-tone-3:"] = "\U0001f93e\U0001f3fd",
+ [":handball::skin-tone-3:"] = "\U0001f93e\U0001f3fd",
+ [":person_playing_handball_tone4:"] = "\U0001f93e\U0001f3fe",
+ [":handball_tone4:"] = "\U0001f93e\U0001f3fe",
+ [":person_playing_handball::skin-tone-4:"] = "\U0001f93e\U0001f3fe",
+ [":handball::skin-tone-4:"] = "\U0001f93e\U0001f3fe",
+ [":person_playing_handball_tone5:"] = "\U0001f93e\U0001f3ff",
+ [":handball_tone5:"] = "\U0001f93e\U0001f3ff",
+ [":person_playing_handball::skin-tone-5:"] = "\U0001f93e\U0001f3ff",
+ [":handball::skin-tone-5:"] = "\U0001f93e\U0001f3ff",
+ [":person_playing_water_polo:"] = "\U0001f93d",
+ [":water_polo:"] = "\U0001f93d",
+ [":person_playing_water_polo_tone1:"] = "\U0001f93d\U0001f3fb",
+ [":water_polo_tone1:"] = "\U0001f93d\U0001f3fb",
+ [":person_playing_water_polo::skin-tone-1:"] = "\U0001f93d\U0001f3fb",
+ [":water_polo::skin-tone-1:"] = "\U0001f93d\U0001f3fb",
+ [":person_playing_water_polo_tone2:"] = "\U0001f93d\U0001f3fc",
+ [":water_polo_tone2:"] = "\U0001f93d\U0001f3fc",
+ [":person_playing_water_polo::skin-tone-2:"] = "\U0001f93d\U0001f3fc",
+ [":water_polo::skin-tone-2:"] = "\U0001f93d\U0001f3fc",
+ [":person_playing_water_polo_tone3:"] = "\U0001f93d\U0001f3fd",
+ [":water_polo_tone3:"] = "\U0001f93d\U0001f3fd",
+ [":person_playing_water_polo::skin-tone-3:"] = "\U0001f93d\U0001f3fd",
+ [":water_polo::skin-tone-3:"] = "\U0001f93d\U0001f3fd",
+ [":person_playing_water_polo_tone4:"] = "\U0001f93d\U0001f3fe",
+ [":water_polo_tone4:"] = "\U0001f93d\U0001f3fe",
+ [":person_playing_water_polo::skin-tone-4:"] = "\U0001f93d\U0001f3fe",
+ [":water_polo::skin-tone-4:"] = "\U0001f93d\U0001f3fe",
+ [":person_playing_water_polo_tone5:"] = "\U0001f93d\U0001f3ff",
+ [":water_polo_tone5:"] = "\U0001f93d\U0001f3ff",
+ [":person_playing_water_polo::skin-tone-5:"] = "\U0001f93d\U0001f3ff",
+ [":water_polo::skin-tone-5:"] = "\U0001f93d\U0001f3ff",
+ [":person_pouting:"] = "\U0001f64e",
+ [":person_with_pouting_face:"] = "\U0001f64e",
+ [":person_pouting_tone1:"] = "\U0001f64e\U0001f3fb",
+ [":person_with_pouting_face_tone1:"] = "\U0001f64e\U0001f3fb",
+ [":person_pouting::skin-tone-1:"] = "\U0001f64e\U0001f3fb",
+ [":person_with_pouting_face::skin-tone-1:"] = "\U0001f64e\U0001f3fb",
+ [":person_pouting_tone2:"] = "\U0001f64e\U0001f3fc",
+ [":person_with_pouting_face_tone2:"] = "\U0001f64e\U0001f3fc",
+ [":person_pouting::skin-tone-2:"] = "\U0001f64e\U0001f3fc",
+ [":person_with_pouting_face::skin-tone-2:"] = "\U0001f64e\U0001f3fc",
+ [":person_pouting_tone3:"] = "\U0001f64e\U0001f3fd",
+ [":person_with_pouting_face_tone3:"] = "\U0001f64e\U0001f3fd",
+ [":person_pouting::skin-tone-3:"] = "\U0001f64e\U0001f3fd",
+ [":person_with_pouting_face::skin-tone-3:"] = "\U0001f64e\U0001f3fd",
+ [":person_pouting_tone4:"] = "\U0001f64e\U0001f3fe",
+ [":person_with_pouting_face_tone4:"] = "\U0001f64e\U0001f3fe",
+ [":person_pouting::skin-tone-4:"] = "\U0001f64e\U0001f3fe",
+ [":person_with_pouting_face::skin-tone-4:"] = "\U0001f64e\U0001f3fe",
+ [":person_pouting_tone5:"] = "\U0001f64e\U0001f3ff",
+ [":person_with_pouting_face_tone5:"] = "\U0001f64e\U0001f3ff",
+ [":person_pouting::skin-tone-5:"] = "\U0001f64e\U0001f3ff",
+ [":person_with_pouting_face::skin-tone-5:"] = "\U0001f64e\U0001f3ff",
+ [":person_raising_hand:"] = "\U0001f64b",
+ [":raising_hand:"] = "\U0001f64b",
+ [":person_raising_hand_tone1:"] = "\U0001f64b\U0001f3fb",
+ [":raising_hand_tone1:"] = "\U0001f64b\U0001f3fb",
+ [":person_raising_hand::skin-tone-1:"] = "\U0001f64b\U0001f3fb",
+ [":raising_hand::skin-tone-1:"] = "\U0001f64b\U0001f3fb",
+ [":person_raising_hand_tone2:"] = "\U0001f64b\U0001f3fc",
+ [":raising_hand_tone2:"] = "\U0001f64b\U0001f3fc",
+ [":person_raising_hand::skin-tone-2:"] = "\U0001f64b\U0001f3fc",
+ [":raising_hand::skin-tone-2:"] = "\U0001f64b\U0001f3fc",
+ [":person_raising_hand_tone3:"] = "\U0001f64b\U0001f3fd",
+ [":raising_hand_tone3:"] = "\U0001f64b\U0001f3fd",
+ [":person_raising_hand::skin-tone-3:"] = "\U0001f64b\U0001f3fd",
+ [":raising_hand::skin-tone-3:"] = "\U0001f64b\U0001f3fd",
+ [":person_raising_hand_tone4:"] = "\U0001f64b\U0001f3fe",
+ [":raising_hand_tone4:"] = "\U0001f64b\U0001f3fe",
+ [":person_raising_hand::skin-tone-4:"] = "\U0001f64b\U0001f3fe",
+ [":raising_hand::skin-tone-4:"] = "\U0001f64b\U0001f3fe",
+ [":person_raising_hand_tone5:"] = "\U0001f64b\U0001f3ff",
+ [":raising_hand_tone5:"] = "\U0001f64b\U0001f3ff",
+ [":person_raising_hand::skin-tone-5:"] = "\U0001f64b\U0001f3ff",
+ [":raising_hand::skin-tone-5:"] = "\U0001f64b\U0001f3ff",
+ [":person_red_hair:"] = "\U0001f9d1\u200d\U0001f9b0",
+ [":person_rowing_boat:"] = "\U0001f6a3",
+ [":rowboat:"] = "\U0001f6a3",
+ [":person_rowing_boat_tone1:"] = "\U0001f6a3\U0001f3fb",
+ [":rowboat_tone1:"] = "\U0001f6a3\U0001f3fb",
+ [":person_rowing_boat::skin-tone-1:"] = "\U0001f6a3\U0001f3fb",
+ [":rowboat::skin-tone-1:"] = "\U0001f6a3\U0001f3fb",
+ [":person_rowing_boat_tone2:"] = "\U0001f6a3\U0001f3fc",
+ [":rowboat_tone2:"] = "\U0001f6a3\U0001f3fc",
+ [":person_rowing_boat::skin-tone-2:"] = "\U0001f6a3\U0001f3fc",
+ [":rowboat::skin-tone-2:"] = "\U0001f6a3\U0001f3fc",
+ [":person_rowing_boat_tone3:"] = "\U0001f6a3\U0001f3fd",
+ [":rowboat_tone3:"] = "\U0001f6a3\U0001f3fd",
+ [":person_rowing_boat::skin-tone-3:"] = "\U0001f6a3\U0001f3fd",
+ [":rowboat::skin-tone-3:"] = "\U0001f6a3\U0001f3fd",
+ [":person_rowing_boat_tone4:"] = "\U0001f6a3\U0001f3fe",
+ [":rowboat_tone4:"] = "\U0001f6a3\U0001f3fe",
+ [":person_rowing_boat::skin-tone-4:"] = "\U0001f6a3\U0001f3fe",
+ [":rowboat::skin-tone-4:"] = "\U0001f6a3\U0001f3fe",
+ [":person_rowing_boat_tone5:"] = "\U0001f6a3\U0001f3ff",
+ [":rowboat_tone5:"] = "\U0001f6a3\U0001f3ff",
+ [":person_rowing_boat::skin-tone-5:"] = "\U0001f6a3\U0001f3ff",
+ [":rowboat::skin-tone-5:"] = "\U0001f6a3\U0001f3ff",
+ [":person_running:"] = "\U0001f3c3",
+ [":runner:"] = "\U0001f3c3",
+ [":person_running_tone1:"] = "\U0001f3c3\U0001f3fb",
+ [":runner_tone1:"] = "\U0001f3c3\U0001f3fb",
+ [":person_running::skin-tone-1:"] = "\U0001f3c3\U0001f3fb",
+ [":runner::skin-tone-1:"] = "\U0001f3c3\U0001f3fb",
+ [":person_running_tone2:"] = "\U0001f3c3\U0001f3fc",
+ [":runner_tone2:"] = "\U0001f3c3\U0001f3fc",
+ [":person_running::skin-tone-2:"] = "\U0001f3c3\U0001f3fc",
+ [":runner::skin-tone-2:"] = "\U0001f3c3\U0001f3fc",
+ [":person_running_tone3:"] = "\U0001f3c3\U0001f3fd",
+ [":runner_tone3:"] = "\U0001f3c3\U0001f3fd",
+ [":person_running::skin-tone-3:"] = "\U0001f3c3\U0001f3fd",
+ [":runner::skin-tone-3:"] = "\U0001f3c3\U0001f3fd",
+ [":person_running_tone4:"] = "\U0001f3c3\U0001f3fe",
+ [":runner_tone4:"] = "\U0001f3c3\U0001f3fe",
+ [":person_running::skin-tone-4:"] = "\U0001f3c3\U0001f3fe",
+ [":runner::skin-tone-4:"] = "\U0001f3c3\U0001f3fe",
+ [":person_running_tone5:"] = "\U0001f3c3\U0001f3ff",
+ [":runner_tone5:"] = "\U0001f3c3\U0001f3ff",
+ [":person_running::skin-tone-5:"] = "\U0001f3c3\U0001f3ff",
+ [":runner::skin-tone-5:"] = "\U0001f3c3\U0001f3ff",
+ [":person_shrugging:"] = "\U0001f937",
+ [":shrug:"] = "\U0001f937",
+ [":person_shrugging_tone1:"] = "\U0001f937\U0001f3fb",
+ [":shrug_tone1:"] = "\U0001f937\U0001f3fb",
+ [":person_shrugging::skin-tone-1:"] = "\U0001f937\U0001f3fb",
+ [":shrug::skin-tone-1:"] = "\U0001f937\U0001f3fb",
+ [":person_shrugging_tone2:"] = "\U0001f937\U0001f3fc",
+ [":shrug_tone2:"] = "\U0001f937\U0001f3fc",
+ [":person_shrugging::skin-tone-2:"] = "\U0001f937\U0001f3fc",
+ [":shrug::skin-tone-2:"] = "\U0001f937\U0001f3fc",
+ [":person_shrugging_tone3:"] = "\U0001f937\U0001f3fd",
+ [":shrug_tone3:"] = "\U0001f937\U0001f3fd",
+ [":person_shrugging::skin-tone-3:"] = "\U0001f937\U0001f3fd",
+ [":shrug::skin-tone-3:"] = "\U0001f937\U0001f3fd",
+ [":person_shrugging_tone4:"] = "\U0001f937\U0001f3fe",
+ [":shrug_tone4:"] = "\U0001f937\U0001f3fe",
+ [":person_shrugging::skin-tone-4:"] = "\U0001f937\U0001f3fe",
+ [":shrug::skin-tone-4:"] = "\U0001f937\U0001f3fe",
+ [":person_shrugging_tone5:"] = "\U0001f937\U0001f3ff",
+ [":shrug_tone5:"] = "\U0001f937\U0001f3ff",
+ [":person_shrugging::skin-tone-5:"] = "\U0001f937\U0001f3ff",
+ [":shrug::skin-tone-5:"] = "\U0001f937\U0001f3ff",
+ [":person_standing:"] = "\U0001f9cd",
+ [":person_standing_tone1:"] = "\U0001f9cd\U0001f3fb",
+ [":person_standing_light_skin_tone:"] = "\U0001f9cd\U0001f3fb",
+ [":person_standing::skin-tone-1:"] = "\U0001f9cd\U0001f3fb",
+ [":person_standing_tone2:"] = "\U0001f9cd\U0001f3fc",
+ [":person_standing_medium_light_skin_tone:"] = "\U0001f9cd\U0001f3fc",
+ [":person_standing::skin-tone-2:"] = "\U0001f9cd\U0001f3fc",
+ [":person_standing_tone3:"] = "\U0001f9cd\U0001f3fd",
+ [":person_standing_medium_skin_tone:"] = "\U0001f9cd\U0001f3fd",
+ [":person_standing::skin-tone-3:"] = "\U0001f9cd\U0001f3fd",
+ [":person_standing_tone4:"] = "\U0001f9cd\U0001f3fe",
+ [":person_standing_medium_dark_skin_tone:"] = "\U0001f9cd\U0001f3fe",
+ [":person_standing::skin-tone-4:"] = "\U0001f9cd\U0001f3fe",
+ [":person_standing_tone5:"] = "\U0001f9cd\U0001f3ff",
+ [":person_standing_dark_skin_tone:"] = "\U0001f9cd\U0001f3ff",
+ [":person_standing::skin-tone-5:"] = "\U0001f9cd\U0001f3ff",
+ [":person_surfing:"] = "\U0001f3c4",
+ [":surfer:"] = "\U0001f3c4",
+ [":person_surfing_tone1:"] = "\U0001f3c4\U0001f3fb",
+ [":surfer_tone1:"] = "\U0001f3c4\U0001f3fb",
+ [":person_surfing::skin-tone-1:"] = "\U0001f3c4\U0001f3fb",
+ [":surfer::skin-tone-1:"] = "\U0001f3c4\U0001f3fb",
+ [":person_surfing_tone2:"] = "\U0001f3c4\U0001f3fc",
+ [":surfer_tone2:"] = "\U0001f3c4\U0001f3fc",
+ [":person_surfing::skin-tone-2:"] = "\U0001f3c4\U0001f3fc",
+ [":surfer::skin-tone-2:"] = "\U0001f3c4\U0001f3fc",
+ [":person_surfing_tone3:"] = "\U0001f3c4\U0001f3fd",
+ [":surfer_tone3:"] = "\U0001f3c4\U0001f3fd",
+ [":person_surfing::skin-tone-3:"] = "\U0001f3c4\U0001f3fd",
+ [":surfer::skin-tone-3:"] = "\U0001f3c4\U0001f3fd",
+ [":person_surfing_tone4:"] = "\U0001f3c4\U0001f3fe",
+ [":surfer_tone4:"] = "\U0001f3c4\U0001f3fe",
+ [":person_surfing::skin-tone-4:"] = "\U0001f3c4\U0001f3fe",
+ [":surfer::skin-tone-4:"] = "\U0001f3c4\U0001f3fe",
+ [":person_surfing_tone5:"] = "\U0001f3c4\U0001f3ff",
+ [":surfer_tone5:"] = "\U0001f3c4\U0001f3ff",
+ [":person_surfing::skin-tone-5:"] = "\U0001f3c4\U0001f3ff",
+ [":surfer::skin-tone-5:"] = "\U0001f3c4\U0001f3ff",
+ [":person_swimming:"] = "\U0001f3ca",
+ [":swimmer:"] = "\U0001f3ca",
+ [":person_swimming_tone1:"] = "\U0001f3ca\U0001f3fb",
+ [":swimmer_tone1:"] = "\U0001f3ca\U0001f3fb",
+ [":person_swimming::skin-tone-1:"] = "\U0001f3ca\U0001f3fb",
+ [":swimmer::skin-tone-1:"] = "\U0001f3ca\U0001f3fb",
+ [":person_swimming_tone2:"] = "\U0001f3ca\U0001f3fc",
+ [":swimmer_tone2:"] = "\U0001f3ca\U0001f3fc",
+ [":person_swimming::skin-tone-2:"] = "\U0001f3ca\U0001f3fc",
+ [":swimmer::skin-tone-2:"] = "\U0001f3ca\U0001f3fc",
+ [":person_swimming_tone3:"] = "\U0001f3ca\U0001f3fd",
+ [":swimmer_tone3:"] = "\U0001f3ca\U0001f3fd",
+ [":person_swimming::skin-tone-3:"] = "\U0001f3ca\U0001f3fd",
+ [":swimmer::skin-tone-3:"] = "\U0001f3ca\U0001f3fd",
+ [":person_swimming_tone4:"] = "\U0001f3ca\U0001f3fe",
+ [":swimmer_tone4:"] = "\U0001f3ca\U0001f3fe",
+ [":person_swimming::skin-tone-4:"] = "\U0001f3ca\U0001f3fe",
+ [":swimmer::skin-tone-4:"] = "\U0001f3ca\U0001f3fe",
+ [":person_swimming_tone5:"] = "\U0001f3ca\U0001f3ff",
+ [":swimmer_tone5:"] = "\U0001f3ca\U0001f3ff",
+ [":person_swimming::skin-tone-5:"] = "\U0001f3ca\U0001f3ff",
+ [":swimmer::skin-tone-5:"] = "\U0001f3ca\U0001f3ff",
+ [":person_tipping_hand:"] = "\U0001f481",
+ [":information_desk_person:"] = "\U0001f481",
+ [":person_tipping_hand_tone1:"] = "\U0001f481\U0001f3fb",
+ [":information_desk_person_tone1:"] = "\U0001f481\U0001f3fb",
+ [":person_tipping_hand::skin-tone-1:"] = "\U0001f481\U0001f3fb",
+ [":information_desk_person::skin-tone-1:"] = "\U0001f481\U0001f3fb",
+ [":person_tipping_hand_tone2:"] = "\U0001f481\U0001f3fc",
+ [":information_desk_person_tone2:"] = "\U0001f481\U0001f3fc",
+ [":person_tipping_hand::skin-tone-2:"] = "\U0001f481\U0001f3fc",
+ [":information_desk_person::skin-tone-2:"] = "\U0001f481\U0001f3fc",
+ [":person_tipping_hand_tone3:"] = "\U0001f481\U0001f3fd",
+ [":information_desk_person_tone3:"] = "\U0001f481\U0001f3fd",
+ [":person_tipping_hand::skin-tone-3:"] = "\U0001f481\U0001f3fd",
+ [":information_desk_person::skin-tone-3:"] = "\U0001f481\U0001f3fd",
+ [":person_tipping_hand_tone4:"] = "\U0001f481\U0001f3fe",
+ [":information_desk_person_tone4:"] = "\U0001f481\U0001f3fe",
+ [":person_tipping_hand::skin-tone-4:"] = "\U0001f481\U0001f3fe",
+ [":information_desk_person::skin-tone-4:"] = "\U0001f481\U0001f3fe",
+ [":person_tipping_hand_tone5:"] = "\U0001f481\U0001f3ff",
+ [":information_desk_person_tone5:"] = "\U0001f481\U0001f3ff",
+ [":person_tipping_hand::skin-tone-5:"] = "\U0001f481\U0001f3ff",
+ [":information_desk_person::skin-tone-5:"] = "\U0001f481\U0001f3ff",
+ [":person_tone1_bald:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b2",
+ [":person_light_skin_tone_bald:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b2",
+ [":person_bald::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b2",
+ [":person_tone1_curly_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b1",
+ [":person_light_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b1",
+ [":person_curly_hair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b1",
+ [":person_tone1_red_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b0",
+ [":person_light_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b0",
+ [":person_red_hair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b0",
+ [":person_tone1_white_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b3",
+ [":person_light_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b3",
+ [":person_white_hair::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9b3",
+ [":person_tone2_bald:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b2",
+ [":person_medium_light_skin_tone_bald:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b2",
+ [":person_bald::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b2",
+ [":person_tone2_curly_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b1",
+ [":person_medium_light_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b1",
+ [":person_curly_hair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b1",
+ [":person_tone2_red_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b0",
+ [":person_medium_light_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b0",
+ [":person_red_hair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b0",
+ [":person_tone2_white_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b3",
+ [":person_medium_light_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b3",
+ [":person_white_hair::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9b3",
+ [":person_tone3_bald:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b2",
+ [":person_medium_skin_tone_bald:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b2",
+ [":person_bald::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b2",
+ [":person_tone3_curly_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b1",
+ [":person_medium_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b1",
+ [":person_curly_hair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b1",
+ [":person_tone3_red_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b0",
+ [":person_medium_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b0",
+ [":person_red_hair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b0",
+ [":person_tone3_white_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b3",
+ [":person_medium_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b3",
+ [":person_white_hair::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9b3",
+ [":person_tone4_bald:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b2",
+ [":person_medium_dark_skin_tone_bald:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b2",
+ [":person_bald::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b2",
+ [":person_tone4_curly_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b1",
+ [":person_medium_dark_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b1",
+ [":person_curly_hair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b1",
+ [":person_tone4_red_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b0",
+ [":person_medium_dark_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b0",
+ [":person_red_hair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b0",
+ [":person_tone4_white_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b3",
+ [":person_medium_dark_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b3",
+ [":person_white_hair::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9b3",
+ [":person_tone5_bald:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b2",
+ [":person_dark_skin_tone_bald:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b2",
+ [":person_bald::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b2",
+ [":person_tone5_curly_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b1",
+ [":person_dark_skin_tone_curly_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b1",
+ [":person_curly_hair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b1",
+ [":person_tone5_red_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b0",
+ [":person_dark_skin_tone_red_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b0",
+ [":person_red_hair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b0",
+ [":person_tone5_white_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b3",
+ [":person_dark_skin_tone_white_hair:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b3",
+ [":person_white_hair::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9b3",
+ [":person_walking:"] = "\U0001f6b6",
+ [":walking:"] = "\U0001f6b6",
+ [":person_walking_tone1:"] = "\U0001f6b6\U0001f3fb",
+ [":walking_tone1:"] = "\U0001f6b6\U0001f3fb",
+ [":person_walking::skin-tone-1:"] = "\U0001f6b6\U0001f3fb",
+ [":walking::skin-tone-1:"] = "\U0001f6b6\U0001f3fb",
+ [":person_walking_tone2:"] = "\U0001f6b6\U0001f3fc",
+ [":walking_tone2:"] = "\U0001f6b6\U0001f3fc",
+ [":person_walking::skin-tone-2:"] = "\U0001f6b6\U0001f3fc",
+ [":walking::skin-tone-2:"] = "\U0001f6b6\U0001f3fc",
+ [":person_walking_tone3:"] = "\U0001f6b6\U0001f3fd",
+ [":walking_tone3:"] = "\U0001f6b6\U0001f3fd",
+ [":person_walking::skin-tone-3:"] = "\U0001f6b6\U0001f3fd",
+ [":walking::skin-tone-3:"] = "\U0001f6b6\U0001f3fd",
+ [":person_walking_tone4:"] = "\U0001f6b6\U0001f3fe",
+ [":walking_tone4:"] = "\U0001f6b6\U0001f3fe",
+ [":person_walking::skin-tone-4:"] = "\U0001f6b6\U0001f3fe",
+ [":walking::skin-tone-4:"] = "\U0001f6b6\U0001f3fe",
+ [":person_walking_tone5:"] = "\U0001f6b6\U0001f3ff",
+ [":walking_tone5:"] = "\U0001f6b6\U0001f3ff",
+ [":person_walking::skin-tone-5:"] = "\U0001f6b6\U0001f3ff",
+ [":walking::skin-tone-5:"] = "\U0001f6b6\U0001f3ff",
+ [":person_wearing_turban:"] = "\U0001f473",
+ [":man_with_turban:"] = "\U0001f473",
+ [":person_wearing_turban_tone1:"] = "\U0001f473\U0001f3fb",
+ [":man_with_turban_tone1:"] = "\U0001f473\U0001f3fb",
+ [":person_wearing_turban::skin-tone-1:"] = "\U0001f473\U0001f3fb",
+ [":man_with_turban::skin-tone-1:"] = "\U0001f473\U0001f3fb",
+ [":person_wearing_turban_tone2:"] = "\U0001f473\U0001f3fc",
+ [":man_with_turban_tone2:"] = "\U0001f473\U0001f3fc",
+ [":person_wearing_turban::skin-tone-2:"] = "\U0001f473\U0001f3fc",
+ [":man_with_turban::skin-tone-2:"] = "\U0001f473\U0001f3fc",
+ [":person_wearing_turban_tone3:"] = "\U0001f473\U0001f3fd",
+ [":man_with_turban_tone3:"] = "\U0001f473\U0001f3fd",
+ [":person_wearing_turban::skin-tone-3:"] = "\U0001f473\U0001f3fd",
+ [":man_with_turban::skin-tone-3:"] = "\U0001f473\U0001f3fd",
+ [":person_wearing_turban_tone4:"] = "\U0001f473\U0001f3fe",
+ [":man_with_turban_tone4:"] = "\U0001f473\U0001f3fe",
+ [":person_wearing_turban::skin-tone-4:"] = "\U0001f473\U0001f3fe",
+ [":man_with_turban::skin-tone-4:"] = "\U0001f473\U0001f3fe",
+ [":person_wearing_turban_tone5:"] = "\U0001f473\U0001f3ff",
+ [":man_with_turban_tone5:"] = "\U0001f473\U0001f3ff",
+ [":person_wearing_turban::skin-tone-5:"] = "\U0001f473\U0001f3ff",
+ [":man_with_turban::skin-tone-5:"] = "\U0001f473\U0001f3ff",
+ [":person_white_hair:"] = "\U0001f9d1\u200d\U0001f9b3",
+ [":person_with_probing_cane:"] = "\U0001f9d1\u200d\U0001f9af",
+ [":person_with_probing_cane_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9af",
+ [":person_with_probing_cane_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9af",
+ [":person_with_probing_cane::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f9af",
+ [":person_with_probing_cane_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9af",
+ [":person_with_probing_cane_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9af",
+ [":person_with_probing_cane::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f9af",
+ [":person_with_probing_cane_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9af",
+ [":person_with_probing_cane_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9af",
+ [":person_with_probing_cane::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f9af",
+ [":person_with_probing_cane_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9af",
+ [":person_with_probing_cane_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9af",
+ [":person_with_probing_cane::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f9af",
+ [":person_with_probing_cane_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9af",
+ [":person_with_probing_cane_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9af",
+ [":person_with_probing_cane::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f9af",
+ [":person_with_veil:"] = "\U0001f470",
+ [":person_with_veil_tone1:"] = "\U0001f470\U0001f3fb",
+ [":person_with_veil::skin-tone-1:"] = "\U0001f470\U0001f3fb",
+ [":person_with_veil_tone2:"] = "\U0001f470\U0001f3fc",
+ [":person_with_veil::skin-tone-2:"] = "\U0001f470\U0001f3fc",
+ [":person_with_veil_tone3:"] = "\U0001f470\U0001f3fd",
+ [":person_with_veil::skin-tone-3:"] = "\U0001f470\U0001f3fd",
+ [":person_with_veil_tone4:"] = "\U0001f470\U0001f3fe",
+ [":person_with_veil::skin-tone-4:"] = "\U0001f470\U0001f3fe",
+ [":person_with_veil_tone5:"] = "\U0001f470\U0001f3ff",
+ [":person_with_veil::skin-tone-5:"] = "\U0001f470\U0001f3ff",
+ [":petri_dish:"] = "\U0001f9eb",
+ [":pick:"] = "\u26cf\ufe0f",
+ [":pickup_truck:"] = "\U0001f6fb",
+ [":pie:"] = "\U0001f967",
+ [":pig:"] = "\U0001f437",
+ [":pig_nose:"] = "\U0001f43d",
+ [":pig2:"] = "\U0001f416",
+ [":pill:"] = "\U0001f48a",
+ [":pilot:"] = "\U0001f9d1\u200d\u2708\ufe0f",
+ [":pilot_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f",
+ [":pilot_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f",
+ [":pilot::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f",
+ [":pilot_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f",
+ [":pilot_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f",
+ [":pilot::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f",
+ [":pilot_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f",
+ [":pilot_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f",
+ [":pilot::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f",
+ [":pilot_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f",
+ [":pilot_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f",
+ [":pilot::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f",
+ [":pilot_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f",
+ [":pilot_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f",
+ [":pilot::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f",
+ [":piñata:"] = "\U0001fa85",
+ [":pinched_fingers:"] = "\U0001f90c",
+ [":pinched_fingers_tone1:"] = "\U0001f90c\U0001f3fb",
+ [":pinched_fingers_light_skin_tone:"] = "\U0001f90c\U0001f3fb",
+ [":pinched_fingers::skin-tone-1:"] = "\U0001f90c\U0001f3fb",
+ [":pinched_fingers_tone2:"] = "\U0001f90c\U0001f3fc",
+ [":pinched_fingers_medium_light_skin_tone:"] = "\U0001f90c\U0001f3fc",
+ [":pinched_fingers::skin-tone-2:"] = "\U0001f90c\U0001f3fc",
+ [":pinched_fingers_tone3:"] = "\U0001f90c\U0001f3fd",
+ [":pinched_fingers_medium_skin_tone:"] = "\U0001f90c\U0001f3fd",
+ [":pinched_fingers::skin-tone-3:"] = "\U0001f90c\U0001f3fd",
+ [":pinched_fingers_tone4:"] = "\U0001f90c\U0001f3fe",
+ [":pinched_fingers_medium_dark_skin_tone:"] = "\U0001f90c\U0001f3fe",
+ [":pinched_fingers::skin-tone-4:"] = "\U0001f90c\U0001f3fe",
+ [":pinched_fingers_tone5:"] = "\U0001f90c\U0001f3ff",
+ [":pinched_fingers_dark_skin_tone:"] = "\U0001f90c\U0001f3ff",
+ [":pinched_fingers::skin-tone-5:"] = "\U0001f90c\U0001f3ff",
+ [":pinching_hand:"] = "\U0001f90f",
+ [":pinching_hand_tone1:"] = "\U0001f90f\U0001f3fb",
+ [":pinching_hand_light_skin_tone:"] = "\U0001f90f\U0001f3fb",
+ [":pinching_hand::skin-tone-1:"] = "\U0001f90f\U0001f3fb",
+ [":pinching_hand_tone2:"] = "\U0001f90f\U0001f3fc",
+ [":pinching_hand_medium_light_skin_tone:"] = "\U0001f90f\U0001f3fc",
+ [":pinching_hand::skin-tone-2:"] = "\U0001f90f\U0001f3fc",
+ [":pinching_hand_tone3:"] = "\U0001f90f\U0001f3fd",
+ [":pinching_hand_medium_skin_tone:"] = "\U0001f90f\U0001f3fd",
+ [":pinching_hand::skin-tone-3:"] = "\U0001f90f\U0001f3fd",
+ [":pinching_hand_tone4:"] = "\U0001f90f\U0001f3fe",
+ [":pinching_hand_medium_dark_skin_tone:"] = "\U0001f90f\U0001f3fe",
+ [":pinching_hand::skin-tone-4:"] = "\U0001f90f\U0001f3fe",
+ [":pinching_hand_tone5:"] = "\U0001f90f\U0001f3ff",
+ [":pinching_hand_dark_skin_tone:"] = "\U0001f90f\U0001f3ff",
+ [":pinching_hand::skin-tone-5:"] = "\U0001f90f\U0001f3ff",
+ [":pineapple:"] = "\U0001f34d",
+ [":ping_pong:"] = "\U0001f3d3",
+ [":table_tennis:"] = "\U0001f3d3",
+ [":pirate_flag:"] = "\U0001f3f4\u200d\u2620\ufe0f",
+ [":pisces:"] = "\u2653",
+ [":pizza:"] = "\U0001f355",
+ [":placard:"] = "\U0001faa7",
+ [":place_of_worship:"] = "\U0001f6d0",
+ [":worship_symbol:"] = "\U0001f6d0",
+ [":play_pause:"] = "\u23ef\ufe0f",
+ [":pleading_face:"] = "\U0001f97a",
+ [":plunger:"] = "\U0001faa0",
+ [":point_down:"] = "\U0001f447",
+ [":point_down_tone1:"] = "\U0001f447\U0001f3fb",
+ [":point_down::skin-tone-1:"] = "\U0001f447\U0001f3fb",
+ [":point_down_tone2:"] = "\U0001f447\U0001f3fc",
+ [":point_down::skin-tone-2:"] = "\U0001f447\U0001f3fc",
+ [":point_down_tone3:"] = "\U0001f447\U0001f3fd",
+ [":point_down::skin-tone-3:"] = "\U0001f447\U0001f3fd",
+ [":point_down_tone4:"] = "\U0001f447\U0001f3fe",
+ [":point_down::skin-tone-4:"] = "\U0001f447\U0001f3fe",
+ [":point_down_tone5:"] = "\U0001f447\U0001f3ff",
+ [":point_down::skin-tone-5:"] = "\U0001f447\U0001f3ff",
+ [":point_left:"] = "\U0001f448",
+ [":point_left_tone1:"] = "\U0001f448\U0001f3fb",
+ [":point_left::skin-tone-1:"] = "\U0001f448\U0001f3fb",
+ [":point_left_tone2:"] = "\U0001f448\U0001f3fc",
+ [":point_left::skin-tone-2:"] = "\U0001f448\U0001f3fc",
+ [":point_left_tone3:"] = "\U0001f448\U0001f3fd",
+ [":point_left::skin-tone-3:"] = "\U0001f448\U0001f3fd",
+ [":point_left_tone4:"] = "\U0001f448\U0001f3fe",
+ [":point_left::skin-tone-4:"] = "\U0001f448\U0001f3fe",
+ [":point_left_tone5:"] = "\U0001f448\U0001f3ff",
+ [":point_left::skin-tone-5:"] = "\U0001f448\U0001f3ff",
+ [":point_right:"] = "\U0001f449",
+ [":point_right_tone1:"] = "\U0001f449\U0001f3fb",
+ [":point_right::skin-tone-1:"] = "\U0001f449\U0001f3fb",
+ [":point_right_tone2:"] = "\U0001f449\U0001f3fc",
+ [":point_right::skin-tone-2:"] = "\U0001f449\U0001f3fc",
+ [":point_right_tone3:"] = "\U0001f449\U0001f3fd",
+ [":point_right::skin-tone-3:"] = "\U0001f449\U0001f3fd",
+ [":point_right_tone4:"] = "\U0001f449\U0001f3fe",
+ [":point_right::skin-tone-4:"] = "\U0001f449\U0001f3fe",
+ [":point_right_tone5:"] = "\U0001f449\U0001f3ff",
+ [":point_right::skin-tone-5:"] = "\U0001f449\U0001f3ff",
+ [":point_up:"] = "\u261d\ufe0f",
+ [":point_up_2:"] = "\U0001f446",
+ [":point_up_2_tone1:"] = "\U0001f446\U0001f3fb",
+ [":point_up_2::skin-tone-1:"] = "\U0001f446\U0001f3fb",
+ [":point_up_2_tone2:"] = "\U0001f446\U0001f3fc",
+ [":point_up_2::skin-tone-2:"] = "\U0001f446\U0001f3fc",
+ [":point_up_2_tone3:"] = "\U0001f446\U0001f3fd",
+ [":point_up_2::skin-tone-3:"] = "\U0001f446\U0001f3fd",
+ [":point_up_2_tone4:"] = "\U0001f446\U0001f3fe",
+ [":point_up_2::skin-tone-4:"] = "\U0001f446\U0001f3fe",
+ [":point_up_2_tone5:"] = "\U0001f446\U0001f3ff",
+ [":point_up_2::skin-tone-5:"] = "\U0001f446\U0001f3ff",
+ [":point_up_tone1:"] = "\u261d\U0001f3fb",
+ [":point_up::skin-tone-1:"] = "\u261d\U0001f3fb",
+ [":point_up_tone2:"] = "\u261d\U0001f3fc",
+ [":point_up::skin-tone-2:"] = "\u261d\U0001f3fc",
+ [":point_up_tone3:"] = "\u261d\U0001f3fd",
+ [":point_up::skin-tone-3:"] = "\u261d\U0001f3fd",
+ [":point_up_tone4:"] = "\u261d\U0001f3fe",
+ [":point_up::skin-tone-4:"] = "\u261d\U0001f3fe",
+ [":point_up_tone5:"] = "\u261d\U0001f3ff",
+ [":point_up::skin-tone-5:"] = "\u261d\U0001f3ff",
+ [":polar_bear:"] = "\U0001f43b\u200d\u2744\ufe0f",
+ [":police_car:"] = "\U0001f693",
+ [":police_officer:"] = "\U0001f46e",
+ [":cop:"] = "\U0001f46e",
+ [":police_officer_tone1:"] = "\U0001f46e\U0001f3fb",
+ [":cop_tone1:"] = "\U0001f46e\U0001f3fb",
+ [":police_officer::skin-tone-1:"] = "\U0001f46e\U0001f3fb",
+ [":cop::skin-tone-1:"] = "\U0001f46e\U0001f3fb",
+ [":police_officer_tone2:"] = "\U0001f46e\U0001f3fc",
+ [":cop_tone2:"] = "\U0001f46e\U0001f3fc",
+ [":police_officer::skin-tone-2:"] = "\U0001f46e\U0001f3fc",
+ [":cop::skin-tone-2:"] = "\U0001f46e\U0001f3fc",
+ [":police_officer_tone3:"] = "\U0001f46e\U0001f3fd",
+ [":cop_tone3:"] = "\U0001f46e\U0001f3fd",
+ [":police_officer::skin-tone-3:"] = "\U0001f46e\U0001f3fd",
+ [":cop::skin-tone-3:"] = "\U0001f46e\U0001f3fd",
+ [":police_officer_tone4:"] = "\U0001f46e\U0001f3fe",
+ [":cop_tone4:"] = "\U0001f46e\U0001f3fe",
+ [":police_officer::skin-tone-4:"] = "\U0001f46e\U0001f3fe",
+ [":cop::skin-tone-4:"] = "\U0001f46e\U0001f3fe",
+ [":police_officer_tone5:"] = "\U0001f46e\U0001f3ff",
+ [":cop_tone5:"] = "\U0001f46e\U0001f3ff",
+ [":police_officer::skin-tone-5:"] = "\U0001f46e\U0001f3ff",
+ [":cop::skin-tone-5:"] = "\U0001f46e\U0001f3ff",
+ [":poodle:"] = "\U0001f429",
+ [":poop:"] = "\U0001f4a9",
+ [":shit:"] = "\U0001f4a9",
+ [":hankey:"] = "\U0001f4a9",
+ [":poo:"] = "\U0001f4a9",
+ [":popcorn:"] = "\U0001f37f",
+ [":post_office:"] = "\U0001f3e3",
+ [":postal_horn:"] = "\U0001f4ef",
+ [":postbox:"] = "\U0001f4ee",
+ [":potable_water:"] = "\U0001f6b0",
+ [":potato:"] = "\U0001f954",
+ [":potted_plant:"] = "\U0001fab4",
+ [":pouch:"] = "\U0001f45d",
+ [":poultry_leg:"] = "\U0001f357",
+ [":pound:"] = "\U0001f4b7",
+ [":pouting_cat:"] = "\U0001f63e",
+ [":pray:"] = "\U0001f64f",
+ [":pray_tone1:"] = "\U0001f64f\U0001f3fb",
+ [":pray::skin-tone-1:"] = "\U0001f64f\U0001f3fb",
+ [":pray_tone2:"] = "\U0001f64f\U0001f3fc",
+ [":pray::skin-tone-2:"] = "\U0001f64f\U0001f3fc",
+ [":pray_tone3:"] = "\U0001f64f\U0001f3fd",
+ [":pray::skin-tone-3:"] = "\U0001f64f\U0001f3fd",
+ [":pray_tone4:"] = "\U0001f64f\U0001f3fe",
+ [":pray::skin-tone-4:"] = "\U0001f64f\U0001f3fe",
+ [":pray_tone5:"] = "\U0001f64f\U0001f3ff",
+ [":pray::skin-tone-5:"] = "\U0001f64f\U0001f3ff",
+ [":prayer_beads:"] = "\U0001f4ff",
+ [":pregnant_woman:"] = "\U0001f930",
+ [":expecting_woman:"] = "\U0001f930",
+ [":pregnant_woman_tone1:"] = "\U0001f930\U0001f3fb",
+ [":expecting_woman_tone1:"] = "\U0001f930\U0001f3fb",
+ [":pregnant_woman::skin-tone-1:"] = "\U0001f930\U0001f3fb",
+ [":expecting_woman::skin-tone-1:"] = "\U0001f930\U0001f3fb",
+ [":pregnant_woman_tone2:"] = "\U0001f930\U0001f3fc",
+ [":expecting_woman_tone2:"] = "\U0001f930\U0001f3fc",
+ [":pregnant_woman::skin-tone-2:"] = "\U0001f930\U0001f3fc",
+ [":expecting_woman::skin-tone-2:"] = "\U0001f930\U0001f3fc",
+ [":pregnant_woman_tone3:"] = "\U0001f930\U0001f3fd",
+ [":expecting_woman_tone3:"] = "\U0001f930\U0001f3fd",
+ [":pregnant_woman::skin-tone-3:"] = "\U0001f930\U0001f3fd",
+ [":expecting_woman::skin-tone-3:"] = "\U0001f930\U0001f3fd",
+ [":pregnant_woman_tone4:"] = "\U0001f930\U0001f3fe",
+ [":expecting_woman_tone4:"] = "\U0001f930\U0001f3fe",
+ [":pregnant_woman::skin-tone-4:"] = "\U0001f930\U0001f3fe",
+ [":expecting_woman::skin-tone-4:"] = "\U0001f930\U0001f3fe",
+ [":pregnant_woman_tone5:"] = "\U0001f930\U0001f3ff",
+ [":expecting_woman_tone5:"] = "\U0001f930\U0001f3ff",
+ [":pregnant_woman::skin-tone-5:"] = "\U0001f930\U0001f3ff",
+ [":expecting_woman::skin-tone-5:"] = "\U0001f930\U0001f3ff",
+ [":pretzel:"] = "\U0001f968",
+ [":prince:"] = "\U0001f934",
+ [":prince_tone1:"] = "\U0001f934\U0001f3fb",
+ [":prince::skin-tone-1:"] = "\U0001f934\U0001f3fb",
+ [":prince_tone2:"] = "\U0001f934\U0001f3fc",
+ [":prince::skin-tone-2:"] = "\U0001f934\U0001f3fc",
+ [":prince_tone3:"] = "\U0001f934\U0001f3fd",
+ [":prince::skin-tone-3:"] = "\U0001f934\U0001f3fd",
+ [":prince_tone4:"] = "\U0001f934\U0001f3fe",
+ [":prince::skin-tone-4:"] = "\U0001f934\U0001f3fe",
+ [":prince_tone5:"] = "\U0001f934\U0001f3ff",
+ [":prince::skin-tone-5:"] = "\U0001f934\U0001f3ff",
+ [":princess:"] = "\U0001f478",
+ [":princess_tone1:"] = "\U0001f478\U0001f3fb",
+ [":princess::skin-tone-1:"] = "\U0001f478\U0001f3fb",
+ [":princess_tone2:"] = "\U0001f478\U0001f3fc",
+ [":princess::skin-tone-2:"] = "\U0001f478\U0001f3fc",
+ [":princess_tone3:"] = "\U0001f478\U0001f3fd",
+ [":princess::skin-tone-3:"] = "\U0001f478\U0001f3fd",
+ [":princess_tone4:"] = "\U0001f478\U0001f3fe",
+ [":princess::skin-tone-4:"] = "\U0001f478\U0001f3fe",
+ [":princess_tone5:"] = "\U0001f478\U0001f3ff",
+ [":princess::skin-tone-5:"] = "\U0001f478\U0001f3ff",
+ [":printer:"] = "\U0001f5a8\ufe0f",
+ [":probing_cane:"] = "\U0001f9af",
+ [":projector:"] = "\U0001f4fd\ufe0f",
+ [":film_projector:"] = "\U0001f4fd\ufe0f",
+ [":punch:"] = "\U0001f44a",
+ [":punch_tone1:"] = "\U0001f44a\U0001f3fb",
+ [":punch::skin-tone-1:"] = "\U0001f44a\U0001f3fb",
+ [":punch_tone2:"] = "\U0001f44a\U0001f3fc",
+ [":punch::skin-tone-2:"] = "\U0001f44a\U0001f3fc",
+ [":punch_tone3:"] = "\U0001f44a\U0001f3fd",
+ [":punch::skin-tone-3:"] = "\U0001f44a\U0001f3fd",
+ [":punch_tone4:"] = "\U0001f44a\U0001f3fe",
+ [":punch::skin-tone-4:"] = "\U0001f44a\U0001f3fe",
+ [":punch_tone5:"] = "\U0001f44a\U0001f3ff",
+ [":punch::skin-tone-5:"] = "\U0001f44a\U0001f3ff",
+ [":purple_circle:"] = "\U0001f7e3",
+ [":purple_heart:"] = "\U0001f49c",
+ [":purple_square:"] = "\U0001f7ea",
+ [":purse:"] = "\U0001f45b",
+ [":pushpin:"] = "\U0001f4cc",
+ [":put_litter_in_its_place:"] = "\U0001f6ae",
+ [":question:"] = "\u2753",
+ [":rabbit:"] = "\U0001f430",
+ [":rabbit2:"] = "\U0001f407",
+ [":raccoon:"] = "\U0001f99d",
+ [":race_car:"] = "\U0001f3ce\ufe0f",
+ [":racing_car:"] = "\U0001f3ce\ufe0f",
+ [":racehorse:"] = "\U0001f40e",
+ [":radio:"] = "\U0001f4fb",
+ [":radio_button:"] = "\U0001f518",
+ [":radioactive:"] = "\u2622\ufe0f",
+ [":radioactive_sign:"] = "\u2622\ufe0f",
+ [":rage:"] = "\U0001f621",
+ [":@"] = "\U0001f621",
+ [":-@"] = "\U0001f621",
+ ["=@"] = "\U0001f621",
+ ["=-@"] = "\U0001f621",
+ [":railway_car:"] = "\U0001f683",
+ [":railway_track:"] = "\U0001f6e4\ufe0f",
+ [":railroad_track:"] = "\U0001f6e4\ufe0f",
+ [":rainbow:"] = "\U0001f308",
+ [":rainbow_flag:"] = "\U0001f3f3\ufe0f\u200d\U0001f308",
+ [":gay_pride_flag:"] = "\U0001f3f3\ufe0f\u200d\U0001f308",
+ [":raised_back_of_hand:"] = "\U0001f91a",
+ [":back_of_hand:"] = "\U0001f91a",
+ [":raised_back_of_hand_tone1:"] = "\U0001f91a\U0001f3fb",
+ [":back_of_hand_tone1:"] = "\U0001f91a\U0001f3fb",
+ [":raised_back_of_hand::skin-tone-1:"] = "\U0001f91a\U0001f3fb",
+ [":back_of_hand::skin-tone-1:"] = "\U0001f91a\U0001f3fb",
+ [":raised_back_of_hand_tone2:"] = "\U0001f91a\U0001f3fc",
+ [":back_of_hand_tone2:"] = "\U0001f91a\U0001f3fc",
+ [":raised_back_of_hand::skin-tone-2:"] = "\U0001f91a\U0001f3fc",
+ [":back_of_hand::skin-tone-2:"] = "\U0001f91a\U0001f3fc",
+ [":raised_back_of_hand_tone3:"] = "\U0001f91a\U0001f3fd",
+ [":back_of_hand_tone3:"] = "\U0001f91a\U0001f3fd",
+ [":raised_back_of_hand::skin-tone-3:"] = "\U0001f91a\U0001f3fd",
+ [":back_of_hand::skin-tone-3:"] = "\U0001f91a\U0001f3fd",
+ [":raised_back_of_hand_tone4:"] = "\U0001f91a\U0001f3fe",
+ [":back_of_hand_tone4:"] = "\U0001f91a\U0001f3fe",
+ [":raised_back_of_hand::skin-tone-4:"] = "\U0001f91a\U0001f3fe",
+ [":back_of_hand::skin-tone-4:"] = "\U0001f91a\U0001f3fe",
+ [":raised_back_of_hand_tone5:"] = "\U0001f91a\U0001f3ff",
+ [":back_of_hand_tone5:"] = "\U0001f91a\U0001f3ff",
+ [":raised_back_of_hand::skin-tone-5:"] = "\U0001f91a\U0001f3ff",
+ [":back_of_hand::skin-tone-5:"] = "\U0001f91a\U0001f3ff",
+ [":raised_hand:"] = "\u270b",
+ [":raised_hand_tone1:"] = "\u270b\U0001f3fb",
+ [":raised_hand::skin-tone-1:"] = "\u270b\U0001f3fb",
+ [":raised_hand_tone2:"] = "\u270b\U0001f3fc",
+ [":raised_hand::skin-tone-2:"] = "\u270b\U0001f3fc",
+ [":raised_hand_tone3:"] = "\u270b\U0001f3fd",
+ [":raised_hand::skin-tone-3:"] = "\u270b\U0001f3fd",
+ [":raised_hand_tone4:"] = "\u270b\U0001f3fe",
+ [":raised_hand::skin-tone-4:"] = "\u270b\U0001f3fe",
+ [":raised_hand_tone5:"] = "\u270b\U0001f3ff",
+ [":raised_hand::skin-tone-5:"] = "\u270b\U0001f3ff",
+ [":raised_hands:"] = "\U0001f64c",
+ [":raised_hands_tone1:"] = "\U0001f64c\U0001f3fb",
+ [":raised_hands::skin-tone-1:"] = "\U0001f64c\U0001f3fb",
+ [":raised_hands_tone2:"] = "\U0001f64c\U0001f3fc",
+ [":raised_hands::skin-tone-2:"] = "\U0001f64c\U0001f3fc",
+ [":raised_hands_tone3:"] = "\U0001f64c\U0001f3fd",
+ [":raised_hands::skin-tone-3:"] = "\U0001f64c\U0001f3fd",
+ [":raised_hands_tone4:"] = "\U0001f64c\U0001f3fe",
+ [":raised_hands::skin-tone-4:"] = "\U0001f64c\U0001f3fe",
+ [":raised_hands_tone5:"] = "\U0001f64c\U0001f3ff",
+ [":raised_hands::skin-tone-5:"] = "\U0001f64c\U0001f3ff",
+ [":ram:"] = "\U0001f40f",
+ [":ramen:"] = "\U0001f35c",
+ [":rat:"] = "\U0001f400",
+ [":razor:"] = "\U0001fa92",
+ [":receipt:"] = "\U0001f9fe",
+ [":record_button:"] = "\u23fa\ufe0f",
+ [":recycle:"] = "\u267b\ufe0f",
+ [":red_car:"] = "\U0001f697",
+ [":red_circle:"] = "\U0001f534",
+ [":red_envelope:"] = "\U0001f9e7",
+ [":red_square:"] = "\U0001f7e5",
+ [":regional_indicator_a:"] = "\U0001f1e6",
+ [":regional_indicator_b:"] = "\U0001f1e7",
+ [":regional_indicator_c:"] = "\U0001f1e8",
+ [":regional_indicator_d:"] = "\U0001f1e9",
+ [":regional_indicator_e:"] = "\U0001f1ea",
+ [":regional_indicator_f:"] = "\U0001f1eb",
+ [":regional_indicator_g:"] = "\U0001f1ec",
+ [":regional_indicator_h:"] = "\U0001f1ed",
+ [":regional_indicator_i:"] = "\U0001f1ee",
+ [":regional_indicator_j:"] = "\U0001f1ef",
+ [":regional_indicator_k:"] = "\U0001f1f0",
+ [":regional_indicator_l:"] = "\U0001f1f1",
+ [":regional_indicator_m:"] = "\U0001f1f2",
+ [":regional_indicator_n:"] = "\U0001f1f3",
+ [":regional_indicator_o:"] = "\U0001f1f4",
+ [":regional_indicator_p:"] = "\U0001f1f5",
+ [":regional_indicator_q:"] = "\U0001f1f6",
+ [":regional_indicator_r:"] = "\U0001f1f7",
+ [":regional_indicator_s:"] = "\U0001f1f8",
+ [":regional_indicator_t:"] = "\U0001f1f9",
+ [":regional_indicator_u:"] = "\U0001f1fa",
+ [":regional_indicator_v:"] = "\U0001f1fb",
+ [":regional_indicator_w:"] = "\U0001f1fc",
+ [":regional_indicator_x:"] = "\U0001f1fd",
+ [":regional_indicator_y:"] = "\U0001f1fe",
+ [":regional_indicator_z:"] = "\U0001f1ff",
+ [":registered:"] = "\u00ae\ufe0f",
+ [":relaxed:"] = "\u263a\ufe0f",
+ [":relieved:"] = "\U0001f60c",
+ [":reminder_ribbon:"] = "\U0001f397\ufe0f",
+ [":repeat:"] = "\U0001f501",
+ [":repeat_one:"] = "\U0001f502",
+ [":restroom:"] = "\U0001f6bb",
+ [":revolving_hearts:"] = "\U0001f49e",
+ [":rewind:"] = "\u23ea",
+ [":rhino:"] = "\U0001f98f",
+ [":rhinoceros:"] = "\U0001f98f",
+ [":ribbon:"] = "\U0001f380",
+ [":rice:"] = "\U0001f35a",
+ [":rice_ball:"] = "\U0001f359",
+ [":rice_cracker:"] = "\U0001f358",
+ [":rice_scene:"] = "\U0001f391",
+ [":right_facing_fist:"] = "\U0001f91c",
+ [":right_fist:"] = "\U0001f91c",
+ [":right_facing_fist_tone1:"] = "\U0001f91c\U0001f3fb",
+ [":right_fist_tone1:"] = "\U0001f91c\U0001f3fb",
+ [":right_facing_fist::skin-tone-1:"] = "\U0001f91c\U0001f3fb",
+ [":right_fist::skin-tone-1:"] = "\U0001f91c\U0001f3fb",
+ [":right_facing_fist_tone2:"] = "\U0001f91c\U0001f3fc",
+ [":right_fist_tone2:"] = "\U0001f91c\U0001f3fc",
+ [":right_facing_fist::skin-tone-2:"] = "\U0001f91c\U0001f3fc",
+ [":right_fist::skin-tone-2:"] = "\U0001f91c\U0001f3fc",
+ [":right_facing_fist_tone3:"] = "\U0001f91c\U0001f3fd",
+ [":right_fist_tone3:"] = "\U0001f91c\U0001f3fd",
+ [":right_facing_fist::skin-tone-3:"] = "\U0001f91c\U0001f3fd",
+ [":right_fist::skin-tone-3:"] = "\U0001f91c\U0001f3fd",
+ [":right_facing_fist_tone4:"] = "\U0001f91c\U0001f3fe",
+ [":right_fist_tone4:"] = "\U0001f91c\U0001f3fe",
+ [":right_facing_fist::skin-tone-4:"] = "\U0001f91c\U0001f3fe",
+ [":right_fist::skin-tone-4:"] = "\U0001f91c\U0001f3fe",
+ [":right_facing_fist_tone5:"] = "\U0001f91c\U0001f3ff",
+ [":right_fist_tone5:"] = "\U0001f91c\U0001f3ff",
+ [":right_facing_fist::skin-tone-5:"] = "\U0001f91c\U0001f3ff",
+ [":right_fist::skin-tone-5:"] = "\U0001f91c\U0001f3ff",
+ [":ring:"] = "\U0001f48d",
+ [":ringed_planet:"] = "\U0001fa90",
+ [":robot:"] = "\U0001f916",
+ [":robot_face:"] = "\U0001f916",
+ [":rock:"] = "\U0001faa8",
+ [":rocket:"] = "\U0001f680",
+ [":rofl:"] = "\U0001f923",
+ [":rolling_on_the_floor_laughing:"] = "\U0001f923",
+ [":roll_of_paper:"] = "\U0001f9fb",
+ [":roller_coaster:"] = "\U0001f3a2",
+ [":roller_skate:"] = "\U0001f6fc",
+ [":rolling_eyes:"] = "\U0001f644",
+ [":face_with_rolling_eyes:"] = "\U0001f644",
+ [":rooster:"] = "\U0001f413",
+ [":rose:"] = "\U0001f339",
+ [":rosette:"] = "\U0001f3f5\ufe0f",
+ [":rotating_light:"] = "\U0001f6a8",
+ [":round_pushpin:"] = "\U0001f4cd",
+ [":rugby_football:"] = "\U0001f3c9",
+ [":running_shirt_with_sash:"] = "\U0001f3bd",
+ [":sa:"] = "\U0001f202\ufe0f",
+ [":safety_pin:"] = "\U0001f9f7",
+ [":safety_vest:"] = "\U0001f9ba",
+ [":sagittarius:"] = "\u2650",
+ [":sailboat:"] = "\u26f5",
+ [":sake:"] = "\U0001f376",
+ [":salad:"] = "\U0001f957",
+ [":green_salad:"] = "\U0001f957",
+ [":salt:"] = "\U0001f9c2",
+ [":sandal:"] = "\U0001f461",
+ [":sandwich:"] = "\U0001f96a",
+ [":santa:"] = "\U0001f385",
+ [":santa_tone1:"] = "\U0001f385\U0001f3fb",
+ [":santa::skin-tone-1:"] = "\U0001f385\U0001f3fb",
+ [":santa_tone2:"] = "\U0001f385\U0001f3fc",
+ [":santa::skin-tone-2:"] = "\U0001f385\U0001f3fc",
+ [":santa_tone3:"] = "\U0001f385\U0001f3fd",
+ [":santa::skin-tone-3:"] = "\U0001f385\U0001f3fd",
+ [":santa_tone4:"] = "\U0001f385\U0001f3fe",
+ [":santa::skin-tone-4:"] = "\U0001f385\U0001f3fe",
+ [":santa_tone5:"] = "\U0001f385\U0001f3ff",
+ [":santa::skin-tone-5:"] = "\U0001f385\U0001f3ff",
+ [":sari:"] = "\U0001f97b",
+ [":satellite:"] = "\U0001f4e1",
+ [":satellite_orbital:"] = "\U0001f6f0\ufe0f",
+ [":sauropod:"] = "\U0001f995",
+ [":saxophone:"] = "\U0001f3b7",
+ [":scales:"] = "\u2696\ufe0f",
+ [":scarf:"] = "\U0001f9e3",
+ [":school:"] = "\U0001f3eb",
+ [":school_satchel:"] = "\U0001f392",
+ [":scientist:"] = "\U0001f9d1\u200d\U0001f52c",
+ [":scientist_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f52c",
+ [":scientist_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f52c",
+ [":scientist::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f52c",
+ [":scientist_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f52c",
+ [":scientist_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f52c",
+ [":scientist::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f52c",
+ [":scientist_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f52c",
+ [":scientist_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f52c",
+ [":scientist::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f52c",
+ [":scientist_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f52c",
+ [":scientist_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f52c",
+ [":scientist::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f52c",
+ [":scientist_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f52c",
+ [":scientist_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f52c",
+ [":scientist::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f52c",
+ [":scissors:"] = "\u2702\ufe0f",
+ [":scooter:"] = "\U0001f6f4",
+ [":scorpion:"] = "\U0001f982",
+ [":scorpius:"] = "\u264f",
+ [":scotland:"] = "\U0001f3f4\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f",
+ [":scream:"] = "\U0001f631",
+ [":scream_cat:"] = "\U0001f640",
+ [":screwdriver:"] = "\U0001fa9b",
+ [":scroll:"] = "\U0001f4dc",
+ [":seal:"] = "\U0001f9ad",
+ [":seat:"] = "\U0001f4ba",
+ [":second_place:"] = "\U0001f948",
+ [":second_place_medal:"] = "\U0001f948",
+ [":secret:"] = "\u3299\ufe0f",
+ [":see_no_evil:"] = "\U0001f648",
+ [":seedling:"] = "\U0001f331",
+ [":selfie:"] = "\U0001f933",
+ [":selfie_tone1:"] = "\U0001f933\U0001f3fb",
+ [":selfie::skin-tone-1:"] = "\U0001f933\U0001f3fb",
+ [":selfie_tone2:"] = "\U0001f933\U0001f3fc",
+ [":selfie::skin-tone-2:"] = "\U0001f933\U0001f3fc",
+ [":selfie_tone3:"] = "\U0001f933\U0001f3fd",
+ [":selfie::skin-tone-3:"] = "\U0001f933\U0001f3fd",
+ [":selfie_tone4:"] = "\U0001f933\U0001f3fe",
+ [":selfie::skin-tone-4:"] = "\U0001f933\U0001f3fe",
+ [":selfie_tone5:"] = "\U0001f933\U0001f3ff",
+ [":selfie::skin-tone-5:"] = "\U0001f933\U0001f3ff",
+ [":service_dog:"] = "\U0001f415\u200d\U0001f9ba",
+ [":seven:"] = "\u0037\ufe0f\u20e3",
+ [":sewing_needle:"] = "\U0001faa1",
+ [":shallow_pan_of_food:"] = "\U0001f958",
+ [":paella:"] = "\U0001f958",
+ [":shamrock:"] = "\u2618\ufe0f",
+ [":shark:"] = "\U0001f988",
+ [":shaved_ice:"] = "\U0001f367",
+ [":sheep:"] = "\U0001f411",
+ [":shell:"] = "\U0001f41a",
+ [":shield:"] = "\U0001f6e1\ufe0f",
+ [":shinto_shrine:"] = "\u26e9\ufe0f",
+ [":ship:"] = "\U0001f6a2",
+ [":shirt:"] = "\U0001f455",
+ [":shopping_bags:"] = "\U0001f6cd\ufe0f",
+ [":shopping_cart:"] = "\U0001f6d2",
+ [":shopping_trolley:"] = "\U0001f6d2",
+ [":shorts:"] = "\U0001fa73",
+ [":shower:"] = "\U0001f6bf",
+ [":shrimp:"] = "\U0001f990",
+ [":shushing_face:"] = "\U0001f92b",
+ [":signal_strength:"] = "\U0001f4f6",
+ [":singer:"] = "\U0001f9d1\u200d\U0001f3a4",
+ [":singer_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a4",
+ [":singer_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a4",
+ [":singer::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3a4",
+ [":singer_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a4",
+ [":singer_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a4",
+ [":singer::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3a4",
+ [":singer_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a4",
+ [":singer_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a4",
+ [":singer::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3a4",
+ [":singer_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a4",
+ [":singer_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a4",
+ [":singer::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3a4",
+ [":singer_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a4",
+ [":singer_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a4",
+ [":singer::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3a4",
+ [":six:"] = "\u0036\ufe0f\u20e3",
+ [":six_pointed_star:"] = "\U0001f52f",
+ [":skateboard:"] = "\U0001f6f9",
+ [":ski:"] = "\U0001f3bf",
+ [":skier:"] = "\u26f7\ufe0f",
+ [":skull:"] = "\U0001f480",
+ [":skeleton:"] = "\U0001f480",
+ [":skull_crossbones:"] = "\u2620\ufe0f",
+ [":skull_and_crossbones:"] = "\u2620\ufe0f",
+ [":skunk:"] = "\U0001f9a8",
+ [":sled:"] = "\U0001f6f7",
+ [":sleeping:"] = "\U0001f634",
+ [":sleeping_accommodation:"] = "\U0001f6cc",
+ [":sleepy:"] = "\U0001f62a",
+ [":slight_frown:"] = "\U0001f641",
+ [":slightly_frowning_face:"] = "\U0001f641",
+ [":slight_smile:"] = "\U0001f642",
+ [":slightly_smiling_face:"] = "\U0001f642",
+ [":)"] = "\U0001f642",
+ [":-)"] = "\U0001f642",
+ ["=)"] = "\U0001f642",
+ ["=-)"] = "\U0001f642",
+ [":slot_machine:"] = "\U0001f3b0",
+ [":sloth:"] = "\U0001f9a5",
+ [":small_blue_diamond:"] = "\U0001f539",
+ [":small_orange_diamond:"] = "\U0001f538",
+ [":small_red_triangle:"] = "\U0001f53a",
+ [":small_red_triangle_down:"] = "\U0001f53b",
+ [":smile:"] = "\U0001f604",
+ [":D"] = "\U0001f604",
+ [":-D"] = "\U0001f604",
+ ["=D"] = "\U0001f604",
+ ["=-D"] = "\U0001f604",
+ [":smile_cat:"] = "\U0001f638",
+ [":smiley:"] = "\U0001f603",
+ [":smiley_cat:"] = "\U0001f63a",
+ [":smiling_face_with_3_hearts:"] = "\U0001f970",
+ [":smiling_face_with_tear:"] = "\U0001f972",
+ [":smiling_imp:"] = "\U0001f608",
+ ["]:)"] = "\U0001f608",
+ ["]:-)"] = "\U0001f608",
+ ["]=)"] = "\U0001f608",
+ ["]=-)"] = "\U0001f608",
+ [":smirk:"] = "\U0001f60f",
+ [":smirk_cat:"] = "\U0001f63c",
+ [":smoking:"] = "\U0001f6ac",
+ [":snail:"] = "\U0001f40c",
+ [":snake:"] = "\U0001f40d",
+ [":sneezing_face:"] = "\U0001f927",
+ [":sneeze:"] = "\U0001f927",
+ [":snowboarder:"] = "\U0001f3c2",
+ [":snowboarder_tone1:"] = "\U0001f3c2\U0001f3fb",
+ [":snowboarder_light_skin_tone:"] = "\U0001f3c2\U0001f3fb",
+ [":snowboarder::skin-tone-1:"] = "\U0001f3c2\U0001f3fb",
+ [":snowboarder_tone2:"] = "\U0001f3c2\U0001f3fc",
+ [":snowboarder_medium_light_skin_tone:"] = "\U0001f3c2\U0001f3fc",
+ [":snowboarder::skin-tone-2:"] = "\U0001f3c2\U0001f3fc",
+ [":snowboarder_tone3:"] = "\U0001f3c2\U0001f3fd",
+ [":snowboarder_medium_skin_tone:"] = "\U0001f3c2\U0001f3fd",
+ [":snowboarder::skin-tone-3:"] = "\U0001f3c2\U0001f3fd",
+ [":snowboarder_tone4:"] = "\U0001f3c2\U0001f3fe",
+ [":snowboarder_medium_dark_skin_tone:"] = "\U0001f3c2\U0001f3fe",
+ [":snowboarder::skin-tone-4:"] = "\U0001f3c2\U0001f3fe",
+ [":snowboarder_tone5:"] = "\U0001f3c2\U0001f3ff",
+ [":snowboarder_dark_skin_tone:"] = "\U0001f3c2\U0001f3ff",
+ [":snowboarder::skin-tone-5:"] = "\U0001f3c2\U0001f3ff",
+ [":snowflake:"] = "\u2744\ufe0f",
+ [":snowman:"] = "\u26c4",
+ [":snowman2:"] = "\u2603\ufe0f",
+ [":soap:"] = "\U0001f9fc",
+ [":sob:"] = "\U0001f62d",
+ [":,'("] = "\U0001f62d",
+ [":,'-("] = "\U0001f62d",
+ [";("] = "\U0001f62d",
+ [";-("] = "\U0001f62d",
+ ["=,'("] = "\U0001f62d",
+ ["=,'-("] = "\U0001f62d",
+ [":soccer:"] = "\u26bd",
+ [":socks:"] = "\U0001f9e6",
+ [":softball:"] = "\U0001f94e",
+ [":soon:"] = "\U0001f51c",
+ [":sos:"] = "\U0001f198",
+ [":sound:"] = "\U0001f509",
+ [":space_invader:"] = "\U0001f47e",
+ [":spades:"] = "\u2660\ufe0f",
+ [":spaghetti:"] = "\U0001f35d",
+ [":sparkle:"] = "\u2747\ufe0f",
+ [":sparkler:"] = "\U0001f387",
+ [":sparkles:"] = "\u2728",
+ [":sparkling_heart:"] = "\U0001f496",
+ [":speak_no_evil:"] = "\U0001f64a",
+ [":speaker:"] = "\U0001f508",
+ [":speaking_head:"] = "\U0001f5e3\ufe0f",
+ [":speaking_head_in_silhouette:"] = "\U0001f5e3\ufe0f",
+ [":speech_balloon:"] = "\U0001f4ac",
+ [":speech_left:"] = "\U0001f5e8\ufe0f",
+ [":left_speech_bubble:"] = "\U0001f5e8\ufe0f",
+ [":speedboat:"] = "\U0001f6a4",
+ [":spider:"] = "\U0001f577\ufe0f",
+ [":spider_web:"] = "\U0001f578\ufe0f",
+ [":sponge:"] = "\U0001f9fd",
+ [":spoon:"] = "\U0001f944",
+ [":squeeze_bottle:"] = "\U0001f9f4",
+ [":squid:"] = "\U0001f991",
+ [":stadium:"] = "\U0001f3df\ufe0f",
+ [":star:"] = "\u2b50",
+ [":star_and_crescent:"] = "\u262a\ufe0f",
+ [":star_of_david:"] = "\u2721\ufe0f",
+ [":star_struck:"] = "\U0001f929",
+ [":star2:"] = "\U0001f31f",
+ [":stars:"] = "\U0001f320",
+ [":station:"] = "\U0001f689",
+ [":statue_of_liberty:"] = "\U0001f5fd",
+ [":steam_locomotive:"] = "\U0001f682",
+ [":stethoscope:"] = "\U0001fa7a",
+ [":stew:"] = "\U0001f372",
+ [":stop_button:"] = "\u23f9\ufe0f",
+ [":stopwatch:"] = "\u23f1\ufe0f",
+ [":straight_ruler:"] = "\U0001f4cf",
+ [":strawberry:"] = "\U0001f353",
+ [":stuck_out_tongue:"] = "\U0001f61b",
+ [":P"] = "\U0001f61b",
+ [":-P"] = "\U0001f61b",
+ ["=P"] = "\U0001f61b",
+ ["=-P"] = "\U0001f61b",
+ [":stuck_out_tongue_closed_eyes:"] = "\U0001f61d",
+ [":stuck_out_tongue_winking_eye:"] = "\U0001f61c",
+ [":student:"] = "\U0001f9d1\u200d\U0001f393",
+ [":student_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f393",
+ [":student_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f393",
+ [":student::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f393",
+ [":student_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f393",
+ [":student_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f393",
+ [":student::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f393",
+ [":student_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f393",
+ [":student_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f393",
+ [":student::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f393",
+ [":student_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f393",
+ [":student_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f393",
+ [":student::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f393",
+ [":student_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f393",
+ [":student_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f393",
+ [":student::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f393",
+ [":stuffed_flatbread:"] = "\U0001f959",
+ [":stuffed_pita:"] = "\U0001f959",
+ [":sun_with_face:"] = "\U0001f31e",
+ [":sunflower:"] = "\U0001f33b",
+ [":sunglasses:"] = "\U0001f60e",
+ ["8-)"] = "\U0001f60e",
+ ["B-)"] = "\U0001f60e",
+ [":sunny:"] = "\u2600\ufe0f",
+ [":sunrise:"] = "\U0001f305",
+ [":sunrise_over_mountains:"] = "\U0001f304",
+ [":superhero:"] = "\U0001f9b8",
+ [":superhero_tone1:"] = "\U0001f9b8\U0001f3fb",
+ [":superhero_light_skin_tone:"] = "\U0001f9b8\U0001f3fb",
+ [":superhero::skin-tone-1:"] = "\U0001f9b8\U0001f3fb",
+ [":superhero_tone2:"] = "\U0001f9b8\U0001f3fc",
+ [":superhero_medium_light_skin_tone:"] = "\U0001f9b8\U0001f3fc",
+ [":superhero::skin-tone-2:"] = "\U0001f9b8\U0001f3fc",
+ [":superhero_tone3:"] = "\U0001f9b8\U0001f3fd",
+ [":superhero_medium_skin_tone:"] = "\U0001f9b8\U0001f3fd",
+ [":superhero::skin-tone-3:"] = "\U0001f9b8\U0001f3fd",
+ [":superhero_tone4:"] = "\U0001f9b8\U0001f3fe",
+ [":superhero_medium_dark_skin_tone:"] = "\U0001f9b8\U0001f3fe",
+ [":superhero::skin-tone-4:"] = "\U0001f9b8\U0001f3fe",
+ [":superhero_tone5:"] = "\U0001f9b8\U0001f3ff",
+ [":superhero_dark_skin_tone:"] = "\U0001f9b8\U0001f3ff",
+ [":superhero::skin-tone-5:"] = "\U0001f9b8\U0001f3ff",
+ [":supervillain:"] = "\U0001f9b9",
+ [":supervillain_tone1:"] = "\U0001f9b9\U0001f3fb",
+ [":supervillain_light_skin_tone:"] = "\U0001f9b9\U0001f3fb",
+ [":supervillain::skin-tone-1:"] = "\U0001f9b9\U0001f3fb",
+ [":supervillain_tone2:"] = "\U0001f9b9\U0001f3fc",
+ [":supervillain_medium_light_skin_tone:"] = "\U0001f9b9\U0001f3fc",
+ [":supervillain::skin-tone-2:"] = "\U0001f9b9\U0001f3fc",
+ [":supervillain_tone3:"] = "\U0001f9b9\U0001f3fd",
+ [":supervillain_medium_skin_tone:"] = "\U0001f9b9\U0001f3fd",
+ [":supervillain::skin-tone-3:"] = "\U0001f9b9\U0001f3fd",
+ [":supervillain_tone4:"] = "\U0001f9b9\U0001f3fe",
+ [":supervillain_medium_dark_skin_tone:"] = "\U0001f9b9\U0001f3fe",
+ [":supervillain::skin-tone-4:"] = "\U0001f9b9\U0001f3fe",
+ [":supervillain_tone5:"] = "\U0001f9b9\U0001f3ff",
+ [":supervillain_dark_skin_tone:"] = "\U0001f9b9\U0001f3ff",
+ [":supervillain::skin-tone-5:"] = "\U0001f9b9\U0001f3ff",
+ [":sushi:"] = "\U0001f363",
+ [":suspension_railway:"] = "\U0001f69f",
+ [":swan:"] = "\U0001f9a2",
+ [":sweat:"] = "\U0001f613",
+ [",:("] = "\U0001f613",
+ [",:-("] = "\U0001f613",
+ [",=("] = "\U0001f613",
+ [",=-("] = "\U0001f613",
+ [":sweat_drops:"] = "\U0001f4a6",
+ [":sweat_smile:"] = "\U0001f605",
+ [",:)"] = "\U0001f605",
+ [",:-)"] = "\U0001f605",
+ [",=)"] = "\U0001f605",
+ [",=-)"] = "\U0001f605",
+ [":sweet_potato:"] = "\U0001f360",
+ [":symbols:"] = "\U0001f523",
+ [":synagogue:"] = "\U0001f54d",
+ [":syringe:"] = "\U0001f489",
+ [":t_rex:"] = "\U0001f996",
+ [":taco:"] = "\U0001f32e",
+ [":tada:"] = "\U0001f389",
+ [":takeout_box:"] = "\U0001f961",
+ [":tamale:"] = "\U0001fad4",
+ [":tanabata_tree:"] = "\U0001f38b",
+ [":tangerine:"] = "\U0001f34a",
+ [":taurus:"] = "\u2649",
+ [":taxi:"] = "\U0001f695",
+ [":tea:"] = "\U0001f375",
+ [":teacher:"] = "\U0001f9d1\u200d\U0001f3eb",
+ [":teacher_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3eb",
+ [":teacher_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3eb",
+ [":teacher::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f3eb",
+ [":teacher_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3eb",
+ [":teacher_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3eb",
+ [":teacher::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f3eb",
+ [":teacher_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3eb",
+ [":teacher_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3eb",
+ [":teacher::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f3eb",
+ [":teacher_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3eb",
+ [":teacher_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3eb",
+ [":teacher::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f3eb",
+ [":teacher_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3eb",
+ [":teacher_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3eb",
+ [":teacher::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f3eb",
+ [":teapot:"] = "\U0001fad6",
+ [":technologist:"] = "\U0001f9d1\u200d\U0001f4bb",
+ [":technologist_tone1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bb",
+ [":technologist_light_skin_tone:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bb",
+ [":technologist::skin-tone-1:"] = "\U0001f9d1\U0001f3fb\u200d\U0001f4bb",
+ [":technologist_tone2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bb",
+ [":technologist_medium_light_skin_tone:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bb",
+ [":technologist::skin-tone-2:"] = "\U0001f9d1\U0001f3fc\u200d\U0001f4bb",
+ [":technologist_tone3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bb",
+ [":technologist_medium_skin_tone:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bb",
+ [":technologist::skin-tone-3:"] = "\U0001f9d1\U0001f3fd\u200d\U0001f4bb",
+ [":technologist_tone4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bb",
+ [":technologist_medium_dark_skin_tone:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bb",
+ [":technologist::skin-tone-4:"] = "\U0001f9d1\U0001f3fe\u200d\U0001f4bb",
+ [":technologist_tone5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bb",
+ [":technologist_dark_skin_tone:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bb",
+ [":technologist::skin-tone-5:"] = "\U0001f9d1\U0001f3ff\u200d\U0001f4bb",
+ [":teddy_bear:"] = "\U0001f9f8",
+ [":telephone:"] = "\u260e\ufe0f",
+ [":telephone_receiver:"] = "\U0001f4de",
+ [":telescope:"] = "\U0001f52d",
+ [":tennis:"] = "\U0001f3be",
+ [":tent:"] = "\u26fa",
+ [":test_tube:"] = "\U0001f9ea",
+ [":thermometer:"] = "\U0001f321\ufe0f",
+ [":thermometer_face:"] = "\U0001f912",
+ [":face_with_thermometer:"] = "\U0001f912",
+ [":thinking:"] = "\U0001f914",
+ [":thinking_face:"] = "\U0001f914",
+ [":third_place:"] = "\U0001f949",
+ [":third_place_medal:"] = "\U0001f949",
+ [":thong_sandal:"] = "\U0001fa74",
+ [":thought_balloon:"] = "\U0001f4ad",
+ [":thread:"] = "\U0001f9f5",
+ [":three:"] = "\u0033\ufe0f\u20e3",
+ [":thumbsdown:"] = "\U0001f44e",
+ [":-1:"] = "\U0001f44e",
+ [":thumbdown:"] = "\U0001f44e",
+ [":thumbsdown_tone1:"] = "\U0001f44e\U0001f3fb",
+ [":_1_tone1:"] = "\U0001f44e\U0001f3fb",
+ [":thumbdown_tone1:"] = "\U0001f44e\U0001f3fb",
+ [":thumbsdown::skin-tone-1:"] = "\U0001f44e\U0001f3fb",
+ [":-1::skin-tone-1:"] = "\U0001f44e\U0001f3fb",
+ [":thumbdown::skin-tone-1:"] = "\U0001f44e\U0001f3fb",
+ [":thumbsdown_tone2:"] = "\U0001f44e\U0001f3fc",
+ [":_1_tone2:"] = "\U0001f44e\U0001f3fc",
+ [":thumbdown_tone2:"] = "\U0001f44e\U0001f3fc",
+ [":thumbsdown::skin-tone-2:"] = "\U0001f44e\U0001f3fc",
+ [":-1::skin-tone-2:"] = "\U0001f44e\U0001f3fc",
+ [":thumbdown::skin-tone-2:"] = "\U0001f44e\U0001f3fc",
+ [":thumbsdown_tone3:"] = "\U0001f44e\U0001f3fd",
+ [":_1_tone3:"] = "\U0001f44e\U0001f3fd",
+ [":thumbdown_tone3:"] = "\U0001f44e\U0001f3fd",
+ [":thumbsdown::skin-tone-3:"] = "\U0001f44e\U0001f3fd",
+ [":-1::skin-tone-3:"] = "\U0001f44e\U0001f3fd",
+ [":thumbdown::skin-tone-3:"] = "\U0001f44e\U0001f3fd",
+ [":thumbsdown_tone4:"] = "\U0001f44e\U0001f3fe",
+ [":_1_tone4:"] = "\U0001f44e\U0001f3fe",
+ [":thumbdown_tone4:"] = "\U0001f44e\U0001f3fe",
+ [":thumbsdown::skin-tone-4:"] = "\U0001f44e\U0001f3fe",
+ [":-1::skin-tone-4:"] = "\U0001f44e\U0001f3fe",
+ [":thumbdown::skin-tone-4:"] = "\U0001f44e\U0001f3fe",
+ [":thumbsdown_tone5:"] = "\U0001f44e\U0001f3ff",
+ [":_1_tone5:"] = "\U0001f44e\U0001f3ff",
+ [":thumbdown_tone5:"] = "\U0001f44e\U0001f3ff",
+ [":thumbsdown::skin-tone-5:"] = "\U0001f44e\U0001f3ff",
+ [":-1::skin-tone-5:"] = "\U0001f44e\U0001f3ff",
+ [":thumbdown::skin-tone-5:"] = "\U0001f44e\U0001f3ff",
+ [":thumbsup:"] = "\U0001f44d",
+ [":+1:"] = "\U0001f44d",
+ [":thumbup:"] = "\U0001f44d",
+ [":thumbsup_tone1:"] = "\U0001f44d\U0001f3fb",
+ [":+1_tone1:"] = "\U0001f44d\U0001f3fb",
+ [":thumbup_tone1:"] = "\U0001f44d\U0001f3fb",
+ [":thumbsup::skin-tone-1:"] = "\U0001f44d\U0001f3fb",
+ [":+1::skin-tone-1:"] = "\U0001f44d\U0001f3fb",
+ [":thumbup::skin-tone-1:"] = "\U0001f44d\U0001f3fb",
+ [":thumbsup_tone2:"] = "\U0001f44d\U0001f3fc",
+ [":+1_tone2:"] = "\U0001f44d\U0001f3fc",
+ [":thumbup_tone2:"] = "\U0001f44d\U0001f3fc",
+ [":thumbsup::skin-tone-2:"] = "\U0001f44d\U0001f3fc",
+ [":+1::skin-tone-2:"] = "\U0001f44d\U0001f3fc",
+ [":thumbup::skin-tone-2:"] = "\U0001f44d\U0001f3fc",
+ [":thumbsup_tone3:"] = "\U0001f44d\U0001f3fd",
+ [":+1_tone3:"] = "\U0001f44d\U0001f3fd",
+ [":thumbup_tone3:"] = "\U0001f44d\U0001f3fd",
+ [":thumbsup::skin-tone-3:"] = "\U0001f44d\U0001f3fd",
+ [":+1::skin-tone-3:"] = "\U0001f44d\U0001f3fd",
+ [":thumbup::skin-tone-3:"] = "\U0001f44d\U0001f3fd",
+ [":thumbsup_tone4:"] = "\U0001f44d\U0001f3fe",
+ [":+1_tone4:"] = "\U0001f44d\U0001f3fe",
+ [":thumbup_tone4:"] = "\U0001f44d\U0001f3fe",
+ [":thumbsup::skin-tone-4:"] = "\U0001f44d\U0001f3fe",
+ [":+1::skin-tone-4:"] = "\U0001f44d\U0001f3fe",
+ [":thumbup::skin-tone-4:"] = "\U0001f44d\U0001f3fe",
+ [":thumbsup_tone5:"] = "\U0001f44d\U0001f3ff",
+ [":+1_tone5:"] = "\U0001f44d\U0001f3ff",
+ [":thumbup_tone5:"] = "\U0001f44d\U0001f3ff",
+ [":thumbsup::skin-tone-5:"] = "\U0001f44d\U0001f3ff",
+ [":+1::skin-tone-5:"] = "\U0001f44d\U0001f3ff",
+ [":thumbup::skin-tone-5:"] = "\U0001f44d\U0001f3ff",
+ [":thunder_cloud_rain:"] = "\u26c8\ufe0f",
+ [":thunder_cloud_and_rain:"] = "\u26c8\ufe0f",
+ [":ticket:"] = "\U0001f3ab",
+ [":tickets:"] = "\U0001f39f\ufe0f",
+ [":admission_tickets:"] = "\U0001f39f\ufe0f",
+ [":tiger:"] = "\U0001f42f",
+ [":tiger2:"] = "\U0001f405",
+ [":timer:"] = "\u23f2\ufe0f",
+ [":timer_clock:"] = "\u23f2\ufe0f",
+ [":tired_face:"] = "\U0001f62b",
+ [":tm:"] = "\u2122\ufe0f",
+ [":toilet:"] = "\U0001f6bd",
+ [":tokyo_tower:"] = "\U0001f5fc",
+ [":tomato:"] = "\U0001f345",
+ [":tongue:"] = "\U0001f445",
+ [":toolbox:"] = "\U0001f9f0",
+ [":tools:"] = "\U0001f6e0\ufe0f",
+ [":hammer_and_wrench:"] = "\U0001f6e0\ufe0f",
+ [":tooth:"] = "\U0001f9b7",
+ [":toothbrush:"] = "\U0001faa5",
+ [":top:"] = "\U0001f51d",
+ [":tophat:"] = "\U0001f3a9",
+ [":track_next:"] = "\u23ed\ufe0f",
+ [":next_track:"] = "\u23ed\ufe0f",
+ [":track_previous:"] = "\u23ee\ufe0f",
+ [":previous_track:"] = "\u23ee\ufe0f",
+ [":trackball:"] = "\U0001f5b2\ufe0f",
+ [":tractor:"] = "\U0001f69c",
+ [":traffic_light:"] = "\U0001f6a5",
+ [":train:"] = "\U0001f68b",
+ [":train2:"] = "\U0001f686",
+ [":tram:"] = "\U0001f68a",
+ [":transgender_flag:"] = "\U0001f3f3\ufe0f\u200d\u26a7\ufe0f",
+ [":transgender_symbol:"] = "\u26a7",
+ [":triangular_flag_on_post:"] = "\U0001f6a9",
+ [":triangular_ruler:"] = "\U0001f4d0",
+ [":trident:"] = "\U0001f531",
+ [":triumph:"] = "\U0001f624",
+ [":trolleybus:"] = "\U0001f68e",
+ [":trophy:"] = "\U0001f3c6",
+ [":tropical_drink:"] = "\U0001f379",
+ [":tropical_fish:"] = "\U0001f420",
+ [":truck:"] = "\U0001f69a",
+ [":trumpet:"] = "\U0001f3ba",
+ [":tulip:"] = "\U0001f337",
+ [":tumbler_glass:"] = "\U0001f943",
+ [":whisky:"] = "\U0001f943",
+ [":turkey:"] = "\U0001f983",
+ [":turtle:"] = "\U0001f422",
+ [":tv:"] = "\U0001f4fa",
+ [":twisted_rightwards_arrows:"] = "\U0001f500",
+ [":two:"] = "\u0032\ufe0f\u20e3",
+ [":two_hearts:"] = "\U0001f495",
+ [":two_men_holding_hands:"] = "\U0001f46c",
+ [":two_women_holding_hands:"] = "\U0001f46d",
+ [":u5272:"] = "\U0001f239",
+ [":u5408:"] = "\U0001f234",
+ [":u55b6:"] = "\U0001f23a",
+ [":u6307:"] = "\U0001f22f",
+ [":u6708:"] = "\U0001f237\ufe0f",
+ [":u6709:"] = "\U0001f236",
+ [":u6e80:"] = "\U0001f235",
+ [":u7121:"] = "\U0001f21a",
+ [":u7533:"] = "\U0001f238",
+ [":u7981:"] = "\U0001f232",
+ [":u7a7a:"] = "\U0001f233",
+ [":umbrella:"] = "\u2614",
+ [":umbrella2:"] = "\u2602\ufe0f",
+ [":unamused:"] = "\U0001f612",
+ [":s"] = "\U0001f612",
+ [":-S"] = "\U0001f612",
+ [":z"] = "\U0001f612",
+ [":-Z"] = "\U0001f612",
+ [":$"] = "\U0001f612",
+ [":-$"] = "\U0001f612",
+ ["=s"] = "\U0001f612",
+ ["=-S"] = "\U0001f612",
+ ["=z"] = "\U0001f612",
+ ["=-Z"] = "\U0001f612",
+ ["=$"] = "\U0001f612",
+ ["=-$"] = "\U0001f612",
+ [":underage:"] = "\U0001f51e",
+ [":unicorn:"] = "\U0001f984",
+ [":unicorn_face:"] = "\U0001f984",
+ [":united_nations:"] = "\U0001f1fa\U0001f1f3",
+ [":unlock:"] = "\U0001f513",
+ [":up:"] = "\U0001f199",
+ [":upside_down:"] = "\U0001f643",
+ [":upside_down_face:"] = "\U0001f643",
+ [":urn:"] = "\u26b1\ufe0f",
+ [":funeral_urn:"] = "\u26b1\ufe0f",
+ [":v:"] = "\u270c\ufe0f",
+ [":v_tone1:"] = "\u270c\U0001f3fb",
+ [":v::skin-tone-1:"] = "\u270c\U0001f3fb",
+ [":v_tone2:"] = "\u270c\U0001f3fc",
+ [":v::skin-tone-2:"] = "\u270c\U0001f3fc",
+ [":v_tone3:"] = "\u270c\U0001f3fd",
+ [":v::skin-tone-3:"] = "\u270c\U0001f3fd",
+ [":v_tone4:"] = "\u270c\U0001f3fe",
+ [":v::skin-tone-4:"] = "\u270c\U0001f3fe",
+ [":v_tone5:"] = "\u270c\U0001f3ff",
+ [":v::skin-tone-5:"] = "\u270c\U0001f3ff",
+ [":vampire:"] = "\U0001f9db",
+ [":vampire_tone1:"] = "\U0001f9db\U0001f3fb",
+ [":vampire_light_skin_tone:"] = "\U0001f9db\U0001f3fb",
+ [":vampire::skin-tone-1:"] = "\U0001f9db\U0001f3fb",
+ [":vampire_tone2:"] = "\U0001f9db\U0001f3fc",
+ [":vampire_medium_light_skin_tone:"] = "\U0001f9db\U0001f3fc",
+ [":vampire::skin-tone-2:"] = "\U0001f9db\U0001f3fc",
+ [":vampire_tone3:"] = "\U0001f9db\U0001f3fd",
+ [":vampire_medium_skin_tone:"] = "\U0001f9db\U0001f3fd",
+ [":vampire::skin-tone-3:"] = "\U0001f9db\U0001f3fd",
+ [":vampire_tone4:"] = "\U0001f9db\U0001f3fe",
+ [":vampire_medium_dark_skin_tone:"] = "\U0001f9db\U0001f3fe",
+ [":vampire::skin-tone-4:"] = "\U0001f9db\U0001f3fe",
+ [":vampire_tone5:"] = "\U0001f9db\U0001f3ff",
+ [":vampire_dark_skin_tone:"] = "\U0001f9db\U0001f3ff",
+ [":vampire::skin-tone-5:"] = "\U0001f9db\U0001f3ff",
+ [":vertical_traffic_light:"] = "\U0001f6a6",
+ [":vhs:"] = "\U0001f4fc",
+ [":vibration_mode:"] = "\U0001f4f3",
+ [":video_camera:"] = "\U0001f4f9",
+ [":video_game:"] = "\U0001f3ae",
+ [":violin:"] = "\U0001f3bb",
+ [":virgo:"] = "\u264d",
+ [":volcano:"] = "\U0001f30b",
+ [":volleyball:"] = "\U0001f3d0",
+ [":vs:"] = "\U0001f19a",
+ [":vulcan:"] = "\U0001f596",
+ [":raised_hand_with_part_between_middle_and_ring_fingers:"] = "\U0001f596",
+ [":vulcan_tone1:"] = "\U0001f596\U0001f3fb",
+ [":raised_hand_with_part_between_middle_and_ring_fingers_tone1:"] = "\U0001f596\U0001f3fb",
+ [":vulcan::skin-tone-1:"] = "\U0001f596\U0001f3fb",
+ [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-1:"] = "\U0001f596\U0001f3fb",
+ [":vulcan_tone2:"] = "\U0001f596\U0001f3fc",
+ [":raised_hand_with_part_between_middle_and_ring_fingers_tone2:"] = "\U0001f596\U0001f3fc",
+ [":vulcan::skin-tone-2:"] = "\U0001f596\U0001f3fc",
+ [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-2:"] = "\U0001f596\U0001f3fc",
+ [":vulcan_tone3:"] = "\U0001f596\U0001f3fd",
+ [":raised_hand_with_part_between_middle_and_ring_fingers_tone3:"] = "\U0001f596\U0001f3fd",
+ [":vulcan::skin-tone-3:"] = "\U0001f596\U0001f3fd",
+ [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-3:"] = "\U0001f596\U0001f3fd",
+ [":vulcan_tone4:"] = "\U0001f596\U0001f3fe",
+ [":raised_hand_with_part_between_middle_and_ring_fingers_tone4:"] = "\U0001f596\U0001f3fe",
+ [":vulcan::skin-tone-4:"] = "\U0001f596\U0001f3fe",
+ [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-4:"] = "\U0001f596\U0001f3fe",
+ [":vulcan_tone5:"] = "\U0001f596\U0001f3ff",
+ [":raised_hand_with_part_between_middle_and_ring_fingers_tone5:"] = "\U0001f596\U0001f3ff",
+ [":vulcan::skin-tone-5:"] = "\U0001f596\U0001f3ff",
+ [":raised_hand_with_part_between_middle_and_ring_fingers::skin-tone-5:"] = "\U0001f596\U0001f3ff",
+ [":waffle:"] = "\U0001f9c7",
+ [":wales:"] = "\U0001f3f4\U000e0067\U000e0062\U000e0077\U000e006c\U000e0073\U000e007f",
+ [":waning_crescent_moon:"] = "\U0001f318",
+ [":waning_gibbous_moon:"] = "\U0001f316",
+ [":warning:"] = "\u26a0\ufe0f",
+ [":wastebasket:"] = "\U0001f5d1\ufe0f",
+ [":watch:"] = "\u231a",
+ [":water_buffalo:"] = "\U0001f403",
+ [":watermelon:"] = "\U0001f349",
+ [":wave:"] = "\U0001f44b",
+ [":wave_tone1:"] = "\U0001f44b\U0001f3fb",
+ [":wave::skin-tone-1:"] = "\U0001f44b\U0001f3fb",
+ [":wave_tone2:"] = "\U0001f44b\U0001f3fc",
+ [":wave::skin-tone-2:"] = "\U0001f44b\U0001f3fc",
+ [":wave_tone3:"] = "\U0001f44b\U0001f3fd",
+ [":wave::skin-tone-3:"] = "\U0001f44b\U0001f3fd",
+ [":wave_tone4:"] = "\U0001f44b\U0001f3fe",
+ [":wave::skin-tone-4:"] = "\U0001f44b\U0001f3fe",
+ [":wave_tone5:"] = "\U0001f44b\U0001f3ff",
+ [":wave::skin-tone-5:"] = "\U0001f44b\U0001f3ff",
+ [":wavy_dash:"] = "\u3030\ufe0f",
+ [":waxing_crescent_moon:"] = "\U0001f312",
+ [":waxing_gibbous_moon:"] = "\U0001f314",
+ [":wc:"] = "\U0001f6be",
+ [":weary:"] = "\U0001f629",
+ [":wedding:"] = "\U0001f492",
+ [":whale:"] = "\U0001f433",
+ [":whale2:"] = "\U0001f40b",
+ [":wheel_of_dharma:"] = "\u2638\ufe0f",
+ [":wheelchair:"] = "\u267f",
+ [":white_check_mark:"] = "\u2705",
+ [":white_circle:"] = "\u26aa",
+ [":white_flower:"] = "\U0001f4ae",
+ [":white_heart:"] = "\U0001f90d",
+ [":white_large_square:"] = "\u2b1c",
+ [":white_medium_small_square:"] = "\u25fd",
+ [":white_medium_square:"] = "\u25fb\ufe0f",
+ [":white_small_square:"] = "\u25ab\ufe0f",
+ [":white_square_button:"] = "\U0001f533",
+ [":white_sun_cloud:"] = "\U0001f325\ufe0f",
+ [":white_sun_behind_cloud:"] = "\U0001f325\ufe0f",
+ [":white_sun_rain_cloud:"] = "\U0001f326\ufe0f",
+ [":white_sun_behind_cloud_with_rain:"] = "\U0001f326\ufe0f",
+ [":white_sun_small_cloud:"] = "\U0001f324\ufe0f",
+ [":white_sun_with_small_cloud:"] = "\U0001f324\ufe0f",
+ [":wilted_rose:"] = "\U0001f940",
+ [":wilted_flower:"] = "\U0001f940",
+ [":wind_blowing_face:"] = "\U0001f32c\ufe0f",
+ [":wind_chime:"] = "\U0001f390",
+ [":window:"] = "\U0001fa9f",
+ [":wine_glass:"] = "\U0001f377",
+ [":wink:"] = "\U0001f609",
+ [";)"] = "\U0001f609",
+ [";-)"] = "\U0001f609",
+ [":wolf:"] = "\U0001f43a",
+ [":woman:"] = "\U0001f469",
+ [":woman_and_man_holding_hands_tone1:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone1_tone2:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_light_skin_tone_medium_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone1_tone3:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_light_skin_tone_medium_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone1_tone4:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone1_tone5:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_light_skin_tone_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone2:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone2_tone1:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_light_skin_tone_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone2_tone3:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_light_skin_tone_medium_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone2_tone4:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone2_tone5:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_light_skin_tone_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone3:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone3_tone1:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_skin_tone_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone3_tone2:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_skin_tone_medium_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone3_tone4:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_skin_tone_medium_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone3_tone5:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_skin_tone_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone4:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone4_tone1:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_dark_skin_tone_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone4_tone2:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone4_tone3:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_dark_skin_tone_medium_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone4_tone5:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_medium_dark_skin_tone_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone5:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone5_tone1:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_dark_skin_tone_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone5_tone2:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone5_tone3:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_dark_skin_tone_medium_skin_tone:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_tone5_tone4:"] = "\U0001f46b",
+ [":woman_and_man_holding_hands_dark_skin_tone_medium_dark_skin_tone:"] = "\U0001f46b",
+ [":woman_artist:"] = "\U0001f469\u200d\U0001f3a8",
+ [":woman_artist_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a8",
+ [":woman_artist_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a8",
+ [":woman_artist::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a8",
+ [":woman_artist_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a8",
+ [":woman_artist_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a8",
+ [":woman_artist::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a8",
+ [":woman_artist_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a8",
+ [":woman_artist_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a8",
+ [":woman_artist::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a8",
+ [":woman_artist_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a8",
+ [":woman_artist_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a8",
+ [":woman_artist::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a8",
+ [":woman_artist_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a8",
+ [":woman_artist_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a8",
+ [":woman_artist::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a8",
+ [":woman_astronaut:"] = "\U0001f469\u200d\U0001f680",
+ [":woman_astronaut_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f680",
+ [":woman_astronaut_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f680",
+ [":woman_astronaut::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f680",
+ [":woman_astronaut_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f680",
+ [":woman_astronaut_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f680",
+ [":woman_astronaut::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f680",
+ [":woman_astronaut_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f680",
+ [":woman_astronaut_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f680",
+ [":woman_astronaut::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f680",
+ [":woman_astronaut_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f680",
+ [":woman_astronaut_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f680",
+ [":woman_astronaut::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f680",
+ [":woman_astronaut_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f680",
+ [":woman_astronaut_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f680",
+ [":woman_astronaut::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f680",
+ [":woman_bald:"] = "\U0001f469\u200d\U0001f9b2",
+ [":woman_bald_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b2",
+ [":woman_bald_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b2",
+ [":woman_bald::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b2",
+ [":woman_bald_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b2",
+ [":woman_bald_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b2",
+ [":woman_bald::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b2",
+ [":woman_bald_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b2",
+ [":woman_bald_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b2",
+ [":woman_bald::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b2",
+ [":woman_bald_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b2",
+ [":woman_bald_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b2",
+ [":woman_bald::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b2",
+ [":woman_bald_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b2",
+ [":woman_bald_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b2",
+ [":woman_bald::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b2",
+ [":woman_biking:"] = "\U0001f6b4\u200d\u2640\ufe0f",
+ [":woman_biking_tone1:"] = "\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_biking_light_skin_tone:"] = "\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_biking::skin-tone-1:"] = "\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_biking_tone2:"] = "\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_biking_medium_light_skin_tone:"] = "\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_biking::skin-tone-2:"] = "\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_biking_tone3:"] = "\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_biking_medium_skin_tone:"] = "\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_biking::skin-tone-3:"] = "\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_biking_tone4:"] = "\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_biking_medium_dark_skin_tone:"] = "\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_biking::skin-tone-4:"] = "\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_biking_tone5:"] = "\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_biking_dark_skin_tone:"] = "\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_biking::skin-tone-5:"] = "\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball:"] = "\u26f9\ufe0f\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_tone1:"] = "\u26f9\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_light_skin_tone:"] = "\u26f9\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball::skin-tone-1:"] = "\u26f9\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_tone2:"] = "\u26f9\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_medium_light_skin_tone:"] = "\u26f9\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball::skin-tone-2:"] = "\u26f9\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_tone3:"] = "\u26f9\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_medium_skin_tone:"] = "\u26f9\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball::skin-tone-3:"] = "\u26f9\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_tone4:"] = "\u26f9\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_medium_dark_skin_tone:"] = "\u26f9\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball::skin-tone-4:"] = "\u26f9\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_tone5:"] = "\u26f9\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball_dark_skin_tone:"] = "\u26f9\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_bouncing_ball::skin-tone-5:"] = "\u26f9\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_bowing:"] = "\U0001f647\u200d\u2640\ufe0f",
+ [":woman_bowing_tone1:"] = "\U0001f647\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_bowing_light_skin_tone:"] = "\U0001f647\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_bowing::skin-tone-1:"] = "\U0001f647\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_bowing_tone2:"] = "\U0001f647\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_bowing_medium_light_skin_tone:"] = "\U0001f647\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_bowing::skin-tone-2:"] = "\U0001f647\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_bowing_tone3:"] = "\U0001f647\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_bowing_medium_skin_tone:"] = "\U0001f647\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_bowing::skin-tone-3:"] = "\U0001f647\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_bowing_tone4:"] = "\U0001f647\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_bowing_medium_dark_skin_tone:"] = "\U0001f647\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_bowing::skin-tone-4:"] = "\U0001f647\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_bowing_tone5:"] = "\U0001f647\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_bowing_dark_skin_tone:"] = "\U0001f647\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_bowing::skin-tone-5:"] = "\U0001f647\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_cartwheeling:"] = "\U0001f938\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_tone1:"] = "\U0001f938\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_light_skin_tone:"] = "\U0001f938\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_cartwheeling::skin-tone-1:"] = "\U0001f938\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_tone2:"] = "\U0001f938\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_medium_light_skin_tone:"] = "\U0001f938\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_cartwheeling::skin-tone-2:"] = "\U0001f938\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_tone3:"] = "\U0001f938\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_medium_skin_tone:"] = "\U0001f938\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_cartwheeling::skin-tone-3:"] = "\U0001f938\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_tone4:"] = "\U0001f938\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_medium_dark_skin_tone:"] = "\U0001f938\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_cartwheeling::skin-tone-4:"] = "\U0001f938\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_tone5:"] = "\U0001f938\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_cartwheeling_dark_skin_tone:"] = "\U0001f938\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_cartwheeling::skin-tone-5:"] = "\U0001f938\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_climbing:"] = "\U0001f9d7\u200d\u2640\ufe0f",
+ [":woman_climbing_tone1:"] = "\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_climbing_light_skin_tone:"] = "\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_climbing::skin-tone-1:"] = "\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_climbing_tone2:"] = "\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_climbing_medium_light_skin_tone:"] = "\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_climbing::skin-tone-2:"] = "\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_climbing_tone3:"] = "\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_climbing_medium_skin_tone:"] = "\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_climbing::skin-tone-3:"] = "\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_climbing_tone4:"] = "\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_climbing_medium_dark_skin_tone:"] = "\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_climbing::skin-tone-4:"] = "\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_climbing_tone5:"] = "\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_climbing_dark_skin_tone:"] = "\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_climbing::skin-tone-5:"] = "\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_construction_worker:"] = "\U0001f477\u200d\u2640\ufe0f",
+ [":woman_construction_worker_tone1:"] = "\U0001f477\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_construction_worker_light_skin_tone:"] = "\U0001f477\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_construction_worker::skin-tone-1:"] = "\U0001f477\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_construction_worker_tone2:"] = "\U0001f477\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_construction_worker_medium_light_skin_tone:"] = "\U0001f477\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_construction_worker::skin-tone-2:"] = "\U0001f477\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_construction_worker_tone3:"] = "\U0001f477\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_construction_worker_medium_skin_tone:"] = "\U0001f477\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_construction_worker::skin-tone-3:"] = "\U0001f477\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_construction_worker_tone4:"] = "\U0001f477\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_construction_worker_medium_dark_skin_tone:"] = "\U0001f477\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_construction_worker::skin-tone-4:"] = "\U0001f477\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_construction_worker_tone5:"] = "\U0001f477\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_construction_worker_dark_skin_tone:"] = "\U0001f477\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_construction_worker::skin-tone-5:"] = "\U0001f477\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_cook:"] = "\U0001f469\u200d\U0001f373",
+ [":woman_cook_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f373",
+ [":woman_cook_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f373",
+ [":woman_cook::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f373",
+ [":woman_cook_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f373",
+ [":woman_cook_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f373",
+ [":woman_cook::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f373",
+ [":woman_cook_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f373",
+ [":woman_cook_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f373",
+ [":woman_cook::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f373",
+ [":woman_cook_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f373",
+ [":woman_cook_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f373",
+ [":woman_cook::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f373",
+ [":woman_cook_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f373",
+ [":woman_cook_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f373",
+ [":woman_cook::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f373",
+ [":woman_curly_haired:"] = "\U0001f469\u200d\U0001f9b1",
+ [":woman_curly_haired_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b1",
+ [":woman_curly_haired_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b1",
+ [":woman_curly_haired::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b1",
+ [":woman_curly_haired_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b1",
+ [":woman_curly_haired_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b1",
+ [":woman_curly_haired::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b1",
+ [":woman_curly_haired_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b1",
+ [":woman_curly_haired_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b1",
+ [":woman_curly_haired::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b1",
+ [":woman_curly_haired_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b1",
+ [":woman_curly_haired_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b1",
+ [":woman_curly_haired::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b1",
+ [":woman_curly_haired_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b1",
+ [":woman_curly_haired_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b1",
+ [":woman_curly_haired::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b1",
+ [":woman_detective:"] = "\U0001f575\ufe0f\u200d\u2640\ufe0f",
+ [":woman_detective_tone1:"] = "\U0001f575\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_detective_light_skin_tone:"] = "\U0001f575\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_detective::skin-tone-1:"] = "\U0001f575\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_detective_tone2:"] = "\U0001f575\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_detective_medium_light_skin_tone:"] = "\U0001f575\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_detective::skin-tone-2:"] = "\U0001f575\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_detective_tone3:"] = "\U0001f575\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_detective_medium_skin_tone:"] = "\U0001f575\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_detective::skin-tone-3:"] = "\U0001f575\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_detective_tone4:"] = "\U0001f575\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_detective_medium_dark_skin_tone:"] = "\U0001f575\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_detective::skin-tone-4:"] = "\U0001f575\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_detective_tone5:"] = "\U0001f575\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_detective_dark_skin_tone:"] = "\U0001f575\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_detective::skin-tone-5:"] = "\U0001f575\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_elf:"] = "\U0001f9dd\u200d\u2640\ufe0f",
+ [":woman_elf_tone1:"] = "\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_elf_light_skin_tone:"] = "\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_elf::skin-tone-1:"] = "\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_elf_tone2:"] = "\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_elf_medium_light_skin_tone:"] = "\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_elf::skin-tone-2:"] = "\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_elf_tone3:"] = "\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_elf_medium_skin_tone:"] = "\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_elf::skin-tone-3:"] = "\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_elf_tone4:"] = "\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_elf_medium_dark_skin_tone:"] = "\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_elf::skin-tone-4:"] = "\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_elf_tone5:"] = "\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_elf_dark_skin_tone:"] = "\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_elf::skin-tone-5:"] = "\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_facepalming:"] = "\U0001f926\u200d\u2640\ufe0f",
+ [":woman_facepalming_tone1:"] = "\U0001f926\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_facepalming_light_skin_tone:"] = "\U0001f926\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_facepalming::skin-tone-1:"] = "\U0001f926\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_facepalming_tone2:"] = "\U0001f926\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_facepalming_medium_light_skin_tone:"] = "\U0001f926\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_facepalming::skin-tone-2:"] = "\U0001f926\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_facepalming_tone3:"] = "\U0001f926\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_facepalming_medium_skin_tone:"] = "\U0001f926\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_facepalming::skin-tone-3:"] = "\U0001f926\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_facepalming_tone4:"] = "\U0001f926\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_facepalming_medium_dark_skin_tone:"] = "\U0001f926\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_facepalming::skin-tone-4:"] = "\U0001f926\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_facepalming_tone5:"] = "\U0001f926\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_facepalming_dark_skin_tone:"] = "\U0001f926\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_facepalming::skin-tone-5:"] = "\U0001f926\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_factory_worker:"] = "\U0001f469\u200d\U0001f3ed",
+ [":woman_factory_worker_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3ed",
+ [":woman_factory_worker_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f3ed",
+ [":woman_factory_worker::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3ed",
+ [":woman_factory_worker_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3ed",
+ [":woman_factory_worker_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f3ed",
+ [":woman_factory_worker::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3ed",
+ [":woman_factory_worker_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3ed",
+ [":woman_factory_worker_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f3ed",
+ [":woman_factory_worker::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3ed",
+ [":woman_factory_worker_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3ed",
+ [":woman_factory_worker_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f3ed",
+ [":woman_factory_worker::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3ed",
+ [":woman_factory_worker_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3ed",
+ [":woman_factory_worker_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f3ed",
+ [":woman_factory_worker::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3ed",
+ [":woman_fairy:"] = "\U0001f9da\u200d\u2640\ufe0f",
+ [":woman_fairy_tone1:"] = "\U0001f9da\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_fairy_light_skin_tone:"] = "\U0001f9da\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_fairy::skin-tone-1:"] = "\U0001f9da\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_fairy_tone2:"] = "\U0001f9da\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_fairy_medium_light_skin_tone:"] = "\U0001f9da\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_fairy::skin-tone-2:"] = "\U0001f9da\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_fairy_tone3:"] = "\U0001f9da\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_fairy_medium_skin_tone:"] = "\U0001f9da\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_fairy::skin-tone-3:"] = "\U0001f9da\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_fairy_tone4:"] = "\U0001f9da\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_fairy_medium_dark_skin_tone:"] = "\U0001f9da\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_fairy::skin-tone-4:"] = "\U0001f9da\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_fairy_tone5:"] = "\U0001f9da\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_fairy_dark_skin_tone:"] = "\U0001f9da\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_fairy::skin-tone-5:"] = "\U0001f9da\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_farmer:"] = "\U0001f469\u200d\U0001f33e",
+ [":woman_farmer_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f33e",
+ [":woman_farmer_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f33e",
+ [":woman_farmer::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f33e",
+ [":woman_farmer_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f33e",
+ [":woman_farmer_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f33e",
+ [":woman_farmer::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f33e",
+ [":woman_farmer_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f33e",
+ [":woman_farmer_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f33e",
+ [":woman_farmer::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f33e",
+ [":woman_farmer_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f33e",
+ [":woman_farmer_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f33e",
+ [":woman_farmer::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f33e",
+ [":woman_farmer_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f33e",
+ [":woman_farmer_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f33e",
+ [":woman_farmer::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f33e",
+ [":woman_feeding_baby:"] = "\U0001f469\u200d\U0001f37c",
+ [":woman_feeding_baby_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f37c",
+ [":woman_feeding_baby_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f37c",
+ [":woman_feeding_baby::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f37c",
+ [":woman_feeding_baby_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f37c",
+ [":woman_feeding_baby_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f37c",
+ [":woman_feeding_baby::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f37c",
+ [":woman_feeding_baby_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f37c",
+ [":woman_feeding_baby_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f37c",
+ [":woman_feeding_baby::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f37c",
+ [":woman_feeding_baby_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f37c",
+ [":woman_feeding_baby_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f37c",
+ [":woman_feeding_baby::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f37c",
+ [":woman_feeding_baby_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f37c",
+ [":woman_feeding_baby_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f37c",
+ [":woman_feeding_baby::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f37c",
+ [":woman_firefighter:"] = "\U0001f469\u200d\U0001f692",
+ [":woman_firefighter_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f692",
+ [":woman_firefighter_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f692",
+ [":woman_firefighter::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f692",
+ [":woman_firefighter_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f692",
+ [":woman_firefighter_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f692",
+ [":woman_firefighter::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f692",
+ [":woman_firefighter_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f692",
+ [":woman_firefighter_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f692",
+ [":woman_firefighter::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f692",
+ [":woman_firefighter_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f692",
+ [":woman_firefighter_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f692",
+ [":woman_firefighter::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f692",
+ [":woman_firefighter_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f692",
+ [":woman_firefighter_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f692",
+ [":woman_firefighter::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f692",
+ [":woman_frowning:"] = "\U0001f64d\u200d\u2640\ufe0f",
+ [":woman_frowning_tone1:"] = "\U0001f64d\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_frowning_light_skin_tone:"] = "\U0001f64d\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_frowning::skin-tone-1:"] = "\U0001f64d\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_frowning_tone2:"] = "\U0001f64d\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_frowning_medium_light_skin_tone:"] = "\U0001f64d\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_frowning::skin-tone-2:"] = "\U0001f64d\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_frowning_tone3:"] = "\U0001f64d\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_frowning_medium_skin_tone:"] = "\U0001f64d\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_frowning::skin-tone-3:"] = "\U0001f64d\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_frowning_tone4:"] = "\U0001f64d\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_frowning_medium_dark_skin_tone:"] = "\U0001f64d\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_frowning::skin-tone-4:"] = "\U0001f64d\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_frowning_tone5:"] = "\U0001f64d\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_frowning_dark_skin_tone:"] = "\U0001f64d\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_frowning::skin-tone-5:"] = "\U0001f64d\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_genie:"] = "\U0001f9de\u200d\u2640\ufe0f",
+ [":woman_gesturing_no:"] = "\U0001f645\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_tone1:"] = "\U0001f645\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_light_skin_tone:"] = "\U0001f645\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_gesturing_no::skin-tone-1:"] = "\U0001f645\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_tone2:"] = "\U0001f645\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_medium_light_skin_tone:"] = "\U0001f645\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_gesturing_no::skin-tone-2:"] = "\U0001f645\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_tone3:"] = "\U0001f645\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_medium_skin_tone:"] = "\U0001f645\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_gesturing_no::skin-tone-3:"] = "\U0001f645\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_tone4:"] = "\U0001f645\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_medium_dark_skin_tone:"] = "\U0001f645\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_gesturing_no::skin-tone-4:"] = "\U0001f645\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_tone5:"] = "\U0001f645\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_gesturing_no_dark_skin_tone:"] = "\U0001f645\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_gesturing_no::skin-tone-5:"] = "\U0001f645\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok:"] = "\U0001f646\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_tone1:"] = "\U0001f646\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_light_skin_tone:"] = "\U0001f646\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok::skin-tone-1:"] = "\U0001f646\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_tone2:"] = "\U0001f646\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_medium_light_skin_tone:"] = "\U0001f646\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok::skin-tone-2:"] = "\U0001f646\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_tone3:"] = "\U0001f646\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_medium_skin_tone:"] = "\U0001f646\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok::skin-tone-3:"] = "\U0001f646\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_tone4:"] = "\U0001f646\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_medium_dark_skin_tone:"] = "\U0001f646\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok::skin-tone-4:"] = "\U0001f646\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_tone5:"] = "\U0001f646\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok_dark_skin_tone:"] = "\U0001f646\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_gesturing_ok::skin-tone-5:"] = "\U0001f646\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage:"] = "\U0001f486\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_tone1:"] = "\U0001f486\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_light_skin_tone:"] = "\U0001f486\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage::skin-tone-1:"] = "\U0001f486\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_tone2:"] = "\U0001f486\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_medium_light_skin_tone:"] = "\U0001f486\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage::skin-tone-2:"] = "\U0001f486\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_tone3:"] = "\U0001f486\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_medium_skin_tone:"] = "\U0001f486\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage::skin-tone-3:"] = "\U0001f486\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_tone4:"] = "\U0001f486\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_medium_dark_skin_tone:"] = "\U0001f486\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage::skin-tone-4:"] = "\U0001f486\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_tone5:"] = "\U0001f486\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage_dark_skin_tone:"] = "\U0001f486\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_getting_face_massage::skin-tone-5:"] = "\U0001f486\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_getting_haircut:"] = "\U0001f487\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_tone1:"] = "\U0001f487\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_light_skin_tone:"] = "\U0001f487\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_getting_haircut::skin-tone-1:"] = "\U0001f487\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_tone2:"] = "\U0001f487\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_medium_light_skin_tone:"] = "\U0001f487\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_getting_haircut::skin-tone-2:"] = "\U0001f487\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_tone3:"] = "\U0001f487\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_medium_skin_tone:"] = "\U0001f487\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_getting_haircut::skin-tone-3:"] = "\U0001f487\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_tone4:"] = "\U0001f487\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_medium_dark_skin_tone:"] = "\U0001f487\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_getting_haircut::skin-tone-4:"] = "\U0001f487\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_tone5:"] = "\U0001f487\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_getting_haircut_dark_skin_tone:"] = "\U0001f487\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_getting_haircut::skin-tone-5:"] = "\U0001f487\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_golfing:"] = "\U0001f3cc\ufe0f\u200d\u2640\ufe0f",
+ [":woman_golfing_tone1:"] = "\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_golfing_light_skin_tone:"] = "\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_golfing::skin-tone-1:"] = "\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_golfing_tone2:"] = "\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_golfing_medium_light_skin_tone:"] = "\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_golfing::skin-tone-2:"] = "\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_golfing_tone3:"] = "\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_golfing_medium_skin_tone:"] = "\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_golfing::skin-tone-3:"] = "\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_golfing_tone4:"] = "\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_golfing_medium_dark_skin_tone:"] = "\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_golfing::skin-tone-4:"] = "\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_golfing_tone5:"] = "\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_golfing_dark_skin_tone:"] = "\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_golfing::skin-tone-5:"] = "\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_guard:"] = "\U0001f482\u200d\u2640\ufe0f",
+ [":woman_guard_tone1:"] = "\U0001f482\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_guard_light_skin_tone:"] = "\U0001f482\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_guard::skin-tone-1:"] = "\U0001f482\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_guard_tone2:"] = "\U0001f482\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_guard_medium_light_skin_tone:"] = "\U0001f482\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_guard::skin-tone-2:"] = "\U0001f482\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_guard_tone3:"] = "\U0001f482\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_guard_medium_skin_tone:"] = "\U0001f482\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_guard::skin-tone-3:"] = "\U0001f482\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_guard_tone4:"] = "\U0001f482\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_guard_medium_dark_skin_tone:"] = "\U0001f482\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_guard::skin-tone-4:"] = "\U0001f482\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_guard_tone5:"] = "\U0001f482\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_guard_dark_skin_tone:"] = "\U0001f482\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_guard::skin-tone-5:"] = "\U0001f482\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_health_worker:"] = "\U0001f469\u200d\u2695\ufe0f",
+ [":woman_health_worker_tone1:"] = "\U0001f469\U0001f3fb\u200d\u2695\ufe0f",
+ [":woman_health_worker_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\u2695\ufe0f",
+ [":woman_health_worker::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\u2695\ufe0f",
+ [":woman_health_worker_tone2:"] = "\U0001f469\U0001f3fc\u200d\u2695\ufe0f",
+ [":woman_health_worker_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\u2695\ufe0f",
+ [":woman_health_worker::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\u2695\ufe0f",
+ [":woman_health_worker_tone3:"] = "\U0001f469\U0001f3fd\u200d\u2695\ufe0f",
+ [":woman_health_worker_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\u2695\ufe0f",
+ [":woman_health_worker::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\u2695\ufe0f",
+ [":woman_health_worker_tone4:"] = "\U0001f469\U0001f3fe\u200d\u2695\ufe0f",
+ [":woman_health_worker_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\u2695\ufe0f",
+ [":woman_health_worker::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\u2695\ufe0f",
+ [":woman_health_worker_tone5:"] = "\U0001f469\U0001f3ff\u200d\u2695\ufe0f",
+ [":woman_health_worker_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\u2695\ufe0f",
+ [":woman_health_worker::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\u2695\ufe0f",
+ [":woman_in_lotus_position:"] = "\U0001f9d8\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_tone1:"] = "\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_light_skin_tone:"] = "\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position::skin-tone-1:"] = "\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_tone2:"] = "\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_medium_light_skin_tone:"] = "\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position::skin-tone-2:"] = "\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_tone3:"] = "\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_medium_skin_tone:"] = "\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position::skin-tone-3:"] = "\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_tone4:"] = "\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_medium_dark_skin_tone:"] = "\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position::skin-tone-4:"] = "\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_tone5:"] = "\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position_dark_skin_tone:"] = "\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_in_lotus_position::skin-tone-5:"] = "\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_in_manual_wheelchair:"] = "\U0001f469\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bd",
+ [":woman_in_manual_wheelchair::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bd",
+ [":woman_in_motorized_wheelchair:"] = "\U0001f469\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bc",
+ [":woman_in_motorized_wheelchair::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9bc",
+ [":woman_in_steamy_room:"] = "\U0001f9d6\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_tone1:"] = "\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_light_skin_tone:"] = "\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room::skin-tone-1:"] = "\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_tone2:"] = "\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_medium_light_skin_tone:"] = "\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room::skin-tone-2:"] = "\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_tone3:"] = "\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_medium_skin_tone:"] = "\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room::skin-tone-3:"] = "\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_tone4:"] = "\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_medium_dark_skin_tone:"] = "\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room::skin-tone-4:"] = "\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_tone5:"] = "\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room_dark_skin_tone:"] = "\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_in_steamy_room::skin-tone-5:"] = "\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo:"] = "\U0001f935\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_tone1:"] = "\U0001f935\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_light_skin_tone:"] = "\U0001f935\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo::skin-tone-1:"] = "\U0001f935\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_tone2:"] = "\U0001f935\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_medium_light_skin_tone:"] = "\U0001f935\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo::skin-tone-2:"] = "\U0001f935\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_tone3:"] = "\U0001f935\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_medium_skin_tone:"] = "\U0001f935\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo::skin-tone-3:"] = "\U0001f935\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_tone4:"] = "\U0001f935\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_medium_dark_skin_tone:"] = "\U0001f935\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo::skin-tone-4:"] = "\U0001f935\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_tone5:"] = "\U0001f935\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo_dark_skin_tone:"] = "\U0001f935\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_in_tuxedo::skin-tone-5:"] = "\U0001f935\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_judge:"] = "\U0001f469\u200d\u2696\ufe0f",
+ [":woman_judge_tone1:"] = "\U0001f469\U0001f3fb\u200d\u2696\ufe0f",
+ [":woman_judge_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\u2696\ufe0f",
+ [":woman_judge::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\u2696\ufe0f",
+ [":woman_judge_tone2:"] = "\U0001f469\U0001f3fc\u200d\u2696\ufe0f",
+ [":woman_judge_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\u2696\ufe0f",
+ [":woman_judge::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\u2696\ufe0f",
+ [":woman_judge_tone3:"] = "\U0001f469\U0001f3fd\u200d\u2696\ufe0f",
+ [":woman_judge_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\u2696\ufe0f",
+ [":woman_judge::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\u2696\ufe0f",
+ [":woman_judge_tone4:"] = "\U0001f469\U0001f3fe\u200d\u2696\ufe0f",
+ [":woman_judge_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\u2696\ufe0f",
+ [":woman_judge::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\u2696\ufe0f",
+ [":woman_judge_tone5:"] = "\U0001f469\U0001f3ff\u200d\u2696\ufe0f",
+ [":woman_judge_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\u2696\ufe0f",
+ [":woman_judge::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\u2696\ufe0f",
+ [":woman_juggling:"] = "\U0001f939\u200d\u2640\ufe0f",
+ [":woman_juggling_tone1:"] = "\U0001f939\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_juggling_light_skin_tone:"] = "\U0001f939\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_juggling::skin-tone-1:"] = "\U0001f939\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_juggling_tone2:"] = "\U0001f939\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_juggling_medium_light_skin_tone:"] = "\U0001f939\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_juggling::skin-tone-2:"] = "\U0001f939\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_juggling_tone3:"] = "\U0001f939\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_juggling_medium_skin_tone:"] = "\U0001f939\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_juggling::skin-tone-3:"] = "\U0001f939\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_juggling_tone4:"] = "\U0001f939\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_juggling_medium_dark_skin_tone:"] = "\U0001f939\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_juggling::skin-tone-4:"] = "\U0001f939\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_juggling_tone5:"] = "\U0001f939\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_juggling_dark_skin_tone:"] = "\U0001f939\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_juggling::skin-tone-5:"] = "\U0001f939\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_kneeling:"] = "\U0001f9ce\u200d\u2640\ufe0f",
+ [":woman_kneeling_tone1:"] = "\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_kneeling_light_skin_tone:"] = "\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_kneeling::skin-tone-1:"] = "\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_kneeling_tone2:"] = "\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_kneeling_medium_light_skin_tone:"] = "\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_kneeling::skin-tone-2:"] = "\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_kneeling_tone3:"] = "\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_kneeling_medium_skin_tone:"] = "\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_kneeling::skin-tone-3:"] = "\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_kneeling_tone4:"] = "\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_kneeling_medium_dark_skin_tone:"] = "\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_kneeling::skin-tone-4:"] = "\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_kneeling_tone5:"] = "\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_kneeling_dark_skin_tone:"] = "\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_kneeling::skin-tone-5:"] = "\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_lifting_weights:"] = "\U0001f3cb\ufe0f\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_tone1:"] = "\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_light_skin_tone:"] = "\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_lifting_weights::skin-tone-1:"] = "\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_tone2:"] = "\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_medium_light_skin_tone:"] = "\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_lifting_weights::skin-tone-2:"] = "\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_tone3:"] = "\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_medium_skin_tone:"] = "\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_lifting_weights::skin-tone-3:"] = "\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_tone4:"] = "\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_medium_dark_skin_tone:"] = "\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_lifting_weights::skin-tone-4:"] = "\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_tone5:"] = "\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_lifting_weights_dark_skin_tone:"] = "\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_lifting_weights::skin-tone-5:"] = "\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_mage:"] = "\U0001f9d9\u200d\u2640\ufe0f",
+ [":woman_mage_tone1:"] = "\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_mage_light_skin_tone:"] = "\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_mage::skin-tone-1:"] = "\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_mage_tone2:"] = "\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_mage_medium_light_skin_tone:"] = "\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_mage::skin-tone-2:"] = "\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_mage_tone3:"] = "\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_mage_medium_skin_tone:"] = "\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_mage::skin-tone-3:"] = "\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_mage_tone4:"] = "\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_mage_medium_dark_skin_tone:"] = "\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_mage::skin-tone-4:"] = "\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_mage_tone5:"] = "\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_mage_dark_skin_tone:"] = "\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_mage::skin-tone-5:"] = "\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_mechanic:"] = "\U0001f469\u200d\U0001f527",
+ [":woman_mechanic_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f527",
+ [":woman_mechanic_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f527",
+ [":woman_mechanic::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f527",
+ [":woman_mechanic_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f527",
+ [":woman_mechanic_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f527",
+ [":woman_mechanic::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f527",
+ [":woman_mechanic_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f527",
+ [":woman_mechanic_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f527",
+ [":woman_mechanic::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f527",
+ [":woman_mechanic_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f527",
+ [":woman_mechanic_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f527",
+ [":woman_mechanic::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f527",
+ [":woman_mechanic_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f527",
+ [":woman_mechanic_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f527",
+ [":woman_mechanic::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f527",
+ [":woman_mountain_biking:"] = "\U0001f6b5\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_tone1:"] = "\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_light_skin_tone:"] = "\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_mountain_biking::skin-tone-1:"] = "\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_tone2:"] = "\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_medium_light_skin_tone:"] = "\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_mountain_biking::skin-tone-2:"] = "\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_tone3:"] = "\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_medium_skin_tone:"] = "\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_mountain_biking::skin-tone-3:"] = "\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_tone4:"] = "\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_medium_dark_skin_tone:"] = "\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_mountain_biking::skin-tone-4:"] = "\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_tone5:"] = "\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_mountain_biking_dark_skin_tone:"] = "\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_mountain_biking::skin-tone-5:"] = "\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_office_worker:"] = "\U0001f469\u200d\U0001f4bc",
+ [":woman_office_worker_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bc",
+ [":woman_office_worker_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bc",
+ [":woman_office_worker::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bc",
+ [":woman_office_worker_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bc",
+ [":woman_office_worker_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bc",
+ [":woman_office_worker::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bc",
+ [":woman_office_worker_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bc",
+ [":woman_office_worker_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bc",
+ [":woman_office_worker::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bc",
+ [":woman_office_worker_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bc",
+ [":woman_office_worker_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bc",
+ [":woman_office_worker::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bc",
+ [":woman_office_worker_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bc",
+ [":woman_office_worker_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bc",
+ [":woman_office_worker::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bc",
+ [":woman_pilot:"] = "\U0001f469\u200d\u2708\ufe0f",
+ [":woman_pilot_tone1:"] = "\U0001f469\U0001f3fb\u200d\u2708\ufe0f",
+ [":woman_pilot_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\u2708\ufe0f",
+ [":woman_pilot::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\u2708\ufe0f",
+ [":woman_pilot_tone2:"] = "\U0001f469\U0001f3fc\u200d\u2708\ufe0f",
+ [":woman_pilot_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\u2708\ufe0f",
+ [":woman_pilot::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\u2708\ufe0f",
+ [":woman_pilot_tone3:"] = "\U0001f469\U0001f3fd\u200d\u2708\ufe0f",
+ [":woman_pilot_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\u2708\ufe0f",
+ [":woman_pilot::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\u2708\ufe0f",
+ [":woman_pilot_tone4:"] = "\U0001f469\U0001f3fe\u200d\u2708\ufe0f",
+ [":woman_pilot_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\u2708\ufe0f",
+ [":woman_pilot::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\u2708\ufe0f",
+ [":woman_pilot_tone5:"] = "\U0001f469\U0001f3ff\u200d\u2708\ufe0f",
+ [":woman_pilot_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\u2708\ufe0f",
+ [":woman_pilot::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\u2708\ufe0f",
+ [":woman_playing_handball:"] = "\U0001f93e\u200d\u2640\ufe0f",
+ [":woman_playing_handball_tone1:"] = "\U0001f93e\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_playing_handball_light_skin_tone:"] = "\U0001f93e\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_playing_handball::skin-tone-1:"] = "\U0001f93e\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_playing_handball_tone2:"] = "\U0001f93e\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_playing_handball_medium_light_skin_tone:"] = "\U0001f93e\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_playing_handball::skin-tone-2:"] = "\U0001f93e\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_playing_handball_tone3:"] = "\U0001f93e\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_playing_handball_medium_skin_tone:"] = "\U0001f93e\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_playing_handball::skin-tone-3:"] = "\U0001f93e\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_playing_handball_tone4:"] = "\U0001f93e\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_playing_handball_medium_dark_skin_tone:"] = "\U0001f93e\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_playing_handball::skin-tone-4:"] = "\U0001f93e\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_playing_handball_tone5:"] = "\U0001f93e\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_playing_handball_dark_skin_tone:"] = "\U0001f93e\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_playing_handball::skin-tone-5:"] = "\U0001f93e\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo:"] = "\U0001f93d\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_tone1:"] = "\U0001f93d\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_light_skin_tone:"] = "\U0001f93d\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo::skin-tone-1:"] = "\U0001f93d\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_tone2:"] = "\U0001f93d\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_medium_light_skin_tone:"] = "\U0001f93d\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo::skin-tone-2:"] = "\U0001f93d\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_tone3:"] = "\U0001f93d\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_medium_skin_tone:"] = "\U0001f93d\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo::skin-tone-3:"] = "\U0001f93d\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_tone4:"] = "\U0001f93d\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_medium_dark_skin_tone:"] = "\U0001f93d\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo::skin-tone-4:"] = "\U0001f93d\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_tone5:"] = "\U0001f93d\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo_dark_skin_tone:"] = "\U0001f93d\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_playing_water_polo::skin-tone-5:"] = "\U0001f93d\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_police_officer:"] = "\U0001f46e\u200d\u2640\ufe0f",
+ [":woman_police_officer_tone1:"] = "\U0001f46e\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_police_officer_light_skin_tone:"] = "\U0001f46e\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_police_officer::skin-tone-1:"] = "\U0001f46e\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_police_officer_tone2:"] = "\U0001f46e\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_police_officer_medium_light_skin_tone:"] = "\U0001f46e\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_police_officer::skin-tone-2:"] = "\U0001f46e\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_police_officer_tone3:"] = "\U0001f46e\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_police_officer_medium_skin_tone:"] = "\U0001f46e\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_police_officer::skin-tone-3:"] = "\U0001f46e\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_police_officer_tone4:"] = "\U0001f46e\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_police_officer_medium_dark_skin_tone:"] = "\U0001f46e\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_police_officer::skin-tone-4:"] = "\U0001f46e\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_police_officer_tone5:"] = "\U0001f46e\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_police_officer_dark_skin_tone:"] = "\U0001f46e\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_police_officer::skin-tone-5:"] = "\U0001f46e\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_pouting:"] = "\U0001f64e\u200d\u2640\ufe0f",
+ [":woman_pouting_tone1:"] = "\U0001f64e\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_pouting_light_skin_tone:"] = "\U0001f64e\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_pouting::skin-tone-1:"] = "\U0001f64e\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_pouting_tone2:"] = "\U0001f64e\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_pouting_medium_light_skin_tone:"] = "\U0001f64e\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_pouting::skin-tone-2:"] = "\U0001f64e\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_pouting_tone3:"] = "\U0001f64e\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_pouting_medium_skin_tone:"] = "\U0001f64e\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_pouting::skin-tone-3:"] = "\U0001f64e\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_pouting_tone4:"] = "\U0001f64e\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_pouting_medium_dark_skin_tone:"] = "\U0001f64e\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_pouting::skin-tone-4:"] = "\U0001f64e\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_pouting_tone5:"] = "\U0001f64e\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_pouting_dark_skin_tone:"] = "\U0001f64e\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_pouting::skin-tone-5:"] = "\U0001f64e\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_raising_hand:"] = "\U0001f64b\u200d\u2640\ufe0f",
+ [":woman_raising_hand_tone1:"] = "\U0001f64b\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_raising_hand_light_skin_tone:"] = "\U0001f64b\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_raising_hand::skin-tone-1:"] = "\U0001f64b\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_raising_hand_tone2:"] = "\U0001f64b\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_raising_hand_medium_light_skin_tone:"] = "\U0001f64b\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_raising_hand::skin-tone-2:"] = "\U0001f64b\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_raising_hand_tone3:"] = "\U0001f64b\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_raising_hand_medium_skin_tone:"] = "\U0001f64b\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_raising_hand::skin-tone-3:"] = "\U0001f64b\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_raising_hand_tone4:"] = "\U0001f64b\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_raising_hand_medium_dark_skin_tone:"] = "\U0001f64b\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_raising_hand::skin-tone-4:"] = "\U0001f64b\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_raising_hand_tone5:"] = "\U0001f64b\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_raising_hand_dark_skin_tone:"] = "\U0001f64b\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_raising_hand::skin-tone-5:"] = "\U0001f64b\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_red_haired:"] = "\U0001f469\u200d\U0001f9b0",
+ [":woman_red_haired_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b0",
+ [":woman_red_haired_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b0",
+ [":woman_red_haired::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b0",
+ [":woman_red_haired_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b0",
+ [":woman_red_haired_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b0",
+ [":woman_red_haired::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b0",
+ [":woman_red_haired_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b0",
+ [":woman_red_haired_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b0",
+ [":woman_red_haired::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b0",
+ [":woman_red_haired_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b0",
+ [":woman_red_haired_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b0",
+ [":woman_red_haired::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b0",
+ [":woman_red_haired_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b0",
+ [":woman_red_haired_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b0",
+ [":woman_red_haired::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b0",
+ [":woman_rowing_boat:"] = "\U0001f6a3\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_tone1:"] = "\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_light_skin_tone:"] = "\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_rowing_boat::skin-tone-1:"] = "\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_tone2:"] = "\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_medium_light_skin_tone:"] = "\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_rowing_boat::skin-tone-2:"] = "\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_tone3:"] = "\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_medium_skin_tone:"] = "\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_rowing_boat::skin-tone-3:"] = "\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_tone4:"] = "\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_medium_dark_skin_tone:"] = "\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_rowing_boat::skin-tone-4:"] = "\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_tone5:"] = "\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_rowing_boat_dark_skin_tone:"] = "\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_rowing_boat::skin-tone-5:"] = "\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_running:"] = "\U0001f3c3\u200d\u2640\ufe0f",
+ [":woman_running_tone1:"] = "\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_running_light_skin_tone:"] = "\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_running::skin-tone-1:"] = "\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_running_tone2:"] = "\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_running_medium_light_skin_tone:"] = "\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_running::skin-tone-2:"] = "\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_running_tone3:"] = "\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_running_medium_skin_tone:"] = "\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_running::skin-tone-3:"] = "\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_running_tone4:"] = "\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_running_medium_dark_skin_tone:"] = "\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_running::skin-tone-4:"] = "\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_running_tone5:"] = "\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_running_dark_skin_tone:"] = "\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_running::skin-tone-5:"] = "\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_scientist:"] = "\U0001f469\u200d\U0001f52c",
+ [":woman_scientist_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f52c",
+ [":woman_scientist_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f52c",
+ [":woman_scientist::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f52c",
+ [":woman_scientist_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f52c",
+ [":woman_scientist_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f52c",
+ [":woman_scientist::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f52c",
+ [":woman_scientist_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f52c",
+ [":woman_scientist_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f52c",
+ [":woman_scientist::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f52c",
+ [":woman_scientist_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f52c",
+ [":woman_scientist_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f52c",
+ [":woman_scientist::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f52c",
+ [":woman_scientist_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f52c",
+ [":woman_scientist_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f52c",
+ [":woman_scientist::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f52c",
+ [":woman_shrugging:"] = "\U0001f937\u200d\u2640\ufe0f",
+ [":woman_shrugging_tone1:"] = "\U0001f937\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_shrugging_light_skin_tone:"] = "\U0001f937\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_shrugging::skin-tone-1:"] = "\U0001f937\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_shrugging_tone2:"] = "\U0001f937\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_shrugging_medium_light_skin_tone:"] = "\U0001f937\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_shrugging::skin-tone-2:"] = "\U0001f937\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_shrugging_tone3:"] = "\U0001f937\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_shrugging_medium_skin_tone:"] = "\U0001f937\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_shrugging::skin-tone-3:"] = "\U0001f937\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_shrugging_tone4:"] = "\U0001f937\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_shrugging_medium_dark_skin_tone:"] = "\U0001f937\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_shrugging::skin-tone-4:"] = "\U0001f937\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_shrugging_tone5:"] = "\U0001f937\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_shrugging_dark_skin_tone:"] = "\U0001f937\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_shrugging::skin-tone-5:"] = "\U0001f937\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_singer:"] = "\U0001f469\u200d\U0001f3a4",
+ [":woman_singer_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a4",
+ [":woman_singer_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a4",
+ [":woman_singer::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3a4",
+ [":woman_singer_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a4",
+ [":woman_singer_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a4",
+ [":woman_singer::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3a4",
+ [":woman_singer_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a4",
+ [":woman_singer_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a4",
+ [":woman_singer::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3a4",
+ [":woman_singer_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a4",
+ [":woman_singer_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a4",
+ [":woman_singer::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3a4",
+ [":woman_singer_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a4",
+ [":woman_singer_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a4",
+ [":woman_singer::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3a4",
+ [":woman_standing:"] = "\U0001f9cd\u200d\u2640\ufe0f",
+ [":woman_standing_tone1:"] = "\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_standing_light_skin_tone:"] = "\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_standing::skin-tone-1:"] = "\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_standing_tone2:"] = "\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_standing_medium_light_skin_tone:"] = "\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_standing::skin-tone-2:"] = "\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_standing_tone3:"] = "\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_standing_medium_skin_tone:"] = "\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_standing::skin-tone-3:"] = "\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_standing_tone4:"] = "\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_standing_medium_dark_skin_tone:"] = "\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_standing::skin-tone-4:"] = "\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_standing_tone5:"] = "\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_standing_dark_skin_tone:"] = "\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_standing::skin-tone-5:"] = "\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_student:"] = "\U0001f469\u200d\U0001f393",
+ [":woman_student_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f393",
+ [":woman_student_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f393",
+ [":woman_student::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f393",
+ [":woman_student_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f393",
+ [":woman_student_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f393",
+ [":woman_student::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f393",
+ [":woman_student_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f393",
+ [":woman_student_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f393",
+ [":woman_student::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f393",
+ [":woman_student_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f393",
+ [":woman_student_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f393",
+ [":woman_student::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f393",
+ [":woman_student_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f393",
+ [":woman_student_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f393",
+ [":woman_student::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f393",
+ [":woman_superhero:"] = "\U0001f9b8\u200d\u2640\ufe0f",
+ [":woman_superhero_tone1:"] = "\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_superhero_light_skin_tone:"] = "\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_superhero::skin-tone-1:"] = "\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_superhero_tone2:"] = "\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_superhero_medium_light_skin_tone:"] = "\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_superhero::skin-tone-2:"] = "\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_superhero_tone3:"] = "\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_superhero_medium_skin_tone:"] = "\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_superhero::skin-tone-3:"] = "\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_superhero_tone4:"] = "\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_superhero_medium_dark_skin_tone:"] = "\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_superhero::skin-tone-4:"] = "\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_superhero_tone5:"] = "\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_superhero_dark_skin_tone:"] = "\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_superhero::skin-tone-5:"] = "\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_supervillain:"] = "\U0001f9b9\u200d\u2640\ufe0f",
+ [":woman_supervillain_tone1:"] = "\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_supervillain_light_skin_tone:"] = "\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_supervillain::skin-tone-1:"] = "\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_supervillain_tone2:"] = "\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_supervillain_medium_light_skin_tone:"] = "\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_supervillain::skin-tone-2:"] = "\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_supervillain_tone3:"] = "\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_supervillain_medium_skin_tone:"] = "\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_supervillain::skin-tone-3:"] = "\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_supervillain_tone4:"] = "\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_supervillain_medium_dark_skin_tone:"] = "\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_supervillain::skin-tone-4:"] = "\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_supervillain_tone5:"] = "\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_supervillain_dark_skin_tone:"] = "\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_supervillain::skin-tone-5:"] = "\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_surfing:"] = "\U0001f3c4\u200d\u2640\ufe0f",
+ [":woman_surfing_tone1:"] = "\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_surfing_light_skin_tone:"] = "\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_surfing::skin-tone-1:"] = "\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_surfing_tone2:"] = "\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_surfing_medium_light_skin_tone:"] = "\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_surfing::skin-tone-2:"] = "\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_surfing_tone3:"] = "\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_surfing_medium_skin_tone:"] = "\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_surfing::skin-tone-3:"] = "\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_surfing_tone4:"] = "\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_surfing_medium_dark_skin_tone:"] = "\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_surfing::skin-tone-4:"] = "\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_surfing_tone5:"] = "\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_surfing_dark_skin_tone:"] = "\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_surfing::skin-tone-5:"] = "\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_swimming:"] = "\U0001f3ca\u200d\u2640\ufe0f",
+ [":woman_swimming_tone1:"] = "\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_swimming_light_skin_tone:"] = "\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_swimming::skin-tone-1:"] = "\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_swimming_tone2:"] = "\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_swimming_medium_light_skin_tone:"] = "\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_swimming::skin-tone-2:"] = "\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_swimming_tone3:"] = "\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_swimming_medium_skin_tone:"] = "\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_swimming::skin-tone-3:"] = "\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_swimming_tone4:"] = "\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_swimming_medium_dark_skin_tone:"] = "\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_swimming::skin-tone-4:"] = "\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_swimming_tone5:"] = "\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_swimming_dark_skin_tone:"] = "\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_swimming::skin-tone-5:"] = "\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_teacher:"] = "\U0001f469\u200d\U0001f3eb",
+ [":woman_teacher_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3eb",
+ [":woman_teacher_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f3eb",
+ [":woman_teacher::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f3eb",
+ [":woman_teacher_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3eb",
+ [":woman_teacher_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f3eb",
+ [":woman_teacher::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f3eb",
+ [":woman_teacher_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3eb",
+ [":woman_teacher_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f3eb",
+ [":woman_teacher::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f3eb",
+ [":woman_teacher_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3eb",
+ [":woman_teacher_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f3eb",
+ [":woman_teacher::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f3eb",
+ [":woman_teacher_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3eb",
+ [":woman_teacher_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f3eb",
+ [":woman_teacher::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f3eb",
+ [":woman_technologist:"] = "\U0001f469\u200d\U0001f4bb",
+ [":woman_technologist_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bb",
+ [":woman_technologist_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bb",
+ [":woman_technologist::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f4bb",
+ [":woman_technologist_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bb",
+ [":woman_technologist_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bb",
+ [":woman_technologist::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f4bb",
+ [":woman_technologist_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bb",
+ [":woman_technologist_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bb",
+ [":woman_technologist::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f4bb",
+ [":woman_technologist_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bb",
+ [":woman_technologist_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bb",
+ [":woman_technologist::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f4bb",
+ [":woman_technologist_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bb",
+ [":woman_technologist_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bb",
+ [":woman_technologist::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f4bb",
+ [":woman_tipping_hand:"] = "\U0001f481\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_tone1:"] = "\U0001f481\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_light_skin_tone:"] = "\U0001f481\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_tipping_hand::skin-tone-1:"] = "\U0001f481\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_tone2:"] = "\U0001f481\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_medium_light_skin_tone:"] = "\U0001f481\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_tipping_hand::skin-tone-2:"] = "\U0001f481\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_tone3:"] = "\U0001f481\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_medium_skin_tone:"] = "\U0001f481\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_tipping_hand::skin-tone-3:"] = "\U0001f481\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_tone4:"] = "\U0001f481\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_medium_dark_skin_tone:"] = "\U0001f481\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_tipping_hand::skin-tone-4:"] = "\U0001f481\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_tone5:"] = "\U0001f481\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_tipping_hand_dark_skin_tone:"] = "\U0001f481\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_tipping_hand::skin-tone-5:"] = "\U0001f481\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_tone1:"] = "\U0001f469\U0001f3fb",
+ [":woman::skin-tone-1:"] = "\U0001f469\U0001f3fb",
+ [":woman_tone2:"] = "\U0001f469\U0001f3fc",
+ [":woman::skin-tone-2:"] = "\U0001f469\U0001f3fc",
+ [":woman_tone3:"] = "\U0001f469\U0001f3fd",
+ [":woman::skin-tone-3:"] = "\U0001f469\U0001f3fd",
+ [":woman_tone4:"] = "\U0001f469\U0001f3fe",
+ [":woman::skin-tone-4:"] = "\U0001f469\U0001f3fe",
+ [":woman_tone5:"] = "\U0001f469\U0001f3ff",
+ [":woman::skin-tone-5:"] = "\U0001f469\U0001f3ff",
+ [":woman_vampire:"] = "\U0001f9db\u200d\u2640\ufe0f",
+ [":woman_vampire_tone1:"] = "\U0001f9db\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_vampire_light_skin_tone:"] = "\U0001f9db\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_vampire::skin-tone-1:"] = "\U0001f9db\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_vampire_tone2:"] = "\U0001f9db\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_vampire_medium_light_skin_tone:"] = "\U0001f9db\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_vampire::skin-tone-2:"] = "\U0001f9db\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_vampire_tone3:"] = "\U0001f9db\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_vampire_medium_skin_tone:"] = "\U0001f9db\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_vampire::skin-tone-3:"] = "\U0001f9db\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_vampire_tone4:"] = "\U0001f9db\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_vampire_medium_dark_skin_tone:"] = "\U0001f9db\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_vampire::skin-tone-4:"] = "\U0001f9db\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_vampire_tone5:"] = "\U0001f9db\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_vampire_dark_skin_tone:"] = "\U0001f9db\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_vampire::skin-tone-5:"] = "\U0001f9db\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_walking:"] = "\U0001f6b6\u200d\u2640\ufe0f",
+ [":woman_walking_tone1:"] = "\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_walking_light_skin_tone:"] = "\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_walking::skin-tone-1:"] = "\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_walking_tone2:"] = "\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_walking_medium_light_skin_tone:"] = "\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_walking::skin-tone-2:"] = "\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_walking_tone3:"] = "\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_walking_medium_skin_tone:"] = "\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_walking::skin-tone-3:"] = "\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_walking_tone4:"] = "\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_walking_medium_dark_skin_tone:"] = "\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_walking::skin-tone-4:"] = "\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_walking_tone5:"] = "\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_walking_dark_skin_tone:"] = "\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_walking::skin-tone-5:"] = "\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_wearing_turban:"] = "\U0001f473\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_tone1:"] = "\U0001f473\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_light_skin_tone:"] = "\U0001f473\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_wearing_turban::skin-tone-1:"] = "\U0001f473\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_tone2:"] = "\U0001f473\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_medium_light_skin_tone:"] = "\U0001f473\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_wearing_turban::skin-tone-2:"] = "\U0001f473\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_tone3:"] = "\U0001f473\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_medium_skin_tone:"] = "\U0001f473\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_wearing_turban::skin-tone-3:"] = "\U0001f473\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_tone4:"] = "\U0001f473\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_medium_dark_skin_tone:"] = "\U0001f473\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_wearing_turban::skin-tone-4:"] = "\U0001f473\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_tone5:"] = "\U0001f473\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_wearing_turban_dark_skin_tone:"] = "\U0001f473\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_wearing_turban::skin-tone-5:"] = "\U0001f473\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_white_haired:"] = "\U0001f469\u200d\U0001f9b3",
+ [":woman_white_haired_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b3",
+ [":woman_white_haired_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b3",
+ [":woman_white_haired::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9b3",
+ [":woman_white_haired_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b3",
+ [":woman_white_haired_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b3",
+ [":woman_white_haired::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9b3",
+ [":woman_white_haired_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b3",
+ [":woman_white_haired_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b3",
+ [":woman_white_haired::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9b3",
+ [":woman_white_haired_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b3",
+ [":woman_white_haired_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b3",
+ [":woman_white_haired::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9b3",
+ [":woman_white_haired_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b3",
+ [":woman_white_haired_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b3",
+ [":woman_white_haired::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9b3",
+ [":woman_with_headscarf:"] = "\U0001f9d5",
+ [":woman_with_headscarf_tone1:"] = "\U0001f9d5\U0001f3fb",
+ [":woman_with_headscarf_light_skin_tone:"] = "\U0001f9d5\U0001f3fb",
+ [":woman_with_headscarf::skin-tone-1:"] = "\U0001f9d5\U0001f3fb",
+ [":woman_with_headscarf_tone2:"] = "\U0001f9d5\U0001f3fc",
+ [":woman_with_headscarf_medium_light_skin_tone:"] = "\U0001f9d5\U0001f3fc",
+ [":woman_with_headscarf::skin-tone-2:"] = "\U0001f9d5\U0001f3fc",
+ [":woman_with_headscarf_tone3:"] = "\U0001f9d5\U0001f3fd",
+ [":woman_with_headscarf_medium_skin_tone:"] = "\U0001f9d5\U0001f3fd",
+ [":woman_with_headscarf::skin-tone-3:"] = "\U0001f9d5\U0001f3fd",
+ [":woman_with_headscarf_tone4:"] = "\U0001f9d5\U0001f3fe",
+ [":woman_with_headscarf_medium_dark_skin_tone:"] = "\U0001f9d5\U0001f3fe",
+ [":woman_with_headscarf::skin-tone-4:"] = "\U0001f9d5\U0001f3fe",
+ [":woman_with_headscarf_tone5:"] = "\U0001f9d5\U0001f3ff",
+ [":woman_with_headscarf_dark_skin_tone:"] = "\U0001f9d5\U0001f3ff",
+ [":woman_with_headscarf::skin-tone-5:"] = "\U0001f9d5\U0001f3ff",
+ [":woman_with_probing_cane:"] = "\U0001f469\u200d\U0001f9af",
+ [":woman_with_probing_cane_tone1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9af",
+ [":woman_with_probing_cane_light_skin_tone:"] = "\U0001f469\U0001f3fb\u200d\U0001f9af",
+ [":woman_with_probing_cane::skin-tone-1:"] = "\U0001f469\U0001f3fb\u200d\U0001f9af",
+ [":woman_with_probing_cane_tone2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9af",
+ [":woman_with_probing_cane_medium_light_skin_tone:"] = "\U0001f469\U0001f3fc\u200d\U0001f9af",
+ [":woman_with_probing_cane::skin-tone-2:"] = "\U0001f469\U0001f3fc\u200d\U0001f9af",
+ [":woman_with_probing_cane_tone3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9af",
+ [":woman_with_probing_cane_medium_skin_tone:"] = "\U0001f469\U0001f3fd\u200d\U0001f9af",
+ [":woman_with_probing_cane::skin-tone-3:"] = "\U0001f469\U0001f3fd\u200d\U0001f9af",
+ [":woman_with_probing_cane_tone4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9af",
+ [":woman_with_probing_cane_medium_dark_skin_tone:"] = "\U0001f469\U0001f3fe\u200d\U0001f9af",
+ [":woman_with_probing_cane::skin-tone-4:"] = "\U0001f469\U0001f3fe\u200d\U0001f9af",
+ [":woman_with_probing_cane_tone5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9af",
+ [":woman_with_probing_cane_dark_skin_tone:"] = "\U0001f469\U0001f3ff\u200d\U0001f9af",
+ [":woman_with_probing_cane::skin-tone-5:"] = "\U0001f469\U0001f3ff\u200d\U0001f9af",
+ [":woman_with_veil:"] = "\U0001f470\u200d\u2640\ufe0f",
+ [":bride_with_veil:"] = "\U0001f470\u200d\u2640\ufe0f",
+ [":woman_with_veil_tone1:"] = "\U0001f470\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_with_veil_light_skin_tone:"] = "\U0001f470\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_with_veil::skin-tone-1:"] = "\U0001f470\U0001f3fb\u200d\u2640\ufe0f",
+ [":bride_with_veil::skin-tone-1:"] = "\U0001f470\U0001f3fb\u200d\u2640\ufe0f",
+ [":woman_with_veil_tone2:"] = "\U0001f470\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_with_veil_medium_light_skin_tone:"] = "\U0001f470\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_with_veil::skin-tone-2:"] = "\U0001f470\U0001f3fc\u200d\u2640\ufe0f",
+ [":bride_with_veil::skin-tone-2:"] = "\U0001f470\U0001f3fc\u200d\u2640\ufe0f",
+ [":woman_with_veil_tone3:"] = "\U0001f470\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_with_veil_medium_skin_tone:"] = "\U0001f470\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_with_veil::skin-tone-3:"] = "\U0001f470\U0001f3fd\u200d\u2640\ufe0f",
+ [":bride_with_veil::skin-tone-3:"] = "\U0001f470\U0001f3fd\u200d\u2640\ufe0f",
+ [":woman_with_veil_tone4:"] = "\U0001f470\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_with_veil_medium_dark_skin_tone:"] = "\U0001f470\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_with_veil::skin-tone-4:"] = "\U0001f470\U0001f3fe\u200d\u2640\ufe0f",
+ [":bride_with_veil::skin-tone-4:"] = "\U0001f470\U0001f3fe\u200d\u2640\ufe0f",
+ [":woman_with_veil_tone5:"] = "\U0001f470\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_with_veil_dark_skin_tone:"] = "\U0001f470\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_with_veil::skin-tone-5:"] = "\U0001f470\U0001f3ff\u200d\u2640\ufe0f",
+ [":bride_with_veil::skin-tone-5:"] = "\U0001f470\U0001f3ff\u200d\u2640\ufe0f",
+ [":woman_zombie:"] = "\U0001f9df\u200d\u2640\ufe0f",
+ [":womans_clothes:"] = "\U0001f45a",
+ [":womans_flat_shoe:"] = "\U0001f97f",
+ [":womans_hat:"] = "\U0001f452",
+ [":women_holding_hands_tone1:"] = "\U0001f46d",
+ [":women_holding_hands_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone1_tone2:"] = "\U0001f46d",
+ [":women_holding_hands_light_skin_tone_medium_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone1_tone3:"] = "\U0001f46d",
+ [":women_holding_hands_light_skin_tone_medium_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone1_tone4:"] = "\U0001f46d",
+ [":women_holding_hands_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone1_tone5:"] = "\U0001f46d",
+ [":women_holding_hands_light_skin_tone_dark_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone2:"] = "\U0001f46d",
+ [":women_holding_hands_medium_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone2_tone1:"] = "\U0001f46d",
+ [":women_holding_hands_medium_light_skin_tone_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone2_tone3:"] = "\U0001f46d",
+ [":women_holding_hands_medium_light_skin_tone_medium_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone2_tone4:"] = "\U0001f46d",
+ [":women_holding_hands_medium_light_skin_tone_medium_dark_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone2_tone5:"] = "\U0001f46d",
+ [":women_holding_hands_medium_light_skin_tone_dark_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone3:"] = "\U0001f46d",
+ [":women_holding_hands_medium_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone3_tone1:"] = "\U0001f46d",
+ [":women_holding_hands_medium_skin_tone_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone3_tone2:"] = "\U0001f46d",
+ [":women_holding_hands_medium_skin_tone_medium_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone3_tone4:"] = "\U0001f46d",
+ [":women_holding_hands_medium_skin_tone_medium_dark_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone3_tone5:"] = "\U0001f46d",
+ [":women_holding_hands_medium_skin_tone_dark_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone4:"] = "\U0001f46d",
+ [":women_holding_hands_medium_dark_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone4_tone1:"] = "\U0001f46d",
+ [":women_holding_hands_medium_dark_skin_tone_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone4_tone2:"] = "\U0001f46d",
+ [":women_holding_hands_medium_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone4_tone3:"] = "\U0001f46d",
+ [":women_holding_hands_medium_dark_skin_tone_medium_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone4_tone5:"] = "\U0001f46d",
+ [":women_holding_hands_medium_dark_skin_tone_dark_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone5:"] = "\U0001f46d",
+ [":women_holding_hands_dark_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone5_tone1:"] = "\U0001f46d",
+ [":women_holding_hands_dark_skin_tone_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone5_tone2:"] = "\U0001f46d",
+ [":women_holding_hands_dark_skin_tone_medium_light_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone5_tone3:"] = "\U0001f46d",
+ [":women_holding_hands_dark_skin_tone_medium_skin_tone:"] = "\U0001f46d",
+ [":women_holding_hands_tone5_tone4:"] = "\U0001f46d",
+ [":women_holding_hands_dark_skin_tone_medium_dark_skin_tone:"] = "\U0001f46d",
+ [":women_with_bunny_ears_partying:"] = "\U0001f46f\u200d\u2640\ufe0f",
+ [":women_wrestling:"] = "\U0001f93c\u200d\u2640\ufe0f",
+ [":womens:"] = "\U0001f6ba",
+ [":wood:"] = "\U0001fab5",
+ [":woozy_face:"] = "\U0001f974",
+ [":worm:"] = "\U0001fab1",
+ [":worried:"] = "\U0001f61f",
+ [":wrench:"] = "\U0001f527",
+ [":writing_hand:"] = "\u270d\ufe0f",
+ [":writing_hand_tone1:"] = "\u270d\U0001f3fb",
+ [":writing_hand::skin-tone-1:"] = "\u270d\U0001f3fb",
+ [":writing_hand_tone2:"] = "\u270d\U0001f3fc",
+ [":writing_hand::skin-tone-2:"] = "\u270d\U0001f3fc",
+ [":writing_hand_tone3:"] = "\u270d\U0001f3fd",
+ [":writing_hand::skin-tone-3:"] = "\u270d\U0001f3fd",
+ [":writing_hand_tone4:"] = "\u270d\U0001f3fe",
+ [":writing_hand::skin-tone-4:"] = "\u270d\U0001f3fe",
+ [":writing_hand_tone5:"] = "\u270d\U0001f3ff",
+ [":writing_hand::skin-tone-5:"] = "\u270d\U0001f3ff",
+ [":x:"] = "\u274c",
+ [":yarn:"] = "\U0001f9f6",
+ [":yawning_face:"] = "\U0001f971",
+ [":yellow_circle:"] = "\U0001f7e1",
+ [":yellow_heart:"] = "\U0001f49b",
+ [":yellow_square:"] = "\U0001f7e8",
+ [":yen:"] = "\U0001f4b4",
+ [":yin_yang:"] = "\u262f\ufe0f",
+ [":yo_yo:"] = "\U0001fa80",
+ [":yum:"] = "\U0001f60b",
+ [":zany_face:"] = "\U0001f92a",
+ [":zap:"] = "\u26a1",
+ [":zebra:"] = "\U0001f993",
+ [":zero:"] = "\u0030\ufe0f\u20e3",
+ [":zipper_mouth:"] = "\U0001f910",
+ [":zipper_mouth_face:"] = "\U0001f910",
+ [":zombie:"] = "\U0001f9df",
+ [":zzz:"] = "\U0001f4a4",
+ };
- s_discordNameLookup = new Dictionary<string, string>
- {
- ["\U0001f4af"] = ":100:",
- ["\U0001f522"] = ":1234:",
- ["\U0001f3b1"] = ":8ball:",
- ["\U0001f170\ufe0f"] = ":a:",
- ["\U0001f170"] = ":a:",
- ["\U0001f18e"] = ":ab:",
- ["\U0001f9ee"] = ":abacus:",
- ["\U0001f524"] = ":abc:",
- ["\U0001f521"] = ":abcd:",
- ["\U0001f251"] = ":accept:",
- ["\U0001fa97"] = ":accordion:",
- ["\U0001fa79"] = ":adhesive_bandage:",
- ["\U0001f9d1"] = ":adult:",
- ["\U0001f9d1\U0001f3fb"] = ":adult_tone1:",
- ["\U0001f9d1\U0001f3fc"] = ":adult_tone2:",
- ["\U0001f9d1\U0001f3fd"] = ":adult_tone3:",
- ["\U0001f9d1\U0001f3fe"] = ":adult_tone4:",
- ["\U0001f9d1\U0001f3ff"] = ":adult_tone5:",
- ["\U0001f6a1"] = ":aerial_tramway:",
- ["\u2708\ufe0f"] = ":airplane:",
- ["\u2708"] = ":airplane:",
- ["\U0001f6ec"] = ":airplane_arriving:",
- ["\U0001f6eb"] = ":airplane_departure:",
- ["\U0001f6e9\ufe0f"] = ":airplane_small:",
- ["\U0001f6e9"] = ":airplane_small:",
- ["\u23f0"] = ":alarm_clock:",
- ["\u2697\ufe0f"] = ":alembic:",
- ["\u2697"] = ":alembic:",
- ["\U0001f47d"] = ":alien:",
- ["\U0001f691"] = ":ambulance:",
- ["\U0001f3fa"] = ":amphora:",
- ["\U0001fac0"] = ":anatomical_heart:",
- ["\u2693"] = ":anchor:",
- ["\U0001f47c"] = ":angel:",
- ["\U0001f47c\U0001f3fb"] = ":angel_tone1:",
- ["\U0001f47c\U0001f3fc"] = ":angel_tone2:",
- ["\U0001f47c\U0001f3fd"] = ":angel_tone3:",
- ["\U0001f47c\U0001f3fe"] = ":angel_tone4:",
- ["\U0001f47c\U0001f3ff"] = ":angel_tone5:",
- ["\U0001f4a2"] = ":anger:",
- ["\U0001f5ef\ufe0f"] = ":anger_right:",
- ["\U0001f5ef"] = ":anger_right:",
- ["\U0001f620"] = ":angry:",
- ["\U0001f627"] = ":anguished:",
- ["\U0001f41c"] = ":ant:",
- ["\U0001f34e"] = ":apple:",
- ["\u2652"] = ":aquarius:",
- ["\u2648"] = ":aries:",
- ["\u25c0\ufe0f"] = ":arrow_backward:",
- ["\u25c0"] = ":arrow_backward:",
- ["\u23ec"] = ":arrow_double_down:",
- ["\u23eb"] = ":arrow_double_up:",
- ["\u2b07\ufe0f"] = ":arrow_down:",
- ["\u2b07"] = ":arrow_down:",
- ["\U0001f53d"] = ":arrow_down_small:",
- ["\u25b6\ufe0f"] = ":arrow_forward:",
- ["\u25b6"] = ":arrow_forward:",
- ["\u2935\ufe0f"] = ":arrow_heading_down:",
- ["\u2935"] = ":arrow_heading_down:",
- ["\u2934\ufe0f"] = ":arrow_heading_up:",
- ["\u2934"] = ":arrow_heading_up:",
- ["\u2b05\ufe0f"] = ":arrow_left:",
- ["\u2b05"] = ":arrow_left:",
- ["\u2199\ufe0f"] = ":arrow_lower_left:",
- ["\u2199"] = ":arrow_lower_left:",
- ["\u2198\ufe0f"] = ":arrow_lower_right:",
- ["\u2198"] = ":arrow_lower_right:",
- ["\u27a1\ufe0f"] = ":arrow_right:",
- ["\u27a1"] = ":arrow_right:",
- ["\u21aa\ufe0f"] = ":arrow_right_hook:",
- ["\u21aa"] = ":arrow_right_hook:",
- ["\u2b06\ufe0f"] = ":arrow_up:",
- ["\u2b06"] = ":arrow_up:",
- ["\u2195\ufe0f"] = ":arrow_up_down:",
- ["\u2195"] = ":arrow_up_down:",
- ["\U0001f53c"] = ":arrow_up_small:",
- ["\u2196\ufe0f"] = ":arrow_upper_left:",
- ["\u2196"] = ":arrow_upper_left:",
- ["\u2197\ufe0f"] = ":arrow_upper_right:",
- ["\u2197"] = ":arrow_upper_right:",
- ["\U0001f503"] = ":arrows_clockwise:",
- ["\U0001f504"] = ":arrows_counterclockwise:",
- ["\U0001f3a8"] = ":art:",
- ["\U0001f69b"] = ":articulated_lorry:",
- ["\U0001f9d1\u200d\U0001f3a8"] = ":artist:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f3a8"] = ":artist_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f3a8"] = ":artist_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f3a8"] = ":artist_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f3a8"] = ":artist_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f3a8"] = ":artist_tone5:",
- ["\u002a\ufe0f\u20e3"] = ":asterisk:",
- ["\u002a\u20e3"] = ":asterisk:",
- ["\U0001f632"] = ":astonished:",
- ["\U0001f9d1\u200d\U0001f680"] = ":astronaut:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f680"] = ":astronaut_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f680"] = ":astronaut_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f680"] = ":astronaut_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f680"] = ":astronaut_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f680"] = ":astronaut_tone5:",
- ["\U0001f45f"] = ":athletic_shoe:",
- ["\U0001f3e7"] = ":atm:",
- ["\u269b\ufe0f"] = ":atom:",
- ["\u269b"] = ":atom:",
- ["\U0001f6fa"] = ":auto_rickshaw:",
- ["\U0001f951"] = ":avocado:",
- ["\U0001fa93"] = ":axe:",
- ["\U0001f171\ufe0f"] = ":b:",
- ["\U0001f171"] = ":b:",
- ["\U0001f476"] = ":baby:",
- ["\U0001f37c"] = ":baby_bottle:",
- ["\U0001f424"] = ":baby_chick:",
- ["\U0001f6bc"] = ":baby_symbol:",
- ["\U0001f476\U0001f3fb"] = ":baby_tone1:",
- ["\U0001f476\U0001f3fc"] = ":baby_tone2:",
- ["\U0001f476\U0001f3fd"] = ":baby_tone3:",
- ["\U0001f476\U0001f3fe"] = ":baby_tone4:",
- ["\U0001f476\U0001f3ff"] = ":baby_tone5:",
- ["\U0001f519"] = ":back:",
- ["\U0001f953"] = ":bacon:",
- ["\U0001f9a1"] = ":badger:",
- ["\U0001f3f8"] = ":badminton:",
- ["\U0001f96f"] = ":bagel:",
- ["\U0001f6c4"] = ":baggage_claim:",
- ["\U0001fa70"] = ":ballet_shoes:",
- ["\U0001f388"] = ":balloon:",
- ["\U0001f5f3\ufe0f"] = ":ballot_box:",
- ["\U0001f5f3"] = ":ballot_box:",
- ["\u2611\ufe0f"] = ":ballot_box_with_check:",
- ["\u2611"] = ":ballot_box_with_check:",
- ["\U0001f38d"] = ":bamboo:",
- ["\U0001f34c"] = ":banana:",
- ["\u203c\ufe0f"] = ":bangbang:",
- ["\u203c"] = ":bangbang:",
- ["\U0001fa95"] = ":banjo:",
- ["\U0001f3e6"] = ":bank:",
- ["\U0001f4ca"] = ":bar_chart:",
- ["\U0001f488"] = ":barber:",
- ["\u26be"] = ":baseball:",
- ["\U0001f9fa"] = ":basket:",
- ["\U0001f3c0"] = ":basketball:",
- ["\U0001f987"] = ":bat:",
- ["\U0001f6c0"] = ":bath:",
- ["\U0001f6c0\U0001f3fb"] = ":bath_tone1:",
- ["\U0001f6c0\U0001f3fc"] = ":bath_tone2:",
- ["\U0001f6c0\U0001f3fd"] = ":bath_tone3:",
- ["\U0001f6c0\U0001f3fe"] = ":bath_tone4:",
- ["\U0001f6c0\U0001f3ff"] = ":bath_tone5:",
- ["\U0001f6c1"] = ":bathtub:",
- ["\U0001f50b"] = ":battery:",
- ["\U0001f3d6\ufe0f"] = ":beach:",
- ["\U0001f3d6"] = ":beach:",
- ["\u26f1\ufe0f"] = ":beach_umbrella:",
- ["\u26f1"] = ":beach_umbrella:",
- ["\U0001f43b"] = ":bear:",
- ["\U0001f9d4"] = ":bearded_person:",
- ["\U0001f9d4\U0001f3fb"] = ":bearded_person_tone1:",
- ["\U0001f9d4\U0001f3fc"] = ":bearded_person_tone2:",
- ["\U0001f9d4\U0001f3fd"] = ":bearded_person_tone3:",
- ["\U0001f9d4\U0001f3fe"] = ":bearded_person_tone4:",
- ["\U0001f9d4\U0001f3ff"] = ":bearded_person_tone5:",
- ["\U0001f9ab"] = ":beaver:",
- ["\U0001f6cf\ufe0f"] = ":bed:",
- ["\U0001f6cf"] = ":bed:",
- ["\U0001f41d"] = ":bee:",
- ["\U0001f37a"] = ":beer:",
- ["\U0001f37b"] = ":beers:",
- ["\U0001fab2"] = ":beetle:",
- ["\U0001f530"] = ":beginner:",
- ["\U0001f514"] = ":bell:",
- ["\U0001fad1"] = ":bell_pepper:",
- ["\U0001f6ce\ufe0f"] = ":bellhop:",
- ["\U0001f6ce"] = ":bellhop:",
- ["\U0001f371"] = ":bento:",
- ["\U0001f9c3"] = ":beverage_box:",
- ["\U0001f6b2"] = ":bike:",
- ["\U0001f459"] = ":bikini:",
- ["\U0001f9e2"] = ":billed_cap:",
- ["\u2623\ufe0f"] = ":biohazard:",
- ["\u2623"] = ":biohazard:",
- ["\U0001f426"] = ":bird:",
- ["\U0001f382"] = ":birthday:",
- ["\U0001f9ac"] = ":bison:",
- ["\U0001f408\u200d\u2b1b"] = ":black_cat:",
- ["\u26ab"] = ":black_circle:",
- ["\U0001f5a4"] = ":black_heart:",
- ["\U0001f0cf"] = ":black_joker:",
- ["\u2b1b"] = ":black_large_square:",
- ["\u25fe"] = ":black_medium_small_square:",
- ["\u25fc\ufe0f"] = ":black_medium_square:",
- ["\u25fc"] = ":black_medium_square:",
- ["\u2712\ufe0f"] = ":black_nib:",
- ["\u2712"] = ":black_nib:",
- ["\u25aa\ufe0f"] = ":black_small_square:",
- ["\u25aa"] = ":black_small_square:",
- ["\U0001f532"] = ":black_square_button:",
- ["\U0001f471\u200d\u2642\ufe0f"] = ":blond_haired_man:",
- ["\U0001f471\U0001f3fb\u200d\u2642\ufe0f"] = ":blond_haired_man_tone1:",
- ["\U0001f471\U0001f3fc\u200d\u2642\ufe0f"] = ":blond_haired_man_tone2:",
- ["\U0001f471\U0001f3fd\u200d\u2642\ufe0f"] = ":blond_haired_man_tone3:",
- ["\U0001f471\U0001f3fe\u200d\u2642\ufe0f"] = ":blond_haired_man_tone4:",
- ["\U0001f471\U0001f3ff\u200d\u2642\ufe0f"] = ":blond_haired_man_tone5:",
- ["\U0001f471"] = ":blond_haired_person:",
- ["\U0001f471\U0001f3fb"] = ":blond_haired_person_tone1:",
- ["\U0001f471\U0001f3fc"] = ":blond_haired_person_tone2:",
- ["\U0001f471\U0001f3fd"] = ":blond_haired_person_tone3:",
- ["\U0001f471\U0001f3fe"] = ":blond_haired_person_tone4:",
- ["\U0001f471\U0001f3ff"] = ":blond_haired_person_tone5:",
- ["\U0001f471\u200d\u2640\ufe0f"] = ":blond_haired_woman:",
- ["\U0001f471\U0001f3fb\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone1:",
- ["\U0001f471\U0001f3fc\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone2:",
- ["\U0001f471\U0001f3fd\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone3:",
- ["\U0001f471\U0001f3fe\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone4:",
- ["\U0001f471\U0001f3ff\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone5:",
- ["\U0001f33c"] = ":blossom:",
- ["\U0001f421"] = ":blowfish:",
- ["\U0001f4d8"] = ":blue_book:",
- ["\U0001f699"] = ":blue_car:",
- ["\U0001f535"] = ":blue_circle:",
- ["\U0001f499"] = ":blue_heart:",
- ["\U0001f7e6"] = ":blue_square:",
- ["\U0001fad0"] = ":blueberries:",
- ["\U0001f60a"] = ":blush:",
- ["\U0001f417"] = ":boar:",
- ["\U0001f4a3"] = ":bomb:",
- ["\U0001f9b4"] = ":bone:",
- ["\U0001f4d6"] = ":book:",
- ["\U0001f516"] = ":bookmark:",
- ["\U0001f4d1"] = ":bookmark_tabs:",
- ["\U0001f4da"] = ":books:",
- ["\U0001f4a5"] = ":boom:",
- ["\U0001fa83"] = ":boomerang:",
- ["\U0001f462"] = ":boot:",
- ["\U0001f490"] = ":bouquet:",
- ["\U0001f3f9"] = ":bow_and_arrow:",
- ["\U0001f963"] = ":bowl_with_spoon:",
- ["\U0001f3b3"] = ":bowling:",
- ["\U0001f94a"] = ":boxing_glove:",
- ["\U0001f466"] = ":boy:",
- ["\U0001f466\U0001f3fb"] = ":boy_tone1:",
- ["\U0001f466\U0001f3fc"] = ":boy_tone2:",
- ["\U0001f466\U0001f3fd"] = ":boy_tone3:",
- ["\U0001f466\U0001f3fe"] = ":boy_tone4:",
- ["\U0001f466\U0001f3ff"] = ":boy_tone5:",
- ["\U0001f9e0"] = ":brain:",
- ["\U0001f35e"] = ":bread:",
- ["\U0001f931"] = ":breast_feeding:",
- ["\U0001f931\U0001f3fb"] = ":breast_feeding_tone1:",
- ["\U0001f931\U0001f3fc"] = ":breast_feeding_tone2:",
- ["\U0001f931\U0001f3fd"] = ":breast_feeding_tone3:",
- ["\U0001f931\U0001f3fe"] = ":breast_feeding_tone4:",
- ["\U0001f931\U0001f3ff"] = ":breast_feeding_tone5:",
- ["\U0001f9f1"] = ":bricks:",
- ["\U0001f309"] = ":bridge_at_night:",
- ["\U0001f4bc"] = ":briefcase:",
- ["\U0001fa72"] = ":briefs:",
- ["\U0001f966"] = ":broccoli:",
- ["\U0001f494"] = ":broken_heart:",
- ["\U0001f9f9"] = ":broom:",
- ["\U0001f7e4"] = ":brown_circle:",
- ["\U0001f90e"] = ":brown_heart:",
- ["\U0001f7eb"] = ":brown_square:",
- ["\U0001f9cb"] = ":bubble_tea:",
- ["\U0001faa3"] = ":bucket:",
- ["\U0001f41b"] = ":bug:",
- ["\U0001f4a1"] = ":bulb:",
- ["\U0001f685"] = ":bullettrain_front:",
- ["\U0001f684"] = ":bullettrain_side:",
- ["\U0001f32f"] = ":burrito:",
- ["\U0001f68c"] = ":bus:",
- ["\U0001f68f"] = ":busstop:",
- ["\U0001f464"] = ":bust_in_silhouette:",
- ["\U0001f465"] = ":busts_in_silhouette:",
- ["\U0001f9c8"] = ":butter:",
- ["\U0001f98b"] = ":butterfly:",
- ["\U0001f335"] = ":cactus:",
- ["\U0001f370"] = ":cake:",
- ["\U0001f4c6"] = ":calendar:",
- ["\U0001f5d3\ufe0f"] = ":calendar_spiral:",
- ["\U0001f5d3"] = ":calendar_spiral:",
- ["\U0001f919"] = ":call_me:",
- ["\U0001f919\U0001f3fb"] = ":call_me_tone1:",
- ["\U0001f919\U0001f3fc"] = ":call_me_tone2:",
- ["\U0001f919\U0001f3fd"] = ":call_me_tone3:",
- ["\U0001f919\U0001f3fe"] = ":call_me_tone4:",
- ["\U0001f919\U0001f3ff"] = ":call_me_tone5:",
- ["\U0001f4f2"] = ":calling:",
- ["\U0001f42b"] = ":camel:",
- ["\U0001f4f7"] = ":camera:",
- ["\U0001f4f8"] = ":camera_with_flash:",
- ["\U0001f3d5\ufe0f"] = ":camping:",
- ["\U0001f3d5"] = ":camping:",
- ["\u264b"] = ":cancer:",
- ["\U0001f56f\ufe0f"] = ":candle:",
- ["\U0001f56f"] = ":candle:",
- ["\U0001f36c"] = ":candy:",
- ["\U0001f96b"] = ":canned_food:",
- ["\U0001f6f6"] = ":canoe:",
- ["\U0001f520"] = ":capital_abcd:",
- ["\u2651"] = ":capricorn:",
- ["\U0001f5c3\ufe0f"] = ":card_box:",
- ["\U0001f5c3"] = ":card_box:",
- ["\U0001f4c7"] = ":card_index:",
- ["\U0001f3a0"] = ":carousel_horse:",
- ["\U0001fa9a"] = ":carpentry_saw:",
- ["\U0001f955"] = ":carrot:",
- ["\U0001f431"] = ":cat:",
- ["\U0001f408"] = ":cat2:",
- ["\U0001f4bf"] = ":cd:",
- ["\u26d3\ufe0f"] = ":chains:",
- ["\u26d3"] = ":chains:",
- ["\U0001fa91"] = ":chair:",
- ["\U0001f37e"] = ":champagne:",
- ["\U0001f942"] = ":champagne_glass:",
- ["\U0001f4b9"] = ":chart:",
- ["\U0001f4c9"] = ":chart_with_downwards_trend:",
- ["\U0001f4c8"] = ":chart_with_upwards_trend:",
- ["\U0001f3c1"] = ":checkered_flag:",
- ["\U0001f9c0"] = ":cheese:",
- ["\U0001f352"] = ":cherries:",
- ["\U0001f338"] = ":cherry_blossom:",
- ["\u265f\ufe0f"] = ":chess_pawn:",
- ["\u265f"] = ":chess_pawn:",
- ["\U0001f330"] = ":chestnut:",
- ["\U0001f414"] = ":chicken:",
- ["\U0001f9d2"] = ":child:",
- ["\U0001f9d2\U0001f3fb"] = ":child_tone1:",
- ["\U0001f9d2\U0001f3fc"] = ":child_tone2:",
- ["\U0001f9d2\U0001f3fd"] = ":child_tone3:",
- ["\U0001f9d2\U0001f3fe"] = ":child_tone4:",
- ["\U0001f9d2\U0001f3ff"] = ":child_tone5:",
- ["\U0001f6b8"] = ":children_crossing:",
- ["\U0001f43f\ufe0f"] = ":chipmunk:",
- ["\U0001f43f"] = ":chipmunk:",
- ["\U0001f36b"] = ":chocolate_bar:",
- ["\U0001f962"] = ":chopsticks:",
- ["\U0001f384"] = ":christmas_tree:",
- ["\u26ea"] = ":church:",
- ["\U0001f3a6"] = ":cinema:",
- ["\U0001f3aa"] = ":circus_tent:",
- ["\U0001f306"] = ":city_dusk:",
- ["\U0001f307"] = ":city_sunset:",
- ["\U0001f3d9\ufe0f"] = ":cityscape:",
- ["\U0001f3d9"] = ":cityscape:",
- ["\U0001f191"] = ":cl:",
- ["\U0001f44f"] = ":clap:",
- ["\U0001f44f\U0001f3fb"] = ":clap_tone1:",
- ["\U0001f44f\U0001f3fc"] = ":clap_tone2:",
- ["\U0001f44f\U0001f3fd"] = ":clap_tone3:",
- ["\U0001f44f\U0001f3fe"] = ":clap_tone4:",
- ["\U0001f44f\U0001f3ff"] = ":clap_tone5:",
- ["\U0001f3ac"] = ":clapper:",
- ["\U0001f3db\ufe0f"] = ":classical_building:",
- ["\U0001f3db"] = ":classical_building:",
- ["\U0001f4cb"] = ":clipboard:",
- ["\U0001f570\ufe0f"] = ":clock:",
- ["\U0001f570"] = ":clock:",
- ["\U0001f550"] = ":clock1:",
- ["\U0001f559"] = ":clock10:",
- ["\U0001f565"] = ":clock1030:",
- ["\U0001f55a"] = ":clock11:",
- ["\U0001f566"] = ":clock1130:",
- ["\U0001f55b"] = ":clock12:",
- ["\U0001f567"] = ":clock1230:",
- ["\U0001f55c"] = ":clock130:",
- ["\U0001f551"] = ":clock2:",
- ["\U0001f55d"] = ":clock230:",
- ["\U0001f552"] = ":clock3:",
- ["\U0001f55e"] = ":clock330:",
- ["\U0001f553"] = ":clock4:",
- ["\U0001f55f"] = ":clock430:",
- ["\U0001f554"] = ":clock5:",
- ["\U0001f560"] = ":clock530:",
- ["\U0001f555"] = ":clock6:",
- ["\U0001f561"] = ":clock630:",
- ["\U0001f556"] = ":clock7:",
- ["\U0001f562"] = ":clock730:",
- ["\U0001f557"] = ":clock8:",
- ["\U0001f563"] = ":clock830:",
- ["\U0001f558"] = ":clock9:",
- ["\U0001f564"] = ":clock930:",
- ["\U0001f4d5"] = ":closed_book:",
- ["\U0001f510"] = ":closed_lock_with_key:",
- ["\U0001f302"] = ":closed_umbrella:",
- ["\u2601\ufe0f"] = ":cloud:",
- ["\u2601"] = ":cloud:",
- ["\U0001f329\ufe0f"] = ":cloud_lightning:",
- ["\U0001f329"] = ":cloud_lightning:",
- ["\U0001f327\ufe0f"] = ":cloud_rain:",
- ["\U0001f327"] = ":cloud_rain:",
- ["\U0001f328\ufe0f"] = ":cloud_snow:",
- ["\U0001f328"] = ":cloud_snow:",
- ["\U0001f32a\ufe0f"] = ":cloud_tornado:",
- ["\U0001f32a"] = ":cloud_tornado:",
- ["\U0001f921"] = ":clown:",
- ["\u2663\ufe0f"] = ":clubs:",
- ["\u2663"] = ":clubs:",
- ["\U0001f9e5"] = ":coat:",
- ["\U0001fab3"] = ":cockroach:",
- ["\U0001f378"] = ":cocktail:",
- ["\U0001f965"] = ":coconut:",
- ["\u2615"] = ":coffee:",
- ["\u26b0\ufe0f"] = ":coffin:",
- ["\u26b0"] = ":coffin:",
- ["\U0001fa99"] = ":coin:",
- ["\U0001f976"] = ":cold_face:",
- ["\U0001f630"] = ":cold_sweat:",
- ["\u2604\ufe0f"] = ":comet:",
- ["\u2604"] = ":comet:",
- ["\U0001f9ed"] = ":compass:",
- ["\U0001f5dc\ufe0f"] = ":compression:",
- ["\U0001f5dc"] = ":compression:",
- ["\U0001f4bb"] = ":computer:",
- ["\U0001f38a"] = ":confetti_ball:",
- ["\U0001f616"] = ":confounded:",
- ["\U0001f615"] = ":confused:",
- ["\u3297\ufe0f"] = ":congratulations:",
- ["\u3297"] = ":congratulations:",
- ["\U0001f6a7"] = ":construction:",
- ["\U0001f3d7\ufe0f"] = ":construction_site:",
- ["\U0001f3d7"] = ":construction_site:",
- ["\U0001f477"] = ":construction_worker:",
- ["\U0001f477\U0001f3fb"] = ":construction_worker_tone1:",
- ["\U0001f477\U0001f3fc"] = ":construction_worker_tone2:",
- ["\U0001f477\U0001f3fd"] = ":construction_worker_tone3:",
- ["\U0001f477\U0001f3fe"] = ":construction_worker_tone4:",
- ["\U0001f477\U0001f3ff"] = ":construction_worker_tone5:",
- ["\U0001f39b\ufe0f"] = ":control_knobs:",
- ["\U0001f39b"] = ":control_knobs:",
- ["\U0001f3ea"] = ":convenience_store:",
- ["\U0001f9d1\u200d\U0001f373"] = ":cook:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f373"] = ":cook_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f373"] = ":cook_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f373"] = ":cook_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f373"] = ":cook_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f373"] = ":cook_tone5:",
- ["\U0001f36a"] = ":cookie:",
- ["\U0001f373"] = ":cooking:",
- ["\U0001f192"] = ":cool:",
- ["\u00a9\ufe0f"] = ":copyright:",
- ["\u00a9"] = ":copyright:",
- ["\U0001f33d"] = ":corn:",
- ["\U0001f6cb\ufe0f"] = ":couch:",
- ["\U0001f6cb"] = ":couch:",
- ["\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468"] = ":couple_mm:",
- ["\U0001f491"] = ":couple_with_heart:",
- ["\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468"] = ":couple_with_heart_woman_man:",
- ["\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469"] = ":couple_ww:",
- ["\U0001f48f"] = ":couplekiss:",
- ["\U0001f42e"] = ":cow:",
- ["\U0001f404"] = ":cow2:",
- ["\U0001f920"] = ":cowboy:",
- ["\U0001f980"] = ":crab:",
- ["\U0001f58d\ufe0f"] = ":crayon:",
- ["\U0001f58d"] = ":crayon:",
- ["\U0001f4b3"] = ":credit_card:",
- ["\U0001f319"] = ":crescent_moon:",
- ["\U0001f997"] = ":cricket:",
- ["\U0001f3cf"] = ":cricket_game:",
- ["\U0001f40a"] = ":crocodile:",
- ["\U0001f950"] = ":croissant:",
- ["\u271d\ufe0f"] = ":cross:",
- ["\u271d"] = ":cross:",
- ["\U0001f38c"] = ":crossed_flags:",
- ["\u2694\ufe0f"] = ":crossed_swords:",
- ["\u2694"] = ":crossed_swords:",
- ["\U0001f451"] = ":crown:",
- ["\U0001f6f3\ufe0f"] = ":cruise_ship:",
- ["\U0001f6f3"] = ":cruise_ship:",
- ["\U0001f622"] = ":cry:",
- ["\U0001f63f"] = ":crying_cat_face:",
- ["\U0001f52e"] = ":crystal_ball:",
- ["\U0001f952"] = ":cucumber:",
- ["\U0001f964"] = ":cup_with_straw:",
- ["\U0001f9c1"] = ":cupcake:",
- ["\U0001f498"] = ":cupid:",
- ["\U0001f94c"] = ":curling_stone:",
- ["\u27b0"] = ":curly_loop:",
- ["\U0001f4b1"] = ":currency_exchange:",
- ["\U0001f35b"] = ":curry:",
- ["\U0001f36e"] = ":custard:",
- ["\U0001f6c3"] = ":customs:",
- ["\U0001f969"] = ":cut_of_meat:",
- ["\U0001f300"] = ":cyclone:",
- ["\U0001f5e1\ufe0f"] = ":dagger:",
- ["\U0001f5e1"] = ":dagger:",
- ["\U0001f483"] = ":dancer:",
- ["\U0001f483\U0001f3fb"] = ":dancer_tone1:",
- ["\U0001f483\U0001f3fc"] = ":dancer_tone2:",
- ["\U0001f483\U0001f3fd"] = ":dancer_tone3:",
- ["\U0001f483\U0001f3fe"] = ":dancer_tone4:",
- ["\U0001f483\U0001f3ff"] = ":dancer_tone5:",
- ["\U0001f361"] = ":dango:",
- ["\U0001f576\ufe0f"] = ":dark_sunglasses:",
- ["\U0001f576"] = ":dark_sunglasses:",
- ["\U0001f3af"] = ":dart:",
- ["\U0001f4a8"] = ":dash:",
- ["\U0001f4c5"] = ":date:",
- ["\U0001f9cf\u200d\u2642\ufe0f"] = ":deaf_man:",
- ["\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f"] = ":deaf_man_tone1:",
- ["\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f"] = ":deaf_man_tone2:",
- ["\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f"] = ":deaf_man_tone3:",
- ["\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f"] = ":deaf_man_tone4:",
- ["\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f"] = ":deaf_man_tone5:",
- ["\U0001f9cf"] = ":deaf_person:",
- ["\U0001f9cf\U0001f3fb"] = ":deaf_person_tone1:",
- ["\U0001f9cf\U0001f3fc"] = ":deaf_person_tone2:",
- ["\U0001f9cf\U0001f3fd"] = ":deaf_person_tone3:",
- ["\U0001f9cf\U0001f3fe"] = ":deaf_person_tone4:",
- ["\U0001f9cf\U0001f3ff"] = ":deaf_person_tone5:",
- ["\U0001f9cf\u200d\u2640\ufe0f"] = ":deaf_woman:",
- ["\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f"] = ":deaf_woman_tone1:",
- ["\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f"] = ":deaf_woman_tone2:",
- ["\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f"] = ":deaf_woman_tone3:",
- ["\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f"] = ":deaf_woman_tone4:",
- ["\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f"] = ":deaf_woman_tone5:",
- ["\U0001f333"] = ":deciduous_tree:",
- ["\U0001f98c"] = ":deer:",
- ["\U0001f3ec"] = ":department_store:",
- ["\U0001f3dc\ufe0f"] = ":desert:",
- ["\U0001f3dc"] = ":desert:",
- ["\U0001f5a5\ufe0f"] = ":desktop:",
- ["\U0001f5a5"] = ":desktop:",
- ["\U0001f575\ufe0f"] = ":detective:",
- ["\U0001f575"] = ":detective:",
- ["\U0001f575\U0001f3fb"] = ":detective_tone1:",
- ["\U0001f575\U0001f3fc"] = ":detective_tone2:",
- ["\U0001f575\U0001f3fd"] = ":detective_tone3:",
- ["\U0001f575\U0001f3fe"] = ":detective_tone4:",
- ["\U0001f575\U0001f3ff"] = ":detective_tone5:",
- ["\U0001f4a0"] = ":diamond_shape_with_a_dot_inside:",
- ["\u2666\ufe0f"] = ":diamonds:",
- ["\u2666"] = ":diamonds:",
- ["\U0001f61e"] = ":disappointed:",
- ["\U0001f625"] = ":disappointed_relieved:",
- ["\U0001f978"] = ":disguised_face:",
- ["\U0001f5c2\ufe0f"] = ":dividers:",
- ["\U0001f5c2"] = ":dividers:",
- ["\U0001f93f"] = ":diving_mask:",
- ["\U0001fa94"] = ":diya_lamp:",
- ["\U0001f4ab"] = ":dizzy:",
- ["\U0001f635"] = ":dizzy_face:",
- ["\U0001f9ec"] = ":dna:",
- ["\U0001f6af"] = ":do_not_litter:",
- ["\U0001f9a4"] = ":dodo:",
- ["\U0001f436"] = ":dog:",
- ["\U0001f415"] = ":dog2:",
- ["\U0001f4b5"] = ":dollar:",
- ["\U0001f38e"] = ":dolls:",
- ["\U0001f42c"] = ":dolphin:",
- ["\U0001f6aa"] = ":door:",
- ["\U0001f369"] = ":doughnut:",
- ["\U0001f54a\ufe0f"] = ":dove:",
- ["\U0001f54a"] = ":dove:",
- ["\U0001f409"] = ":dragon:",
- ["\U0001f432"] = ":dragon_face:",
- ["\U0001f457"] = ":dress:",
- ["\U0001f42a"] = ":dromedary_camel:",
- ["\U0001f924"] = ":drooling_face:",
- ["\U0001fa78"] = ":drop_of_blood:",
- ["\U0001f4a7"] = ":droplet:",
- ["\U0001f941"] = ":drum:",
- ["\U0001f986"] = ":duck:",
- ["\U0001f95f"] = ":dumpling:",
- ["\U0001f4c0"] = ":dvd:",
- ["\U0001f4e7"] = ":e_mail:",
- ["\U0001f985"] = ":eagle:",
- ["\U0001f442"] = ":ear:",
- ["\U0001f33e"] = ":ear_of_rice:",
- ["\U0001f442\U0001f3fb"] = ":ear_tone1:",
- ["\U0001f442\U0001f3fc"] = ":ear_tone2:",
- ["\U0001f442\U0001f3fd"] = ":ear_tone3:",
- ["\U0001f442\U0001f3fe"] = ":ear_tone4:",
- ["\U0001f442\U0001f3ff"] = ":ear_tone5:",
- ["\U0001f9bb"] = ":ear_with_hearing_aid:",
- ["\U0001f9bb\U0001f3fb"] = ":ear_with_hearing_aid_tone1:",
- ["\U0001f9bb\U0001f3fc"] = ":ear_with_hearing_aid_tone2:",
- ["\U0001f9bb\U0001f3fd"] = ":ear_with_hearing_aid_tone3:",
- ["\U0001f9bb\U0001f3fe"] = ":ear_with_hearing_aid_tone4:",
- ["\U0001f9bb\U0001f3ff"] = ":ear_with_hearing_aid_tone5:",
- ["\U0001f30d"] = ":earth_africa:",
- ["\U0001f30e"] = ":earth_americas:",
- ["\U0001f30f"] = ":earth_asia:",
- ["\U0001f95a"] = ":egg:",
- ["\U0001f346"] = ":eggplant:",
- ["\u0038\ufe0f\u20e3"] = ":eight:",
- ["\u0038\u20e3"] = ":eight:",
- ["\u2734\ufe0f"] = ":eight_pointed_black_star:",
- ["\u2734"] = ":eight_pointed_black_star:",
- ["\u2733\ufe0f"] = ":eight_spoked_asterisk:",
- ["\u2733"] = ":eight_spoked_asterisk:",
- ["\u23cf\ufe0f"] = ":eject:",
- ["\u23cf"] = ":eject:",
- ["\U0001f50c"] = ":electric_plug:",
- ["\U0001f418"] = ":elephant:",
- ["\U0001f6d7"] = ":elevator:",
- ["\U0001f9dd"] = ":elf:",
- ["\U0001f9dd\U0001f3fb"] = ":elf_tone1:",
- ["\U0001f9dd\U0001f3fc"] = ":elf_tone2:",
- ["\U0001f9dd\U0001f3fd"] = ":elf_tone3:",
- ["\U0001f9dd\U0001f3fe"] = ":elf_tone4:",
- ["\U0001f9dd\U0001f3ff"] = ":elf_tone5:",
- ["\U0001f51a"] = ":end:",
- ["\U0001f3f4\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f"] = ":england:",
- ["\u2709\ufe0f"] = ":envelope:",
- ["\u2709"] = ":envelope:",
- ["\U0001f4e9"] = ":envelope_with_arrow:",
- ["\U0001f4b6"] = ":euro:",
- ["\U0001f3f0"] = ":european_castle:",
- ["\U0001f3e4"] = ":european_post_office:",
- ["\U0001f332"] = ":evergreen_tree:",
- ["\u2757"] = ":exclamation:",
- ["\U0001f92f"] = ":exploding_head:",
- ["\U0001f611"] = ":expressionless:",
- ["\U0001f441\ufe0f"] = ":eye:",
- ["\U0001f441"] = ":eye:",
- ["\U0001f441\u200d\U0001f5e8"] = ":eye_in_speech_bubble:",
- ["\U0001f453"] = ":eyeglasses:",
- ["\U0001f440"] = ":eyes:",
- ["\U0001f92e"] = ":face_vomiting:",
- ["\U0001f92d"] = ":face_with_hand_over_mouth:",
- ["\U0001f9d0"] = ":face_with_monocle:",
- ["\U0001f928"] = ":face_with_raised_eyebrow:",
- ["\U0001f92c"] = ":face_with_symbols_over_mouth:",
- ["\U0001f3ed"] = ":factory:",
- ["\U0001f9d1\u200d\U0001f3ed"] = ":factory_worker:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f3ed"] = ":factory_worker_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f3ed"] = ":factory_worker_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f3ed"] = ":factory_worker_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f3ed"] = ":factory_worker_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f3ed"] = ":factory_worker_tone5:",
- ["\U0001f9da"] = ":fairy:",
- ["\U0001f9da\U0001f3fb"] = ":fairy_tone1:",
- ["\U0001f9da\U0001f3fc"] = ":fairy_tone2:",
- ["\U0001f9da\U0001f3fd"] = ":fairy_tone3:",
- ["\U0001f9da\U0001f3fe"] = ":fairy_tone4:",
- ["\U0001f9da\U0001f3ff"] = ":fairy_tone5:",
- ["\U0001f9c6"] = ":falafel:",
- ["\U0001f342"] = ":fallen_leaf:",
- ["\U0001f46a"] = ":family:",
- ["\U0001f468\u200d\U0001f466"] = ":family_man_boy:",
- ["\U0001f468\u200d\U0001f466\u200d\U0001f466"] = ":family_man_boy_boy:",
- ["\U0001f468\u200d\U0001f467"] = ":family_man_girl:",
- ["\U0001f468\u200d\U0001f467\u200d\U0001f466"] = ":family_man_girl_boy:",
- ["\U0001f468\u200d\U0001f467\u200d\U0001f467"] = ":family_man_girl_girl:",
- ["\U0001f468\u200d\U0001f469\u200d\U0001f466"] = ":family_man_woman_boy:",
- ["\U0001f468\u200d\U0001f468\u200d\U0001f466"] = ":family_mmb:",
- ["\U0001f468\u200d\U0001f468\u200d\U0001f466\u200d\U0001f466"] = ":family_mmbb:",
- ["\U0001f468\u200d\U0001f468\u200d\U0001f467"] = ":family_mmg:",
- ["\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f466"] = ":family_mmgb:",
- ["\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f467"] = ":family_mmgg:",
- ["\U0001f468\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466"] = ":family_mwbb:",
- ["\U0001f468\u200d\U0001f469\u200d\U0001f467"] = ":family_mwg:",
- ["\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466"] = ":family_mwgb:",
- ["\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467"] = ":family_mwgg:",
- ["\U0001f469\u200d\U0001f466"] = ":family_woman_boy:",
- ["\U0001f469\u200d\U0001f466\u200d\U0001f466"] = ":family_woman_boy_boy:",
- ["\U0001f469\u200d\U0001f467"] = ":family_woman_girl:",
- ["\U0001f469\u200d\U0001f467\u200d\U0001f466"] = ":family_woman_girl_boy:",
- ["\U0001f469\u200d\U0001f467\u200d\U0001f467"] = ":family_woman_girl_girl:",
- ["\U0001f469\u200d\U0001f469\u200d\U0001f466"] = ":family_wwb:",
- ["\U0001f469\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466"] = ":family_wwbb:",
- ["\U0001f469\u200d\U0001f469\u200d\U0001f467"] = ":family_wwg:",
- ["\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466"] = ":family_wwgb:",
- ["\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467"] = ":family_wwgg:",
- ["\U0001f9d1\u200d\U0001f33e"] = ":farmer:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f33e"] = ":farmer_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f33e"] = ":farmer_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f33e"] = ":farmer_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f33e"] = ":farmer_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f33e"] = ":farmer_tone5:",
- ["\u23e9"] = ":fast_forward:",
- ["\U0001f4e0"] = ":fax:",
- ["\U0001f628"] = ":fearful:",
- ["\U0001fab6"] = ":feather:",
- ["\U0001f43e"] = ":feet:",
- ["\u2640\ufe0f"] = ":female_sign:",
- ["\u2640"] = ":female_sign:",
- ["\U0001f3a1"] = ":ferris_wheel:",
- ["\u26f4\ufe0f"] = ":ferry:",
- ["\u26f4"] = ":ferry:",
- ["\U0001f3d1"] = ":field_hockey:",
- ["\U0001f5c4\ufe0f"] = ":file_cabinet:",
- ["\U0001f5c4"] = ":file_cabinet:",
- ["\U0001f4c1"] = ":file_folder:",
- ["\U0001f39e\ufe0f"] = ":film_frames:",
- ["\U0001f39e"] = ":film_frames:",
- ["\U0001f91e"] = ":fingers_crossed:",
- ["\U0001f91e\U0001f3fb"] = ":fingers_crossed_tone1:",
- ["\U0001f91e\U0001f3fc"] = ":fingers_crossed_tone2:",
- ["\U0001f91e\U0001f3fd"] = ":fingers_crossed_tone3:",
- ["\U0001f91e\U0001f3fe"] = ":fingers_crossed_tone4:",
- ["\U0001f91e\U0001f3ff"] = ":fingers_crossed_tone5:",
- ["\U0001f525"] = ":fire:",
- ["\U0001f692"] = ":fire_engine:",
- ["\U0001f9ef"] = ":fire_extinguisher:",
- ["\U0001f9e8"] = ":firecracker:",
- ["\U0001f9d1\u200d\U0001f692"] = ":firefighter:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f692"] = ":firefighter_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f692"] = ":firefighter_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f692"] = ":firefighter_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f692"] = ":firefighter_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f692"] = ":firefighter_tone5:",
- ["\U0001f386"] = ":fireworks:",
- ["\U0001f947"] = ":first_place:",
- ["\U0001f313"] = ":first_quarter_moon:",
- ["\U0001f31b"] = ":first_quarter_moon_with_face:",
- ["\U0001f41f"] = ":fish:",
- ["\U0001f365"] = ":fish_cake:",
- ["\U0001f3a3"] = ":fishing_pole_and_fish:",
- ["\u270a"] = ":fist:",
- ["\u270a\U0001f3fb"] = ":fist_tone1:",
- ["\u270a\U0001f3fc"] = ":fist_tone2:",
- ["\u270a\U0001f3fd"] = ":fist_tone3:",
- ["\u270a\U0001f3fe"] = ":fist_tone4:",
- ["\u270a\U0001f3ff"] = ":fist_tone5:",
- ["\u0035\ufe0f\u20e3"] = ":five:",
- ["\u0035\u20e3"] = ":five:",
- ["\U0001f1e6\U0001f1e8"] = ":flag_ac:",
- ["\U0001f1e6\U0001f1e9"] = ":flag_ad:",
- ["\U0001f1e6\U0001f1ea"] = ":flag_ae:",
- ["\U0001f1e6\U0001f1eb"] = ":flag_af:",
- ["\U0001f1e6\U0001f1ec"] = ":flag_ag:",
- ["\U0001f1e6\U0001f1ee"] = ":flag_ai:",
- ["\U0001f1e6\U0001f1f1"] = ":flag_al:",
- ["\U0001f1e6\U0001f1f2"] = ":flag_am:",
- ["\U0001f1e6\U0001f1f4"] = ":flag_ao:",
- ["\U0001f1e6\U0001f1f6"] = ":flag_aq:",
- ["\U0001f1e6\U0001f1f7"] = ":flag_ar:",
- ["\U0001f1e6\U0001f1f8"] = ":flag_as:",
- ["\U0001f1e6\U0001f1f9"] = ":flag_at:",
- ["\U0001f1e6\U0001f1fa"] = ":flag_au:",
- ["\U0001f1e6\U0001f1fc"] = ":flag_aw:",
- ["\U0001f1e6\U0001f1fd"] = ":flag_ax:",
- ["\U0001f1e6\U0001f1ff"] = ":flag_az:",
- ["\U0001f1e7\U0001f1e6"] = ":flag_ba:",
- ["\U0001f1e7\U0001f1e7"] = ":flag_bb:",
- ["\U0001f1e7\U0001f1e9"] = ":flag_bd:",
- ["\U0001f1e7\U0001f1ea"] = ":flag_be:",
- ["\U0001f1e7\U0001f1eb"] = ":flag_bf:",
- ["\U0001f1e7\U0001f1ec"] = ":flag_bg:",
- ["\U0001f1e7\U0001f1ed"] = ":flag_bh:",
- ["\U0001f1e7\U0001f1ee"] = ":flag_bi:",
- ["\U0001f1e7\U0001f1ef"] = ":flag_bj:",
- ["\U0001f1e7\U0001f1f1"] = ":flag_bl:",
- ["\U0001f3f4"] = ":flag_black:",
- ["\U0001f1e7\U0001f1f2"] = ":flag_bm:",
- ["\U0001f1e7\U0001f1f3"] = ":flag_bn:",
- ["\U0001f1e7\U0001f1f4"] = ":flag_bo:",
- ["\U0001f1e7\U0001f1f6"] = ":flag_bq:",
- ["\U0001f1e7\U0001f1f7"] = ":flag_br:",
- ["\U0001f1e7\U0001f1f8"] = ":flag_bs:",
- ["\U0001f1e7\U0001f1f9"] = ":flag_bt:",
- ["\U0001f1e7\U0001f1fb"] = ":flag_bv:",
- ["\U0001f1e7\U0001f1fc"] = ":flag_bw:",
- ["\U0001f1e7\U0001f1fe"] = ":flag_by:",
- ["\U0001f1e7\U0001f1ff"] = ":flag_bz:",
- ["\U0001f1e8\U0001f1e6"] = ":flag_ca:",
- ["\U0001f1e8\U0001f1e8"] = ":flag_cc:",
- ["\U0001f1e8\U0001f1e9"] = ":flag_cd:",
- ["\U0001f1e8\U0001f1eb"] = ":flag_cf:",
- ["\U0001f1e8\U0001f1ec"] = ":flag_cg:",
- ["\U0001f1e8\U0001f1ed"] = ":flag_ch:",
- ["\U0001f1e8\U0001f1ee"] = ":flag_ci:",
- ["\U0001f1e8\U0001f1f0"] = ":flag_ck:",
- ["\U0001f1e8\U0001f1f1"] = ":flag_cl:",
- ["\U0001f1e8\U0001f1f2"] = ":flag_cm:",
- ["\U0001f1e8\U0001f1f3"] = ":flag_cn:",
- ["\U0001f1e8\U0001f1f4"] = ":flag_co:",
- ["\U0001f1e8\U0001f1f5"] = ":flag_cp:",
- ["\U0001f1e8\U0001f1f7"] = ":flag_cr:",
- ["\U0001f1e8\U0001f1fa"] = ":flag_cu:",
- ["\U0001f1e8\U0001f1fb"] = ":flag_cv:",
- ["\U0001f1e8\U0001f1fc"] = ":flag_cw:",
- ["\U0001f1e8\U0001f1fd"] = ":flag_cx:",
- ["\U0001f1e8\U0001f1fe"] = ":flag_cy:",
- ["\U0001f1e8\U0001f1ff"] = ":flag_cz:",
- ["\U0001f1e9\U0001f1ea"] = ":flag_de:",
- ["\U0001f1e9\U0001f1ec"] = ":flag_dg:",
- ["\U0001f1e9\U0001f1ef"] = ":flag_dj:",
- ["\U0001f1e9\U0001f1f0"] = ":flag_dk:",
- ["\U0001f1e9\U0001f1f2"] = ":flag_dm:",
- ["\U0001f1e9\U0001f1f4"] = ":flag_do:",
- ["\U0001f1e9\U0001f1ff"] = ":flag_dz:",
- ["\U0001f1ea\U0001f1e6"] = ":flag_ea:",
- ["\U0001f1ea\U0001f1e8"] = ":flag_ec:",
- ["\U0001f1ea\U0001f1ea"] = ":flag_ee:",
- ["\U0001f1ea\U0001f1ec"] = ":flag_eg:",
- ["\U0001f1ea\U0001f1ed"] = ":flag_eh:",
- ["\U0001f1ea\U0001f1f7"] = ":flag_er:",
- ["\U0001f1ea\U0001f1f8"] = ":flag_es:",
- ["\U0001f1ea\U0001f1f9"] = ":flag_et:",
- ["\U0001f1ea\U0001f1fa"] = ":flag_eu:",
- ["\U0001f1eb\U0001f1ee"] = ":flag_fi:",
- ["\U0001f1eb\U0001f1ef"] = ":flag_fj:",
- ["\U0001f1eb\U0001f1f0"] = ":flag_fk:",
- ["\U0001f1eb\U0001f1f2"] = ":flag_fm:",
- ["\U0001f1eb\U0001f1f4"] = ":flag_fo:",
- ["\U0001f1eb\U0001f1f7"] = ":flag_fr:",
- ["\U0001f1ec\U0001f1e6"] = ":flag_ga:",
- ["\U0001f1ec\U0001f1e7"] = ":flag_gb:",
- ["\U0001f1ec\U0001f1e9"] = ":flag_gd:",
- ["\U0001f1ec\U0001f1ea"] = ":flag_ge:",
- ["\U0001f1ec\U0001f1eb"] = ":flag_gf:",
- ["\U0001f1ec\U0001f1ec"] = ":flag_gg:",
- ["\U0001f1ec\U0001f1ed"] = ":flag_gh:",
- ["\U0001f1ec\U0001f1ee"] = ":flag_gi:",
- ["\U0001f1ec\U0001f1f1"] = ":flag_gl:",
- ["\U0001f1ec\U0001f1f2"] = ":flag_gm:",
- ["\U0001f1ec\U0001f1f3"] = ":flag_gn:",
- ["\U0001f1ec\U0001f1f5"] = ":flag_gp:",
- ["\U0001f1ec\U0001f1f6"] = ":flag_gq:",
- ["\U0001f1ec\U0001f1f7"] = ":flag_gr:",
- ["\U0001f1ec\U0001f1f8"] = ":flag_gs:",
- ["\U0001f1ec\U0001f1f9"] = ":flag_gt:",
- ["\U0001f1ec\U0001f1fa"] = ":flag_gu:",
- ["\U0001f1ec\U0001f1fc"] = ":flag_gw:",
- ["\U0001f1ec\U0001f1fe"] = ":flag_gy:",
- ["\U0001f1ed\U0001f1f0"] = ":flag_hk:",
- ["\U0001f1ed\U0001f1f2"] = ":flag_hm:",
- ["\U0001f1ed\U0001f1f3"] = ":flag_hn:",
- ["\U0001f1ed\U0001f1f7"] = ":flag_hr:",
- ["\U0001f1ed\U0001f1f9"] = ":flag_ht:",
- ["\U0001f1ed\U0001f1fa"] = ":flag_hu:",
- ["\U0001f1ee\U0001f1e8"] = ":flag_ic:",
- ["\U0001f1ee\U0001f1e9"] = ":flag_id:",
- ["\U0001f1ee\U0001f1ea"] = ":flag_ie:",
- ["\U0001f1ee\U0001f1f1"] = ":flag_il:",
- ["\U0001f1ee\U0001f1f2"] = ":flag_im:",
- ["\U0001f1ee\U0001f1f3"] = ":flag_in:",
- ["\U0001f1ee\U0001f1f4"] = ":flag_io:",
- ["\U0001f1ee\U0001f1f6"] = ":flag_iq:",
- ["\U0001f1ee\U0001f1f7"] = ":flag_ir:",
- ["\U0001f1ee\U0001f1f8"] = ":flag_is:",
- ["\U0001f1ee\U0001f1f9"] = ":flag_it:",
- ["\U0001f1ef\U0001f1ea"] = ":flag_je:",
- ["\U0001f1ef\U0001f1f2"] = ":flag_jm:",
- ["\U0001f1ef\U0001f1f4"] = ":flag_jo:",
- ["\U0001f1ef\U0001f1f5"] = ":flag_jp:",
- ["\U0001f1f0\U0001f1ea"] = ":flag_ke:",
- ["\U0001f1f0\U0001f1ec"] = ":flag_kg:",
- ["\U0001f1f0\U0001f1ed"] = ":flag_kh:",
- ["\U0001f1f0\U0001f1ee"] = ":flag_ki:",
- ["\U0001f1f0\U0001f1f2"] = ":flag_km:",
- ["\U0001f1f0\U0001f1f3"] = ":flag_kn:",
- ["\U0001f1f0\U0001f1f5"] = ":flag_kp:",
- ["\U0001f1f0\U0001f1f7"] = ":flag_kr:",
- ["\U0001f1f0\U0001f1fc"] = ":flag_kw:",
- ["\U0001f1f0\U0001f1fe"] = ":flag_ky:",
- ["\U0001f1f0\U0001f1ff"] = ":flag_kz:",
- ["\U0001f1f1\U0001f1e6"] = ":flag_la:",
- ["\U0001f1f1\U0001f1e7"] = ":flag_lb:",
- ["\U0001f1f1\U0001f1e8"] = ":flag_lc:",
- ["\U0001f1f1\U0001f1ee"] = ":flag_li:",
- ["\U0001f1f1\U0001f1f0"] = ":flag_lk:",
- ["\U0001f1f1\U0001f1f7"] = ":flag_lr:",
- ["\U0001f1f1\U0001f1f8"] = ":flag_ls:",
- ["\U0001f1f1\U0001f1f9"] = ":flag_lt:",
- ["\U0001f1f1\U0001f1fa"] = ":flag_lu:",
- ["\U0001f1f1\U0001f1fb"] = ":flag_lv:",
- ["\U0001f1f1\U0001f1fe"] = ":flag_ly:",
- ["\U0001f1f2\U0001f1e6"] = ":flag_ma:",
- ["\U0001f1f2\U0001f1e8"] = ":flag_mc:",
- ["\U0001f1f2\U0001f1e9"] = ":flag_md:",
- ["\U0001f1f2\U0001f1ea"] = ":flag_me:",
- ["\U0001f1f2\U0001f1eb"] = ":flag_mf:",
- ["\U0001f1f2\U0001f1ec"] = ":flag_mg:",
- ["\U0001f1f2\U0001f1ed"] = ":flag_mh:",
- ["\U0001f1f2\U0001f1f0"] = ":flag_mk:",
- ["\U0001f1f2\U0001f1f1"] = ":flag_ml:",
- ["\U0001f1f2\U0001f1f2"] = ":flag_mm:",
- ["\U0001f1f2\U0001f1f3"] = ":flag_mn:",
- ["\U0001f1f2\U0001f1f4"] = ":flag_mo:",
- ["\U0001f1f2\U0001f1f5"] = ":flag_mp:",
- ["\U0001f1f2\U0001f1f6"] = ":flag_mq:",
- ["\U0001f1f2\U0001f1f7"] = ":flag_mr:",
- ["\U0001f1f2\U0001f1f8"] = ":flag_ms:",
- ["\U0001f1f2\U0001f1f9"] = ":flag_mt:",
- ["\U0001f1f2\U0001f1fa"] = ":flag_mu:",
- ["\U0001f1f2\U0001f1fb"] = ":flag_mv:",
- ["\U0001f1f2\U0001f1fc"] = ":flag_mw:",
- ["\U0001f1f2\U0001f1fd"] = ":flag_mx:",
- ["\U0001f1f2\U0001f1fe"] = ":flag_my:",
- ["\U0001f1f2\U0001f1ff"] = ":flag_mz:",
- ["\U0001f1f3\U0001f1e6"] = ":flag_na:",
- ["\U0001f1f3\U0001f1e8"] = ":flag_nc:",
- ["\U0001f1f3\U0001f1ea"] = ":flag_ne:",
- ["\U0001f1f3\U0001f1eb"] = ":flag_nf:",
- ["\U0001f1f3\U0001f1ec"] = ":flag_ng:",
- ["\U0001f1f3\U0001f1ee"] = ":flag_ni:",
- ["\U0001f1f3\U0001f1f1"] = ":flag_nl:",
- ["\U0001f1f3\U0001f1f4"] = ":flag_no:",
- ["\U0001f1f3\U0001f1f5"] = ":flag_np:",
- ["\U0001f1f3\U0001f1f7"] = ":flag_nr:",
- ["\U0001f1f3\U0001f1fa"] = ":flag_nu:",
- ["\U0001f1f3\U0001f1ff"] = ":flag_nz:",
- ["\U0001f1f4\U0001f1f2"] = ":flag_om:",
- ["\U0001f1f5\U0001f1e6"] = ":flag_pa:",
- ["\U0001f1f5\U0001f1ea"] = ":flag_pe:",
- ["\U0001f1f5\U0001f1eb"] = ":flag_pf:",
- ["\U0001f1f5\U0001f1ec"] = ":flag_pg:",
- ["\U0001f1f5\U0001f1ed"] = ":flag_ph:",
- ["\U0001f1f5\U0001f1f0"] = ":flag_pk:",
- ["\U0001f1f5\U0001f1f1"] = ":flag_pl:",
- ["\U0001f1f5\U0001f1f2"] = ":flag_pm:",
- ["\U0001f1f5\U0001f1f3"] = ":flag_pn:",
- ["\U0001f1f5\U0001f1f7"] = ":flag_pr:",
- ["\U0001f1f5\U0001f1f8"] = ":flag_ps:",
- ["\U0001f1f5\U0001f1f9"] = ":flag_pt:",
- ["\U0001f1f5\U0001f1fc"] = ":flag_pw:",
- ["\U0001f1f5\U0001f1fe"] = ":flag_py:",
- ["\U0001f1f6\U0001f1e6"] = ":flag_qa:",
- ["\U0001f1f7\U0001f1ea"] = ":flag_re:",
- ["\U0001f1f7\U0001f1f4"] = ":flag_ro:",
- ["\U0001f1f7\U0001f1f8"] = ":flag_rs:",
- ["\U0001f1f7\U0001f1fa"] = ":flag_ru:",
- ["\U0001f1f7\U0001f1fc"] = ":flag_rw:",
- ["\U0001f1f8\U0001f1e6"] = ":flag_sa:",
- ["\U0001f1f8\U0001f1e7"] = ":flag_sb:",
- ["\U0001f1f8\U0001f1e8"] = ":flag_sc:",
- ["\U0001f1f8\U0001f1e9"] = ":flag_sd:",
- ["\U0001f1f8\U0001f1ea"] = ":flag_se:",
- ["\U0001f1f8\U0001f1ec"] = ":flag_sg:",
- ["\U0001f1f8\U0001f1ed"] = ":flag_sh:",
- ["\U0001f1f8\U0001f1ee"] = ":flag_si:",
- ["\U0001f1f8\U0001f1ef"] = ":flag_sj:",
- ["\U0001f1f8\U0001f1f0"] = ":flag_sk:",
- ["\U0001f1f8\U0001f1f1"] = ":flag_sl:",
- ["\U0001f1f8\U0001f1f2"] = ":flag_sm:",
- ["\U0001f1f8\U0001f1f3"] = ":flag_sn:",
- ["\U0001f1f8\U0001f1f4"] = ":flag_so:",
- ["\U0001f1f8\U0001f1f7"] = ":flag_sr:",
- ["\U0001f1f8\U0001f1f8"] = ":flag_ss:",
- ["\U0001f1f8\U0001f1f9"] = ":flag_st:",
- ["\U0001f1f8\U0001f1fb"] = ":flag_sv:",
- ["\U0001f1f8\U0001f1fd"] = ":flag_sx:",
- ["\U0001f1f8\U0001f1fe"] = ":flag_sy:",
- ["\U0001f1f8\U0001f1ff"] = ":flag_sz:",
- ["\U0001f1f9\U0001f1e6"] = ":flag_ta:",
- ["\U0001f1f9\U0001f1e8"] = ":flag_tc:",
- ["\U0001f1f9\U0001f1e9"] = ":flag_td:",
- ["\U0001f1f9\U0001f1eb"] = ":flag_tf:",
- ["\U0001f1f9\U0001f1ec"] = ":flag_tg:",
- ["\U0001f1f9\U0001f1ed"] = ":flag_th:",
- ["\U0001f1f9\U0001f1ef"] = ":flag_tj:",
- ["\U0001f1f9\U0001f1f0"] = ":flag_tk:",
- ["\U0001f1f9\U0001f1f1"] = ":flag_tl:",
- ["\U0001f1f9\U0001f1f2"] = ":flag_tm:",
- ["\U0001f1f9\U0001f1f3"] = ":flag_tn:",
- ["\U0001f1f9\U0001f1f4"] = ":flag_to:",
- ["\U0001f1f9\U0001f1f7"] = ":flag_tr:",
- ["\U0001f1f9\U0001f1f9"] = ":flag_tt:",
- ["\U0001f1f9\U0001f1fb"] = ":flag_tv:",
- ["\U0001f1f9\U0001f1fc"] = ":flag_tw:",
- ["\U0001f1f9\U0001f1ff"] = ":flag_tz:",
- ["\U0001f1fa\U0001f1e6"] = ":flag_ua:",
- ["\U0001f1fa\U0001f1ec"] = ":flag_ug:",
- ["\U0001f1fa\U0001f1f2"] = ":flag_um:",
- ["\U0001f1fa\U0001f1f8"] = ":flag_us:",
- ["\U0001f1fa\U0001f1fe"] = ":flag_uy:",
- ["\U0001f1fa\U0001f1ff"] = ":flag_uz:",
- ["\U0001f1fb\U0001f1e6"] = ":flag_va:",
- ["\U0001f1fb\U0001f1e8"] = ":flag_vc:",
- ["\U0001f1fb\U0001f1ea"] = ":flag_ve:",
- ["\U0001f1fb\U0001f1ec"] = ":flag_vg:",
- ["\U0001f1fb\U0001f1ee"] = ":flag_vi:",
- ["\U0001f1fb\U0001f1f3"] = ":flag_vn:",
- ["\U0001f1fb\U0001f1fa"] = ":flag_vu:",
- ["\U0001f1fc\U0001f1eb"] = ":flag_wf:",
- ["\U0001f3f3\ufe0f"] = ":flag_white:",
- ["\U0001f3f3"] = ":flag_white:",
- ["\U0001f1fc\U0001f1f8"] = ":flag_ws:",
- ["\U0001f1fd\U0001f1f0"] = ":flag_xk:",
- ["\U0001f1fe\U0001f1ea"] = ":flag_ye:",
- ["\U0001f1fe\U0001f1f9"] = ":flag_yt:",
- ["\U0001f1ff\U0001f1e6"] = ":flag_za:",
- ["\U0001f1ff\U0001f1f2"] = ":flag_zm:",
- ["\U0001f1ff\U0001f1fc"] = ":flag_zw:",
- ["\U0001f38f"] = ":flags:",
- ["\U0001f9a9"] = ":flamingo:",
- ["\U0001f526"] = ":flashlight:",
- ["\U0001fad3"] = ":flatbread:",
- ["\u269c\ufe0f"] = ":fleur_de_lis:",
- ["\u269c"] = ":fleur_de_lis:",
- ["\U0001f4be"] = ":floppy_disk:",
- ["\U0001f3b4"] = ":flower_playing_cards:",
- ["\U0001f633"] = ":flushed:",
- ["\U0001fab0"] = ":fly:",
- ["\U0001f94f"] = ":flying_disc:",
- ["\U0001f6f8"] = ":flying_saucer:",
- ["\U0001f32b\ufe0f"] = ":fog:",
- ["\U0001f32b"] = ":fog:",
- ["\U0001f301"] = ":foggy:",
- ["\U0001fad5"] = ":fondue:",
- ["\U0001f9b6"] = ":foot:",
- ["\U0001f9b6\U0001f3fb"] = ":foot_tone1:",
- ["\U0001f9b6\U0001f3fc"] = ":foot_tone2:",
- ["\U0001f9b6\U0001f3fd"] = ":foot_tone3:",
- ["\U0001f9b6\U0001f3fe"] = ":foot_tone4:",
- ["\U0001f9b6\U0001f3ff"] = ":foot_tone5:",
- ["\U0001f3c8"] = ":football:",
- ["\U0001f463"] = ":footprints:",
- ["\U0001f374"] = ":fork_and_knife:",
- ["\U0001f37d\ufe0f"] = ":fork_knife_plate:",
- ["\U0001f37d"] = ":fork_knife_plate:",
- ["\U0001f960"] = ":fortune_cookie:",
- ["\u26f2"] = ":fountain:",
- ["\u0034\ufe0f\u20e3"] = ":four:",
- ["\u0034\u20e3"] = ":four:",
- ["\U0001f340"] = ":four_leaf_clover:",
- ["\U0001f98a"] = ":fox:",
- ["\U0001f5bc\ufe0f"] = ":frame_photo:",
- ["\U0001f5bc"] = ":frame_photo:",
- ["\U0001f193"] = ":free:",
- ["\U0001f956"] = ":french_bread:",
- ["\U0001f364"] = ":fried_shrimp:",
- ["\U0001f35f"] = ":fries:",
- ["\U0001f438"] = ":frog:",
- ["\U0001f626"] = ":frowning:",
- ["\u2639\ufe0f"] = ":frowning2:",
- ["\u2639"] = ":frowning2:",
- ["\u26fd"] = ":fuelpump:",
- ["\U0001f315"] = ":full_moon:",
- ["\U0001f31d"] = ":full_moon_with_face:",
- ["\U0001f3b2"] = ":game_die:",
- ["\U0001f9c4"] = ":garlic:",
- ["\u2699\ufe0f"] = ":gear:",
- ["\u2699"] = ":gear:",
- ["\U0001f48e"] = ":gem:",
- ["\u264a"] = ":gemini:",
- ["\U0001f9de"] = ":genie:",
- ["\U0001f47b"] = ":ghost:",
- ["\U0001f381"] = ":gift:",
- ["\U0001f49d"] = ":gift_heart:",
- ["\U0001f992"] = ":giraffe:",
- ["\U0001f467"] = ":girl:",
- ["\U0001f467\U0001f3fb"] = ":girl_tone1:",
- ["\U0001f467\U0001f3fc"] = ":girl_tone2:",
- ["\U0001f467\U0001f3fd"] = ":girl_tone3:",
- ["\U0001f467\U0001f3fe"] = ":girl_tone4:",
- ["\U0001f467\U0001f3ff"] = ":girl_tone5:",
- ["\U0001f310"] = ":globe_with_meridians:",
- ["\U0001f9e4"] = ":gloves:",
- ["\U0001f945"] = ":goal:",
- ["\U0001f410"] = ":goat:",
- ["\U0001f97d"] = ":goggles:",
- ["\u26f3"] = ":golf:",
- ["\U0001f98d"] = ":gorilla:",
- ["\U0001f347"] = ":grapes:",
- ["\U0001f34f"] = ":green_apple:",
- ["\U0001f4d7"] = ":green_book:",
- ["\U0001f7e2"] = ":green_circle:",
- ["\U0001f49a"] = ":green_heart:",
- ["\U0001f7e9"] = ":green_square:",
- ["\u2755"] = ":grey_exclamation:",
- ["\u2754"] = ":grey_question:",
- ["\U0001f62c"] = ":grimacing:",
- ["\U0001f601"] = ":grin:",
- ["\U0001f600"] = ":grinning:",
- ["\U0001f482"] = ":guard:",
- ["\U0001f482\U0001f3fb"] = ":guard_tone1:",
- ["\U0001f482\U0001f3fc"] = ":guard_tone2:",
- ["\U0001f482\U0001f3fd"] = ":guard_tone3:",
- ["\U0001f482\U0001f3fe"] = ":guard_tone4:",
- ["\U0001f482\U0001f3ff"] = ":guard_tone5:",
- ["\U0001f9ae"] = ":guide_dog:",
- ["\U0001f3b8"] = ":guitar:",
- ["\U0001f52b"] = ":gun:",
- ["\U0001f354"] = ":hamburger:",
- ["\U0001f528"] = ":hammer:",
- ["\u2692\ufe0f"] = ":hammer_pick:",
- ["\u2692"] = ":hammer_pick:",
- ["\U0001f439"] = ":hamster:",
- ["\U0001f590\ufe0f"] = ":hand_splayed:",
- ["\U0001f590"] = ":hand_splayed:",
- ["\U0001f590\U0001f3fb"] = ":hand_splayed_tone1:",
- ["\U0001f590\U0001f3fc"] = ":hand_splayed_tone2:",
- ["\U0001f590\U0001f3fd"] = ":hand_splayed_tone3:",
- ["\U0001f590\U0001f3fe"] = ":hand_splayed_tone4:",
- ["\U0001f590\U0001f3ff"] = ":hand_splayed_tone5:",
- ["\U0001f45c"] = ":handbag:",
- ["\U0001f91d"] = ":handshake:",
- ["\u0023\ufe0f\u20e3"] = ":hash:",
- ["\u0023\u20e3"] = ":hash:",
- ["\U0001f425"] = ":hatched_chick:",
- ["\U0001f423"] = ":hatching_chick:",
- ["\U0001f915"] = ":head_bandage:",
- ["\U0001f3a7"] = ":headphones:",
- ["\U0001faa6"] = ":headstone:",
- ["\U0001f9d1\u200d\u2695\ufe0f"] = ":health_worker:",
- ["\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f"] = ":health_worker_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f"] = ":health_worker_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f"] = ":health_worker_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f"] = ":health_worker_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f"] = ":health_worker_tone5:",
- ["\U0001f649"] = ":hear_no_evil:",
- ["\u2764\ufe0f"] = ":heart:",
- ["\u2764"] = ":heart:",
- ["\U0001f49f"] = ":heart_decoration:",
- ["\u2763\ufe0f"] = ":heart_exclamation:",
- ["\u2763"] = ":heart_exclamation:",
- ["\U0001f60d"] = ":heart_eyes:",
- ["\U0001f63b"] = ":heart_eyes_cat:",
- ["\U0001f493"] = ":heartbeat:",
- ["\U0001f497"] = ":heartpulse:",
- ["\u2665\ufe0f"] = ":hearts:",
- ["\u2665"] = ":hearts:",
- ["\u2714\ufe0f"] = ":heavy_check_mark:",
- ["\u2714"] = ":heavy_check_mark:",
- ["\u2797"] = ":heavy_division_sign:",
- ["\U0001f4b2"] = ":heavy_dollar_sign:",
- ["\u2796"] = ":heavy_minus_sign:",
- ["\u2716\ufe0f"] = ":heavy_multiplication_x:",
- ["\u2716"] = ":heavy_multiplication_x:",
- ["\u2795"] = ":heavy_plus_sign:",
- ["\U0001f994"] = ":hedgehog:",
- ["\U0001f681"] = ":helicopter:",
- ["\u26d1\ufe0f"] = ":helmet_with_cross:",
- ["\u26d1"] = ":helmet_with_cross:",
- ["\U0001f33f"] = ":herb:",
- ["\U0001f33a"] = ":hibiscus:",
- ["\U0001f506"] = ":high_brightness:",
- ["\U0001f460"] = ":high_heel:",
- ["\U0001f97e"] = ":hiking_boot:",
- ["\U0001f6d5"] = ":hindu_temple:",
- ["\U0001f99b"] = ":hippopotamus:",
- ["\U0001f3d2"] = ":hockey:",
- ["\U0001f573\ufe0f"] = ":hole:",
- ["\U0001f573"] = ":hole:",
- ["\U0001f3d8\ufe0f"] = ":homes:",
- ["\U0001f3d8"] = ":homes:",
- ["\U0001f36f"] = ":honey_pot:",
- ["\U0001fa9d"] = ":hook:",
- ["\U0001f434"] = ":horse:",
- ["\U0001f3c7"] = ":horse_racing:",
- ["\U0001f3c7\U0001f3fb"] = ":horse_racing_tone1:",
- ["\U0001f3c7\U0001f3fc"] = ":horse_racing_tone2:",
- ["\U0001f3c7\U0001f3fd"] = ":horse_racing_tone3:",
- ["\U0001f3c7\U0001f3fe"] = ":horse_racing_tone4:",
- ["\U0001f3c7\U0001f3ff"] = ":horse_racing_tone5:",
- ["\U0001f3e5"] = ":hospital:",
- ["\U0001f975"] = ":hot_face:",
- ["\U0001f336\ufe0f"] = ":hot_pepper:",
- ["\U0001f336"] = ":hot_pepper:",
- ["\U0001f32d"] = ":hotdog:",
- ["\U0001f3e8"] = ":hotel:",
- ["\u2668\ufe0f"] = ":hotsprings:",
- ["\u2668"] = ":hotsprings:",
- ["\u231b"] = ":hourglass:",
- ["\u23f3"] = ":hourglass_flowing_sand:",
- ["\U0001f3e0"] = ":house:",
- ["\U0001f3da\ufe0f"] = ":house_abandoned:",
- ["\U0001f3da"] = ":house_abandoned:",
- ["\U0001f3e1"] = ":house_with_garden:",
- ["\U0001f917"] = ":hugging:",
- ["\U0001f62f"] = ":hushed:",
- ["\U0001f6d6"] = ":hut:",
- ["\U0001f368"] = ":ice_cream:",
- ["\U0001f9ca"] = ":ice_cube:",
- ["\u26f8\ufe0f"] = ":ice_skate:",
- ["\u26f8"] = ":ice_skate:",
- ["\U0001f366"] = ":icecream:",
- ["\U0001f194"] = ":id:",
- ["\U0001f250"] = ":ideograph_advantage:",
- ["\U0001f47f"] = ":imp:",
- ["\U0001f4e5"] = ":inbox_tray:",
- ["\U0001f4e8"] = ":incoming_envelope:",
- ["\u267e\ufe0f"] = ":infinity:",
- ["\u267e"] = ":infinity:",
- ["\u2139\ufe0f"] = ":information_source:",
- ["\u2139"] = ":information_source:",
- ["\U0001f607"] = ":innocent:",
- ["\u2049\ufe0f"] = ":interrobang:",
- ["\u2049"] = ":interrobang:",
- ["\U0001f3dd\ufe0f"] = ":island:",
- ["\U0001f3dd"] = ":island:",
- ["\U0001f3ee"] = ":izakaya_lantern:",
- ["\U0001f383"] = ":jack_o_lantern:",
- ["\U0001f5fe"] = ":japan:",
- ["\U0001f3ef"] = ":japanese_castle:",
- ["\U0001f47a"] = ":japanese_goblin:",
- ["\U0001f479"] = ":japanese_ogre:",
- ["\U0001f456"] = ":jeans:",
- ["\U0001f9e9"] = ":jigsaw:",
- ["\U0001f602"] = ":joy:",
- ["\U0001f639"] = ":joy_cat:",
- ["\U0001f579\ufe0f"] = ":joystick:",
- ["\U0001f579"] = ":joystick:",
- ["\U0001f9d1\u200d\u2696\ufe0f"] = ":judge:",
- ["\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f"] = ":judge_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f"] = ":judge_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f"] = ":judge_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f"] = ":judge_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f"] = ":judge_tone5:",
- ["\U0001f54b"] = ":kaaba:",
- ["\U0001f998"] = ":kangaroo:",
- ["\U0001f511"] = ":key:",
- ["\U0001f5dd\ufe0f"] = ":key2:",
- ["\U0001f5dd"] = ":key2:",
- ["\u2328\ufe0f"] = ":keyboard:",
- ["\u2328"] = ":keyboard:",
- ["\U0001f51f"] = ":keycap_ten:",
- ["\U0001f458"] = ":kimono:",
- ["\U0001f48b"] = ":kiss:",
- ["\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468"] = ":kiss_mm:",
- ["\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468"] = ":kiss_woman_man:",
- ["\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469"] = ":kiss_ww:",
- ["\U0001f617"] = ":kissing:",
- ["\U0001f63d"] = ":kissing_cat:",
- ["\U0001f61a"] = ":kissing_closed_eyes:",
- ["\U0001f618"] = ":kissing_heart:",
- ["\U0001f619"] = ":kissing_smiling_eyes:",
- ["\U0001fa81"] = ":kite:",
- ["\U0001f95d"] = ":kiwi:",
- ["\U0001f52a"] = ":knife:",
- ["\U0001faa2"] = ":knot:",
- ["\U0001f428"] = ":koala:",
- ["\U0001f201"] = ":koko:",
- ["\U0001f97c"] = ":lab_coat:",
- ["\U0001f3f7\ufe0f"] = ":label:",
- ["\U0001f3f7"] = ":label:",
- ["\U0001f94d"] = ":lacrosse:",
- ["\U0001fa9c"] = ":ladder:",
- ["\U0001f41e"] = ":lady_beetle:",
- ["\U0001f537"] = ":large_blue_diamond:",
- ["\U0001f536"] = ":large_orange_diamond:",
- ["\U0001f317"] = ":last_quarter_moon:",
- ["\U0001f31c"] = ":last_quarter_moon_with_face:",
- ["\U0001f606"] = ":laughing:",
- ["\U0001f96c"] = ":leafy_green:",
- ["\U0001f343"] = ":leaves:",
- ["\U0001f4d2"] = ":ledger:",
- ["\U0001f91b"] = ":left_facing_fist:",
- ["\U0001f91b\U0001f3fb"] = ":left_facing_fist_tone1:",
- ["\U0001f91b\U0001f3fc"] = ":left_facing_fist_tone2:",
- ["\U0001f91b\U0001f3fd"] = ":left_facing_fist_tone3:",
- ["\U0001f91b\U0001f3fe"] = ":left_facing_fist_tone4:",
- ["\U0001f91b\U0001f3ff"] = ":left_facing_fist_tone5:",
- ["\U0001f6c5"] = ":left_luggage:",
- ["\u2194\ufe0f"] = ":left_right_arrow:",
- ["\u2194"] = ":left_right_arrow:",
- ["\u21a9\ufe0f"] = ":leftwards_arrow_with_hook:",
- ["\u21a9"] = ":leftwards_arrow_with_hook:",
- ["\U0001f9b5"] = ":leg:",
- ["\U0001f9b5\U0001f3fb"] = ":leg_tone1:",
- ["\U0001f9b5\U0001f3fc"] = ":leg_tone2:",
- ["\U0001f9b5\U0001f3fd"] = ":leg_tone3:",
- ["\U0001f9b5\U0001f3fe"] = ":leg_tone4:",
- ["\U0001f9b5\U0001f3ff"] = ":leg_tone5:",
- ["\U0001f34b"] = ":lemon:",
- ["\u264c"] = ":leo:",
- ["\U0001f406"] = ":leopard:",
- ["\U0001f39a\ufe0f"] = ":level_slider:",
- ["\U0001f39a"] = ":level_slider:",
- ["\U0001f574\ufe0f"] = ":levitate:",
- ["\U0001f574"] = ":levitate:",
- ["\U0001f574\U0001f3fb"] = ":levitate_tone1:",
- ["\U0001f574\U0001f3fc"] = ":levitate_tone2:",
- ["\U0001f574\U0001f3fd"] = ":levitate_tone3:",
- ["\U0001f574\U0001f3fe"] = ":levitate_tone4:",
- ["\U0001f574\U0001f3ff"] = ":levitate_tone5:",
- ["\u264e"] = ":libra:",
- ["\U0001f688"] = ":light_rail:",
- ["\U0001f517"] = ":link:",
- ["\U0001f981"] = ":lion_face:",
- ["\U0001f444"] = ":lips:",
- ["\U0001f484"] = ":lipstick:",
- ["\U0001f98e"] = ":lizard:",
- ["\U0001f999"] = ":llama:",
- ["\U0001f99e"] = ":lobster:",
- ["\U0001f512"] = ":lock:",
- ["\U0001f50f"] = ":lock_with_ink_pen:",
- ["\U0001f36d"] = ":lollipop:",
- ["\U0001fa98"] = ":long_drum:",
- ["\u27bf"] = ":loop:",
- ["\U0001f50a"] = ":loud_sound:",
- ["\U0001f4e2"] = ":loudspeaker:",
- ["\U0001f3e9"] = ":love_hotel:",
- ["\U0001f48c"] = ":love_letter:",
- ["\U0001f91f"] = ":love_you_gesture:",
- ["\U0001f91f\U0001f3fb"] = ":love_you_gesture_tone1:",
- ["\U0001f91f\U0001f3fc"] = ":love_you_gesture_tone2:",
- ["\U0001f91f\U0001f3fd"] = ":love_you_gesture_tone3:",
- ["\U0001f91f\U0001f3fe"] = ":love_you_gesture_tone4:",
- ["\U0001f91f\U0001f3ff"] = ":love_you_gesture_tone5:",
- ["\U0001f505"] = ":low_brightness:",
- ["\U0001f9f3"] = ":luggage:",
- ["\U0001fac1"] = ":lungs:",
- ["\U0001f925"] = ":lying_face:",
- ["\u24c2\ufe0f"] = ":m:",
- ["\u24c2"] = ":m:",
- ["\U0001f50d"] = ":mag:",
- ["\U0001f50e"] = ":mag_right:",
- ["\U0001f9d9"] = ":mage:",
- ["\U0001f9d9\U0001f3fb"] = ":mage_tone1:",
- ["\U0001f9d9\U0001f3fc"] = ":mage_tone2:",
- ["\U0001f9d9\U0001f3fd"] = ":mage_tone3:",
- ["\U0001f9d9\U0001f3fe"] = ":mage_tone4:",
- ["\U0001f9d9\U0001f3ff"] = ":mage_tone5:",
- ["\U0001fa84"] = ":magic_wand:",
- ["\U0001f9f2"] = ":magnet:",
- ["\U0001f004"] = ":mahjong:",
- ["\U0001f4eb"] = ":mailbox:",
- ["\U0001f4ea"] = ":mailbox_closed:",
- ["\U0001f4ec"] = ":mailbox_with_mail:",
- ["\U0001f4ed"] = ":mailbox_with_no_mail:",
- ["\u2642\ufe0f"] = ":male_sign:",
- ["\u2642"] = ":male_sign:",
- ["\U0001f9a3"] = ":mammoth:",
- ["\U0001f468"] = ":man:",
- ["\U0001f468\u200d\U0001f3a8"] = ":man_artist:",
- ["\U0001f468\U0001f3fb\u200d\U0001f3a8"] = ":man_artist_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f3a8"] = ":man_artist_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f3a8"] = ":man_artist_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f3a8"] = ":man_artist_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f3a8"] = ":man_artist_tone5:",
- ["\U0001f468\u200d\U0001f680"] = ":man_astronaut:",
- ["\U0001f468\U0001f3fb\u200d\U0001f680"] = ":man_astronaut_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f680"] = ":man_astronaut_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f680"] = ":man_astronaut_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f680"] = ":man_astronaut_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f680"] = ":man_astronaut_tone5:",
- ["\U0001f468\u200d\U0001f9b2"] = ":man_bald:",
- ["\U0001f468\U0001f3fb\u200d\U0001f9b2"] = ":man_bald_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f9b2"] = ":man_bald_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f9b2"] = ":man_bald_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f9b2"] = ":man_bald_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f9b2"] = ":man_bald_tone5:",
- ["\U0001f6b4\u200d\u2642\ufe0f"] = ":man_biking:",
- ["\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f"] = ":man_biking_tone1:",
- ["\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f"] = ":man_biking_tone2:",
- ["\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f"] = ":man_biking_tone3:",
- ["\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f"] = ":man_biking_tone4:",
- ["\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f"] = ":man_biking_tone5:",
- ["\u26f9\ufe0f\u200d\u2642\ufe0f"] = ":man_bouncing_ball:",
- ["\u26f9\U0001f3fb\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone1:",
- ["\u26f9\U0001f3fc\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone2:",
- ["\u26f9\U0001f3fd\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone3:",
- ["\u26f9\U0001f3fe\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone4:",
- ["\u26f9\U0001f3ff\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone5:",
- ["\U0001f647\u200d\u2642\ufe0f"] = ":man_bowing:",
- ["\U0001f647\U0001f3fb\u200d\u2642\ufe0f"] = ":man_bowing_tone1:",
- ["\U0001f647\U0001f3fc\u200d\u2642\ufe0f"] = ":man_bowing_tone2:",
- ["\U0001f647\U0001f3fd\u200d\u2642\ufe0f"] = ":man_bowing_tone3:",
- ["\U0001f647\U0001f3fe\u200d\u2642\ufe0f"] = ":man_bowing_tone4:",
- ["\U0001f647\U0001f3ff\u200d\u2642\ufe0f"] = ":man_bowing_tone5:",
- ["\U0001f938\u200d\u2642\ufe0f"] = ":man_cartwheeling:",
- ["\U0001f938\U0001f3fb\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone1:",
- ["\U0001f938\U0001f3fc\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone2:",
- ["\U0001f938\U0001f3fd\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone3:",
- ["\U0001f938\U0001f3fe\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone4:",
- ["\U0001f938\U0001f3ff\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone5:",
- ["\U0001f9d7\u200d\u2642\ufe0f"] = ":man_climbing:",
- ["\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f"] = ":man_climbing_tone1:",
- ["\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f"] = ":man_climbing_tone2:",
- ["\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f"] = ":man_climbing_tone3:",
- ["\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f"] = ":man_climbing_tone4:",
- ["\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f"] = ":man_climbing_tone5:",
- ["\U0001f477\u200d\u2642\ufe0f"] = ":man_construction_worker:",
- ["\U0001f477\U0001f3fb\u200d\u2642\ufe0f"] = ":man_construction_worker_tone1:",
- ["\U0001f477\U0001f3fc\u200d\u2642\ufe0f"] = ":man_construction_worker_tone2:",
- ["\U0001f477\U0001f3fd\u200d\u2642\ufe0f"] = ":man_construction_worker_tone3:",
- ["\U0001f477\U0001f3fe\u200d\u2642\ufe0f"] = ":man_construction_worker_tone4:",
- ["\U0001f477\U0001f3ff\u200d\u2642\ufe0f"] = ":man_construction_worker_tone5:",
- ["\U0001f468\u200d\U0001f373"] = ":man_cook:",
- ["\U0001f468\U0001f3fb\u200d\U0001f373"] = ":man_cook_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f373"] = ":man_cook_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f373"] = ":man_cook_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f373"] = ":man_cook_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f373"] = ":man_cook_tone5:",
- ["\U0001f468\u200d\U0001f9b1"] = ":man_curly_haired:",
- ["\U0001f468\U0001f3fb\u200d\U0001f9b1"] = ":man_curly_haired_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f9b1"] = ":man_curly_haired_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f9b1"] = ":man_curly_haired_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f9b1"] = ":man_curly_haired_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f9b1"] = ":man_curly_haired_tone5:",
- ["\U0001f57a"] = ":man_dancing:",
- ["\U0001f57a\U0001f3fb"] = ":man_dancing_tone1:",
- ["\U0001f57a\U0001f3fc"] = ":man_dancing_tone2:",
- ["\U0001f57a\U0001f3fd"] = ":man_dancing_tone3:",
- ["\U0001f57a\U0001f3fe"] = ":man_dancing_tone4:",
- ["\U0001f57a\U0001f3ff"] = ":man_dancing_tone5:",
- ["\U0001f575\ufe0f\u200d\u2642\ufe0f"] = ":man_detective:",
- ["\U0001f575\U0001f3fb\u200d\u2642\ufe0f"] = ":man_detective_tone1:",
- ["\U0001f575\U0001f3fc\u200d\u2642\ufe0f"] = ":man_detective_tone2:",
- ["\U0001f575\U0001f3fd\u200d\u2642\ufe0f"] = ":man_detective_tone3:",
- ["\U0001f575\U0001f3fe\u200d\u2642\ufe0f"] = ":man_detective_tone4:",
- ["\U0001f575\U0001f3ff\u200d\u2642\ufe0f"] = ":man_detective_tone5:",
- ["\U0001f9dd\u200d\u2642\ufe0f"] = ":man_elf:",
- ["\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f"] = ":man_elf_tone1:",
- ["\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f"] = ":man_elf_tone2:",
- ["\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f"] = ":man_elf_tone3:",
- ["\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f"] = ":man_elf_tone4:",
- ["\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f"] = ":man_elf_tone5:",
- ["\U0001f926\u200d\u2642\ufe0f"] = ":man_facepalming:",
- ["\U0001f926\U0001f3fb\u200d\u2642\ufe0f"] = ":man_facepalming_tone1:",
- ["\U0001f926\U0001f3fc\u200d\u2642\ufe0f"] = ":man_facepalming_tone2:",
- ["\U0001f926\U0001f3fd\u200d\u2642\ufe0f"] = ":man_facepalming_tone3:",
- ["\U0001f926\U0001f3fe\u200d\u2642\ufe0f"] = ":man_facepalming_tone4:",
- ["\U0001f926\U0001f3ff\u200d\u2642\ufe0f"] = ":man_facepalming_tone5:",
- ["\U0001f468\u200d\U0001f3ed"] = ":man_factory_worker:",
- ["\U0001f468\U0001f3fb\u200d\U0001f3ed"] = ":man_factory_worker_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f3ed"] = ":man_factory_worker_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f3ed"] = ":man_factory_worker_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f3ed"] = ":man_factory_worker_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f3ed"] = ":man_factory_worker_tone5:",
- ["\U0001f9da\u200d\u2642\ufe0f"] = ":man_fairy:",
- ["\U0001f9da\U0001f3fb\u200d\u2642\ufe0f"] = ":man_fairy_tone1:",
- ["\U0001f9da\U0001f3fc\u200d\u2642\ufe0f"] = ":man_fairy_tone2:",
- ["\U0001f9da\U0001f3fd\u200d\u2642\ufe0f"] = ":man_fairy_tone3:",
- ["\U0001f9da\U0001f3fe\u200d\u2642\ufe0f"] = ":man_fairy_tone4:",
- ["\U0001f9da\U0001f3ff\u200d\u2642\ufe0f"] = ":man_fairy_tone5:",
- ["\U0001f468\u200d\U0001f33e"] = ":man_farmer:",
- ["\U0001f468\U0001f3fb\u200d\U0001f33e"] = ":man_farmer_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f33e"] = ":man_farmer_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f33e"] = ":man_farmer_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f33e"] = ":man_farmer_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f33e"] = ":man_farmer_tone5:",
- ["\U0001f468\u200d\U0001f37c"] = ":man_feeding_baby:",
- ["\U0001f468\U0001f3fb\u200d\U0001f37c"] = ":man_feeding_baby_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f37c"] = ":man_feeding_baby_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f37c"] = ":man_feeding_baby_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f37c"] = ":man_feeding_baby_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f37c"] = ":man_feeding_baby_tone5:",
- ["\U0001f468\u200d\U0001f692"] = ":man_firefighter:",
- ["\U0001f468\U0001f3fb\u200d\U0001f692"] = ":man_firefighter_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f692"] = ":man_firefighter_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f692"] = ":man_firefighter_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f692"] = ":man_firefighter_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f692"] = ":man_firefighter_tone5:",
- ["\U0001f64d\u200d\u2642\ufe0f"] = ":man_frowning:",
- ["\U0001f64d\U0001f3fb\u200d\u2642\ufe0f"] = ":man_frowning_tone1:",
- ["\U0001f64d\U0001f3fc\u200d\u2642\ufe0f"] = ":man_frowning_tone2:",
- ["\U0001f64d\U0001f3fd\u200d\u2642\ufe0f"] = ":man_frowning_tone3:",
- ["\U0001f64d\U0001f3fe\u200d\u2642\ufe0f"] = ":man_frowning_tone4:",
- ["\U0001f64d\U0001f3ff\u200d\u2642\ufe0f"] = ":man_frowning_tone5:",
- ["\U0001f9de\u200d\u2642\ufe0f"] = ":man_genie:",
- ["\U0001f645\u200d\u2642\ufe0f"] = ":man_gesturing_no:",
- ["\U0001f645\U0001f3fb\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone1:",
- ["\U0001f645\U0001f3fc\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone2:",
- ["\U0001f645\U0001f3fd\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone3:",
- ["\U0001f645\U0001f3fe\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone4:",
- ["\U0001f645\U0001f3ff\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone5:",
- ["\U0001f646\u200d\u2642\ufe0f"] = ":man_gesturing_ok:",
- ["\U0001f646\U0001f3fb\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone1:",
- ["\U0001f646\U0001f3fc\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone2:",
- ["\U0001f646\U0001f3fd\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone3:",
- ["\U0001f646\U0001f3fe\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone4:",
- ["\U0001f646\U0001f3ff\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone5:",
- ["\U0001f486\u200d\u2642\ufe0f"] = ":man_getting_face_massage:",
- ["\U0001f486\U0001f3fb\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone1:",
- ["\U0001f486\U0001f3fc\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone2:",
- ["\U0001f486\U0001f3fd\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone3:",
- ["\U0001f486\U0001f3fe\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone4:",
- ["\U0001f486\U0001f3ff\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone5:",
- ["\U0001f487\u200d\u2642\ufe0f"] = ":man_getting_haircut:",
- ["\U0001f487\U0001f3fb\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone1:",
- ["\U0001f487\U0001f3fc\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone2:",
- ["\U0001f487\U0001f3fd\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone3:",
- ["\U0001f487\U0001f3fe\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone4:",
- ["\U0001f487\U0001f3ff\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone5:",
- ["\U0001f3cc\ufe0f\u200d\u2642\ufe0f"] = ":man_golfing:",
- ["\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f"] = ":man_golfing_tone1:",
- ["\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f"] = ":man_golfing_tone2:",
- ["\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f"] = ":man_golfing_tone3:",
- ["\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f"] = ":man_golfing_tone4:",
- ["\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f"] = ":man_golfing_tone5:",
- ["\U0001f482\u200d\u2642\ufe0f"] = ":man_guard:",
- ["\U0001f482\U0001f3fb\u200d\u2642\ufe0f"] = ":man_guard_tone1:",
- ["\U0001f482\U0001f3fc\u200d\u2642\ufe0f"] = ":man_guard_tone2:",
- ["\U0001f482\U0001f3fd\u200d\u2642\ufe0f"] = ":man_guard_tone3:",
- ["\U0001f482\U0001f3fe\u200d\u2642\ufe0f"] = ":man_guard_tone4:",
- ["\U0001f482\U0001f3ff\u200d\u2642\ufe0f"] = ":man_guard_tone5:",
- ["\U0001f468\u200d\u2695\ufe0f"] = ":man_health_worker:",
- ["\U0001f468\U0001f3fb\u200d\u2695\ufe0f"] = ":man_health_worker_tone1:",
- ["\U0001f468\U0001f3fc\u200d\u2695\ufe0f"] = ":man_health_worker_tone2:",
- ["\U0001f468\U0001f3fd\u200d\u2695\ufe0f"] = ":man_health_worker_tone3:",
- ["\U0001f468\U0001f3fe\u200d\u2695\ufe0f"] = ":man_health_worker_tone4:",
- ["\U0001f468\U0001f3ff\u200d\u2695\ufe0f"] = ":man_health_worker_tone5:",
- ["\U0001f9d8\u200d\u2642\ufe0f"] = ":man_in_lotus_position:",
- ["\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone1:",
- ["\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone2:",
- ["\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone3:",
- ["\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone4:",
- ["\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone5:",
- ["\U0001f468\u200d\U0001f9bd"] = ":man_in_manual_wheelchair:",
- ["\U0001f468\U0001f3fb\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone5:",
- ["\U0001f468\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair:",
- ["\U0001f468\U0001f3fb\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone5:",
- ["\U0001f9d6\u200d\u2642\ufe0f"] = ":man_in_steamy_room:",
- ["\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone1:",
- ["\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone2:",
- ["\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone3:",
- ["\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone4:",
- ["\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone5:",
- ["\U0001f935\u200d\u2642\ufe0f"] = ":man_in_tuxedo:",
- ["\U0001f935\U0001f3fb\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone1:",
- ["\U0001f935\U0001f3fc\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone2:",
- ["\U0001f935\U0001f3fd\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone3:",
- ["\U0001f935\U0001f3fe\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone4:",
- ["\U0001f935\U0001f3ff\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone5:",
- ["\U0001f468\u200d\u2696\ufe0f"] = ":man_judge:",
- ["\U0001f468\U0001f3fb\u200d\u2696\ufe0f"] = ":man_judge_tone1:",
- ["\U0001f468\U0001f3fc\u200d\u2696\ufe0f"] = ":man_judge_tone2:",
- ["\U0001f468\U0001f3fd\u200d\u2696\ufe0f"] = ":man_judge_tone3:",
- ["\U0001f468\U0001f3fe\u200d\u2696\ufe0f"] = ":man_judge_tone4:",
- ["\U0001f468\U0001f3ff\u200d\u2696\ufe0f"] = ":man_judge_tone5:",
- ["\U0001f939\u200d\u2642\ufe0f"] = ":man_juggling:",
- ["\U0001f939\U0001f3fb\u200d\u2642\ufe0f"] = ":man_juggling_tone1:",
- ["\U0001f939\U0001f3fc\u200d\u2642\ufe0f"] = ":man_juggling_tone2:",
- ["\U0001f939\U0001f3fd\u200d\u2642\ufe0f"] = ":man_juggling_tone3:",
- ["\U0001f939\U0001f3fe\u200d\u2642\ufe0f"] = ":man_juggling_tone4:",
- ["\U0001f939\U0001f3ff\u200d\u2642\ufe0f"] = ":man_juggling_tone5:",
- ["\U0001f9ce\u200d\u2642\ufe0f"] = ":man_kneeling:",
- ["\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f"] = ":man_kneeling_tone1:",
- ["\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f"] = ":man_kneeling_tone2:",
- ["\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f"] = ":man_kneeling_tone3:",
- ["\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f"] = ":man_kneeling_tone4:",
- ["\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f"] = ":man_kneeling_tone5:",
- ["\U0001f3cb\ufe0f\u200d\u2642\ufe0f"] = ":man_lifting_weights:",
- ["\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone1:",
- ["\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone2:",
- ["\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone3:",
- ["\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone4:",
- ["\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone5:",
- ["\U0001f9d9\u200d\u2642\ufe0f"] = ":man_mage:",
- ["\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f"] = ":man_mage_tone1:",
- ["\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f"] = ":man_mage_tone2:",
- ["\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f"] = ":man_mage_tone3:",
- ["\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f"] = ":man_mage_tone4:",
- ["\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f"] = ":man_mage_tone5:",
- ["\U0001f468\u200d\U0001f527"] = ":man_mechanic:",
- ["\U0001f468\U0001f3fb\u200d\U0001f527"] = ":man_mechanic_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f527"] = ":man_mechanic_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f527"] = ":man_mechanic_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f527"] = ":man_mechanic_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f527"] = ":man_mechanic_tone5:",
- ["\U0001f6b5\u200d\u2642\ufe0f"] = ":man_mountain_biking:",
- ["\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone1:",
- ["\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone2:",
- ["\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone3:",
- ["\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone4:",
- ["\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone5:",
- ["\U0001f468\u200d\U0001f4bc"] = ":man_office_worker:",
- ["\U0001f468\U0001f3fb\u200d\U0001f4bc"] = ":man_office_worker_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f4bc"] = ":man_office_worker_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f4bc"] = ":man_office_worker_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f4bc"] = ":man_office_worker_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f4bc"] = ":man_office_worker_tone5:",
- ["\U0001f468\u200d\u2708\ufe0f"] = ":man_pilot:",
- ["\U0001f468\U0001f3fb\u200d\u2708\ufe0f"] = ":man_pilot_tone1:",
- ["\U0001f468\U0001f3fc\u200d\u2708\ufe0f"] = ":man_pilot_tone2:",
- ["\U0001f468\U0001f3fd\u200d\u2708\ufe0f"] = ":man_pilot_tone3:",
- ["\U0001f468\U0001f3fe\u200d\u2708\ufe0f"] = ":man_pilot_tone4:",
- ["\U0001f468\U0001f3ff\u200d\u2708\ufe0f"] = ":man_pilot_tone5:",
- ["\U0001f93e\u200d\u2642\ufe0f"] = ":man_playing_handball:",
- ["\U0001f93e\U0001f3fb\u200d\u2642\ufe0f"] = ":man_playing_handball_tone1:",
- ["\U0001f93e\U0001f3fc\u200d\u2642\ufe0f"] = ":man_playing_handball_tone2:",
- ["\U0001f93e\U0001f3fd\u200d\u2642\ufe0f"] = ":man_playing_handball_tone3:",
- ["\U0001f93e\U0001f3fe\u200d\u2642\ufe0f"] = ":man_playing_handball_tone4:",
- ["\U0001f93e\U0001f3ff\u200d\u2642\ufe0f"] = ":man_playing_handball_tone5:",
- ["\U0001f93d\u200d\u2642\ufe0f"] = ":man_playing_water_polo:",
- ["\U0001f93d\U0001f3fb\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone1:",
- ["\U0001f93d\U0001f3fc\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone2:",
- ["\U0001f93d\U0001f3fd\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone3:",
- ["\U0001f93d\U0001f3fe\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone4:",
- ["\U0001f93d\U0001f3ff\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone5:",
- ["\U0001f46e\u200d\u2642\ufe0f"] = ":man_police_officer:",
- ["\U0001f46e\U0001f3fb\u200d\u2642\ufe0f"] = ":man_police_officer_tone1:",
- ["\U0001f46e\U0001f3fc\u200d\u2642\ufe0f"] = ":man_police_officer_tone2:",
- ["\U0001f46e\U0001f3fd\u200d\u2642\ufe0f"] = ":man_police_officer_tone3:",
- ["\U0001f46e\U0001f3fe\u200d\u2642\ufe0f"] = ":man_police_officer_tone4:",
- ["\U0001f46e\U0001f3ff\u200d\u2642\ufe0f"] = ":man_police_officer_tone5:",
- ["\U0001f64e\u200d\u2642\ufe0f"] = ":man_pouting:",
- ["\U0001f64e\U0001f3fb\u200d\u2642\ufe0f"] = ":man_pouting_tone1:",
- ["\U0001f64e\U0001f3fc\u200d\u2642\ufe0f"] = ":man_pouting_tone2:",
- ["\U0001f64e\U0001f3fd\u200d\u2642\ufe0f"] = ":man_pouting_tone3:",
- ["\U0001f64e\U0001f3fe\u200d\u2642\ufe0f"] = ":man_pouting_tone4:",
- ["\U0001f64e\U0001f3ff\u200d\u2642\ufe0f"] = ":man_pouting_tone5:",
- ["\U0001f64b\u200d\u2642\ufe0f"] = ":man_raising_hand:",
- ["\U0001f64b\U0001f3fb\u200d\u2642\ufe0f"] = ":man_raising_hand_tone1:",
- ["\U0001f64b\U0001f3fc\u200d\u2642\ufe0f"] = ":man_raising_hand_tone2:",
- ["\U0001f64b\U0001f3fd\u200d\u2642\ufe0f"] = ":man_raising_hand_tone3:",
- ["\U0001f64b\U0001f3fe\u200d\u2642\ufe0f"] = ":man_raising_hand_tone4:",
- ["\U0001f64b\U0001f3ff\u200d\u2642\ufe0f"] = ":man_raising_hand_tone5:",
- ["\U0001f468\u200d\U0001f9b0"] = ":man_red_haired:",
- ["\U0001f468\U0001f3fb\u200d\U0001f9b0"] = ":man_red_haired_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f9b0"] = ":man_red_haired_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f9b0"] = ":man_red_haired_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f9b0"] = ":man_red_haired_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f9b0"] = ":man_red_haired_tone5:",
- ["\U0001f6a3\u200d\u2642\ufe0f"] = ":man_rowing_boat:",
- ["\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone1:",
- ["\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone2:",
- ["\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone3:",
- ["\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone4:",
- ["\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone5:",
- ["\U0001f3c3\u200d\u2642\ufe0f"] = ":man_running:",
- ["\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f"] = ":man_running_tone1:",
- ["\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f"] = ":man_running_tone2:",
- ["\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f"] = ":man_running_tone3:",
- ["\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f"] = ":man_running_tone4:",
- ["\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f"] = ":man_running_tone5:",
- ["\U0001f468\u200d\U0001f52c"] = ":man_scientist:",
- ["\U0001f468\U0001f3fb\u200d\U0001f52c"] = ":man_scientist_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f52c"] = ":man_scientist_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f52c"] = ":man_scientist_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f52c"] = ":man_scientist_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f52c"] = ":man_scientist_tone5:",
- ["\U0001f937\u200d\u2642\ufe0f"] = ":man_shrugging:",
- ["\U0001f937\U0001f3fb\u200d\u2642\ufe0f"] = ":man_shrugging_tone1:",
- ["\U0001f937\U0001f3fc\u200d\u2642\ufe0f"] = ":man_shrugging_tone2:",
- ["\U0001f937\U0001f3fd\u200d\u2642\ufe0f"] = ":man_shrugging_tone3:",
- ["\U0001f937\U0001f3fe\u200d\u2642\ufe0f"] = ":man_shrugging_tone4:",
- ["\U0001f937\U0001f3ff\u200d\u2642\ufe0f"] = ":man_shrugging_tone5:",
- ["\U0001f468\u200d\U0001f3a4"] = ":man_singer:",
- ["\U0001f468\U0001f3fb\u200d\U0001f3a4"] = ":man_singer_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f3a4"] = ":man_singer_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f3a4"] = ":man_singer_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f3a4"] = ":man_singer_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f3a4"] = ":man_singer_tone5:",
- ["\U0001f9cd\u200d\u2642\ufe0f"] = ":man_standing:",
- ["\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f"] = ":man_standing_tone1:",
- ["\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f"] = ":man_standing_tone2:",
- ["\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f"] = ":man_standing_tone3:",
- ["\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f"] = ":man_standing_tone4:",
- ["\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f"] = ":man_standing_tone5:",
- ["\U0001f468\u200d\U0001f393"] = ":man_student:",
- ["\U0001f468\U0001f3fb\u200d\U0001f393"] = ":man_student_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f393"] = ":man_student_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f393"] = ":man_student_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f393"] = ":man_student_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f393"] = ":man_student_tone5:",
- ["\U0001f9b8\u200d\u2642\ufe0f"] = ":man_superhero:",
- ["\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f"] = ":man_superhero_tone1:",
- ["\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f"] = ":man_superhero_tone2:",
- ["\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f"] = ":man_superhero_tone3:",
- ["\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f"] = ":man_superhero_tone4:",
- ["\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f"] = ":man_superhero_tone5:",
- ["\U0001f9b9\u200d\u2642\ufe0f"] = ":man_supervillain:",
- ["\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f"] = ":man_supervillain_tone1:",
- ["\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f"] = ":man_supervillain_tone2:",
- ["\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f"] = ":man_supervillain_tone3:",
- ["\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f"] = ":man_supervillain_tone4:",
- ["\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f"] = ":man_supervillain_tone5:",
- ["\U0001f3c4\u200d\u2642\ufe0f"] = ":man_surfing:",
- ["\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f"] = ":man_surfing_tone1:",
- ["\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f"] = ":man_surfing_tone2:",
- ["\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f"] = ":man_surfing_tone3:",
- ["\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f"] = ":man_surfing_tone4:",
- ["\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f"] = ":man_surfing_tone5:",
- ["\U0001f3ca\u200d\u2642\ufe0f"] = ":man_swimming:",
- ["\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f"] = ":man_swimming_tone1:",
- ["\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f"] = ":man_swimming_tone2:",
- ["\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f"] = ":man_swimming_tone3:",
- ["\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f"] = ":man_swimming_tone4:",
- ["\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f"] = ":man_swimming_tone5:",
- ["\U0001f468\u200d\U0001f3eb"] = ":man_teacher:",
- ["\U0001f468\U0001f3fb\u200d\U0001f3eb"] = ":man_teacher_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f3eb"] = ":man_teacher_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f3eb"] = ":man_teacher_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f3eb"] = ":man_teacher_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f3eb"] = ":man_teacher_tone5:",
- ["\U0001f468\u200d\U0001f4bb"] = ":man_technologist:",
- ["\U0001f468\U0001f3fb\u200d\U0001f4bb"] = ":man_technologist_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f4bb"] = ":man_technologist_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f4bb"] = ":man_technologist_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f4bb"] = ":man_technologist_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f4bb"] = ":man_technologist_tone5:",
- ["\U0001f481\u200d\u2642\ufe0f"] = ":man_tipping_hand:",
- ["\U0001f481\U0001f3fb\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone1:",
- ["\U0001f481\U0001f3fc\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone2:",
- ["\U0001f481\U0001f3fd\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone3:",
- ["\U0001f481\U0001f3fe\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone4:",
- ["\U0001f481\U0001f3ff\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone5:",
- ["\U0001f468\U0001f3fb"] = ":man_tone1:",
- ["\U0001f468\U0001f3fc"] = ":man_tone2:",
- ["\U0001f468\U0001f3fd"] = ":man_tone3:",
- ["\U0001f468\U0001f3fe"] = ":man_tone4:",
- ["\U0001f468\U0001f3ff"] = ":man_tone5:",
- ["\U0001f9db\u200d\u2642\ufe0f"] = ":man_vampire:",
- ["\U0001f9db\U0001f3fb\u200d\u2642\ufe0f"] = ":man_vampire_tone1:",
- ["\U0001f9db\U0001f3fc\u200d\u2642\ufe0f"] = ":man_vampire_tone2:",
- ["\U0001f9db\U0001f3fd\u200d\u2642\ufe0f"] = ":man_vampire_tone3:",
- ["\U0001f9db\U0001f3fe\u200d\u2642\ufe0f"] = ":man_vampire_tone4:",
- ["\U0001f9db\U0001f3ff\u200d\u2642\ufe0f"] = ":man_vampire_tone5:",
- ["\U0001f6b6\u200d\u2642\ufe0f"] = ":man_walking:",
- ["\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f"] = ":man_walking_tone1:",
- ["\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f"] = ":man_walking_tone2:",
- ["\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f"] = ":man_walking_tone3:",
- ["\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f"] = ":man_walking_tone4:",
- ["\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f"] = ":man_walking_tone5:",
- ["\U0001f473\u200d\u2642\ufe0f"] = ":man_wearing_turban:",
- ["\U0001f473\U0001f3fb\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone1:",
- ["\U0001f473\U0001f3fc\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone2:",
- ["\U0001f473\U0001f3fd\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone3:",
- ["\U0001f473\U0001f3fe\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone4:",
- ["\U0001f473\U0001f3ff\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone5:",
- ["\U0001f468\u200d\U0001f9b3"] = ":man_white_haired:",
- ["\U0001f468\U0001f3fb\u200d\U0001f9b3"] = ":man_white_haired_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f9b3"] = ":man_white_haired_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f9b3"] = ":man_white_haired_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f9b3"] = ":man_white_haired_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f9b3"] = ":man_white_haired_tone5:",
- ["\U0001f472"] = ":man_with_chinese_cap:",
- ["\U0001f472\U0001f3fb"] = ":man_with_chinese_cap_tone1:",
- ["\U0001f472\U0001f3fc"] = ":man_with_chinese_cap_tone2:",
- ["\U0001f472\U0001f3fd"] = ":man_with_chinese_cap_tone3:",
- ["\U0001f472\U0001f3fe"] = ":man_with_chinese_cap_tone4:",
- ["\U0001f472\U0001f3ff"] = ":man_with_chinese_cap_tone5:",
- ["\U0001f468\u200d\U0001f9af"] = ":man_with_probing_cane:",
- ["\U0001f468\U0001f3fb\u200d\U0001f9af"] = ":man_with_probing_cane_tone1:",
- ["\U0001f468\U0001f3fc\u200d\U0001f9af"] = ":man_with_probing_cane_tone2:",
- ["\U0001f468\U0001f3fd\u200d\U0001f9af"] = ":man_with_probing_cane_tone3:",
- ["\U0001f468\U0001f3fe\u200d\U0001f9af"] = ":man_with_probing_cane_tone4:",
- ["\U0001f468\U0001f3ff\u200d\U0001f9af"] = ":man_with_probing_cane_tone5:",
- ["\U0001f470\u200d\u2642\ufe0f"] = ":man_with_veil:",
- ["\U0001f470\U0001f3fb\u200d\u2642\ufe0f"] = ":man_with_veil_tone1:",
- ["\U0001f470\U0001f3fc\u200d\u2642\ufe0f"] = ":man_with_veil_tone2:",
- ["\U0001f470\U0001f3fd\u200d\u2642\ufe0f"] = ":man_with_veil_tone3:",
- ["\U0001f470\U0001f3fe\u200d\u2642\ufe0f"] = ":man_with_veil_tone4:",
- ["\U0001f470\U0001f3ff\u200d\u2642\ufe0f"] = ":man_with_veil_tone5:",
- ["\U0001f9df\u200d\u2642\ufe0f"] = ":man_zombie:",
- ["\U0001f96d"] = ":mango:",
- ["\U0001f45e"] = ":mans_shoe:",
- ["\U0001f9bd"] = ":manual_wheelchair:",
- ["\U0001f5fa\ufe0f"] = ":map:",
- ["\U0001f5fa"] = ":map:",
- ["\U0001f341"] = ":maple_leaf:",
- ["\U0001f94b"] = ":martial_arts_uniform:",
- ["\U0001f637"] = ":mask:",
- ["\U0001f9c9"] = ":mate:",
- ["\U0001f356"] = ":meat_on_bone:",
- ["\U0001f9d1\u200d\U0001f527"] = ":mechanic:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f527"] = ":mechanic_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f527"] = ":mechanic_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f527"] = ":mechanic_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f527"] = ":mechanic_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f527"] = ":mechanic_tone5:",
- ["\U0001f9be"] = ":mechanical_arm:",
- ["\U0001f9bf"] = ":mechanical_leg:",
- ["\U0001f3c5"] = ":medal:",
- ["\u2695\ufe0f"] = ":medical_symbol:",
- ["\u2695"] = ":medical_symbol:",
- ["\U0001f4e3"] = ":mega:",
- ["\U0001f348"] = ":melon:",
- ["\U0001f46f\u200d\u2642\ufe0f"] = ":men_with_bunny_ears_partying:",
- ["\U0001f93c\u200d\u2642\ufe0f"] = ":men_wrestling:",
- ["\U0001f54e"] = ":menorah:",
- ["\U0001f6b9"] = ":mens:",
- ["\U0001f9dc\u200d\u2640\ufe0f"] = ":mermaid:",
- ["\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f"] = ":mermaid_tone1:",
- ["\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f"] = ":mermaid_tone2:",
- ["\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f"] = ":mermaid_tone3:",
- ["\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f"] = ":mermaid_tone4:",
- ["\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f"] = ":mermaid_tone5:",
- ["\U0001f9dc\u200d\u2642\ufe0f"] = ":merman:",
- ["\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f"] = ":merman_tone1:",
- ["\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f"] = ":merman_tone2:",
- ["\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f"] = ":merman_tone3:",
- ["\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f"] = ":merman_tone4:",
- ["\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f"] = ":merman_tone5:",
- ["\U0001f9dc"] = ":merperson:",
- ["\U0001f9dc\U0001f3fb"] = ":merperson_tone1:",
- ["\U0001f9dc\U0001f3fc"] = ":merperson_tone2:",
- ["\U0001f9dc\U0001f3fd"] = ":merperson_tone3:",
- ["\U0001f9dc\U0001f3fe"] = ":merperson_tone4:",
- ["\U0001f9dc\U0001f3ff"] = ":merperson_tone5:",
- ["\U0001f918"] = ":metal:",
- ["\U0001f918\U0001f3fb"] = ":metal_tone1:",
- ["\U0001f918\U0001f3fc"] = ":metal_tone2:",
- ["\U0001f918\U0001f3fd"] = ":metal_tone3:",
- ["\U0001f918\U0001f3fe"] = ":metal_tone4:",
- ["\U0001f918\U0001f3ff"] = ":metal_tone5:",
- ["\U0001f687"] = ":metro:",
- ["\U0001f9a0"] = ":microbe:",
- ["\U0001f3a4"] = ":microphone:",
- ["\U0001f399\ufe0f"] = ":microphone2:",
- ["\U0001f399"] = ":microphone2:",
- ["\U0001f52c"] = ":microscope:",
- ["\U0001f595"] = ":middle_finger:",
- ["\U0001f595\U0001f3fb"] = ":middle_finger_tone1:",
- ["\U0001f595\U0001f3fc"] = ":middle_finger_tone2:",
- ["\U0001f595\U0001f3fd"] = ":middle_finger_tone3:",
- ["\U0001f595\U0001f3fe"] = ":middle_finger_tone4:",
- ["\U0001f595\U0001f3ff"] = ":middle_finger_tone5:",
- ["\U0001fa96"] = ":military_helmet:",
- ["\U0001f396\ufe0f"] = ":military_medal:",
- ["\U0001f396"] = ":military_medal:",
- ["\U0001f95b"] = ":milk:",
- ["\U0001f30c"] = ":milky_way:",
- ["\U0001f690"] = ":minibus:",
- ["\U0001f4bd"] = ":minidisc:",
- ["\U0001fa9e"] = ":mirror:",
- ["\U0001f4f1"] = ":mobile_phone:",
- ["\U0001f4f4"] = ":mobile_phone_off:",
- ["\U0001f911"] = ":money_mouth:",
- ["\U0001f4b8"] = ":money_with_wings:",
- ["\U0001f4b0"] = ":moneybag:",
- ["\U0001f412"] = ":monkey:",
- ["\U0001f435"] = ":monkey_face:",
- ["\U0001f69d"] = ":monorail:",
- ["\U0001f96e"] = ":moon_cake:",
- ["\U0001f393"] = ":mortar_board:",
- ["\U0001f54c"] = ":mosque:",
- ["\U0001f99f"] = ":mosquito:",
- ["\U0001f6f5"] = ":motor_scooter:",
- ["\U0001f6e5\ufe0f"] = ":motorboat:",
- ["\U0001f6e5"] = ":motorboat:",
- ["\U0001f3cd\ufe0f"] = ":motorcycle:",
- ["\U0001f3cd"] = ":motorcycle:",
- ["\U0001f9bc"] = ":motorized_wheelchair:",
- ["\U0001f6e3\ufe0f"] = ":motorway:",
- ["\U0001f6e3"] = ":motorway:",
- ["\U0001f5fb"] = ":mount_fuji:",
- ["\u26f0\ufe0f"] = ":mountain:",
- ["\u26f0"] = ":mountain:",
- ["\U0001f6a0"] = ":mountain_cableway:",
- ["\U0001f69e"] = ":mountain_railway:",
- ["\U0001f3d4\ufe0f"] = ":mountain_snow:",
- ["\U0001f3d4"] = ":mountain_snow:",
- ["\U0001f42d"] = ":mouse:",
- ["\U0001f5b1\ufe0f"] = ":mouse_three_button:",
- ["\U0001f5b1"] = ":mouse_three_button:",
- ["\U0001faa4"] = ":mouse_trap:",
- ["\U0001f401"] = ":mouse2:",
- ["\U0001f3a5"] = ":movie_camera:",
- ["\U0001f5ff"] = ":moyai:",
- ["\U0001f936"] = ":mrs_claus:",
- ["\U0001f936\U0001f3fb"] = ":mrs_claus_tone1:",
- ["\U0001f936\U0001f3fc"] = ":mrs_claus_tone2:",
- ["\U0001f936\U0001f3fd"] = ":mrs_claus_tone3:",
- ["\U0001f936\U0001f3fe"] = ":mrs_claus_tone4:",
- ["\U0001f936\U0001f3ff"] = ":mrs_claus_tone5:",
- ["\U0001f4aa"] = ":muscle:",
- ["\U0001f4aa\U0001f3fb"] = ":muscle_tone1:",
- ["\U0001f4aa\U0001f3fc"] = ":muscle_tone2:",
- ["\U0001f4aa\U0001f3fd"] = ":muscle_tone3:",
- ["\U0001f4aa\U0001f3fe"] = ":muscle_tone4:",
- ["\U0001f4aa\U0001f3ff"] = ":muscle_tone5:",
- ["\U0001f344"] = ":mushroom:",
- ["\U0001f3b9"] = ":musical_keyboard:",
- ["\U0001f3b5"] = ":musical_note:",
- ["\U0001f3bc"] = ":musical_score:",
- ["\U0001f507"] = ":mute:",
- ["\U0001f9d1\u200d\U0001f384"] = ":mx_claus:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f384"] = ":mx_claus_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f384"] = ":mx_claus_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f384"] = ":mx_claus_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f384"] = ":mx_claus_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f384"] = ":mx_claus_tone5:",
- ["\U0001f485"] = ":nail_care:",
- ["\U0001f485\U0001f3fb"] = ":nail_care_tone1:",
- ["\U0001f485\U0001f3fc"] = ":nail_care_tone2:",
- ["\U0001f485\U0001f3fd"] = ":nail_care_tone3:",
- ["\U0001f485\U0001f3fe"] = ":nail_care_tone4:",
- ["\U0001f485\U0001f3ff"] = ":nail_care_tone5:",
- ["\U0001f4db"] = ":name_badge:",
- ["\U0001f922"] = ":nauseated_face:",
- ["\U0001f9ff"] = ":nazar_amulet:",
- ["\U0001f454"] = ":necktie:",
- ["\u274e"] = ":negative_squared_cross_mark:",
- ["\U0001f913"] = ":nerd:",
- ["\U0001fa86"] = ":nesting_dolls:",
- ["\U0001f610"] = ":neutral_face:",
- ["\U0001f195"] = ":new:",
- ["\U0001f311"] = ":new_moon:",
- ["\U0001f31a"] = ":new_moon_with_face:",
- ["\U0001f4f0"] = ":newspaper:",
- ["\U0001f5de\ufe0f"] = ":newspaper2:",
- ["\U0001f5de"] = ":newspaper2:",
- ["\U0001f196"] = ":ng:",
- ["\U0001f303"] = ":night_with_stars:",
- ["\u0039\ufe0f\u20e3"] = ":nine:",
- ["\u0039\u20e3"] = ":nine:",
- ["\U0001f977"] = ":ninja:",
- ["\U0001f977\U0001f3fb"] = ":ninja_tone1:",
- ["\U0001f977\U0001f3fc"] = ":ninja_tone2:",
- ["\U0001f977\U0001f3fd"] = ":ninja_tone3:",
- ["\U0001f977\U0001f3fe"] = ":ninja_tone4:",
- ["\U0001f977\U0001f3ff"] = ":ninja_tone5:",
- ["\U0001f515"] = ":no_bell:",
- ["\U0001f6b3"] = ":no_bicycles:",
- ["\u26d4"] = ":no_entry:",
- ["\U0001f6ab"] = ":no_entry_sign:",
- ["\U0001f4f5"] = ":no_mobile_phones:",
- ["\U0001f636"] = ":no_mouth:",
- ["\U0001f6b7"] = ":no_pedestrians:",
- ["\U0001f6ad"] = ":no_smoking:",
- ["\U0001f6b1"] = ":non_potable_water:",
- ["\U0001f443"] = ":nose:",
- ["\U0001f443\U0001f3fb"] = ":nose_tone1:",
- ["\U0001f443\U0001f3fc"] = ":nose_tone2:",
- ["\U0001f443\U0001f3fd"] = ":nose_tone3:",
- ["\U0001f443\U0001f3fe"] = ":nose_tone4:",
- ["\U0001f443\U0001f3ff"] = ":nose_tone5:",
- ["\U0001f4d3"] = ":notebook:",
- ["\U0001f4d4"] = ":notebook_with_decorative_cover:",
- ["\U0001f5d2\ufe0f"] = ":notepad_spiral:",
- ["\U0001f5d2"] = ":notepad_spiral:",
- ["\U0001f3b6"] = ":notes:",
- ["\U0001f529"] = ":nut_and_bolt:",
- ["\u2b55"] = ":o:",
- ["\U0001f17e\ufe0f"] = ":o2:",
- ["\U0001f17e"] = ":o2:",
- ["\U0001f30a"] = ":ocean:",
- ["\U0001f6d1"] = ":octagonal_sign:",
- ["\U0001f419"] = ":octopus:",
- ["\U0001f362"] = ":oden:",
- ["\U0001f3e2"] = ":office:",
- ["\U0001f9d1\u200d\U0001f4bc"] = ":office_worker:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f4bc"] = ":office_worker_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f4bc"] = ":office_worker_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f4bc"] = ":office_worker_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f4bc"] = ":office_worker_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f4bc"] = ":office_worker_tone5:",
- ["\U0001f6e2\ufe0f"] = ":oil:",
- ["\U0001f6e2"] = ":oil:",
- ["\U0001f197"] = ":ok:",
- ["\U0001f44c"] = ":ok_hand:",
- ["\U0001f44c\U0001f3fb"] = ":ok_hand_tone1:",
- ["\U0001f44c\U0001f3fc"] = ":ok_hand_tone2:",
- ["\U0001f44c\U0001f3fd"] = ":ok_hand_tone3:",
- ["\U0001f44c\U0001f3fe"] = ":ok_hand_tone4:",
- ["\U0001f44c\U0001f3ff"] = ":ok_hand_tone5:",
- ["\U0001f9d3"] = ":older_adult:",
- ["\U0001f9d3\U0001f3fb"] = ":older_adult_tone1:",
- ["\U0001f9d3\U0001f3fc"] = ":older_adult_tone2:",
- ["\U0001f9d3\U0001f3fd"] = ":older_adult_tone3:",
- ["\U0001f9d3\U0001f3fe"] = ":older_adult_tone4:",
- ["\U0001f9d3\U0001f3ff"] = ":older_adult_tone5:",
- ["\U0001f474"] = ":older_man:",
- ["\U0001f474\U0001f3fb"] = ":older_man_tone1:",
- ["\U0001f474\U0001f3fc"] = ":older_man_tone2:",
- ["\U0001f474\U0001f3fd"] = ":older_man_tone3:",
- ["\U0001f474\U0001f3fe"] = ":older_man_tone4:",
- ["\U0001f474\U0001f3ff"] = ":older_man_tone5:",
- ["\U0001f475"] = ":older_woman:",
- ["\U0001f475\U0001f3fb"] = ":older_woman_tone1:",
- ["\U0001f475\U0001f3fc"] = ":older_woman_tone2:",
- ["\U0001f475\U0001f3fd"] = ":older_woman_tone3:",
- ["\U0001f475\U0001f3fe"] = ":older_woman_tone4:",
- ["\U0001f475\U0001f3ff"] = ":older_woman_tone5:",
- ["\U0001fad2"] = ":olive:",
- ["\U0001f549\ufe0f"] = ":om_symbol:",
- ["\U0001f549"] = ":om_symbol:",
- ["\U0001f51b"] = ":on:",
- ["\U0001f698"] = ":oncoming_automobile:",
- ["\U0001f68d"] = ":oncoming_bus:",
- ["\U0001f694"] = ":oncoming_police_car:",
- ["\U0001f696"] = ":oncoming_taxi:",
- ["\u0031\ufe0f\u20e3"] = ":one:",
- ["\u0031\u20e3"] = ":one:",
- ["\U0001fa71"] = ":one_piece_swimsuit:",
- ["\U0001f9c5"] = ":onion:",
- ["\U0001f4c2"] = ":open_file_folder:",
- ["\U0001f450"] = ":open_hands:",
- ["\U0001f450\U0001f3fb"] = ":open_hands_tone1:",
- ["\U0001f450\U0001f3fc"] = ":open_hands_tone2:",
- ["\U0001f450\U0001f3fd"] = ":open_hands_tone3:",
- ["\U0001f450\U0001f3fe"] = ":open_hands_tone4:",
- ["\U0001f450\U0001f3ff"] = ":open_hands_tone5:",
- ["\U0001f62e"] = ":open_mouth:",
- ["\u26ce"] = ":ophiuchus:",
- ["\U0001f4d9"] = ":orange_book:",
- ["\U0001f7e0"] = ":orange_circle:",
- ["\U0001f9e1"] = ":orange_heart:",
- ["\U0001f7e7"] = ":orange_square:",
- ["\U0001f9a7"] = ":orangutan:",
- ["\u2626\ufe0f"] = ":orthodox_cross:",
- ["\u2626"] = ":orthodox_cross:",
- ["\U0001f9a6"] = ":otter:",
- ["\U0001f4e4"] = ":outbox_tray:",
- ["\U0001f989"] = ":owl:",
- ["\U0001f402"] = ":ox:",
- ["\U0001f9aa"] = ":oyster:",
- ["\U0001f4e6"] = ":package:",
- ["\U0001f4c4"] = ":page_facing_up:",
- ["\U0001f4c3"] = ":page_with_curl:",
- ["\U0001f4df"] = ":pager:",
- ["\U0001f58c\ufe0f"] = ":paintbrush:",
- ["\U0001f58c"] = ":paintbrush:",
- ["\U0001f334"] = ":palm_tree:",
- ["\U0001f932"] = ":palms_up_together:",
- ["\U0001f932\U0001f3fb"] = ":palms_up_together_tone1:",
- ["\U0001f932\U0001f3fc"] = ":palms_up_together_tone2:",
- ["\U0001f932\U0001f3fd"] = ":palms_up_together_tone3:",
- ["\U0001f932\U0001f3fe"] = ":palms_up_together_tone4:",
- ["\U0001f932\U0001f3ff"] = ":palms_up_together_tone5:",
- ["\U0001f95e"] = ":pancakes:",
- ["\U0001f43c"] = ":panda_face:",
- ["\U0001f4ce"] = ":paperclip:",
- ["\U0001f587\ufe0f"] = ":paperclips:",
- ["\U0001f587"] = ":paperclips:",
- ["\U0001fa82"] = ":parachute:",
- ["\U0001f3de\ufe0f"] = ":park:",
- ["\U0001f3de"] = ":park:",
- ["\U0001f17f\ufe0f"] = ":parking:",
- ["\U0001f17f"] = ":parking:",
- ["\U0001f99c"] = ":parrot:",
- ["\u303d\ufe0f"] = ":part_alternation_mark:",
- ["\u303d"] = ":part_alternation_mark:",
- ["\u26c5"] = ":partly_sunny:",
- ["\U0001f973"] = ":partying_face:",
- ["\U0001f6c2"] = ":passport_control:",
- ["\u23f8\ufe0f"] = ":pause_button:",
- ["\u23f8"] = ":pause_button:",
- ["\u262e\ufe0f"] = ":peace:",
- ["\u262e"] = ":peace:",
- ["\U0001f351"] = ":peach:",
- ["\U0001f99a"] = ":peacock:",
- ["\U0001f95c"] = ":peanuts:",
- ["\U0001f350"] = ":pear:",
- ["\U0001f58a\ufe0f"] = ":pen_ballpoint:",
- ["\U0001f58a"] = ":pen_ballpoint:",
- ["\U0001f58b\ufe0f"] = ":pen_fountain:",
- ["\U0001f58b"] = ":pen_fountain:",
- ["\U0001f4dd"] = ":pencil:",
- ["\u270f\ufe0f"] = ":pencil2:",
- ["\u270f"] = ":pencil2:",
- ["\U0001f427"] = ":penguin:",
- ["\U0001f614"] = ":pensive:",
- ["\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1"] = ":people_holding_hands_tone5_tone4:",
- ["\U0001fac2"] = ":people_hugging:",
- ["\U0001f46f"] = ":people_with_bunny_ears_partying:",
- ["\U0001f93c"] = ":people_wrestling:",
- ["\U0001f3ad"] = ":performing_arts:",
- ["\U0001f623"] = ":persevere:",
- ["\U0001f9d1\u200d\U0001f9b2"] = ":person_bald:",
- ["\U0001f6b4"] = ":person_biking:",
- ["\U0001f6b4\U0001f3fb"] = ":person_biking_tone1:",
- ["\U0001f6b4\U0001f3fc"] = ":person_biking_tone2:",
- ["\U0001f6b4\U0001f3fd"] = ":person_biking_tone3:",
- ["\U0001f6b4\U0001f3fe"] = ":person_biking_tone4:",
- ["\U0001f6b4\U0001f3ff"] = ":person_biking_tone5:",
- ["\u26f9\ufe0f"] = ":person_bouncing_ball:",
- ["\u26f9"] = ":person_bouncing_ball:",
- ["\u26f9\U0001f3fb"] = ":person_bouncing_ball_tone1:",
- ["\u26f9\U0001f3fc"] = ":person_bouncing_ball_tone2:",
- ["\u26f9\U0001f3fd"] = ":person_bouncing_ball_tone3:",
- ["\u26f9\U0001f3fe"] = ":person_bouncing_ball_tone4:",
- ["\u26f9\U0001f3ff"] = ":person_bouncing_ball_tone5:",
- ["\U0001f647"] = ":person_bowing:",
- ["\U0001f647\U0001f3fb"] = ":person_bowing_tone1:",
- ["\U0001f647\U0001f3fc"] = ":person_bowing_tone2:",
- ["\U0001f647\U0001f3fd"] = ":person_bowing_tone3:",
- ["\U0001f647\U0001f3fe"] = ":person_bowing_tone4:",
- ["\U0001f647\U0001f3ff"] = ":person_bowing_tone5:",
- ["\U0001f9d7"] = ":person_climbing:",
- ["\U0001f9d7\U0001f3fb"] = ":person_climbing_tone1:",
- ["\U0001f9d7\U0001f3fc"] = ":person_climbing_tone2:",
- ["\U0001f9d7\U0001f3fd"] = ":person_climbing_tone3:",
- ["\U0001f9d7\U0001f3fe"] = ":person_climbing_tone4:",
- ["\U0001f9d7\U0001f3ff"] = ":person_climbing_tone5:",
- ["\U0001f9d1\u200d\U0001f9b1"] = ":person_curly_hair:",
- ["\U0001f938"] = ":person_doing_cartwheel:",
- ["\U0001f938\U0001f3fb"] = ":person_doing_cartwheel_tone1:",
- ["\U0001f938\U0001f3fc"] = ":person_doing_cartwheel_tone2:",
- ["\U0001f938\U0001f3fd"] = ":person_doing_cartwheel_tone3:",
- ["\U0001f938\U0001f3fe"] = ":person_doing_cartwheel_tone4:",
- ["\U0001f938\U0001f3ff"] = ":person_doing_cartwheel_tone5:",
- ["\U0001f926"] = ":person_facepalming:",
- ["\U0001f926\U0001f3fb"] = ":person_facepalming_tone1:",
- ["\U0001f926\U0001f3fc"] = ":person_facepalming_tone2:",
- ["\U0001f926\U0001f3fd"] = ":person_facepalming_tone3:",
- ["\U0001f926\U0001f3fe"] = ":person_facepalming_tone4:",
- ["\U0001f926\U0001f3ff"] = ":person_facepalming_tone5:",
- ["\U0001f9d1\u200d\U0001f37c"] = ":person_feeding_baby:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f37c"] = ":person_feeding_baby_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f37c"] = ":person_feeding_baby_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f37c"] = ":person_feeding_baby_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f37c"] = ":person_feeding_baby_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f37c"] = ":person_feeding_baby_tone5:",
- ["\U0001f93a"] = ":person_fencing:",
- ["\U0001f64d"] = ":person_frowning:",
- ["\U0001f64d\U0001f3fb"] = ":person_frowning_tone1:",
- ["\U0001f64d\U0001f3fc"] = ":person_frowning_tone2:",
- ["\U0001f64d\U0001f3fd"] = ":person_frowning_tone3:",
- ["\U0001f64d\U0001f3fe"] = ":person_frowning_tone4:",
- ["\U0001f64d\U0001f3ff"] = ":person_frowning_tone5:",
- ["\U0001f645"] = ":person_gesturing_no:",
- ["\U0001f645\U0001f3fb"] = ":person_gesturing_no_tone1:",
- ["\U0001f645\U0001f3fc"] = ":person_gesturing_no_tone2:",
- ["\U0001f645\U0001f3fd"] = ":person_gesturing_no_tone3:",
- ["\U0001f645\U0001f3fe"] = ":person_gesturing_no_tone4:",
- ["\U0001f645\U0001f3ff"] = ":person_gesturing_no_tone5:",
- ["\U0001f646"] = ":person_gesturing_ok:",
- ["\U0001f646\U0001f3fb"] = ":person_gesturing_ok_tone1:",
- ["\U0001f646\U0001f3fc"] = ":person_gesturing_ok_tone2:",
- ["\U0001f646\U0001f3fd"] = ":person_gesturing_ok_tone3:",
- ["\U0001f646\U0001f3fe"] = ":person_gesturing_ok_tone4:",
- ["\U0001f646\U0001f3ff"] = ":person_gesturing_ok_tone5:",
- ["\U0001f487"] = ":person_getting_haircut:",
- ["\U0001f487\U0001f3fb"] = ":person_getting_haircut_tone1:",
- ["\U0001f487\U0001f3fc"] = ":person_getting_haircut_tone2:",
- ["\U0001f487\U0001f3fd"] = ":person_getting_haircut_tone3:",
- ["\U0001f487\U0001f3fe"] = ":person_getting_haircut_tone4:",
- ["\U0001f487\U0001f3ff"] = ":person_getting_haircut_tone5:",
- ["\U0001f486"] = ":person_getting_massage:",
- ["\U0001f486\U0001f3fb"] = ":person_getting_massage_tone1:",
- ["\U0001f486\U0001f3fc"] = ":person_getting_massage_tone2:",
- ["\U0001f486\U0001f3fd"] = ":person_getting_massage_tone3:",
- ["\U0001f486\U0001f3fe"] = ":person_getting_massage_tone4:",
- ["\U0001f486\U0001f3ff"] = ":person_getting_massage_tone5:",
- ["\U0001f3cc\ufe0f"] = ":person_golfing:",
- ["\U0001f3cc"] = ":person_golfing:",
- ["\U0001f3cc\U0001f3fb"] = ":person_golfing_tone1:",
- ["\U0001f3cc\U0001f3fc"] = ":person_golfing_tone2:",
- ["\U0001f3cc\U0001f3fd"] = ":person_golfing_tone3:",
- ["\U0001f3cc\U0001f3fe"] = ":person_golfing_tone4:",
- ["\U0001f3cc\U0001f3ff"] = ":person_golfing_tone5:",
- ["\U0001f6cc\U0001f3fb"] = ":person_in_bed_tone1:",
- ["\U0001f6cc\U0001f3fc"] = ":person_in_bed_tone2:",
- ["\U0001f6cc\U0001f3fd"] = ":person_in_bed_tone3:",
- ["\U0001f6cc\U0001f3fe"] = ":person_in_bed_tone4:",
- ["\U0001f6cc\U0001f3ff"] = ":person_in_bed_tone5:",
- ["\U0001f9d8"] = ":person_in_lotus_position:",
- ["\U0001f9d8\U0001f3fb"] = ":person_in_lotus_position_tone1:",
- ["\U0001f9d8\U0001f3fc"] = ":person_in_lotus_position_tone2:",
- ["\U0001f9d8\U0001f3fd"] = ":person_in_lotus_position_tone3:",
- ["\U0001f9d8\U0001f3fe"] = ":person_in_lotus_position_tone4:",
- ["\U0001f9d8\U0001f3ff"] = ":person_in_lotus_position_tone5:",
- ["\U0001f9d1\u200d\U0001f9bd"] = ":person_in_manual_wheelchair:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone5:",
- ["\U0001f9d1\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone5:",
- ["\U0001f9d6"] = ":person_in_steamy_room:",
- ["\U0001f9d6\U0001f3fb"] = ":person_in_steamy_room_tone1:",
- ["\U0001f9d6\U0001f3fc"] = ":person_in_steamy_room_tone2:",
- ["\U0001f9d6\U0001f3fd"] = ":person_in_steamy_room_tone3:",
- ["\U0001f9d6\U0001f3fe"] = ":person_in_steamy_room_tone4:",
- ["\U0001f9d6\U0001f3ff"] = ":person_in_steamy_room_tone5:",
- ["\U0001f935"] = ":person_in_tuxedo:",
- ["\U0001f935\U0001f3fb"] = ":person_in_tuxedo_tone1:",
- ["\U0001f935\U0001f3fc"] = ":person_in_tuxedo_tone2:",
- ["\U0001f935\U0001f3fd"] = ":person_in_tuxedo_tone3:",
- ["\U0001f935\U0001f3fe"] = ":person_in_tuxedo_tone4:",
- ["\U0001f935\U0001f3ff"] = ":person_in_tuxedo_tone5:",
- ["\U0001f939"] = ":person_juggling:",
- ["\U0001f939\U0001f3fb"] = ":person_juggling_tone1:",
- ["\U0001f939\U0001f3fc"] = ":person_juggling_tone2:",
- ["\U0001f939\U0001f3fd"] = ":person_juggling_tone3:",
- ["\U0001f939\U0001f3fe"] = ":person_juggling_tone4:",
- ["\U0001f939\U0001f3ff"] = ":person_juggling_tone5:",
- ["\U0001f9ce"] = ":person_kneeling:",
- ["\U0001f9ce\U0001f3fb"] = ":person_kneeling_tone1:",
- ["\U0001f9ce\U0001f3fc"] = ":person_kneeling_tone2:",
- ["\U0001f9ce\U0001f3fd"] = ":person_kneeling_tone3:",
- ["\U0001f9ce\U0001f3fe"] = ":person_kneeling_tone4:",
- ["\U0001f9ce\U0001f3ff"] = ":person_kneeling_tone5:",
- ["\U0001f3cb\ufe0f"] = ":person_lifting_weights:",
- ["\U0001f3cb"] = ":person_lifting_weights:",
- ["\U0001f3cb\U0001f3fb"] = ":person_lifting_weights_tone1:",
- ["\U0001f3cb\U0001f3fc"] = ":person_lifting_weights_tone2:",
- ["\U0001f3cb\U0001f3fd"] = ":person_lifting_weights_tone3:",
- ["\U0001f3cb\U0001f3fe"] = ":person_lifting_weights_tone4:",
- ["\U0001f3cb\U0001f3ff"] = ":person_lifting_weights_tone5:",
- ["\U0001f6b5"] = ":person_mountain_biking:",
- ["\U0001f6b5\U0001f3fb"] = ":person_mountain_biking_tone1:",
- ["\U0001f6b5\U0001f3fc"] = ":person_mountain_biking_tone2:",
- ["\U0001f6b5\U0001f3fd"] = ":person_mountain_biking_tone3:",
- ["\U0001f6b5\U0001f3fe"] = ":person_mountain_biking_tone4:",
- ["\U0001f6b5\U0001f3ff"] = ":person_mountain_biking_tone5:",
- ["\U0001f93e"] = ":person_playing_handball:",
- ["\U0001f93e\U0001f3fb"] = ":person_playing_handball_tone1:",
- ["\U0001f93e\U0001f3fc"] = ":person_playing_handball_tone2:",
- ["\U0001f93e\U0001f3fd"] = ":person_playing_handball_tone3:",
- ["\U0001f93e\U0001f3fe"] = ":person_playing_handball_tone4:",
- ["\U0001f93e\U0001f3ff"] = ":person_playing_handball_tone5:",
- ["\U0001f93d"] = ":person_playing_water_polo:",
- ["\U0001f93d\U0001f3fb"] = ":person_playing_water_polo_tone1:",
- ["\U0001f93d\U0001f3fc"] = ":person_playing_water_polo_tone2:",
- ["\U0001f93d\U0001f3fd"] = ":person_playing_water_polo_tone3:",
- ["\U0001f93d\U0001f3fe"] = ":person_playing_water_polo_tone4:",
- ["\U0001f93d\U0001f3ff"] = ":person_playing_water_polo_tone5:",
- ["\U0001f64e"] = ":person_pouting:",
- ["\U0001f64e\U0001f3fb"] = ":person_pouting_tone1:",
- ["\U0001f64e\U0001f3fc"] = ":person_pouting_tone2:",
- ["\U0001f64e\U0001f3fd"] = ":person_pouting_tone3:",
- ["\U0001f64e\U0001f3fe"] = ":person_pouting_tone4:",
- ["\U0001f64e\U0001f3ff"] = ":person_pouting_tone5:",
- ["\U0001f64b"] = ":person_raising_hand:",
- ["\U0001f64b\U0001f3fb"] = ":person_raising_hand_tone1:",
- ["\U0001f64b\U0001f3fc"] = ":person_raising_hand_tone2:",
- ["\U0001f64b\U0001f3fd"] = ":person_raising_hand_tone3:",
- ["\U0001f64b\U0001f3fe"] = ":person_raising_hand_tone4:",
- ["\U0001f64b\U0001f3ff"] = ":person_raising_hand_tone5:",
- ["\U0001f9d1\u200d\U0001f9b0"] = ":person_red_hair:",
- ["\U0001f6a3"] = ":person_rowing_boat:",
- ["\U0001f6a3\U0001f3fb"] = ":person_rowing_boat_tone1:",
- ["\U0001f6a3\U0001f3fc"] = ":person_rowing_boat_tone2:",
- ["\U0001f6a3\U0001f3fd"] = ":person_rowing_boat_tone3:",
- ["\U0001f6a3\U0001f3fe"] = ":person_rowing_boat_tone4:",
- ["\U0001f6a3\U0001f3ff"] = ":person_rowing_boat_tone5:",
- ["\U0001f3c3"] = ":person_running:",
- ["\U0001f3c3\U0001f3fb"] = ":person_running_tone1:",
- ["\U0001f3c3\U0001f3fc"] = ":person_running_tone2:",
- ["\U0001f3c3\U0001f3fd"] = ":person_running_tone3:",
- ["\U0001f3c3\U0001f3fe"] = ":person_running_tone4:",
- ["\U0001f3c3\U0001f3ff"] = ":person_running_tone5:",
- ["\U0001f937"] = ":person_shrugging:",
- ["\U0001f937\U0001f3fb"] = ":person_shrugging_tone1:",
- ["\U0001f937\U0001f3fc"] = ":person_shrugging_tone2:",
- ["\U0001f937\U0001f3fd"] = ":person_shrugging_tone3:",
- ["\U0001f937\U0001f3fe"] = ":person_shrugging_tone4:",
- ["\U0001f937\U0001f3ff"] = ":person_shrugging_tone5:",
- ["\U0001f9cd"] = ":person_standing:",
- ["\U0001f9cd\U0001f3fb"] = ":person_standing_tone1:",
- ["\U0001f9cd\U0001f3fc"] = ":person_standing_tone2:",
- ["\U0001f9cd\U0001f3fd"] = ":person_standing_tone3:",
- ["\U0001f9cd\U0001f3fe"] = ":person_standing_tone4:",
- ["\U0001f9cd\U0001f3ff"] = ":person_standing_tone5:",
- ["\U0001f3c4"] = ":person_surfing:",
- ["\U0001f3c4\U0001f3fb"] = ":person_surfing_tone1:",
- ["\U0001f3c4\U0001f3fc"] = ":person_surfing_tone2:",
- ["\U0001f3c4\U0001f3fd"] = ":person_surfing_tone3:",
- ["\U0001f3c4\U0001f3fe"] = ":person_surfing_tone4:",
- ["\U0001f3c4\U0001f3ff"] = ":person_surfing_tone5:",
- ["\U0001f3ca"] = ":person_swimming:",
- ["\U0001f3ca\U0001f3fb"] = ":person_swimming_tone1:",
- ["\U0001f3ca\U0001f3fc"] = ":person_swimming_tone2:",
- ["\U0001f3ca\U0001f3fd"] = ":person_swimming_tone3:",
- ["\U0001f3ca\U0001f3fe"] = ":person_swimming_tone4:",
- ["\U0001f3ca\U0001f3ff"] = ":person_swimming_tone5:",
- ["\U0001f481"] = ":person_tipping_hand:",
- ["\U0001f481\U0001f3fb"] = ":person_tipping_hand_tone1:",
- ["\U0001f481\U0001f3fc"] = ":person_tipping_hand_tone2:",
- ["\U0001f481\U0001f3fd"] = ":person_tipping_hand_tone3:",
- ["\U0001f481\U0001f3fe"] = ":person_tipping_hand_tone4:",
- ["\U0001f481\U0001f3ff"] = ":person_tipping_hand_tone5:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f9b2"] = ":person_tone1_bald:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f9b1"] = ":person_tone1_curly_hair:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f9b0"] = ":person_tone1_red_hair:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f9b3"] = ":person_tone1_white_hair:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f9b2"] = ":person_tone2_bald:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f9b1"] = ":person_tone2_curly_hair:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f9b0"] = ":person_tone2_red_hair:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f9b3"] = ":person_tone2_white_hair:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f9b2"] = ":person_tone3_bald:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f9b1"] = ":person_tone3_curly_hair:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f9b0"] = ":person_tone3_red_hair:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f9b3"] = ":person_tone3_white_hair:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f9b2"] = ":person_tone4_bald:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f9b1"] = ":person_tone4_curly_hair:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f9b0"] = ":person_tone4_red_hair:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f9b3"] = ":person_tone4_white_hair:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f9b2"] = ":person_tone5_bald:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f9b1"] = ":person_tone5_curly_hair:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f9b0"] = ":person_tone5_red_hair:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f9b3"] = ":person_tone5_white_hair:",
- ["\U0001f6b6"] = ":person_walking:",
- ["\U0001f6b6\U0001f3fb"] = ":person_walking_tone1:",
- ["\U0001f6b6\U0001f3fc"] = ":person_walking_tone2:",
- ["\U0001f6b6\U0001f3fd"] = ":person_walking_tone3:",
- ["\U0001f6b6\U0001f3fe"] = ":person_walking_tone4:",
- ["\U0001f6b6\U0001f3ff"] = ":person_walking_tone5:",
- ["\U0001f473"] = ":person_wearing_turban:",
- ["\U0001f473\U0001f3fb"] = ":person_wearing_turban_tone1:",
- ["\U0001f473\U0001f3fc"] = ":person_wearing_turban_tone2:",
- ["\U0001f473\U0001f3fd"] = ":person_wearing_turban_tone3:",
- ["\U0001f473\U0001f3fe"] = ":person_wearing_turban_tone4:",
- ["\U0001f473\U0001f3ff"] = ":person_wearing_turban_tone5:",
- ["\U0001f9d1\u200d\U0001f9b3"] = ":person_white_hair:",
- ["\U0001f9d1\u200d\U0001f9af"] = ":person_with_probing_cane:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f9af"] = ":person_with_probing_cane_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f9af"] = ":person_with_probing_cane_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f9af"] = ":person_with_probing_cane_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f9af"] = ":person_with_probing_cane_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f9af"] = ":person_with_probing_cane_tone5:",
- ["\U0001f470"] = ":person_with_veil:",
- ["\U0001f470\U0001f3fb"] = ":person_with_veil_tone1:",
- ["\U0001f470\U0001f3fc"] = ":person_with_veil_tone2:",
- ["\U0001f470\U0001f3fd"] = ":person_with_veil_tone3:",
- ["\U0001f470\U0001f3fe"] = ":person_with_veil_tone4:",
- ["\U0001f470\U0001f3ff"] = ":person_with_veil_tone5:",
- ["\U0001f9eb"] = ":petri_dish:",
- ["\u26cf\ufe0f"] = ":pick:",
- ["\u26cf"] = ":pick:",
- ["\U0001f6fb"] = ":pickup_truck:",
- ["\U0001f967"] = ":pie:",
- ["\U0001f437"] = ":pig:",
- ["\U0001f43d"] = ":pig_nose:",
- ["\U0001f416"] = ":pig2:",
- ["\U0001f48a"] = ":pill:",
- ["\U0001f9d1\u200d\u2708\ufe0f"] = ":pilot:",
- ["\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f"] = ":pilot_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f"] = ":pilot_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f"] = ":pilot_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f"] = ":pilot_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f"] = ":pilot_tone5:",
- ["\U0001fa85"] = ":piñata:",
- ["\U0001f90c"] = ":pinched_fingers:",
- ["\U0001f90c\U0001f3fb"] = ":pinched_fingers_tone1:",
- ["\U0001f90c\U0001f3fc"] = ":pinched_fingers_tone2:",
- ["\U0001f90c\U0001f3fd"] = ":pinched_fingers_tone3:",
- ["\U0001f90c\U0001f3fe"] = ":pinched_fingers_tone4:",
- ["\U0001f90c\U0001f3ff"] = ":pinched_fingers_tone5:",
- ["\U0001f90f"] = ":pinching_hand:",
- ["\U0001f90f\U0001f3fb"] = ":pinching_hand_tone1:",
- ["\U0001f90f\U0001f3fc"] = ":pinching_hand_tone2:",
- ["\U0001f90f\U0001f3fd"] = ":pinching_hand_tone3:",
- ["\U0001f90f\U0001f3fe"] = ":pinching_hand_tone4:",
- ["\U0001f90f\U0001f3ff"] = ":pinching_hand_tone5:",
- ["\U0001f34d"] = ":pineapple:",
- ["\U0001f3d3"] = ":ping_pong:",
- ["\U0001f3f4\u200d\u2620\ufe0f"] = ":pirate_flag:",
- ["\u2653"] = ":pisces:",
- ["\U0001f355"] = ":pizza:",
- ["\U0001faa7"] = ":placard:",
- ["\U0001f6d0"] = ":place_of_worship:",
- ["\u23ef\ufe0f"] = ":play_pause:",
- ["\u23ef"] = ":play_pause:",
- ["\U0001f97a"] = ":pleading_face:",
- ["\U0001faa0"] = ":plunger:",
- ["\U0001f447"] = ":point_down:",
- ["\U0001f447\U0001f3fb"] = ":point_down_tone1:",
- ["\U0001f447\U0001f3fc"] = ":point_down_tone2:",
- ["\U0001f447\U0001f3fd"] = ":point_down_tone3:",
- ["\U0001f447\U0001f3fe"] = ":point_down_tone4:",
- ["\U0001f447\U0001f3ff"] = ":point_down_tone5:",
- ["\U0001f448"] = ":point_left:",
- ["\U0001f448\U0001f3fb"] = ":point_left_tone1:",
- ["\U0001f448\U0001f3fc"] = ":point_left_tone2:",
- ["\U0001f448\U0001f3fd"] = ":point_left_tone3:",
- ["\U0001f448\U0001f3fe"] = ":point_left_tone4:",
- ["\U0001f448\U0001f3ff"] = ":point_left_tone5:",
- ["\U0001f449"] = ":point_right:",
- ["\U0001f449\U0001f3fb"] = ":point_right_tone1:",
- ["\U0001f449\U0001f3fc"] = ":point_right_tone2:",
- ["\U0001f449\U0001f3fd"] = ":point_right_tone3:",
- ["\U0001f449\U0001f3fe"] = ":point_right_tone4:",
- ["\U0001f449\U0001f3ff"] = ":point_right_tone5:",
- ["\u261d\ufe0f"] = ":point_up:",
- ["\u261d"] = ":point_up:",
- ["\U0001f446"] = ":point_up_2:",
- ["\U0001f446\U0001f3fb"] = ":point_up_2_tone1:",
- ["\U0001f446\U0001f3fc"] = ":point_up_2_tone2:",
- ["\U0001f446\U0001f3fd"] = ":point_up_2_tone3:",
- ["\U0001f446\U0001f3fe"] = ":point_up_2_tone4:",
- ["\U0001f446\U0001f3ff"] = ":point_up_2_tone5:",
- ["\u261d\U0001f3fb"] = ":point_up_tone1:",
- ["\u261d\U0001f3fc"] = ":point_up_tone2:",
- ["\u261d\U0001f3fd"] = ":point_up_tone3:",
- ["\u261d\U0001f3fe"] = ":point_up_tone4:",
- ["\u261d\U0001f3ff"] = ":point_up_tone5:",
- ["\U0001f43b\u200d\u2744\ufe0f"] = ":polar_bear:",
- ["\U0001f693"] = ":police_car:",
- ["\U0001f46e"] = ":police_officer:",
- ["\U0001f46e\U0001f3fb"] = ":police_officer_tone1:",
- ["\U0001f46e\U0001f3fc"] = ":police_officer_tone2:",
- ["\U0001f46e\U0001f3fd"] = ":police_officer_tone3:",
- ["\U0001f46e\U0001f3fe"] = ":police_officer_tone4:",
- ["\U0001f46e\U0001f3ff"] = ":police_officer_tone5:",
- ["\U0001f429"] = ":poodle:",
- ["\U0001f4a9"] = ":poop:",
- ["\U0001f37f"] = ":popcorn:",
- ["\U0001f3e3"] = ":post_office:",
- ["\U0001f4ef"] = ":postal_horn:",
- ["\U0001f4ee"] = ":postbox:",
- ["\U0001f6b0"] = ":potable_water:",
- ["\U0001f954"] = ":potato:",
- ["\U0001fab4"] = ":potted_plant:",
- ["\U0001f45d"] = ":pouch:",
- ["\U0001f357"] = ":poultry_leg:",
- ["\U0001f4b7"] = ":pound:",
- ["\U0001f63e"] = ":pouting_cat:",
- ["\U0001f64f"] = ":pray:",
- ["\U0001f64f\U0001f3fb"] = ":pray_tone1:",
- ["\U0001f64f\U0001f3fc"] = ":pray_tone2:",
- ["\U0001f64f\U0001f3fd"] = ":pray_tone3:",
- ["\U0001f64f\U0001f3fe"] = ":pray_tone4:",
- ["\U0001f64f\U0001f3ff"] = ":pray_tone5:",
- ["\U0001f4ff"] = ":prayer_beads:",
- ["\U0001f930"] = ":pregnant_woman:",
- ["\U0001f930\U0001f3fb"] = ":pregnant_woman_tone1:",
- ["\U0001f930\U0001f3fc"] = ":pregnant_woman_tone2:",
- ["\U0001f930\U0001f3fd"] = ":pregnant_woman_tone3:",
- ["\U0001f930\U0001f3fe"] = ":pregnant_woman_tone4:",
- ["\U0001f930\U0001f3ff"] = ":pregnant_woman_tone5:",
- ["\U0001f968"] = ":pretzel:",
- ["\U0001f934"] = ":prince:",
- ["\U0001f934\U0001f3fb"] = ":prince_tone1:",
- ["\U0001f934\U0001f3fc"] = ":prince_tone2:",
- ["\U0001f934\U0001f3fd"] = ":prince_tone3:",
- ["\U0001f934\U0001f3fe"] = ":prince_tone4:",
- ["\U0001f934\U0001f3ff"] = ":prince_tone5:",
- ["\U0001f478"] = ":princess:",
- ["\U0001f478\U0001f3fb"] = ":princess_tone1:",
- ["\U0001f478\U0001f3fc"] = ":princess_tone2:",
- ["\U0001f478\U0001f3fd"] = ":princess_tone3:",
- ["\U0001f478\U0001f3fe"] = ":princess_tone4:",
- ["\U0001f478\U0001f3ff"] = ":princess_tone5:",
- ["\U0001f5a8\ufe0f"] = ":printer:",
- ["\U0001f5a8"] = ":printer:",
- ["\U0001f9af"] = ":probing_cane:",
- ["\U0001f4fd\ufe0f"] = ":projector:",
- ["\U0001f4fd"] = ":projector:",
- ["\U0001f44a"] = ":punch:",
- ["\U0001f44a\U0001f3fb"] = ":punch_tone1:",
- ["\U0001f44a\U0001f3fc"] = ":punch_tone2:",
- ["\U0001f44a\U0001f3fd"] = ":punch_tone3:",
- ["\U0001f44a\U0001f3fe"] = ":punch_tone4:",
- ["\U0001f44a\U0001f3ff"] = ":punch_tone5:",
- ["\U0001f7e3"] = ":purple_circle:",
- ["\U0001f49c"] = ":purple_heart:",
- ["\U0001f7ea"] = ":purple_square:",
- ["\U0001f45b"] = ":purse:",
- ["\U0001f4cc"] = ":pushpin:",
- ["\U0001f6ae"] = ":put_litter_in_its_place:",
- ["\u2753"] = ":question:",
- ["\U0001f430"] = ":rabbit:",
- ["\U0001f407"] = ":rabbit2:",
- ["\U0001f99d"] = ":raccoon:",
- ["\U0001f3ce\ufe0f"] = ":race_car:",
- ["\U0001f3ce"] = ":race_car:",
- ["\U0001f40e"] = ":racehorse:",
- ["\U0001f4fb"] = ":radio:",
- ["\U0001f518"] = ":radio_button:",
- ["\u2622\ufe0f"] = ":radioactive:",
- ["\u2622"] = ":radioactive:",
- ["\U0001f621"] = ":rage:",
- ["\U0001f683"] = ":railway_car:",
- ["\U0001f6e4\ufe0f"] = ":railway_track:",
- ["\U0001f6e4"] = ":railway_track:",
- ["\U0001f308"] = ":rainbow:",
- ["\U0001f3f3\ufe0f\u200d\U0001f308"] = ":rainbow_flag:",
- ["\U0001f91a"] = ":raised_back_of_hand:",
- ["\U0001f91a\U0001f3fb"] = ":raised_back_of_hand_tone1:",
- ["\U0001f91a\U0001f3fc"] = ":raised_back_of_hand_tone2:",
- ["\U0001f91a\U0001f3fd"] = ":raised_back_of_hand_tone3:",
- ["\U0001f91a\U0001f3fe"] = ":raised_back_of_hand_tone4:",
- ["\U0001f91a\U0001f3ff"] = ":raised_back_of_hand_tone5:",
- ["\u270b"] = ":raised_hand:",
- ["\u270b\U0001f3fb"] = ":raised_hand_tone1:",
- ["\u270b\U0001f3fc"] = ":raised_hand_tone2:",
- ["\u270b\U0001f3fd"] = ":raised_hand_tone3:",
- ["\u270b\U0001f3fe"] = ":raised_hand_tone4:",
- ["\u270b\U0001f3ff"] = ":raised_hand_tone5:",
- ["\U0001f64c"] = ":raised_hands:",
- ["\U0001f64c\U0001f3fb"] = ":raised_hands_tone1:",
- ["\U0001f64c\U0001f3fc"] = ":raised_hands_tone2:",
- ["\U0001f64c\U0001f3fd"] = ":raised_hands_tone3:",
- ["\U0001f64c\U0001f3fe"] = ":raised_hands_tone4:",
- ["\U0001f64c\U0001f3ff"] = ":raised_hands_tone5:",
- ["\U0001f40f"] = ":ram:",
- ["\U0001f35c"] = ":ramen:",
- ["\U0001f400"] = ":rat:",
- ["\U0001fa92"] = ":razor:",
- ["\U0001f9fe"] = ":receipt:",
- ["\u23fa\ufe0f"] = ":record_button:",
- ["\u23fa"] = ":record_button:",
- ["\u267b\ufe0f"] = ":recycle:",
- ["\u267b"] = ":recycle:",
- ["\U0001f697"] = ":red_car:",
- ["\U0001f534"] = ":red_circle:",
- ["\U0001f9e7"] = ":red_envelope:",
- ["\U0001f7e5"] = ":red_square:",
- ["\U0001f1e6"] = ":regional_indicator_a:",
- ["\U0001f1e7"] = ":regional_indicator_b:",
- ["\U0001f1e8"] = ":regional_indicator_c:",
- ["\U0001f1e9"] = ":regional_indicator_d:",
- ["\U0001f1ea"] = ":regional_indicator_e:",
- ["\U0001f1eb"] = ":regional_indicator_f:",
- ["\U0001f1ec"] = ":regional_indicator_g:",
- ["\U0001f1ed"] = ":regional_indicator_h:",
- ["\U0001f1ee"] = ":regional_indicator_i:",
- ["\U0001f1ef"] = ":regional_indicator_j:",
- ["\U0001f1f0"] = ":regional_indicator_k:",
- ["\U0001f1f1"] = ":regional_indicator_l:",
- ["\U0001f1f2"] = ":regional_indicator_m:",
- ["\U0001f1f3"] = ":regional_indicator_n:",
- ["\U0001f1f4"] = ":regional_indicator_o:",
- ["\U0001f1f5"] = ":regional_indicator_p:",
- ["\U0001f1f6"] = ":regional_indicator_q:",
- ["\U0001f1f7"] = ":regional_indicator_r:",
- ["\U0001f1f8"] = ":regional_indicator_s:",
- ["\U0001f1f9"] = ":regional_indicator_t:",
- ["\U0001f1fa"] = ":regional_indicator_u:",
- ["\U0001f1fb"] = ":regional_indicator_v:",
- ["\U0001f1fc"] = ":regional_indicator_w:",
- ["\U0001f1fd"] = ":regional_indicator_x:",
- ["\U0001f1fe"] = ":regional_indicator_y:",
- ["\U0001f1ff"] = ":regional_indicator_z:",
- ["\u00ae\ufe0f"] = ":registered:",
- ["\u00ae"] = ":registered:",
- ["\u263a\ufe0f"] = ":relaxed:",
- ["\u263a"] = ":relaxed:",
- ["\U0001f60c"] = ":relieved:",
- ["\U0001f397\ufe0f"] = ":reminder_ribbon:",
- ["\U0001f397"] = ":reminder_ribbon:",
- ["\U0001f501"] = ":repeat:",
- ["\U0001f502"] = ":repeat_one:",
- ["\U0001f6bb"] = ":restroom:",
- ["\U0001f49e"] = ":revolving_hearts:",
- ["\u23ea"] = ":rewind:",
- ["\U0001f98f"] = ":rhino:",
- ["\U0001f380"] = ":ribbon:",
- ["\U0001f35a"] = ":rice:",
- ["\U0001f359"] = ":rice_ball:",
- ["\U0001f358"] = ":rice_cracker:",
- ["\U0001f391"] = ":rice_scene:",
- ["\U0001f91c"] = ":right_facing_fist:",
- ["\U0001f91c\U0001f3fb"] = ":right_facing_fist_tone1:",
- ["\U0001f91c\U0001f3fc"] = ":right_facing_fist_tone2:",
- ["\U0001f91c\U0001f3fd"] = ":right_facing_fist_tone3:",
- ["\U0001f91c\U0001f3fe"] = ":right_facing_fist_tone4:",
- ["\U0001f91c\U0001f3ff"] = ":right_facing_fist_tone5:",
- ["\U0001f48d"] = ":ring:",
- ["\U0001fa90"] = ":ringed_planet:",
- ["\U0001f916"] = ":robot:",
- ["\U0001faa8"] = ":rock:",
- ["\U0001f680"] = ":rocket:",
- ["\U0001f923"] = ":rofl:",
- ["\U0001f9fb"] = ":roll_of_paper:",
- ["\U0001f3a2"] = ":roller_coaster:",
- ["\U0001f6fc"] = ":roller_skate:",
- ["\U0001f644"] = ":rolling_eyes:",
- ["\U0001f413"] = ":rooster:",
- ["\U0001f339"] = ":rose:",
- ["\U0001f3f5\ufe0f"] = ":rosette:",
- ["\U0001f3f5"] = ":rosette:",
- ["\U0001f6a8"] = ":rotating_light:",
- ["\U0001f4cd"] = ":round_pushpin:",
- ["\U0001f3c9"] = ":rugby_football:",
- ["\U0001f3bd"] = ":running_shirt_with_sash:",
- ["\U0001f202\ufe0f"] = ":sa:",
- ["\U0001f202"] = ":sa:",
- ["\U0001f9f7"] = ":safety_pin:",
- ["\U0001f9ba"] = ":safety_vest:",
- ["\u2650"] = ":sagittarius:",
- ["\u26f5"] = ":sailboat:",
- ["\U0001f376"] = ":sake:",
- ["\U0001f957"] = ":salad:",
- ["\U0001f9c2"] = ":salt:",
- ["\U0001f461"] = ":sandal:",
- ["\U0001f96a"] = ":sandwich:",
- ["\U0001f385"] = ":santa:",
- ["\U0001f385\U0001f3fb"] = ":santa_tone1:",
- ["\U0001f385\U0001f3fc"] = ":santa_tone2:",
- ["\U0001f385\U0001f3fd"] = ":santa_tone3:",
- ["\U0001f385\U0001f3fe"] = ":santa_tone4:",
- ["\U0001f385\U0001f3ff"] = ":santa_tone5:",
- ["\U0001f97b"] = ":sari:",
- ["\U0001f4e1"] = ":satellite:",
- ["\U0001f6f0\ufe0f"] = ":satellite_orbital:",
- ["\U0001f6f0"] = ":satellite_orbital:",
- ["\U0001f995"] = ":sauropod:",
- ["\U0001f3b7"] = ":saxophone:",
- ["\u2696\ufe0f"] = ":scales:",
- ["\u2696"] = ":scales:",
- ["\U0001f9e3"] = ":scarf:",
- ["\U0001f3eb"] = ":school:",
- ["\U0001f392"] = ":school_satchel:",
- ["\U0001f9d1\u200d\U0001f52c"] = ":scientist:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f52c"] = ":scientist_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f52c"] = ":scientist_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f52c"] = ":scientist_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f52c"] = ":scientist_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f52c"] = ":scientist_tone5:",
- ["\u2702\ufe0f"] = ":scissors:",
- ["\u2702"] = ":scissors:",
- ["\U0001f6f4"] = ":scooter:",
- ["\U0001f982"] = ":scorpion:",
- ["\u264f"] = ":scorpius:",
- ["\U0001f3f4\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f"] = ":scotland:",
- ["\U0001f631"] = ":scream:",
- ["\U0001f640"] = ":scream_cat:",
- ["\U0001fa9b"] = ":screwdriver:",
- ["\U0001f4dc"] = ":scroll:",
- ["\U0001f9ad"] = ":seal:",
- ["\U0001f4ba"] = ":seat:",
- ["\U0001f948"] = ":second_place:",
- ["\u3299\ufe0f"] = ":secret:",
- ["\u3299"] = ":secret:",
- ["\U0001f648"] = ":see_no_evil:",
- ["\U0001f331"] = ":seedling:",
- ["\U0001f933"] = ":selfie:",
- ["\U0001f933\U0001f3fb"] = ":selfie_tone1:",
- ["\U0001f933\U0001f3fc"] = ":selfie_tone2:",
- ["\U0001f933\U0001f3fd"] = ":selfie_tone3:",
- ["\U0001f933\U0001f3fe"] = ":selfie_tone4:",
- ["\U0001f933\U0001f3ff"] = ":selfie_tone5:",
- ["\U0001f415\u200d\U0001f9ba"] = ":service_dog:",
- ["\u0037\ufe0f\u20e3"] = ":seven:",
- ["\u0037\u20e3"] = ":seven:",
- ["\U0001faa1"] = ":sewing_needle:",
- ["\U0001f958"] = ":shallow_pan_of_food:",
- ["\u2618\ufe0f"] = ":shamrock:",
- ["\u2618"] = ":shamrock:",
- ["\U0001f988"] = ":shark:",
- ["\U0001f367"] = ":shaved_ice:",
- ["\U0001f411"] = ":sheep:",
- ["\U0001f41a"] = ":shell:",
- ["\U0001f6e1\ufe0f"] = ":shield:",
- ["\U0001f6e1"] = ":shield:",
- ["\u26e9\ufe0f"] = ":shinto_shrine:",
- ["\u26e9"] = ":shinto_shrine:",
- ["\U0001f6a2"] = ":ship:",
- ["\U0001f455"] = ":shirt:",
- ["\U0001f6cd\ufe0f"] = ":shopping_bags:",
- ["\U0001f6cd"] = ":shopping_bags:",
- ["\U0001f6d2"] = ":shopping_cart:",
- ["\U0001fa73"] = ":shorts:",
- ["\U0001f6bf"] = ":shower:",
- ["\U0001f990"] = ":shrimp:",
- ["\U0001f92b"] = ":shushing_face:",
- ["\U0001f4f6"] = ":signal_strength:",
- ["\U0001f9d1\u200d\U0001f3a4"] = ":singer:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f3a4"] = ":singer_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f3a4"] = ":singer_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f3a4"] = ":singer_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f3a4"] = ":singer_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f3a4"] = ":singer_tone5:",
- ["\u0036\ufe0f\u20e3"] = ":six:",
- ["\u0036\u20e3"] = ":six:",
- ["\U0001f52f"] = ":six_pointed_star:",
- ["\U0001f6f9"] = ":skateboard:",
- ["\U0001f3bf"] = ":ski:",
- ["\u26f7\ufe0f"] = ":skier:",
- ["\u26f7"] = ":skier:",
- ["\U0001f480"] = ":skull:",
- ["\u2620\ufe0f"] = ":skull_crossbones:",
- ["\u2620"] = ":skull_crossbones:",
- ["\U0001f9a8"] = ":skunk:",
- ["\U0001f6f7"] = ":sled:",
- ["\U0001f634"] = ":sleeping:",
- ["\U0001f6cc"] = ":sleeping_accommodation:",
- ["\U0001f62a"] = ":sleepy:",
- ["\U0001f641"] = ":slight_frown:",
- ["\U0001f642"] = ":slight_smile:",
- ["\U0001f3b0"] = ":slot_machine:",
- ["\U0001f9a5"] = ":sloth:",
- ["\U0001f539"] = ":small_blue_diamond:",
- ["\U0001f538"] = ":small_orange_diamond:",
- ["\U0001f53a"] = ":small_red_triangle:",
- ["\U0001f53b"] = ":small_red_triangle_down:",
- ["\U0001f604"] = ":smile:",
- ["\U0001f638"] = ":smile_cat:",
- ["\U0001f603"] = ":smiley:",
- ["\U0001f63a"] = ":smiley_cat:",
- ["\U0001f970"] = ":smiling_face_with_3_hearts:",
- ["\U0001f972"] = ":smiling_face_with_tear:",
- ["\U0001f608"] = ":smiling_imp:",
- ["\U0001f60f"] = ":smirk:",
- ["\U0001f63c"] = ":smirk_cat:",
- ["\U0001f6ac"] = ":smoking:",
- ["\U0001f40c"] = ":snail:",
- ["\U0001f40d"] = ":snake:",
- ["\U0001f927"] = ":sneezing_face:",
- ["\U0001f3c2"] = ":snowboarder:",
- ["\U0001f3c2\U0001f3fb"] = ":snowboarder_tone1:",
- ["\U0001f3c2\U0001f3fc"] = ":snowboarder_tone2:",
- ["\U0001f3c2\U0001f3fd"] = ":snowboarder_tone3:",
- ["\U0001f3c2\U0001f3fe"] = ":snowboarder_tone4:",
- ["\U0001f3c2\U0001f3ff"] = ":snowboarder_tone5:",
- ["\u2744\ufe0f"] = ":snowflake:",
- ["\u2744"] = ":snowflake:",
- ["\u26c4"] = ":snowman:",
- ["\u2603\ufe0f"] = ":snowman2:",
- ["\u2603"] = ":snowman2:",
- ["\U0001f9fc"] = ":soap:",
- ["\U0001f62d"] = ":sob:",
- ["\u26bd"] = ":soccer:",
- ["\U0001f9e6"] = ":socks:",
- ["\U0001f94e"] = ":softball:",
- ["\U0001f51c"] = ":soon:",
- ["\U0001f198"] = ":sos:",
- ["\U0001f509"] = ":sound:",
- ["\U0001f47e"] = ":space_invader:",
- ["\u2660\ufe0f"] = ":spades:",
- ["\u2660"] = ":spades:",
- ["\U0001f35d"] = ":spaghetti:",
- ["\u2747\ufe0f"] = ":sparkle:",
- ["\u2747"] = ":sparkle:",
- ["\U0001f387"] = ":sparkler:",
- ["\u2728"] = ":sparkles:",
- ["\U0001f496"] = ":sparkling_heart:",
- ["\U0001f64a"] = ":speak_no_evil:",
- ["\U0001f508"] = ":speaker:",
- ["\U0001f5e3\ufe0f"] = ":speaking_head:",
- ["\U0001f5e3"] = ":speaking_head:",
- ["\U0001f4ac"] = ":speech_balloon:",
- ["\U0001f5e8\ufe0f"] = ":speech_left:",
- ["\U0001f5e8"] = ":speech_left:",
- ["\U0001f6a4"] = ":speedboat:",
- ["\U0001f577\ufe0f"] = ":spider:",
- ["\U0001f577"] = ":spider:",
- ["\U0001f578\ufe0f"] = ":spider_web:",
- ["\U0001f578"] = ":spider_web:",
- ["\U0001f9fd"] = ":sponge:",
- ["\U0001f944"] = ":spoon:",
- ["\U0001f9f4"] = ":squeeze_bottle:",
- ["\U0001f991"] = ":squid:",
- ["\U0001f3df\ufe0f"] = ":stadium:",
- ["\U0001f3df"] = ":stadium:",
- ["\u2b50"] = ":star:",
- ["\u262a\ufe0f"] = ":star_and_crescent:",
- ["\u262a"] = ":star_and_crescent:",
- ["\u2721\ufe0f"] = ":star_of_david:",
- ["\u2721"] = ":star_of_david:",
- ["\U0001f929"] = ":star_struck:",
- ["\U0001f31f"] = ":star2:",
- ["\U0001f320"] = ":stars:",
- ["\U0001f689"] = ":station:",
- ["\U0001f5fd"] = ":statue_of_liberty:",
- ["\U0001f682"] = ":steam_locomotive:",
- ["\U0001fa7a"] = ":stethoscope:",
- ["\U0001f372"] = ":stew:",
- ["\u23f9\ufe0f"] = ":stop_button:",
- ["\u23f9"] = ":stop_button:",
- ["\u23f1\ufe0f"] = ":stopwatch:",
- ["\u23f1"] = ":stopwatch:",
- ["\U0001f4cf"] = ":straight_ruler:",
- ["\U0001f353"] = ":strawberry:",
- ["\U0001f61b"] = ":stuck_out_tongue:",
- ["\U0001f61d"] = ":stuck_out_tongue_closed_eyes:",
- ["\U0001f61c"] = ":stuck_out_tongue_winking_eye:",
- ["\U0001f9d1\u200d\U0001f393"] = ":student:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f393"] = ":student_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f393"] = ":student_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f393"] = ":student_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f393"] = ":student_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f393"] = ":student_tone5:",
- ["\U0001f959"] = ":stuffed_flatbread:",
- ["\U0001f31e"] = ":sun_with_face:",
- ["\U0001f33b"] = ":sunflower:",
- ["\U0001f60e"] = ":sunglasses:",
- ["\u2600\ufe0f"] = ":sunny:",
- ["\u2600"] = ":sunny:",
- ["\U0001f305"] = ":sunrise:",
- ["\U0001f304"] = ":sunrise_over_mountains:",
- ["\U0001f9b8"] = ":superhero:",
- ["\U0001f9b8\U0001f3fb"] = ":superhero_tone1:",
- ["\U0001f9b8\U0001f3fc"] = ":superhero_tone2:",
- ["\U0001f9b8\U0001f3fd"] = ":superhero_tone3:",
- ["\U0001f9b8\U0001f3fe"] = ":superhero_tone4:",
- ["\U0001f9b8\U0001f3ff"] = ":superhero_tone5:",
- ["\U0001f9b9"] = ":supervillain:",
- ["\U0001f9b9\U0001f3fb"] = ":supervillain_tone1:",
- ["\U0001f9b9\U0001f3fc"] = ":supervillain_tone2:",
- ["\U0001f9b9\U0001f3fd"] = ":supervillain_tone3:",
- ["\U0001f9b9\U0001f3fe"] = ":supervillain_tone4:",
- ["\U0001f9b9\U0001f3ff"] = ":supervillain_tone5:",
- ["\U0001f363"] = ":sushi:",
- ["\U0001f69f"] = ":suspension_railway:",
- ["\U0001f9a2"] = ":swan:",
- ["\U0001f613"] = ":sweat:",
- ["\U0001f4a6"] = ":sweat_drops:",
- ["\U0001f605"] = ":sweat_smile:",
- ["\U0001f360"] = ":sweet_potato:",
- ["\U0001f523"] = ":symbols:",
- ["\U0001f54d"] = ":synagogue:",
- ["\U0001f489"] = ":syringe:",
- ["\U0001f996"] = ":t_rex:",
- ["\U0001f32e"] = ":taco:",
- ["\U0001f389"] = ":tada:",
- ["\U0001f961"] = ":takeout_box:",
- ["\U0001fad4"] = ":tamale:",
- ["\U0001f38b"] = ":tanabata_tree:",
- ["\U0001f34a"] = ":tangerine:",
- ["\u2649"] = ":taurus:",
- ["\U0001f695"] = ":taxi:",
- ["\U0001f375"] = ":tea:",
- ["\U0001f9d1\u200d\U0001f3eb"] = ":teacher:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f3eb"] = ":teacher_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f3eb"] = ":teacher_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f3eb"] = ":teacher_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f3eb"] = ":teacher_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f3eb"] = ":teacher_tone5:",
- ["\U0001fad6"] = ":teapot:",
- ["\U0001f9d1\u200d\U0001f4bb"] = ":technologist:",
- ["\U0001f9d1\U0001f3fb\u200d\U0001f4bb"] = ":technologist_tone1:",
- ["\U0001f9d1\U0001f3fc\u200d\U0001f4bb"] = ":technologist_tone2:",
- ["\U0001f9d1\U0001f3fd\u200d\U0001f4bb"] = ":technologist_tone3:",
- ["\U0001f9d1\U0001f3fe\u200d\U0001f4bb"] = ":technologist_tone4:",
- ["\U0001f9d1\U0001f3ff\u200d\U0001f4bb"] = ":technologist_tone5:",
- ["\U0001f9f8"] = ":teddy_bear:",
- ["\u260e\ufe0f"] = ":telephone:",
- ["\u260e"] = ":telephone:",
- ["\U0001f4de"] = ":telephone_receiver:",
- ["\U0001f52d"] = ":telescope:",
- ["\U0001f3be"] = ":tennis:",
- ["\u26fa"] = ":tent:",
- ["\U0001f9ea"] = ":test_tube:",
- ["\U0001f321\ufe0f"] = ":thermometer:",
- ["\U0001f321"] = ":thermometer:",
- ["\U0001f912"] = ":thermometer_face:",
- ["\U0001f914"] = ":thinking:",
- ["\U0001f949"] = ":third_place:",
- ["\U0001fa74"] = ":thong_sandal:",
- ["\U0001f4ad"] = ":thought_balloon:",
- ["\U0001f9f5"] = ":thread:",
- ["\u0033\ufe0f\u20e3"] = ":three:",
- ["\u0033\u20e3"] = ":three:",
- ["\U0001f44e"] = ":thumbsdown:",
- ["\U0001f44e\U0001f3fb"] = ":thumbsdown_tone1:",
- ["\U0001f44e\U0001f3fc"] = ":thumbsdown_tone2:",
- ["\U0001f44e\U0001f3fd"] = ":thumbsdown_tone3:",
- ["\U0001f44e\U0001f3fe"] = ":thumbsdown_tone4:",
- ["\U0001f44e\U0001f3ff"] = ":thumbsdown_tone5:",
- ["\U0001f44d"] = ":thumbsup:",
- ["\U0001f44d\U0001f3fb"] = ":thumbsup_tone1:",
- ["\U0001f44d\U0001f3fc"] = ":thumbsup_tone2:",
- ["\U0001f44d\U0001f3fd"] = ":thumbsup_tone3:",
- ["\U0001f44d\U0001f3fe"] = ":thumbsup_tone4:",
- ["\U0001f44d\U0001f3ff"] = ":thumbsup_tone5:",
- ["\u26c8\ufe0f"] = ":thunder_cloud_rain:",
- ["\u26c8"] = ":thunder_cloud_rain:",
- ["\U0001f3ab"] = ":ticket:",
- ["\U0001f39f\ufe0f"] = ":tickets:",
- ["\U0001f39f"] = ":tickets:",
- ["\U0001f42f"] = ":tiger:",
- ["\U0001f405"] = ":tiger2:",
- ["\u23f2\ufe0f"] = ":timer:",
- ["\u23f2"] = ":timer:",
- ["\U0001f62b"] = ":tired_face:",
- ["\u2122\ufe0f"] = ":tm:",
- ["\u2122"] = ":tm:",
- ["\U0001f6bd"] = ":toilet:",
- ["\U0001f5fc"] = ":tokyo_tower:",
- ["\U0001f345"] = ":tomato:",
- ["\U0001f445"] = ":tongue:",
- ["\U0001f9f0"] = ":toolbox:",
- ["\U0001f6e0\ufe0f"] = ":tools:",
- ["\U0001f6e0"] = ":tools:",
- ["\U0001f9b7"] = ":tooth:",
- ["\U0001faa5"] = ":toothbrush:",
- ["\U0001f51d"] = ":top:",
- ["\U0001f3a9"] = ":tophat:",
- ["\u23ed\ufe0f"] = ":track_next:",
- ["\u23ed"] = ":track_next:",
- ["\u23ee\ufe0f"] = ":track_previous:",
- ["\u23ee"] = ":track_previous:",
- ["\U0001f5b2\ufe0f"] = ":trackball:",
- ["\U0001f5b2"] = ":trackball:",
- ["\U0001f69c"] = ":tractor:",
- ["\U0001f6a5"] = ":traffic_light:",
- ["\U0001f68b"] = ":train:",
- ["\U0001f686"] = ":train2:",
- ["\U0001f68a"] = ":tram:",
- ["\U0001f3f3\ufe0f\u200d\u26a7\ufe0f"] = ":transgender_flag:",
- ["\u26a7"] = ":transgender_symbol:",
- ["\U0001f6a9"] = ":triangular_flag_on_post:",
- ["\U0001f4d0"] = ":triangular_ruler:",
- ["\U0001f531"] = ":trident:",
- ["\U0001f624"] = ":triumph:",
- ["\U0001f68e"] = ":trolleybus:",
- ["\U0001f3c6"] = ":trophy:",
- ["\U0001f379"] = ":tropical_drink:",
- ["\U0001f420"] = ":tropical_fish:",
- ["\U0001f69a"] = ":truck:",
- ["\U0001f3ba"] = ":trumpet:",
- ["\U0001f337"] = ":tulip:",
- ["\U0001f943"] = ":tumbler_glass:",
- ["\U0001f983"] = ":turkey:",
- ["\U0001f422"] = ":turtle:",
- ["\U0001f4fa"] = ":tv:",
- ["\U0001f500"] = ":twisted_rightwards_arrows:",
- ["\u0032\ufe0f\u20e3"] = ":two:",
- ["\u0032\u20e3"] = ":two:",
- ["\U0001f495"] = ":two_hearts:",
- ["\U0001f46c"] = ":two_men_holding_hands:",
- ["\U0001f239"] = ":u5272:",
- ["\U0001f234"] = ":u5408:",
- ["\U0001f23a"] = ":u55b6:",
- ["\U0001f22f"] = ":u6307:",
- ["\U0001f237\ufe0f"] = ":u6708:",
- ["\U0001f237"] = ":u6708:",
- ["\U0001f236"] = ":u6709:",
- ["\U0001f235"] = ":u6e80:",
- ["\U0001f21a"] = ":u7121:",
- ["\U0001f238"] = ":u7533:",
- ["\U0001f232"] = ":u7981:",
- ["\U0001f233"] = ":u7a7a:",
- ["\u2614"] = ":umbrella:",
- ["\u2602\ufe0f"] = ":umbrella2:",
- ["\u2602"] = ":umbrella2:",
- ["\U0001f612"] = ":unamused:",
- ["\U0001f51e"] = ":underage:",
- ["\U0001f984"] = ":unicorn:",
- ["\U0001f1fa\U0001f1f3"] = ":united_nations:",
- ["\U0001f513"] = ":unlock:",
- ["\U0001f199"] = ":up:",
- ["\U0001f643"] = ":upside_down:",
- ["\u26b1\ufe0f"] = ":urn:",
- ["\u26b1"] = ":urn:",
- ["\u270c\ufe0f"] = ":v:",
- ["\u270c"] = ":v:",
- ["\u270c\U0001f3fb"] = ":v_tone1:",
- ["\u270c\U0001f3fc"] = ":v_tone2:",
- ["\u270c\U0001f3fd"] = ":v_tone3:",
- ["\u270c\U0001f3fe"] = ":v_tone4:",
- ["\u270c\U0001f3ff"] = ":v_tone5:",
- ["\U0001f9db"] = ":vampire:",
- ["\U0001f9db\U0001f3fb"] = ":vampire_tone1:",
- ["\U0001f9db\U0001f3fc"] = ":vampire_tone2:",
- ["\U0001f9db\U0001f3fd"] = ":vampire_tone3:",
- ["\U0001f9db\U0001f3fe"] = ":vampire_tone4:",
- ["\U0001f9db\U0001f3ff"] = ":vampire_tone5:",
- ["\U0001f6a6"] = ":vertical_traffic_light:",
- ["\U0001f4fc"] = ":vhs:",
- ["\U0001f4f3"] = ":vibration_mode:",
- ["\U0001f4f9"] = ":video_camera:",
- ["\U0001f3ae"] = ":video_game:",
- ["\U0001f3bb"] = ":violin:",
- ["\u264d"] = ":virgo:",
- ["\U0001f30b"] = ":volcano:",
- ["\U0001f3d0"] = ":volleyball:",
- ["\U0001f19a"] = ":vs:",
- ["\U0001f596"] = ":vulcan:",
- ["\U0001f596\U0001f3fb"] = ":vulcan_tone1:",
- ["\U0001f596\U0001f3fc"] = ":vulcan_tone2:",
- ["\U0001f596\U0001f3fd"] = ":vulcan_tone3:",
- ["\U0001f596\U0001f3fe"] = ":vulcan_tone4:",
- ["\U0001f596\U0001f3ff"] = ":vulcan_tone5:",
- ["\U0001f9c7"] = ":waffle:",
- ["\U0001f3f4\U000e0067\U000e0062\U000e0077\U000e006c\U000e0073\U000e007f"] = ":wales:",
- ["\U0001f318"] = ":waning_crescent_moon:",
- ["\U0001f316"] = ":waning_gibbous_moon:",
- ["\u26a0\ufe0f"] = ":warning:",
- ["\u26a0"] = ":warning:",
- ["\U0001f5d1\ufe0f"] = ":wastebasket:",
- ["\U0001f5d1"] = ":wastebasket:",
- ["\u231a"] = ":watch:",
- ["\U0001f403"] = ":water_buffalo:",
- ["\U0001f349"] = ":watermelon:",
- ["\U0001f44b"] = ":wave:",
- ["\U0001f44b\U0001f3fb"] = ":wave_tone1:",
- ["\U0001f44b\U0001f3fc"] = ":wave_tone2:",
- ["\U0001f44b\U0001f3fd"] = ":wave_tone3:",
- ["\U0001f44b\U0001f3fe"] = ":wave_tone4:",
- ["\U0001f44b\U0001f3ff"] = ":wave_tone5:",
- ["\u3030\ufe0f"] = ":wavy_dash:",
- ["\u3030"] = ":wavy_dash:",
- ["\U0001f312"] = ":waxing_crescent_moon:",
- ["\U0001f314"] = ":waxing_gibbous_moon:",
- ["\U0001f6be"] = ":wc:",
- ["\U0001f629"] = ":weary:",
- ["\U0001f492"] = ":wedding:",
- ["\U0001f433"] = ":whale:",
- ["\U0001f40b"] = ":whale2:",
- ["\u2638\ufe0f"] = ":wheel_of_dharma:",
- ["\u2638"] = ":wheel_of_dharma:",
- ["\u267f"] = ":wheelchair:",
- ["\u2705"] = ":white_check_mark:",
- ["\u26aa"] = ":white_circle:",
- ["\U0001f4ae"] = ":white_flower:",
- ["\U0001f90d"] = ":white_heart:",
- ["\u2b1c"] = ":white_large_square:",
- ["\u25fd"] = ":white_medium_small_square:",
- ["\u25fb\ufe0f"] = ":white_medium_square:",
- ["\u25fb"] = ":white_medium_square:",
- ["\u25ab\ufe0f"] = ":white_small_square:",
- ["\u25ab"] = ":white_small_square:",
- ["\U0001f533"] = ":white_square_button:",
- ["\U0001f325\ufe0f"] = ":white_sun_cloud:",
- ["\U0001f325"] = ":white_sun_cloud:",
- ["\U0001f326\ufe0f"] = ":white_sun_rain_cloud:",
- ["\U0001f326"] = ":white_sun_rain_cloud:",
- ["\U0001f324\ufe0f"] = ":white_sun_small_cloud:",
- ["\U0001f324"] = ":white_sun_small_cloud:",
- ["\U0001f940"] = ":wilted_rose:",
- ["\U0001f32c\ufe0f"] = ":wind_blowing_face:",
- ["\U0001f32c"] = ":wind_blowing_face:",
- ["\U0001f390"] = ":wind_chime:",
- ["\U0001fa9f"] = ":window:",
- ["\U0001f377"] = ":wine_glass:",
- ["\U0001f609"] = ":wink:",
- ["\U0001f43a"] = ":wolf:",
- ["\U0001f469"] = ":woman:",
- ["\U0001f46b"] = ":woman_and_man_holding_hands_tone5_tone4:",
- ["\U0001f469\u200d\U0001f3a8"] = ":woman_artist:",
- ["\U0001f469\U0001f3fb\u200d\U0001f3a8"] = ":woman_artist_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f3a8"] = ":woman_artist_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f3a8"] = ":woman_artist_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f3a8"] = ":woman_artist_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f3a8"] = ":woman_artist_tone5:",
- ["\U0001f469\u200d\U0001f680"] = ":woman_astronaut:",
- ["\U0001f469\U0001f3fb\u200d\U0001f680"] = ":woman_astronaut_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f680"] = ":woman_astronaut_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f680"] = ":woman_astronaut_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f680"] = ":woman_astronaut_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f680"] = ":woman_astronaut_tone5:",
- ["\U0001f469\u200d\U0001f9b2"] = ":woman_bald:",
- ["\U0001f469\U0001f3fb\u200d\U0001f9b2"] = ":woman_bald_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f9b2"] = ":woman_bald_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f9b2"] = ":woman_bald_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f9b2"] = ":woman_bald_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f9b2"] = ":woman_bald_tone5:",
- ["\U0001f6b4\u200d\u2640\ufe0f"] = ":woman_biking:",
- ["\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_biking_tone1:",
- ["\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_biking_tone2:",
- ["\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_biking_tone3:",
- ["\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_biking_tone4:",
- ["\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_biking_tone5:",
- ["\u26f9\ufe0f\u200d\u2640\ufe0f"] = ":woman_bouncing_ball:",
- ["\u26f9\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone1:",
- ["\u26f9\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone2:",
- ["\u26f9\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone3:",
- ["\u26f9\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone4:",
- ["\u26f9\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone5:",
- ["\U0001f647\u200d\u2640\ufe0f"] = ":woman_bowing:",
- ["\U0001f647\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_bowing_tone1:",
- ["\U0001f647\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_bowing_tone2:",
- ["\U0001f647\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_bowing_tone3:",
- ["\U0001f647\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_bowing_tone4:",
- ["\U0001f647\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_bowing_tone5:",
- ["\U0001f938\u200d\u2640\ufe0f"] = ":woman_cartwheeling:",
- ["\U0001f938\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone1:",
- ["\U0001f938\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone2:",
- ["\U0001f938\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone3:",
- ["\U0001f938\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone4:",
- ["\U0001f938\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone5:",
- ["\U0001f9d7\u200d\u2640\ufe0f"] = ":woman_climbing:",
- ["\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_climbing_tone1:",
- ["\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_climbing_tone2:",
- ["\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_climbing_tone3:",
- ["\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_climbing_tone4:",
- ["\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_climbing_tone5:",
- ["\U0001f477\u200d\u2640\ufe0f"] = ":woman_construction_worker:",
- ["\U0001f477\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone1:",
- ["\U0001f477\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone2:",
- ["\U0001f477\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone3:",
- ["\U0001f477\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone4:",
- ["\U0001f477\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone5:",
- ["\U0001f469\u200d\U0001f373"] = ":woman_cook:",
- ["\U0001f469\U0001f3fb\u200d\U0001f373"] = ":woman_cook_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f373"] = ":woman_cook_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f373"] = ":woman_cook_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f373"] = ":woman_cook_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f373"] = ":woman_cook_tone5:",
- ["\U0001f469\u200d\U0001f9b1"] = ":woman_curly_haired:",
- ["\U0001f469\U0001f3fb\u200d\U0001f9b1"] = ":woman_curly_haired_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f9b1"] = ":woman_curly_haired_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f9b1"] = ":woman_curly_haired_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f9b1"] = ":woman_curly_haired_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f9b1"] = ":woman_curly_haired_tone5:",
- ["\U0001f575\ufe0f\u200d\u2640\ufe0f"] = ":woman_detective:",
- ["\U0001f575\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_detective_tone1:",
- ["\U0001f575\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_detective_tone2:",
- ["\U0001f575\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_detective_tone3:",
- ["\U0001f575\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_detective_tone4:",
- ["\U0001f575\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_detective_tone5:",
- ["\U0001f9dd\u200d\u2640\ufe0f"] = ":woman_elf:",
- ["\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_elf_tone1:",
- ["\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_elf_tone2:",
- ["\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_elf_tone3:",
- ["\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_elf_tone4:",
- ["\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_elf_tone5:",
- ["\U0001f926\u200d\u2640\ufe0f"] = ":woman_facepalming:",
- ["\U0001f926\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_facepalming_tone1:",
- ["\U0001f926\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_facepalming_tone2:",
- ["\U0001f926\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_facepalming_tone3:",
- ["\U0001f926\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_facepalming_tone4:",
- ["\U0001f926\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_facepalming_tone5:",
- ["\U0001f469\u200d\U0001f3ed"] = ":woman_factory_worker:",
- ["\U0001f469\U0001f3fb\u200d\U0001f3ed"] = ":woman_factory_worker_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f3ed"] = ":woman_factory_worker_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f3ed"] = ":woman_factory_worker_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f3ed"] = ":woman_factory_worker_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f3ed"] = ":woman_factory_worker_tone5:",
- ["\U0001f9da\u200d\u2640\ufe0f"] = ":woman_fairy:",
- ["\U0001f9da\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_fairy_tone1:",
- ["\U0001f9da\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_fairy_tone2:",
- ["\U0001f9da\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_fairy_tone3:",
- ["\U0001f9da\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_fairy_tone4:",
- ["\U0001f9da\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_fairy_tone5:",
- ["\U0001f469\u200d\U0001f33e"] = ":woman_farmer:",
- ["\U0001f469\U0001f3fb\u200d\U0001f33e"] = ":woman_farmer_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f33e"] = ":woman_farmer_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f33e"] = ":woman_farmer_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f33e"] = ":woman_farmer_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f33e"] = ":woman_farmer_tone5:",
- ["\U0001f469\u200d\U0001f37c"] = ":woman_feeding_baby:",
- ["\U0001f469\U0001f3fb\u200d\U0001f37c"] = ":woman_feeding_baby_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f37c"] = ":woman_feeding_baby_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f37c"] = ":woman_feeding_baby_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f37c"] = ":woman_feeding_baby_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f37c"] = ":woman_feeding_baby_tone5:",
- ["\U0001f469\u200d\U0001f692"] = ":woman_firefighter:",
- ["\U0001f469\U0001f3fb\u200d\U0001f692"] = ":woman_firefighter_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f692"] = ":woman_firefighter_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f692"] = ":woman_firefighter_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f692"] = ":woman_firefighter_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f692"] = ":woman_firefighter_tone5:",
- ["\U0001f64d\u200d\u2640\ufe0f"] = ":woman_frowning:",
- ["\U0001f64d\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_frowning_tone1:",
- ["\U0001f64d\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_frowning_tone2:",
- ["\U0001f64d\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_frowning_tone3:",
- ["\U0001f64d\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_frowning_tone4:",
- ["\U0001f64d\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_frowning_tone5:",
- ["\U0001f9de\u200d\u2640\ufe0f"] = ":woman_genie:",
- ["\U0001f645\u200d\u2640\ufe0f"] = ":woman_gesturing_no:",
- ["\U0001f645\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone1:",
- ["\U0001f645\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone2:",
- ["\U0001f645\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone3:",
- ["\U0001f645\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone4:",
- ["\U0001f645\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone5:",
- ["\U0001f646\u200d\u2640\ufe0f"] = ":woman_gesturing_ok:",
- ["\U0001f646\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone1:",
- ["\U0001f646\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone2:",
- ["\U0001f646\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone3:",
- ["\U0001f646\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone4:",
- ["\U0001f646\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone5:",
- ["\U0001f486\u200d\u2640\ufe0f"] = ":woman_getting_face_massage:",
- ["\U0001f486\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone1:",
- ["\U0001f486\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone2:",
- ["\U0001f486\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone3:",
- ["\U0001f486\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone4:",
- ["\U0001f486\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone5:",
- ["\U0001f487\u200d\u2640\ufe0f"] = ":woman_getting_haircut:",
- ["\U0001f487\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone1:",
- ["\U0001f487\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone2:",
- ["\U0001f487\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone3:",
- ["\U0001f487\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone4:",
- ["\U0001f487\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone5:",
- ["\U0001f3cc\ufe0f\u200d\u2640\ufe0f"] = ":woman_golfing:",
- ["\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_golfing_tone1:",
- ["\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_golfing_tone2:",
- ["\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_golfing_tone3:",
- ["\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_golfing_tone4:",
- ["\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_golfing_tone5:",
- ["\U0001f482\u200d\u2640\ufe0f"] = ":woman_guard:",
- ["\U0001f482\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_guard_tone1:",
- ["\U0001f482\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_guard_tone2:",
- ["\U0001f482\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_guard_tone3:",
- ["\U0001f482\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_guard_tone4:",
- ["\U0001f482\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_guard_tone5:",
- ["\U0001f469\u200d\u2695\ufe0f"] = ":woman_health_worker:",
- ["\U0001f469\U0001f3fb\u200d\u2695\ufe0f"] = ":woman_health_worker_tone1:",
- ["\U0001f469\U0001f3fc\u200d\u2695\ufe0f"] = ":woman_health_worker_tone2:",
- ["\U0001f469\U0001f3fd\u200d\u2695\ufe0f"] = ":woman_health_worker_tone3:",
- ["\U0001f469\U0001f3fe\u200d\u2695\ufe0f"] = ":woman_health_worker_tone4:",
- ["\U0001f469\U0001f3ff\u200d\u2695\ufe0f"] = ":woman_health_worker_tone5:",
- ["\U0001f9d8\u200d\u2640\ufe0f"] = ":woman_in_lotus_position:",
- ["\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone1:",
- ["\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone2:",
- ["\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone3:",
- ["\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone4:",
- ["\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone5:",
- ["\U0001f469\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair:",
- ["\U0001f469\U0001f3fb\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone5:",
- ["\U0001f469\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair:",
- ["\U0001f469\U0001f3fb\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone5:",
- ["\U0001f9d6\u200d\u2640\ufe0f"] = ":woman_in_steamy_room:",
- ["\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone1:",
- ["\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone2:",
- ["\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone3:",
- ["\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone4:",
- ["\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone5:",
- ["\U0001f935\u200d\u2640\ufe0f"] = ":woman_in_tuxedo:",
- ["\U0001f935\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone1:",
- ["\U0001f935\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone2:",
- ["\U0001f935\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone3:",
- ["\U0001f935\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone4:",
- ["\U0001f935\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone5:",
- ["\U0001f469\u200d\u2696\ufe0f"] = ":woman_judge:",
- ["\U0001f469\U0001f3fb\u200d\u2696\ufe0f"] = ":woman_judge_tone1:",
- ["\U0001f469\U0001f3fc\u200d\u2696\ufe0f"] = ":woman_judge_tone2:",
- ["\U0001f469\U0001f3fd\u200d\u2696\ufe0f"] = ":woman_judge_tone3:",
- ["\U0001f469\U0001f3fe\u200d\u2696\ufe0f"] = ":woman_judge_tone4:",
- ["\U0001f469\U0001f3ff\u200d\u2696\ufe0f"] = ":woman_judge_tone5:",
- ["\U0001f939\u200d\u2640\ufe0f"] = ":woman_juggling:",
- ["\U0001f939\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_juggling_tone1:",
- ["\U0001f939\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_juggling_tone2:",
- ["\U0001f939\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_juggling_tone3:",
- ["\U0001f939\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_juggling_tone4:",
- ["\U0001f939\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_juggling_tone5:",
- ["\U0001f9ce\u200d\u2640\ufe0f"] = ":woman_kneeling:",
- ["\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_kneeling_tone1:",
- ["\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_kneeling_tone2:",
- ["\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_kneeling_tone3:",
- ["\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_kneeling_tone4:",
- ["\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_kneeling_tone5:",
- ["\U0001f3cb\ufe0f\u200d\u2640\ufe0f"] = ":woman_lifting_weights:",
- ["\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone1:",
- ["\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone2:",
- ["\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone3:",
- ["\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone4:",
- ["\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone5:",
- ["\U0001f9d9\u200d\u2640\ufe0f"] = ":woman_mage:",
- ["\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_mage_tone1:",
- ["\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_mage_tone2:",
- ["\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_mage_tone3:",
- ["\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_mage_tone4:",
- ["\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_mage_tone5:",
- ["\U0001f469\u200d\U0001f527"] = ":woman_mechanic:",
- ["\U0001f469\U0001f3fb\u200d\U0001f527"] = ":woman_mechanic_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f527"] = ":woman_mechanic_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f527"] = ":woman_mechanic_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f527"] = ":woman_mechanic_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f527"] = ":woman_mechanic_tone5:",
- ["\U0001f6b5\u200d\u2640\ufe0f"] = ":woman_mountain_biking:",
- ["\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone1:",
- ["\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone2:",
- ["\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone3:",
- ["\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone4:",
- ["\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone5:",
- ["\U0001f469\u200d\U0001f4bc"] = ":woman_office_worker:",
- ["\U0001f469\U0001f3fb\u200d\U0001f4bc"] = ":woman_office_worker_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f4bc"] = ":woman_office_worker_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f4bc"] = ":woman_office_worker_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f4bc"] = ":woman_office_worker_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f4bc"] = ":woman_office_worker_tone5:",
- ["\U0001f469\u200d\u2708\ufe0f"] = ":woman_pilot:",
- ["\U0001f469\U0001f3fb\u200d\u2708\ufe0f"] = ":woman_pilot_tone1:",
- ["\U0001f469\U0001f3fc\u200d\u2708\ufe0f"] = ":woman_pilot_tone2:",
- ["\U0001f469\U0001f3fd\u200d\u2708\ufe0f"] = ":woman_pilot_tone3:",
- ["\U0001f469\U0001f3fe\u200d\u2708\ufe0f"] = ":woman_pilot_tone4:",
- ["\U0001f469\U0001f3ff\u200d\u2708\ufe0f"] = ":woman_pilot_tone5:",
- ["\U0001f93e\u200d\u2640\ufe0f"] = ":woman_playing_handball:",
- ["\U0001f93e\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone1:",
- ["\U0001f93e\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone2:",
- ["\U0001f93e\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone3:",
- ["\U0001f93e\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone4:",
- ["\U0001f93e\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone5:",
- ["\U0001f93d\u200d\u2640\ufe0f"] = ":woman_playing_water_polo:",
- ["\U0001f93d\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone1:",
- ["\U0001f93d\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone2:",
- ["\U0001f93d\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone3:",
- ["\U0001f93d\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone4:",
- ["\U0001f93d\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone5:",
- ["\U0001f46e\u200d\u2640\ufe0f"] = ":woman_police_officer:",
- ["\U0001f46e\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_police_officer_tone1:",
- ["\U0001f46e\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_police_officer_tone2:",
- ["\U0001f46e\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_police_officer_tone3:",
- ["\U0001f46e\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_police_officer_tone4:",
- ["\U0001f46e\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_police_officer_tone5:",
- ["\U0001f64e\u200d\u2640\ufe0f"] = ":woman_pouting:",
- ["\U0001f64e\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_pouting_tone1:",
- ["\U0001f64e\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_pouting_tone2:",
- ["\U0001f64e\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_pouting_tone3:",
- ["\U0001f64e\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_pouting_tone4:",
- ["\U0001f64e\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_pouting_tone5:",
- ["\U0001f64b\u200d\u2640\ufe0f"] = ":woman_raising_hand:",
- ["\U0001f64b\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone1:",
- ["\U0001f64b\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone2:",
- ["\U0001f64b\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone3:",
- ["\U0001f64b\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone4:",
- ["\U0001f64b\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone5:",
- ["\U0001f469\u200d\U0001f9b0"] = ":woman_red_haired:",
- ["\U0001f469\U0001f3fb\u200d\U0001f9b0"] = ":woman_red_haired_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f9b0"] = ":woman_red_haired_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f9b0"] = ":woman_red_haired_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f9b0"] = ":woman_red_haired_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f9b0"] = ":woman_red_haired_tone5:",
- ["\U0001f6a3\u200d\u2640\ufe0f"] = ":woman_rowing_boat:",
- ["\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone1:",
- ["\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone2:",
- ["\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone3:",
- ["\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone4:",
- ["\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone5:",
- ["\U0001f3c3\u200d\u2640\ufe0f"] = ":woman_running:",
- ["\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_running_tone1:",
- ["\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_running_tone2:",
- ["\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_running_tone3:",
- ["\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_running_tone4:",
- ["\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_running_tone5:",
- ["\U0001f469\u200d\U0001f52c"] = ":woman_scientist:",
- ["\U0001f469\U0001f3fb\u200d\U0001f52c"] = ":woman_scientist_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f52c"] = ":woman_scientist_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f52c"] = ":woman_scientist_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f52c"] = ":woman_scientist_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f52c"] = ":woman_scientist_tone5:",
- ["\U0001f937\u200d\u2640\ufe0f"] = ":woman_shrugging:",
- ["\U0001f937\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_shrugging_tone1:",
- ["\U0001f937\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_shrugging_tone2:",
- ["\U0001f937\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_shrugging_tone3:",
- ["\U0001f937\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_shrugging_tone4:",
- ["\U0001f937\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_shrugging_tone5:",
- ["\U0001f469\u200d\U0001f3a4"] = ":woman_singer:",
- ["\U0001f469\U0001f3fb\u200d\U0001f3a4"] = ":woman_singer_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f3a4"] = ":woman_singer_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f3a4"] = ":woman_singer_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f3a4"] = ":woman_singer_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f3a4"] = ":woman_singer_tone5:",
- ["\U0001f9cd\u200d\u2640\ufe0f"] = ":woman_standing:",
- ["\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_standing_tone1:",
- ["\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_standing_tone2:",
- ["\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_standing_tone3:",
- ["\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_standing_tone4:",
- ["\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_standing_tone5:",
- ["\U0001f469\u200d\U0001f393"] = ":woman_student:",
- ["\U0001f469\U0001f3fb\u200d\U0001f393"] = ":woman_student_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f393"] = ":woman_student_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f393"] = ":woman_student_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f393"] = ":woman_student_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f393"] = ":woman_student_tone5:",
- ["\U0001f9b8\u200d\u2640\ufe0f"] = ":woman_superhero:",
- ["\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_superhero_tone1:",
- ["\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_superhero_tone2:",
- ["\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_superhero_tone3:",
- ["\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_superhero_tone4:",
- ["\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_superhero_tone5:",
- ["\U0001f9b9\u200d\u2640\ufe0f"] = ":woman_supervillain:",
- ["\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_supervillain_tone1:",
- ["\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_supervillain_tone2:",
- ["\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_supervillain_tone3:",
- ["\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_supervillain_tone4:",
- ["\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_supervillain_tone5:",
- ["\U0001f3c4\u200d\u2640\ufe0f"] = ":woman_surfing:",
- ["\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_surfing_tone1:",
- ["\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_surfing_tone2:",
- ["\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_surfing_tone3:",
- ["\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_surfing_tone4:",
- ["\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_surfing_tone5:",
- ["\U0001f3ca\u200d\u2640\ufe0f"] = ":woman_swimming:",
- ["\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_swimming_tone1:",
- ["\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_swimming_tone2:",
- ["\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_swimming_tone3:",
- ["\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_swimming_tone4:",
- ["\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_swimming_tone5:",
- ["\U0001f469\u200d\U0001f3eb"] = ":woman_teacher:",
- ["\U0001f469\U0001f3fb\u200d\U0001f3eb"] = ":woman_teacher_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f3eb"] = ":woman_teacher_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f3eb"] = ":woman_teacher_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f3eb"] = ":woman_teacher_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f3eb"] = ":woman_teacher_tone5:",
- ["\U0001f469\u200d\U0001f4bb"] = ":woman_technologist:",
- ["\U0001f469\U0001f3fb\u200d\U0001f4bb"] = ":woman_technologist_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f4bb"] = ":woman_technologist_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f4bb"] = ":woman_technologist_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f4bb"] = ":woman_technologist_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f4bb"] = ":woman_technologist_tone5:",
- ["\U0001f481\u200d\u2640\ufe0f"] = ":woman_tipping_hand:",
- ["\U0001f481\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone1:",
- ["\U0001f481\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone2:",
- ["\U0001f481\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone3:",
- ["\U0001f481\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone4:",
- ["\U0001f481\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone5:",
- ["\U0001f469\U0001f3fb"] = ":woman_tone1:",
- ["\U0001f469\U0001f3fc"] = ":woman_tone2:",
- ["\U0001f469\U0001f3fd"] = ":woman_tone3:",
- ["\U0001f469\U0001f3fe"] = ":woman_tone4:",
- ["\U0001f469\U0001f3ff"] = ":woman_tone5:",
- ["\U0001f9db\u200d\u2640\ufe0f"] = ":woman_vampire:",
- ["\U0001f9db\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_vampire_tone1:",
- ["\U0001f9db\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_vampire_tone2:",
- ["\U0001f9db\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_vampire_tone3:",
- ["\U0001f9db\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_vampire_tone4:",
- ["\U0001f9db\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_vampire_tone5:",
- ["\U0001f6b6\u200d\u2640\ufe0f"] = ":woman_walking:",
- ["\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_walking_tone1:",
- ["\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_walking_tone2:",
- ["\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_walking_tone3:",
- ["\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_walking_tone4:",
- ["\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_walking_tone5:",
- ["\U0001f473\u200d\u2640\ufe0f"] = ":woman_wearing_turban:",
- ["\U0001f473\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone1:",
- ["\U0001f473\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone2:",
- ["\U0001f473\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone3:",
- ["\U0001f473\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone4:",
- ["\U0001f473\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone5:",
- ["\U0001f469\u200d\U0001f9b3"] = ":woman_white_haired:",
- ["\U0001f469\U0001f3fb\u200d\U0001f9b3"] = ":woman_white_haired_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f9b3"] = ":woman_white_haired_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f9b3"] = ":woman_white_haired_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f9b3"] = ":woman_white_haired_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f9b3"] = ":woman_white_haired_tone5:",
- ["\U0001f9d5"] = ":woman_with_headscarf:",
- ["\U0001f9d5\U0001f3fb"] = ":woman_with_headscarf_tone1:",
- ["\U0001f9d5\U0001f3fc"] = ":woman_with_headscarf_tone2:",
- ["\U0001f9d5\U0001f3fd"] = ":woman_with_headscarf_tone3:",
- ["\U0001f9d5\U0001f3fe"] = ":woman_with_headscarf_tone4:",
- ["\U0001f9d5\U0001f3ff"] = ":woman_with_headscarf_tone5:",
- ["\U0001f469\u200d\U0001f9af"] = ":woman_with_probing_cane:",
- ["\U0001f469\U0001f3fb\u200d\U0001f9af"] = ":woman_with_probing_cane_tone1:",
- ["\U0001f469\U0001f3fc\u200d\U0001f9af"] = ":woman_with_probing_cane_tone2:",
- ["\U0001f469\U0001f3fd\u200d\U0001f9af"] = ":woman_with_probing_cane_tone3:",
- ["\U0001f469\U0001f3fe\u200d\U0001f9af"] = ":woman_with_probing_cane_tone4:",
- ["\U0001f469\U0001f3ff\u200d\U0001f9af"] = ":woman_with_probing_cane_tone5:",
- ["\U0001f470\u200d\u2640\ufe0f"] = ":woman_with_veil:",
- ["\U0001f470\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_with_veil_tone1:",
- ["\U0001f470\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_with_veil_tone2:",
- ["\U0001f470\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_with_veil_tone3:",
- ["\U0001f470\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_with_veil_tone4:",
- ["\U0001f470\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_with_veil_tone5:",
- ["\U0001f9df\u200d\u2640\ufe0f"] = ":woman_zombie:",
- ["\U0001f45a"] = ":womans_clothes:",
- ["\U0001f97f"] = ":womans_flat_shoe:",
- ["\U0001f452"] = ":womans_hat:",
- ["\U0001f46d"] = ":women_holding_hands_tone5_tone4:",
- ["\U0001f46f\u200d\u2640\ufe0f"] = ":women_with_bunny_ears_partying:",
- ["\U0001f93c\u200d\u2640\ufe0f"] = ":women_wrestling:",
- ["\U0001f6ba"] = ":womens:",
- ["\U0001fab5"] = ":wood:",
- ["\U0001f974"] = ":woozy_face:",
- ["\U0001fab1"] = ":worm:",
- ["\U0001f61f"] = ":worried:",
- ["\U0001f527"] = ":wrench:",
- ["\u270d\ufe0f"] = ":writing_hand:",
- ["\u270d"] = ":writing_hand:",
- ["\u270d\U0001f3fb"] = ":writing_hand_tone1:",
- ["\u270d\U0001f3fc"] = ":writing_hand_tone2:",
- ["\u270d\U0001f3fd"] = ":writing_hand_tone3:",
- ["\u270d\U0001f3fe"] = ":writing_hand_tone4:",
- ["\u270d\U0001f3ff"] = ":writing_hand_tone5:",
- ["\u274c"] = ":x:",
- ["\U0001f9f6"] = ":yarn:",
- ["\U0001f971"] = ":yawning_face:",
- ["\U0001f7e1"] = ":yellow_circle:",
- ["\U0001f49b"] = ":yellow_heart:",
- ["\U0001f7e8"] = ":yellow_square:",
- ["\U0001f4b4"] = ":yen:",
- ["\u262f\ufe0f"] = ":yin_yang:",
- ["\u262f"] = ":yin_yang:",
- ["\U0001fa80"] = ":yo_yo:",
- ["\U0001f60b"] = ":yum:",
- ["\U0001f92a"] = ":zany_face:",
- ["\u26a1"] = ":zap:",
- ["\U0001f993"] = ":zebra:",
- ["\u0030\ufe0f\u20e3"] = ":zero:",
- ["\u0030\u20e3"] = ":zero:",
- ["\U0001f910"] = ":zipper_mouth:",
- ["\U0001f9df"] = ":zombie:",
- ["\U0001f4a4"] = ":zzz:",
- };
- #endregion
+ s_discordNameLookup = new Dictionary<string, string>
+ {
+ ["\U0001f4af"] = ":100:",
+ ["\U0001f522"] = ":1234:",
+ ["\U0001f3b1"] = ":8ball:",
+ ["\U0001f170\ufe0f"] = ":a:",
+ ["\U0001f170"] = ":a:",
+ ["\U0001f18e"] = ":ab:",
+ ["\U0001f9ee"] = ":abacus:",
+ ["\U0001f524"] = ":abc:",
+ ["\U0001f521"] = ":abcd:",
+ ["\U0001f251"] = ":accept:",
+ ["\U0001fa97"] = ":accordion:",
+ ["\U0001fa79"] = ":adhesive_bandage:",
+ ["\U0001f9d1"] = ":adult:",
+ ["\U0001f9d1\U0001f3fb"] = ":adult_tone1:",
+ ["\U0001f9d1\U0001f3fc"] = ":adult_tone2:",
+ ["\U0001f9d1\U0001f3fd"] = ":adult_tone3:",
+ ["\U0001f9d1\U0001f3fe"] = ":adult_tone4:",
+ ["\U0001f9d1\U0001f3ff"] = ":adult_tone5:",
+ ["\U0001f6a1"] = ":aerial_tramway:",
+ ["\u2708\ufe0f"] = ":airplane:",
+ ["\u2708"] = ":airplane:",
+ ["\U0001f6ec"] = ":airplane_arriving:",
+ ["\U0001f6eb"] = ":airplane_departure:",
+ ["\U0001f6e9\ufe0f"] = ":airplane_small:",
+ ["\U0001f6e9"] = ":airplane_small:",
+ ["\u23f0"] = ":alarm_clock:",
+ ["\u2697\ufe0f"] = ":alembic:",
+ ["\u2697"] = ":alembic:",
+ ["\U0001f47d"] = ":alien:",
+ ["\U0001f691"] = ":ambulance:",
+ ["\U0001f3fa"] = ":amphora:",
+ ["\U0001fac0"] = ":anatomical_heart:",
+ ["\u2693"] = ":anchor:",
+ ["\U0001f47c"] = ":angel:",
+ ["\U0001f47c\U0001f3fb"] = ":angel_tone1:",
+ ["\U0001f47c\U0001f3fc"] = ":angel_tone2:",
+ ["\U0001f47c\U0001f3fd"] = ":angel_tone3:",
+ ["\U0001f47c\U0001f3fe"] = ":angel_tone4:",
+ ["\U0001f47c\U0001f3ff"] = ":angel_tone5:",
+ ["\U0001f4a2"] = ":anger:",
+ ["\U0001f5ef\ufe0f"] = ":anger_right:",
+ ["\U0001f5ef"] = ":anger_right:",
+ ["\U0001f620"] = ":angry:",
+ ["\U0001f627"] = ":anguished:",
+ ["\U0001f41c"] = ":ant:",
+ ["\U0001f34e"] = ":apple:",
+ ["\u2652"] = ":aquarius:",
+ ["\u2648"] = ":aries:",
+ ["\u25c0\ufe0f"] = ":arrow_backward:",
+ ["\u25c0"] = ":arrow_backward:",
+ ["\u23ec"] = ":arrow_double_down:",
+ ["\u23eb"] = ":arrow_double_up:",
+ ["\u2b07\ufe0f"] = ":arrow_down:",
+ ["\u2b07"] = ":arrow_down:",
+ ["\U0001f53d"] = ":arrow_down_small:",
+ ["\u25b6\ufe0f"] = ":arrow_forward:",
+ ["\u25b6"] = ":arrow_forward:",
+ ["\u2935\ufe0f"] = ":arrow_heading_down:",
+ ["\u2935"] = ":arrow_heading_down:",
+ ["\u2934\ufe0f"] = ":arrow_heading_up:",
+ ["\u2934"] = ":arrow_heading_up:",
+ ["\u2b05\ufe0f"] = ":arrow_left:",
+ ["\u2b05"] = ":arrow_left:",
+ ["\u2199\ufe0f"] = ":arrow_lower_left:",
+ ["\u2199"] = ":arrow_lower_left:",
+ ["\u2198\ufe0f"] = ":arrow_lower_right:",
+ ["\u2198"] = ":arrow_lower_right:",
+ ["\u27a1\ufe0f"] = ":arrow_right:",
+ ["\u27a1"] = ":arrow_right:",
+ ["\u21aa\ufe0f"] = ":arrow_right_hook:",
+ ["\u21aa"] = ":arrow_right_hook:",
+ ["\u2b06\ufe0f"] = ":arrow_up:",
+ ["\u2b06"] = ":arrow_up:",
+ ["\u2195\ufe0f"] = ":arrow_up_down:",
+ ["\u2195"] = ":arrow_up_down:",
+ ["\U0001f53c"] = ":arrow_up_small:",
+ ["\u2196\ufe0f"] = ":arrow_upper_left:",
+ ["\u2196"] = ":arrow_upper_left:",
+ ["\u2197\ufe0f"] = ":arrow_upper_right:",
+ ["\u2197"] = ":arrow_upper_right:",
+ ["\U0001f503"] = ":arrows_clockwise:",
+ ["\U0001f504"] = ":arrows_counterclockwise:",
+ ["\U0001f3a8"] = ":art:",
+ ["\U0001f69b"] = ":articulated_lorry:",
+ ["\U0001f9d1\u200d\U0001f3a8"] = ":artist:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f3a8"] = ":artist_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f3a8"] = ":artist_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f3a8"] = ":artist_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f3a8"] = ":artist_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f3a8"] = ":artist_tone5:",
+ ["\u002a\ufe0f\u20e3"] = ":asterisk:",
+ ["\u002a\u20e3"] = ":asterisk:",
+ ["\U0001f632"] = ":astonished:",
+ ["\U0001f9d1\u200d\U0001f680"] = ":astronaut:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f680"] = ":astronaut_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f680"] = ":astronaut_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f680"] = ":astronaut_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f680"] = ":astronaut_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f680"] = ":astronaut_tone5:",
+ ["\U0001f45f"] = ":athletic_shoe:",
+ ["\U0001f3e7"] = ":atm:",
+ ["\u269b\ufe0f"] = ":atom:",
+ ["\u269b"] = ":atom:",
+ ["\U0001f6fa"] = ":auto_rickshaw:",
+ ["\U0001f951"] = ":avocado:",
+ ["\U0001fa93"] = ":axe:",
+ ["\U0001f171\ufe0f"] = ":b:",
+ ["\U0001f171"] = ":b:",
+ ["\U0001f476"] = ":baby:",
+ ["\U0001f37c"] = ":baby_bottle:",
+ ["\U0001f424"] = ":baby_chick:",
+ ["\U0001f6bc"] = ":baby_symbol:",
+ ["\U0001f476\U0001f3fb"] = ":baby_tone1:",
+ ["\U0001f476\U0001f3fc"] = ":baby_tone2:",
+ ["\U0001f476\U0001f3fd"] = ":baby_tone3:",
+ ["\U0001f476\U0001f3fe"] = ":baby_tone4:",
+ ["\U0001f476\U0001f3ff"] = ":baby_tone5:",
+ ["\U0001f519"] = ":back:",
+ ["\U0001f953"] = ":bacon:",
+ ["\U0001f9a1"] = ":badger:",
+ ["\U0001f3f8"] = ":badminton:",
+ ["\U0001f96f"] = ":bagel:",
+ ["\U0001f6c4"] = ":baggage_claim:",
+ ["\U0001fa70"] = ":ballet_shoes:",
+ ["\U0001f388"] = ":balloon:",
+ ["\U0001f5f3\ufe0f"] = ":ballot_box:",
+ ["\U0001f5f3"] = ":ballot_box:",
+ ["\u2611\ufe0f"] = ":ballot_box_with_check:",
+ ["\u2611"] = ":ballot_box_with_check:",
+ ["\U0001f38d"] = ":bamboo:",
+ ["\U0001f34c"] = ":banana:",
+ ["\u203c\ufe0f"] = ":bangbang:",
+ ["\u203c"] = ":bangbang:",
+ ["\U0001fa95"] = ":banjo:",
+ ["\U0001f3e6"] = ":bank:",
+ ["\U0001f4ca"] = ":bar_chart:",
+ ["\U0001f488"] = ":barber:",
+ ["\u26be"] = ":baseball:",
+ ["\U0001f9fa"] = ":basket:",
+ ["\U0001f3c0"] = ":basketball:",
+ ["\U0001f987"] = ":bat:",
+ ["\U0001f6c0"] = ":bath:",
+ ["\U0001f6c0\U0001f3fb"] = ":bath_tone1:",
+ ["\U0001f6c0\U0001f3fc"] = ":bath_tone2:",
+ ["\U0001f6c0\U0001f3fd"] = ":bath_tone3:",
+ ["\U0001f6c0\U0001f3fe"] = ":bath_tone4:",
+ ["\U0001f6c0\U0001f3ff"] = ":bath_tone5:",
+ ["\U0001f6c1"] = ":bathtub:",
+ ["\U0001f50b"] = ":battery:",
+ ["\U0001f3d6\ufe0f"] = ":beach:",
+ ["\U0001f3d6"] = ":beach:",
+ ["\u26f1\ufe0f"] = ":beach_umbrella:",
+ ["\u26f1"] = ":beach_umbrella:",
+ ["\U0001f43b"] = ":bear:",
+ ["\U0001f9d4"] = ":bearded_person:",
+ ["\U0001f9d4\U0001f3fb"] = ":bearded_person_tone1:",
+ ["\U0001f9d4\U0001f3fc"] = ":bearded_person_tone2:",
+ ["\U0001f9d4\U0001f3fd"] = ":bearded_person_tone3:",
+ ["\U0001f9d4\U0001f3fe"] = ":bearded_person_tone4:",
+ ["\U0001f9d4\U0001f3ff"] = ":bearded_person_tone5:",
+ ["\U0001f9ab"] = ":beaver:",
+ ["\U0001f6cf\ufe0f"] = ":bed:",
+ ["\U0001f6cf"] = ":bed:",
+ ["\U0001f41d"] = ":bee:",
+ ["\U0001f37a"] = ":beer:",
+ ["\U0001f37b"] = ":beers:",
+ ["\U0001fab2"] = ":beetle:",
+ ["\U0001f530"] = ":beginner:",
+ ["\U0001f514"] = ":bell:",
+ ["\U0001fad1"] = ":bell_pepper:",
+ ["\U0001f6ce\ufe0f"] = ":bellhop:",
+ ["\U0001f6ce"] = ":bellhop:",
+ ["\U0001f371"] = ":bento:",
+ ["\U0001f9c3"] = ":beverage_box:",
+ ["\U0001f6b2"] = ":bike:",
+ ["\U0001f459"] = ":bikini:",
+ ["\U0001f9e2"] = ":billed_cap:",
+ ["\u2623\ufe0f"] = ":biohazard:",
+ ["\u2623"] = ":biohazard:",
+ ["\U0001f426"] = ":bird:",
+ ["\U0001f382"] = ":birthday:",
+ ["\U0001f9ac"] = ":bison:",
+ ["\U0001f408\u200d\u2b1b"] = ":black_cat:",
+ ["\u26ab"] = ":black_circle:",
+ ["\U0001f5a4"] = ":black_heart:",
+ ["\U0001f0cf"] = ":black_joker:",
+ ["\u2b1b"] = ":black_large_square:",
+ ["\u25fe"] = ":black_medium_small_square:",
+ ["\u25fc\ufe0f"] = ":black_medium_square:",
+ ["\u25fc"] = ":black_medium_square:",
+ ["\u2712\ufe0f"] = ":black_nib:",
+ ["\u2712"] = ":black_nib:",
+ ["\u25aa\ufe0f"] = ":black_small_square:",
+ ["\u25aa"] = ":black_small_square:",
+ ["\U0001f532"] = ":black_square_button:",
+ ["\U0001f471\u200d\u2642\ufe0f"] = ":blond_haired_man:",
+ ["\U0001f471\U0001f3fb\u200d\u2642\ufe0f"] = ":blond_haired_man_tone1:",
+ ["\U0001f471\U0001f3fc\u200d\u2642\ufe0f"] = ":blond_haired_man_tone2:",
+ ["\U0001f471\U0001f3fd\u200d\u2642\ufe0f"] = ":blond_haired_man_tone3:",
+ ["\U0001f471\U0001f3fe\u200d\u2642\ufe0f"] = ":blond_haired_man_tone4:",
+ ["\U0001f471\U0001f3ff\u200d\u2642\ufe0f"] = ":blond_haired_man_tone5:",
+ ["\U0001f471"] = ":blond_haired_person:",
+ ["\U0001f471\U0001f3fb"] = ":blond_haired_person_tone1:",
+ ["\U0001f471\U0001f3fc"] = ":blond_haired_person_tone2:",
+ ["\U0001f471\U0001f3fd"] = ":blond_haired_person_tone3:",
+ ["\U0001f471\U0001f3fe"] = ":blond_haired_person_tone4:",
+ ["\U0001f471\U0001f3ff"] = ":blond_haired_person_tone5:",
+ ["\U0001f471\u200d\u2640\ufe0f"] = ":blond_haired_woman:",
+ ["\U0001f471\U0001f3fb\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone1:",
+ ["\U0001f471\U0001f3fc\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone2:",
+ ["\U0001f471\U0001f3fd\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone3:",
+ ["\U0001f471\U0001f3fe\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone4:",
+ ["\U0001f471\U0001f3ff\u200d\u2640\ufe0f"] = ":blond_haired_woman_tone5:",
+ ["\U0001f33c"] = ":blossom:",
+ ["\U0001f421"] = ":blowfish:",
+ ["\U0001f4d8"] = ":blue_book:",
+ ["\U0001f699"] = ":blue_car:",
+ ["\U0001f535"] = ":blue_circle:",
+ ["\U0001f499"] = ":blue_heart:",
+ ["\U0001f7e6"] = ":blue_square:",
+ ["\U0001fad0"] = ":blueberries:",
+ ["\U0001f60a"] = ":blush:",
+ ["\U0001f417"] = ":boar:",
+ ["\U0001f4a3"] = ":bomb:",
+ ["\U0001f9b4"] = ":bone:",
+ ["\U0001f4d6"] = ":book:",
+ ["\U0001f516"] = ":bookmark:",
+ ["\U0001f4d1"] = ":bookmark_tabs:",
+ ["\U0001f4da"] = ":books:",
+ ["\U0001f4a5"] = ":boom:",
+ ["\U0001fa83"] = ":boomerang:",
+ ["\U0001f462"] = ":boot:",
+ ["\U0001f490"] = ":bouquet:",
+ ["\U0001f3f9"] = ":bow_and_arrow:",
+ ["\U0001f963"] = ":bowl_with_spoon:",
+ ["\U0001f3b3"] = ":bowling:",
+ ["\U0001f94a"] = ":boxing_glove:",
+ ["\U0001f466"] = ":boy:",
+ ["\U0001f466\U0001f3fb"] = ":boy_tone1:",
+ ["\U0001f466\U0001f3fc"] = ":boy_tone2:",
+ ["\U0001f466\U0001f3fd"] = ":boy_tone3:",
+ ["\U0001f466\U0001f3fe"] = ":boy_tone4:",
+ ["\U0001f466\U0001f3ff"] = ":boy_tone5:",
+ ["\U0001f9e0"] = ":brain:",
+ ["\U0001f35e"] = ":bread:",
+ ["\U0001f931"] = ":breast_feeding:",
+ ["\U0001f931\U0001f3fb"] = ":breast_feeding_tone1:",
+ ["\U0001f931\U0001f3fc"] = ":breast_feeding_tone2:",
+ ["\U0001f931\U0001f3fd"] = ":breast_feeding_tone3:",
+ ["\U0001f931\U0001f3fe"] = ":breast_feeding_tone4:",
+ ["\U0001f931\U0001f3ff"] = ":breast_feeding_tone5:",
+ ["\U0001f9f1"] = ":bricks:",
+ ["\U0001f309"] = ":bridge_at_night:",
+ ["\U0001f4bc"] = ":briefcase:",
+ ["\U0001fa72"] = ":briefs:",
+ ["\U0001f966"] = ":broccoli:",
+ ["\U0001f494"] = ":broken_heart:",
+ ["\U0001f9f9"] = ":broom:",
+ ["\U0001f7e4"] = ":brown_circle:",
+ ["\U0001f90e"] = ":brown_heart:",
+ ["\U0001f7eb"] = ":brown_square:",
+ ["\U0001f9cb"] = ":bubble_tea:",
+ ["\U0001faa3"] = ":bucket:",
+ ["\U0001f41b"] = ":bug:",
+ ["\U0001f4a1"] = ":bulb:",
+ ["\U0001f685"] = ":bullettrain_front:",
+ ["\U0001f684"] = ":bullettrain_side:",
+ ["\U0001f32f"] = ":burrito:",
+ ["\U0001f68c"] = ":bus:",
+ ["\U0001f68f"] = ":busstop:",
+ ["\U0001f464"] = ":bust_in_silhouette:",
+ ["\U0001f465"] = ":busts_in_silhouette:",
+ ["\U0001f9c8"] = ":butter:",
+ ["\U0001f98b"] = ":butterfly:",
+ ["\U0001f335"] = ":cactus:",
+ ["\U0001f370"] = ":cake:",
+ ["\U0001f4c6"] = ":calendar:",
+ ["\U0001f5d3\ufe0f"] = ":calendar_spiral:",
+ ["\U0001f5d3"] = ":calendar_spiral:",
+ ["\U0001f919"] = ":call_me:",
+ ["\U0001f919\U0001f3fb"] = ":call_me_tone1:",
+ ["\U0001f919\U0001f3fc"] = ":call_me_tone2:",
+ ["\U0001f919\U0001f3fd"] = ":call_me_tone3:",
+ ["\U0001f919\U0001f3fe"] = ":call_me_tone4:",
+ ["\U0001f919\U0001f3ff"] = ":call_me_tone5:",
+ ["\U0001f4f2"] = ":calling:",
+ ["\U0001f42b"] = ":camel:",
+ ["\U0001f4f7"] = ":camera:",
+ ["\U0001f4f8"] = ":camera_with_flash:",
+ ["\U0001f3d5\ufe0f"] = ":camping:",
+ ["\U0001f3d5"] = ":camping:",
+ ["\u264b"] = ":cancer:",
+ ["\U0001f56f\ufe0f"] = ":candle:",
+ ["\U0001f56f"] = ":candle:",
+ ["\U0001f36c"] = ":candy:",
+ ["\U0001f96b"] = ":canned_food:",
+ ["\U0001f6f6"] = ":canoe:",
+ ["\U0001f520"] = ":capital_abcd:",
+ ["\u2651"] = ":capricorn:",
+ ["\U0001f5c3\ufe0f"] = ":card_box:",
+ ["\U0001f5c3"] = ":card_box:",
+ ["\U0001f4c7"] = ":card_index:",
+ ["\U0001f3a0"] = ":carousel_horse:",
+ ["\U0001fa9a"] = ":carpentry_saw:",
+ ["\U0001f955"] = ":carrot:",
+ ["\U0001f431"] = ":cat:",
+ ["\U0001f408"] = ":cat2:",
+ ["\U0001f4bf"] = ":cd:",
+ ["\u26d3\ufe0f"] = ":chains:",
+ ["\u26d3"] = ":chains:",
+ ["\U0001fa91"] = ":chair:",
+ ["\U0001f37e"] = ":champagne:",
+ ["\U0001f942"] = ":champagne_glass:",
+ ["\U0001f4b9"] = ":chart:",
+ ["\U0001f4c9"] = ":chart_with_downwards_trend:",
+ ["\U0001f4c8"] = ":chart_with_upwards_trend:",
+ ["\U0001f3c1"] = ":checkered_flag:",
+ ["\U0001f9c0"] = ":cheese:",
+ ["\U0001f352"] = ":cherries:",
+ ["\U0001f338"] = ":cherry_blossom:",
+ ["\u265f\ufe0f"] = ":chess_pawn:",
+ ["\u265f"] = ":chess_pawn:",
+ ["\U0001f330"] = ":chestnut:",
+ ["\U0001f414"] = ":chicken:",
+ ["\U0001f9d2"] = ":child:",
+ ["\U0001f9d2\U0001f3fb"] = ":child_tone1:",
+ ["\U0001f9d2\U0001f3fc"] = ":child_tone2:",
+ ["\U0001f9d2\U0001f3fd"] = ":child_tone3:",
+ ["\U0001f9d2\U0001f3fe"] = ":child_tone4:",
+ ["\U0001f9d2\U0001f3ff"] = ":child_tone5:",
+ ["\U0001f6b8"] = ":children_crossing:",
+ ["\U0001f43f\ufe0f"] = ":chipmunk:",
+ ["\U0001f43f"] = ":chipmunk:",
+ ["\U0001f36b"] = ":chocolate_bar:",
+ ["\U0001f962"] = ":chopsticks:",
+ ["\U0001f384"] = ":christmas_tree:",
+ ["\u26ea"] = ":church:",
+ ["\U0001f3a6"] = ":cinema:",
+ ["\U0001f3aa"] = ":circus_tent:",
+ ["\U0001f306"] = ":city_dusk:",
+ ["\U0001f307"] = ":city_sunset:",
+ ["\U0001f3d9\ufe0f"] = ":cityscape:",
+ ["\U0001f3d9"] = ":cityscape:",
+ ["\U0001f191"] = ":cl:",
+ ["\U0001f44f"] = ":clap:",
+ ["\U0001f44f\U0001f3fb"] = ":clap_tone1:",
+ ["\U0001f44f\U0001f3fc"] = ":clap_tone2:",
+ ["\U0001f44f\U0001f3fd"] = ":clap_tone3:",
+ ["\U0001f44f\U0001f3fe"] = ":clap_tone4:",
+ ["\U0001f44f\U0001f3ff"] = ":clap_tone5:",
+ ["\U0001f3ac"] = ":clapper:",
+ ["\U0001f3db\ufe0f"] = ":classical_building:",
+ ["\U0001f3db"] = ":classical_building:",
+ ["\U0001f4cb"] = ":clipboard:",
+ ["\U0001f570\ufe0f"] = ":clock:",
+ ["\U0001f570"] = ":clock:",
+ ["\U0001f550"] = ":clock1:",
+ ["\U0001f559"] = ":clock10:",
+ ["\U0001f565"] = ":clock1030:",
+ ["\U0001f55a"] = ":clock11:",
+ ["\U0001f566"] = ":clock1130:",
+ ["\U0001f55b"] = ":clock12:",
+ ["\U0001f567"] = ":clock1230:",
+ ["\U0001f55c"] = ":clock130:",
+ ["\U0001f551"] = ":clock2:",
+ ["\U0001f55d"] = ":clock230:",
+ ["\U0001f552"] = ":clock3:",
+ ["\U0001f55e"] = ":clock330:",
+ ["\U0001f553"] = ":clock4:",
+ ["\U0001f55f"] = ":clock430:",
+ ["\U0001f554"] = ":clock5:",
+ ["\U0001f560"] = ":clock530:",
+ ["\U0001f555"] = ":clock6:",
+ ["\U0001f561"] = ":clock630:",
+ ["\U0001f556"] = ":clock7:",
+ ["\U0001f562"] = ":clock730:",
+ ["\U0001f557"] = ":clock8:",
+ ["\U0001f563"] = ":clock830:",
+ ["\U0001f558"] = ":clock9:",
+ ["\U0001f564"] = ":clock930:",
+ ["\U0001f4d5"] = ":closed_book:",
+ ["\U0001f510"] = ":closed_lock_with_key:",
+ ["\U0001f302"] = ":closed_umbrella:",
+ ["\u2601\ufe0f"] = ":cloud:",
+ ["\u2601"] = ":cloud:",
+ ["\U0001f329\ufe0f"] = ":cloud_lightning:",
+ ["\U0001f329"] = ":cloud_lightning:",
+ ["\U0001f327\ufe0f"] = ":cloud_rain:",
+ ["\U0001f327"] = ":cloud_rain:",
+ ["\U0001f328\ufe0f"] = ":cloud_snow:",
+ ["\U0001f328"] = ":cloud_snow:",
+ ["\U0001f32a\ufe0f"] = ":cloud_tornado:",
+ ["\U0001f32a"] = ":cloud_tornado:",
+ ["\U0001f921"] = ":clown:",
+ ["\u2663\ufe0f"] = ":clubs:",
+ ["\u2663"] = ":clubs:",
+ ["\U0001f9e5"] = ":coat:",
+ ["\U0001fab3"] = ":cockroach:",
+ ["\U0001f378"] = ":cocktail:",
+ ["\U0001f965"] = ":coconut:",
+ ["\u2615"] = ":coffee:",
+ ["\u26b0\ufe0f"] = ":coffin:",
+ ["\u26b0"] = ":coffin:",
+ ["\U0001fa99"] = ":coin:",
+ ["\U0001f976"] = ":cold_face:",
+ ["\U0001f630"] = ":cold_sweat:",
+ ["\u2604\ufe0f"] = ":comet:",
+ ["\u2604"] = ":comet:",
+ ["\U0001f9ed"] = ":compass:",
+ ["\U0001f5dc\ufe0f"] = ":compression:",
+ ["\U0001f5dc"] = ":compression:",
+ ["\U0001f4bb"] = ":computer:",
+ ["\U0001f38a"] = ":confetti_ball:",
+ ["\U0001f616"] = ":confounded:",
+ ["\U0001f615"] = ":confused:",
+ ["\u3297\ufe0f"] = ":congratulations:",
+ ["\u3297"] = ":congratulations:",
+ ["\U0001f6a7"] = ":construction:",
+ ["\U0001f3d7\ufe0f"] = ":construction_site:",
+ ["\U0001f3d7"] = ":construction_site:",
+ ["\U0001f477"] = ":construction_worker:",
+ ["\U0001f477\U0001f3fb"] = ":construction_worker_tone1:",
+ ["\U0001f477\U0001f3fc"] = ":construction_worker_tone2:",
+ ["\U0001f477\U0001f3fd"] = ":construction_worker_tone3:",
+ ["\U0001f477\U0001f3fe"] = ":construction_worker_tone4:",
+ ["\U0001f477\U0001f3ff"] = ":construction_worker_tone5:",
+ ["\U0001f39b\ufe0f"] = ":control_knobs:",
+ ["\U0001f39b"] = ":control_knobs:",
+ ["\U0001f3ea"] = ":convenience_store:",
+ ["\U0001f9d1\u200d\U0001f373"] = ":cook:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f373"] = ":cook_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f373"] = ":cook_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f373"] = ":cook_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f373"] = ":cook_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f373"] = ":cook_tone5:",
+ ["\U0001f36a"] = ":cookie:",
+ ["\U0001f373"] = ":cooking:",
+ ["\U0001f192"] = ":cool:",
+ ["\u00a9\ufe0f"] = ":copyright:",
+ ["\u00a9"] = ":copyright:",
+ ["\U0001f33d"] = ":corn:",
+ ["\U0001f6cb\ufe0f"] = ":couch:",
+ ["\U0001f6cb"] = ":couch:",
+ ["\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468"] = ":couple_mm:",
+ ["\U0001f491"] = ":couple_with_heart:",
+ ["\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468"] = ":couple_with_heart_woman_man:",
+ ["\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469"] = ":couple_ww:",
+ ["\U0001f48f"] = ":couplekiss:",
+ ["\U0001f42e"] = ":cow:",
+ ["\U0001f404"] = ":cow2:",
+ ["\U0001f920"] = ":cowboy:",
+ ["\U0001f980"] = ":crab:",
+ ["\U0001f58d\ufe0f"] = ":crayon:",
+ ["\U0001f58d"] = ":crayon:",
+ ["\U0001f4b3"] = ":credit_card:",
+ ["\U0001f319"] = ":crescent_moon:",
+ ["\U0001f997"] = ":cricket:",
+ ["\U0001f3cf"] = ":cricket_game:",
+ ["\U0001f40a"] = ":crocodile:",
+ ["\U0001f950"] = ":croissant:",
+ ["\u271d\ufe0f"] = ":cross:",
+ ["\u271d"] = ":cross:",
+ ["\U0001f38c"] = ":crossed_flags:",
+ ["\u2694\ufe0f"] = ":crossed_swords:",
+ ["\u2694"] = ":crossed_swords:",
+ ["\U0001f451"] = ":crown:",
+ ["\U0001f6f3\ufe0f"] = ":cruise_ship:",
+ ["\U0001f6f3"] = ":cruise_ship:",
+ ["\U0001f622"] = ":cry:",
+ ["\U0001f63f"] = ":crying_cat_face:",
+ ["\U0001f52e"] = ":crystal_ball:",
+ ["\U0001f952"] = ":cucumber:",
+ ["\U0001f964"] = ":cup_with_straw:",
+ ["\U0001f9c1"] = ":cupcake:",
+ ["\U0001f498"] = ":cupid:",
+ ["\U0001f94c"] = ":curling_stone:",
+ ["\u27b0"] = ":curly_loop:",
+ ["\U0001f4b1"] = ":currency_exchange:",
+ ["\U0001f35b"] = ":curry:",
+ ["\U0001f36e"] = ":custard:",
+ ["\U0001f6c3"] = ":customs:",
+ ["\U0001f969"] = ":cut_of_meat:",
+ ["\U0001f300"] = ":cyclone:",
+ ["\U0001f5e1\ufe0f"] = ":dagger:",
+ ["\U0001f5e1"] = ":dagger:",
+ ["\U0001f483"] = ":dancer:",
+ ["\U0001f483\U0001f3fb"] = ":dancer_tone1:",
+ ["\U0001f483\U0001f3fc"] = ":dancer_tone2:",
+ ["\U0001f483\U0001f3fd"] = ":dancer_tone3:",
+ ["\U0001f483\U0001f3fe"] = ":dancer_tone4:",
+ ["\U0001f483\U0001f3ff"] = ":dancer_tone5:",
+ ["\U0001f361"] = ":dango:",
+ ["\U0001f576\ufe0f"] = ":dark_sunglasses:",
+ ["\U0001f576"] = ":dark_sunglasses:",
+ ["\U0001f3af"] = ":dart:",
+ ["\U0001f4a8"] = ":dash:",
+ ["\U0001f4c5"] = ":date:",
+ ["\U0001f9cf\u200d\u2642\ufe0f"] = ":deaf_man:",
+ ["\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f"] = ":deaf_man_tone1:",
+ ["\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f"] = ":deaf_man_tone2:",
+ ["\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f"] = ":deaf_man_tone3:",
+ ["\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f"] = ":deaf_man_tone4:",
+ ["\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f"] = ":deaf_man_tone5:",
+ ["\U0001f9cf"] = ":deaf_person:",
+ ["\U0001f9cf\U0001f3fb"] = ":deaf_person_tone1:",
+ ["\U0001f9cf\U0001f3fc"] = ":deaf_person_tone2:",
+ ["\U0001f9cf\U0001f3fd"] = ":deaf_person_tone3:",
+ ["\U0001f9cf\U0001f3fe"] = ":deaf_person_tone4:",
+ ["\U0001f9cf\U0001f3ff"] = ":deaf_person_tone5:",
+ ["\U0001f9cf\u200d\u2640\ufe0f"] = ":deaf_woman:",
+ ["\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f"] = ":deaf_woman_tone1:",
+ ["\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f"] = ":deaf_woman_tone2:",
+ ["\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f"] = ":deaf_woman_tone3:",
+ ["\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f"] = ":deaf_woman_tone4:",
+ ["\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f"] = ":deaf_woman_tone5:",
+ ["\U0001f333"] = ":deciduous_tree:",
+ ["\U0001f98c"] = ":deer:",
+ ["\U0001f3ec"] = ":department_store:",
+ ["\U0001f3dc\ufe0f"] = ":desert:",
+ ["\U0001f3dc"] = ":desert:",
+ ["\U0001f5a5\ufe0f"] = ":desktop:",
+ ["\U0001f5a5"] = ":desktop:",
+ ["\U0001f575\ufe0f"] = ":detective:",
+ ["\U0001f575"] = ":detective:",
+ ["\U0001f575\U0001f3fb"] = ":detective_tone1:",
+ ["\U0001f575\U0001f3fc"] = ":detective_tone2:",
+ ["\U0001f575\U0001f3fd"] = ":detective_tone3:",
+ ["\U0001f575\U0001f3fe"] = ":detective_tone4:",
+ ["\U0001f575\U0001f3ff"] = ":detective_tone5:",
+ ["\U0001f4a0"] = ":diamond_shape_with_a_dot_inside:",
+ ["\u2666\ufe0f"] = ":diamonds:",
+ ["\u2666"] = ":diamonds:",
+ ["\U0001f61e"] = ":disappointed:",
+ ["\U0001f625"] = ":disappointed_relieved:",
+ ["\U0001f978"] = ":disguised_face:",
+ ["\U0001f5c2\ufe0f"] = ":dividers:",
+ ["\U0001f5c2"] = ":dividers:",
+ ["\U0001f93f"] = ":diving_mask:",
+ ["\U0001fa94"] = ":diya_lamp:",
+ ["\U0001f4ab"] = ":dizzy:",
+ ["\U0001f635"] = ":dizzy_face:",
+ ["\U0001f9ec"] = ":dna:",
+ ["\U0001f6af"] = ":do_not_litter:",
+ ["\U0001f9a4"] = ":dodo:",
+ ["\U0001f436"] = ":dog:",
+ ["\U0001f415"] = ":dog2:",
+ ["\U0001f4b5"] = ":dollar:",
+ ["\U0001f38e"] = ":dolls:",
+ ["\U0001f42c"] = ":dolphin:",
+ ["\U0001f6aa"] = ":door:",
+ ["\U0001f369"] = ":doughnut:",
+ ["\U0001f54a\ufe0f"] = ":dove:",
+ ["\U0001f54a"] = ":dove:",
+ ["\U0001f409"] = ":dragon:",
+ ["\U0001f432"] = ":dragon_face:",
+ ["\U0001f457"] = ":dress:",
+ ["\U0001f42a"] = ":dromedary_camel:",
+ ["\U0001f924"] = ":drooling_face:",
+ ["\U0001fa78"] = ":drop_of_blood:",
+ ["\U0001f4a7"] = ":droplet:",
+ ["\U0001f941"] = ":drum:",
+ ["\U0001f986"] = ":duck:",
+ ["\U0001f95f"] = ":dumpling:",
+ ["\U0001f4c0"] = ":dvd:",
+ ["\U0001f4e7"] = ":e_mail:",
+ ["\U0001f985"] = ":eagle:",
+ ["\U0001f442"] = ":ear:",
+ ["\U0001f33e"] = ":ear_of_rice:",
+ ["\U0001f442\U0001f3fb"] = ":ear_tone1:",
+ ["\U0001f442\U0001f3fc"] = ":ear_tone2:",
+ ["\U0001f442\U0001f3fd"] = ":ear_tone3:",
+ ["\U0001f442\U0001f3fe"] = ":ear_tone4:",
+ ["\U0001f442\U0001f3ff"] = ":ear_tone5:",
+ ["\U0001f9bb"] = ":ear_with_hearing_aid:",
+ ["\U0001f9bb\U0001f3fb"] = ":ear_with_hearing_aid_tone1:",
+ ["\U0001f9bb\U0001f3fc"] = ":ear_with_hearing_aid_tone2:",
+ ["\U0001f9bb\U0001f3fd"] = ":ear_with_hearing_aid_tone3:",
+ ["\U0001f9bb\U0001f3fe"] = ":ear_with_hearing_aid_tone4:",
+ ["\U0001f9bb\U0001f3ff"] = ":ear_with_hearing_aid_tone5:",
+ ["\U0001f30d"] = ":earth_africa:",
+ ["\U0001f30e"] = ":earth_americas:",
+ ["\U0001f30f"] = ":earth_asia:",
+ ["\U0001f95a"] = ":egg:",
+ ["\U0001f346"] = ":eggplant:",
+ ["\u0038\ufe0f\u20e3"] = ":eight:",
+ ["\u0038\u20e3"] = ":eight:",
+ ["\u2734\ufe0f"] = ":eight_pointed_black_star:",
+ ["\u2734"] = ":eight_pointed_black_star:",
+ ["\u2733\ufe0f"] = ":eight_spoked_asterisk:",
+ ["\u2733"] = ":eight_spoked_asterisk:",
+ ["\u23cf\ufe0f"] = ":eject:",
+ ["\u23cf"] = ":eject:",
+ ["\U0001f50c"] = ":electric_plug:",
+ ["\U0001f418"] = ":elephant:",
+ ["\U0001f6d7"] = ":elevator:",
+ ["\U0001f9dd"] = ":elf:",
+ ["\U0001f9dd\U0001f3fb"] = ":elf_tone1:",
+ ["\U0001f9dd\U0001f3fc"] = ":elf_tone2:",
+ ["\U0001f9dd\U0001f3fd"] = ":elf_tone3:",
+ ["\U0001f9dd\U0001f3fe"] = ":elf_tone4:",
+ ["\U0001f9dd\U0001f3ff"] = ":elf_tone5:",
+ ["\U0001f51a"] = ":end:",
+ ["\U0001f3f4\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f"] = ":england:",
+ ["\u2709\ufe0f"] = ":envelope:",
+ ["\u2709"] = ":envelope:",
+ ["\U0001f4e9"] = ":envelope_with_arrow:",
+ ["\U0001f4b6"] = ":euro:",
+ ["\U0001f3f0"] = ":european_castle:",
+ ["\U0001f3e4"] = ":european_post_office:",
+ ["\U0001f332"] = ":evergreen_tree:",
+ ["\u2757"] = ":exclamation:",
+ ["\U0001f92f"] = ":exploding_head:",
+ ["\U0001f611"] = ":expressionless:",
+ ["\U0001f441\ufe0f"] = ":eye:",
+ ["\U0001f441"] = ":eye:",
+ ["\U0001f441\u200d\U0001f5e8"] = ":eye_in_speech_bubble:",
+ ["\U0001f453"] = ":eyeglasses:",
+ ["\U0001f440"] = ":eyes:",
+ ["\U0001f92e"] = ":face_vomiting:",
+ ["\U0001f92d"] = ":face_with_hand_over_mouth:",
+ ["\U0001f9d0"] = ":face_with_monocle:",
+ ["\U0001f928"] = ":face_with_raised_eyebrow:",
+ ["\U0001f92c"] = ":face_with_symbols_over_mouth:",
+ ["\U0001f3ed"] = ":factory:",
+ ["\U0001f9d1\u200d\U0001f3ed"] = ":factory_worker:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f3ed"] = ":factory_worker_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f3ed"] = ":factory_worker_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f3ed"] = ":factory_worker_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f3ed"] = ":factory_worker_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f3ed"] = ":factory_worker_tone5:",
+ ["\U0001f9da"] = ":fairy:",
+ ["\U0001f9da\U0001f3fb"] = ":fairy_tone1:",
+ ["\U0001f9da\U0001f3fc"] = ":fairy_tone2:",
+ ["\U0001f9da\U0001f3fd"] = ":fairy_tone3:",
+ ["\U0001f9da\U0001f3fe"] = ":fairy_tone4:",
+ ["\U0001f9da\U0001f3ff"] = ":fairy_tone5:",
+ ["\U0001f9c6"] = ":falafel:",
+ ["\U0001f342"] = ":fallen_leaf:",
+ ["\U0001f46a"] = ":family:",
+ ["\U0001f468\u200d\U0001f466"] = ":family_man_boy:",
+ ["\U0001f468\u200d\U0001f466\u200d\U0001f466"] = ":family_man_boy_boy:",
+ ["\U0001f468\u200d\U0001f467"] = ":family_man_girl:",
+ ["\U0001f468\u200d\U0001f467\u200d\U0001f466"] = ":family_man_girl_boy:",
+ ["\U0001f468\u200d\U0001f467\u200d\U0001f467"] = ":family_man_girl_girl:",
+ ["\U0001f468\u200d\U0001f469\u200d\U0001f466"] = ":family_man_woman_boy:",
+ ["\U0001f468\u200d\U0001f468\u200d\U0001f466"] = ":family_mmb:",
+ ["\U0001f468\u200d\U0001f468\u200d\U0001f466\u200d\U0001f466"] = ":family_mmbb:",
+ ["\U0001f468\u200d\U0001f468\u200d\U0001f467"] = ":family_mmg:",
+ ["\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f466"] = ":family_mmgb:",
+ ["\U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f467"] = ":family_mmgg:",
+ ["\U0001f468\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466"] = ":family_mwbb:",
+ ["\U0001f468\u200d\U0001f469\u200d\U0001f467"] = ":family_mwg:",
+ ["\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466"] = ":family_mwgb:",
+ ["\U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467"] = ":family_mwgg:",
+ ["\U0001f469\u200d\U0001f466"] = ":family_woman_boy:",
+ ["\U0001f469\u200d\U0001f466\u200d\U0001f466"] = ":family_woman_boy_boy:",
+ ["\U0001f469\u200d\U0001f467"] = ":family_woman_girl:",
+ ["\U0001f469\u200d\U0001f467\u200d\U0001f466"] = ":family_woman_girl_boy:",
+ ["\U0001f469\u200d\U0001f467\u200d\U0001f467"] = ":family_woman_girl_girl:",
+ ["\U0001f469\u200d\U0001f469\u200d\U0001f466"] = ":family_wwb:",
+ ["\U0001f469\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466"] = ":family_wwbb:",
+ ["\U0001f469\u200d\U0001f469\u200d\U0001f467"] = ":family_wwg:",
+ ["\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466"] = ":family_wwgb:",
+ ["\U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467"] = ":family_wwgg:",
+ ["\U0001f9d1\u200d\U0001f33e"] = ":farmer:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f33e"] = ":farmer_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f33e"] = ":farmer_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f33e"] = ":farmer_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f33e"] = ":farmer_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f33e"] = ":farmer_tone5:",
+ ["\u23e9"] = ":fast_forward:",
+ ["\U0001f4e0"] = ":fax:",
+ ["\U0001f628"] = ":fearful:",
+ ["\U0001fab6"] = ":feather:",
+ ["\U0001f43e"] = ":feet:",
+ ["\u2640\ufe0f"] = ":female_sign:",
+ ["\u2640"] = ":female_sign:",
+ ["\U0001f3a1"] = ":ferris_wheel:",
+ ["\u26f4\ufe0f"] = ":ferry:",
+ ["\u26f4"] = ":ferry:",
+ ["\U0001f3d1"] = ":field_hockey:",
+ ["\U0001f5c4\ufe0f"] = ":file_cabinet:",
+ ["\U0001f5c4"] = ":file_cabinet:",
+ ["\U0001f4c1"] = ":file_folder:",
+ ["\U0001f39e\ufe0f"] = ":film_frames:",
+ ["\U0001f39e"] = ":film_frames:",
+ ["\U0001f91e"] = ":fingers_crossed:",
+ ["\U0001f91e\U0001f3fb"] = ":fingers_crossed_tone1:",
+ ["\U0001f91e\U0001f3fc"] = ":fingers_crossed_tone2:",
+ ["\U0001f91e\U0001f3fd"] = ":fingers_crossed_tone3:",
+ ["\U0001f91e\U0001f3fe"] = ":fingers_crossed_tone4:",
+ ["\U0001f91e\U0001f3ff"] = ":fingers_crossed_tone5:",
+ ["\U0001f525"] = ":fire:",
+ ["\U0001f692"] = ":fire_engine:",
+ ["\U0001f9ef"] = ":fire_extinguisher:",
+ ["\U0001f9e8"] = ":firecracker:",
+ ["\U0001f9d1\u200d\U0001f692"] = ":firefighter:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f692"] = ":firefighter_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f692"] = ":firefighter_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f692"] = ":firefighter_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f692"] = ":firefighter_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f692"] = ":firefighter_tone5:",
+ ["\U0001f386"] = ":fireworks:",
+ ["\U0001f947"] = ":first_place:",
+ ["\U0001f313"] = ":first_quarter_moon:",
+ ["\U0001f31b"] = ":first_quarter_moon_with_face:",
+ ["\U0001f41f"] = ":fish:",
+ ["\U0001f365"] = ":fish_cake:",
+ ["\U0001f3a3"] = ":fishing_pole_and_fish:",
+ ["\u270a"] = ":fist:",
+ ["\u270a\U0001f3fb"] = ":fist_tone1:",
+ ["\u270a\U0001f3fc"] = ":fist_tone2:",
+ ["\u270a\U0001f3fd"] = ":fist_tone3:",
+ ["\u270a\U0001f3fe"] = ":fist_tone4:",
+ ["\u270a\U0001f3ff"] = ":fist_tone5:",
+ ["\u0035\ufe0f\u20e3"] = ":five:",
+ ["\u0035\u20e3"] = ":five:",
+ ["\U0001f1e6\U0001f1e8"] = ":flag_ac:",
+ ["\U0001f1e6\U0001f1e9"] = ":flag_ad:",
+ ["\U0001f1e6\U0001f1ea"] = ":flag_ae:",
+ ["\U0001f1e6\U0001f1eb"] = ":flag_af:",
+ ["\U0001f1e6\U0001f1ec"] = ":flag_ag:",
+ ["\U0001f1e6\U0001f1ee"] = ":flag_ai:",
+ ["\U0001f1e6\U0001f1f1"] = ":flag_al:",
+ ["\U0001f1e6\U0001f1f2"] = ":flag_am:",
+ ["\U0001f1e6\U0001f1f4"] = ":flag_ao:",
+ ["\U0001f1e6\U0001f1f6"] = ":flag_aq:",
+ ["\U0001f1e6\U0001f1f7"] = ":flag_ar:",
+ ["\U0001f1e6\U0001f1f8"] = ":flag_as:",
+ ["\U0001f1e6\U0001f1f9"] = ":flag_at:",
+ ["\U0001f1e6\U0001f1fa"] = ":flag_au:",
+ ["\U0001f1e6\U0001f1fc"] = ":flag_aw:",
+ ["\U0001f1e6\U0001f1fd"] = ":flag_ax:",
+ ["\U0001f1e6\U0001f1ff"] = ":flag_az:",
+ ["\U0001f1e7\U0001f1e6"] = ":flag_ba:",
+ ["\U0001f1e7\U0001f1e7"] = ":flag_bb:",
+ ["\U0001f1e7\U0001f1e9"] = ":flag_bd:",
+ ["\U0001f1e7\U0001f1ea"] = ":flag_be:",
+ ["\U0001f1e7\U0001f1eb"] = ":flag_bf:",
+ ["\U0001f1e7\U0001f1ec"] = ":flag_bg:",
+ ["\U0001f1e7\U0001f1ed"] = ":flag_bh:",
+ ["\U0001f1e7\U0001f1ee"] = ":flag_bi:",
+ ["\U0001f1e7\U0001f1ef"] = ":flag_bj:",
+ ["\U0001f1e7\U0001f1f1"] = ":flag_bl:",
+ ["\U0001f3f4"] = ":flag_black:",
+ ["\U0001f1e7\U0001f1f2"] = ":flag_bm:",
+ ["\U0001f1e7\U0001f1f3"] = ":flag_bn:",
+ ["\U0001f1e7\U0001f1f4"] = ":flag_bo:",
+ ["\U0001f1e7\U0001f1f6"] = ":flag_bq:",
+ ["\U0001f1e7\U0001f1f7"] = ":flag_br:",
+ ["\U0001f1e7\U0001f1f8"] = ":flag_bs:",
+ ["\U0001f1e7\U0001f1f9"] = ":flag_bt:",
+ ["\U0001f1e7\U0001f1fb"] = ":flag_bv:",
+ ["\U0001f1e7\U0001f1fc"] = ":flag_bw:",
+ ["\U0001f1e7\U0001f1fe"] = ":flag_by:",
+ ["\U0001f1e7\U0001f1ff"] = ":flag_bz:",
+ ["\U0001f1e8\U0001f1e6"] = ":flag_ca:",
+ ["\U0001f1e8\U0001f1e8"] = ":flag_cc:",
+ ["\U0001f1e8\U0001f1e9"] = ":flag_cd:",
+ ["\U0001f1e8\U0001f1eb"] = ":flag_cf:",
+ ["\U0001f1e8\U0001f1ec"] = ":flag_cg:",
+ ["\U0001f1e8\U0001f1ed"] = ":flag_ch:",
+ ["\U0001f1e8\U0001f1ee"] = ":flag_ci:",
+ ["\U0001f1e8\U0001f1f0"] = ":flag_ck:",
+ ["\U0001f1e8\U0001f1f1"] = ":flag_cl:",
+ ["\U0001f1e8\U0001f1f2"] = ":flag_cm:",
+ ["\U0001f1e8\U0001f1f3"] = ":flag_cn:",
+ ["\U0001f1e8\U0001f1f4"] = ":flag_co:",
+ ["\U0001f1e8\U0001f1f5"] = ":flag_cp:",
+ ["\U0001f1e8\U0001f1f7"] = ":flag_cr:",
+ ["\U0001f1e8\U0001f1fa"] = ":flag_cu:",
+ ["\U0001f1e8\U0001f1fb"] = ":flag_cv:",
+ ["\U0001f1e8\U0001f1fc"] = ":flag_cw:",
+ ["\U0001f1e8\U0001f1fd"] = ":flag_cx:",
+ ["\U0001f1e8\U0001f1fe"] = ":flag_cy:",
+ ["\U0001f1e8\U0001f1ff"] = ":flag_cz:",
+ ["\U0001f1e9\U0001f1ea"] = ":flag_de:",
+ ["\U0001f1e9\U0001f1ec"] = ":flag_dg:",
+ ["\U0001f1e9\U0001f1ef"] = ":flag_dj:",
+ ["\U0001f1e9\U0001f1f0"] = ":flag_dk:",
+ ["\U0001f1e9\U0001f1f2"] = ":flag_dm:",
+ ["\U0001f1e9\U0001f1f4"] = ":flag_do:",
+ ["\U0001f1e9\U0001f1ff"] = ":flag_dz:",
+ ["\U0001f1ea\U0001f1e6"] = ":flag_ea:",
+ ["\U0001f1ea\U0001f1e8"] = ":flag_ec:",
+ ["\U0001f1ea\U0001f1ea"] = ":flag_ee:",
+ ["\U0001f1ea\U0001f1ec"] = ":flag_eg:",
+ ["\U0001f1ea\U0001f1ed"] = ":flag_eh:",
+ ["\U0001f1ea\U0001f1f7"] = ":flag_er:",
+ ["\U0001f1ea\U0001f1f8"] = ":flag_es:",
+ ["\U0001f1ea\U0001f1f9"] = ":flag_et:",
+ ["\U0001f1ea\U0001f1fa"] = ":flag_eu:",
+ ["\U0001f1eb\U0001f1ee"] = ":flag_fi:",
+ ["\U0001f1eb\U0001f1ef"] = ":flag_fj:",
+ ["\U0001f1eb\U0001f1f0"] = ":flag_fk:",
+ ["\U0001f1eb\U0001f1f2"] = ":flag_fm:",
+ ["\U0001f1eb\U0001f1f4"] = ":flag_fo:",
+ ["\U0001f1eb\U0001f1f7"] = ":flag_fr:",
+ ["\U0001f1ec\U0001f1e6"] = ":flag_ga:",
+ ["\U0001f1ec\U0001f1e7"] = ":flag_gb:",
+ ["\U0001f1ec\U0001f1e9"] = ":flag_gd:",
+ ["\U0001f1ec\U0001f1ea"] = ":flag_ge:",
+ ["\U0001f1ec\U0001f1eb"] = ":flag_gf:",
+ ["\U0001f1ec\U0001f1ec"] = ":flag_gg:",
+ ["\U0001f1ec\U0001f1ed"] = ":flag_gh:",
+ ["\U0001f1ec\U0001f1ee"] = ":flag_gi:",
+ ["\U0001f1ec\U0001f1f1"] = ":flag_gl:",
+ ["\U0001f1ec\U0001f1f2"] = ":flag_gm:",
+ ["\U0001f1ec\U0001f1f3"] = ":flag_gn:",
+ ["\U0001f1ec\U0001f1f5"] = ":flag_gp:",
+ ["\U0001f1ec\U0001f1f6"] = ":flag_gq:",
+ ["\U0001f1ec\U0001f1f7"] = ":flag_gr:",
+ ["\U0001f1ec\U0001f1f8"] = ":flag_gs:",
+ ["\U0001f1ec\U0001f1f9"] = ":flag_gt:",
+ ["\U0001f1ec\U0001f1fa"] = ":flag_gu:",
+ ["\U0001f1ec\U0001f1fc"] = ":flag_gw:",
+ ["\U0001f1ec\U0001f1fe"] = ":flag_gy:",
+ ["\U0001f1ed\U0001f1f0"] = ":flag_hk:",
+ ["\U0001f1ed\U0001f1f2"] = ":flag_hm:",
+ ["\U0001f1ed\U0001f1f3"] = ":flag_hn:",
+ ["\U0001f1ed\U0001f1f7"] = ":flag_hr:",
+ ["\U0001f1ed\U0001f1f9"] = ":flag_ht:",
+ ["\U0001f1ed\U0001f1fa"] = ":flag_hu:",
+ ["\U0001f1ee\U0001f1e8"] = ":flag_ic:",
+ ["\U0001f1ee\U0001f1e9"] = ":flag_id:",
+ ["\U0001f1ee\U0001f1ea"] = ":flag_ie:",
+ ["\U0001f1ee\U0001f1f1"] = ":flag_il:",
+ ["\U0001f1ee\U0001f1f2"] = ":flag_im:",
+ ["\U0001f1ee\U0001f1f3"] = ":flag_in:",
+ ["\U0001f1ee\U0001f1f4"] = ":flag_io:",
+ ["\U0001f1ee\U0001f1f6"] = ":flag_iq:",
+ ["\U0001f1ee\U0001f1f7"] = ":flag_ir:",
+ ["\U0001f1ee\U0001f1f8"] = ":flag_is:",
+ ["\U0001f1ee\U0001f1f9"] = ":flag_it:",
+ ["\U0001f1ef\U0001f1ea"] = ":flag_je:",
+ ["\U0001f1ef\U0001f1f2"] = ":flag_jm:",
+ ["\U0001f1ef\U0001f1f4"] = ":flag_jo:",
+ ["\U0001f1ef\U0001f1f5"] = ":flag_jp:",
+ ["\U0001f1f0\U0001f1ea"] = ":flag_ke:",
+ ["\U0001f1f0\U0001f1ec"] = ":flag_kg:",
+ ["\U0001f1f0\U0001f1ed"] = ":flag_kh:",
+ ["\U0001f1f0\U0001f1ee"] = ":flag_ki:",
+ ["\U0001f1f0\U0001f1f2"] = ":flag_km:",
+ ["\U0001f1f0\U0001f1f3"] = ":flag_kn:",
+ ["\U0001f1f0\U0001f1f5"] = ":flag_kp:",
+ ["\U0001f1f0\U0001f1f7"] = ":flag_kr:",
+ ["\U0001f1f0\U0001f1fc"] = ":flag_kw:",
+ ["\U0001f1f0\U0001f1fe"] = ":flag_ky:",
+ ["\U0001f1f0\U0001f1ff"] = ":flag_kz:",
+ ["\U0001f1f1\U0001f1e6"] = ":flag_la:",
+ ["\U0001f1f1\U0001f1e7"] = ":flag_lb:",
+ ["\U0001f1f1\U0001f1e8"] = ":flag_lc:",
+ ["\U0001f1f1\U0001f1ee"] = ":flag_li:",
+ ["\U0001f1f1\U0001f1f0"] = ":flag_lk:",
+ ["\U0001f1f1\U0001f1f7"] = ":flag_lr:",
+ ["\U0001f1f1\U0001f1f8"] = ":flag_ls:",
+ ["\U0001f1f1\U0001f1f9"] = ":flag_lt:",
+ ["\U0001f1f1\U0001f1fa"] = ":flag_lu:",
+ ["\U0001f1f1\U0001f1fb"] = ":flag_lv:",
+ ["\U0001f1f1\U0001f1fe"] = ":flag_ly:",
+ ["\U0001f1f2\U0001f1e6"] = ":flag_ma:",
+ ["\U0001f1f2\U0001f1e8"] = ":flag_mc:",
+ ["\U0001f1f2\U0001f1e9"] = ":flag_md:",
+ ["\U0001f1f2\U0001f1ea"] = ":flag_me:",
+ ["\U0001f1f2\U0001f1eb"] = ":flag_mf:",
+ ["\U0001f1f2\U0001f1ec"] = ":flag_mg:",
+ ["\U0001f1f2\U0001f1ed"] = ":flag_mh:",
+ ["\U0001f1f2\U0001f1f0"] = ":flag_mk:",
+ ["\U0001f1f2\U0001f1f1"] = ":flag_ml:",
+ ["\U0001f1f2\U0001f1f2"] = ":flag_mm:",
+ ["\U0001f1f2\U0001f1f3"] = ":flag_mn:",
+ ["\U0001f1f2\U0001f1f4"] = ":flag_mo:",
+ ["\U0001f1f2\U0001f1f5"] = ":flag_mp:",
+ ["\U0001f1f2\U0001f1f6"] = ":flag_mq:",
+ ["\U0001f1f2\U0001f1f7"] = ":flag_mr:",
+ ["\U0001f1f2\U0001f1f8"] = ":flag_ms:",
+ ["\U0001f1f2\U0001f1f9"] = ":flag_mt:",
+ ["\U0001f1f2\U0001f1fa"] = ":flag_mu:",
+ ["\U0001f1f2\U0001f1fb"] = ":flag_mv:",
+ ["\U0001f1f2\U0001f1fc"] = ":flag_mw:",
+ ["\U0001f1f2\U0001f1fd"] = ":flag_mx:",
+ ["\U0001f1f2\U0001f1fe"] = ":flag_my:",
+ ["\U0001f1f2\U0001f1ff"] = ":flag_mz:",
+ ["\U0001f1f3\U0001f1e6"] = ":flag_na:",
+ ["\U0001f1f3\U0001f1e8"] = ":flag_nc:",
+ ["\U0001f1f3\U0001f1ea"] = ":flag_ne:",
+ ["\U0001f1f3\U0001f1eb"] = ":flag_nf:",
+ ["\U0001f1f3\U0001f1ec"] = ":flag_ng:",
+ ["\U0001f1f3\U0001f1ee"] = ":flag_ni:",
+ ["\U0001f1f3\U0001f1f1"] = ":flag_nl:",
+ ["\U0001f1f3\U0001f1f4"] = ":flag_no:",
+ ["\U0001f1f3\U0001f1f5"] = ":flag_np:",
+ ["\U0001f1f3\U0001f1f7"] = ":flag_nr:",
+ ["\U0001f1f3\U0001f1fa"] = ":flag_nu:",
+ ["\U0001f1f3\U0001f1ff"] = ":flag_nz:",
+ ["\U0001f1f4\U0001f1f2"] = ":flag_om:",
+ ["\U0001f1f5\U0001f1e6"] = ":flag_pa:",
+ ["\U0001f1f5\U0001f1ea"] = ":flag_pe:",
+ ["\U0001f1f5\U0001f1eb"] = ":flag_pf:",
+ ["\U0001f1f5\U0001f1ec"] = ":flag_pg:",
+ ["\U0001f1f5\U0001f1ed"] = ":flag_ph:",
+ ["\U0001f1f5\U0001f1f0"] = ":flag_pk:",
+ ["\U0001f1f5\U0001f1f1"] = ":flag_pl:",
+ ["\U0001f1f5\U0001f1f2"] = ":flag_pm:",
+ ["\U0001f1f5\U0001f1f3"] = ":flag_pn:",
+ ["\U0001f1f5\U0001f1f7"] = ":flag_pr:",
+ ["\U0001f1f5\U0001f1f8"] = ":flag_ps:",
+ ["\U0001f1f5\U0001f1f9"] = ":flag_pt:",
+ ["\U0001f1f5\U0001f1fc"] = ":flag_pw:",
+ ["\U0001f1f5\U0001f1fe"] = ":flag_py:",
+ ["\U0001f1f6\U0001f1e6"] = ":flag_qa:",
+ ["\U0001f1f7\U0001f1ea"] = ":flag_re:",
+ ["\U0001f1f7\U0001f1f4"] = ":flag_ro:",
+ ["\U0001f1f7\U0001f1f8"] = ":flag_rs:",
+ ["\U0001f1f7\U0001f1fa"] = ":flag_ru:",
+ ["\U0001f1f7\U0001f1fc"] = ":flag_rw:",
+ ["\U0001f1f8\U0001f1e6"] = ":flag_sa:",
+ ["\U0001f1f8\U0001f1e7"] = ":flag_sb:",
+ ["\U0001f1f8\U0001f1e8"] = ":flag_sc:",
+ ["\U0001f1f8\U0001f1e9"] = ":flag_sd:",
+ ["\U0001f1f8\U0001f1ea"] = ":flag_se:",
+ ["\U0001f1f8\U0001f1ec"] = ":flag_sg:",
+ ["\U0001f1f8\U0001f1ed"] = ":flag_sh:",
+ ["\U0001f1f8\U0001f1ee"] = ":flag_si:",
+ ["\U0001f1f8\U0001f1ef"] = ":flag_sj:",
+ ["\U0001f1f8\U0001f1f0"] = ":flag_sk:",
+ ["\U0001f1f8\U0001f1f1"] = ":flag_sl:",
+ ["\U0001f1f8\U0001f1f2"] = ":flag_sm:",
+ ["\U0001f1f8\U0001f1f3"] = ":flag_sn:",
+ ["\U0001f1f8\U0001f1f4"] = ":flag_so:",
+ ["\U0001f1f8\U0001f1f7"] = ":flag_sr:",
+ ["\U0001f1f8\U0001f1f8"] = ":flag_ss:",
+ ["\U0001f1f8\U0001f1f9"] = ":flag_st:",
+ ["\U0001f1f8\U0001f1fb"] = ":flag_sv:",
+ ["\U0001f1f8\U0001f1fd"] = ":flag_sx:",
+ ["\U0001f1f8\U0001f1fe"] = ":flag_sy:",
+ ["\U0001f1f8\U0001f1ff"] = ":flag_sz:",
+ ["\U0001f1f9\U0001f1e6"] = ":flag_ta:",
+ ["\U0001f1f9\U0001f1e8"] = ":flag_tc:",
+ ["\U0001f1f9\U0001f1e9"] = ":flag_td:",
+ ["\U0001f1f9\U0001f1eb"] = ":flag_tf:",
+ ["\U0001f1f9\U0001f1ec"] = ":flag_tg:",
+ ["\U0001f1f9\U0001f1ed"] = ":flag_th:",
+ ["\U0001f1f9\U0001f1ef"] = ":flag_tj:",
+ ["\U0001f1f9\U0001f1f0"] = ":flag_tk:",
+ ["\U0001f1f9\U0001f1f1"] = ":flag_tl:",
+ ["\U0001f1f9\U0001f1f2"] = ":flag_tm:",
+ ["\U0001f1f9\U0001f1f3"] = ":flag_tn:",
+ ["\U0001f1f9\U0001f1f4"] = ":flag_to:",
+ ["\U0001f1f9\U0001f1f7"] = ":flag_tr:",
+ ["\U0001f1f9\U0001f1f9"] = ":flag_tt:",
+ ["\U0001f1f9\U0001f1fb"] = ":flag_tv:",
+ ["\U0001f1f9\U0001f1fc"] = ":flag_tw:",
+ ["\U0001f1f9\U0001f1ff"] = ":flag_tz:",
+ ["\U0001f1fa\U0001f1e6"] = ":flag_ua:",
+ ["\U0001f1fa\U0001f1ec"] = ":flag_ug:",
+ ["\U0001f1fa\U0001f1f2"] = ":flag_um:",
+ ["\U0001f1fa\U0001f1f8"] = ":flag_us:",
+ ["\U0001f1fa\U0001f1fe"] = ":flag_uy:",
+ ["\U0001f1fa\U0001f1ff"] = ":flag_uz:",
+ ["\U0001f1fb\U0001f1e6"] = ":flag_va:",
+ ["\U0001f1fb\U0001f1e8"] = ":flag_vc:",
+ ["\U0001f1fb\U0001f1ea"] = ":flag_ve:",
+ ["\U0001f1fb\U0001f1ec"] = ":flag_vg:",
+ ["\U0001f1fb\U0001f1ee"] = ":flag_vi:",
+ ["\U0001f1fb\U0001f1f3"] = ":flag_vn:",
+ ["\U0001f1fb\U0001f1fa"] = ":flag_vu:",
+ ["\U0001f1fc\U0001f1eb"] = ":flag_wf:",
+ ["\U0001f3f3\ufe0f"] = ":flag_white:",
+ ["\U0001f3f3"] = ":flag_white:",
+ ["\U0001f1fc\U0001f1f8"] = ":flag_ws:",
+ ["\U0001f1fd\U0001f1f0"] = ":flag_xk:",
+ ["\U0001f1fe\U0001f1ea"] = ":flag_ye:",
+ ["\U0001f1fe\U0001f1f9"] = ":flag_yt:",
+ ["\U0001f1ff\U0001f1e6"] = ":flag_za:",
+ ["\U0001f1ff\U0001f1f2"] = ":flag_zm:",
+ ["\U0001f1ff\U0001f1fc"] = ":flag_zw:",
+ ["\U0001f38f"] = ":flags:",
+ ["\U0001f9a9"] = ":flamingo:",
+ ["\U0001f526"] = ":flashlight:",
+ ["\U0001fad3"] = ":flatbread:",
+ ["\u269c\ufe0f"] = ":fleur_de_lis:",
+ ["\u269c"] = ":fleur_de_lis:",
+ ["\U0001f4be"] = ":floppy_disk:",
+ ["\U0001f3b4"] = ":flower_playing_cards:",
+ ["\U0001f633"] = ":flushed:",
+ ["\U0001fab0"] = ":fly:",
+ ["\U0001f94f"] = ":flying_disc:",
+ ["\U0001f6f8"] = ":flying_saucer:",
+ ["\U0001f32b\ufe0f"] = ":fog:",
+ ["\U0001f32b"] = ":fog:",
+ ["\U0001f301"] = ":foggy:",
+ ["\U0001fad5"] = ":fondue:",
+ ["\U0001f9b6"] = ":foot:",
+ ["\U0001f9b6\U0001f3fb"] = ":foot_tone1:",
+ ["\U0001f9b6\U0001f3fc"] = ":foot_tone2:",
+ ["\U0001f9b6\U0001f3fd"] = ":foot_tone3:",
+ ["\U0001f9b6\U0001f3fe"] = ":foot_tone4:",
+ ["\U0001f9b6\U0001f3ff"] = ":foot_tone5:",
+ ["\U0001f3c8"] = ":football:",
+ ["\U0001f463"] = ":footprints:",
+ ["\U0001f374"] = ":fork_and_knife:",
+ ["\U0001f37d\ufe0f"] = ":fork_knife_plate:",
+ ["\U0001f37d"] = ":fork_knife_plate:",
+ ["\U0001f960"] = ":fortune_cookie:",
+ ["\u26f2"] = ":fountain:",
+ ["\u0034\ufe0f\u20e3"] = ":four:",
+ ["\u0034\u20e3"] = ":four:",
+ ["\U0001f340"] = ":four_leaf_clover:",
+ ["\U0001f98a"] = ":fox:",
+ ["\U0001f5bc\ufe0f"] = ":frame_photo:",
+ ["\U0001f5bc"] = ":frame_photo:",
+ ["\U0001f193"] = ":free:",
+ ["\U0001f956"] = ":french_bread:",
+ ["\U0001f364"] = ":fried_shrimp:",
+ ["\U0001f35f"] = ":fries:",
+ ["\U0001f438"] = ":frog:",
+ ["\U0001f626"] = ":frowning:",
+ ["\u2639\ufe0f"] = ":frowning2:",
+ ["\u2639"] = ":frowning2:",
+ ["\u26fd"] = ":fuelpump:",
+ ["\U0001f315"] = ":full_moon:",
+ ["\U0001f31d"] = ":full_moon_with_face:",
+ ["\U0001f3b2"] = ":game_die:",
+ ["\U0001f9c4"] = ":garlic:",
+ ["\u2699\ufe0f"] = ":gear:",
+ ["\u2699"] = ":gear:",
+ ["\U0001f48e"] = ":gem:",
+ ["\u264a"] = ":gemini:",
+ ["\U0001f9de"] = ":genie:",
+ ["\U0001f47b"] = ":ghost:",
+ ["\U0001f381"] = ":gift:",
+ ["\U0001f49d"] = ":gift_heart:",
+ ["\U0001f992"] = ":giraffe:",
+ ["\U0001f467"] = ":girl:",
+ ["\U0001f467\U0001f3fb"] = ":girl_tone1:",
+ ["\U0001f467\U0001f3fc"] = ":girl_tone2:",
+ ["\U0001f467\U0001f3fd"] = ":girl_tone3:",
+ ["\U0001f467\U0001f3fe"] = ":girl_tone4:",
+ ["\U0001f467\U0001f3ff"] = ":girl_tone5:",
+ ["\U0001f310"] = ":globe_with_meridians:",
+ ["\U0001f9e4"] = ":gloves:",
+ ["\U0001f945"] = ":goal:",
+ ["\U0001f410"] = ":goat:",
+ ["\U0001f97d"] = ":goggles:",
+ ["\u26f3"] = ":golf:",
+ ["\U0001f98d"] = ":gorilla:",
+ ["\U0001f347"] = ":grapes:",
+ ["\U0001f34f"] = ":green_apple:",
+ ["\U0001f4d7"] = ":green_book:",
+ ["\U0001f7e2"] = ":green_circle:",
+ ["\U0001f49a"] = ":green_heart:",
+ ["\U0001f7e9"] = ":green_square:",
+ ["\u2755"] = ":grey_exclamation:",
+ ["\u2754"] = ":grey_question:",
+ ["\U0001f62c"] = ":grimacing:",
+ ["\U0001f601"] = ":grin:",
+ ["\U0001f600"] = ":grinning:",
+ ["\U0001f482"] = ":guard:",
+ ["\U0001f482\U0001f3fb"] = ":guard_tone1:",
+ ["\U0001f482\U0001f3fc"] = ":guard_tone2:",
+ ["\U0001f482\U0001f3fd"] = ":guard_tone3:",
+ ["\U0001f482\U0001f3fe"] = ":guard_tone4:",
+ ["\U0001f482\U0001f3ff"] = ":guard_tone5:",
+ ["\U0001f9ae"] = ":guide_dog:",
+ ["\U0001f3b8"] = ":guitar:",
+ ["\U0001f52b"] = ":gun:",
+ ["\U0001f354"] = ":hamburger:",
+ ["\U0001f528"] = ":hammer:",
+ ["\u2692\ufe0f"] = ":hammer_pick:",
+ ["\u2692"] = ":hammer_pick:",
+ ["\U0001f439"] = ":hamster:",
+ ["\U0001f590\ufe0f"] = ":hand_splayed:",
+ ["\U0001f590"] = ":hand_splayed:",
+ ["\U0001f590\U0001f3fb"] = ":hand_splayed_tone1:",
+ ["\U0001f590\U0001f3fc"] = ":hand_splayed_tone2:",
+ ["\U0001f590\U0001f3fd"] = ":hand_splayed_tone3:",
+ ["\U0001f590\U0001f3fe"] = ":hand_splayed_tone4:",
+ ["\U0001f590\U0001f3ff"] = ":hand_splayed_tone5:",
+ ["\U0001f45c"] = ":handbag:",
+ ["\U0001f91d"] = ":handshake:",
+ ["\u0023\ufe0f\u20e3"] = ":hash:",
+ ["\u0023\u20e3"] = ":hash:",
+ ["\U0001f425"] = ":hatched_chick:",
+ ["\U0001f423"] = ":hatching_chick:",
+ ["\U0001f915"] = ":head_bandage:",
+ ["\U0001f3a7"] = ":headphones:",
+ ["\U0001faa6"] = ":headstone:",
+ ["\U0001f9d1\u200d\u2695\ufe0f"] = ":health_worker:",
+ ["\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f"] = ":health_worker_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f"] = ":health_worker_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f"] = ":health_worker_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f"] = ":health_worker_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f"] = ":health_worker_tone5:",
+ ["\U0001f649"] = ":hear_no_evil:",
+ ["\u2764\ufe0f"] = ":heart:",
+ ["\u2764"] = ":heart:",
+ ["\U0001f49f"] = ":heart_decoration:",
+ ["\u2763\ufe0f"] = ":heart_exclamation:",
+ ["\u2763"] = ":heart_exclamation:",
+ ["\U0001f60d"] = ":heart_eyes:",
+ ["\U0001f63b"] = ":heart_eyes_cat:",
+ ["\U0001f493"] = ":heartbeat:",
+ ["\U0001f497"] = ":heartpulse:",
+ ["\u2665\ufe0f"] = ":hearts:",
+ ["\u2665"] = ":hearts:",
+ ["\u2714\ufe0f"] = ":heavy_check_mark:",
+ ["\u2714"] = ":heavy_check_mark:",
+ ["\u2797"] = ":heavy_division_sign:",
+ ["\U0001f4b2"] = ":heavy_dollar_sign:",
+ ["\u2796"] = ":heavy_minus_sign:",
+ ["\u2716\ufe0f"] = ":heavy_multiplication_x:",
+ ["\u2716"] = ":heavy_multiplication_x:",
+ ["\u2795"] = ":heavy_plus_sign:",
+ ["\U0001f994"] = ":hedgehog:",
+ ["\U0001f681"] = ":helicopter:",
+ ["\u26d1\ufe0f"] = ":helmet_with_cross:",
+ ["\u26d1"] = ":helmet_with_cross:",
+ ["\U0001f33f"] = ":herb:",
+ ["\U0001f33a"] = ":hibiscus:",
+ ["\U0001f506"] = ":high_brightness:",
+ ["\U0001f460"] = ":high_heel:",
+ ["\U0001f97e"] = ":hiking_boot:",
+ ["\U0001f6d5"] = ":hindu_temple:",
+ ["\U0001f99b"] = ":hippopotamus:",
+ ["\U0001f3d2"] = ":hockey:",
+ ["\U0001f573\ufe0f"] = ":hole:",
+ ["\U0001f573"] = ":hole:",
+ ["\U0001f3d8\ufe0f"] = ":homes:",
+ ["\U0001f3d8"] = ":homes:",
+ ["\U0001f36f"] = ":honey_pot:",
+ ["\U0001fa9d"] = ":hook:",
+ ["\U0001f434"] = ":horse:",
+ ["\U0001f3c7"] = ":horse_racing:",
+ ["\U0001f3c7\U0001f3fb"] = ":horse_racing_tone1:",
+ ["\U0001f3c7\U0001f3fc"] = ":horse_racing_tone2:",
+ ["\U0001f3c7\U0001f3fd"] = ":horse_racing_tone3:",
+ ["\U0001f3c7\U0001f3fe"] = ":horse_racing_tone4:",
+ ["\U0001f3c7\U0001f3ff"] = ":horse_racing_tone5:",
+ ["\U0001f3e5"] = ":hospital:",
+ ["\U0001f975"] = ":hot_face:",
+ ["\U0001f336\ufe0f"] = ":hot_pepper:",
+ ["\U0001f336"] = ":hot_pepper:",
+ ["\U0001f32d"] = ":hotdog:",
+ ["\U0001f3e8"] = ":hotel:",
+ ["\u2668\ufe0f"] = ":hotsprings:",
+ ["\u2668"] = ":hotsprings:",
+ ["\u231b"] = ":hourglass:",
+ ["\u23f3"] = ":hourglass_flowing_sand:",
+ ["\U0001f3e0"] = ":house:",
+ ["\U0001f3da\ufe0f"] = ":house_abandoned:",
+ ["\U0001f3da"] = ":house_abandoned:",
+ ["\U0001f3e1"] = ":house_with_garden:",
+ ["\U0001f917"] = ":hugging:",
+ ["\U0001f62f"] = ":hushed:",
+ ["\U0001f6d6"] = ":hut:",
+ ["\U0001f368"] = ":ice_cream:",
+ ["\U0001f9ca"] = ":ice_cube:",
+ ["\u26f8\ufe0f"] = ":ice_skate:",
+ ["\u26f8"] = ":ice_skate:",
+ ["\U0001f366"] = ":icecream:",
+ ["\U0001f194"] = ":id:",
+ ["\U0001f250"] = ":ideograph_advantage:",
+ ["\U0001f47f"] = ":imp:",
+ ["\U0001f4e5"] = ":inbox_tray:",
+ ["\U0001f4e8"] = ":incoming_envelope:",
+ ["\u267e\ufe0f"] = ":infinity:",
+ ["\u267e"] = ":infinity:",
+ ["\u2139\ufe0f"] = ":information_source:",
+ ["\u2139"] = ":information_source:",
+ ["\U0001f607"] = ":innocent:",
+ ["\u2049\ufe0f"] = ":interrobang:",
+ ["\u2049"] = ":interrobang:",
+ ["\U0001f3dd\ufe0f"] = ":island:",
+ ["\U0001f3dd"] = ":island:",
+ ["\U0001f3ee"] = ":izakaya_lantern:",
+ ["\U0001f383"] = ":jack_o_lantern:",
+ ["\U0001f5fe"] = ":japan:",
+ ["\U0001f3ef"] = ":japanese_castle:",
+ ["\U0001f47a"] = ":japanese_goblin:",
+ ["\U0001f479"] = ":japanese_ogre:",
+ ["\U0001f456"] = ":jeans:",
+ ["\U0001f9e9"] = ":jigsaw:",
+ ["\U0001f602"] = ":joy:",
+ ["\U0001f639"] = ":joy_cat:",
+ ["\U0001f579\ufe0f"] = ":joystick:",
+ ["\U0001f579"] = ":joystick:",
+ ["\U0001f9d1\u200d\u2696\ufe0f"] = ":judge:",
+ ["\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f"] = ":judge_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f"] = ":judge_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f"] = ":judge_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f"] = ":judge_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f"] = ":judge_tone5:",
+ ["\U0001f54b"] = ":kaaba:",
+ ["\U0001f998"] = ":kangaroo:",
+ ["\U0001f511"] = ":key:",
+ ["\U0001f5dd\ufe0f"] = ":key2:",
+ ["\U0001f5dd"] = ":key2:",
+ ["\u2328\ufe0f"] = ":keyboard:",
+ ["\u2328"] = ":keyboard:",
+ ["\U0001f51f"] = ":keycap_ten:",
+ ["\U0001f458"] = ":kimono:",
+ ["\U0001f48b"] = ":kiss:",
+ ["\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468"] = ":kiss_mm:",
+ ["\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468"] = ":kiss_woman_man:",
+ ["\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469"] = ":kiss_ww:",
+ ["\U0001f617"] = ":kissing:",
+ ["\U0001f63d"] = ":kissing_cat:",
+ ["\U0001f61a"] = ":kissing_closed_eyes:",
+ ["\U0001f618"] = ":kissing_heart:",
+ ["\U0001f619"] = ":kissing_smiling_eyes:",
+ ["\U0001fa81"] = ":kite:",
+ ["\U0001f95d"] = ":kiwi:",
+ ["\U0001f52a"] = ":knife:",
+ ["\U0001faa2"] = ":knot:",
+ ["\U0001f428"] = ":koala:",
+ ["\U0001f201"] = ":koko:",
+ ["\U0001f97c"] = ":lab_coat:",
+ ["\U0001f3f7\ufe0f"] = ":label:",
+ ["\U0001f3f7"] = ":label:",
+ ["\U0001f94d"] = ":lacrosse:",
+ ["\U0001fa9c"] = ":ladder:",
+ ["\U0001f41e"] = ":lady_beetle:",
+ ["\U0001f537"] = ":large_blue_diamond:",
+ ["\U0001f536"] = ":large_orange_diamond:",
+ ["\U0001f317"] = ":last_quarter_moon:",
+ ["\U0001f31c"] = ":last_quarter_moon_with_face:",
+ ["\U0001f606"] = ":laughing:",
+ ["\U0001f96c"] = ":leafy_green:",
+ ["\U0001f343"] = ":leaves:",
+ ["\U0001f4d2"] = ":ledger:",
+ ["\U0001f91b"] = ":left_facing_fist:",
+ ["\U0001f91b\U0001f3fb"] = ":left_facing_fist_tone1:",
+ ["\U0001f91b\U0001f3fc"] = ":left_facing_fist_tone2:",
+ ["\U0001f91b\U0001f3fd"] = ":left_facing_fist_tone3:",
+ ["\U0001f91b\U0001f3fe"] = ":left_facing_fist_tone4:",
+ ["\U0001f91b\U0001f3ff"] = ":left_facing_fist_tone5:",
+ ["\U0001f6c5"] = ":left_luggage:",
+ ["\u2194\ufe0f"] = ":left_right_arrow:",
+ ["\u2194"] = ":left_right_arrow:",
+ ["\u21a9\ufe0f"] = ":leftwards_arrow_with_hook:",
+ ["\u21a9"] = ":leftwards_arrow_with_hook:",
+ ["\U0001f9b5"] = ":leg:",
+ ["\U0001f9b5\U0001f3fb"] = ":leg_tone1:",
+ ["\U0001f9b5\U0001f3fc"] = ":leg_tone2:",
+ ["\U0001f9b5\U0001f3fd"] = ":leg_tone3:",
+ ["\U0001f9b5\U0001f3fe"] = ":leg_tone4:",
+ ["\U0001f9b5\U0001f3ff"] = ":leg_tone5:",
+ ["\U0001f34b"] = ":lemon:",
+ ["\u264c"] = ":leo:",
+ ["\U0001f406"] = ":leopard:",
+ ["\U0001f39a\ufe0f"] = ":level_slider:",
+ ["\U0001f39a"] = ":level_slider:",
+ ["\U0001f574\ufe0f"] = ":levitate:",
+ ["\U0001f574"] = ":levitate:",
+ ["\U0001f574\U0001f3fb"] = ":levitate_tone1:",
+ ["\U0001f574\U0001f3fc"] = ":levitate_tone2:",
+ ["\U0001f574\U0001f3fd"] = ":levitate_tone3:",
+ ["\U0001f574\U0001f3fe"] = ":levitate_tone4:",
+ ["\U0001f574\U0001f3ff"] = ":levitate_tone5:",
+ ["\u264e"] = ":libra:",
+ ["\U0001f688"] = ":light_rail:",
+ ["\U0001f517"] = ":link:",
+ ["\U0001f981"] = ":lion_face:",
+ ["\U0001f444"] = ":lips:",
+ ["\U0001f484"] = ":lipstick:",
+ ["\U0001f98e"] = ":lizard:",
+ ["\U0001f999"] = ":llama:",
+ ["\U0001f99e"] = ":lobster:",
+ ["\U0001f512"] = ":lock:",
+ ["\U0001f50f"] = ":lock_with_ink_pen:",
+ ["\U0001f36d"] = ":lollipop:",
+ ["\U0001fa98"] = ":long_drum:",
+ ["\u27bf"] = ":loop:",
+ ["\U0001f50a"] = ":loud_sound:",
+ ["\U0001f4e2"] = ":loudspeaker:",
+ ["\U0001f3e9"] = ":love_hotel:",
+ ["\U0001f48c"] = ":love_letter:",
+ ["\U0001f91f"] = ":love_you_gesture:",
+ ["\U0001f91f\U0001f3fb"] = ":love_you_gesture_tone1:",
+ ["\U0001f91f\U0001f3fc"] = ":love_you_gesture_tone2:",
+ ["\U0001f91f\U0001f3fd"] = ":love_you_gesture_tone3:",
+ ["\U0001f91f\U0001f3fe"] = ":love_you_gesture_tone4:",
+ ["\U0001f91f\U0001f3ff"] = ":love_you_gesture_tone5:",
+ ["\U0001f505"] = ":low_brightness:",
+ ["\U0001f9f3"] = ":luggage:",
+ ["\U0001fac1"] = ":lungs:",
+ ["\U0001f925"] = ":lying_face:",
+ ["\u24c2\ufe0f"] = ":m:",
+ ["\u24c2"] = ":m:",
+ ["\U0001f50d"] = ":mag:",
+ ["\U0001f50e"] = ":mag_right:",
+ ["\U0001f9d9"] = ":mage:",
+ ["\U0001f9d9\U0001f3fb"] = ":mage_tone1:",
+ ["\U0001f9d9\U0001f3fc"] = ":mage_tone2:",
+ ["\U0001f9d9\U0001f3fd"] = ":mage_tone3:",
+ ["\U0001f9d9\U0001f3fe"] = ":mage_tone4:",
+ ["\U0001f9d9\U0001f3ff"] = ":mage_tone5:",
+ ["\U0001fa84"] = ":magic_wand:",
+ ["\U0001f9f2"] = ":magnet:",
+ ["\U0001f004"] = ":mahjong:",
+ ["\U0001f4eb"] = ":mailbox:",
+ ["\U0001f4ea"] = ":mailbox_closed:",
+ ["\U0001f4ec"] = ":mailbox_with_mail:",
+ ["\U0001f4ed"] = ":mailbox_with_no_mail:",
+ ["\u2642\ufe0f"] = ":male_sign:",
+ ["\u2642"] = ":male_sign:",
+ ["\U0001f9a3"] = ":mammoth:",
+ ["\U0001f468"] = ":man:",
+ ["\U0001f468\u200d\U0001f3a8"] = ":man_artist:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f3a8"] = ":man_artist_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f3a8"] = ":man_artist_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f3a8"] = ":man_artist_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f3a8"] = ":man_artist_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f3a8"] = ":man_artist_tone5:",
+ ["\U0001f468\u200d\U0001f680"] = ":man_astronaut:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f680"] = ":man_astronaut_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f680"] = ":man_astronaut_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f680"] = ":man_astronaut_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f680"] = ":man_astronaut_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f680"] = ":man_astronaut_tone5:",
+ ["\U0001f468\u200d\U0001f9b2"] = ":man_bald:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f9b2"] = ":man_bald_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f9b2"] = ":man_bald_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f9b2"] = ":man_bald_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f9b2"] = ":man_bald_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f9b2"] = ":man_bald_tone5:",
+ ["\U0001f6b4\u200d\u2642\ufe0f"] = ":man_biking:",
+ ["\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f"] = ":man_biking_tone1:",
+ ["\U0001f6b4\U0001f3fc\u200d\u2642\ufe0f"] = ":man_biking_tone2:",
+ ["\U0001f6b4\U0001f3fd\u200d\u2642\ufe0f"] = ":man_biking_tone3:",
+ ["\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f"] = ":man_biking_tone4:",
+ ["\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f"] = ":man_biking_tone5:",
+ ["\u26f9\ufe0f\u200d\u2642\ufe0f"] = ":man_bouncing_ball:",
+ ["\u26f9\U0001f3fb\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone1:",
+ ["\u26f9\U0001f3fc\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone2:",
+ ["\u26f9\U0001f3fd\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone3:",
+ ["\u26f9\U0001f3fe\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone4:",
+ ["\u26f9\U0001f3ff\u200d\u2642\ufe0f"] = ":man_bouncing_ball_tone5:",
+ ["\U0001f647\u200d\u2642\ufe0f"] = ":man_bowing:",
+ ["\U0001f647\U0001f3fb\u200d\u2642\ufe0f"] = ":man_bowing_tone1:",
+ ["\U0001f647\U0001f3fc\u200d\u2642\ufe0f"] = ":man_bowing_tone2:",
+ ["\U0001f647\U0001f3fd\u200d\u2642\ufe0f"] = ":man_bowing_tone3:",
+ ["\U0001f647\U0001f3fe\u200d\u2642\ufe0f"] = ":man_bowing_tone4:",
+ ["\U0001f647\U0001f3ff\u200d\u2642\ufe0f"] = ":man_bowing_tone5:",
+ ["\U0001f938\u200d\u2642\ufe0f"] = ":man_cartwheeling:",
+ ["\U0001f938\U0001f3fb\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone1:",
+ ["\U0001f938\U0001f3fc\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone2:",
+ ["\U0001f938\U0001f3fd\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone3:",
+ ["\U0001f938\U0001f3fe\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone4:",
+ ["\U0001f938\U0001f3ff\u200d\u2642\ufe0f"] = ":man_cartwheeling_tone5:",
+ ["\U0001f9d7\u200d\u2642\ufe0f"] = ":man_climbing:",
+ ["\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f"] = ":man_climbing_tone1:",
+ ["\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f"] = ":man_climbing_tone2:",
+ ["\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f"] = ":man_climbing_tone3:",
+ ["\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f"] = ":man_climbing_tone4:",
+ ["\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f"] = ":man_climbing_tone5:",
+ ["\U0001f477\u200d\u2642\ufe0f"] = ":man_construction_worker:",
+ ["\U0001f477\U0001f3fb\u200d\u2642\ufe0f"] = ":man_construction_worker_tone1:",
+ ["\U0001f477\U0001f3fc\u200d\u2642\ufe0f"] = ":man_construction_worker_tone2:",
+ ["\U0001f477\U0001f3fd\u200d\u2642\ufe0f"] = ":man_construction_worker_tone3:",
+ ["\U0001f477\U0001f3fe\u200d\u2642\ufe0f"] = ":man_construction_worker_tone4:",
+ ["\U0001f477\U0001f3ff\u200d\u2642\ufe0f"] = ":man_construction_worker_tone5:",
+ ["\U0001f468\u200d\U0001f373"] = ":man_cook:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f373"] = ":man_cook_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f373"] = ":man_cook_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f373"] = ":man_cook_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f373"] = ":man_cook_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f373"] = ":man_cook_tone5:",
+ ["\U0001f468\u200d\U0001f9b1"] = ":man_curly_haired:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f9b1"] = ":man_curly_haired_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f9b1"] = ":man_curly_haired_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f9b1"] = ":man_curly_haired_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f9b1"] = ":man_curly_haired_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f9b1"] = ":man_curly_haired_tone5:",
+ ["\U0001f57a"] = ":man_dancing:",
+ ["\U0001f57a\U0001f3fb"] = ":man_dancing_tone1:",
+ ["\U0001f57a\U0001f3fc"] = ":man_dancing_tone2:",
+ ["\U0001f57a\U0001f3fd"] = ":man_dancing_tone3:",
+ ["\U0001f57a\U0001f3fe"] = ":man_dancing_tone4:",
+ ["\U0001f57a\U0001f3ff"] = ":man_dancing_tone5:",
+ ["\U0001f575\ufe0f\u200d\u2642\ufe0f"] = ":man_detective:",
+ ["\U0001f575\U0001f3fb\u200d\u2642\ufe0f"] = ":man_detective_tone1:",
+ ["\U0001f575\U0001f3fc\u200d\u2642\ufe0f"] = ":man_detective_tone2:",
+ ["\U0001f575\U0001f3fd\u200d\u2642\ufe0f"] = ":man_detective_tone3:",
+ ["\U0001f575\U0001f3fe\u200d\u2642\ufe0f"] = ":man_detective_tone4:",
+ ["\U0001f575\U0001f3ff\u200d\u2642\ufe0f"] = ":man_detective_tone5:",
+ ["\U0001f9dd\u200d\u2642\ufe0f"] = ":man_elf:",
+ ["\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f"] = ":man_elf_tone1:",
+ ["\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f"] = ":man_elf_tone2:",
+ ["\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f"] = ":man_elf_tone3:",
+ ["\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f"] = ":man_elf_tone4:",
+ ["\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f"] = ":man_elf_tone5:",
+ ["\U0001f926\u200d\u2642\ufe0f"] = ":man_facepalming:",
+ ["\U0001f926\U0001f3fb\u200d\u2642\ufe0f"] = ":man_facepalming_tone1:",
+ ["\U0001f926\U0001f3fc\u200d\u2642\ufe0f"] = ":man_facepalming_tone2:",
+ ["\U0001f926\U0001f3fd\u200d\u2642\ufe0f"] = ":man_facepalming_tone3:",
+ ["\U0001f926\U0001f3fe\u200d\u2642\ufe0f"] = ":man_facepalming_tone4:",
+ ["\U0001f926\U0001f3ff\u200d\u2642\ufe0f"] = ":man_facepalming_tone5:",
+ ["\U0001f468\u200d\U0001f3ed"] = ":man_factory_worker:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f3ed"] = ":man_factory_worker_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f3ed"] = ":man_factory_worker_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f3ed"] = ":man_factory_worker_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f3ed"] = ":man_factory_worker_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f3ed"] = ":man_factory_worker_tone5:",
+ ["\U0001f9da\u200d\u2642\ufe0f"] = ":man_fairy:",
+ ["\U0001f9da\U0001f3fb\u200d\u2642\ufe0f"] = ":man_fairy_tone1:",
+ ["\U0001f9da\U0001f3fc\u200d\u2642\ufe0f"] = ":man_fairy_tone2:",
+ ["\U0001f9da\U0001f3fd\u200d\u2642\ufe0f"] = ":man_fairy_tone3:",
+ ["\U0001f9da\U0001f3fe\u200d\u2642\ufe0f"] = ":man_fairy_tone4:",
+ ["\U0001f9da\U0001f3ff\u200d\u2642\ufe0f"] = ":man_fairy_tone5:",
+ ["\U0001f468\u200d\U0001f33e"] = ":man_farmer:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f33e"] = ":man_farmer_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f33e"] = ":man_farmer_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f33e"] = ":man_farmer_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f33e"] = ":man_farmer_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f33e"] = ":man_farmer_tone5:",
+ ["\U0001f468\u200d\U0001f37c"] = ":man_feeding_baby:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f37c"] = ":man_feeding_baby_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f37c"] = ":man_feeding_baby_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f37c"] = ":man_feeding_baby_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f37c"] = ":man_feeding_baby_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f37c"] = ":man_feeding_baby_tone5:",
+ ["\U0001f468\u200d\U0001f692"] = ":man_firefighter:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f692"] = ":man_firefighter_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f692"] = ":man_firefighter_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f692"] = ":man_firefighter_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f692"] = ":man_firefighter_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f692"] = ":man_firefighter_tone5:",
+ ["\U0001f64d\u200d\u2642\ufe0f"] = ":man_frowning:",
+ ["\U0001f64d\U0001f3fb\u200d\u2642\ufe0f"] = ":man_frowning_tone1:",
+ ["\U0001f64d\U0001f3fc\u200d\u2642\ufe0f"] = ":man_frowning_tone2:",
+ ["\U0001f64d\U0001f3fd\u200d\u2642\ufe0f"] = ":man_frowning_tone3:",
+ ["\U0001f64d\U0001f3fe\u200d\u2642\ufe0f"] = ":man_frowning_tone4:",
+ ["\U0001f64d\U0001f3ff\u200d\u2642\ufe0f"] = ":man_frowning_tone5:",
+ ["\U0001f9de\u200d\u2642\ufe0f"] = ":man_genie:",
+ ["\U0001f645\u200d\u2642\ufe0f"] = ":man_gesturing_no:",
+ ["\U0001f645\U0001f3fb\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone1:",
+ ["\U0001f645\U0001f3fc\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone2:",
+ ["\U0001f645\U0001f3fd\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone3:",
+ ["\U0001f645\U0001f3fe\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone4:",
+ ["\U0001f645\U0001f3ff\u200d\u2642\ufe0f"] = ":man_gesturing_no_tone5:",
+ ["\U0001f646\u200d\u2642\ufe0f"] = ":man_gesturing_ok:",
+ ["\U0001f646\U0001f3fb\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone1:",
+ ["\U0001f646\U0001f3fc\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone2:",
+ ["\U0001f646\U0001f3fd\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone3:",
+ ["\U0001f646\U0001f3fe\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone4:",
+ ["\U0001f646\U0001f3ff\u200d\u2642\ufe0f"] = ":man_gesturing_ok_tone5:",
+ ["\U0001f486\u200d\u2642\ufe0f"] = ":man_getting_face_massage:",
+ ["\U0001f486\U0001f3fb\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone1:",
+ ["\U0001f486\U0001f3fc\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone2:",
+ ["\U0001f486\U0001f3fd\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone3:",
+ ["\U0001f486\U0001f3fe\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone4:",
+ ["\U0001f486\U0001f3ff\u200d\u2642\ufe0f"] = ":man_getting_face_massage_tone5:",
+ ["\U0001f487\u200d\u2642\ufe0f"] = ":man_getting_haircut:",
+ ["\U0001f487\U0001f3fb\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone1:",
+ ["\U0001f487\U0001f3fc\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone2:",
+ ["\U0001f487\U0001f3fd\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone3:",
+ ["\U0001f487\U0001f3fe\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone4:",
+ ["\U0001f487\U0001f3ff\u200d\u2642\ufe0f"] = ":man_getting_haircut_tone5:",
+ ["\U0001f3cc\ufe0f\u200d\u2642\ufe0f"] = ":man_golfing:",
+ ["\U0001f3cc\U0001f3fb\u200d\u2642\ufe0f"] = ":man_golfing_tone1:",
+ ["\U0001f3cc\U0001f3fc\u200d\u2642\ufe0f"] = ":man_golfing_tone2:",
+ ["\U0001f3cc\U0001f3fd\u200d\u2642\ufe0f"] = ":man_golfing_tone3:",
+ ["\U0001f3cc\U0001f3fe\u200d\u2642\ufe0f"] = ":man_golfing_tone4:",
+ ["\U0001f3cc\U0001f3ff\u200d\u2642\ufe0f"] = ":man_golfing_tone5:",
+ ["\U0001f482\u200d\u2642\ufe0f"] = ":man_guard:",
+ ["\U0001f482\U0001f3fb\u200d\u2642\ufe0f"] = ":man_guard_tone1:",
+ ["\U0001f482\U0001f3fc\u200d\u2642\ufe0f"] = ":man_guard_tone2:",
+ ["\U0001f482\U0001f3fd\u200d\u2642\ufe0f"] = ":man_guard_tone3:",
+ ["\U0001f482\U0001f3fe\u200d\u2642\ufe0f"] = ":man_guard_tone4:",
+ ["\U0001f482\U0001f3ff\u200d\u2642\ufe0f"] = ":man_guard_tone5:",
+ ["\U0001f468\u200d\u2695\ufe0f"] = ":man_health_worker:",
+ ["\U0001f468\U0001f3fb\u200d\u2695\ufe0f"] = ":man_health_worker_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\u2695\ufe0f"] = ":man_health_worker_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\u2695\ufe0f"] = ":man_health_worker_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\u2695\ufe0f"] = ":man_health_worker_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\u2695\ufe0f"] = ":man_health_worker_tone5:",
+ ["\U0001f9d8\u200d\u2642\ufe0f"] = ":man_in_lotus_position:",
+ ["\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone1:",
+ ["\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone2:",
+ ["\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone3:",
+ ["\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone4:",
+ ["\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f"] = ":man_in_lotus_position_tone5:",
+ ["\U0001f468\u200d\U0001f9bd"] = ":man_in_manual_wheelchair:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f9bd"] = ":man_in_manual_wheelchair_tone5:",
+ ["\U0001f468\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f9bc"] = ":man_in_motorized_wheelchair_tone5:",
+ ["\U0001f9d6\u200d\u2642\ufe0f"] = ":man_in_steamy_room:",
+ ["\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone1:",
+ ["\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone2:",
+ ["\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone3:",
+ ["\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone4:",
+ ["\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f"] = ":man_in_steamy_room_tone5:",
+ ["\U0001f935\u200d\u2642\ufe0f"] = ":man_in_tuxedo:",
+ ["\U0001f935\U0001f3fb\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone1:",
+ ["\U0001f935\U0001f3fc\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone2:",
+ ["\U0001f935\U0001f3fd\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone3:",
+ ["\U0001f935\U0001f3fe\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone4:",
+ ["\U0001f935\U0001f3ff\u200d\u2642\ufe0f"] = ":man_in_tuxedo_tone5:",
+ ["\U0001f468\u200d\u2696\ufe0f"] = ":man_judge:",
+ ["\U0001f468\U0001f3fb\u200d\u2696\ufe0f"] = ":man_judge_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\u2696\ufe0f"] = ":man_judge_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\u2696\ufe0f"] = ":man_judge_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\u2696\ufe0f"] = ":man_judge_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\u2696\ufe0f"] = ":man_judge_tone5:",
+ ["\U0001f939\u200d\u2642\ufe0f"] = ":man_juggling:",
+ ["\U0001f939\U0001f3fb\u200d\u2642\ufe0f"] = ":man_juggling_tone1:",
+ ["\U0001f939\U0001f3fc\u200d\u2642\ufe0f"] = ":man_juggling_tone2:",
+ ["\U0001f939\U0001f3fd\u200d\u2642\ufe0f"] = ":man_juggling_tone3:",
+ ["\U0001f939\U0001f3fe\u200d\u2642\ufe0f"] = ":man_juggling_tone4:",
+ ["\U0001f939\U0001f3ff\u200d\u2642\ufe0f"] = ":man_juggling_tone5:",
+ ["\U0001f9ce\u200d\u2642\ufe0f"] = ":man_kneeling:",
+ ["\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f"] = ":man_kneeling_tone1:",
+ ["\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f"] = ":man_kneeling_tone2:",
+ ["\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f"] = ":man_kneeling_tone3:",
+ ["\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f"] = ":man_kneeling_tone4:",
+ ["\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f"] = ":man_kneeling_tone5:",
+ ["\U0001f3cb\ufe0f\u200d\u2642\ufe0f"] = ":man_lifting_weights:",
+ ["\U0001f3cb\U0001f3fb\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone1:",
+ ["\U0001f3cb\U0001f3fc\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone2:",
+ ["\U0001f3cb\U0001f3fd\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone3:",
+ ["\U0001f3cb\U0001f3fe\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone4:",
+ ["\U0001f3cb\U0001f3ff\u200d\u2642\ufe0f"] = ":man_lifting_weights_tone5:",
+ ["\U0001f9d9\u200d\u2642\ufe0f"] = ":man_mage:",
+ ["\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f"] = ":man_mage_tone1:",
+ ["\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f"] = ":man_mage_tone2:",
+ ["\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f"] = ":man_mage_tone3:",
+ ["\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f"] = ":man_mage_tone4:",
+ ["\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f"] = ":man_mage_tone5:",
+ ["\U0001f468\u200d\U0001f527"] = ":man_mechanic:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f527"] = ":man_mechanic_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f527"] = ":man_mechanic_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f527"] = ":man_mechanic_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f527"] = ":man_mechanic_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f527"] = ":man_mechanic_tone5:",
+ ["\U0001f6b5\u200d\u2642\ufe0f"] = ":man_mountain_biking:",
+ ["\U0001f6b5\U0001f3fb\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone1:",
+ ["\U0001f6b5\U0001f3fc\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone2:",
+ ["\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone3:",
+ ["\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone4:",
+ ["\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f"] = ":man_mountain_biking_tone5:",
+ ["\U0001f468\u200d\U0001f4bc"] = ":man_office_worker:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f4bc"] = ":man_office_worker_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f4bc"] = ":man_office_worker_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f4bc"] = ":man_office_worker_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f4bc"] = ":man_office_worker_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f4bc"] = ":man_office_worker_tone5:",
+ ["\U0001f468\u200d\u2708\ufe0f"] = ":man_pilot:",
+ ["\U0001f468\U0001f3fb\u200d\u2708\ufe0f"] = ":man_pilot_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\u2708\ufe0f"] = ":man_pilot_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\u2708\ufe0f"] = ":man_pilot_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\u2708\ufe0f"] = ":man_pilot_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\u2708\ufe0f"] = ":man_pilot_tone5:",
+ ["\U0001f93e\u200d\u2642\ufe0f"] = ":man_playing_handball:",
+ ["\U0001f93e\U0001f3fb\u200d\u2642\ufe0f"] = ":man_playing_handball_tone1:",
+ ["\U0001f93e\U0001f3fc\u200d\u2642\ufe0f"] = ":man_playing_handball_tone2:",
+ ["\U0001f93e\U0001f3fd\u200d\u2642\ufe0f"] = ":man_playing_handball_tone3:",
+ ["\U0001f93e\U0001f3fe\u200d\u2642\ufe0f"] = ":man_playing_handball_tone4:",
+ ["\U0001f93e\U0001f3ff\u200d\u2642\ufe0f"] = ":man_playing_handball_tone5:",
+ ["\U0001f93d\u200d\u2642\ufe0f"] = ":man_playing_water_polo:",
+ ["\U0001f93d\U0001f3fb\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone1:",
+ ["\U0001f93d\U0001f3fc\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone2:",
+ ["\U0001f93d\U0001f3fd\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone3:",
+ ["\U0001f93d\U0001f3fe\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone4:",
+ ["\U0001f93d\U0001f3ff\u200d\u2642\ufe0f"] = ":man_playing_water_polo_tone5:",
+ ["\U0001f46e\u200d\u2642\ufe0f"] = ":man_police_officer:",
+ ["\U0001f46e\U0001f3fb\u200d\u2642\ufe0f"] = ":man_police_officer_tone1:",
+ ["\U0001f46e\U0001f3fc\u200d\u2642\ufe0f"] = ":man_police_officer_tone2:",
+ ["\U0001f46e\U0001f3fd\u200d\u2642\ufe0f"] = ":man_police_officer_tone3:",
+ ["\U0001f46e\U0001f3fe\u200d\u2642\ufe0f"] = ":man_police_officer_tone4:",
+ ["\U0001f46e\U0001f3ff\u200d\u2642\ufe0f"] = ":man_police_officer_tone5:",
+ ["\U0001f64e\u200d\u2642\ufe0f"] = ":man_pouting:",
+ ["\U0001f64e\U0001f3fb\u200d\u2642\ufe0f"] = ":man_pouting_tone1:",
+ ["\U0001f64e\U0001f3fc\u200d\u2642\ufe0f"] = ":man_pouting_tone2:",
+ ["\U0001f64e\U0001f3fd\u200d\u2642\ufe0f"] = ":man_pouting_tone3:",
+ ["\U0001f64e\U0001f3fe\u200d\u2642\ufe0f"] = ":man_pouting_tone4:",
+ ["\U0001f64e\U0001f3ff\u200d\u2642\ufe0f"] = ":man_pouting_tone5:",
+ ["\U0001f64b\u200d\u2642\ufe0f"] = ":man_raising_hand:",
+ ["\U0001f64b\U0001f3fb\u200d\u2642\ufe0f"] = ":man_raising_hand_tone1:",
+ ["\U0001f64b\U0001f3fc\u200d\u2642\ufe0f"] = ":man_raising_hand_tone2:",
+ ["\U0001f64b\U0001f3fd\u200d\u2642\ufe0f"] = ":man_raising_hand_tone3:",
+ ["\U0001f64b\U0001f3fe\u200d\u2642\ufe0f"] = ":man_raising_hand_tone4:",
+ ["\U0001f64b\U0001f3ff\u200d\u2642\ufe0f"] = ":man_raising_hand_tone5:",
+ ["\U0001f468\u200d\U0001f9b0"] = ":man_red_haired:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f9b0"] = ":man_red_haired_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f9b0"] = ":man_red_haired_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f9b0"] = ":man_red_haired_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f9b0"] = ":man_red_haired_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f9b0"] = ":man_red_haired_tone5:",
+ ["\U0001f6a3\u200d\u2642\ufe0f"] = ":man_rowing_boat:",
+ ["\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone1:",
+ ["\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone2:",
+ ["\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone3:",
+ ["\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone4:",
+ ["\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f"] = ":man_rowing_boat_tone5:",
+ ["\U0001f3c3\u200d\u2642\ufe0f"] = ":man_running:",
+ ["\U0001f3c3\U0001f3fb\u200d\u2642\ufe0f"] = ":man_running_tone1:",
+ ["\U0001f3c3\U0001f3fc\u200d\u2642\ufe0f"] = ":man_running_tone2:",
+ ["\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f"] = ":man_running_tone3:",
+ ["\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f"] = ":man_running_tone4:",
+ ["\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f"] = ":man_running_tone5:",
+ ["\U0001f468\u200d\U0001f52c"] = ":man_scientist:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f52c"] = ":man_scientist_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f52c"] = ":man_scientist_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f52c"] = ":man_scientist_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f52c"] = ":man_scientist_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f52c"] = ":man_scientist_tone5:",
+ ["\U0001f937\u200d\u2642\ufe0f"] = ":man_shrugging:",
+ ["\U0001f937\U0001f3fb\u200d\u2642\ufe0f"] = ":man_shrugging_tone1:",
+ ["\U0001f937\U0001f3fc\u200d\u2642\ufe0f"] = ":man_shrugging_tone2:",
+ ["\U0001f937\U0001f3fd\u200d\u2642\ufe0f"] = ":man_shrugging_tone3:",
+ ["\U0001f937\U0001f3fe\u200d\u2642\ufe0f"] = ":man_shrugging_tone4:",
+ ["\U0001f937\U0001f3ff\u200d\u2642\ufe0f"] = ":man_shrugging_tone5:",
+ ["\U0001f468\u200d\U0001f3a4"] = ":man_singer:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f3a4"] = ":man_singer_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f3a4"] = ":man_singer_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f3a4"] = ":man_singer_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f3a4"] = ":man_singer_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f3a4"] = ":man_singer_tone5:",
+ ["\U0001f9cd\u200d\u2642\ufe0f"] = ":man_standing:",
+ ["\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f"] = ":man_standing_tone1:",
+ ["\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f"] = ":man_standing_tone2:",
+ ["\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f"] = ":man_standing_tone3:",
+ ["\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f"] = ":man_standing_tone4:",
+ ["\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f"] = ":man_standing_tone5:",
+ ["\U0001f468\u200d\U0001f393"] = ":man_student:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f393"] = ":man_student_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f393"] = ":man_student_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f393"] = ":man_student_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f393"] = ":man_student_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f393"] = ":man_student_tone5:",
+ ["\U0001f9b8\u200d\u2642\ufe0f"] = ":man_superhero:",
+ ["\U0001f9b8\U0001f3fb\u200d\u2642\ufe0f"] = ":man_superhero_tone1:",
+ ["\U0001f9b8\U0001f3fc\u200d\u2642\ufe0f"] = ":man_superhero_tone2:",
+ ["\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f"] = ":man_superhero_tone3:",
+ ["\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f"] = ":man_superhero_tone4:",
+ ["\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f"] = ":man_superhero_tone5:",
+ ["\U0001f9b9\u200d\u2642\ufe0f"] = ":man_supervillain:",
+ ["\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f"] = ":man_supervillain_tone1:",
+ ["\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f"] = ":man_supervillain_tone2:",
+ ["\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f"] = ":man_supervillain_tone3:",
+ ["\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f"] = ":man_supervillain_tone4:",
+ ["\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f"] = ":man_supervillain_tone5:",
+ ["\U0001f3c4\u200d\u2642\ufe0f"] = ":man_surfing:",
+ ["\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f"] = ":man_surfing_tone1:",
+ ["\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f"] = ":man_surfing_tone2:",
+ ["\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f"] = ":man_surfing_tone3:",
+ ["\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f"] = ":man_surfing_tone4:",
+ ["\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f"] = ":man_surfing_tone5:",
+ ["\U0001f3ca\u200d\u2642\ufe0f"] = ":man_swimming:",
+ ["\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f"] = ":man_swimming_tone1:",
+ ["\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f"] = ":man_swimming_tone2:",
+ ["\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f"] = ":man_swimming_tone3:",
+ ["\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f"] = ":man_swimming_tone4:",
+ ["\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f"] = ":man_swimming_tone5:",
+ ["\U0001f468\u200d\U0001f3eb"] = ":man_teacher:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f3eb"] = ":man_teacher_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f3eb"] = ":man_teacher_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f3eb"] = ":man_teacher_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f3eb"] = ":man_teacher_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f3eb"] = ":man_teacher_tone5:",
+ ["\U0001f468\u200d\U0001f4bb"] = ":man_technologist:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f4bb"] = ":man_technologist_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f4bb"] = ":man_technologist_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f4bb"] = ":man_technologist_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f4bb"] = ":man_technologist_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f4bb"] = ":man_technologist_tone5:",
+ ["\U0001f481\u200d\u2642\ufe0f"] = ":man_tipping_hand:",
+ ["\U0001f481\U0001f3fb\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone1:",
+ ["\U0001f481\U0001f3fc\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone2:",
+ ["\U0001f481\U0001f3fd\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone3:",
+ ["\U0001f481\U0001f3fe\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone4:",
+ ["\U0001f481\U0001f3ff\u200d\u2642\ufe0f"] = ":man_tipping_hand_tone5:",
+ ["\U0001f468\U0001f3fb"] = ":man_tone1:",
+ ["\U0001f468\U0001f3fc"] = ":man_tone2:",
+ ["\U0001f468\U0001f3fd"] = ":man_tone3:",
+ ["\U0001f468\U0001f3fe"] = ":man_tone4:",
+ ["\U0001f468\U0001f3ff"] = ":man_tone5:",
+ ["\U0001f9db\u200d\u2642\ufe0f"] = ":man_vampire:",
+ ["\U0001f9db\U0001f3fb\u200d\u2642\ufe0f"] = ":man_vampire_tone1:",
+ ["\U0001f9db\U0001f3fc\u200d\u2642\ufe0f"] = ":man_vampire_tone2:",
+ ["\U0001f9db\U0001f3fd\u200d\u2642\ufe0f"] = ":man_vampire_tone3:",
+ ["\U0001f9db\U0001f3fe\u200d\u2642\ufe0f"] = ":man_vampire_tone4:",
+ ["\U0001f9db\U0001f3ff\u200d\u2642\ufe0f"] = ":man_vampire_tone5:",
+ ["\U0001f6b6\u200d\u2642\ufe0f"] = ":man_walking:",
+ ["\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f"] = ":man_walking_tone1:",
+ ["\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f"] = ":man_walking_tone2:",
+ ["\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f"] = ":man_walking_tone3:",
+ ["\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f"] = ":man_walking_tone4:",
+ ["\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f"] = ":man_walking_tone5:",
+ ["\U0001f473\u200d\u2642\ufe0f"] = ":man_wearing_turban:",
+ ["\U0001f473\U0001f3fb\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone1:",
+ ["\U0001f473\U0001f3fc\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone2:",
+ ["\U0001f473\U0001f3fd\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone3:",
+ ["\U0001f473\U0001f3fe\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone4:",
+ ["\U0001f473\U0001f3ff\u200d\u2642\ufe0f"] = ":man_wearing_turban_tone5:",
+ ["\U0001f468\u200d\U0001f9b3"] = ":man_white_haired:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f9b3"] = ":man_white_haired_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f9b3"] = ":man_white_haired_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f9b3"] = ":man_white_haired_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f9b3"] = ":man_white_haired_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f9b3"] = ":man_white_haired_tone5:",
+ ["\U0001f472"] = ":man_with_chinese_cap:",
+ ["\U0001f472\U0001f3fb"] = ":man_with_chinese_cap_tone1:",
+ ["\U0001f472\U0001f3fc"] = ":man_with_chinese_cap_tone2:",
+ ["\U0001f472\U0001f3fd"] = ":man_with_chinese_cap_tone3:",
+ ["\U0001f472\U0001f3fe"] = ":man_with_chinese_cap_tone4:",
+ ["\U0001f472\U0001f3ff"] = ":man_with_chinese_cap_tone5:",
+ ["\U0001f468\u200d\U0001f9af"] = ":man_with_probing_cane:",
+ ["\U0001f468\U0001f3fb\u200d\U0001f9af"] = ":man_with_probing_cane_tone1:",
+ ["\U0001f468\U0001f3fc\u200d\U0001f9af"] = ":man_with_probing_cane_tone2:",
+ ["\U0001f468\U0001f3fd\u200d\U0001f9af"] = ":man_with_probing_cane_tone3:",
+ ["\U0001f468\U0001f3fe\u200d\U0001f9af"] = ":man_with_probing_cane_tone4:",
+ ["\U0001f468\U0001f3ff\u200d\U0001f9af"] = ":man_with_probing_cane_tone5:",
+ ["\U0001f470\u200d\u2642\ufe0f"] = ":man_with_veil:",
+ ["\U0001f470\U0001f3fb\u200d\u2642\ufe0f"] = ":man_with_veil_tone1:",
+ ["\U0001f470\U0001f3fc\u200d\u2642\ufe0f"] = ":man_with_veil_tone2:",
+ ["\U0001f470\U0001f3fd\u200d\u2642\ufe0f"] = ":man_with_veil_tone3:",
+ ["\U0001f470\U0001f3fe\u200d\u2642\ufe0f"] = ":man_with_veil_tone4:",
+ ["\U0001f470\U0001f3ff\u200d\u2642\ufe0f"] = ":man_with_veil_tone5:",
+ ["\U0001f9df\u200d\u2642\ufe0f"] = ":man_zombie:",
+ ["\U0001f96d"] = ":mango:",
+ ["\U0001f45e"] = ":mans_shoe:",
+ ["\U0001f9bd"] = ":manual_wheelchair:",
+ ["\U0001f5fa\ufe0f"] = ":map:",
+ ["\U0001f5fa"] = ":map:",
+ ["\U0001f341"] = ":maple_leaf:",
+ ["\U0001f94b"] = ":martial_arts_uniform:",
+ ["\U0001f637"] = ":mask:",
+ ["\U0001f9c9"] = ":mate:",
+ ["\U0001f356"] = ":meat_on_bone:",
+ ["\U0001f9d1\u200d\U0001f527"] = ":mechanic:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f527"] = ":mechanic_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f527"] = ":mechanic_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f527"] = ":mechanic_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f527"] = ":mechanic_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f527"] = ":mechanic_tone5:",
+ ["\U0001f9be"] = ":mechanical_arm:",
+ ["\U0001f9bf"] = ":mechanical_leg:",
+ ["\U0001f3c5"] = ":medal:",
+ ["\u2695\ufe0f"] = ":medical_symbol:",
+ ["\u2695"] = ":medical_symbol:",
+ ["\U0001f4e3"] = ":mega:",
+ ["\U0001f348"] = ":melon:",
+ ["\U0001f46f\u200d\u2642\ufe0f"] = ":men_with_bunny_ears_partying:",
+ ["\U0001f93c\u200d\u2642\ufe0f"] = ":men_wrestling:",
+ ["\U0001f54e"] = ":menorah:",
+ ["\U0001f6b9"] = ":mens:",
+ ["\U0001f9dc\u200d\u2640\ufe0f"] = ":mermaid:",
+ ["\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f"] = ":mermaid_tone1:",
+ ["\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f"] = ":mermaid_tone2:",
+ ["\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f"] = ":mermaid_tone3:",
+ ["\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f"] = ":mermaid_tone4:",
+ ["\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f"] = ":mermaid_tone5:",
+ ["\U0001f9dc\u200d\u2642\ufe0f"] = ":merman:",
+ ["\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f"] = ":merman_tone1:",
+ ["\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f"] = ":merman_tone2:",
+ ["\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f"] = ":merman_tone3:",
+ ["\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f"] = ":merman_tone4:",
+ ["\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f"] = ":merman_tone5:",
+ ["\U0001f9dc"] = ":merperson:",
+ ["\U0001f9dc\U0001f3fb"] = ":merperson_tone1:",
+ ["\U0001f9dc\U0001f3fc"] = ":merperson_tone2:",
+ ["\U0001f9dc\U0001f3fd"] = ":merperson_tone3:",
+ ["\U0001f9dc\U0001f3fe"] = ":merperson_tone4:",
+ ["\U0001f9dc\U0001f3ff"] = ":merperson_tone5:",
+ ["\U0001f918"] = ":metal:",
+ ["\U0001f918\U0001f3fb"] = ":metal_tone1:",
+ ["\U0001f918\U0001f3fc"] = ":metal_tone2:",
+ ["\U0001f918\U0001f3fd"] = ":metal_tone3:",
+ ["\U0001f918\U0001f3fe"] = ":metal_tone4:",
+ ["\U0001f918\U0001f3ff"] = ":metal_tone5:",
+ ["\U0001f687"] = ":metro:",
+ ["\U0001f9a0"] = ":microbe:",
+ ["\U0001f3a4"] = ":microphone:",
+ ["\U0001f399\ufe0f"] = ":microphone2:",
+ ["\U0001f399"] = ":microphone2:",
+ ["\U0001f52c"] = ":microscope:",
+ ["\U0001f595"] = ":middle_finger:",
+ ["\U0001f595\U0001f3fb"] = ":middle_finger_tone1:",
+ ["\U0001f595\U0001f3fc"] = ":middle_finger_tone2:",
+ ["\U0001f595\U0001f3fd"] = ":middle_finger_tone3:",
+ ["\U0001f595\U0001f3fe"] = ":middle_finger_tone4:",
+ ["\U0001f595\U0001f3ff"] = ":middle_finger_tone5:",
+ ["\U0001fa96"] = ":military_helmet:",
+ ["\U0001f396\ufe0f"] = ":military_medal:",
+ ["\U0001f396"] = ":military_medal:",
+ ["\U0001f95b"] = ":milk:",
+ ["\U0001f30c"] = ":milky_way:",
+ ["\U0001f690"] = ":minibus:",
+ ["\U0001f4bd"] = ":minidisc:",
+ ["\U0001fa9e"] = ":mirror:",
+ ["\U0001f4f1"] = ":mobile_phone:",
+ ["\U0001f4f4"] = ":mobile_phone_off:",
+ ["\U0001f911"] = ":money_mouth:",
+ ["\U0001f4b8"] = ":money_with_wings:",
+ ["\U0001f4b0"] = ":moneybag:",
+ ["\U0001f412"] = ":monkey:",
+ ["\U0001f435"] = ":monkey_face:",
+ ["\U0001f69d"] = ":monorail:",
+ ["\U0001f96e"] = ":moon_cake:",
+ ["\U0001f393"] = ":mortar_board:",
+ ["\U0001f54c"] = ":mosque:",
+ ["\U0001f99f"] = ":mosquito:",
+ ["\U0001f6f5"] = ":motor_scooter:",
+ ["\U0001f6e5\ufe0f"] = ":motorboat:",
+ ["\U0001f6e5"] = ":motorboat:",
+ ["\U0001f3cd\ufe0f"] = ":motorcycle:",
+ ["\U0001f3cd"] = ":motorcycle:",
+ ["\U0001f9bc"] = ":motorized_wheelchair:",
+ ["\U0001f6e3\ufe0f"] = ":motorway:",
+ ["\U0001f6e3"] = ":motorway:",
+ ["\U0001f5fb"] = ":mount_fuji:",
+ ["\u26f0\ufe0f"] = ":mountain:",
+ ["\u26f0"] = ":mountain:",
+ ["\U0001f6a0"] = ":mountain_cableway:",
+ ["\U0001f69e"] = ":mountain_railway:",
+ ["\U0001f3d4\ufe0f"] = ":mountain_snow:",
+ ["\U0001f3d4"] = ":mountain_snow:",
+ ["\U0001f42d"] = ":mouse:",
+ ["\U0001f5b1\ufe0f"] = ":mouse_three_button:",
+ ["\U0001f5b1"] = ":mouse_three_button:",
+ ["\U0001faa4"] = ":mouse_trap:",
+ ["\U0001f401"] = ":mouse2:",
+ ["\U0001f3a5"] = ":movie_camera:",
+ ["\U0001f5ff"] = ":moyai:",
+ ["\U0001f936"] = ":mrs_claus:",
+ ["\U0001f936\U0001f3fb"] = ":mrs_claus_tone1:",
+ ["\U0001f936\U0001f3fc"] = ":mrs_claus_tone2:",
+ ["\U0001f936\U0001f3fd"] = ":mrs_claus_tone3:",
+ ["\U0001f936\U0001f3fe"] = ":mrs_claus_tone4:",
+ ["\U0001f936\U0001f3ff"] = ":mrs_claus_tone5:",
+ ["\U0001f4aa"] = ":muscle:",
+ ["\U0001f4aa\U0001f3fb"] = ":muscle_tone1:",
+ ["\U0001f4aa\U0001f3fc"] = ":muscle_tone2:",
+ ["\U0001f4aa\U0001f3fd"] = ":muscle_tone3:",
+ ["\U0001f4aa\U0001f3fe"] = ":muscle_tone4:",
+ ["\U0001f4aa\U0001f3ff"] = ":muscle_tone5:",
+ ["\U0001f344"] = ":mushroom:",
+ ["\U0001f3b9"] = ":musical_keyboard:",
+ ["\U0001f3b5"] = ":musical_note:",
+ ["\U0001f3bc"] = ":musical_score:",
+ ["\U0001f507"] = ":mute:",
+ ["\U0001f9d1\u200d\U0001f384"] = ":mx_claus:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f384"] = ":mx_claus_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f384"] = ":mx_claus_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f384"] = ":mx_claus_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f384"] = ":mx_claus_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f384"] = ":mx_claus_tone5:",
+ ["\U0001f485"] = ":nail_care:",
+ ["\U0001f485\U0001f3fb"] = ":nail_care_tone1:",
+ ["\U0001f485\U0001f3fc"] = ":nail_care_tone2:",
+ ["\U0001f485\U0001f3fd"] = ":nail_care_tone3:",
+ ["\U0001f485\U0001f3fe"] = ":nail_care_tone4:",
+ ["\U0001f485\U0001f3ff"] = ":nail_care_tone5:",
+ ["\U0001f4db"] = ":name_badge:",
+ ["\U0001f922"] = ":nauseated_face:",
+ ["\U0001f9ff"] = ":nazar_amulet:",
+ ["\U0001f454"] = ":necktie:",
+ ["\u274e"] = ":negative_squared_cross_mark:",
+ ["\U0001f913"] = ":nerd:",
+ ["\U0001fa86"] = ":nesting_dolls:",
+ ["\U0001f610"] = ":neutral_face:",
+ ["\U0001f195"] = ":new:",
+ ["\U0001f311"] = ":new_moon:",
+ ["\U0001f31a"] = ":new_moon_with_face:",
+ ["\U0001f4f0"] = ":newspaper:",
+ ["\U0001f5de\ufe0f"] = ":newspaper2:",
+ ["\U0001f5de"] = ":newspaper2:",
+ ["\U0001f196"] = ":ng:",
+ ["\U0001f303"] = ":night_with_stars:",
+ ["\u0039\ufe0f\u20e3"] = ":nine:",
+ ["\u0039\u20e3"] = ":nine:",
+ ["\U0001f977"] = ":ninja:",
+ ["\U0001f977\U0001f3fb"] = ":ninja_tone1:",
+ ["\U0001f977\U0001f3fc"] = ":ninja_tone2:",
+ ["\U0001f977\U0001f3fd"] = ":ninja_tone3:",
+ ["\U0001f977\U0001f3fe"] = ":ninja_tone4:",
+ ["\U0001f977\U0001f3ff"] = ":ninja_tone5:",
+ ["\U0001f515"] = ":no_bell:",
+ ["\U0001f6b3"] = ":no_bicycles:",
+ ["\u26d4"] = ":no_entry:",
+ ["\U0001f6ab"] = ":no_entry_sign:",
+ ["\U0001f4f5"] = ":no_mobile_phones:",
+ ["\U0001f636"] = ":no_mouth:",
+ ["\U0001f6b7"] = ":no_pedestrians:",
+ ["\U0001f6ad"] = ":no_smoking:",
+ ["\U0001f6b1"] = ":non_potable_water:",
+ ["\U0001f443"] = ":nose:",
+ ["\U0001f443\U0001f3fb"] = ":nose_tone1:",
+ ["\U0001f443\U0001f3fc"] = ":nose_tone2:",
+ ["\U0001f443\U0001f3fd"] = ":nose_tone3:",
+ ["\U0001f443\U0001f3fe"] = ":nose_tone4:",
+ ["\U0001f443\U0001f3ff"] = ":nose_tone5:",
+ ["\U0001f4d3"] = ":notebook:",
+ ["\U0001f4d4"] = ":notebook_with_decorative_cover:",
+ ["\U0001f5d2\ufe0f"] = ":notepad_spiral:",
+ ["\U0001f5d2"] = ":notepad_spiral:",
+ ["\U0001f3b6"] = ":notes:",
+ ["\U0001f529"] = ":nut_and_bolt:",
+ ["\u2b55"] = ":o:",
+ ["\U0001f17e\ufe0f"] = ":o2:",
+ ["\U0001f17e"] = ":o2:",
+ ["\U0001f30a"] = ":ocean:",
+ ["\U0001f6d1"] = ":octagonal_sign:",
+ ["\U0001f419"] = ":octopus:",
+ ["\U0001f362"] = ":oden:",
+ ["\U0001f3e2"] = ":office:",
+ ["\U0001f9d1\u200d\U0001f4bc"] = ":office_worker:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f4bc"] = ":office_worker_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f4bc"] = ":office_worker_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f4bc"] = ":office_worker_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f4bc"] = ":office_worker_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f4bc"] = ":office_worker_tone5:",
+ ["\U0001f6e2\ufe0f"] = ":oil:",
+ ["\U0001f6e2"] = ":oil:",
+ ["\U0001f197"] = ":ok:",
+ ["\U0001f44c"] = ":ok_hand:",
+ ["\U0001f44c\U0001f3fb"] = ":ok_hand_tone1:",
+ ["\U0001f44c\U0001f3fc"] = ":ok_hand_tone2:",
+ ["\U0001f44c\U0001f3fd"] = ":ok_hand_tone3:",
+ ["\U0001f44c\U0001f3fe"] = ":ok_hand_tone4:",
+ ["\U0001f44c\U0001f3ff"] = ":ok_hand_tone5:",
+ ["\U0001f9d3"] = ":older_adult:",
+ ["\U0001f9d3\U0001f3fb"] = ":older_adult_tone1:",
+ ["\U0001f9d3\U0001f3fc"] = ":older_adult_tone2:",
+ ["\U0001f9d3\U0001f3fd"] = ":older_adult_tone3:",
+ ["\U0001f9d3\U0001f3fe"] = ":older_adult_tone4:",
+ ["\U0001f9d3\U0001f3ff"] = ":older_adult_tone5:",
+ ["\U0001f474"] = ":older_man:",
+ ["\U0001f474\U0001f3fb"] = ":older_man_tone1:",
+ ["\U0001f474\U0001f3fc"] = ":older_man_tone2:",
+ ["\U0001f474\U0001f3fd"] = ":older_man_tone3:",
+ ["\U0001f474\U0001f3fe"] = ":older_man_tone4:",
+ ["\U0001f474\U0001f3ff"] = ":older_man_tone5:",
+ ["\U0001f475"] = ":older_woman:",
+ ["\U0001f475\U0001f3fb"] = ":older_woman_tone1:",
+ ["\U0001f475\U0001f3fc"] = ":older_woman_tone2:",
+ ["\U0001f475\U0001f3fd"] = ":older_woman_tone3:",
+ ["\U0001f475\U0001f3fe"] = ":older_woman_tone4:",
+ ["\U0001f475\U0001f3ff"] = ":older_woman_tone5:",
+ ["\U0001fad2"] = ":olive:",
+ ["\U0001f549\ufe0f"] = ":om_symbol:",
+ ["\U0001f549"] = ":om_symbol:",
+ ["\U0001f51b"] = ":on:",
+ ["\U0001f698"] = ":oncoming_automobile:",
+ ["\U0001f68d"] = ":oncoming_bus:",
+ ["\U0001f694"] = ":oncoming_police_car:",
+ ["\U0001f696"] = ":oncoming_taxi:",
+ ["\u0031\ufe0f\u20e3"] = ":one:",
+ ["\u0031\u20e3"] = ":one:",
+ ["\U0001fa71"] = ":one_piece_swimsuit:",
+ ["\U0001f9c5"] = ":onion:",
+ ["\U0001f4c2"] = ":open_file_folder:",
+ ["\U0001f450"] = ":open_hands:",
+ ["\U0001f450\U0001f3fb"] = ":open_hands_tone1:",
+ ["\U0001f450\U0001f3fc"] = ":open_hands_tone2:",
+ ["\U0001f450\U0001f3fd"] = ":open_hands_tone3:",
+ ["\U0001f450\U0001f3fe"] = ":open_hands_tone4:",
+ ["\U0001f450\U0001f3ff"] = ":open_hands_tone5:",
+ ["\U0001f62e"] = ":open_mouth:",
+ ["\u26ce"] = ":ophiuchus:",
+ ["\U0001f4d9"] = ":orange_book:",
+ ["\U0001f7e0"] = ":orange_circle:",
+ ["\U0001f9e1"] = ":orange_heart:",
+ ["\U0001f7e7"] = ":orange_square:",
+ ["\U0001f9a7"] = ":orangutan:",
+ ["\u2626\ufe0f"] = ":orthodox_cross:",
+ ["\u2626"] = ":orthodox_cross:",
+ ["\U0001f9a6"] = ":otter:",
+ ["\U0001f4e4"] = ":outbox_tray:",
+ ["\U0001f989"] = ":owl:",
+ ["\U0001f402"] = ":ox:",
+ ["\U0001f9aa"] = ":oyster:",
+ ["\U0001f4e6"] = ":package:",
+ ["\U0001f4c4"] = ":page_facing_up:",
+ ["\U0001f4c3"] = ":page_with_curl:",
+ ["\U0001f4df"] = ":pager:",
+ ["\U0001f58c\ufe0f"] = ":paintbrush:",
+ ["\U0001f58c"] = ":paintbrush:",
+ ["\U0001f334"] = ":palm_tree:",
+ ["\U0001f932"] = ":palms_up_together:",
+ ["\U0001f932\U0001f3fb"] = ":palms_up_together_tone1:",
+ ["\U0001f932\U0001f3fc"] = ":palms_up_together_tone2:",
+ ["\U0001f932\U0001f3fd"] = ":palms_up_together_tone3:",
+ ["\U0001f932\U0001f3fe"] = ":palms_up_together_tone4:",
+ ["\U0001f932\U0001f3ff"] = ":palms_up_together_tone5:",
+ ["\U0001f95e"] = ":pancakes:",
+ ["\U0001f43c"] = ":panda_face:",
+ ["\U0001f4ce"] = ":paperclip:",
+ ["\U0001f587\ufe0f"] = ":paperclips:",
+ ["\U0001f587"] = ":paperclips:",
+ ["\U0001fa82"] = ":parachute:",
+ ["\U0001f3de\ufe0f"] = ":park:",
+ ["\U0001f3de"] = ":park:",
+ ["\U0001f17f\ufe0f"] = ":parking:",
+ ["\U0001f17f"] = ":parking:",
+ ["\U0001f99c"] = ":parrot:",
+ ["\u303d\ufe0f"] = ":part_alternation_mark:",
+ ["\u303d"] = ":part_alternation_mark:",
+ ["\u26c5"] = ":partly_sunny:",
+ ["\U0001f973"] = ":partying_face:",
+ ["\U0001f6c2"] = ":passport_control:",
+ ["\u23f8\ufe0f"] = ":pause_button:",
+ ["\u23f8"] = ":pause_button:",
+ ["\u262e\ufe0f"] = ":peace:",
+ ["\u262e"] = ":peace:",
+ ["\U0001f351"] = ":peach:",
+ ["\U0001f99a"] = ":peacock:",
+ ["\U0001f95c"] = ":peanuts:",
+ ["\U0001f350"] = ":pear:",
+ ["\U0001f58a\ufe0f"] = ":pen_ballpoint:",
+ ["\U0001f58a"] = ":pen_ballpoint:",
+ ["\U0001f58b\ufe0f"] = ":pen_fountain:",
+ ["\U0001f58b"] = ":pen_fountain:",
+ ["\U0001f4dd"] = ":pencil:",
+ ["\u270f\ufe0f"] = ":pencil2:",
+ ["\u270f"] = ":pencil2:",
+ ["\U0001f427"] = ":penguin:",
+ ["\U0001f614"] = ":pensive:",
+ ["\U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1"] = ":people_holding_hands_tone5_tone4:",
+ ["\U0001fac2"] = ":people_hugging:",
+ ["\U0001f46f"] = ":people_with_bunny_ears_partying:",
+ ["\U0001f93c"] = ":people_wrestling:",
+ ["\U0001f3ad"] = ":performing_arts:",
+ ["\U0001f623"] = ":persevere:",
+ ["\U0001f9d1\u200d\U0001f9b2"] = ":person_bald:",
+ ["\U0001f6b4"] = ":person_biking:",
+ ["\U0001f6b4\U0001f3fb"] = ":person_biking_tone1:",
+ ["\U0001f6b4\U0001f3fc"] = ":person_biking_tone2:",
+ ["\U0001f6b4\U0001f3fd"] = ":person_biking_tone3:",
+ ["\U0001f6b4\U0001f3fe"] = ":person_biking_tone4:",
+ ["\U0001f6b4\U0001f3ff"] = ":person_biking_tone5:",
+ ["\u26f9\ufe0f"] = ":person_bouncing_ball:",
+ ["\u26f9"] = ":person_bouncing_ball:",
+ ["\u26f9\U0001f3fb"] = ":person_bouncing_ball_tone1:",
+ ["\u26f9\U0001f3fc"] = ":person_bouncing_ball_tone2:",
+ ["\u26f9\U0001f3fd"] = ":person_bouncing_ball_tone3:",
+ ["\u26f9\U0001f3fe"] = ":person_bouncing_ball_tone4:",
+ ["\u26f9\U0001f3ff"] = ":person_bouncing_ball_tone5:",
+ ["\U0001f647"] = ":person_bowing:",
+ ["\U0001f647\U0001f3fb"] = ":person_bowing_tone1:",
+ ["\U0001f647\U0001f3fc"] = ":person_bowing_tone2:",
+ ["\U0001f647\U0001f3fd"] = ":person_bowing_tone3:",
+ ["\U0001f647\U0001f3fe"] = ":person_bowing_tone4:",
+ ["\U0001f647\U0001f3ff"] = ":person_bowing_tone5:",
+ ["\U0001f9d7"] = ":person_climbing:",
+ ["\U0001f9d7\U0001f3fb"] = ":person_climbing_tone1:",
+ ["\U0001f9d7\U0001f3fc"] = ":person_climbing_tone2:",
+ ["\U0001f9d7\U0001f3fd"] = ":person_climbing_tone3:",
+ ["\U0001f9d7\U0001f3fe"] = ":person_climbing_tone4:",
+ ["\U0001f9d7\U0001f3ff"] = ":person_climbing_tone5:",
+ ["\U0001f9d1\u200d\U0001f9b1"] = ":person_curly_hair:",
+ ["\U0001f938"] = ":person_doing_cartwheel:",
+ ["\U0001f938\U0001f3fb"] = ":person_doing_cartwheel_tone1:",
+ ["\U0001f938\U0001f3fc"] = ":person_doing_cartwheel_tone2:",
+ ["\U0001f938\U0001f3fd"] = ":person_doing_cartwheel_tone3:",
+ ["\U0001f938\U0001f3fe"] = ":person_doing_cartwheel_tone4:",
+ ["\U0001f938\U0001f3ff"] = ":person_doing_cartwheel_tone5:",
+ ["\U0001f926"] = ":person_facepalming:",
+ ["\U0001f926\U0001f3fb"] = ":person_facepalming_tone1:",
+ ["\U0001f926\U0001f3fc"] = ":person_facepalming_tone2:",
+ ["\U0001f926\U0001f3fd"] = ":person_facepalming_tone3:",
+ ["\U0001f926\U0001f3fe"] = ":person_facepalming_tone4:",
+ ["\U0001f926\U0001f3ff"] = ":person_facepalming_tone5:",
+ ["\U0001f9d1\u200d\U0001f37c"] = ":person_feeding_baby:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f37c"] = ":person_feeding_baby_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f37c"] = ":person_feeding_baby_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f37c"] = ":person_feeding_baby_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f37c"] = ":person_feeding_baby_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f37c"] = ":person_feeding_baby_tone5:",
+ ["\U0001f93a"] = ":person_fencing:",
+ ["\U0001f64d"] = ":person_frowning:",
+ ["\U0001f64d\U0001f3fb"] = ":person_frowning_tone1:",
+ ["\U0001f64d\U0001f3fc"] = ":person_frowning_tone2:",
+ ["\U0001f64d\U0001f3fd"] = ":person_frowning_tone3:",
+ ["\U0001f64d\U0001f3fe"] = ":person_frowning_tone4:",
+ ["\U0001f64d\U0001f3ff"] = ":person_frowning_tone5:",
+ ["\U0001f645"] = ":person_gesturing_no:",
+ ["\U0001f645\U0001f3fb"] = ":person_gesturing_no_tone1:",
+ ["\U0001f645\U0001f3fc"] = ":person_gesturing_no_tone2:",
+ ["\U0001f645\U0001f3fd"] = ":person_gesturing_no_tone3:",
+ ["\U0001f645\U0001f3fe"] = ":person_gesturing_no_tone4:",
+ ["\U0001f645\U0001f3ff"] = ":person_gesturing_no_tone5:",
+ ["\U0001f646"] = ":person_gesturing_ok:",
+ ["\U0001f646\U0001f3fb"] = ":person_gesturing_ok_tone1:",
+ ["\U0001f646\U0001f3fc"] = ":person_gesturing_ok_tone2:",
+ ["\U0001f646\U0001f3fd"] = ":person_gesturing_ok_tone3:",
+ ["\U0001f646\U0001f3fe"] = ":person_gesturing_ok_tone4:",
+ ["\U0001f646\U0001f3ff"] = ":person_gesturing_ok_tone5:",
+ ["\U0001f487"] = ":person_getting_haircut:",
+ ["\U0001f487\U0001f3fb"] = ":person_getting_haircut_tone1:",
+ ["\U0001f487\U0001f3fc"] = ":person_getting_haircut_tone2:",
+ ["\U0001f487\U0001f3fd"] = ":person_getting_haircut_tone3:",
+ ["\U0001f487\U0001f3fe"] = ":person_getting_haircut_tone4:",
+ ["\U0001f487\U0001f3ff"] = ":person_getting_haircut_tone5:",
+ ["\U0001f486"] = ":person_getting_massage:",
+ ["\U0001f486\U0001f3fb"] = ":person_getting_massage_tone1:",
+ ["\U0001f486\U0001f3fc"] = ":person_getting_massage_tone2:",
+ ["\U0001f486\U0001f3fd"] = ":person_getting_massage_tone3:",
+ ["\U0001f486\U0001f3fe"] = ":person_getting_massage_tone4:",
+ ["\U0001f486\U0001f3ff"] = ":person_getting_massage_tone5:",
+ ["\U0001f3cc\ufe0f"] = ":person_golfing:",
+ ["\U0001f3cc"] = ":person_golfing:",
+ ["\U0001f3cc\U0001f3fb"] = ":person_golfing_tone1:",
+ ["\U0001f3cc\U0001f3fc"] = ":person_golfing_tone2:",
+ ["\U0001f3cc\U0001f3fd"] = ":person_golfing_tone3:",
+ ["\U0001f3cc\U0001f3fe"] = ":person_golfing_tone4:",
+ ["\U0001f3cc\U0001f3ff"] = ":person_golfing_tone5:",
+ ["\U0001f6cc\U0001f3fb"] = ":person_in_bed_tone1:",
+ ["\U0001f6cc\U0001f3fc"] = ":person_in_bed_tone2:",
+ ["\U0001f6cc\U0001f3fd"] = ":person_in_bed_tone3:",
+ ["\U0001f6cc\U0001f3fe"] = ":person_in_bed_tone4:",
+ ["\U0001f6cc\U0001f3ff"] = ":person_in_bed_tone5:",
+ ["\U0001f9d8"] = ":person_in_lotus_position:",
+ ["\U0001f9d8\U0001f3fb"] = ":person_in_lotus_position_tone1:",
+ ["\U0001f9d8\U0001f3fc"] = ":person_in_lotus_position_tone2:",
+ ["\U0001f9d8\U0001f3fd"] = ":person_in_lotus_position_tone3:",
+ ["\U0001f9d8\U0001f3fe"] = ":person_in_lotus_position_tone4:",
+ ["\U0001f9d8\U0001f3ff"] = ":person_in_lotus_position_tone5:",
+ ["\U0001f9d1\u200d\U0001f9bd"] = ":person_in_manual_wheelchair:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f9bd"] = ":person_in_manual_wheelchair_tone5:",
+ ["\U0001f9d1\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f9bc"] = ":person_in_motorized_wheelchair_tone5:",
+ ["\U0001f9d6"] = ":person_in_steamy_room:",
+ ["\U0001f9d6\U0001f3fb"] = ":person_in_steamy_room_tone1:",
+ ["\U0001f9d6\U0001f3fc"] = ":person_in_steamy_room_tone2:",
+ ["\U0001f9d6\U0001f3fd"] = ":person_in_steamy_room_tone3:",
+ ["\U0001f9d6\U0001f3fe"] = ":person_in_steamy_room_tone4:",
+ ["\U0001f9d6\U0001f3ff"] = ":person_in_steamy_room_tone5:",
+ ["\U0001f935"] = ":person_in_tuxedo:",
+ ["\U0001f935\U0001f3fb"] = ":person_in_tuxedo_tone1:",
+ ["\U0001f935\U0001f3fc"] = ":person_in_tuxedo_tone2:",
+ ["\U0001f935\U0001f3fd"] = ":person_in_tuxedo_tone3:",
+ ["\U0001f935\U0001f3fe"] = ":person_in_tuxedo_tone4:",
+ ["\U0001f935\U0001f3ff"] = ":person_in_tuxedo_tone5:",
+ ["\U0001f939"] = ":person_juggling:",
+ ["\U0001f939\U0001f3fb"] = ":person_juggling_tone1:",
+ ["\U0001f939\U0001f3fc"] = ":person_juggling_tone2:",
+ ["\U0001f939\U0001f3fd"] = ":person_juggling_tone3:",
+ ["\U0001f939\U0001f3fe"] = ":person_juggling_tone4:",
+ ["\U0001f939\U0001f3ff"] = ":person_juggling_tone5:",
+ ["\U0001f9ce"] = ":person_kneeling:",
+ ["\U0001f9ce\U0001f3fb"] = ":person_kneeling_tone1:",
+ ["\U0001f9ce\U0001f3fc"] = ":person_kneeling_tone2:",
+ ["\U0001f9ce\U0001f3fd"] = ":person_kneeling_tone3:",
+ ["\U0001f9ce\U0001f3fe"] = ":person_kneeling_tone4:",
+ ["\U0001f9ce\U0001f3ff"] = ":person_kneeling_tone5:",
+ ["\U0001f3cb\ufe0f"] = ":person_lifting_weights:",
+ ["\U0001f3cb"] = ":person_lifting_weights:",
+ ["\U0001f3cb\U0001f3fb"] = ":person_lifting_weights_tone1:",
+ ["\U0001f3cb\U0001f3fc"] = ":person_lifting_weights_tone2:",
+ ["\U0001f3cb\U0001f3fd"] = ":person_lifting_weights_tone3:",
+ ["\U0001f3cb\U0001f3fe"] = ":person_lifting_weights_tone4:",
+ ["\U0001f3cb\U0001f3ff"] = ":person_lifting_weights_tone5:",
+ ["\U0001f6b5"] = ":person_mountain_biking:",
+ ["\U0001f6b5\U0001f3fb"] = ":person_mountain_biking_tone1:",
+ ["\U0001f6b5\U0001f3fc"] = ":person_mountain_biking_tone2:",
+ ["\U0001f6b5\U0001f3fd"] = ":person_mountain_biking_tone3:",
+ ["\U0001f6b5\U0001f3fe"] = ":person_mountain_biking_tone4:",
+ ["\U0001f6b5\U0001f3ff"] = ":person_mountain_biking_tone5:",
+ ["\U0001f93e"] = ":person_playing_handball:",
+ ["\U0001f93e\U0001f3fb"] = ":person_playing_handball_tone1:",
+ ["\U0001f93e\U0001f3fc"] = ":person_playing_handball_tone2:",
+ ["\U0001f93e\U0001f3fd"] = ":person_playing_handball_tone3:",
+ ["\U0001f93e\U0001f3fe"] = ":person_playing_handball_tone4:",
+ ["\U0001f93e\U0001f3ff"] = ":person_playing_handball_tone5:",
+ ["\U0001f93d"] = ":person_playing_water_polo:",
+ ["\U0001f93d\U0001f3fb"] = ":person_playing_water_polo_tone1:",
+ ["\U0001f93d\U0001f3fc"] = ":person_playing_water_polo_tone2:",
+ ["\U0001f93d\U0001f3fd"] = ":person_playing_water_polo_tone3:",
+ ["\U0001f93d\U0001f3fe"] = ":person_playing_water_polo_tone4:",
+ ["\U0001f93d\U0001f3ff"] = ":person_playing_water_polo_tone5:",
+ ["\U0001f64e"] = ":person_pouting:",
+ ["\U0001f64e\U0001f3fb"] = ":person_pouting_tone1:",
+ ["\U0001f64e\U0001f3fc"] = ":person_pouting_tone2:",
+ ["\U0001f64e\U0001f3fd"] = ":person_pouting_tone3:",
+ ["\U0001f64e\U0001f3fe"] = ":person_pouting_tone4:",
+ ["\U0001f64e\U0001f3ff"] = ":person_pouting_tone5:",
+ ["\U0001f64b"] = ":person_raising_hand:",
+ ["\U0001f64b\U0001f3fb"] = ":person_raising_hand_tone1:",
+ ["\U0001f64b\U0001f3fc"] = ":person_raising_hand_tone2:",
+ ["\U0001f64b\U0001f3fd"] = ":person_raising_hand_tone3:",
+ ["\U0001f64b\U0001f3fe"] = ":person_raising_hand_tone4:",
+ ["\U0001f64b\U0001f3ff"] = ":person_raising_hand_tone5:",
+ ["\U0001f9d1\u200d\U0001f9b0"] = ":person_red_hair:",
+ ["\U0001f6a3"] = ":person_rowing_boat:",
+ ["\U0001f6a3\U0001f3fb"] = ":person_rowing_boat_tone1:",
+ ["\U0001f6a3\U0001f3fc"] = ":person_rowing_boat_tone2:",
+ ["\U0001f6a3\U0001f3fd"] = ":person_rowing_boat_tone3:",
+ ["\U0001f6a3\U0001f3fe"] = ":person_rowing_boat_tone4:",
+ ["\U0001f6a3\U0001f3ff"] = ":person_rowing_boat_tone5:",
+ ["\U0001f3c3"] = ":person_running:",
+ ["\U0001f3c3\U0001f3fb"] = ":person_running_tone1:",
+ ["\U0001f3c3\U0001f3fc"] = ":person_running_tone2:",
+ ["\U0001f3c3\U0001f3fd"] = ":person_running_tone3:",
+ ["\U0001f3c3\U0001f3fe"] = ":person_running_tone4:",
+ ["\U0001f3c3\U0001f3ff"] = ":person_running_tone5:",
+ ["\U0001f937"] = ":person_shrugging:",
+ ["\U0001f937\U0001f3fb"] = ":person_shrugging_tone1:",
+ ["\U0001f937\U0001f3fc"] = ":person_shrugging_tone2:",
+ ["\U0001f937\U0001f3fd"] = ":person_shrugging_tone3:",
+ ["\U0001f937\U0001f3fe"] = ":person_shrugging_tone4:",
+ ["\U0001f937\U0001f3ff"] = ":person_shrugging_tone5:",
+ ["\U0001f9cd"] = ":person_standing:",
+ ["\U0001f9cd\U0001f3fb"] = ":person_standing_tone1:",
+ ["\U0001f9cd\U0001f3fc"] = ":person_standing_tone2:",
+ ["\U0001f9cd\U0001f3fd"] = ":person_standing_tone3:",
+ ["\U0001f9cd\U0001f3fe"] = ":person_standing_tone4:",
+ ["\U0001f9cd\U0001f3ff"] = ":person_standing_tone5:",
+ ["\U0001f3c4"] = ":person_surfing:",
+ ["\U0001f3c4\U0001f3fb"] = ":person_surfing_tone1:",
+ ["\U0001f3c4\U0001f3fc"] = ":person_surfing_tone2:",
+ ["\U0001f3c4\U0001f3fd"] = ":person_surfing_tone3:",
+ ["\U0001f3c4\U0001f3fe"] = ":person_surfing_tone4:",
+ ["\U0001f3c4\U0001f3ff"] = ":person_surfing_tone5:",
+ ["\U0001f3ca"] = ":person_swimming:",
+ ["\U0001f3ca\U0001f3fb"] = ":person_swimming_tone1:",
+ ["\U0001f3ca\U0001f3fc"] = ":person_swimming_tone2:",
+ ["\U0001f3ca\U0001f3fd"] = ":person_swimming_tone3:",
+ ["\U0001f3ca\U0001f3fe"] = ":person_swimming_tone4:",
+ ["\U0001f3ca\U0001f3ff"] = ":person_swimming_tone5:",
+ ["\U0001f481"] = ":person_tipping_hand:",
+ ["\U0001f481\U0001f3fb"] = ":person_tipping_hand_tone1:",
+ ["\U0001f481\U0001f3fc"] = ":person_tipping_hand_tone2:",
+ ["\U0001f481\U0001f3fd"] = ":person_tipping_hand_tone3:",
+ ["\U0001f481\U0001f3fe"] = ":person_tipping_hand_tone4:",
+ ["\U0001f481\U0001f3ff"] = ":person_tipping_hand_tone5:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f9b2"] = ":person_tone1_bald:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f9b1"] = ":person_tone1_curly_hair:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f9b0"] = ":person_tone1_red_hair:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f9b3"] = ":person_tone1_white_hair:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f9b2"] = ":person_tone2_bald:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f9b1"] = ":person_tone2_curly_hair:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f9b0"] = ":person_tone2_red_hair:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f9b3"] = ":person_tone2_white_hair:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f9b2"] = ":person_tone3_bald:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f9b1"] = ":person_tone3_curly_hair:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f9b0"] = ":person_tone3_red_hair:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f9b3"] = ":person_tone3_white_hair:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f9b2"] = ":person_tone4_bald:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f9b1"] = ":person_tone4_curly_hair:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f9b0"] = ":person_tone4_red_hair:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f9b3"] = ":person_tone4_white_hair:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f9b2"] = ":person_tone5_bald:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f9b1"] = ":person_tone5_curly_hair:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f9b0"] = ":person_tone5_red_hair:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f9b3"] = ":person_tone5_white_hair:",
+ ["\U0001f6b6"] = ":person_walking:",
+ ["\U0001f6b6\U0001f3fb"] = ":person_walking_tone1:",
+ ["\U0001f6b6\U0001f3fc"] = ":person_walking_tone2:",
+ ["\U0001f6b6\U0001f3fd"] = ":person_walking_tone3:",
+ ["\U0001f6b6\U0001f3fe"] = ":person_walking_tone4:",
+ ["\U0001f6b6\U0001f3ff"] = ":person_walking_tone5:",
+ ["\U0001f473"] = ":person_wearing_turban:",
+ ["\U0001f473\U0001f3fb"] = ":person_wearing_turban_tone1:",
+ ["\U0001f473\U0001f3fc"] = ":person_wearing_turban_tone2:",
+ ["\U0001f473\U0001f3fd"] = ":person_wearing_turban_tone3:",
+ ["\U0001f473\U0001f3fe"] = ":person_wearing_turban_tone4:",
+ ["\U0001f473\U0001f3ff"] = ":person_wearing_turban_tone5:",
+ ["\U0001f9d1\u200d\U0001f9b3"] = ":person_white_hair:",
+ ["\U0001f9d1\u200d\U0001f9af"] = ":person_with_probing_cane:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f9af"] = ":person_with_probing_cane_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f9af"] = ":person_with_probing_cane_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f9af"] = ":person_with_probing_cane_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f9af"] = ":person_with_probing_cane_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f9af"] = ":person_with_probing_cane_tone5:",
+ ["\U0001f470"] = ":person_with_veil:",
+ ["\U0001f470\U0001f3fb"] = ":person_with_veil_tone1:",
+ ["\U0001f470\U0001f3fc"] = ":person_with_veil_tone2:",
+ ["\U0001f470\U0001f3fd"] = ":person_with_veil_tone3:",
+ ["\U0001f470\U0001f3fe"] = ":person_with_veil_tone4:",
+ ["\U0001f470\U0001f3ff"] = ":person_with_veil_tone5:",
+ ["\U0001f9eb"] = ":petri_dish:",
+ ["\u26cf\ufe0f"] = ":pick:",
+ ["\u26cf"] = ":pick:",
+ ["\U0001f6fb"] = ":pickup_truck:",
+ ["\U0001f967"] = ":pie:",
+ ["\U0001f437"] = ":pig:",
+ ["\U0001f43d"] = ":pig_nose:",
+ ["\U0001f416"] = ":pig2:",
+ ["\U0001f48a"] = ":pill:",
+ ["\U0001f9d1\u200d\u2708\ufe0f"] = ":pilot:",
+ ["\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f"] = ":pilot_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f"] = ":pilot_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f"] = ":pilot_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f"] = ":pilot_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f"] = ":pilot_tone5:",
+ ["\U0001fa85"] = ":piñata:",
+ ["\U0001f90c"] = ":pinched_fingers:",
+ ["\U0001f90c\U0001f3fb"] = ":pinched_fingers_tone1:",
+ ["\U0001f90c\U0001f3fc"] = ":pinched_fingers_tone2:",
+ ["\U0001f90c\U0001f3fd"] = ":pinched_fingers_tone3:",
+ ["\U0001f90c\U0001f3fe"] = ":pinched_fingers_tone4:",
+ ["\U0001f90c\U0001f3ff"] = ":pinched_fingers_tone5:",
+ ["\U0001f90f"] = ":pinching_hand:",
+ ["\U0001f90f\U0001f3fb"] = ":pinching_hand_tone1:",
+ ["\U0001f90f\U0001f3fc"] = ":pinching_hand_tone2:",
+ ["\U0001f90f\U0001f3fd"] = ":pinching_hand_tone3:",
+ ["\U0001f90f\U0001f3fe"] = ":pinching_hand_tone4:",
+ ["\U0001f90f\U0001f3ff"] = ":pinching_hand_tone5:",
+ ["\U0001f34d"] = ":pineapple:",
+ ["\U0001f3d3"] = ":ping_pong:",
+ ["\U0001f3f4\u200d\u2620\ufe0f"] = ":pirate_flag:",
+ ["\u2653"] = ":pisces:",
+ ["\U0001f355"] = ":pizza:",
+ ["\U0001faa7"] = ":placard:",
+ ["\U0001f6d0"] = ":place_of_worship:",
+ ["\u23ef\ufe0f"] = ":play_pause:",
+ ["\u23ef"] = ":play_pause:",
+ ["\U0001f97a"] = ":pleading_face:",
+ ["\U0001faa0"] = ":plunger:",
+ ["\U0001f447"] = ":point_down:",
+ ["\U0001f447\U0001f3fb"] = ":point_down_tone1:",
+ ["\U0001f447\U0001f3fc"] = ":point_down_tone2:",
+ ["\U0001f447\U0001f3fd"] = ":point_down_tone3:",
+ ["\U0001f447\U0001f3fe"] = ":point_down_tone4:",
+ ["\U0001f447\U0001f3ff"] = ":point_down_tone5:",
+ ["\U0001f448"] = ":point_left:",
+ ["\U0001f448\U0001f3fb"] = ":point_left_tone1:",
+ ["\U0001f448\U0001f3fc"] = ":point_left_tone2:",
+ ["\U0001f448\U0001f3fd"] = ":point_left_tone3:",
+ ["\U0001f448\U0001f3fe"] = ":point_left_tone4:",
+ ["\U0001f448\U0001f3ff"] = ":point_left_tone5:",
+ ["\U0001f449"] = ":point_right:",
+ ["\U0001f449\U0001f3fb"] = ":point_right_tone1:",
+ ["\U0001f449\U0001f3fc"] = ":point_right_tone2:",
+ ["\U0001f449\U0001f3fd"] = ":point_right_tone3:",
+ ["\U0001f449\U0001f3fe"] = ":point_right_tone4:",
+ ["\U0001f449\U0001f3ff"] = ":point_right_tone5:",
+ ["\u261d\ufe0f"] = ":point_up:",
+ ["\u261d"] = ":point_up:",
+ ["\U0001f446"] = ":point_up_2:",
+ ["\U0001f446\U0001f3fb"] = ":point_up_2_tone1:",
+ ["\U0001f446\U0001f3fc"] = ":point_up_2_tone2:",
+ ["\U0001f446\U0001f3fd"] = ":point_up_2_tone3:",
+ ["\U0001f446\U0001f3fe"] = ":point_up_2_tone4:",
+ ["\U0001f446\U0001f3ff"] = ":point_up_2_tone5:",
+ ["\u261d\U0001f3fb"] = ":point_up_tone1:",
+ ["\u261d\U0001f3fc"] = ":point_up_tone2:",
+ ["\u261d\U0001f3fd"] = ":point_up_tone3:",
+ ["\u261d\U0001f3fe"] = ":point_up_tone4:",
+ ["\u261d\U0001f3ff"] = ":point_up_tone5:",
+ ["\U0001f43b\u200d\u2744\ufe0f"] = ":polar_bear:",
+ ["\U0001f693"] = ":police_car:",
+ ["\U0001f46e"] = ":police_officer:",
+ ["\U0001f46e\U0001f3fb"] = ":police_officer_tone1:",
+ ["\U0001f46e\U0001f3fc"] = ":police_officer_tone2:",
+ ["\U0001f46e\U0001f3fd"] = ":police_officer_tone3:",
+ ["\U0001f46e\U0001f3fe"] = ":police_officer_tone4:",
+ ["\U0001f46e\U0001f3ff"] = ":police_officer_tone5:",
+ ["\U0001f429"] = ":poodle:",
+ ["\U0001f4a9"] = ":poop:",
+ ["\U0001f37f"] = ":popcorn:",
+ ["\U0001f3e3"] = ":post_office:",
+ ["\U0001f4ef"] = ":postal_horn:",
+ ["\U0001f4ee"] = ":postbox:",
+ ["\U0001f6b0"] = ":potable_water:",
+ ["\U0001f954"] = ":potato:",
+ ["\U0001fab4"] = ":potted_plant:",
+ ["\U0001f45d"] = ":pouch:",
+ ["\U0001f357"] = ":poultry_leg:",
+ ["\U0001f4b7"] = ":pound:",
+ ["\U0001f63e"] = ":pouting_cat:",
+ ["\U0001f64f"] = ":pray:",
+ ["\U0001f64f\U0001f3fb"] = ":pray_tone1:",
+ ["\U0001f64f\U0001f3fc"] = ":pray_tone2:",
+ ["\U0001f64f\U0001f3fd"] = ":pray_tone3:",
+ ["\U0001f64f\U0001f3fe"] = ":pray_tone4:",
+ ["\U0001f64f\U0001f3ff"] = ":pray_tone5:",
+ ["\U0001f4ff"] = ":prayer_beads:",
+ ["\U0001f930"] = ":pregnant_woman:",
+ ["\U0001f930\U0001f3fb"] = ":pregnant_woman_tone1:",
+ ["\U0001f930\U0001f3fc"] = ":pregnant_woman_tone2:",
+ ["\U0001f930\U0001f3fd"] = ":pregnant_woman_tone3:",
+ ["\U0001f930\U0001f3fe"] = ":pregnant_woman_tone4:",
+ ["\U0001f930\U0001f3ff"] = ":pregnant_woman_tone5:",
+ ["\U0001f968"] = ":pretzel:",
+ ["\U0001f934"] = ":prince:",
+ ["\U0001f934\U0001f3fb"] = ":prince_tone1:",
+ ["\U0001f934\U0001f3fc"] = ":prince_tone2:",
+ ["\U0001f934\U0001f3fd"] = ":prince_tone3:",
+ ["\U0001f934\U0001f3fe"] = ":prince_tone4:",
+ ["\U0001f934\U0001f3ff"] = ":prince_tone5:",
+ ["\U0001f478"] = ":princess:",
+ ["\U0001f478\U0001f3fb"] = ":princess_tone1:",
+ ["\U0001f478\U0001f3fc"] = ":princess_tone2:",
+ ["\U0001f478\U0001f3fd"] = ":princess_tone3:",
+ ["\U0001f478\U0001f3fe"] = ":princess_tone4:",
+ ["\U0001f478\U0001f3ff"] = ":princess_tone5:",
+ ["\U0001f5a8\ufe0f"] = ":printer:",
+ ["\U0001f5a8"] = ":printer:",
+ ["\U0001f9af"] = ":probing_cane:",
+ ["\U0001f4fd\ufe0f"] = ":projector:",
+ ["\U0001f4fd"] = ":projector:",
+ ["\U0001f44a"] = ":punch:",
+ ["\U0001f44a\U0001f3fb"] = ":punch_tone1:",
+ ["\U0001f44a\U0001f3fc"] = ":punch_tone2:",
+ ["\U0001f44a\U0001f3fd"] = ":punch_tone3:",
+ ["\U0001f44a\U0001f3fe"] = ":punch_tone4:",
+ ["\U0001f44a\U0001f3ff"] = ":punch_tone5:",
+ ["\U0001f7e3"] = ":purple_circle:",
+ ["\U0001f49c"] = ":purple_heart:",
+ ["\U0001f7ea"] = ":purple_square:",
+ ["\U0001f45b"] = ":purse:",
+ ["\U0001f4cc"] = ":pushpin:",
+ ["\U0001f6ae"] = ":put_litter_in_its_place:",
+ ["\u2753"] = ":question:",
+ ["\U0001f430"] = ":rabbit:",
+ ["\U0001f407"] = ":rabbit2:",
+ ["\U0001f99d"] = ":raccoon:",
+ ["\U0001f3ce\ufe0f"] = ":race_car:",
+ ["\U0001f3ce"] = ":race_car:",
+ ["\U0001f40e"] = ":racehorse:",
+ ["\U0001f4fb"] = ":radio:",
+ ["\U0001f518"] = ":radio_button:",
+ ["\u2622\ufe0f"] = ":radioactive:",
+ ["\u2622"] = ":radioactive:",
+ ["\U0001f621"] = ":rage:",
+ ["\U0001f683"] = ":railway_car:",
+ ["\U0001f6e4\ufe0f"] = ":railway_track:",
+ ["\U0001f6e4"] = ":railway_track:",
+ ["\U0001f308"] = ":rainbow:",
+ ["\U0001f3f3\ufe0f\u200d\U0001f308"] = ":rainbow_flag:",
+ ["\U0001f91a"] = ":raised_back_of_hand:",
+ ["\U0001f91a\U0001f3fb"] = ":raised_back_of_hand_tone1:",
+ ["\U0001f91a\U0001f3fc"] = ":raised_back_of_hand_tone2:",
+ ["\U0001f91a\U0001f3fd"] = ":raised_back_of_hand_tone3:",
+ ["\U0001f91a\U0001f3fe"] = ":raised_back_of_hand_tone4:",
+ ["\U0001f91a\U0001f3ff"] = ":raised_back_of_hand_tone5:",
+ ["\u270b"] = ":raised_hand:",
+ ["\u270b\U0001f3fb"] = ":raised_hand_tone1:",
+ ["\u270b\U0001f3fc"] = ":raised_hand_tone2:",
+ ["\u270b\U0001f3fd"] = ":raised_hand_tone3:",
+ ["\u270b\U0001f3fe"] = ":raised_hand_tone4:",
+ ["\u270b\U0001f3ff"] = ":raised_hand_tone5:",
+ ["\U0001f64c"] = ":raised_hands:",
+ ["\U0001f64c\U0001f3fb"] = ":raised_hands_tone1:",
+ ["\U0001f64c\U0001f3fc"] = ":raised_hands_tone2:",
+ ["\U0001f64c\U0001f3fd"] = ":raised_hands_tone3:",
+ ["\U0001f64c\U0001f3fe"] = ":raised_hands_tone4:",
+ ["\U0001f64c\U0001f3ff"] = ":raised_hands_tone5:",
+ ["\U0001f40f"] = ":ram:",
+ ["\U0001f35c"] = ":ramen:",
+ ["\U0001f400"] = ":rat:",
+ ["\U0001fa92"] = ":razor:",
+ ["\U0001f9fe"] = ":receipt:",
+ ["\u23fa\ufe0f"] = ":record_button:",
+ ["\u23fa"] = ":record_button:",
+ ["\u267b\ufe0f"] = ":recycle:",
+ ["\u267b"] = ":recycle:",
+ ["\U0001f697"] = ":red_car:",
+ ["\U0001f534"] = ":red_circle:",
+ ["\U0001f9e7"] = ":red_envelope:",
+ ["\U0001f7e5"] = ":red_square:",
+ ["\U0001f1e6"] = ":regional_indicator_a:",
+ ["\U0001f1e7"] = ":regional_indicator_b:",
+ ["\U0001f1e8"] = ":regional_indicator_c:",
+ ["\U0001f1e9"] = ":regional_indicator_d:",
+ ["\U0001f1ea"] = ":regional_indicator_e:",
+ ["\U0001f1eb"] = ":regional_indicator_f:",
+ ["\U0001f1ec"] = ":regional_indicator_g:",
+ ["\U0001f1ed"] = ":regional_indicator_h:",
+ ["\U0001f1ee"] = ":regional_indicator_i:",
+ ["\U0001f1ef"] = ":regional_indicator_j:",
+ ["\U0001f1f0"] = ":regional_indicator_k:",
+ ["\U0001f1f1"] = ":regional_indicator_l:",
+ ["\U0001f1f2"] = ":regional_indicator_m:",
+ ["\U0001f1f3"] = ":regional_indicator_n:",
+ ["\U0001f1f4"] = ":regional_indicator_o:",
+ ["\U0001f1f5"] = ":regional_indicator_p:",
+ ["\U0001f1f6"] = ":regional_indicator_q:",
+ ["\U0001f1f7"] = ":regional_indicator_r:",
+ ["\U0001f1f8"] = ":regional_indicator_s:",
+ ["\U0001f1f9"] = ":regional_indicator_t:",
+ ["\U0001f1fa"] = ":regional_indicator_u:",
+ ["\U0001f1fb"] = ":regional_indicator_v:",
+ ["\U0001f1fc"] = ":regional_indicator_w:",
+ ["\U0001f1fd"] = ":regional_indicator_x:",
+ ["\U0001f1fe"] = ":regional_indicator_y:",
+ ["\U0001f1ff"] = ":regional_indicator_z:",
+ ["\u00ae\ufe0f"] = ":registered:",
+ ["\u00ae"] = ":registered:",
+ ["\u263a\ufe0f"] = ":relaxed:",
+ ["\u263a"] = ":relaxed:",
+ ["\U0001f60c"] = ":relieved:",
+ ["\U0001f397\ufe0f"] = ":reminder_ribbon:",
+ ["\U0001f397"] = ":reminder_ribbon:",
+ ["\U0001f501"] = ":repeat:",
+ ["\U0001f502"] = ":repeat_one:",
+ ["\U0001f6bb"] = ":restroom:",
+ ["\U0001f49e"] = ":revolving_hearts:",
+ ["\u23ea"] = ":rewind:",
+ ["\U0001f98f"] = ":rhino:",
+ ["\U0001f380"] = ":ribbon:",
+ ["\U0001f35a"] = ":rice:",
+ ["\U0001f359"] = ":rice_ball:",
+ ["\U0001f358"] = ":rice_cracker:",
+ ["\U0001f391"] = ":rice_scene:",
+ ["\U0001f91c"] = ":right_facing_fist:",
+ ["\U0001f91c\U0001f3fb"] = ":right_facing_fist_tone1:",
+ ["\U0001f91c\U0001f3fc"] = ":right_facing_fist_tone2:",
+ ["\U0001f91c\U0001f3fd"] = ":right_facing_fist_tone3:",
+ ["\U0001f91c\U0001f3fe"] = ":right_facing_fist_tone4:",
+ ["\U0001f91c\U0001f3ff"] = ":right_facing_fist_tone5:",
+ ["\U0001f48d"] = ":ring:",
+ ["\U0001fa90"] = ":ringed_planet:",
+ ["\U0001f916"] = ":robot:",
+ ["\U0001faa8"] = ":rock:",
+ ["\U0001f680"] = ":rocket:",
+ ["\U0001f923"] = ":rofl:",
+ ["\U0001f9fb"] = ":roll_of_paper:",
+ ["\U0001f3a2"] = ":roller_coaster:",
+ ["\U0001f6fc"] = ":roller_skate:",
+ ["\U0001f644"] = ":rolling_eyes:",
+ ["\U0001f413"] = ":rooster:",
+ ["\U0001f339"] = ":rose:",
+ ["\U0001f3f5\ufe0f"] = ":rosette:",
+ ["\U0001f3f5"] = ":rosette:",
+ ["\U0001f6a8"] = ":rotating_light:",
+ ["\U0001f4cd"] = ":round_pushpin:",
+ ["\U0001f3c9"] = ":rugby_football:",
+ ["\U0001f3bd"] = ":running_shirt_with_sash:",
+ ["\U0001f202\ufe0f"] = ":sa:",
+ ["\U0001f202"] = ":sa:",
+ ["\U0001f9f7"] = ":safety_pin:",
+ ["\U0001f9ba"] = ":safety_vest:",
+ ["\u2650"] = ":sagittarius:",
+ ["\u26f5"] = ":sailboat:",
+ ["\U0001f376"] = ":sake:",
+ ["\U0001f957"] = ":salad:",
+ ["\U0001f9c2"] = ":salt:",
+ ["\U0001f461"] = ":sandal:",
+ ["\U0001f96a"] = ":sandwich:",
+ ["\U0001f385"] = ":santa:",
+ ["\U0001f385\U0001f3fb"] = ":santa_tone1:",
+ ["\U0001f385\U0001f3fc"] = ":santa_tone2:",
+ ["\U0001f385\U0001f3fd"] = ":santa_tone3:",
+ ["\U0001f385\U0001f3fe"] = ":santa_tone4:",
+ ["\U0001f385\U0001f3ff"] = ":santa_tone5:",
+ ["\U0001f97b"] = ":sari:",
+ ["\U0001f4e1"] = ":satellite:",
+ ["\U0001f6f0\ufe0f"] = ":satellite_orbital:",
+ ["\U0001f6f0"] = ":satellite_orbital:",
+ ["\U0001f995"] = ":sauropod:",
+ ["\U0001f3b7"] = ":saxophone:",
+ ["\u2696\ufe0f"] = ":scales:",
+ ["\u2696"] = ":scales:",
+ ["\U0001f9e3"] = ":scarf:",
+ ["\U0001f3eb"] = ":school:",
+ ["\U0001f392"] = ":school_satchel:",
+ ["\U0001f9d1\u200d\U0001f52c"] = ":scientist:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f52c"] = ":scientist_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f52c"] = ":scientist_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f52c"] = ":scientist_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f52c"] = ":scientist_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f52c"] = ":scientist_tone5:",
+ ["\u2702\ufe0f"] = ":scissors:",
+ ["\u2702"] = ":scissors:",
+ ["\U0001f6f4"] = ":scooter:",
+ ["\U0001f982"] = ":scorpion:",
+ ["\u264f"] = ":scorpius:",
+ ["\U0001f3f4\U000e0067\U000e0062\U000e0073\U000e0063\U000e0074\U000e007f"] = ":scotland:",
+ ["\U0001f631"] = ":scream:",
+ ["\U0001f640"] = ":scream_cat:",
+ ["\U0001fa9b"] = ":screwdriver:",
+ ["\U0001f4dc"] = ":scroll:",
+ ["\U0001f9ad"] = ":seal:",
+ ["\U0001f4ba"] = ":seat:",
+ ["\U0001f948"] = ":second_place:",
+ ["\u3299\ufe0f"] = ":secret:",
+ ["\u3299"] = ":secret:",
+ ["\U0001f648"] = ":see_no_evil:",
+ ["\U0001f331"] = ":seedling:",
+ ["\U0001f933"] = ":selfie:",
+ ["\U0001f933\U0001f3fb"] = ":selfie_tone1:",
+ ["\U0001f933\U0001f3fc"] = ":selfie_tone2:",
+ ["\U0001f933\U0001f3fd"] = ":selfie_tone3:",
+ ["\U0001f933\U0001f3fe"] = ":selfie_tone4:",
+ ["\U0001f933\U0001f3ff"] = ":selfie_tone5:",
+ ["\U0001f415\u200d\U0001f9ba"] = ":service_dog:",
+ ["\u0037\ufe0f\u20e3"] = ":seven:",
+ ["\u0037\u20e3"] = ":seven:",
+ ["\U0001faa1"] = ":sewing_needle:",
+ ["\U0001f958"] = ":shallow_pan_of_food:",
+ ["\u2618\ufe0f"] = ":shamrock:",
+ ["\u2618"] = ":shamrock:",
+ ["\U0001f988"] = ":shark:",
+ ["\U0001f367"] = ":shaved_ice:",
+ ["\U0001f411"] = ":sheep:",
+ ["\U0001f41a"] = ":shell:",
+ ["\U0001f6e1\ufe0f"] = ":shield:",
+ ["\U0001f6e1"] = ":shield:",
+ ["\u26e9\ufe0f"] = ":shinto_shrine:",
+ ["\u26e9"] = ":shinto_shrine:",
+ ["\U0001f6a2"] = ":ship:",
+ ["\U0001f455"] = ":shirt:",
+ ["\U0001f6cd\ufe0f"] = ":shopping_bags:",
+ ["\U0001f6cd"] = ":shopping_bags:",
+ ["\U0001f6d2"] = ":shopping_cart:",
+ ["\U0001fa73"] = ":shorts:",
+ ["\U0001f6bf"] = ":shower:",
+ ["\U0001f990"] = ":shrimp:",
+ ["\U0001f92b"] = ":shushing_face:",
+ ["\U0001f4f6"] = ":signal_strength:",
+ ["\U0001f9d1\u200d\U0001f3a4"] = ":singer:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f3a4"] = ":singer_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f3a4"] = ":singer_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f3a4"] = ":singer_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f3a4"] = ":singer_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f3a4"] = ":singer_tone5:",
+ ["\u0036\ufe0f\u20e3"] = ":six:",
+ ["\u0036\u20e3"] = ":six:",
+ ["\U0001f52f"] = ":six_pointed_star:",
+ ["\U0001f6f9"] = ":skateboard:",
+ ["\U0001f3bf"] = ":ski:",
+ ["\u26f7\ufe0f"] = ":skier:",
+ ["\u26f7"] = ":skier:",
+ ["\U0001f480"] = ":skull:",
+ ["\u2620\ufe0f"] = ":skull_crossbones:",
+ ["\u2620"] = ":skull_crossbones:",
+ ["\U0001f9a8"] = ":skunk:",
+ ["\U0001f6f7"] = ":sled:",
+ ["\U0001f634"] = ":sleeping:",
+ ["\U0001f6cc"] = ":sleeping_accommodation:",
+ ["\U0001f62a"] = ":sleepy:",
+ ["\U0001f641"] = ":slight_frown:",
+ ["\U0001f642"] = ":slight_smile:",
+ ["\U0001f3b0"] = ":slot_machine:",
+ ["\U0001f9a5"] = ":sloth:",
+ ["\U0001f539"] = ":small_blue_diamond:",
+ ["\U0001f538"] = ":small_orange_diamond:",
+ ["\U0001f53a"] = ":small_red_triangle:",
+ ["\U0001f53b"] = ":small_red_triangle_down:",
+ ["\U0001f604"] = ":smile:",
+ ["\U0001f638"] = ":smile_cat:",
+ ["\U0001f603"] = ":smiley:",
+ ["\U0001f63a"] = ":smiley_cat:",
+ ["\U0001f970"] = ":smiling_face_with_3_hearts:",
+ ["\U0001f972"] = ":smiling_face_with_tear:",
+ ["\U0001f608"] = ":smiling_imp:",
+ ["\U0001f60f"] = ":smirk:",
+ ["\U0001f63c"] = ":smirk_cat:",
+ ["\U0001f6ac"] = ":smoking:",
+ ["\U0001f40c"] = ":snail:",
+ ["\U0001f40d"] = ":snake:",
+ ["\U0001f927"] = ":sneezing_face:",
+ ["\U0001f3c2"] = ":snowboarder:",
+ ["\U0001f3c2\U0001f3fb"] = ":snowboarder_tone1:",
+ ["\U0001f3c2\U0001f3fc"] = ":snowboarder_tone2:",
+ ["\U0001f3c2\U0001f3fd"] = ":snowboarder_tone3:",
+ ["\U0001f3c2\U0001f3fe"] = ":snowboarder_tone4:",
+ ["\U0001f3c2\U0001f3ff"] = ":snowboarder_tone5:",
+ ["\u2744\ufe0f"] = ":snowflake:",
+ ["\u2744"] = ":snowflake:",
+ ["\u26c4"] = ":snowman:",
+ ["\u2603\ufe0f"] = ":snowman2:",
+ ["\u2603"] = ":snowman2:",
+ ["\U0001f9fc"] = ":soap:",
+ ["\U0001f62d"] = ":sob:",
+ ["\u26bd"] = ":soccer:",
+ ["\U0001f9e6"] = ":socks:",
+ ["\U0001f94e"] = ":softball:",
+ ["\U0001f51c"] = ":soon:",
+ ["\U0001f198"] = ":sos:",
+ ["\U0001f509"] = ":sound:",
+ ["\U0001f47e"] = ":space_invader:",
+ ["\u2660\ufe0f"] = ":spades:",
+ ["\u2660"] = ":spades:",
+ ["\U0001f35d"] = ":spaghetti:",
+ ["\u2747\ufe0f"] = ":sparkle:",
+ ["\u2747"] = ":sparkle:",
+ ["\U0001f387"] = ":sparkler:",
+ ["\u2728"] = ":sparkles:",
+ ["\U0001f496"] = ":sparkling_heart:",
+ ["\U0001f64a"] = ":speak_no_evil:",
+ ["\U0001f508"] = ":speaker:",
+ ["\U0001f5e3\ufe0f"] = ":speaking_head:",
+ ["\U0001f5e3"] = ":speaking_head:",
+ ["\U0001f4ac"] = ":speech_balloon:",
+ ["\U0001f5e8\ufe0f"] = ":speech_left:",
+ ["\U0001f5e8"] = ":speech_left:",
+ ["\U0001f6a4"] = ":speedboat:",
+ ["\U0001f577\ufe0f"] = ":spider:",
+ ["\U0001f577"] = ":spider:",
+ ["\U0001f578\ufe0f"] = ":spider_web:",
+ ["\U0001f578"] = ":spider_web:",
+ ["\U0001f9fd"] = ":sponge:",
+ ["\U0001f944"] = ":spoon:",
+ ["\U0001f9f4"] = ":squeeze_bottle:",
+ ["\U0001f991"] = ":squid:",
+ ["\U0001f3df\ufe0f"] = ":stadium:",
+ ["\U0001f3df"] = ":stadium:",
+ ["\u2b50"] = ":star:",
+ ["\u262a\ufe0f"] = ":star_and_crescent:",
+ ["\u262a"] = ":star_and_crescent:",
+ ["\u2721\ufe0f"] = ":star_of_david:",
+ ["\u2721"] = ":star_of_david:",
+ ["\U0001f929"] = ":star_struck:",
+ ["\U0001f31f"] = ":star2:",
+ ["\U0001f320"] = ":stars:",
+ ["\U0001f689"] = ":station:",
+ ["\U0001f5fd"] = ":statue_of_liberty:",
+ ["\U0001f682"] = ":steam_locomotive:",
+ ["\U0001fa7a"] = ":stethoscope:",
+ ["\U0001f372"] = ":stew:",
+ ["\u23f9\ufe0f"] = ":stop_button:",
+ ["\u23f9"] = ":stop_button:",
+ ["\u23f1\ufe0f"] = ":stopwatch:",
+ ["\u23f1"] = ":stopwatch:",
+ ["\U0001f4cf"] = ":straight_ruler:",
+ ["\U0001f353"] = ":strawberry:",
+ ["\U0001f61b"] = ":stuck_out_tongue:",
+ ["\U0001f61d"] = ":stuck_out_tongue_closed_eyes:",
+ ["\U0001f61c"] = ":stuck_out_tongue_winking_eye:",
+ ["\U0001f9d1\u200d\U0001f393"] = ":student:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f393"] = ":student_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f393"] = ":student_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f393"] = ":student_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f393"] = ":student_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f393"] = ":student_tone5:",
+ ["\U0001f959"] = ":stuffed_flatbread:",
+ ["\U0001f31e"] = ":sun_with_face:",
+ ["\U0001f33b"] = ":sunflower:",
+ ["\U0001f60e"] = ":sunglasses:",
+ ["\u2600\ufe0f"] = ":sunny:",
+ ["\u2600"] = ":sunny:",
+ ["\U0001f305"] = ":sunrise:",
+ ["\U0001f304"] = ":sunrise_over_mountains:",
+ ["\U0001f9b8"] = ":superhero:",
+ ["\U0001f9b8\U0001f3fb"] = ":superhero_tone1:",
+ ["\U0001f9b8\U0001f3fc"] = ":superhero_tone2:",
+ ["\U0001f9b8\U0001f3fd"] = ":superhero_tone3:",
+ ["\U0001f9b8\U0001f3fe"] = ":superhero_tone4:",
+ ["\U0001f9b8\U0001f3ff"] = ":superhero_tone5:",
+ ["\U0001f9b9"] = ":supervillain:",
+ ["\U0001f9b9\U0001f3fb"] = ":supervillain_tone1:",
+ ["\U0001f9b9\U0001f3fc"] = ":supervillain_tone2:",
+ ["\U0001f9b9\U0001f3fd"] = ":supervillain_tone3:",
+ ["\U0001f9b9\U0001f3fe"] = ":supervillain_tone4:",
+ ["\U0001f9b9\U0001f3ff"] = ":supervillain_tone5:",
+ ["\U0001f363"] = ":sushi:",
+ ["\U0001f69f"] = ":suspension_railway:",
+ ["\U0001f9a2"] = ":swan:",
+ ["\U0001f613"] = ":sweat:",
+ ["\U0001f4a6"] = ":sweat_drops:",
+ ["\U0001f605"] = ":sweat_smile:",
+ ["\U0001f360"] = ":sweet_potato:",
+ ["\U0001f523"] = ":symbols:",
+ ["\U0001f54d"] = ":synagogue:",
+ ["\U0001f489"] = ":syringe:",
+ ["\U0001f996"] = ":t_rex:",
+ ["\U0001f32e"] = ":taco:",
+ ["\U0001f389"] = ":tada:",
+ ["\U0001f961"] = ":takeout_box:",
+ ["\U0001fad4"] = ":tamale:",
+ ["\U0001f38b"] = ":tanabata_tree:",
+ ["\U0001f34a"] = ":tangerine:",
+ ["\u2649"] = ":taurus:",
+ ["\U0001f695"] = ":taxi:",
+ ["\U0001f375"] = ":tea:",
+ ["\U0001f9d1\u200d\U0001f3eb"] = ":teacher:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f3eb"] = ":teacher_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f3eb"] = ":teacher_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f3eb"] = ":teacher_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f3eb"] = ":teacher_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f3eb"] = ":teacher_tone5:",
+ ["\U0001fad6"] = ":teapot:",
+ ["\U0001f9d1\u200d\U0001f4bb"] = ":technologist:",
+ ["\U0001f9d1\U0001f3fb\u200d\U0001f4bb"] = ":technologist_tone1:",
+ ["\U0001f9d1\U0001f3fc\u200d\U0001f4bb"] = ":technologist_tone2:",
+ ["\U0001f9d1\U0001f3fd\u200d\U0001f4bb"] = ":technologist_tone3:",
+ ["\U0001f9d1\U0001f3fe\u200d\U0001f4bb"] = ":technologist_tone4:",
+ ["\U0001f9d1\U0001f3ff\u200d\U0001f4bb"] = ":technologist_tone5:",
+ ["\U0001f9f8"] = ":teddy_bear:",
+ ["\u260e\ufe0f"] = ":telephone:",
+ ["\u260e"] = ":telephone:",
+ ["\U0001f4de"] = ":telephone_receiver:",
+ ["\U0001f52d"] = ":telescope:",
+ ["\U0001f3be"] = ":tennis:",
+ ["\u26fa"] = ":tent:",
+ ["\U0001f9ea"] = ":test_tube:",
+ ["\U0001f321\ufe0f"] = ":thermometer:",
+ ["\U0001f321"] = ":thermometer:",
+ ["\U0001f912"] = ":thermometer_face:",
+ ["\U0001f914"] = ":thinking:",
+ ["\U0001f949"] = ":third_place:",
+ ["\U0001fa74"] = ":thong_sandal:",
+ ["\U0001f4ad"] = ":thought_balloon:",
+ ["\U0001f9f5"] = ":thread:",
+ ["\u0033\ufe0f\u20e3"] = ":three:",
+ ["\u0033\u20e3"] = ":three:",
+ ["\U0001f44e"] = ":thumbsdown:",
+ ["\U0001f44e\U0001f3fb"] = ":thumbsdown_tone1:",
+ ["\U0001f44e\U0001f3fc"] = ":thumbsdown_tone2:",
+ ["\U0001f44e\U0001f3fd"] = ":thumbsdown_tone3:",
+ ["\U0001f44e\U0001f3fe"] = ":thumbsdown_tone4:",
+ ["\U0001f44e\U0001f3ff"] = ":thumbsdown_tone5:",
+ ["\U0001f44d"] = ":thumbsup:",
+ ["\U0001f44d\U0001f3fb"] = ":thumbsup_tone1:",
+ ["\U0001f44d\U0001f3fc"] = ":thumbsup_tone2:",
+ ["\U0001f44d\U0001f3fd"] = ":thumbsup_tone3:",
+ ["\U0001f44d\U0001f3fe"] = ":thumbsup_tone4:",
+ ["\U0001f44d\U0001f3ff"] = ":thumbsup_tone5:",
+ ["\u26c8\ufe0f"] = ":thunder_cloud_rain:",
+ ["\u26c8"] = ":thunder_cloud_rain:",
+ ["\U0001f3ab"] = ":ticket:",
+ ["\U0001f39f\ufe0f"] = ":tickets:",
+ ["\U0001f39f"] = ":tickets:",
+ ["\U0001f42f"] = ":tiger:",
+ ["\U0001f405"] = ":tiger2:",
+ ["\u23f2\ufe0f"] = ":timer:",
+ ["\u23f2"] = ":timer:",
+ ["\U0001f62b"] = ":tired_face:",
+ ["\u2122\ufe0f"] = ":tm:",
+ ["\u2122"] = ":tm:",
+ ["\U0001f6bd"] = ":toilet:",
+ ["\U0001f5fc"] = ":tokyo_tower:",
+ ["\U0001f345"] = ":tomato:",
+ ["\U0001f445"] = ":tongue:",
+ ["\U0001f9f0"] = ":toolbox:",
+ ["\U0001f6e0\ufe0f"] = ":tools:",
+ ["\U0001f6e0"] = ":tools:",
+ ["\U0001f9b7"] = ":tooth:",
+ ["\U0001faa5"] = ":toothbrush:",
+ ["\U0001f51d"] = ":top:",
+ ["\U0001f3a9"] = ":tophat:",
+ ["\u23ed\ufe0f"] = ":track_next:",
+ ["\u23ed"] = ":track_next:",
+ ["\u23ee\ufe0f"] = ":track_previous:",
+ ["\u23ee"] = ":track_previous:",
+ ["\U0001f5b2\ufe0f"] = ":trackball:",
+ ["\U0001f5b2"] = ":trackball:",
+ ["\U0001f69c"] = ":tractor:",
+ ["\U0001f6a5"] = ":traffic_light:",
+ ["\U0001f68b"] = ":train:",
+ ["\U0001f686"] = ":train2:",
+ ["\U0001f68a"] = ":tram:",
+ ["\U0001f3f3\ufe0f\u200d\u26a7\ufe0f"] = ":transgender_flag:",
+ ["\u26a7"] = ":transgender_symbol:",
+ ["\U0001f6a9"] = ":triangular_flag_on_post:",
+ ["\U0001f4d0"] = ":triangular_ruler:",
+ ["\U0001f531"] = ":trident:",
+ ["\U0001f624"] = ":triumph:",
+ ["\U0001f68e"] = ":trolleybus:",
+ ["\U0001f3c6"] = ":trophy:",
+ ["\U0001f379"] = ":tropical_drink:",
+ ["\U0001f420"] = ":tropical_fish:",
+ ["\U0001f69a"] = ":truck:",
+ ["\U0001f3ba"] = ":trumpet:",
+ ["\U0001f337"] = ":tulip:",
+ ["\U0001f943"] = ":tumbler_glass:",
+ ["\U0001f983"] = ":turkey:",
+ ["\U0001f422"] = ":turtle:",
+ ["\U0001f4fa"] = ":tv:",
+ ["\U0001f500"] = ":twisted_rightwards_arrows:",
+ ["\u0032\ufe0f\u20e3"] = ":two:",
+ ["\u0032\u20e3"] = ":two:",
+ ["\U0001f495"] = ":two_hearts:",
+ ["\U0001f46c"] = ":two_men_holding_hands:",
+ ["\U0001f239"] = ":u5272:",
+ ["\U0001f234"] = ":u5408:",
+ ["\U0001f23a"] = ":u55b6:",
+ ["\U0001f22f"] = ":u6307:",
+ ["\U0001f237\ufe0f"] = ":u6708:",
+ ["\U0001f237"] = ":u6708:",
+ ["\U0001f236"] = ":u6709:",
+ ["\U0001f235"] = ":u6e80:",
+ ["\U0001f21a"] = ":u7121:",
+ ["\U0001f238"] = ":u7533:",
+ ["\U0001f232"] = ":u7981:",
+ ["\U0001f233"] = ":u7a7a:",
+ ["\u2614"] = ":umbrella:",
+ ["\u2602\ufe0f"] = ":umbrella2:",
+ ["\u2602"] = ":umbrella2:",
+ ["\U0001f612"] = ":unamused:",
+ ["\U0001f51e"] = ":underage:",
+ ["\U0001f984"] = ":unicorn:",
+ ["\U0001f1fa\U0001f1f3"] = ":united_nations:",
+ ["\U0001f513"] = ":unlock:",
+ ["\U0001f199"] = ":up:",
+ ["\U0001f643"] = ":upside_down:",
+ ["\u26b1\ufe0f"] = ":urn:",
+ ["\u26b1"] = ":urn:",
+ ["\u270c\ufe0f"] = ":v:",
+ ["\u270c"] = ":v:",
+ ["\u270c\U0001f3fb"] = ":v_tone1:",
+ ["\u270c\U0001f3fc"] = ":v_tone2:",
+ ["\u270c\U0001f3fd"] = ":v_tone3:",
+ ["\u270c\U0001f3fe"] = ":v_tone4:",
+ ["\u270c\U0001f3ff"] = ":v_tone5:",
+ ["\U0001f9db"] = ":vampire:",
+ ["\U0001f9db\U0001f3fb"] = ":vampire_tone1:",
+ ["\U0001f9db\U0001f3fc"] = ":vampire_tone2:",
+ ["\U0001f9db\U0001f3fd"] = ":vampire_tone3:",
+ ["\U0001f9db\U0001f3fe"] = ":vampire_tone4:",
+ ["\U0001f9db\U0001f3ff"] = ":vampire_tone5:",
+ ["\U0001f6a6"] = ":vertical_traffic_light:",
+ ["\U0001f4fc"] = ":vhs:",
+ ["\U0001f4f3"] = ":vibration_mode:",
+ ["\U0001f4f9"] = ":video_camera:",
+ ["\U0001f3ae"] = ":video_game:",
+ ["\U0001f3bb"] = ":violin:",
+ ["\u264d"] = ":virgo:",
+ ["\U0001f30b"] = ":volcano:",
+ ["\U0001f3d0"] = ":volleyball:",
+ ["\U0001f19a"] = ":vs:",
+ ["\U0001f596"] = ":vulcan:",
+ ["\U0001f596\U0001f3fb"] = ":vulcan_tone1:",
+ ["\U0001f596\U0001f3fc"] = ":vulcan_tone2:",
+ ["\U0001f596\U0001f3fd"] = ":vulcan_tone3:",
+ ["\U0001f596\U0001f3fe"] = ":vulcan_tone4:",
+ ["\U0001f596\U0001f3ff"] = ":vulcan_tone5:",
+ ["\U0001f9c7"] = ":waffle:",
+ ["\U0001f3f4\U000e0067\U000e0062\U000e0077\U000e006c\U000e0073\U000e007f"] = ":wales:",
+ ["\U0001f318"] = ":waning_crescent_moon:",
+ ["\U0001f316"] = ":waning_gibbous_moon:",
+ ["\u26a0\ufe0f"] = ":warning:",
+ ["\u26a0"] = ":warning:",
+ ["\U0001f5d1\ufe0f"] = ":wastebasket:",
+ ["\U0001f5d1"] = ":wastebasket:",
+ ["\u231a"] = ":watch:",
+ ["\U0001f403"] = ":water_buffalo:",
+ ["\U0001f349"] = ":watermelon:",
+ ["\U0001f44b"] = ":wave:",
+ ["\U0001f44b\U0001f3fb"] = ":wave_tone1:",
+ ["\U0001f44b\U0001f3fc"] = ":wave_tone2:",
+ ["\U0001f44b\U0001f3fd"] = ":wave_tone3:",
+ ["\U0001f44b\U0001f3fe"] = ":wave_tone4:",
+ ["\U0001f44b\U0001f3ff"] = ":wave_tone5:",
+ ["\u3030\ufe0f"] = ":wavy_dash:",
+ ["\u3030"] = ":wavy_dash:",
+ ["\U0001f312"] = ":waxing_crescent_moon:",
+ ["\U0001f314"] = ":waxing_gibbous_moon:",
+ ["\U0001f6be"] = ":wc:",
+ ["\U0001f629"] = ":weary:",
+ ["\U0001f492"] = ":wedding:",
+ ["\U0001f433"] = ":whale:",
+ ["\U0001f40b"] = ":whale2:",
+ ["\u2638\ufe0f"] = ":wheel_of_dharma:",
+ ["\u2638"] = ":wheel_of_dharma:",
+ ["\u267f"] = ":wheelchair:",
+ ["\u2705"] = ":white_check_mark:",
+ ["\u26aa"] = ":white_circle:",
+ ["\U0001f4ae"] = ":white_flower:",
+ ["\U0001f90d"] = ":white_heart:",
+ ["\u2b1c"] = ":white_large_square:",
+ ["\u25fd"] = ":white_medium_small_square:",
+ ["\u25fb\ufe0f"] = ":white_medium_square:",
+ ["\u25fb"] = ":white_medium_square:",
+ ["\u25ab\ufe0f"] = ":white_small_square:",
+ ["\u25ab"] = ":white_small_square:",
+ ["\U0001f533"] = ":white_square_button:",
+ ["\U0001f325\ufe0f"] = ":white_sun_cloud:",
+ ["\U0001f325"] = ":white_sun_cloud:",
+ ["\U0001f326\ufe0f"] = ":white_sun_rain_cloud:",
+ ["\U0001f326"] = ":white_sun_rain_cloud:",
+ ["\U0001f324\ufe0f"] = ":white_sun_small_cloud:",
+ ["\U0001f324"] = ":white_sun_small_cloud:",
+ ["\U0001f940"] = ":wilted_rose:",
+ ["\U0001f32c\ufe0f"] = ":wind_blowing_face:",
+ ["\U0001f32c"] = ":wind_blowing_face:",
+ ["\U0001f390"] = ":wind_chime:",
+ ["\U0001fa9f"] = ":window:",
+ ["\U0001f377"] = ":wine_glass:",
+ ["\U0001f609"] = ":wink:",
+ ["\U0001f43a"] = ":wolf:",
+ ["\U0001f469"] = ":woman:",
+ ["\U0001f46b"] = ":woman_and_man_holding_hands_tone5_tone4:",
+ ["\U0001f469\u200d\U0001f3a8"] = ":woman_artist:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f3a8"] = ":woman_artist_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f3a8"] = ":woman_artist_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f3a8"] = ":woman_artist_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f3a8"] = ":woman_artist_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f3a8"] = ":woman_artist_tone5:",
+ ["\U0001f469\u200d\U0001f680"] = ":woman_astronaut:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f680"] = ":woman_astronaut_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f680"] = ":woman_astronaut_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f680"] = ":woman_astronaut_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f680"] = ":woman_astronaut_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f680"] = ":woman_astronaut_tone5:",
+ ["\U0001f469\u200d\U0001f9b2"] = ":woman_bald:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f9b2"] = ":woman_bald_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f9b2"] = ":woman_bald_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f9b2"] = ":woman_bald_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f9b2"] = ":woman_bald_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f9b2"] = ":woman_bald_tone5:",
+ ["\U0001f6b4\u200d\u2640\ufe0f"] = ":woman_biking:",
+ ["\U0001f6b4\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_biking_tone1:",
+ ["\U0001f6b4\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_biking_tone2:",
+ ["\U0001f6b4\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_biking_tone3:",
+ ["\U0001f6b4\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_biking_tone4:",
+ ["\U0001f6b4\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_biking_tone5:",
+ ["\u26f9\ufe0f\u200d\u2640\ufe0f"] = ":woman_bouncing_ball:",
+ ["\u26f9\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone1:",
+ ["\u26f9\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone2:",
+ ["\u26f9\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone3:",
+ ["\u26f9\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone4:",
+ ["\u26f9\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_bouncing_ball_tone5:",
+ ["\U0001f647\u200d\u2640\ufe0f"] = ":woman_bowing:",
+ ["\U0001f647\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_bowing_tone1:",
+ ["\U0001f647\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_bowing_tone2:",
+ ["\U0001f647\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_bowing_tone3:",
+ ["\U0001f647\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_bowing_tone4:",
+ ["\U0001f647\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_bowing_tone5:",
+ ["\U0001f938\u200d\u2640\ufe0f"] = ":woman_cartwheeling:",
+ ["\U0001f938\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone1:",
+ ["\U0001f938\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone2:",
+ ["\U0001f938\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone3:",
+ ["\U0001f938\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone4:",
+ ["\U0001f938\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_cartwheeling_tone5:",
+ ["\U0001f9d7\u200d\u2640\ufe0f"] = ":woman_climbing:",
+ ["\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_climbing_tone1:",
+ ["\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_climbing_tone2:",
+ ["\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_climbing_tone3:",
+ ["\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_climbing_tone4:",
+ ["\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_climbing_tone5:",
+ ["\U0001f477\u200d\u2640\ufe0f"] = ":woman_construction_worker:",
+ ["\U0001f477\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone1:",
+ ["\U0001f477\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone2:",
+ ["\U0001f477\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone3:",
+ ["\U0001f477\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone4:",
+ ["\U0001f477\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_construction_worker_tone5:",
+ ["\U0001f469\u200d\U0001f373"] = ":woman_cook:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f373"] = ":woman_cook_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f373"] = ":woman_cook_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f373"] = ":woman_cook_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f373"] = ":woman_cook_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f373"] = ":woman_cook_tone5:",
+ ["\U0001f469\u200d\U0001f9b1"] = ":woman_curly_haired:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f9b1"] = ":woman_curly_haired_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f9b1"] = ":woman_curly_haired_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f9b1"] = ":woman_curly_haired_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f9b1"] = ":woman_curly_haired_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f9b1"] = ":woman_curly_haired_tone5:",
+ ["\U0001f575\ufe0f\u200d\u2640\ufe0f"] = ":woman_detective:",
+ ["\U0001f575\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_detective_tone1:",
+ ["\U0001f575\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_detective_tone2:",
+ ["\U0001f575\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_detective_tone3:",
+ ["\U0001f575\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_detective_tone4:",
+ ["\U0001f575\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_detective_tone5:",
+ ["\U0001f9dd\u200d\u2640\ufe0f"] = ":woman_elf:",
+ ["\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_elf_tone1:",
+ ["\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_elf_tone2:",
+ ["\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_elf_tone3:",
+ ["\U0001f9dd\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_elf_tone4:",
+ ["\U0001f9dd\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_elf_tone5:",
+ ["\U0001f926\u200d\u2640\ufe0f"] = ":woman_facepalming:",
+ ["\U0001f926\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_facepalming_tone1:",
+ ["\U0001f926\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_facepalming_tone2:",
+ ["\U0001f926\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_facepalming_tone3:",
+ ["\U0001f926\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_facepalming_tone4:",
+ ["\U0001f926\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_facepalming_tone5:",
+ ["\U0001f469\u200d\U0001f3ed"] = ":woman_factory_worker:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f3ed"] = ":woman_factory_worker_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f3ed"] = ":woman_factory_worker_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f3ed"] = ":woman_factory_worker_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f3ed"] = ":woman_factory_worker_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f3ed"] = ":woman_factory_worker_tone5:",
+ ["\U0001f9da\u200d\u2640\ufe0f"] = ":woman_fairy:",
+ ["\U0001f9da\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_fairy_tone1:",
+ ["\U0001f9da\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_fairy_tone2:",
+ ["\U0001f9da\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_fairy_tone3:",
+ ["\U0001f9da\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_fairy_tone4:",
+ ["\U0001f9da\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_fairy_tone5:",
+ ["\U0001f469\u200d\U0001f33e"] = ":woman_farmer:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f33e"] = ":woman_farmer_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f33e"] = ":woman_farmer_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f33e"] = ":woman_farmer_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f33e"] = ":woman_farmer_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f33e"] = ":woman_farmer_tone5:",
+ ["\U0001f469\u200d\U0001f37c"] = ":woman_feeding_baby:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f37c"] = ":woman_feeding_baby_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f37c"] = ":woman_feeding_baby_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f37c"] = ":woman_feeding_baby_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f37c"] = ":woman_feeding_baby_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f37c"] = ":woman_feeding_baby_tone5:",
+ ["\U0001f469\u200d\U0001f692"] = ":woman_firefighter:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f692"] = ":woman_firefighter_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f692"] = ":woman_firefighter_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f692"] = ":woman_firefighter_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f692"] = ":woman_firefighter_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f692"] = ":woman_firefighter_tone5:",
+ ["\U0001f64d\u200d\u2640\ufe0f"] = ":woman_frowning:",
+ ["\U0001f64d\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_frowning_tone1:",
+ ["\U0001f64d\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_frowning_tone2:",
+ ["\U0001f64d\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_frowning_tone3:",
+ ["\U0001f64d\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_frowning_tone4:",
+ ["\U0001f64d\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_frowning_tone5:",
+ ["\U0001f9de\u200d\u2640\ufe0f"] = ":woman_genie:",
+ ["\U0001f645\u200d\u2640\ufe0f"] = ":woman_gesturing_no:",
+ ["\U0001f645\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone1:",
+ ["\U0001f645\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone2:",
+ ["\U0001f645\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone3:",
+ ["\U0001f645\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone4:",
+ ["\U0001f645\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_gesturing_no_tone5:",
+ ["\U0001f646\u200d\u2640\ufe0f"] = ":woman_gesturing_ok:",
+ ["\U0001f646\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone1:",
+ ["\U0001f646\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone2:",
+ ["\U0001f646\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone3:",
+ ["\U0001f646\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone4:",
+ ["\U0001f646\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_gesturing_ok_tone5:",
+ ["\U0001f486\u200d\u2640\ufe0f"] = ":woman_getting_face_massage:",
+ ["\U0001f486\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone1:",
+ ["\U0001f486\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone2:",
+ ["\U0001f486\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone3:",
+ ["\U0001f486\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone4:",
+ ["\U0001f486\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_getting_face_massage_tone5:",
+ ["\U0001f487\u200d\u2640\ufe0f"] = ":woman_getting_haircut:",
+ ["\U0001f487\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone1:",
+ ["\U0001f487\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone2:",
+ ["\U0001f487\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone3:",
+ ["\U0001f487\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone4:",
+ ["\U0001f487\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_getting_haircut_tone5:",
+ ["\U0001f3cc\ufe0f\u200d\u2640\ufe0f"] = ":woman_golfing:",
+ ["\U0001f3cc\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_golfing_tone1:",
+ ["\U0001f3cc\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_golfing_tone2:",
+ ["\U0001f3cc\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_golfing_tone3:",
+ ["\U0001f3cc\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_golfing_tone4:",
+ ["\U0001f3cc\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_golfing_tone5:",
+ ["\U0001f482\u200d\u2640\ufe0f"] = ":woman_guard:",
+ ["\U0001f482\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_guard_tone1:",
+ ["\U0001f482\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_guard_tone2:",
+ ["\U0001f482\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_guard_tone3:",
+ ["\U0001f482\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_guard_tone4:",
+ ["\U0001f482\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_guard_tone5:",
+ ["\U0001f469\u200d\u2695\ufe0f"] = ":woman_health_worker:",
+ ["\U0001f469\U0001f3fb\u200d\u2695\ufe0f"] = ":woman_health_worker_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\u2695\ufe0f"] = ":woman_health_worker_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\u2695\ufe0f"] = ":woman_health_worker_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\u2695\ufe0f"] = ":woman_health_worker_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\u2695\ufe0f"] = ":woman_health_worker_tone5:",
+ ["\U0001f9d8\u200d\u2640\ufe0f"] = ":woman_in_lotus_position:",
+ ["\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone1:",
+ ["\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone2:",
+ ["\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone3:",
+ ["\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone4:",
+ ["\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_in_lotus_position_tone5:",
+ ["\U0001f469\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f9bd"] = ":woman_in_manual_wheelchair_tone5:",
+ ["\U0001f469\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f9bc"] = ":woman_in_motorized_wheelchair_tone5:",
+ ["\U0001f9d6\u200d\u2640\ufe0f"] = ":woman_in_steamy_room:",
+ ["\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone1:",
+ ["\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone2:",
+ ["\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone3:",
+ ["\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone4:",
+ ["\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_in_steamy_room_tone5:",
+ ["\U0001f935\u200d\u2640\ufe0f"] = ":woman_in_tuxedo:",
+ ["\U0001f935\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone1:",
+ ["\U0001f935\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone2:",
+ ["\U0001f935\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone3:",
+ ["\U0001f935\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone4:",
+ ["\U0001f935\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_in_tuxedo_tone5:",
+ ["\U0001f469\u200d\u2696\ufe0f"] = ":woman_judge:",
+ ["\U0001f469\U0001f3fb\u200d\u2696\ufe0f"] = ":woman_judge_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\u2696\ufe0f"] = ":woman_judge_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\u2696\ufe0f"] = ":woman_judge_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\u2696\ufe0f"] = ":woman_judge_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\u2696\ufe0f"] = ":woman_judge_tone5:",
+ ["\U0001f939\u200d\u2640\ufe0f"] = ":woman_juggling:",
+ ["\U0001f939\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_juggling_tone1:",
+ ["\U0001f939\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_juggling_tone2:",
+ ["\U0001f939\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_juggling_tone3:",
+ ["\U0001f939\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_juggling_tone4:",
+ ["\U0001f939\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_juggling_tone5:",
+ ["\U0001f9ce\u200d\u2640\ufe0f"] = ":woman_kneeling:",
+ ["\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_kneeling_tone1:",
+ ["\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_kneeling_tone2:",
+ ["\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_kneeling_tone3:",
+ ["\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_kneeling_tone4:",
+ ["\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_kneeling_tone5:",
+ ["\U0001f3cb\ufe0f\u200d\u2640\ufe0f"] = ":woman_lifting_weights:",
+ ["\U0001f3cb\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone1:",
+ ["\U0001f3cb\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone2:",
+ ["\U0001f3cb\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone3:",
+ ["\U0001f3cb\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone4:",
+ ["\U0001f3cb\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_lifting_weights_tone5:",
+ ["\U0001f9d9\u200d\u2640\ufe0f"] = ":woman_mage:",
+ ["\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_mage_tone1:",
+ ["\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_mage_tone2:",
+ ["\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_mage_tone3:",
+ ["\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_mage_tone4:",
+ ["\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_mage_tone5:",
+ ["\U0001f469\u200d\U0001f527"] = ":woman_mechanic:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f527"] = ":woman_mechanic_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f527"] = ":woman_mechanic_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f527"] = ":woman_mechanic_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f527"] = ":woman_mechanic_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f527"] = ":woman_mechanic_tone5:",
+ ["\U0001f6b5\u200d\u2640\ufe0f"] = ":woman_mountain_biking:",
+ ["\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone1:",
+ ["\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone2:",
+ ["\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone3:",
+ ["\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone4:",
+ ["\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_mountain_biking_tone5:",
+ ["\U0001f469\u200d\U0001f4bc"] = ":woman_office_worker:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f4bc"] = ":woman_office_worker_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f4bc"] = ":woman_office_worker_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f4bc"] = ":woman_office_worker_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f4bc"] = ":woman_office_worker_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f4bc"] = ":woman_office_worker_tone5:",
+ ["\U0001f469\u200d\u2708\ufe0f"] = ":woman_pilot:",
+ ["\U0001f469\U0001f3fb\u200d\u2708\ufe0f"] = ":woman_pilot_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\u2708\ufe0f"] = ":woman_pilot_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\u2708\ufe0f"] = ":woman_pilot_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\u2708\ufe0f"] = ":woman_pilot_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\u2708\ufe0f"] = ":woman_pilot_tone5:",
+ ["\U0001f93e\u200d\u2640\ufe0f"] = ":woman_playing_handball:",
+ ["\U0001f93e\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone1:",
+ ["\U0001f93e\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone2:",
+ ["\U0001f93e\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone3:",
+ ["\U0001f93e\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone4:",
+ ["\U0001f93e\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_playing_handball_tone5:",
+ ["\U0001f93d\u200d\u2640\ufe0f"] = ":woman_playing_water_polo:",
+ ["\U0001f93d\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone1:",
+ ["\U0001f93d\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone2:",
+ ["\U0001f93d\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone3:",
+ ["\U0001f93d\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone4:",
+ ["\U0001f93d\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_playing_water_polo_tone5:",
+ ["\U0001f46e\u200d\u2640\ufe0f"] = ":woman_police_officer:",
+ ["\U0001f46e\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_police_officer_tone1:",
+ ["\U0001f46e\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_police_officer_tone2:",
+ ["\U0001f46e\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_police_officer_tone3:",
+ ["\U0001f46e\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_police_officer_tone4:",
+ ["\U0001f46e\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_police_officer_tone5:",
+ ["\U0001f64e\u200d\u2640\ufe0f"] = ":woman_pouting:",
+ ["\U0001f64e\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_pouting_tone1:",
+ ["\U0001f64e\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_pouting_tone2:",
+ ["\U0001f64e\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_pouting_tone3:",
+ ["\U0001f64e\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_pouting_tone4:",
+ ["\U0001f64e\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_pouting_tone5:",
+ ["\U0001f64b\u200d\u2640\ufe0f"] = ":woman_raising_hand:",
+ ["\U0001f64b\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone1:",
+ ["\U0001f64b\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone2:",
+ ["\U0001f64b\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone3:",
+ ["\U0001f64b\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone4:",
+ ["\U0001f64b\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_raising_hand_tone5:",
+ ["\U0001f469\u200d\U0001f9b0"] = ":woman_red_haired:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f9b0"] = ":woman_red_haired_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f9b0"] = ":woman_red_haired_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f9b0"] = ":woman_red_haired_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f9b0"] = ":woman_red_haired_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f9b0"] = ":woman_red_haired_tone5:",
+ ["\U0001f6a3\u200d\u2640\ufe0f"] = ":woman_rowing_boat:",
+ ["\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone1:",
+ ["\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone2:",
+ ["\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone3:",
+ ["\U0001f6a3\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone4:",
+ ["\U0001f6a3\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_rowing_boat_tone5:",
+ ["\U0001f3c3\u200d\u2640\ufe0f"] = ":woman_running:",
+ ["\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_running_tone1:",
+ ["\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_running_tone2:",
+ ["\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_running_tone3:",
+ ["\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_running_tone4:",
+ ["\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_running_tone5:",
+ ["\U0001f469\u200d\U0001f52c"] = ":woman_scientist:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f52c"] = ":woman_scientist_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f52c"] = ":woman_scientist_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f52c"] = ":woman_scientist_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f52c"] = ":woman_scientist_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f52c"] = ":woman_scientist_tone5:",
+ ["\U0001f937\u200d\u2640\ufe0f"] = ":woman_shrugging:",
+ ["\U0001f937\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_shrugging_tone1:",
+ ["\U0001f937\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_shrugging_tone2:",
+ ["\U0001f937\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_shrugging_tone3:",
+ ["\U0001f937\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_shrugging_tone4:",
+ ["\U0001f937\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_shrugging_tone5:",
+ ["\U0001f469\u200d\U0001f3a4"] = ":woman_singer:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f3a4"] = ":woman_singer_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f3a4"] = ":woman_singer_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f3a4"] = ":woman_singer_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f3a4"] = ":woman_singer_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f3a4"] = ":woman_singer_tone5:",
+ ["\U0001f9cd\u200d\u2640\ufe0f"] = ":woman_standing:",
+ ["\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_standing_tone1:",
+ ["\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_standing_tone2:",
+ ["\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_standing_tone3:",
+ ["\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_standing_tone4:",
+ ["\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_standing_tone5:",
+ ["\U0001f469\u200d\U0001f393"] = ":woman_student:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f393"] = ":woman_student_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f393"] = ":woman_student_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f393"] = ":woman_student_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f393"] = ":woman_student_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f393"] = ":woman_student_tone5:",
+ ["\U0001f9b8\u200d\u2640\ufe0f"] = ":woman_superhero:",
+ ["\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_superhero_tone1:",
+ ["\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_superhero_tone2:",
+ ["\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_superhero_tone3:",
+ ["\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_superhero_tone4:",
+ ["\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_superhero_tone5:",
+ ["\U0001f9b9\u200d\u2640\ufe0f"] = ":woman_supervillain:",
+ ["\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_supervillain_tone1:",
+ ["\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_supervillain_tone2:",
+ ["\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_supervillain_tone3:",
+ ["\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_supervillain_tone4:",
+ ["\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_supervillain_tone5:",
+ ["\U0001f3c4\u200d\u2640\ufe0f"] = ":woman_surfing:",
+ ["\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_surfing_tone1:",
+ ["\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_surfing_tone2:",
+ ["\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_surfing_tone3:",
+ ["\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_surfing_tone4:",
+ ["\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_surfing_tone5:",
+ ["\U0001f3ca\u200d\u2640\ufe0f"] = ":woman_swimming:",
+ ["\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_swimming_tone1:",
+ ["\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_swimming_tone2:",
+ ["\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_swimming_tone3:",
+ ["\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_swimming_tone4:",
+ ["\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_swimming_tone5:",
+ ["\U0001f469\u200d\U0001f3eb"] = ":woman_teacher:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f3eb"] = ":woman_teacher_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f3eb"] = ":woman_teacher_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f3eb"] = ":woman_teacher_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f3eb"] = ":woman_teacher_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f3eb"] = ":woman_teacher_tone5:",
+ ["\U0001f469\u200d\U0001f4bb"] = ":woman_technologist:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f4bb"] = ":woman_technologist_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f4bb"] = ":woman_technologist_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f4bb"] = ":woman_technologist_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f4bb"] = ":woman_technologist_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f4bb"] = ":woman_technologist_tone5:",
+ ["\U0001f481\u200d\u2640\ufe0f"] = ":woman_tipping_hand:",
+ ["\U0001f481\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone1:",
+ ["\U0001f481\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone2:",
+ ["\U0001f481\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone3:",
+ ["\U0001f481\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone4:",
+ ["\U0001f481\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_tipping_hand_tone5:",
+ ["\U0001f469\U0001f3fb"] = ":woman_tone1:",
+ ["\U0001f469\U0001f3fc"] = ":woman_tone2:",
+ ["\U0001f469\U0001f3fd"] = ":woman_tone3:",
+ ["\U0001f469\U0001f3fe"] = ":woman_tone4:",
+ ["\U0001f469\U0001f3ff"] = ":woman_tone5:",
+ ["\U0001f9db\u200d\u2640\ufe0f"] = ":woman_vampire:",
+ ["\U0001f9db\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_vampire_tone1:",
+ ["\U0001f9db\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_vampire_tone2:",
+ ["\U0001f9db\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_vampire_tone3:",
+ ["\U0001f9db\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_vampire_tone4:",
+ ["\U0001f9db\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_vampire_tone5:",
+ ["\U0001f6b6\u200d\u2640\ufe0f"] = ":woman_walking:",
+ ["\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_walking_tone1:",
+ ["\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_walking_tone2:",
+ ["\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_walking_tone3:",
+ ["\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_walking_tone4:",
+ ["\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_walking_tone5:",
+ ["\U0001f473\u200d\u2640\ufe0f"] = ":woman_wearing_turban:",
+ ["\U0001f473\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone1:",
+ ["\U0001f473\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone2:",
+ ["\U0001f473\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone3:",
+ ["\U0001f473\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone4:",
+ ["\U0001f473\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_wearing_turban_tone5:",
+ ["\U0001f469\u200d\U0001f9b3"] = ":woman_white_haired:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f9b3"] = ":woman_white_haired_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f9b3"] = ":woman_white_haired_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f9b3"] = ":woman_white_haired_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f9b3"] = ":woman_white_haired_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f9b3"] = ":woman_white_haired_tone5:",
+ ["\U0001f9d5"] = ":woman_with_headscarf:",
+ ["\U0001f9d5\U0001f3fb"] = ":woman_with_headscarf_tone1:",
+ ["\U0001f9d5\U0001f3fc"] = ":woman_with_headscarf_tone2:",
+ ["\U0001f9d5\U0001f3fd"] = ":woman_with_headscarf_tone3:",
+ ["\U0001f9d5\U0001f3fe"] = ":woman_with_headscarf_tone4:",
+ ["\U0001f9d5\U0001f3ff"] = ":woman_with_headscarf_tone5:",
+ ["\U0001f469\u200d\U0001f9af"] = ":woman_with_probing_cane:",
+ ["\U0001f469\U0001f3fb\u200d\U0001f9af"] = ":woman_with_probing_cane_tone1:",
+ ["\U0001f469\U0001f3fc\u200d\U0001f9af"] = ":woman_with_probing_cane_tone2:",
+ ["\U0001f469\U0001f3fd\u200d\U0001f9af"] = ":woman_with_probing_cane_tone3:",
+ ["\U0001f469\U0001f3fe\u200d\U0001f9af"] = ":woman_with_probing_cane_tone4:",
+ ["\U0001f469\U0001f3ff\u200d\U0001f9af"] = ":woman_with_probing_cane_tone5:",
+ ["\U0001f470\u200d\u2640\ufe0f"] = ":woman_with_veil:",
+ ["\U0001f470\U0001f3fb\u200d\u2640\ufe0f"] = ":woman_with_veil_tone1:",
+ ["\U0001f470\U0001f3fc\u200d\u2640\ufe0f"] = ":woman_with_veil_tone2:",
+ ["\U0001f470\U0001f3fd\u200d\u2640\ufe0f"] = ":woman_with_veil_tone3:",
+ ["\U0001f470\U0001f3fe\u200d\u2640\ufe0f"] = ":woman_with_veil_tone4:",
+ ["\U0001f470\U0001f3ff\u200d\u2640\ufe0f"] = ":woman_with_veil_tone5:",
+ ["\U0001f9df\u200d\u2640\ufe0f"] = ":woman_zombie:",
+ ["\U0001f45a"] = ":womans_clothes:",
+ ["\U0001f97f"] = ":womans_flat_shoe:",
+ ["\U0001f452"] = ":womans_hat:",
+ ["\U0001f46d"] = ":women_holding_hands_tone5_tone4:",
+ ["\U0001f46f\u200d\u2640\ufe0f"] = ":women_with_bunny_ears_partying:",
+ ["\U0001f93c\u200d\u2640\ufe0f"] = ":women_wrestling:",
+ ["\U0001f6ba"] = ":womens:",
+ ["\U0001fab5"] = ":wood:",
+ ["\U0001f974"] = ":woozy_face:",
+ ["\U0001fab1"] = ":worm:",
+ ["\U0001f61f"] = ":worried:",
+ ["\U0001f527"] = ":wrench:",
+ ["\u270d\ufe0f"] = ":writing_hand:",
+ ["\u270d"] = ":writing_hand:",
+ ["\u270d\U0001f3fb"] = ":writing_hand_tone1:",
+ ["\u270d\U0001f3fc"] = ":writing_hand_tone2:",
+ ["\u270d\U0001f3fd"] = ":writing_hand_tone3:",
+ ["\u270d\U0001f3fe"] = ":writing_hand_tone4:",
+ ["\u270d\U0001f3ff"] = ":writing_hand_tone5:",
+ ["\u274c"] = ":x:",
+ ["\U0001f9f6"] = ":yarn:",
+ ["\U0001f971"] = ":yawning_face:",
+ ["\U0001f7e1"] = ":yellow_circle:",
+ ["\U0001f49b"] = ":yellow_heart:",
+ ["\U0001f7e8"] = ":yellow_square:",
+ ["\U0001f4b4"] = ":yen:",
+ ["\u262f\ufe0f"] = ":yin_yang:",
+ ["\u262f"] = ":yin_yang:",
+ ["\U0001fa80"] = ":yo_yo:",
+ ["\U0001f60b"] = ":yum:",
+ ["\U0001f92a"] = ":zany_face:",
+ ["\u26a1"] = ":zap:",
+ ["\U0001f993"] = ":zebra:",
+ ["\u0030\ufe0f\u20e3"] = ":zero:",
+ ["\u0030\u20e3"] = ":zero:",
+ ["\U0001f910"] = ":zipper_mouth:",
+ ["\U0001f9df"] = ":zombie:",
+ ["\U0001f4a4"] = ":zzz:",
+ };
+ #endregion
+ }
}
}
diff --git a/DisCatSharp/Entities/Emoji/DiscordEmoji.cs b/DisCatSharp/Entities/Emoji/DiscordEmoji.cs
index efed0680c..6f839e490 100644
--- a/DisCatSharp/Entities/Emoji/DiscordEmoji.cs
+++ b/DisCatSharp/Entities/Emoji/DiscordEmoji.cs
@@ -1,375 +1,376 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord emoji.
-/// </summary>
-public partial class DiscordEmoji : SnowflakeObject, IEquatable<DiscordEmoji>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of this emoji.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets IDs the roles this emoji is enabled for.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<ulong> Roles => this._rolesLazy.Value;
-
- [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
- public List<ulong> RolesInternal;
- private readonly Lazy<IReadOnlyList<ulong>> _rolesLazy;
-
- /// <summary>
- /// Gets whether this emoji requires colons to use.
- /// </summary>
- [JsonProperty("require_colons")]
- public bool RequiresColons { get; internal set; }
-
- /// <summary>
- /// Gets whether this emoji is managed by an integration.
- /// </summary>
- [JsonProperty("managed")]
- public bool IsManaged { get; internal set; }
-
- /// <summary>
- /// Gets whether this emoji is animated.
- /// </summary>
- [JsonProperty("animated")]
- public bool IsAnimated { get; internal set; }
-
- /// <summary>
- /// Gets whether the emoji is available for use.
- /// An emoji may not be available due to loss of server boost.
- /// </summary>
- [JsonProperty("available", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsAvailable { get; internal set; }
-
- /// <summary>
- /// Gets the image URL of this emoji.
- /// </summary>
- [JsonIgnore]
- public string Url =>
- this.Id == 0
- ? throw new InvalidOperationException("Cannot get URL of unicode emojis.")
- : this.IsAnimated
- ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMOJIS}/{this.Id.ToString(CultureInfo.InvariantCulture)}.gif"
- : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMOJIS}/{this.Id.ToString(CultureInfo.InvariantCulture)}.png";
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordEmoji"/> class.
+ /// Represents a Discord emoji.
/// </summary>
- internal DiscordEmoji()
+ public partial class DiscordEmoji : SnowflakeObject, IEquatable<DiscordEmoji>
{
- this._rolesLazy = new Lazy<IReadOnlyList<ulong>>(() => new ReadOnlyCollection<ulong>(this.RolesInternal));
- }
-
- /// <summary>
- /// Gets emoji's name in non-Unicode format (eg. :thinking: instead of the Unicode representation of the emoji).
- /// </summary>
- public string GetDiscordName()
- {
- s_discordNameLookup.TryGetValue(this.Name, out var name);
-
- return name ?? $":{ this.Name }:";
- }
+ /// <summary>
+ /// Gets the name of this emoji.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets IDs the roles this emoji is enabled for.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<ulong> Roles => this._rolesLazy.Value;
+
+ [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
+ public List<ulong> RolesInternal;
+ private readonly Lazy<IReadOnlyList<ulong>> _rolesLazy;
+
+ /// <summary>
+ /// Gets whether this emoji requires colons to use.
+ /// </summary>
+ [JsonProperty("require_colons")]
+ public bool RequiresColons { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this emoji is managed by an integration.
+ /// </summary>
+ [JsonProperty("managed")]
+ public bool IsManaged { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this emoji is animated.
+ /// </summary>
+ [JsonProperty("animated")]
+ public bool IsAnimated { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the emoji is available for use.
+ /// An emoji may not be available due to loss of server boost.
+ /// </summary>
+ [JsonProperty("available", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsAvailable { get; internal set; }
+
+ /// <summary>
+ /// Gets the image URL of this emoji.
+ /// </summary>
+ [JsonIgnore]
+ public string Url =>
+ this.Id == 0
+ ? throw new InvalidOperationException("Cannot get URL of unicode emojis.")
+ : this.IsAnimated
+ ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMOJIS}/{this.Id.ToString(CultureInfo.InvariantCulture)}.gif"
+ : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMOJIS}/{this.Id.ToString(CultureInfo.InvariantCulture)}.png";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEmoji"/> class.
+ /// </summary>
+ internal DiscordEmoji()
+ {
+ this._rolesLazy = new Lazy<IReadOnlyList<ulong>>(() => new ReadOnlyCollection<ulong>(this.RolesInternal));
+ }
- /// <summary>
- /// Returns a string representation of this emoji.
- /// </summary>
- /// <returns>String representation of this emoji.</returns>
- public override string ToString()
- => this.Id != 0
- ? this.IsAnimated
- ? $"<a:{this.Name}:{this.Id.ToString(CultureInfo.InvariantCulture)}>"
- : $"<:{this.Name}:{this.Id.ToString(CultureInfo.InvariantCulture)}>"
- : this.Name;
+ /// <summary>
+ /// Gets emoji's name in non-Unicode format (eg. :thinking: instead of the Unicode representation of the emoji).
+ /// </summary>
+ public string GetDiscordName()
+ {
+ s_discordNameLookup.TryGetValue(this.Name, out var name);
- /// <summary>
- /// Checks whether this <see cref="DiscordEmoji"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordEmoji"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordEmoji);
+ return name ?? $":{ this.Name }:";
+ }
- /// <summary>
- /// Checks whether this <see cref="DiscordEmoji"/> is equal to another <see cref="DiscordEmoji"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordEmoji"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordEmoji"/> is equal to this <see cref="DiscordEmoji"/>.</returns>
- public bool Equals(DiscordEmoji e)
- => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.Name == e.Name));
+ /// <summary>
+ /// Returns a string representation of this emoji.
+ /// </summary>
+ /// <returns>String representation of this emoji.</returns>
+ public override string ToString()
+ => this.Id != 0
+ ? this.IsAnimated
+ ? $"<a:{this.Name}:{this.Id.ToString(CultureInfo.InvariantCulture)}>"
+ : $"<:{this.Name}:{this.Id.ToString(CultureInfo.InvariantCulture)}>"
+ : this.Name;
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordEmoji"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordEmoji"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordEmoji);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordEmoji"/> is equal to another <see cref="DiscordEmoji"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordEmoji"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordEmoji"/> is equal to this <see cref="DiscordEmoji"/>.</returns>
+ public bool Equals(DiscordEmoji e)
+ => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.Name == e.Name));
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordEmoji"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordEmoji"/>.</returns>
+ public override int GetHashCode()
+ {
+ var hash = 13;
+ hash = (hash * 7) + this.Id.GetHashCode();
+ hash = (hash * 7) + this.Name.GetHashCode();
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordEmoji"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordEmoji"/>.</returns>
- public override int GetHashCode()
- {
- var hash = 13;
- hash = (hash * 7) + this.Id.GetHashCode();
- hash = (hash * 7) + this.Name.GetHashCode();
+ return hash;
+ }
- return hash;
- }
+ /// <summary>
+ /// Gets the reactions string.
+ /// </summary>
+ internal string ToReactionString()
+ => this.Id != 0 ? $"{this.Name}:{this.Id.ToString(CultureInfo.InvariantCulture)}" : this.Name;
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordEmoji"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First emoji to compare.</param>
+ /// <param name="e2">Second emoji to compare.</param>
+ /// <returns>Whether the two emoji are equal.</returns>
+ public static bool operator ==(DiscordEmoji e1, DiscordEmoji e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- /// <summary>
- /// Gets the reactions string.
- /// </summary>
- internal string ToReactionString()
- => this.Id != 0 ? $"{this.Name}:{this.Id.ToString(CultureInfo.InvariantCulture)}" : this.Name;
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
- /// <summary>
- /// Gets whether the two <see cref="DiscordEmoji"/> objects are equal.
- /// </summary>
- /// <param name="e1">First emoji to compare.</param>
- /// <param name="e2">Second emoji to compare.</param>
- /// <returns>Whether the two emoji are equal.</returns>
- public static bool operator ==(DiscordEmoji e1, DiscordEmoji e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordEmoji"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First emoji to compare.</param>
+ /// <param name="e2">Second emoji to compare.</param>
+ /// <returns>Whether the two emoji are not equal.</returns>
+ public static bool operator !=(DiscordEmoji e1, DiscordEmoji e2)
+ => !(e1 == e2);
+
+ /// <summary>
+ /// Implicitly converts this emoji to its string representation.
+ /// </summary>
+ /// <param name="e1">Emoji to convert.</param>
+ public static implicit operator string(DiscordEmoji e1)
+ => e1.ToString();
+
+ /// <summary>
+ /// Checks whether specified unicode entity is a valid unicode emoji.
+ /// </summary>
+ /// <param name="unicodeEntity">Entity to check.</param>
+ /// <returns>Whether it's a valid emoji.</returns>
+ public static bool IsValidUnicode(string unicodeEntity)
+ => s_discordNameLookup.ContainsKey(unicodeEntity);
+
+ /// <summary>
+ /// Creates an emoji object from a unicode entity.
+ /// </summary>
+ /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
+ /// <param name="unicodeEntity">Unicode entity to create the object from.</param>
+ /// <returns>Create <see cref="DiscordEmoji"/> object.</returns>
+ public static DiscordEmoji FromUnicode(BaseDiscordClient client, string unicodeEntity)
+ => !IsValidUnicode(unicodeEntity)
+ ? throw new ArgumentException("Specified unicode entity is not a valid unicode emoji.", nameof(unicodeEntity))
+ : new DiscordEmoji { Name = unicodeEntity, Discord = client };
+
+ /// <summary>
+ /// Creates an emoji object from a unicode entity.
+ /// </summary>
+ /// <param name="unicodeEntity">Unicode entity to create the object from.</param>
+ /// <returns>Create <see cref="DiscordEmoji"/> object.</returns>
+ public static DiscordEmoji FromUnicode(string unicodeEntity)
+ => FromUnicode(null, unicodeEntity);
+
+ /// <summary>
+ /// Attempts to create an emoji object from a unicode entity.
+ /// </summary>
+ /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
+ /// <param name="unicodeEntity">Unicode entity to create the object from.</param>
+ /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public static bool TryFromUnicode(BaseDiscordClient client, string unicodeEntity, out DiscordEmoji emoji)
+ {
+ // this is a round-trip operation because of FE0F inconsistencies.
+ // through this, the inconsistency is normalized.
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
- }
+ emoji = null;
+ if (!s_discordNameLookup.TryGetValue(unicodeEntity, out var discordName))
+ return false;
- /// <summary>
- /// Gets whether the two <see cref="DiscordEmoji"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First emoji to compare.</param>
- /// <param name="e2">Second emoji to compare.</param>
- /// <returns>Whether the two emoji are not equal.</returns>
- public static bool operator !=(DiscordEmoji e1, DiscordEmoji e2)
- => !(e1 == e2);
+ if (!s_unicodeEmojis.TryGetValue(discordName, out unicodeEntity))
+ return false;
- /// <summary>
- /// Implicitly converts this emoji to its string representation.
- /// </summary>
- /// <param name="e1">Emoji to convert.</param>
- public static implicit operator string(DiscordEmoji e1)
- => e1.ToString();
+ emoji = new DiscordEmoji { Name = unicodeEntity, Discord = client };
+ return true;
+ }
- /// <summary>
- /// Checks whether specified unicode entity is a valid unicode emoji.
- /// </summary>
- /// <param name="unicodeEntity">Entity to check.</param>
- /// <returns>Whether it's a valid emoji.</returns>
- public static bool IsValidUnicode(string unicodeEntity)
- => s_discordNameLookup.ContainsKey(unicodeEntity);
+ /// <summary>
+ /// Attempts to create an emoji object from a unicode entity.
+ /// </summary>
+ /// <param name="unicodeEntity">Unicode entity to create the object from.</param>
+ /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public static bool TryFromUnicode(string unicodeEntity, out DiscordEmoji emoji)
+ => TryFromUnicode(null, unicodeEntity, out emoji);
+
+ /// <summary>
+ /// Creates an emoji object from a guild emote.
+ /// </summary>
+ /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
+ /// <param name="id">Id of the emote.</param>
+ /// <returns>Create <see cref="DiscordEmoji"/> object.</returns>
+ public static DiscordEmoji FromGuildEmote(BaseDiscordClient client, ulong id)
+ {
+ if (client == null)
+ throw new ArgumentNullException(nameof(client), "Client cannot be null.");
- /// <summary>
- /// Creates an emoji object from a unicode entity.
- /// </summary>
- /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
- /// <param name="unicodeEntity">Unicode entity to create the object from.</param>
- /// <returns>Create <see cref="DiscordEmoji"/> object.</returns>
- public static DiscordEmoji FromUnicode(BaseDiscordClient client, string unicodeEntity)
- => !IsValidUnicode(unicodeEntity)
- ? throw new ArgumentException("Specified unicode entity is not a valid unicode emoji.", nameof(unicodeEntity))
- : new DiscordEmoji { Name = unicodeEntity, Discord = client };
+ foreach (var guild in client.Guilds.Values)
+ {
+ if (guild.Emojis.TryGetValue(id, out var found))
+ return found;
+ }
- /// <summary>
- /// Creates an emoji object from a unicode entity.
- /// </summary>
- /// <param name="unicodeEntity">Unicode entity to create the object from.</param>
- /// <returns>Create <see cref="DiscordEmoji"/> object.</returns>
- public static DiscordEmoji FromUnicode(string unicodeEntity)
- => FromUnicode(null, unicodeEntity);
+ throw new KeyNotFoundException("Given emote was not found.");
+ }
- /// <summary>
- /// Attempts to create an emoji object from a unicode entity.
- /// </summary>
- /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
- /// <param name="unicodeEntity">Unicode entity to create the object from.</param>
- /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
- /// <returns>Whether the operation was successful.</returns>
- public static bool TryFromUnicode(BaseDiscordClient client, string unicodeEntity, out DiscordEmoji emoji)
- {
- // this is a round-trip operation because of FE0F inconsistencies.
- // through this, the inconsistency is normalized.
+ /// <summary>
+ /// Attempts to create an emoji object from a guild emote.
+ /// </summary>
+ /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
+ /// <param name="id">Id of the emote.</param>
+ /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public static bool TryFromGuildEmote(BaseDiscordClient client, ulong id, out DiscordEmoji emoji)
+ {
+ if (client == null)
+ throw new ArgumentNullException(nameof(client), "Client cannot be null.");
- emoji = null;
- if (!s_discordNameLookup.TryGetValue(unicodeEntity, out var discordName))
- return false;
+ foreach (var guild in client.Guilds.Values)
+ {
+ if (guild.Emojis.TryGetValue(id, out emoji))
+ return true;
+ }
- if (!s_unicodeEmojis.TryGetValue(discordName, out unicodeEntity))
+ emoji = null;
return false;
-
- emoji = new DiscordEmoji { Name = unicodeEntity, Discord = client };
- return true;
- }
-
- /// <summary>
- /// Attempts to create an emoji object from a unicode entity.
- /// </summary>
- /// <param name="unicodeEntity">Unicode entity to create the object from.</param>
- /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
- /// <returns>Whether the operation was successful.</returns>
- public static bool TryFromUnicode(string unicodeEntity, out DiscordEmoji emoji)
- => TryFromUnicode(null, unicodeEntity, out emoji);
-
- /// <summary>
- /// Creates an emoji object from a guild emote.
- /// </summary>
- /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
- /// <param name="id">Id of the emote.</param>
- /// <returns>Create <see cref="DiscordEmoji"/> object.</returns>
- public static DiscordEmoji FromGuildEmote(BaseDiscordClient client, ulong id)
- {
- if (client == null)
- throw new ArgumentNullException(nameof(client), "Client cannot be null.");
-
- foreach (var guild in client.Guilds.Values)
- {
- if (guild.Emojis.TryGetValue(id, out var found))
- return found;
}
- throw new KeyNotFoundException("Given emote was not found.");
- }
-
- /// <summary>
- /// Attempts to create an emoji object from a guild emote.
- /// </summary>
- /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
- /// <param name="id">Id of the emote.</param>
- /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
- /// <returns>Whether the operation was successful.</returns>
- public static bool TryFromGuildEmote(BaseDiscordClient client, ulong id, out DiscordEmoji emoji)
- {
- if (client == null)
- throw new ArgumentNullException(nameof(client), "Client cannot be null.");
-
- foreach (var guild in client.Guilds.Values)
+ /// <summary>
+ /// Creates an emoji obejct from emote name that includes colons (eg. :thinking:). This method also supports
+ /// skin tone variations (eg. :ok_hand::skin-tone-2:), standard emoticons (eg. :D), as well as guild emoji
+ /// (still specified by :name:).
+ /// </summary>
+ /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
+ /// <param name="name">Name of the emote to find, including colons (eg. :thinking:).</param>
+ /// <param name="includeGuilds">Should guild emojis be included in the search.</param>
+ /// <returns>Create <see cref="DiscordEmoji"/> object.</returns>
+ public static DiscordEmoji FromName(BaseDiscordClient client, string name, bool includeGuilds = true)
{
- if (guild.Emojis.TryGetValue(id, out emoji))
- return true;
- }
+ if (client == null)
+ throw new ArgumentNullException(nameof(client), "Client cannot be null.");
- emoji = null;
- return false;
- }
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentNullException(nameof(name), "Name cannot be empty or null.");
- /// <summary>
- /// Creates an emoji obejct from emote name that includes colons (eg. :thinking:). This method also supports
- /// skin tone variations (eg. :ok_hand::skin-tone-2:), standard emoticons (eg. :D), as well as guild emoji
- /// (still specified by :name:).
- /// </summary>
- /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
- /// <param name="name">Name of the emote to find, including colons (eg. :thinking:).</param>
- /// <param name="includeGuilds">Should guild emojis be included in the search.</param>
- /// <returns>Create <see cref="DiscordEmoji"/> object.</returns>
- public static DiscordEmoji FromName(BaseDiscordClient client, string name, bool includeGuilds = true)
- {
- if (client == null)
- throw new ArgumentNullException(nameof(client), "Client cannot be null.");
-
- if (string.IsNullOrWhiteSpace(name))
- throw new ArgumentNullException(nameof(name), "Name cannot be empty or null.");
+ if (s_unicodeEmojis.TryGetValue(name, out var unicodeEntity))
+ return new DiscordEmoji { Discord = client, Name = unicodeEntity };
- if (s_unicodeEmojis.TryGetValue(name, out var unicodeEntity))
- return new DiscordEmoji { Discord = client, Name = unicodeEntity };
+ if (includeGuilds)
+ {
+ var allEmojis = client.Guilds.Values
+ .SelectMany(xg => xg.Emojis.Values); // save cycles - don't order
- if (includeGuilds)
- {
- var allEmojis = client.Guilds.Values
- .SelectMany(xg => xg.Emojis.Values); // save cycles - don't order
+ var ek = name.AsSpan().Slice(1, name.Length - 2);
+ foreach (var emoji in allEmojis)
+ if (emoji.Name.AsSpan().SequenceEqual(ek))
+ return emoji;
+ }
- var ek = name.AsSpan().Slice(1, name.Length - 2);
- foreach (var emoji in allEmojis)
- if (emoji.Name.AsSpan().SequenceEqual(ek))
- return emoji;
+ throw new ArgumentException("Invalid emoji name specified.", nameof(name));
}
- throw new ArgumentException("Invalid emoji name specified.", nameof(name));
- }
-
- /// <summary>
- /// Attempts to create an emoji object from emote name that includes colons (eg. :thinking:). This method also
- /// supports skin tone variations (eg. :ok_hand::skin-tone-2:), standard emoticons (eg. :D), as well as guild
- /// emoji (still specified by :name:).
- /// </summary>
- /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
- /// <param name="name">Name of the emote to find, including colons (eg. :thinking:).</param>
- /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
- /// <returns>Whether the operation was successful.</returns>
- public static bool TryFromName(BaseDiscordClient client, string name, out DiscordEmoji emoji)
- => TryFromName(client, name, true, out emoji);
-
- /// <summary>
- /// Attempts to create an emoji object from emote name that includes colons (eg. :thinking:). This method also
- /// supports skin tone variations (eg. :ok_hand::skin-tone-2:), standard emoticons (eg. :D), as well as guild
- /// emoji (still specified by :name:).
- /// </summary>
- /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
- /// <param name="name">Name of the emote to find, including colons (eg. :thinking:).</param>
- /// <param name="includeGuilds">Should guild emojis be included in the search.</param>
- /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
- /// <returns>Whether the operation was successful.</returns>
- public static bool TryFromName(BaseDiscordClient client, string name, bool includeGuilds, out DiscordEmoji emoji)
- {
- if (client == null)
- throw new ArgumentNullException(nameof(client), "Client cannot be null.");
-
- if (string.IsNullOrWhiteSpace(name))
- throw new ArgumentNullException(nameof(name), "Name cannot be empty or null.");
-
- if (s_unicodeEmojis.TryGetValue(name, out var unicodeEntity))
+ /// <summary>
+ /// Attempts to create an emoji object from emote name that includes colons (eg. :thinking:). This method also
+ /// supports skin tone variations (eg. :ok_hand::skin-tone-2:), standard emoticons (eg. :D), as well as guild
+ /// emoji (still specified by :name:).
+ /// </summary>
+ /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
+ /// <param name="name">Name of the emote to find, including colons (eg. :thinking:).</param>
+ /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public static bool TryFromName(BaseDiscordClient client, string name, out DiscordEmoji emoji)
+ => TryFromName(client, name, true, out emoji);
+
+ /// <summary>
+ /// Attempts to create an emoji object from emote name that includes colons (eg. :thinking:). This method also
+ /// supports skin tone variations (eg. :ok_hand::skin-tone-2:), standard emoticons (eg. :D), as well as guild
+ /// emoji (still specified by :name:).
+ /// </summary>
+ /// <param name="client"><see cref="BaseDiscordClient"/> to attach to the object.</param>
+ /// <param name="name">Name of the emote to find, including colons (eg. :thinking:).</param>
+ /// <param name="includeGuilds">Should guild emojis be included in the search.</param>
+ /// <param name="emoji">Resulting <see cref="DiscordEmoji"/> object.</param>
+ /// <returns>Whether the operation was successful.</returns>
+ public static bool TryFromName(BaseDiscordClient client, string name, bool includeGuilds, out DiscordEmoji emoji)
{
- emoji = new DiscordEmoji { Discord = client, Name = unicodeEntity };
- return true;
- }
+ if (client == null)
+ throw new ArgumentNullException(nameof(client), "Client cannot be null.");
- if (includeGuilds)
- {
- var allEmojis = client.Guilds.Values
- .SelectMany(xg => xg.Emojis.Values); // save cycles - don't order
-
- var ek = name.AsSpan().Slice(1, name.Length - 2);
- foreach (var xemoji in allEmojis)
- if (xemoji.Name.AsSpan().SequenceEqual(ek))
- {
- emoji = xemoji;
- return true;
- }
- }
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentNullException(nameof(name), "Name cannot be empty or null.");
- emoji = null;
- return false;
+ if (s_unicodeEmojis.TryGetValue(name, out var unicodeEntity))
+ {
+ emoji = new DiscordEmoji { Discord = client, Name = unicodeEntity };
+ return true;
+ }
+
+ if (includeGuilds)
+ {
+ var allEmojis = client.Guilds.Values
+ .SelectMany(xg => xg.Emojis.Values); // save cycles - don't order
+
+ var ek = name.AsSpan().Slice(1, name.Length - 2);
+ foreach (var xemoji in allEmojis)
+ if (xemoji.Name.AsSpan().SequenceEqual(ek))
+ {
+ emoji = xemoji;
+ return true;
+ }
+ }
+
+ emoji = null;
+ return false;
+ }
}
}
diff --git a/DisCatSharp/Entities/Emoji/DiscordUnicodeEmoji.cs b/DisCatSharp/Entities/Emoji/DiscordUnicodeEmoji.cs
index f68892127..9985a43cd 100644
--- a/DisCatSharp/Entities/Emoji/DiscordUnicodeEmoji.cs
+++ b/DisCatSharp/Entities/Emoji/DiscordUnicodeEmoji.cs
@@ -1,2147 +1,2148 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord unicode emoji.
-/// </summary>
-public class DiscordUnicodeEmoji
+namespace DisCatSharp.Entities
{
+ /// <summary>
+ /// Represents a discord unicode emoji.
+ /// </summary>
+ public class DiscordUnicodeEmoji
+ {
#pragma warning disable ConstFieldDocumentationHeader
#pragma warning disable IDE1006
- public const string _1 = "\U0001f44e";
- public const string _100 = "\U0001f4af";
- public const string _1234 = "\U0001f522";
- public const string _1SkinTone1 = "\U0001f44e\U0001f3fb";
- public const string _1SkinTone2 = "\U0001f44e\U0001f3fc";
- public const string _1SkinTone3 = "\U0001f44e\U0001f3fd";
- public const string _1SkinTone4 = "\U0001f44e\U0001f3fe";
- public const string _1SkinTone5 = "\U0001f44e\U0001f3ff";
- public const string _8ball = "\U0001f3b1";
- public const string A = "\U0001f170";
- public const string AB = "\U0001f18e";
- public const string ABC = "\U0001f524";
- public const string ABCD = "\U0001f521";
- public const string ACCEPT = "\U0001f251";
- public const string ADMISSION_TICKETS = "\U0001f39f";
- public const string AERIAL_TRAMWAY = "\U0001f6a1";
- public const string AIRPLANE = "\U00002708";
- public const string AIRPLANE_ARRIVING = "\U0001f6ec";
- public const string AIRPLANE_DEPARTURE = "\U0001f6eb";
- public const string AIRPLANE_SMALL = "\U0001f6e9";
- public const string ALARM_CLOCK = "\U000023f0";
- public const string ALEMBIC = "\U00002697";
- public const string ALIEN = "\U0001f47d";
- public const string AMBULANCE = "\U0001f691";
- public const string AMPHORA = "\U0001f3fa";
- public const string ANCHOR = "\U00002693";
- public const string ANGEL = "\U0001f47c";
- public const string ANGEL_SKIN_TONE1 = "\U0001f47c\U0001f3fb";
- public const string ANGEL_SKIN_TONE2 = "\U0001f47c\U0001f3fc";
- public const string ANGEL_SKIN_TONE3 = "\U0001f47c\U0001f3fd";
- public const string ANGEL_SKIN_TONE4 = "\U0001f47c\U0001f3fe";
- public const string ANGEL_SKIN_TONE5 = "\U0001f47c\U0001f3ff";
- public const string ANGER = "\U0001f4a2";
- public const string ANGER_RIGHT = "\U0001f5ef";
- public const string ANGRY = "\U0001f620";
- public const string ANGUISHED = "\U0001f627";
- public const string ANT = "\U0001f41c";
- public const string APPLE = "\U0001f34e";
- public const string AQUARIUS = "\U00002652";
- public const string ARCHERY = "\U0001f3f9";
- public const string ARIES = "\U00002648";
- public const string ARROW_BACKWARD = "\U000025c0";
- public const string ARROW_DOUBLE_DOWN = "\U000023ec";
- public const string ARROW_DOUBLE_UP = "\U000023eb";
- public const string ARROW_DOWN = "\U00002b07";
- public const string ARROW_DOWN_SMALL = "\U0001f53d";
- public const string ARROW_FORWARD = "\U000025b6";
- public const string ARROW_HEADING_DOWN = "\U00002935";
- public const string ARROW_HEADING_UP = "\U00002934";
- public const string ARROW_LEFT = "\U00002b05";
- public const string ARROW_LOWER_LEFT = "\U00002199";
- public const string ARROW_LOWER_RIGHT = "\U00002198";
- public const string ARROW_RIGHT = "\U000027a1";
- public const string ARROW_RIGHT_HOOK = "\U000021aa";
- public const string ARROWS_CLOCKWISE = "\U0001f503";
- public const string ARROWS_COUNTERCLOCKWISE = "\U0001f504";
- public const string ARROW_UP = "\U00002b06";
- public const string ARROW_UP_DOWN = "\U00002195";
- public const string ARROW_UPPER_LEFT = "\U00002196";
- public const string ARROW_UPPER_RIGHT = "\U00002197";
- public const string ARROW_UP_SMALL = "\U0001f53c";
- public const string ART = "\U0001f3a8";
- public const string ARTICULATED_LORRY = "\U0001f69b";
- public const string ASTERISK = "\U0000002a\U000020e3";
- public const string ASTONISHED = "\U0001f632";
- public const string ATHLETIC_SHOE = "\U0001f45f";
- public const string ATM = "\U0001f3e7";
- public const string ATOM = "\U0000269b";
- public const string ATOM_SYMBOL = "\U0000269b";
- public const string AVOCADO = "\U0001f951";
- public const string B = "\U0001f171";
- public const string BABY = "\U0001f476";
- public const string BABY_BOTTLE = "\U0001f37c";
- public const string BABY_CHICK = "\U0001f424";
- public const string BABY_SKIN_TONE1 = "\U0001f476\U0001f3fb";
- public const string BABY_SKIN_TONE2 = "\U0001f476\U0001f3fc";
- public const string BABY_SKIN_TONE3 = "\U0001f476\U0001f3fd";
- public const string BABY_SKIN_TONE4 = "\U0001f476\U0001f3fe";
- public const string BABY_SKIN_TONE5 = "\U0001f476\U0001f3ff";
- public const string BABY_SYMBOL = "\U0001f6bc";
- public const string BACK = "\U0001f519";
- public const string BACK_OF_HAND = "\U0001f91a";
- public const string BACK_OF_HAND_SKIN_TONE1 = "\U0001f91a\U0001f3fb";
- public const string BACK_OF_HAND_SKIN_TONE2 = "\U0001f91a\U0001f3fc";
- public const string BACK_OF_HAND_SKIN_TONE3 = "\U0001f91a\U0001f3fd";
- public const string BACK_OF_HAND_SKIN_TONE4 = "\U0001f91a\U0001f3fe";
- public const string BACK_OF_HAND_SKIN_TONE5 = "\U0001f91a\U0001f3ff";
- public const string BACON = "\U0001f953";
- public const string BADMINTON = "\U0001f3f8";
- public const string BAGGAGE_CLAIM = "\U0001f6c4";
- public const string BAGUETTE_BREAD = "\U0001f956";
- public const string BALLOON = "\U0001f388";
- public const string BALLOT_BOX = "\U0001f5f3";
- public const string BALLOT_BOX_WITH_BALLOT = "\U0001f5f3";
- public const string BALLOT_BOX_WITH_CHECK = "\U00002611";
- public const string BAMBOO = "\U0001f38d";
- public const string BANANA = "\U0001f34c";
- public const string BANGBANG = "\U0000203c";
- public const string BANK = "\U0001f3e6";
- public const string BARBER = "\U0001f488";
- public const string BAR_CHART = "\U0001f4ca";
- public const string BASEBALL = "\U000026be";
- public const string BASKETBALL = "\U0001f3c0";
- public const string BASKETBALL_PLAYER = "\U000026f9";
- public const string BASKETBALL_PLAYER_SKIN_TONE1 = "\U000026f9\U0001f3fb";
- public const string BASKETBALL_PLAYER_SKIN_TONE2 = "\U000026f9\U0001f3fc";
- public const string BASKETBALL_PLAYER_SKIN_TONE3 = "\U000026f9\U0001f3fd";
- public const string BASKETBALL_PLAYER_SKIN_TONE4 = "\U000026f9\U0001f3fe";
- public const string BASKETBALL_PLAYER_SKIN_TONE5 = "\U000026f9\U0001f3ff";
- public const string BAT = "\U0001f987";
- public const string BATH = "\U0001f6c0";
- public const string BATH_SKIN_TONE1 = "\U0001f6c0\U0001f3fb";
- public const string BATH_SKIN_TONE2 = "\U0001f6c0\U0001f3fc";
- public const string BATH_SKIN_TONE3 = "\U0001f6c0\U0001f3fd";
- public const string BATH_SKIN_TONE4 = "\U0001f6c0\U0001f3fe";
- public const string BATH_SKIN_TONE5 = "\U0001f6c0\U0001f3ff";
- public const string BATHTUB = "\U0001f6c1";
- public const string BATTERY = "\U0001f50b";
- public const string BEACH = "\U0001f3d6";
- public const string BEACH_UMBRELLA = "\U000026f1";
- public const string BEACH_WITH_UMBRELLA = "\U0001f3d6";
- public const string BEAR = "\U0001f43b";
- public const string BED = "\U0001f6cf";
- public const string BEE = "\U0001f41d";
- public const string BEER = "\U0001f37a";
- public const string BEERS = "\U0001f37b";
- public const string BEETLE = "\U0001f41e";
- public const string BEGINNER = "\U0001f530";
- public const string BELL = "\U0001f514";
- public const string BELLHOP = "\U0001f6ce";
- public const string BELLHOP_BELL = "\U0001f6ce";
- public const string BENTO = "\U0001f371";
- public const string BICYCLIST = "\U0001f6b4";
- public const string BICYCLIST_SKIN_TONE1 = "\U0001f6b4\U0001f3fb";
- public const string BICYCLIST_SKIN_TONE2 = "\U0001f6b4\U0001f3fc";
- public const string BICYCLIST_SKIN_TONE3 = "\U0001f6b4\U0001f3fd";
- public const string BICYCLIST_SKIN_TONE4 = "\U0001f6b4\U0001f3fe";
- public const string BICYCLIST_SKIN_TONE5 = "\U0001f6b4\U0001f3ff";
- public const string BIKE = "\U0001f6b2";
- public const string BIKINI = "\U0001f459";
- public const string BIOHAZARD = "\U00002623";
- public const string BIOHAZARD_SIGN = "\U00002623";
- public const string BIRD = "\U0001f426";
- public const string BIRTHDAY = "\U0001f382";
- public const string BLACK_CIRCLE = "\U000026ab";
- public const string BLACK_HEART = "\U0001f5a4";
- public const string BLACK_JOKER = "\U0001f0cf";
- public const string BLACK_LARGE_SQUARE = "\U00002b1b";
- public const string BLACK_MEDIUM_SMALL_SQUARE = "\U000025fe";
- public const string BLACK_MEDIUM_SQUARE = "\U000025fc";
- public const string BLACK_NIB = "\U00002712";
- public const string BLACK_SMALL_SQUARE = "\U000025aa";
- public const string BLACK_SQUARE_BUTTON = "\U0001f532";
- public const string BLOSSOM = "\U0001f33c";
- public const string BLOWFISH = "\U0001f421";
- public const string BLUE_BOOK = "\U0001f4d8";
- public const string BLUE_CAR = "\U0001f699";
- public const string BLUE_HEART = "\U0001f499";
- public const string BLUSH = "\U0001f60a";
- public const string BOAR = "\U0001f417";
- public const string BOMB = "\U0001f4a3";
- public const string BOOK = "\U0001f4d6";
- public const string BOOKMARK = "\U0001f516";
- public const string BOOKMARK_TABS = "\U0001f4d1";
- public const string BOOKS = "\U0001f4da";
- public const string BOOM = "\U0001f4a5";
- public const string BOOT = "\U0001f462";
- public const string BOTTLE_WITH_POPPING_CORK = "\U0001f37e";
- public const string BOUQUET = "\U0001f490";
- public const string BOW = "\U0001f647";
- public const string BOW_AND_ARROW = "\U0001f3f9";
- public const string BOWLING = "\U0001f3b3";
- public const string BOW_SKIN_TONE1 = "\U0001f647\U0001f3fb";
- public const string BOW_SKIN_TONE2 = "\U0001f647\U0001f3fc";
- public const string BOW_SKIN_TONE3 = "\U0001f647\U0001f3fd";
- public const string BOW_SKIN_TONE4 = "\U0001f647\U0001f3fe";
- public const string BOW_SKIN_TONE5 = "\U0001f647\U0001f3ff";
- public const string BOXING_GLOVE = "\U0001f94a";
- public const string BOXING_GLOVES = "\U0001f94a";
- public const string BOY = "\U0001f466";
- public const string BOY_SKIN_TONE1 = "\U0001f466\U0001f3fb";
- public const string BOY_SKIN_TONE2 = "\U0001f466\U0001f3fc";
- public const string BOY_SKIN_TONE3 = "\U0001f466\U0001f3fd";
- public const string BOY_SKIN_TONE4 = "\U0001f466\U0001f3fe";
- public const string BOY_SKIN_TONE5 = "\U0001f466\U0001f3ff";
- public const string BREAD = "\U0001f35e";
- public const string BRIDE_WITH_VEIL = "\U0001f470";
- public const string BRIDE_WITH_VEIL_SKIN_TONE1 = "\U0001f470\U0001f3fb";
- public const string BRIDE_WITH_VEIL_SKIN_TONE2 = "\U0001f470\U0001f3fc";
- public const string BRIDE_WITH_VEIL_SKIN_TONE3 = "\U0001f470\U0001f3fd";
- public const string BRIDE_WITH_VEIL_SKIN_TONE4 = "\U0001f470\U0001f3fe";
- public const string BRIDE_WITH_VEIL_SKIN_TONE5 = "\U0001f470\U0001f3ff";
- public const string BRIDGE_AT_NIGHT = "\U0001f309";
- public const string BRIEFCASE = "\U0001f4bc";
- public const string BROKEN_HEART = "\U0001f494";
- public const string BUG = "\U0001f41b";
- public const string BUILDING_CONSTRUCTION = "\U0001f3d7";
- public const string BULB = "\U0001f4a1";
- public const string BULLETTRAIN_FRONT = "\U0001f685";
- public const string BULLETTRAIN_SIDE = "\U0001f684";
- public const string BURRITO = "\U0001f32f";
- public const string BUS = "\U0001f68c";
- public const string BUSSTOP = "\U0001f68f";
- public const string BUST_IN_SILHOUETTE = "\U0001f464";
- public const string BUSTS_IN_SILHOUETTE = "\U0001f465";
- public const string BUTTERFLY = "\U0001f98b";
- public const string CACTUS = "\U0001f335";
- public const string CAKE = "\U0001f370";
- public const string CALENDAR = "\U0001f4c6";
- public const string CALENDAR_SPIRAL = "\U0001f5d3";
- public const string CALLING = "\U0001f4f2";
- public const string CALL_ME = "\U0001f919";
- public const string CALL_ME_HAND = "\U0001f919";
- public const string CALL_ME_HAND_SKIN_TONE1 = "\U0001f919\U0001f3fb";
- public const string CALL_ME_HAND_SKIN_TONE2 = "\U0001f919\U0001f3fc";
- public const string CALL_ME_HAND_SKIN_TONE3 = "\U0001f919\U0001f3fd";
- public const string CALL_ME_HAND_SKIN_TONE4 = "\U0001f919\U0001f3fe";
- public const string CALL_ME_HAND_SKIN_TONE5 = "\U0001f919\U0001f3ff";
- public const string CALL_ME_SKIN_TONE1 = "\U0001f919\U0001f3fb";
- public const string CALL_ME_SKIN_TONE2 = "\U0001f919\U0001f3fc";
- public const string CALL_ME_SKIN_TONE3 = "\U0001f919\U0001f3fd";
- public const string CALL_ME_SKIN_TONE4 = "\U0001f919\U0001f3fe";
- public const string CALL_ME_SKIN_TONE5 = "\U0001f919\U0001f3ff";
- public const string CAMEL = "\U0001f42b";
- public const string CAMERA = "\U0001f4f7";
- public const string CAMERA_WITH_FLASH = "\U0001f4f8";
- public const string CAMPING = "\U0001f3d5";
- public const string CANCER = "\U0000264b";
- public const string CANDLE = "\U0001f56f";
- public const string CANDY = "\U0001f36c";
- public const string CANOE = "\U0001f6f6";
- public const string CAPITAL_ABCD = "\U0001f520";
- public const string CAPRICORN = "\U00002651";
- public const string CARD_BOX = "\U0001f5c3";
- public const string CARD_FILE_BOX = "\U0001f5c3";
- public const string CARD_INDEX = "\U0001f4c7";
- public const string CARD_INDEX_DIVIDERS = "\U0001f5c2";
- public const string CAROUSEL_HORSE = "\U0001f3a0";
- public const string CARROT = "\U0001f955";
- public const string CARTWHEEL = "\U0001f938";
- public const string CARTWHEEL_SKIN_TONE1 = "\U0001f938\U0001f3fb";
- public const string CARTWHEEL_SKIN_TONE2 = "\U0001f938\U0001f3fc";
- public const string CARTWHEEL_SKIN_TONE3 = "\U0001f938\U0001f3fd";
- public const string CARTWHEEL_SKIN_TONE4 = "\U0001f938\U0001f3fe";
- public const string CARTWHEEL_SKIN_TONE5 = "\U0001f938\U0001f3ff";
- public const string CAT = "\U0001f431";
- public const string CAT2 = "\U0001f408";
- public const string CD = "\U0001f4bf";
- public const string CHAINS = "\U000026d3";
- public const string CHAMPAGNE = "\U0001f37e";
- public const string CHAMPAGNE_GLASS = "\U0001f942";
- public const string CHART = "\U0001f4b9";
- public const string CHART_WITH_DOWNWARDS_TREND = "\U0001f4c9";
- public const string CHART_WITH_UPWARDS_TREND = "\U0001f4c8";
- public const string CHECKERED_FLAG = "\U0001f3c1";
- public const string CHEESE = "\U0001f9c0";
- public const string CHEESE_WEDGE = "\U0001f9c0";
- public const string CHERRIES = "\U0001f352";
- public const string CHERRY_BLOSSOM = "\U0001f338";
- public const string CHESTNUT = "\U0001f330";
- public const string CHICKEN = "\U0001f414";
- public const string CHILDREN_CROSSING = "\U0001f6b8";
- public const string CHIPMUNK = "\U0001f43f";
- public const string CHOCOLATE_BAR = "\U0001f36b";
- public const string CHRISTMAS_TREE = "\U0001f384";
- public const string CHURCH = "\U000026ea";
- public const string CINEMA = "\U0001f3a6";
- public const string CIRCUS_TENT = "\U0001f3aa";
- public const string CITY_DUSK = "\U0001f306";
- public const string CITYSCAPE = "\U0001f3d9";
- public const string CITY_SUNRISE = "\U0001f307";
- public const string CITY_SUNSET = "\U0001f307";
- public const string CL = "\U0001f191";
- public const string CLAP = "\U0001f44f";
- public const string CLAPPER = "\U0001f3ac";
- public const string CLAP_SKIN_TONE1 = "\U0001f44f\U0001f3fb";
- public const string CLAP_SKIN_TONE2 = "\U0001f44f\U0001f3fc";
- public const string CLAP_SKIN_TONE3 = "\U0001f44f\U0001f3fd";
- public const string CLAP_SKIN_TONE4 = "\U0001f44f\U0001f3fe";
- public const string CLAP_SKIN_TONE5 = "\U0001f44f\U0001f3ff";
- public const string CLASSICAL_BUILDING = "\U0001f3db";
- public const string CLINKING_GLASS = "\U0001f942";
- public const string CLIPBOARD = "\U0001f4cb";
- public const string CLOCK = "\U0001f570";
- public const string CLOCK1 = "\U0001f550";
- public const string CLOCK10 = "\U0001f559";
- public const string CLOCK1030 = "\U0001f565";
- public const string CLOCK11 = "\U0001f55a";
- public const string CLOCK1130 = "\U0001f566";
- public const string CLOCK12 = "\U0001f55b";
- public const string CLOCK1230 = "\U0001f567";
- public const string CLOCK130 = "\U0001f55c";
- public const string CLOCK2 = "\U0001f551";
- public const string CLOCK230 = "\U0001f55d";
- public const string CLOCK3 = "\U0001f552";
- public const string CLOCK330 = "\U0001f55e";
- public const string CLOCK4 = "\U0001f553";
- public const string CLOCK430 = "\U0001f55f";
- public const string CLOCK5 = "\U0001f554";
- public const string CLOCK530 = "\U0001f560";
- public const string CLOCK6 = "\U0001f555";
- public const string CLOCK630 = "\U0001f561";
- public const string CLOCK7 = "\U0001f556";
- public const string CLOCK730 = "\U0001f562";
- public const string CLOCK8 = "\U0001f557";
- public const string CLOCK830 = "\U0001f563";
- public const string CLOCK9 = "\U0001f558";
- public const string CLOCK930 = "\U0001f564";
- public const string CLOSED_BOOK = "\U0001f4d5";
- public const string CLOSED_LOCK_WITH_KEY = "\U0001f510";
- public const string CLOSED_UMBRELLA = "\U0001f302";
- public const string CLOUD = "\U00002601";
- public const string CLOUD_LIGHTNING = "\U0001f329";
- public const string CLOUD_RAIN = "\U0001f327";
- public const string CLOUD_SNOW = "\U0001f328";
- public const string CLOUD_TORNADO = "\U0001f32a";
- public const string CLOUD_WITH_LIGHTNING = "\U0001f329";
- public const string CLOUD_WITH_RAIN = "\U0001f327";
- public const string CLOUD_WITH_SNOW = "\U0001f328";
- public const string CLOUD_WITH_TORNADO = "\U0001f32a";
- public const string CLOWN = "\U0001f921";
- public const string CLOWN_FACE = "\U0001f921";
- public const string CLUBS = "\U00002663";
- public const string COCKTAIL = "\U0001f378";
- public const string COFFEE = "\U00002615";
- public const string COFFIN = "\U000026b0";
- public const string COLD_SWEAT = "\U0001f630";
- public const string COMET = "\U00002604";
- public const string COMPRESSION = "\U0001f5dc";
- public const string COMPUTER = "\U0001f4bb";
- public const string CONFETTI_BALL = "\U0001f38a";
- public const string CONFOUNDED = "\U0001f616";
- public const string CONFUSED = "\U0001f615";
- public const string CONGRATULATIONS = "\U00003297";
- public const string CONSTRUCTION = "\U0001f6a7";
- public const string CONSTRUCTION_SITE = "\U0001f3d7";
- public const string CONSTRUCTION_WORKER = "\U0001f477";
- public const string CONSTRUCTION_WORKER_SKIN_TONE1 = "\U0001f477\U0001f3fb";
- public const string CONSTRUCTION_WORKER_SKIN_TONE2 = "\U0001f477\U0001f3fc";
- public const string CONSTRUCTION_WORKER_SKIN_TONE3 = "\U0001f477\U0001f3fd";
- public const string CONSTRUCTION_WORKER_SKIN_TONE4 = "\U0001f477\U0001f3fe";
- public const string CONSTRUCTION_WORKER_SKIN_TONE5 = "\U0001f477\U0001f3ff";
- public const string CONTROL_KNOBS = "\U0001f39b";
- public const string CONVENIENCE_STORE = "\U0001f3ea";
- public const string COOKIE = "\U0001f36a";
- public const string COOKING = "\U0001f373";
- public const string COOL = "\U0001f192";
- public const string COP = "\U0001f46e";
- public const string COP_SKIN_TONE1 = "\U0001f46e\U0001f3fb";
- public const string COP_SKIN_TONE2 = "\U0001f46e\U0001f3fc";
- public const string COP_SKIN_TONE3 = "\U0001f46e\U0001f3fd";
- public const string COP_SKIN_TONE4 = "\U0001f46e\U0001f3fe";
- public const string COP_SKIN_TONE5 = "\U0001f46e\U0001f3ff";
- public const string COPYRIGHT = "\U000000a9";
- public const string CORN = "\U0001f33d";
- public const string COUCH = "\U0001f6cb";
- public const string COUCH_AND_LAMP = "\U0001f6cb";
- public const string COUPLE = "\U0001f46b";
- public const string COUPLEKISS = "\U0001f48f";
- public const string COUPLEKISS_MM = "\U0001f468\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f48b\U0000200d\U0001f468";
- public const string COUPLEKISS_WW = "\U0001f469\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f48b\U0000200d\U0001f469";
- public const string COUPLE_MM = "\U0001f468\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f468";
- public const string COUPLE_WITH_HEART = "\U0001f491";
- public const string COUPLE_WITH_HEART_MM = "\U0001f468\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f468";
- public const string COUPLE_WITH_HEART_WW = "\U0001f469\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f469";
- public const string COUPLE_WW = "\U0001f469\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f469";
- public const string COW = "\U0001f42e";
- public const string COW2 = "\U0001f404";
- public const string COWBOY = "\U0001f920";
- public const string CRAB = "\U0001f980";
- public const string CRAYON = "\U0001f58d";
- public const string CREDIT_CARD = "\U0001f4b3";
- public const string CRESCENT_MOON = "\U0001f319";
- public const string CRICKET = "\U0001f3cf";
- public const string CRICKET_BAT_BALL = "\U0001f3cf";
- public const string CROCODILE = "\U0001f40a";
- public const string CROISSANT = "\U0001f950";
- public const string CROSS = "\U0000271d";
- public const string CROSSED_FLAGS = "\U0001f38c";
- public const string CROSSED_SWORDS = "\U00002694";
- public const string CROWN = "\U0001f451";
- public const string CRUISE_SHIP = "\U0001f6f3";
- public const string CRY = "\U0001f622";
- public const string CRYING_CAT_FACE = "\U0001f63f";
- public const string CRYSTAL_BALL = "\U0001f52e";
- public const string CUCUMBER = "\U0001f952";
- public const string CUPID = "\U0001f498";
- public const string CURLY_LOOP = "\U000027b0";
- public const string CURRENCY_EXCHANGE = "\U0001f4b1";
- public const string CURRY = "\U0001f35b";
- public const string CUSTARD = "\U0001f36e";
- public const string CUSTOMS = "\U0001f6c3";
- public const string CYCLONE = "\U0001f300";
- public const string DAGGER = "\U0001f5e1";
- public const string DAGGER_KNIFE = "\U0001f5e1";
- public const string DANCER = "\U0001f483";
- public const string DANCERS = "\U0001f46f";
- public const string DANCER_SKIN_TONE1 = "\U0001f483\U0001f3fb";
- public const string DANCER_SKIN_TONE2 = "\U0001f483\U0001f3fc";
- public const string DANCER_SKIN_TONE3 = "\U0001f483\U0001f3fd";
- public const string DANCER_SKIN_TONE4 = "\U0001f483\U0001f3fe";
- public const string DANCER_SKIN_TONE5 = "\U0001f483\U0001f3ff";
- public const string DANGO = "\U0001f361";
- public const string DARK_SUNGLASSES = "\U0001f576";
- public const string DART = "\U0001f3af";
- public const string DASH = "\U0001f4a8";
- public const string DATE = "\U0001f4c5";
- public const string DECIDUOUS_TREE = "\U0001f333";
- public const string DEER = "\U0001f98c";
- public const string DEPARTMENT_STORE = "\U0001f3ec";
- public const string DERELICT_HOUSE_BUILDING = "\U0001f3da";
- public const string DESERT = "\U0001f3dc";
- public const string DESERT_ISLAND = "\U0001f3dd";
- public const string DESKTOP = "\U0001f5a5";
- public const string DESKTOP_COMPUTER = "\U0001f5a5";
- public const string DIAMONDS = "\U00002666";
- public const string DIAMOND_SHAPE_WITH_A_DOT_INSIDE = "\U0001f4a0";
- public const string DISAPPOINTED = "\U0001f61e";
- public const string DISAPPOINTED_RELIEVED = "\U0001f625";
- public const string DIVIDERS = "\U0001f5c2";
- public const string DIZZY = "\U0001f4ab";
- public const string DIZZY_FACE = "\U0001f635";
- public const string DOG = "\U0001f436";
- public const string DOG2 = "\U0001f415";
- public const string DOLLAR = "\U0001f4b5";
- public const string DOLLS = "\U0001f38e";
- public const string DOLPHIN = "\U0001f42c";
- public const string DO_NOT_LITTER = "\U0001f6af";
- public const string DOOR = "\U0001f6aa";
- public const string DOUBLE_VERTICAL_BAR = "\U000023f8";
- public const string DOUGHNUT = "\U0001f369";
- public const string DOVE = "\U0001f54a";
- public const string DOVE_OF_PEACE = "\U0001f54a";
- public const string DRAGON = "\U0001f409";
- public const string DRAGON_FACE = "\U0001f432";
- public const string DRESS = "\U0001f457";
- public const string DROMEDARY_CAMEL = "\U0001f42a";
- public const string DROOL = "\U0001f924";
- public const string DROOLING_FACE = "\U0001f924";
- public const string DROPLET = "\U0001f4a7";
- public const string DRUM = "\U0001f941";
- public const string DRUM_WITH_DRUMSTICKS = "\U0001f941";
- public const string DUCK = "\U0001f986";
- public const string DVD = "\U0001f4c0";
- public const string EAGLE = "\U0001f985";
- public const string EAR = "\U0001f442";
- public const string EAR_OF_RICE = "\U0001f33e";
- public const string EAR_SKIN_TONE1 = "\U0001f442\U0001f3fb";
- public const string EAR_SKIN_TONE2 = "\U0001f442\U0001f3fc";
- public const string EAR_SKIN_TONE3 = "\U0001f442\U0001f3fd";
- public const string EAR_SKIN_TONE4 = "\U0001f442\U0001f3fe";
- public const string EAR_SKIN_TONE5 = "\U0001f442\U0001f3ff";
- public const string EARTH_AFRICA = "\U0001f30d";
- public const string EARTH_AMERICAS = "\U0001f30e";
- public const string EARTH_ASIA = "\U0001f30f";
- public const string EGG = "\U0001f95a";
- public const string EGGPLANT = "\U0001f346";
- public const string EIGHT = "\U00000038\U000020e3";
- public const string EIGHT_POINTED_BLACK_STAR = "\U00002734";
- public const string EIGHT_SPOKED_ASTERISK = "\U00002733";
- public const string EJECT = "\U000023cf";
- public const string EJECT_SYMBOL = "\U000023cf";
- public const string ELECTRIC_PLUG = "\U0001f50c";
- public const string ELEPHANT = "\U0001f418";
- public const string E_MAIL = "\U0001f4e7";
- public const string EMAIL = "\U0001f4e7";
- public const string END = "\U0001f51a";
- public const string ENVELOPE = "\U00002709";
- public const string ENVELOPE_WITH_ARROW = "\U0001f4e9";
- public const string EURO = "\U0001f4b6";
- public const string EUROPEAN_CASTLE = "\U0001f3f0";
- public const string EUROPEAN_POST_OFFICE = "\U0001f3e4";
- public const string EVERGREEN_TREE = "\U0001f332";
- public const string EXCLAMATION = "\U00002757";
- public const string EXPECTING_WOMAN = "\U0001f930";
- public const string EXPECTING_WOMAN_SKIN_TONE1 = "\U0001f930\U0001f3fb";
- public const string EXPECTING_WOMAN_SKIN_TONE2 = "\U0001f930\U0001f3fc";
- public const string EXPECTING_WOMAN_SKIN_TONE3 = "\U0001f930\U0001f3fd";
- public const string EXPECTING_WOMAN_SKIN_TONE4 = "\U0001f930\U0001f3fe";
- public const string EXPECTING_WOMAN_SKIN_TONE5 = "\U0001f930\U0001f3ff";
- public const string EXPRESSIONLESS = "\U0001f611";
- public const string EYE = "\U0001f441";
- public const string EYEGLASSES = "\U0001f453";
- public const string EYE_IN_SPEECH_BUBBLE = "\U0001f441\U0000200d\U0001f5e8";
- public const string EYES = "\U0001f440";
- public const string FACE_PALM = "\U0001f926";
- public const string FACEPALM = "\U0001f926";
- public const string FACE_PALM_SKIN_TONE1 = "\U0001f926\U0001f3fb";
- public const string FACEPALM_SKIN_TONE1 = "\U0001f926\U0001f3fb";
- public const string FACE_PALM_SKIN_TONE2 = "\U0001f926\U0001f3fc";
- public const string FACEPALM_SKIN_TONE2 = "\U0001f926\U0001f3fc";
- public const string FACE_PALM_SKIN_TONE3 = "\U0001f926\U0001f3fd";
- public const string FACEPALM_SKIN_TONE3 = "\U0001f926\U0001f3fd";
- public const string FACE_PALM_SKIN_TONE4 = "\U0001f926\U0001f3fe";
- public const string FACEPALM_SKIN_TONE4 = "\U0001f926\U0001f3fe";
- public const string FACE_PALM_SKIN_TONE5 = "\U0001f926\U0001f3ff";
- public const string FACEPALM_SKIN_TONE5 = "\U0001f926\U0001f3ff";
- public const string FACE_WITH_COWBOY_HAT = "\U0001f920";
- public const string FACE_WITH_HEAD_BANDAGE = "\U0001f915";
- public const string FACE_WITH_ROLLING_EYES = "\U0001f644";
- public const string FACE_WITH_THERMOMETER = "\U0001f912";
- public const string FACTORY = "\U0001f3ed";
- public const string FALLEN_LEAF = "\U0001f342";
- public const string FAMILY = "\U0001f46a";
- public const string FAMILY_MMB = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f466";
- public const string FAMILY_MMBB = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f466\U0000200d\U0001f466";
- public const string FAMILY_MMG = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f467";
- public const string FAMILY_MMGB = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f467\U0000200d\U0001f466";
- public const string FAMILY_MMGG = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f467\U0000200d\U0001f467";
- public const string FAMILY_MWBB = "\U0001f468\U0000200d\U0001f469\U0000200d\U0001f466\U0000200d\U0001f466";
- public const string FAMILY_MWG = "\U0001f468\U0000200d\U0001f469\U0000200d\U0001f467";
- public const string FAMILY_MWGB = "\U0001f468\U0000200d\U0001f469\U0000200d\U0001f467\U0000200d\U0001f466";
- public const string FAMILY_MWGG = "\U0001f468\U0000200d\U0001f469\U0000200d\U0001f467\U0000200d\U0001f467";
- public const string FAMILY_WWB = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f466";
- public const string FAMILY_WWBB = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f466\U0000200d\U0001f466";
- public const string FAMILY_WWG = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f467";
- public const string FAMILY_WWGB = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f467\U0000200d\U0001f466";
- public const string FAMILY_WWGG = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f467\U0000200d\U0001f467";
- public const string FAST_FORWARD = "\U000023e9";
- public const string FAX = "\U0001f4e0";
- public const string FEARFUL = "\U0001f628";
- public const string FEET = "\U0001f43e";
- public const string FENCER = "\U0001f93a";
- public const string FENCING = "\U0001f93a";
- public const string FERRIS_WHEEL = "\U0001f3a1";
- public const string FERRY = "\U000026f4";
- public const string FIELD_HOCKEY = "\U0001f3d1";
- public const string FILE_CABINET = "\U0001f5c4";
- public const string FILE_FOLDER = "\U0001f4c1";
- public const string FILM_FRAMES = "\U0001f39e";
- public const string FILM_PROJECTOR = "\U0001f4fd";
- public const string FINGERS_CROSSED = "\U0001f91e";
- public const string FINGERS_CROSSED_SKIN_TONE1 = "\U0001f91e\U0001f3fb";
- public const string FINGERS_CROSSED_SKIN_TONE2 = "\U0001f91e\U0001f3fc";
- public const string FINGERS_CROSSED_SKIN_TONE3 = "\U0001f91e\U0001f3fd";
- public const string FINGERS_CROSSED_SKIN_TONE4 = "\U0001f91e\U0001f3fe";
- public const string FINGERS_CROSSED_SKIN_TONE5 = "\U0001f91e\U0001f3ff";
- public const string FIRE = "\U0001f525";
- public const string FIRE_ENGINE = "\U0001f692";
- public const string FIREWORKS = "\U0001f386";
- public const string FIRST_PLACE = "\U0001f947";
- public const string FIRST_PLACE_MEDAL = "\U0001f947";
- public const string FIRST_QUARTER_MOON = "\U0001f313";
- public const string FIRST_QUARTER_MOON_WITH_FACE = "\U0001f31b";
- public const string FISH = "\U0001f41f";
- public const string FISH_CAKE = "\U0001f365";
- public const string FISHING_POLE_AND_FISH = "\U0001f3a3";
- public const string FIST = "\U0000270a";
- public const string FIST_SKIN_TONE1 = "\U0000270a\U0001f3fb";
- public const string FIST_SKIN_TONE2 = "\U0000270a\U0001f3fc";
- public const string FIST_SKIN_TONE3 = "\U0000270a\U0001f3fd";
- public const string FIST_SKIN_TONE4 = "\U0000270a\U0001f3fe";
- public const string FIST_SKIN_TONE5 = "\U0000270a\U0001f3ff";
- public const string FIVE = "\U00000035\U000020e3";
- public const string FLAG_AC = "\U0001f1e6\U0001f1e8";
- public const string FLAG_AD = "\U0001f1e6\U0001f1e9";
- public const string FLAG_AE = "\U0001f1e6\U0001f1ea";
- public const string FLAG_AF = "\U0001f1e6\U0001f1eb";
- public const string FLAG_AG = "\U0001f1e6\U0001f1ec";
- public const string FLAG_AI = "\U0001f1e6\U0001f1ee";
- public const string FLAG_AL = "\U0001f1e6\U0001f1f1";
- public const string FLAG_AM = "\U0001f1e6\U0001f1f2";
- public const string FLAG_AO = "\U0001f1e6\U0001f1f4";
- public const string FLAG_AQ = "\U0001f1e6\U0001f1f6";
- public const string FLAG_AR = "\U0001f1e6\U0001f1f7";
- public const string FLAG_AS = "\U0001f1e6\U0001f1f8";
- public const string FLAG_AT = "\U0001f1e6\U0001f1f9";
- public const string FLAG_AU = "\U0001f1e6\U0001f1fa";
- public const string FLAG_AW = "\U0001f1e6\U0001f1fc";
- public const string FLAG_AX = "\U0001f1e6\U0001f1fd";
- public const string FLAG_AZ = "\U0001f1e6\U0001f1ff";
- public const string FLAG_BA = "\U0001f1e7\U0001f1e6";
- public const string FLAG_BB = "\U0001f1e7\U0001f1e7";
- public const string FLAG_BD = "\U0001f1e7\U0001f1e9";
- public const string FLAG_BE = "\U0001f1e7\U0001f1ea";
- public const string FLAG_BF = "\U0001f1e7\U0001f1eb";
- public const string FLAG_BG = "\U0001f1e7\U0001f1ec";
- public const string FLAG_BH = "\U0001f1e7\U0001f1ed";
- public const string FLAG_BI = "\U0001f1e7\U0001f1ee";
- public const string FLAG_BJ = "\U0001f1e7\U0001f1ef";
- public const string FLAG_BL = "\U0001f1e7\U0001f1f1";
- public const string FLAG_BLACK = "\U0001f3f4";
- public const string FLAG_BM = "\U0001f1e7\U0001f1f2";
- public const string FLAG_BN = "\U0001f1e7\U0001f1f3";
- public const string FLAG_BO = "\U0001f1e7\U0001f1f4";
- public const string FLAG_BQ = "\U0001f1e7\U0001f1f6";
- public const string FLAG_BR = "\U0001f1e7\U0001f1f7";
- public const string FLAG_BS = "\U0001f1e7\U0001f1f8";
- public const string FLAG_BT = "\U0001f1e7\U0001f1f9";
- public const string FLAG_BV = "\U0001f1e7\U0001f1fb";
- public const string FLAG_BW = "\U0001f1e7\U0001f1fc";
- public const string FLAG_BY = "\U0001f1e7\U0001f1fe";
- public const string FLAG_BZ = "\U0001f1e7\U0001f1ff";
- public const string FLAG_CA = "\U0001f1e8\U0001f1e6";
- public const string FLAG_CC = "\U0001f1e8\U0001f1e8";
- public const string FLAG_CD = "\U0001f1e8\U0001f1e9";
- public const string FLAG_CF = "\U0001f1e8\U0001f1eb";
- public const string FLAG_CG = "\U0001f1e8\U0001f1ec";
- public const string FLAG_CH = "\U0001f1e8\U0001f1ed";
- public const string FLAG_CI = "\U0001f1e8\U0001f1ee";
- public const string FLAG_CK = "\U0001f1e8\U0001f1f0";
- public const string FLAG_CL = "\U0001f1e8\U0001f1f1";
- public const string FLAG_CM = "\U0001f1e8\U0001f1f2";
- public const string FLAG_CN = "\U0001f1e8\U0001f1f3";
- public const string FLAG_CO = "\U0001f1e8\U0001f1f4";
- public const string FLAG_CP = "\U0001f1e8\U0001f1f5";
- public const string FLAG_CR = "\U0001f1e8\U0001f1f7";
- public const string FLAG_CU = "\U0001f1e8\U0001f1fa";
- public const string FLAG_CV = "\U0001f1e8\U0001f1fb";
- public const string FLAG_CW = "\U0001f1e8\U0001f1fc";
- public const string FLAG_CX = "\U0001f1e8\U0001f1fd";
- public const string FLAG_CY = "\U0001f1e8\U0001f1fe";
- public const string FLAG_CZ = "\U0001f1e8\U0001f1ff";
- public const string FLAG_DE = "\U0001f1e9\U0001f1ea";
- public const string FLAG_DG = "\U0001f1e9\U0001f1ec";
- public const string FLAG_DJ = "\U0001f1e9\U0001f1ef";
- public const string FLAG_DK = "\U0001f1e9\U0001f1f0";
- public const string FLAG_DM = "\U0001f1e9\U0001f1f2";
- public const string FLAG_DO = "\U0001f1e9\U0001f1f4";
- public const string FLAG_DZ = "\U0001f1e9\U0001f1ff";
- public const string FLAG_EA = "\U0001f1ea\U0001f1e6";
- public const string FLAG_EC = "\U0001f1ea\U0001f1e8";
- public const string FLAG_EE = "\U0001f1ea\U0001f1ea";
- public const string FLAG_EG = "\U0001f1ea\U0001f1ec";
- public const string FLAG_EH = "\U0001f1ea\U0001f1ed";
- public const string FLAG_ER = "\U0001f1ea\U0001f1f7";
- public const string FLAG_ES = "\U0001f1ea\U0001f1f8";
- public const string FLAG_ET = "\U0001f1ea\U0001f1f9";
- public const string FLAG_EU = "\U0001f1ea\U0001f1fa";
- public const string FLAG_FI = "\U0001f1eb\U0001f1ee";
- public const string FLAG_FJ = "\U0001f1eb\U0001f1ef";
- public const string FLAG_FK = "\U0001f1eb\U0001f1f0";
- public const string FLAG_FM = "\U0001f1eb\U0001f1f2";
- public const string FLAG_FO = "\U0001f1eb\U0001f1f4";
- public const string FLAG_FR = "\U0001f1eb\U0001f1f7";
- public const string FLAG_GA = "\U0001f1ec\U0001f1e6";
- public const string FLAG_GB = "\U0001f1ec\U0001f1e7";
- public const string FLAG_GD = "\U0001f1ec\U0001f1e9";
- public const string FLAG_GE = "\U0001f1ec\U0001f1ea";
- public const string FLAG_GF = "\U0001f1ec\U0001f1eb";
- public const string FLAG_GG = "\U0001f1ec\U0001f1ec";
- public const string FLAG_GH = "\U0001f1ec\U0001f1ed";
- public const string FLAG_GI = "\U0001f1ec\U0001f1ee";
- public const string FLAG_GL = "\U0001f1ec\U0001f1f1";
- public const string FLAG_GM = "\U0001f1ec\U0001f1f2";
- public const string FLAG_GN = "\U0001f1ec\U0001f1f3";
- public const string FLAG_GP = "\U0001f1ec\U0001f1f5";
- public const string FLAG_GQ = "\U0001f1ec\U0001f1f6";
- public const string FLAG_GR = "\U0001f1ec\U0001f1f7";
- public const string FLAG_GS = "\U0001f1ec\U0001f1f8";
- public const string FLAG_GT = "\U0001f1ec\U0001f1f9";
- public const string FLAG_GU = "\U0001f1ec\U0001f1fa";
- public const string FLAG_GW = "\U0001f1ec\U0001f1fc";
- public const string FLAG_GY = "\U0001f1ec\U0001f1fe";
- public const string FLAG_HK = "\U0001f1ed\U0001f1f0";
- public const string FLAG_HM = "\U0001f1ed\U0001f1f2";
- public const string FLAG_HN = "\U0001f1ed\U0001f1f3";
- public const string FLAG_HR = "\U0001f1ed\U0001f1f7";
- public const string FLAG_HT = "\U0001f1ed\U0001f1f9";
- public const string FLAG_HU = "\U0001f1ed\U0001f1fa";
- public const string FLAG_IC = "\U0001f1ee\U0001f1e8";
- public const string FLAG_ID = "\U0001f1ee\U0001f1e9";
- public const string FLAG_IE = "\U0001f1ee\U0001f1ea";
- public const string FLAG_IL = "\U0001f1ee\U0001f1f1";
- public const string FLAG_IM = "\U0001f1ee\U0001f1f2";
- public const string FLAG_IN = "\U0001f1ee\U0001f1f3";
- public const string FLAG_IO = "\U0001f1ee\U0001f1f4";
- public const string FLAG_IQ = "\U0001f1ee\U0001f1f6";
- public const string FLAG_IR = "\U0001f1ee\U0001f1f7";
- public const string FLAG_IS = "\U0001f1ee\U0001f1f8";
- public const string FLAG_IT = "\U0001f1ee\U0001f1f9";
- public const string FLAG_JE = "\U0001f1ef\U0001f1ea";
- public const string FLAG_JM = "\U0001f1ef\U0001f1f2";
- public const string FLAG_JO = "\U0001f1ef\U0001f1f4";
- public const string FLAG_JP = "\U0001f1ef\U0001f1f5";
- public const string FLAG_KE = "\U0001f1f0\U0001f1ea";
- public const string FLAG_KG = "\U0001f1f0\U0001f1ec";
- public const string FLAG_KH = "\U0001f1f0\U0001f1ed";
- public const string FLAG_KI = "\U0001f1f0\U0001f1ee";
- public const string FLAG_KM = "\U0001f1f0\U0001f1f2";
- public const string FLAG_KN = "\U0001f1f0\U0001f1f3";
- public const string FLAG_KP = "\U0001f1f0\U0001f1f5";
- public const string FLAG_KR = "\U0001f1f0\U0001f1f7";
- public const string FLAG_KW = "\U0001f1f0\U0001f1fc";
- public const string FLAG_KY = "\U0001f1f0\U0001f1fe";
- public const string FLAG_KZ = "\U0001f1f0\U0001f1ff";
- public const string FLAG_LA = "\U0001f1f1\U0001f1e6";
- public const string FLAG_LB = "\U0001f1f1\U0001f1e7";
- public const string FLAG_LC = "\U0001f1f1\U0001f1e8";
- public const string FLAG_LI = "\U0001f1f1\U0001f1ee";
- public const string FLAG_LK = "\U0001f1f1\U0001f1f0";
- public const string FLAG_LR = "\U0001f1f1\U0001f1f7";
- public const string FLAG_LS = "\U0001f1f1\U0001f1f8";
- public const string FLAG_LT = "\U0001f1f1\U0001f1f9";
- public const string FLAG_LU = "\U0001f1f1\U0001f1fa";
- public const string FLAG_LV = "\U0001f1f1\U0001f1fb";
- public const string FLAG_LY = "\U0001f1f1\U0001f1fe";
- public const string FLAG_MA = "\U0001f1f2\U0001f1e6";
- public const string FLAG_MC = "\U0001f1f2\U0001f1e8";
- public const string FLAG_MD = "\U0001f1f2\U0001f1e9";
- public const string FLAG_ME = "\U0001f1f2\U0001f1ea";
- public const string FLAG_MF = "\U0001f1f2\U0001f1eb";
- public const string FLAG_MG = "\U0001f1f2\U0001f1ec";
- public const string FLAG_MH = "\U0001f1f2\U0001f1ed";
- public const string FLAG_MK = "\U0001f1f2\U0001f1f0";
- public const string FLAG_ML = "\U0001f1f2\U0001f1f1";
- public const string FLAG_MM = "\U0001f1f2\U0001f1f2";
- public const string FLAG_MN = "\U0001f1f2\U0001f1f3";
- public const string FLAG_MO = "\U0001f1f2\U0001f1f4";
- public const string FLAG_MP = "\U0001f1f2\U0001f1f5";
- public const string FLAG_MQ = "\U0001f1f2\U0001f1f6";
- public const string FLAG_MR = "\U0001f1f2\U0001f1f7";
- public const string FLAG_MS = "\U0001f1f2\U0001f1f8";
- public const string FLAG_MT = "\U0001f1f2\U0001f1f9";
- public const string FLAG_MU = "\U0001f1f2\U0001f1fa";
- public const string FLAG_MV = "\U0001f1f2\U0001f1fb";
- public const string FLAG_MW = "\U0001f1f2\U0001f1fc";
- public const string FLAG_MX = "\U0001f1f2\U0001f1fd";
- public const string FLAG_MY = "\U0001f1f2\U0001f1fe";
- public const string FLAG_MZ = "\U0001f1f2\U0001f1ff";
- public const string FLAG_NA = "\U0001f1f3\U0001f1e6";
- public const string FLAG_NC = "\U0001f1f3\U0001f1e8";
- public const string FLAG_NE = "\U0001f1f3\U0001f1ea";
- public const string FLAG_NF = "\U0001f1f3\U0001f1eb";
- public const string FLAG_NG = "\U0001f1f3\U0001f1ec";
- public const string FLAG_NI = "\U0001f1f3\U0001f1ee";
- public const string FLAG_NL = "\U0001f1f3\U0001f1f1";
- public const string FLAG_NO = "\U0001f1f3\U0001f1f4";
- public const string FLAG_NP = "\U0001f1f3\U0001f1f5";
- public const string FLAG_NR = "\U0001f1f3\U0001f1f7";
- public const string FLAG_NU = "\U0001f1f3\U0001f1fa";
- public const string FLAG_NZ = "\U0001f1f3\U0001f1ff";
- public const string FLAG_OM = "\U0001f1f4\U0001f1f2";
- public const string FLAG_PA = "\U0001f1f5\U0001f1e6";
- public const string FLAG_PE = "\U0001f1f5\U0001f1ea";
- public const string FLAG_PF = "\U0001f1f5\U0001f1eb";
- public const string FLAG_PG = "\U0001f1f5\U0001f1ec";
- public const string FLAG_PH = "\U0001f1f5\U0001f1ed";
- public const string FLAG_PK = "\U0001f1f5\U0001f1f0";
- public const string FLAG_PL = "\U0001f1f5\U0001f1f1";
- public const string FLAG_PM = "\U0001f1f5\U0001f1f2";
- public const string FLAG_PN = "\U0001f1f5\U0001f1f3";
- public const string FLAG_PR = "\U0001f1f5\U0001f1f7";
- public const string FLAG_PS = "\U0001f1f5\U0001f1f8";
- public const string FLAG_PT = "\U0001f1f5\U0001f1f9";
- public const string FLAG_PW = "\U0001f1f5\U0001f1fc";
- public const string FLAG_PY = "\U0001f1f5\U0001f1fe";
- public const string FLAG_QA = "\U0001f1f6\U0001f1e6";
- public const string FLAG_RE = "\U0001f1f7\U0001f1ea";
- public const string FLAG_RO = "\U0001f1f7\U0001f1f4";
- public const string FLAG_RS = "\U0001f1f7\U0001f1f8";
- public const string FLAG_RU = "\U0001f1f7\U0001f1fa";
- public const string FLAG_RW = "\U0001f1f7\U0001f1fc";
- public const string FLAGS = "\U0001f38f";
- public const string FLAG_SA = "\U0001f1f8\U0001f1e6";
- public const string FLAG_SB = "\U0001f1f8\U0001f1e7";
- public const string FLAG_SC = "\U0001f1f8\U0001f1e8";
- public const string FLAG_SD = "\U0001f1f8\U0001f1e9";
- public const string FLAG_SE = "\U0001f1f8\U0001f1ea";
- public const string FLAG_SG = "\U0001f1f8\U0001f1ec";
- public const string FLAG_SH = "\U0001f1f8\U0001f1ed";
- public const string FLAG_SI = "\U0001f1f8\U0001f1ee";
- public const string FLAG_SJ = "\U0001f1f8\U0001f1ef";
- public const string FLAG_SK = "\U0001f1f8\U0001f1f0";
- public const string FLAG_SL = "\U0001f1f8\U0001f1f1";
- public const string FLAG_SM = "\U0001f1f8\U0001f1f2";
- public const string FLAG_SN = "\U0001f1f8\U0001f1f3";
- public const string FLAG_SO = "\U0001f1f8\U0001f1f4";
- public const string FLAG_SR = "\U0001f1f8\U0001f1f7";
- public const string FLAG_SS = "\U0001f1f8\U0001f1f8";
- public const string FLAG_ST = "\U0001f1f8\U0001f1f9";
- public const string FLAG_SV = "\U0001f1f8\U0001f1fb";
- public const string FLAG_SX = "\U0001f1f8\U0001f1fd";
- public const string FLAG_SY = "\U0001f1f8\U0001f1fe";
- public const string FLAG_SZ = "\U0001f1f8\U0001f1ff";
- public const string FLAG_TA = "\U0001f1f9\U0001f1e6";
- public const string FLAG_TC = "\U0001f1f9\U0001f1e8";
- public const string FLAG_TD = "\U0001f1f9\U0001f1e9";
- public const string FLAG_TF = "\U0001f1f9\U0001f1eb";
- public const string FLAG_TG = "\U0001f1f9\U0001f1ec";
- public const string FLAG_TH = "\U0001f1f9\U0001f1ed";
- public const string FLAG_TJ = "\U0001f1f9\U0001f1ef";
- public const string FLAG_TK = "\U0001f1f9\U0001f1f0";
- public const string FLAG_TL = "\U0001f1f9\U0001f1f1";
- public const string FLAG_TM = "\U0001f1f9\U0001f1f2";
- public const string FLAG_TN = "\U0001f1f9\U0001f1f3";
- public const string FLAG_TO = "\U0001f1f9\U0001f1f4";
- public const string FLAG_TR = "\U0001f1f9\U0001f1f7";
- public const string FLAG_TT = "\U0001f1f9\U0001f1f9";
- public const string FLAG_TV = "\U0001f1f9\U0001f1fb";
- public const string FLAG_TW = "\U0001f1f9\U0001f1fc";
- public const string FLAG_TZ = "\U0001f1f9\U0001f1ff";
- public const string FLAG_UA = "\U0001f1fa\U0001f1e6";
- public const string FLAG_UG = "\U0001f1fa\U0001f1ec";
- public const string FLAG_UM = "\U0001f1fa\U0001f1f2";
- public const string FLAG_US = "\U0001f1fa\U0001f1f8";
- public const string FLAG_UY = "\U0001f1fa\U0001f1fe";
- public const string FLAG_UZ = "\U0001f1fa\U0001f1ff";
- public const string FLAG_VA = "\U0001f1fb\U0001f1e6";
- public const string FLAG_VC = "\U0001f1fb\U0001f1e8";
- public const string FLAG_VE = "\U0001f1fb\U0001f1ea";
- public const string FLAG_VG = "\U0001f1fb\U0001f1ec";
- public const string FLAG_VI = "\U0001f1fb\U0001f1ee";
- public const string FLAG_VN = "\U0001f1fb\U0001f1f3";
- public const string FLAG_VU = "\U0001f1fb\U0001f1fa";
- public const string FLAG_WF = "\U0001f1fc\U0001f1eb";
- public const string FLAG_WHITE = "\U0001f3f3";
- public const string FLAG_WS = "\U0001f1fc\U0001f1f8";
- public const string FLAG_XK = "\U0001f1fd\U0001f1f0";
- public const string FLAG_YE = "\U0001f1fe\U0001f1ea";
- public const string FLAG_YT = "\U0001f1fe\U0001f1f9";
- public const string FLAG_ZA = "\U0001f1ff\U0001f1e6";
- public const string FLAG_ZM = "\U0001f1ff\U0001f1f2";
- public const string FLAG_ZW = "\U0001f1ff\U0001f1fc";
- public const string FLAME = "\U0001f525";
- public const string FLAN = "\U0001f36e";
- public const string FLASHLIGHT = "\U0001f526";
- public const string FLEUR_DE_LIS = "\U0000269c";
- public const string FLOPPY_DISK = "\U0001f4be";
- public const string FLOWER_PLAYING_CARDS = "\U0001f3b4";
- public const string FLUSHED = "\U0001f633";
- public const string FOG = "\U0001f32b";
- public const string FOGGY = "\U0001f301";
- public const string FOOTBALL = "\U0001f3c8";
- public const string FOOTPRINTS = "\U0001f463";
- public const string FORK_AND_KNIFE = "\U0001f374";
- public const string FORK_AND_KNIFE_WITH_PLATE = "\U0001f37d";
- public const string FORK_KNIFE_PLATE = "\U0001f37d";
- public const string FOUNTAIN = "\U000026f2";
- public const string FOUR = "\U00000034\U000020e3";
- public const string FOUR_LEAF_CLOVER = "\U0001f340";
- public const string FOX = "\U0001f98a";
- public const string FOX_FACE = "\U0001f98a";
- public const string FRAME_PHOTO = "\U0001f5bc";
- public const string FRAME_WITH_PICTURE = "\U0001f5bc";
- public const string FREE = "\U0001f193";
- public const string FRENCH_BREAD = "\U0001f956";
- public const string FRIED_SHRIMP = "\U0001f364";
- public const string FRIES = "\U0001f35f";
- public const string FROG = "\U0001f438";
- public const string FROWNING = "\U0001f626";
- public const string FROWNING2 = "\U00002639";
- public const string FUELPUMP = "\U000026fd";
- public const string FULL_MOON = "\U0001f315";
- public const string FULL_MOON_WITH_FACE = "\U0001f31d";
- public const string FUNERAL_URN = "\U000026b1";
- public const string GAME_DIE = "\U0001f3b2";
- public const string GAY_PRIDE_FLAG = "\U0001f3f3\U0000fe0f\U0000200d\U0001f308";
- public const string GEAR = "\U00002699";
- public const string GEM = "\U0001f48e";
- public const string GEMINI = "\U0000264a";
- public const string GHOST = "\U0001f47b";
- public const string GIFT = "\U0001f381";
- public const string GIFT_HEART = "\U0001f49d";
- public const string GIRL = "\U0001f467";
- public const string GIRL_SKIN_TONE1 = "\U0001f467\U0001f3fb";
- public const string GIRL_SKIN_TONE2 = "\U0001f467\U0001f3fc";
- public const string GIRL_SKIN_TONE3 = "\U0001f467\U0001f3fd";
- public const string GIRL_SKIN_TONE4 = "\U0001f467\U0001f3fe";
- public const string GIRL_SKIN_TONE5 = "\U0001f467\U0001f3ff";
- public const string GLASS_OF_MILK = "\U0001f95b";
- public const string GLOBE_WITH_MERIDIANS = "\U0001f310";
- public const string GOAL = "\U0001f945";
- public const string GOAL_NET = "\U0001f945";
- public const string GOAT = "\U0001f410";
- public const string GOLF = "\U000026f3";
- public const string GOLFER = "\U0001f3cc";
- public const string GOLFER_SKIN_TONE1 = "\U0001f3cc\U0001f3fb";
- public const string GOLFER_SKIN_TONE2 = "\U0001f3cc\U0001f3fc";
- public const string GOLFER_SKIN_TONE3 = "\U0001f3cc\U0001f3fd";
- public const string GOLFER_SKIN_TONE4 = "\U0001f3cc\U0001f3fe";
- public const string GOLFER_SKIN_TONE5 = "\U0001f3cc\U0001f3ff";
- public const string GORILLA = "\U0001f98d";
- public const string GRANDMA = "\U0001f475";
- public const string GRANDMA_SKIN_TONE1 = "\U0001f475\U0001f3fb";
- public const string GRANDMA_SKIN_TONE2 = "\U0001f475\U0001f3fc";
- public const string GRANDMA_SKIN_TONE3 = "\U0001f475\U0001f3fd";
- public const string GRANDMA_SKIN_TONE4 = "\U0001f475\U0001f3fe";
- public const string GRANDMA_SKIN_TONE5 = "\U0001f475\U0001f3ff";
- public const string GRAPES = "\U0001f347";
- public const string GREEN_APPLE = "\U0001f34f";
- public const string GREEN_BOOK = "\U0001f4d7";
- public const string GREEN_HEART = "\U0001f49a";
- public const string GREEN_SALAD = "\U0001f957";
- public const string GREY_EXCLAMATION = "\U00002755";
- public const string GREY_QUESTION = "\U00002754";
- public const string GRIMACING = "\U0001f62c";
- public const string GRIN = "\U0001f601";
- public const string GRINNING = "\U0001f600";
- public const string GUARDSMAN = "\U0001f482";
- public const string GUARDSMAN_SKIN_TONE1 = "\U0001f482\U0001f3fb";
- public const string GUARDSMAN_SKIN_TONE2 = "\U0001f482\U0001f3fc";
- public const string GUARDSMAN_SKIN_TONE3 = "\U0001f482\U0001f3fd";
- public const string GUARDSMAN_SKIN_TONE4 = "\U0001f482\U0001f3fe";
- public const string GUARDSMAN_SKIN_TONE5 = "\U0001f482\U0001f3ff";
- public const string GUITAR = "\U0001f3b8";
- public const string GUN = "\U0001f52b";
- public const string HAIRCUT = "\U0001f487";
- public const string HAIRCUT_SKIN_TONE1 = "\U0001f487\U0001f3fb";
- public const string HAIRCUT_SKIN_TONE2 = "\U0001f487\U0001f3fc";
- public const string HAIRCUT_SKIN_TONE3 = "\U0001f487\U0001f3fd";
- public const string HAIRCUT_SKIN_TONE4 = "\U0001f487\U0001f3fe";
- public const string HAIRCUT_SKIN_TONE5 = "\U0001f487\U0001f3ff";
- public const string HAMBURGER = "\U0001f354";
- public const string HAMMER = "\U0001f528";
- public const string HAMMER_AND_PICK = "\U00002692";
- public const string HAMMER_AND_WRENCH = "\U0001f6e0";
- public const string HAMMER_PICK = "\U00002692";
- public const string HAMSTER = "\U0001f439";
- public const string HANDBAG = "\U0001f45c";
- public const string HANDBALL = "\U0001f93e";
- public const string HANDBALL_SKIN_TONE1 = "\U0001f93e\U0001f3fb";
- public const string HANDBALL_SKIN_TONE2 = "\U0001f93e\U0001f3fc";
- public const string HANDBALL_SKIN_TONE3 = "\U0001f93e\U0001f3fd";
- public const string HANDBALL_SKIN_TONE4 = "\U0001f93e\U0001f3fe";
- public const string HANDBALL_SKIN_TONE5 = "\U0001f93e\U0001f3ff";
- public const string HANDSHAKE = "\U0001f91d";
- public const string HAND_SPLAYED = "\U0001f590";
- public const string HAND_SPLAYED_SKIN_TONE1 = "\U0001f590\U0001f3fb";
- public const string HAND_SPLAYED_SKIN_TONE2 = "\U0001f590\U0001f3fc";
- public const string HAND_SPLAYED_SKIN_TONE3 = "\U0001f590\U0001f3fd";
- public const string HAND_SPLAYED_SKIN_TONE4 = "\U0001f590\U0001f3fe";
- public const string HAND_SPLAYED_SKIN_TONE5 = "\U0001f590\U0001f3ff";
- public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED = "\U0001f91e";
- public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE1 = "\U0001f91e\U0001f3fb";
- public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE2 = "\U0001f91e\U0001f3fc";
- public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE3 = "\U0001f91e\U0001f3fd";
- public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE4 = "\U0001f91e\U0001f3fe";
- public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE5 = "\U0001f91e\U0001f3ff";
- public const string HANKEY = "\U0001f4a9";
- public const string HASH = "\U00000023\U000020e3";
- public const string HATCHED_CHICK = "\U0001f425";
- public const string HATCHING_CHICK = "\U0001f423";
- public const string HEAD_BANDAGE = "\U0001f915";
- public const string HEADPHONES = "\U0001f3a7";
- public const string HEAR_NO_EVIL = "\U0001f649";
- public const string HEART = "\U00002764";
- public const string HEARTBEAT = "\U0001f493";
- public const string HEART_DECORATION = "\U0001f49f";
- public const string HEART_EXCLAMATION = "\U00002763";
- public const string HEART_EYES = "\U0001f60d";
- public const string HEART_EYES_CAT = "\U0001f63b";
- public const string HEARTPULSE = "\U0001f497";
- public const string HEARTS = "\U00002665";
- public const string HEAVY_CHECK_MARK = "\U00002714";
- public const string HEAVY_DIVISION_SIGN = "\U00002797";
- public const string HEAVY_DOLLAR_SIGN = "\U0001f4b2";
- public const string HEAVY_HEART_EXCLAMATION_MARK_ORNAMENT = "\U00002763";
- public const string HEAVY_MINUS_SIGN = "\U00002796";
- public const string HEAVY_MULTIPLICATION_X = "\U00002716";
- public const string HEAVY_PLUS_SIGN = "\U00002795";
- public const string HELICOPTER = "\U0001f681";
- public const string HELMET_WITH_CROSS = "\U000026d1";
- public const string HELMET_WITH_WHITE_CROSS = "\U000026d1";
- public const string HERB = "\U0001f33f";
- public const string HIBISCUS = "\U0001f33a";
- public const string HIGH_BRIGHTNESS = "\U0001f506";
- public const string HIGH_HEEL = "\U0001f460";
- public const string HOCKEY = "\U0001f3d2";
- public const string HOLE = "\U0001f573";
- public const string HOMES = "\U0001f3d8";
- public const string HONEY_POT = "\U0001f36f";
- public const string HORSE = "\U0001f434";
- public const string HORSE_RACING = "\U0001f3c7";
- public const string HORSE_RACING_SKIN_TONE1 = "\U0001f3c7\U0001f3fb";
- public const string HORSE_RACING_SKIN_TONE2 = "\U0001f3c7\U0001f3fc";
- public const string HORSE_RACING_SKIN_TONE3 = "\U0001f3c7\U0001f3fd";
- public const string HORSE_RACING_SKIN_TONE4 = "\U0001f3c7\U0001f3fe";
- public const string HORSE_RACING_SKIN_TONE5 = "\U0001f3c7\U0001f3ff";
- public const string HOSPITAL = "\U0001f3e5";
- public const string HOTDOG = "\U0001f32d";
- public const string HOT_DOG = "\U0001f32d";
- public const string HOTEL = "\U0001f3e8";
- public const string HOT_PEPPER = "\U0001f336";
- public const string HOTSPRINGS = "\U00002668";
- public const string HOURGLASS = "\U0000231b";
- public const string HOURGLASS_FLOWING_SAND = "\U000023f3";
- public const string HOUSE = "\U0001f3e0";
- public const string HOUSE_ABANDONED = "\U0001f3da";
- public const string HOUSE_BUILDINGS = "\U0001f3d8";
- public const string HOUSE_WITH_GARDEN = "\U0001f3e1";
- public const string HUGGING = "\U0001f917";
- public const string HUGGING_FACE = "\U0001f917";
- public const string HUSHED = "\U0001f62f";
- public const string ICECREAM = "\U0001f366";
- public const string ICE_CREAM = "\U0001f368";
- public const string ICE_SKATE = "\U000026f8";
- public const string ID = "\U0001f194";
- public const string IDEOGRAPH_ADVANTAGE = "\U0001f250";
- public const string IMP = "\U0001f47f";
- public const string INBOX_TRAY = "\U0001f4e5";
- public const string INCOMING_ENVELOPE = "\U0001f4e8";
- public const string INFORMATION_DESK_PERSON = "\U0001f481";
- public const string INFORMATION_DESK_PERSON_SKIN_TONE1 = "\U0001f481\U0001f3fb";
- public const string INFORMATION_DESK_PERSON_SKIN_TONE2 = "\U0001f481\U0001f3fc";
- public const string INFORMATION_DESK_PERSON_SKIN_TONE3 = "\U0001f481\U0001f3fd";
- public const string INFORMATION_DESK_PERSON_SKIN_TONE4 = "\U0001f481\U0001f3fe";
- public const string INFORMATION_DESK_PERSON_SKIN_TONE5 = "\U0001f481\U0001f3ff";
- public const string INFORMATION_SOURCE = "\U00002139";
- public const string INNOCENT = "\U0001f607";
- public const string INTERROBANG = "\U00002049";
- public const string IPHONE = "\U0001f4f1";
- public const string ISLAND = "\U0001f3dd";
- public const string IZAKAYA_LANTERN = "\U0001f3ee";
- public const string JACK_O_LANTERN = "\U0001f383";
- public const string JAPAN = "\U0001f5fe";
- public const string JAPANESE_CASTLE = "\U0001f3ef";
- public const string JAPANESE_GOBLIN = "\U0001f47a";
- public const string JAPANESE_OGRE = "\U0001f479";
- public const string JEANS = "\U0001f456";
- public const string JOY = "\U0001f602";
- public const string JOY_CAT = "\U0001f639";
- public const string JOYSTICK = "\U0001f579";
- public const string JUGGLER = "\U0001f939";
- public const string JUGGLER_SKIN_TONE1 = "\U0001f939\U0001f3fb";
- public const string JUGGLER_SKIN_TONE2 = "\U0001f939\U0001f3fc";
- public const string JUGGLER_SKIN_TONE3 = "\U0001f939\U0001f3fd";
- public const string JUGGLER_SKIN_TONE4 = "\U0001f939\U0001f3fe";
- public const string JUGGLER_SKIN_TONE5 = "\U0001f939\U0001f3ff";
- public const string JUGGLING = "\U0001f939";
- public const string JUGGLING_SKIN_TONE1 = "\U0001f939\U0001f3fb";
- public const string JUGGLING_SKIN_TONE2 = "\U0001f939\U0001f3fc";
- public const string JUGGLING_SKIN_TONE3 = "\U0001f939\U0001f3fd";
- public const string JUGGLING_SKIN_TONE4 = "\U0001f939\U0001f3fe";
- public const string JUGGLING_SKIN_TONE5 = "\U0001f939\U0001f3ff";
- public const string KAABA = "\U0001f54b";
- public const string KARATE_UNIFORM = "\U0001f94b";
- public const string KAYAK = "\U0001f6f6";
- public const string KEY = "\U0001f511";
- public const string KEY2 = "\U0001f5dd";
- public const string KEYBOARD = "\U00002328";
- public const string KEYCAP_ASTERISK = "\U0000002a\U000020e3";
- public const string KEYCAP_TEN = "\U0001f51f";
- public const string KIMONO = "\U0001f458";
- public const string KISS = "\U0001f48b";
- public const string KISSING = "\U0001f617";
- public const string KISSING_CAT = "\U0001f63d";
- public const string KISSING_CLOSED_EYES = "\U0001f61a";
- public const string KISSING_HEART = "\U0001f618";
- public const string KISSING_SMILING_EYES = "\U0001f619";
- public const string KISS_MM = "\U0001f468\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f48b\U0000200d\U0001f468";
- public const string KISS_WW = "\U0001f469\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f48b\U0000200d\U0001f469";
- public const string KIWI = "\U0001f95d";
- public const string KIWIFRUIT = "\U0001f95d";
- public const string KNIFE = "\U0001f52a";
- public const string KOALA = "\U0001f428";
- public const string KOKO = "\U0001f201";
- public const string LABEL = "\U0001f3f7";
- public const string LARGE_BLUE_CIRCLE = "\U0001f535";
- public const string LARGE_BLUE_DIAMOND = "\U0001f537";
- public const string LARGE_ORANGE_DIAMOND = "\U0001f536";
- public const string LAST_QUARTER_MOON = "\U0001f317";
- public const string LAST_QUARTER_MOON_WITH_FACE = "\U0001f31c";
- public const string LATIN_CROSS = "\U0000271d";
- public const string LAUGHING = "\U0001f606";
- public const string LEAVES = "\U0001f343";
- public const string LEDGER = "\U0001f4d2";
- public const string LEFT_FACING_FIST = "\U0001f91b";
- public const string LEFT_FACING_FIST_SKIN_TONE1 = "\U0001f91b\U0001f3fb";
- public const string LEFT_FACING_FIST_SKIN_TONE2 = "\U0001f91b\U0001f3fc";
- public const string LEFT_FACING_FIST_SKIN_TONE3 = "\U0001f91b\U0001f3fd";
- public const string LEFT_FACING_FIST_SKIN_TONE4 = "\U0001f91b\U0001f3fe";
- public const string LEFT_FACING_FIST_SKIN_TONE5 = "\U0001f91b\U0001f3ff";
- public const string LEFT_FIST = "\U0001f91b";
- public const string LEFT_FIST_SKIN_TONE1 = "\U0001f91b\U0001f3fb";
- public const string LEFT_FIST_SKIN_TONE2 = "\U0001f91b\U0001f3fc";
- public const string LEFT_FIST_SKIN_TONE3 = "\U0001f91b\U0001f3fd";
- public const string LEFT_FIST_SKIN_TONE4 = "\U0001f91b\U0001f3fe";
- public const string LEFT_FIST_SKIN_TONE5 = "\U0001f91b\U0001f3ff";
- public const string LEFT_LUGGAGE = "\U0001f6c5";
- public const string LEFT_RIGHT_ARROW = "\U00002194";
- public const string LEFT_SPEECH_BUBBLE = "\U0001f5e8";
- public const string LEFTWARDS_ARROW_WITH_HOOK = "\U000021a9";
- public const string LEMON = "\U0001f34b";
- public const string LEO = "\U0000264c";
- public const string LEOPARD = "\U0001f406";
- public const string LEVEL_SLIDER = "\U0001f39a";
- public const string LEVITATE = "\U0001f574";
- public const string LEVITATE_SKIN_TONE1 = "\U0001f574\U0001f3fb";
- public const string LEVITATE_SKIN_TONE2 = "\U0001f574\U0001f3fc";
- public const string LEVITATE_SKIN_TONE3 = "\U0001f574\U0001f3fd";
- public const string LEVITATE_SKIN_TONE4 = "\U0001f574\U0001f3fe";
- public const string LEVITATE_SKIN_TONE5 = "\U0001f574\U0001f3ff";
- public const string LIAR = "\U0001f925";
- public const string LIBRA = "\U0000264e";
- public const string LIFTER = "\U0001f3cb";
- public const string LIFTER_SKIN_TONE1 = "\U0001f3cb\U0001f3fb";
- public const string LIFTER_SKIN_TONE2 = "\U0001f3cb\U0001f3fc";
- public const string LIFTER_SKIN_TONE3 = "\U0001f3cb\U0001f3fd";
- public const string LIFTER_SKIN_TONE4 = "\U0001f3cb\U0001f3fe";
- public const string LIFTER_SKIN_TONE5 = "\U0001f3cb\U0001f3ff";
- public const string LIGHT_RAIL = "\U0001f688";
- public const string LINK = "\U0001f517";
- public const string LINKED_PAPERCLIPS = "\U0001f587";
- public const string LION = "\U0001f981";
- public const string LION_FACE = "\U0001f981";
- public const string LIPS = "\U0001f444";
- public const string LIPSTICK = "\U0001f484";
- public const string LIZARD = "\U0001f98e";
- public const string LOCK = "\U0001f512";
- public const string LOCK_WITH_INK_PEN = "\U0001f50f";
- public const string LOLLIPOP = "\U0001f36d";
- public const string LOOP = "\U000027bf";
- public const string LOUD_SOUND = "\U0001f50a";
- public const string LOUDSPEAKER = "\U0001f4e2";
- public const string LOVE_HOTEL = "\U0001f3e9";
- public const string LOVE_LETTER = "\U0001f48c";
- public const string LOW_BRIGHTNESS = "\U0001f505";
- public const string LOWER_LEFT_BALLPOINT_PEN = "\U0001f58a";
- public const string LOWER_LEFT_CRAYON = "\U0001f58d";
- public const string LOWER_LEFT_FOUNTAIN_PEN = "\U0001f58b";
- public const string LOWER_LEFT_PAINTBRUSH = "\U0001f58c";
- public const string LYING_FACE = "\U0001f925";
- public const string M = "\U000024c2";
- public const string MAG = "\U0001f50d";
- public const string MAG_RIGHT = "\U0001f50e";
- public const string MAHJONG = "\U0001f004";
- public const string MAILBOX = "\U0001f4eb";
- public const string MAILBOX_CLOSED = "\U0001f4ea";
- public const string MAILBOX_WITH_MAIL = "\U0001f4ec";
- public const string MAILBOX_WITH_NO_MAIL = "\U0001f4ed";
- public const string MALE_DANCER = "\U0001f57a";
- public const string MALE_DANCER_SKIN_TONE1 = "\U0001f57a\U0001f3fb";
- public const string MALE_DANCER_SKIN_TONE2 = "\U0001f57a\U0001f3fc";
- public const string MALE_DANCER_SKIN_TONE3 = "\U0001f57a\U0001f3fd";
- public const string MALE_DANCER_SKIN_TONE4 = "\U0001f57a\U0001f3fe";
- public const string MALE_DANCER_SKIN_TONE5 = "\U0001f57a\U0001f3ff";
- public const string MAN = "\U0001f468";
- public const string MAN_DANCING = "\U0001f57a";
- public const string MAN_DANCING_SKIN_TONE1 = "\U0001f57a\U0001f3fb";
- public const string MAN_DANCING_SKIN_TONE2 = "\U0001f57a\U0001f3fc";
- public const string MAN_DANCING_SKIN_TONE3 = "\U0001f57a\U0001f3fd";
- public const string MAN_DANCING_SKIN_TONE4 = "\U0001f57a\U0001f3fe";
- public const string MAN_DANCING_SKIN_TONE5 = "\U0001f57a\U0001f3ff";
- public const string MAN_IN_BUSINESS_SUIT_LEVITATING = "\U0001f574";
- public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE1 = "\U0001f574\U0001f3fb";
- public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE2 = "\U0001f574\U0001f3fc";
- public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE3 = "\U0001f574\U0001f3fd";
- public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE4 = "\U0001f574\U0001f3fe";
- public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE5 = "\U0001f574\U0001f3ff";
- public const string MAN_IN_TUXEDO = "\U0001f935";
- public const string MAN_IN_TUXEDO_SKIN_TONE1 = "\U0001f935\U0001f3fb";
- public const string MAN_IN_TUXEDO_SKIN_TONE2 = "\U0001f935\U0001f3fc";
- public const string MAN_IN_TUXEDO_SKIN_TONE3 = "\U0001f935\U0001f3fd";
- public const string MAN_IN_TUXEDO_SKIN_TONE4 = "\U0001f935\U0001f3fe";
- public const string MAN_IN_TUXEDO_SKIN_TONE5 = "\U0001f935\U0001f3ff";
- public const string MAN_SKIN_TONE1 = "\U0001f468\U0001f3fb";
- public const string MAN_SKIN_TONE2 = "\U0001f468\U0001f3fc";
- public const string MAN_SKIN_TONE3 = "\U0001f468\U0001f3fd";
- public const string MAN_SKIN_TONE4 = "\U0001f468\U0001f3fe";
- public const string MAN_SKIN_TONE5 = "\U0001f468\U0001f3ff";
- public const string MANS_SHOE = "\U0001f45e";
- public const string MANTLEPIECE_CLOCK = "\U0001f570";
- public const string MAN_WITH_GUA_PI_MAO = "\U0001f472";
- public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE1 = "\U0001f472\U0001f3fb";
- public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE2 = "\U0001f472\U0001f3fc";
- public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE3 = "\U0001f472\U0001f3fd";
- public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE4 = "\U0001f472\U0001f3fe";
- public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE5 = "\U0001f472\U0001f3ff";
- public const string MAN_WITH_TURBAN = "\U0001f473";
- public const string MAN_WITH_TURBAN_SKIN_TONE1 = "\U0001f473\U0001f3fb";
- public const string MAN_WITH_TURBAN_SKIN_TONE2 = "\U0001f473\U0001f3fc";
- public const string MAN_WITH_TURBAN_SKIN_TONE3 = "\U0001f473\U0001f3fd";
- public const string MAN_WITH_TURBAN_SKIN_TONE4 = "\U0001f473\U0001f3fe";
- public const string MAN_WITH_TURBAN_SKIN_TONE5 = "\U0001f473\U0001f3ff";
- public const string MAP = "\U0001f5fa";
- public const string MAPLE_LEAF = "\U0001f341";
- public const string MARTIAL_ARTS_UNIFORM = "\U0001f94b";
- public const string MASK = "\U0001f637";
- public const string MASSAGE = "\U0001f486";
- public const string MASSAGE_SKIN_TONE1 = "\U0001f486\U0001f3fb";
- public const string MASSAGE_SKIN_TONE2 = "\U0001f486\U0001f3fc";
- public const string MASSAGE_SKIN_TONE3 = "\U0001f486\U0001f3fd";
- public const string MASSAGE_SKIN_TONE4 = "\U0001f486\U0001f3fe";
- public const string MASSAGE_SKIN_TONE5 = "\U0001f486\U0001f3ff";
- public const string MEAT_ON_BONE = "\U0001f356";
- public const string MEDAL = "\U0001f3c5";
- public const string MEGA = "\U0001f4e3";
- public const string MELON = "\U0001f348";
- public const string MENORAH = "\U0001f54e";
- public const string MENS = "\U0001f6b9";
- public const string METAL = "\U0001f918";
- public const string METAL_SKIN_TONE1 = "\U0001f918\U0001f3fb";
- public const string METAL_SKIN_TONE2 = "\U0001f918\U0001f3fc";
- public const string METAL_SKIN_TONE3 = "\U0001f918\U0001f3fd";
- public const string METAL_SKIN_TONE4 = "\U0001f918\U0001f3fe";
- public const string METAL_SKIN_TONE5 = "\U0001f918\U0001f3ff";
- public const string METRO = "\U0001f687";
- public const string MICROPHONE = "\U0001f3a4";
- public const string MICROPHONE2 = "\U0001f399";
- public const string MICROSCOPE = "\U0001f52c";
- public const string MIDDLE_FINGER = "\U0001f595";
- public const string MIDDLE_FINGER_SKIN_TONE1 = "\U0001f595\U0001f3fb";
- public const string MIDDLE_FINGER_SKIN_TONE2 = "\U0001f595\U0001f3fc";
- public const string MIDDLE_FINGER_SKIN_TONE3 = "\U0001f595\U0001f3fd";
- public const string MIDDLE_FINGER_SKIN_TONE4 = "\U0001f595\U0001f3fe";
- public const string MIDDLE_FINGER_SKIN_TONE5 = "\U0001f595\U0001f3ff";
- public const string MILITARY_MEDAL = "\U0001f396";
- public const string MILK = "\U0001f95b";
- public const string MILKY_WAY = "\U0001f30c";
- public const string MINIBUS = "\U0001f690";
- public const string MINIDISC = "\U0001f4bd";
- public const string MOBILE_PHONE_OFF = "\U0001f4f4";
- public const string MONEYBAG = "\U0001f4b0";
- public const string MONEY_MOUTH = "\U0001f911";
- public const string MONEY_MOUTH_FACE = "\U0001f911";
- public const string MONEY_WITH_WINGS = "\U0001f4b8";
- public const string MONKEY = "\U0001f412";
- public const string MONKEY_FACE = "\U0001f435";
- public const string MONORAIL = "\U0001f69d";
- public const string MORTAR_BOARD = "\U0001f393";
- public const string MOSQUE = "\U0001f54c";
- public const string MOTHER_CHRISTMAS = "\U0001f936";
- public const string MOTHER_CHRISTMAS_SKIN_TONE1 = "\U0001f936\U0001f3fb";
- public const string MOTHER_CHRISTMAS_SKIN_TONE2 = "\U0001f936\U0001f3fc";
- public const string MOTHER_CHRISTMAS_SKIN_TONE3 = "\U0001f936\U0001f3fd";
- public const string MOTHER_CHRISTMAS_SKIN_TONE4 = "\U0001f936\U0001f3fe";
- public const string MOTHER_CHRISTMAS_SKIN_TONE5 = "\U0001f936\U0001f3ff";
- public const string MOTORBIKE = "\U0001f6f5";
- public const string MOTORBOAT = "\U0001f6e5";
- public const string MOTORCYCLE = "\U0001f3cd";
- public const string MOTOR_SCOOTER = "\U0001f6f5";
- public const string MOTORWAY = "\U0001f6e3";
- public const string MOUNTAIN = "\U000026f0";
- public const string MOUNTAIN_BICYCLIST = "\U0001f6b5";
- public const string MOUNTAIN_BICYCLIST_SKIN_TONE1 = "\U0001f6b5\U0001f3fb";
- public const string MOUNTAIN_BICYCLIST_SKIN_TONE2 = "\U0001f6b5\U0001f3fc";
- public const string MOUNTAIN_BICYCLIST_SKIN_TONE3 = "\U0001f6b5\U0001f3fd";
- public const string MOUNTAIN_BICYCLIST_SKIN_TONE4 = "\U0001f6b5\U0001f3fe";
- public const string MOUNTAIN_BICYCLIST_SKIN_TONE5 = "\U0001f6b5\U0001f3ff";
- public const string MOUNTAIN_CABLEWAY = "\U0001f6a0";
- public const string MOUNTAIN_RAILWAY = "\U0001f69e";
- public const string MOUNTAIN_SNOW = "\U0001f3d4";
- public const string MOUNT_FUJI = "\U0001f5fb";
- public const string MOUSE = "\U0001f42d";
- public const string MOUSE2 = "\U0001f401";
- public const string MOUSE_THREE_BUTTON = "\U0001f5b1";
- public const string MOVIE_CAMERA = "\U0001f3a5";
- public const string MOYAI = "\U0001f5ff";
- public const string MRS_CLAUS = "\U0001f936";
- public const string MRS_CLAUS_SKIN_TONE1 = "\U0001f936\U0001f3fb";
- public const string MRS_CLAUS_SKIN_TONE2 = "\U0001f936\U0001f3fc";
- public const string MRS_CLAUS_SKIN_TONE3 = "\U0001f936\U0001f3fd";
- public const string MRS_CLAUS_SKIN_TONE4 = "\U0001f936\U0001f3fe";
- public const string MRS_CLAUS_SKIN_TONE5 = "\U0001f936\U0001f3ff";
- public const string MUSCLE = "\U0001f4aa";
- public const string MUSCLE_SKIN_TONE1 = "\U0001f4aa\U0001f3fb";
- public const string MUSCLE_SKIN_TONE2 = "\U0001f4aa\U0001f3fc";
- public const string MUSCLE_SKIN_TONE3 = "\U0001f4aa\U0001f3fd";
- public const string MUSCLE_SKIN_TONE4 = "\U0001f4aa\U0001f3fe";
- public const string MUSCLE_SKIN_TONE5 = "\U0001f4aa\U0001f3ff";
- public const string MUSHROOM = "\U0001f344";
- public const string MUSICAL_KEYBOARD = "\U0001f3b9";
- public const string MUSICAL_NOTE = "\U0001f3b5";
- public const string MUSICAL_SCORE = "\U0001f3bc";
- public const string MUTE = "\U0001f507";
- public const string NAIL_CARE = "\U0001f485";
- public const string NAIL_CARE_SKIN_TONE1 = "\U0001f485\U0001f3fb";
- public const string NAIL_CARE_SKIN_TONE2 = "\U0001f485\U0001f3fc";
- public const string NAIL_CARE_SKIN_TONE3 = "\U0001f485\U0001f3fd";
- public const string NAIL_CARE_SKIN_TONE4 = "\U0001f485\U0001f3fe";
- public const string NAIL_CARE_SKIN_TONE5 = "\U0001f485\U0001f3ff";
- public const string NAME_BADGE = "\U0001f4db";
- public const string NATIONAL_PARK = "\U0001f3de";
- public const string NAUSEATED_FACE = "\U0001f922";
- public const string NECKTIE = "\U0001f454";
- public const string NEGATIVE_SQUARED_CROSS_MARK = "\U0000274e";
- public const string NERD = "\U0001f913";
- public const string NERD_FACE = "\U0001f913";
- public const string NEUTRAL_FACE = "\U0001f610";
- public const string NEW = "\U0001f195";
- public const string NEW_MOON = "\U0001f311";
- public const string NEW_MOON_WITH_FACE = "\U0001f31a";
- public const string NEWSPAPER = "\U0001f4f0";
- public const string NEWSPAPER2 = "\U0001f5de";
- public const string NEXT_TRACK = "\U000023ed";
- public const string NG = "\U0001f196";
- public const string NIGHT_WITH_STARS = "\U0001f303";
- public const string NINE = "\U00000039\U000020e3";
- public const string NO_BELL = "\U0001f515";
- public const string NO_BICYCLES = "\U0001f6b3";
- public const string NO_ENTRY = "\U000026d4";
- public const string NO_ENTRY_SIGN = "\U0001f6ab";
- public const string NO_GOOD = "\U0001f645";
- public const string NO_GOOD_SKIN_TONE1 = "\U0001f645\U0001f3fb";
- public const string NO_GOOD_SKIN_TONE2 = "\U0001f645\U0001f3fc";
- public const string NO_GOOD_SKIN_TONE3 = "\U0001f645\U0001f3fd";
- public const string NO_GOOD_SKIN_TONE4 = "\U0001f645\U0001f3fe";
- public const string NO_GOOD_SKIN_TONE5 = "\U0001f645\U0001f3ff";
- public const string NO_MOBILE_PHONES = "\U0001f4f5";
- public const string NO_MOUTH = "\U0001f636";
- public const string NON_POTABLE_WATER = "\U0001f6b1";
- public const string NO_PEDESTRIANS = "\U0001f6b7";
- public const string NOSE = "\U0001f443";
- public const string NOSE_SKIN_TONE1 = "\U0001f443\U0001f3fb";
- public const string NOSE_SKIN_TONE2 = "\U0001f443\U0001f3fc";
- public const string NOSE_SKIN_TONE3 = "\U0001f443\U0001f3fd";
- public const string NOSE_SKIN_TONE4 = "\U0001f443\U0001f3fe";
- public const string NOSE_SKIN_TONE5 = "\U0001f443\U0001f3ff";
- public const string NO_SMOKING = "\U0001f6ad";
- public const string NOTEBOOK = "\U0001f4d3";
- public const string NOTEBOOK_WITH_DECORATIVE_COVER = "\U0001f4d4";
- public const string NOTEPAD_SPIRAL = "\U0001f5d2";
- public const string NOTES = "\U0001f3b6";
- public const string NUT_AND_BOLT = "\U0001f529";
- public const string O = "\U00002b55";
- public const string O2 = "\U0001f17e";
- public const string OCEAN = "\U0001f30a";
- public const string OCTAGONAL_SIGN = "\U0001f6d1";
- public const string OCTOPUS = "\U0001f419";
- public const string ODEN = "\U0001f362";
- public const string OFFICE = "\U0001f3e2";
- public const string OIL = "\U0001f6e2";
- public const string OIL_DRUM = "\U0001f6e2";
- public const string OK = "\U0001f197";
- public const string OK_HAND = "\U0001f44c";
- public const string OK_HAND_SKIN_TONE1 = "\U0001f44c\U0001f3fb";
- public const string OK_HAND_SKIN_TONE2 = "\U0001f44c\U0001f3fc";
- public const string OK_HAND_SKIN_TONE3 = "\U0001f44c\U0001f3fd";
- public const string OK_HAND_SKIN_TONE4 = "\U0001f44c\U0001f3fe";
- public const string OK_HAND_SKIN_TONE5 = "\U0001f44c\U0001f3ff";
- public const string OK_WOMAN = "\U0001f646";
- public const string OK_WOMAN_SKIN_TONE1 = "\U0001f646\U0001f3fb";
- public const string OK_WOMAN_SKIN_TONE2 = "\U0001f646\U0001f3fc";
- public const string OK_WOMAN_SKIN_TONE3 = "\U0001f646\U0001f3fd";
- public const string OK_WOMAN_SKIN_TONE4 = "\U0001f646\U0001f3fe";
- public const string OK_WOMAN_SKIN_TONE5 = "\U0001f646\U0001f3ff";
- public const string OLDER_MAN = "\U0001f474";
- public const string OLDER_MAN_SKIN_TONE1 = "\U0001f474\U0001f3fb";
- public const string OLDER_MAN_SKIN_TONE2 = "\U0001f474\U0001f3fc";
- public const string OLDER_MAN_SKIN_TONE3 = "\U0001f474\U0001f3fd";
- public const string OLDER_MAN_SKIN_TONE4 = "\U0001f474\U0001f3fe";
- public const string OLDER_MAN_SKIN_TONE5 = "\U0001f474\U0001f3ff";
- public const string OLDER_WOMAN = "\U0001f475";
- public const string OLDER_WOMAN_SKIN_TONE1 = "\U0001f475\U0001f3fb";
- public const string OLDER_WOMAN_SKIN_TONE2 = "\U0001f475\U0001f3fc";
- public const string OLDER_WOMAN_SKIN_TONE3 = "\U0001f475\U0001f3fd";
- public const string OLDER_WOMAN_SKIN_TONE4 = "\U0001f475\U0001f3fe";
- public const string OLDER_WOMAN_SKIN_TONE5 = "\U0001f475\U0001f3ff";
- public const string OLD_KEY = "\U0001f5dd";
- public const string OM_SYMBOL = "\U0001f549";
- public const string ON = "\U0001f51b";
- public const string ONCOMING_AUTOMOBILE = "\U0001f698";
- public const string ONCOMING_BUS = "\U0001f68d";
- public const string ONCOMING_POLICE_CAR = "\U0001f694";
- public const string ONCOMING_TAXI = "\U0001f696";
- public const string ONE = "\U00000031\U000020e3";
- public const string OPEN_FILE_FOLDER = "\U0001f4c2";
- public const string OPEN_HANDS = "\U0001f450";
- public const string OPEN_HANDS_SKIN_TONE1 = "\U0001f450\U0001f3fb";
- public const string OPEN_HANDS_SKIN_TONE2 = "\U0001f450\U0001f3fc";
- public const string OPEN_HANDS_SKIN_TONE3 = "\U0001f450\U0001f3fd";
- public const string OPEN_HANDS_SKIN_TONE4 = "\U0001f450\U0001f3fe";
- public const string OPEN_HANDS_SKIN_TONE5 = "\U0001f450\U0001f3ff";
- public const string OPEN_MOUTH = "\U0001f62e";
- public const string OPHIUCHUS = "\U000026ce";
- public const string ORANGE_BOOK = "\U0001f4d9";
- public const string ORTHODOX_CROSS = "\U00002626";
- public const string OUTBOX_TRAY = "\U0001f4e4";
- public const string OWL = "\U0001f989";
- public const string OX = "\U0001f402";
- public const string PACKAGE = "\U0001f4e6";
- public const string PAELLA = "\U0001f958";
- public const string PAGE_FACING_UP = "\U0001f4c4";
- public const string PAGER = "\U0001f4df";
- public const string PAGE_WITH_CURL = "\U0001f4c3";
- public const string PAINTBRUSH = "\U0001f58c";
- public const string PALM_TREE = "\U0001f334";
- public const string PANCAKES = "\U0001f95e";
- public const string PANDA_FACE = "\U0001f43c";
- public const string PAPERCLIP = "\U0001f4ce";
- public const string PAPERCLIPS = "\U0001f587";
- public const string PARK = "\U0001f3de";
- public const string PARKING = "\U0001f17f";
- public const string PART_ALTERNATION_MARK = "\U0000303d";
- public const string PARTLY_SUNNY = "\U000026c5";
- public const string PASSENGER_SHIP = "\U0001f6f3";
- public const string PASSPORT_CONTROL = "\U0001f6c2";
- public const string PAUSE_BUTTON = "\U000023f8";
- public const string PAW_PRINTS = "\U0001f43e";
- public const string PEACE = "\U0000262e";
- public const string PEACE_SYMBOL = "\U0000262e";
- public const string PEACH = "\U0001f351";
- public const string PEANUTS = "\U0001f95c";
- public const string PEAR = "\U0001f350";
- public const string PEN_BALLPOINT = "\U0001f58a";
- public const string PENCIL = "\U0001f4dd";
- public const string PENCIL2 = "\U0000270f";
- public const string PEN_FOUNTAIN = "\U0001f58b";
- public const string PENGUIN = "\U0001f427";
- public const string PENSIVE = "\U0001f614";
- public const string PERFORMING_ARTS = "\U0001f3ad";
- public const string PERSEVERE = "\U0001f623";
- public const string PERSON_DOING_CARTWHEEL = "\U0001f938";
- public const string PERSON_DOING_CARTWHEEL_SKIN_TONE1 = "\U0001f938\U0001f3fb";
- public const string PERSON_DOING_CARTWHEEL_SKIN_TONE2 = "\U0001f938\U0001f3fc";
- public const string PERSON_DOING_CARTWHEEL_SKIN_TONE3 = "\U0001f938\U0001f3fd";
- public const string PERSON_DOING_CARTWHEEL_SKIN_TONE4 = "\U0001f938\U0001f3fe";
- public const string PERSON_DOING_CARTWHEEL_SKIN_TONE5 = "\U0001f938\U0001f3ff";
- public const string PERSON_FROWNING = "\U0001f64d";
- public const string PERSON_FROWNING_SKIN_TONE1 = "\U0001f64d\U0001f3fb";
- public const string PERSON_FROWNING_SKIN_TONE2 = "\U0001f64d\U0001f3fc";
- public const string PERSON_FROWNING_SKIN_TONE3 = "\U0001f64d\U0001f3fd";
- public const string PERSON_FROWNING_SKIN_TONE4 = "\U0001f64d\U0001f3fe";
- public const string PERSON_FROWNING_SKIN_TONE5 = "\U0001f64d\U0001f3ff";
- public const string PERSON_WITH_BALL = "\U000026f9";
- public const string PERSON_WITH_BALL_SKIN_TONE1 = "\U000026f9\U0001f3fb";
- public const string PERSON_WITH_BALL_SKIN_TONE2 = "\U000026f9\U0001f3fc";
- public const string PERSON_WITH_BALL_SKIN_TONE3 = "\U000026f9\U0001f3fd";
- public const string PERSON_WITH_BALL_SKIN_TONE4 = "\U000026f9\U0001f3fe";
- public const string PERSON_WITH_BALL_SKIN_TONE5 = "\U000026f9\U0001f3ff";
- public const string PERSON_WITH_BLOND_HAIR = "\U0001f471";
- public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE1 = "\U0001f471\U0001f3fb";
- public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE2 = "\U0001f471\U0001f3fc";
- public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE3 = "\U0001f471\U0001f3fd";
- public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE4 = "\U0001f471\U0001f3fe";
- public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE5 = "\U0001f471\U0001f3ff";
- public const string PERSON_WITH_POUTING_FACE = "\U0001f64e";
- public const string PERSON_WITH_POUTING_FACE_SKIN_TONE1 = "\U0001f64e\U0001f3fb";
- public const string PERSON_WITH_POUTING_FACE_SKIN_TONE2 = "\U0001f64e\U0001f3fc";
- public const string PERSON_WITH_POUTING_FACE_SKIN_TONE3 = "\U0001f64e\U0001f3fd";
- public const string PERSON_WITH_POUTING_FACE_SKIN_TONE4 = "\U0001f64e\U0001f3fe";
- public const string PERSON_WITH_POUTING_FACE_SKIN_TONE5 = "\U0001f64e\U0001f3ff";
- public const string PICK = "\U000026cf";
- public const string PIG = "\U0001f437";
- public const string PIG2 = "\U0001f416";
- public const string PIG_NOSE = "\U0001f43d";
- public const string PILL = "\U0001f48a";
- public const string PINEAPPLE = "\U0001f34d";
- public const string PING_PONG = "\U0001f3d3";
- public const string PISCES = "\U00002653";
- public const string PIZZA = "\U0001f355";
- public const string PLACE_OF_WORSHIP = "\U0001f6d0";
- public const string PLAY_PAUSE = "\U000023ef";
- public const string PLUS1 = "\U0001f44d";
- public const string PLUS1_SKIN_TONE1 = "\U0001f44d\U0001f3fb";
- public const string PLUS1_SKIN_TONE2 = "\U0001f44d\U0001f3fc";
- public const string PLUS1_SKIN_TONE3 = "\U0001f44d\U0001f3fd";
- public const string PLUS1_SKIN_TONE4 = "\U0001f44d\U0001f3fe";
- public const string PLUS1_SKIN_TONE5 = "\U0001f44d\U0001f3ff";
- public const string POINT_DOWN = "\U0001f447";
- public const string POINT_DOWN_SKIN_TONE1 = "\U0001f447\U0001f3fb";
- public const string POINT_DOWN_SKIN_TONE2 = "\U0001f447\U0001f3fc";
- public const string POINT_DOWN_SKIN_TONE3 = "\U0001f447\U0001f3fd";
- public const string POINT_DOWN_SKIN_TONE4 = "\U0001f447\U0001f3fe";
- public const string POINT_DOWN_SKIN_TONE5 = "\U0001f447\U0001f3ff";
- public const string POINT_LEFT = "\U0001f448";
- public const string POINT_LEFT_SKIN_TONE1 = "\U0001f448\U0001f3fb";
- public const string POINT_LEFT_SKIN_TONE2 = "\U0001f448\U0001f3fc";
- public const string POINT_LEFT_SKIN_TONE3 = "\U0001f448\U0001f3fd";
- public const string POINT_LEFT_SKIN_TONE4 = "\U0001f448\U0001f3fe";
- public const string POINT_LEFT_SKIN_TONE5 = "\U0001f448\U0001f3ff";
- public const string POINT_RIGHT = "\U0001f449";
- public const string POINT_RIGHT_SKIN_TONE1 = "\U0001f449\U0001f3fb";
- public const string POINT_RIGHT_SKIN_TONE2 = "\U0001f449\U0001f3fc";
- public const string POINT_RIGHT_SKIN_TONE3 = "\U0001f449\U0001f3fd";
- public const string POINT_RIGHT_SKIN_TONE4 = "\U0001f449\U0001f3fe";
- public const string POINT_RIGHT_SKIN_TONE5 = "\U0001f449\U0001f3ff";
- public const string POINT_UP = "\U0000261d";
- public const string POINT_UP2 = "\U0001f446";
- public const string POINT_UP2_SKIN_TONE1 = "\U0001f446\U0001f3fb";
- public const string POINT_UP2_SKIN_TONE2 = "\U0001f446\U0001f3fc";
- public const string POINT_UP2_SKIN_TONE3 = "\U0001f446\U0001f3fd";
- public const string POINT_UP2_SKIN_TONE4 = "\U0001f446\U0001f3fe";
- public const string POINT_UP2_SKIN_TONE5 = "\U0001f446\U0001f3ff";
- public const string POINT_UP_SKIN_TONE1 = "\U0000261d\U0001f3fb";
- public const string POINT_UP_SKIN_TONE2 = "\U0000261d\U0001f3fc";
- public const string POINT_UP_SKIN_TONE3 = "\U0000261d\U0001f3fd";
- public const string POINT_UP_SKIN_TONE4 = "\U0000261d\U0001f3fe";
- public const string POINT_UP_SKIN_TONE5 = "\U0000261d\U0001f3ff";
- public const string POLICE_CAR = "\U0001f693";
- public const string POO = "\U0001f4a9";
- public const string POODLE = "\U0001f429";
- public const string POOP = "\U0001f4a9";
- public const string POPCORN = "\U0001f37f";
- public const string POSTAL_HORN = "\U0001f4ef";
- public const string POSTBOX = "\U0001f4ee";
- public const string POST_OFFICE = "\U0001f3e3";
- public const string POTABLE_WATER = "\U0001f6b0";
- public const string POTATO = "\U0001f954";
- public const string POUCH = "\U0001f45d";
- public const string POULTRY_LEG = "\U0001f357";
- public const string POUND = "\U0001f4b7";
- public const string POUTING_CAT = "\U0001f63e";
- public const string PRAY = "\U0001f64f";
- public const string PRAYER_BEADS = "\U0001f4ff";
- public const string PRAY_SKIN_TONE1 = "\U0001f64f\U0001f3fb";
- public const string PRAY_SKIN_TONE2 = "\U0001f64f\U0001f3fc";
- public const string PRAY_SKIN_TONE3 = "\U0001f64f\U0001f3fd";
- public const string PRAY_SKIN_TONE4 = "\U0001f64f\U0001f3fe";
- public const string PRAY_SKIN_TONE5 = "\U0001f64f\U0001f3ff";
- public const string PREGNANT_WOMAN = "\U0001f930";
- public const string PREGNANT_WOMAN_SKIN_TONE1 = "\U0001f930\U0001f3fb";
- public const string PREGNANT_WOMAN_SKIN_TONE2 = "\U0001f930\U0001f3fc";
- public const string PREGNANT_WOMAN_SKIN_TONE3 = "\U0001f930\U0001f3fd";
- public const string PREGNANT_WOMAN_SKIN_TONE4 = "\U0001f930\U0001f3fe";
- public const string PREGNANT_WOMAN_SKIN_TONE5 = "\U0001f930\U0001f3ff";
- public const string PREVIOUS_TRACK = "\U000023ee";
- public const string PRINCE = "\U0001f934";
- public const string PRINCE_SKIN_TONE1 = "\U0001f934\U0001f3fb";
- public const string PRINCE_SKIN_TONE2 = "\U0001f934\U0001f3fc";
- public const string PRINCE_SKIN_TONE3 = "\U0001f934\U0001f3fd";
- public const string PRINCE_SKIN_TONE4 = "\U0001f934\U0001f3fe";
- public const string PRINCE_SKIN_TONE5 = "\U0001f934\U0001f3ff";
- public const string PRINCESS = "\U0001f478";
- public const string PRINCESS_SKIN_TONE1 = "\U0001f478\U0001f3fb";
- public const string PRINCESS_SKIN_TONE2 = "\U0001f478\U0001f3fc";
- public const string PRINCESS_SKIN_TONE3 = "\U0001f478\U0001f3fd";
- public const string PRINCESS_SKIN_TONE4 = "\U0001f478\U0001f3fe";
- public const string PRINCESS_SKIN_TONE5 = "\U0001f478\U0001f3ff";
- public const string PRINTER = "\U0001f5a8";
- public const string PROJECTOR = "\U0001f4fd";
- public const string PUDDING = "\U0001f36e";
- public const string PUNCH = "\U0001f44a";
- public const string PUNCH_SKIN_TONE1 = "\U0001f44a\U0001f3fb";
- public const string PUNCH_SKIN_TONE2 = "\U0001f44a\U0001f3fc";
- public const string PUNCH_SKIN_TONE3 = "\U0001f44a\U0001f3fd";
- public const string PUNCH_SKIN_TONE4 = "\U0001f44a\U0001f3fe";
- public const string PUNCH_SKIN_TONE5 = "\U0001f44a\U0001f3ff";
- public const string PURPLE_HEART = "\U0001f49c";
- public const string PURSE = "\U0001f45b";
- public const string PUSHPIN = "\U0001f4cc";
- public const string PUT_LITTER_IN_ITS_PLACE = "\U0001f6ae";
- public const string QUESTION = "\U00002753";
- public const string RABBIT = "\U0001f430";
- public const string RABBIT2 = "\U0001f407";
- public const string RACE_CAR = "\U0001f3ce";
- public const string RACEHORSE = "\U0001f40e";
- public const string RACING_CAR = "\U0001f3ce";
- public const string RACING_MOTORCYCLE = "\U0001f3cd";
- public const string RADIO = "\U0001f4fb";
- public const string RADIOACTIVE = "\U00002622";
- public const string RADIOACTIVE_SIGN = "\U00002622";
- public const string RADIO_BUTTON = "\U0001f518";
- public const string RAGE = "\U0001f621";
- public const string RAILROAD_TRACK = "\U0001f6e4";
- public const string RAILWAY_CAR = "\U0001f683";
- public const string RAILWAY_TRACK = "\U0001f6e4";
- public const string RAINBOW = "\U0001f308";
- public const string RAINBOW_FLAG = "\U0001f3f3\U0000fe0f\U0000200d\U0001f308";
- public const string RAISED_BACK_OF_HAND = "\U0001f91a";
- public const string RAISED_BACK_OF_HAND_SKIN_TONE1 = "\U0001f91a\U0001f3fb";
- public const string RAISED_BACK_OF_HAND_SKIN_TONE2 = "\U0001f91a\U0001f3fc";
- public const string RAISED_BACK_OF_HAND_SKIN_TONE3 = "\U0001f91a\U0001f3fd";
- public const string RAISED_BACK_OF_HAND_SKIN_TONE4 = "\U0001f91a\U0001f3fe";
- public const string RAISED_BACK_OF_HAND_SKIN_TONE5 = "\U0001f91a\U0001f3ff";
- public const string RAISED_HAND = "\U0000270b";
- public const string RAISED_HANDS = "\U0001f64c";
- public const string RAISED_HAND_SKIN_TONE1 = "\U0000270b\U0001f3fb";
- public const string RAISED_HAND_SKIN_TONE2 = "\U0000270b\U0001f3fc";
- public const string RAISED_HAND_SKIN_TONE3 = "\U0000270b\U0001f3fd";
- public const string RAISED_HAND_SKIN_TONE4 = "\U0000270b\U0001f3fe";
- public const string RAISED_HAND_SKIN_TONE5 = "\U0000270b\U0001f3ff";
- public const string RAISED_HANDS_SKIN_TONE1 = "\U0001f64c\U0001f3fb";
- public const string RAISED_HANDS_SKIN_TONE2 = "\U0001f64c\U0001f3fc";
- public const string RAISED_HANDS_SKIN_TONE3 = "\U0001f64c\U0001f3fd";
- public const string RAISED_HANDS_SKIN_TONE4 = "\U0001f64c\U0001f3fe";
- public const string RAISED_HANDS_SKIN_TONE5 = "\U0001f64c\U0001f3ff";
- public const string RAISED_HAND_WITH_FINGERS_SPLAYED = "\U0001f590";
- public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE1 = "\U0001f590\U0001f3fb";
- public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE2 = "\U0001f590\U0001f3fc";
- public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE3 = "\U0001f590\U0001f3fd";
- public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE4 = "\U0001f590\U0001f3fe";
- public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE5 = "\U0001f590\U0001f3ff";
- public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS = "\U0001f596";
- public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE1 = "\U0001f596\U0001f3fb";
- public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE2 = "\U0001f596\U0001f3fc";
- public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE3 = "\U0001f596\U0001f3fd";
- public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE4 = "\U0001f596\U0001f3fe";
- public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE5 = "\U0001f596\U0001f3ff";
- public const string RAISING_HAND = "\U0001f64b";
- public const string RAISING_HAND_SKIN_TONE1 = "\U0001f64b\U0001f3fb";
- public const string RAISING_HAND_SKIN_TONE2 = "\U0001f64b\U0001f3fc";
- public const string RAISING_HAND_SKIN_TONE3 = "\U0001f64b\U0001f3fd";
- public const string RAISING_HAND_SKIN_TONE4 = "\U0001f64b\U0001f3fe";
- public const string RAISING_HAND_SKIN_TONE5 = "\U0001f64b\U0001f3ff";
- public const string RAM = "\U0001f40f";
- public const string RAMEN = "\U0001f35c";
- public const string RAT = "\U0001f400";
- public const string RECORD_BUTTON = "\U000023fa";
- public const string RECYCLE = "\U0000267b";
- public const string RED_CAR = "\U0001f697";
- public const string RED_CIRCLE = "\U0001f534";
- public const string REGIONAL_INDICATOR_A = "\U0001f1e6";
- public const string REGIONAL_INDICATOR_B = "\U0001f1e7";
- public const string REGIONAL_INDICATOR_C = "\U0001f1e8";
- public const string REGIONAL_INDICATOR_D = "\U0001f1e9";
- public const string REGIONAL_INDICATOR_E = "\U0001f1ea";
- public const string REGIONAL_INDICATOR_F = "\U0001f1eb";
- public const string REGIONAL_INDICATOR_G = "\U0001f1ec";
- public const string REGIONAL_INDICATOR_H = "\U0001f1ed";
- public const string REGIONAL_INDICATOR_I = "\U0001f1ee";
- public const string REGIONAL_INDICATOR_J = "\U0001f1ef";
- public const string REGIONAL_INDICATOR_K = "\U0001f1f0";
- public const string REGIONAL_INDICATOR_L = "\U0001f1f1";
- public const string REGIONAL_INDICATOR_M = "\U0001f1f2";
- public const string REGIONAL_INDICATOR_N = "\U0001f1f3";
- public const string REGIONAL_INDICATOR_O = "\U0001f1f4";
- public const string REGIONAL_INDICATOR_P = "\U0001f1f5";
- public const string REGIONAL_INDICATOR_Q = "\U0001f1f6";
- public const string REGIONAL_INDICATOR_R = "\U0001f1f7";
- public const string REGIONAL_INDICATOR_S = "\U0001f1f8";
- public const string REGIONAL_INDICATOR_T = "\U0001f1f9";
- public const string REGIONAL_INDICATOR_U = "\U0001f1fa";
- public const string REGIONAL_INDICATOR_V = "\U0001f1fb";
- public const string REGIONAL_INDICATOR_W = "\U0001f1fc";
- public const string REGIONAL_INDICATOR_X = "\U0001f1fd";
- public const string REGIONAL_INDICATOR_Y = "\U0001f1fe";
- public const string REGIONAL_INDICATOR_Z = "\U0001f1ff";
- public const string REGISTERED = "\U000000ae";
- public const string RELAXED = "\U0000263a";
- public const string RELIEVED = "\U0001f60c";
- public const string REMINDER_RIBBON = "\U0001f397";
- public const string REPEAT = "\U0001f501";
- public const string REPEAT_ONE = "\U0001f502";
- public const string RESTROOM = "\U0001f6bb";
- public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED = "\U0001f595";
- public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE1 = "\U0001f595\U0001f3fb";
- public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE2 = "\U0001f595\U0001f3fc";
- public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE3 = "\U0001f595\U0001f3fd";
- public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE4 = "\U0001f595\U0001f3fe";
- public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE5 = "\U0001f595\U0001f3ff";
- public const string REVOLVING_HEARTS = "\U0001f49e";
- public const string REWIND = "\U000023ea";
- public const string RHINO = "\U0001f98f";
- public const string RHINOCEROS = "\U0001f98f";
- public const string RIBBON = "\U0001f380";
- public const string RICE = "\U0001f35a";
- public const string RICE_BALL = "\U0001f359";
- public const string RICE_CRACKER = "\U0001f358";
- public const string RICE_SCENE = "\U0001f391";
- public const string RIGHT_ANGER_BUBBLE = "\U0001f5ef";
- public const string RIGHT_FACING_FIST = "\U0001f91c";
- public const string RIGHT_FACING_FIST_SKIN_TONE1 = "\U0001f91c\U0001f3fb";
- public const string RIGHT_FACING_FIST_SKIN_TONE2 = "\U0001f91c\U0001f3fc";
- public const string RIGHT_FACING_FIST_SKIN_TONE3 = "\U0001f91c\U0001f3fd";
- public const string RIGHT_FACING_FIST_SKIN_TONE4 = "\U0001f91c\U0001f3fe";
- public const string RIGHT_FACING_FIST_SKIN_TONE5 = "\U0001f91c\U0001f3ff";
- public const string RIGHT_FIST = "\U0001f91c";
- public const string RIGHT_FIST_SKIN_TONE1 = "\U0001f91c\U0001f3fb";
- public const string RIGHT_FIST_SKIN_TONE2 = "\U0001f91c\U0001f3fc";
- public const string RIGHT_FIST_SKIN_TONE3 = "\U0001f91c\U0001f3fd";
- public const string RIGHT_FIST_SKIN_TONE4 = "\U0001f91c\U0001f3fe";
- public const string RIGHT_FIST_SKIN_TONE5 = "\U0001f91c\U0001f3ff";
- public const string RING = "\U0001f48d";
- public const string ROBOT = "\U0001f916";
- public const string ROBOT_FACE = "\U0001f916";
- public const string ROCKET = "\U0001f680";
- public const string ROFL = "\U0001f923";
- public const string ROLLED_UP_NEWSPAPER = "\U0001f5de";
- public const string ROLLER_COASTER = "\U0001f3a2";
- public const string ROLLING_EYES = "\U0001f644";
- public const string ROLLING_ON_THE_FLOOR_LAUGHING = "\U0001f923";
- public const string ROOSTER = "\U0001f413";
- public const string ROSE = "\U0001f339";
- public const string ROSETTE = "\U0001f3f5";
- public const string ROTATING_LIGHT = "\U0001f6a8";
- public const string ROUND_PUSHPIN = "\U0001f4cd";
- public const string ROWBOAT = "\U0001f6a3";
- public const string ROWBOAT_SKIN_TONE1 = "\U0001f6a3\U0001f3fb";
- public const string ROWBOAT_SKIN_TONE2 = "\U0001f6a3\U0001f3fc";
- public const string ROWBOAT_SKIN_TONE3 = "\U0001f6a3\U0001f3fd";
- public const string ROWBOAT_SKIN_TONE4 = "\U0001f6a3\U0001f3fe";
- public const string ROWBOAT_SKIN_TONE5 = "\U0001f6a3\U0001f3ff";
- public const string RUGBY_FOOTBALL = "\U0001f3c9";
- public const string RUNNER = "\U0001f3c3";
- public const string RUNNER_SKIN_TONE1 = "\U0001f3c3\U0001f3fb";
- public const string RUNNER_SKIN_TONE2 = "\U0001f3c3\U0001f3fc";
- public const string RUNNER_SKIN_TONE3 = "\U0001f3c3\U0001f3fd";
- public const string RUNNER_SKIN_TONE4 = "\U0001f3c3\U0001f3fe";
- public const string RUNNER_SKIN_TONE5 = "\U0001f3c3\U0001f3ff";
- public const string RUNNING_SHIRT_WITH_SASH = "\U0001f3bd";
- public const string SA = "\U0001f202";
- public const string SAGITTARIUS = "\U00002650";
- public const string SAILBOAT = "\U000026f5";
- public const string SAKE = "\U0001f376";
- public const string SALAD = "\U0001f957";
- public const string SANDAL = "\U0001f461";
- public const string SANTA = "\U0001f385";
- public const string SANTA_SKIN_TONE1 = "\U0001f385\U0001f3fb";
- public const string SANTA_SKIN_TONE2 = "\U0001f385\U0001f3fc";
- public const string SANTA_SKIN_TONE3 = "\U0001f385\U0001f3fd";
- public const string SANTA_SKIN_TONE4 = "\U0001f385\U0001f3fe";
- public const string SANTA_SKIN_TONE5 = "\U0001f385\U0001f3ff";
- public const string SATELLITE = "\U0001f4e1";
- public const string SATELLITE_ORBITAL = "\U0001f6f0";
- public const string SATISFIED = "\U0001f606";
- public const string SAXOPHONE = "\U0001f3b7";
- public const string SCALES = "\U00002696";
- public const string SCHOOL = "\U0001f3eb";
- public const string SCHOOL_SATCHEL = "\U0001f392";
- public const string SCISSORS = "\U00002702";
- public const string SCOOTER = "\U0001f6f4";
- public const string SCORPION = "\U0001f982";
- public const string SCORPIUS = "\U0000264f";
- public const string SCREAM = "\U0001f631";
- public const string SCREAM_CAT = "\U0001f640";
- public const string SCROLL = "\U0001f4dc";
- public const string SEAT = "\U0001f4ba";
- public const string SECOND_PLACE = "\U0001f948";
- public const string SECOND_PLACE_MEDAL = "\U0001f948";
- public const string SECRET = "\U00003299";
- public const string SEEDLING = "\U0001f331";
- public const string SEE_NO_EVIL = "\U0001f648";
- public const string SELFIE = "\U0001f933";
- public const string SELFIE_SKIN_TONE1 = "\U0001f933\U0001f3fb";
- public const string SELFIE_SKIN_TONE2 = "\U0001f933\U0001f3fc";
- public const string SELFIE_SKIN_TONE3 = "\U0001f933\U0001f3fd";
- public const string SELFIE_SKIN_TONE4 = "\U0001f933\U0001f3fe";
- public const string SELFIE_SKIN_TONE5 = "\U0001f933\U0001f3ff";
- public const string SEVEN = "\U00000037\U000020e3";
- public const string SHAKING_HANDS = "\U0001f91d";
- public const string SHALLOW_PAN_OF_FOOD = "\U0001f958";
- public const string SHAMROCK = "\U00002618";
- public const string SHARK = "\U0001f988";
- public const string SHAVED_ICE = "\U0001f367";
- public const string SHEEP = "\U0001f411";
- public const string SHELL = "\U0001f41a";
- public const string SHELLED_PEANUT = "\U0001f95c";
- public const string SHIELD = "\U0001f6e1";
- public const string SHINTO_SHRINE = "\U000026e9";
- public const string SHIP = "\U0001f6a2";
- public const string SHIRT = "\U0001f455";
- public const string SHIT = "\U0001f4a9";
- public const string SHOPPING_BAGS = "\U0001f6cd";
- public const string SHOPPING_CART = "\U0001f6d2";
- public const string SHOPPING_TROLLEY = "\U0001f6d2";
- public const string SHOWER = "\U0001f6bf";
- public const string SHRIMP = "\U0001f990";
- public const string SHRUG = "\U0001f937";
- public const string SHRUG_SKIN_TONE1 = "\U0001f937\U0001f3fb";
- public const string SHRUG_SKIN_TONE2 = "\U0001f937\U0001f3fc";
- public const string SHRUG_SKIN_TONE3 = "\U0001f937\U0001f3fd";
- public const string SHRUG_SKIN_TONE4 = "\U0001f937\U0001f3fe";
- public const string SHRUG_SKIN_TONE5 = "\U0001f937\U0001f3ff";
- public const string SICK = "\U0001f922";
- public const string SIGNAL_STRENGTH = "\U0001f4f6";
- public const string SIGN_OF_THE_HORNS = "\U0001f918";
- public const string SIGN_OF_THE_HORNS_SKIN_TONE1 = "\U0001f918\U0001f3fb";
- public const string SIGN_OF_THE_HORNS_SKIN_TONE2 = "\U0001f918\U0001f3fc";
- public const string SIGN_OF_THE_HORNS_SKIN_TONE3 = "\U0001f918\U0001f3fd";
- public const string SIGN_OF_THE_HORNS_SKIN_TONE4 = "\U0001f918\U0001f3fe";
- public const string SIGN_OF_THE_HORNS_SKIN_TONE5 = "\U0001f918\U0001f3ff";
- public const string SIX = "\U00000036\U000020e3";
- public const string SIX_POINTED_STAR = "\U0001f52f";
- public const string SKELETON = "\U0001f480";
- public const string SKI = "\U0001f3bf";
- public const string SKIER = "\U000026f7";
- public const string SKIER_SKIN_TONE1 = "\U000026f7\U0001f3fb";
- public const string SKIER_SKIN_TONE2 = "\U000026f7\U0001f3fc";
- public const string SKIER_SKIN_TONE3 = "\U000026f7\U0001f3fd";
- public const string SKIER_SKIN_TONE4 = "\U000026f7\U0001f3fe";
- public const string SKIER_SKIN_TONE5 = "\U000026f7\U0001f3ff";
- public const string SKULL = "\U0001f480";
- public const string SKULL_AND_CROSSBONES = "\U00002620";
- public const string SKULL_CROSSBONES = "\U00002620";
- public const string SLEEPING = "\U0001f634";
- public const string SLEEPING_ACCOMMODATION = "\U0001f6cc";
- public const string SLEEPING_ACCOMMODATION_SKIN_TONE1 = "\U0001f6cc\U0001f3fb";
- public const string SLEEPING_ACCOMMODATION_SKIN_TONE2 = "\U0001f6cc\U0001f3fc";
- public const string SLEEPING_ACCOMMODATION_SKIN_TONE3 = "\U0001f6cc\U0001f3fd";
- public const string SLEEPING_ACCOMMODATION_SKIN_TONE4 = "\U0001f6cc\U0001f3fe";
- public const string SLEEPING_ACCOMMODATION_SKIN_TONE5 = "\U0001f6cc\U0001f3ff";
- public const string SLEEPY = "\U0001f62a";
- public const string SLEUTH_OR_SPY = "\U0001f575";
- public const string SLEUTH_OR_SPY_SKIN_TONE1 = "\U0001f575\U0001f3fb";
- public const string SLEUTH_OR_SPY_SKIN_TONE2 = "\U0001f575\U0001f3fc";
- public const string SLEUTH_OR_SPY_SKIN_TONE3 = "\U0001f575\U0001f3fd";
- public const string SLEUTH_OR_SPY_SKIN_TONE4 = "\U0001f575\U0001f3fe";
- public const string SLEUTH_OR_SPY_SKIN_TONE5 = "\U0001f575\U0001f3ff";
- public const string SLIGHT_FROWN = "\U0001f641";
- public const string SLIGHTLY_FROWNING_FACE = "\U0001f641";
- public const string SLIGHTLY_SMILING_FACE = "\U0001f642";
- public const string SLIGHT_SMILE = "\U0001f642";
- public const string SLOT_MACHINE = "\U0001f3b0";
- public const string SMALL_AIRPLANE = "\U0001f6e9";
- public const string SMALL_BLUE_DIAMOND = "\U0001f539";
- public const string SMALL_ORANGE_DIAMOND = "\U0001f538";
- public const string SMALL_RED_TRIANGLE = "\U0001f53a";
- public const string SMALL_RED_TRIANGLE_DOWN = "\U0001f53b";
- public const string SMILE = "\U0001f604";
- public const string SMILE_CAT = "\U0001f638";
- public const string SMILEY = "\U0001f603";
- public const string SMILEY_CAT = "\U0001f63a";
- public const string SMILING_IMP = "\U0001f608";
- public const string SMIRK = "\U0001f60f";
- public const string SMIRK_CAT = "\U0001f63c";
- public const string SMOKING = "\U0001f6ac";
- public const string SNAIL = "\U0001f40c";
- public const string SNAKE = "\U0001f40d";
- public const string SNEEZE = "\U0001f927";
- public const string SNEEZING_FACE = "\U0001f927";
- public const string SNOWBOARDER = "\U0001f3c2";
- public const string SNOWBOARDER_SKIN_TONE1 = "\U0001f3c2\U0001f3fb";
- public const string SNOWBOARDER_SKIN_TONE2 = "\U0001f3c2\U0001f3fc";
- public const string SNOWBOARDER_SKIN_TONE3 = "\U0001f3c2\U0001f3fd";
- public const string SNOWBOARDER_SKIN_TONE4 = "\U0001f3c2\U0001f3fe";
- public const string SNOWBOARDER_SKIN_TONE5 = "\U0001f3c2\U0001f3ff";
- public const string SNOW_CAPPED_MOUNTAIN = "\U0001f3d4";
- public const string SNOWFLAKE = "\U00002744";
- public const string SNOWMAN = "\U000026c4";
- public const string SNOWMAN2 = "\U00002603";
- public const string SOB = "\U0001f62d";
- public const string SOCCER = "\U000026bd";
- public const string SOON = "\U0001f51c";
- public const string SOS = "\U0001f198";
- public const string SOUND = "\U0001f509";
- public const string SPACE_INVADER = "\U0001f47e";
- public const string SPADES = "\U00002660";
- public const string SPAGHETTI = "\U0001f35d";
- public const string SPARKLE = "\U00002747";
- public const string SPARKLER = "\U0001f387";
- public const string SPARKLES = "\U00002728";
- public const string SPARKLING_HEART = "\U0001f496";
- public const string SPEAKER = "\U0001f508";
- public const string SPEAKING_HEAD = "\U0001f5e3";
- public const string SPEAKING_HEAD_IN_SILHOUETTE = "\U0001f5e3";
- public const string SPEAK_NO_EVIL = "\U0001f64a";
- public const string SPEECH_BALLOON = "\U0001f4ac";
- public const string SPEECH_LEFT = "\U0001f5e8";
- public const string SPEEDBOAT = "\U0001f6a4";
- public const string SPIDER = "\U0001f577";
- public const string SPIDER_WEB = "\U0001f578";
- public const string SPIRAL_CALENDAR_PAD = "\U0001f5d3";
- public const string SPIRAL_NOTE_PAD = "\U0001f5d2";
- public const string SPOON = "\U0001f944";
- public const string SPORTS_MEDAL = "\U0001f3c5";
- public const string SPY = "\U0001f575";
- public const string SPY_SKIN_TONE1 = "\U0001f575\U0001f3fb";
- public const string SPY_SKIN_TONE2 = "\U0001f575\U0001f3fc";
- public const string SPY_SKIN_TONE3 = "\U0001f575\U0001f3fd";
- public const string SPY_SKIN_TONE4 = "\U0001f575\U0001f3fe";
- public const string SPY_SKIN_TONE5 = "\U0001f575\U0001f3ff";
- public const string SQUID = "\U0001f991";
- public const string STADIUM = "\U0001f3df";
- public const string STAR = "\U00002b50";
- public const string STAR2 = "\U0001f31f";
- public const string STAR_AND_CRESCENT = "\U0000262a";
- public const string STAR_OF_DAVID = "\U00002721";
- public const string STARS = "\U0001f320";
- public const string STATION = "\U0001f689";
- public const string STATUE_OF_LIBERTY = "\U0001f5fd";
- public const string STEAM_LOCOMOTIVE = "\U0001f682";
- public const string STEW = "\U0001f372";
- public const string STOP_BUTTON = "\U000023f9";
- public const string STOP_SIGN = "\U0001f6d1";
- public const string STOPWATCH = "\U000023f1";
- public const string STRAIGHT_RULER = "\U0001f4cf";
- public const string STRAWBERRY = "\U0001f353";
- public const string STUCK_OUT_TONGUE = "\U0001f61b";
- public const string STUCK_OUT_TONGUE_CLOSED_EYES = "\U0001f61d";
- public const string STUCK_OUT_TONGUE_WINKING_EYE = "\U0001f61c";
- public const string STUDIO_MICROPHONE = "\U0001f399";
- public const string STUFFED_FLATBREAD = "\U0001f959";
- public const string STUFFED_PITA = "\U0001f959";
- public const string SUNFLOWER = "\U0001f33b";
- public const string SUNGLASSES = "\U0001f60e";
- public const string SUNNY = "\U00002600";
- public const string SUNRISE = "\U0001f305";
- public const string SUNRISE_OVER_MOUNTAINS = "\U0001f304";
- public const string SUN_WITH_FACE = "\U0001f31e";
- public const string SURFER = "\U0001f3c4";
- public const string SURFER_SKIN_TONE1 = "\U0001f3c4\U0001f3fb";
- public const string SURFER_SKIN_TONE2 = "\U0001f3c4\U0001f3fc";
- public const string SURFER_SKIN_TONE3 = "\U0001f3c4\U0001f3fd";
- public const string SURFER_SKIN_TONE4 = "\U0001f3c4\U0001f3fe";
- public const string SURFER_SKIN_TONE5 = "\U0001f3c4\U0001f3ff";
- public const string SUSHI = "\U0001f363";
- public const string SUSPENSION_RAILWAY = "\U0001f69f";
- public const string SWEAT = "\U0001f613";
- public const string SWEAT_DROPS = "\U0001f4a6";
- public const string SWEAT_SMILE = "\U0001f605";
- public const string SWEET_POTATO = "\U0001f360";
- public const string SWIMMER = "\U0001f3ca";
- public const string SWIMMER_SKIN_TONE1 = "\U0001f3ca\U0001f3fb";
- public const string SWIMMER_SKIN_TONE2 = "\U0001f3ca\U0001f3fc";
- public const string SWIMMER_SKIN_TONE3 = "\U0001f3ca\U0001f3fd";
- public const string SWIMMER_SKIN_TONE4 = "\U0001f3ca\U0001f3fe";
- public const string SWIMMER_SKIN_TONE5 = "\U0001f3ca\U0001f3ff";
- public const string SYMBOLS = "\U0001f523";
- public const string SYNAGOGUE = "\U0001f54d";
- public const string SYRINGE = "\U0001f489";
- public const string TABLE_TENNIS = "\U0001f3d3";
- public const string TACO = "\U0001f32e";
- public const string TADA = "\U0001f389";
- public const string TANABATA_TREE = "\U0001f38b";
- public const string TANGERINE = "\U0001f34a";
- public const string TAURUS = "\U00002649";
- public const string TAXI = "\U0001f695";
- public const string TEA = "\U0001f375";
- public const string TELEPHONE = "\U0000260e";
- public const string TELEPHONE_RECEIVER = "\U0001f4de";
- public const string TELESCOPE = "\U0001f52d";
- public const string TENNIS = "\U0001f3be";
- public const string TENT = "\U000026fa";
- public const string THERMOMETER = "\U0001f321";
- public const string THERMOMETER_FACE = "\U0001f912";
- public const string THINKING = "\U0001f914";
- public const string THINKING_FACE = "\U0001f914";
- public const string THIRD_PLACE = "\U0001f949";
- public const string THIRD_PLACE_MEDAL = "\U0001f949";
- public const string THOUGHT_BALLOON = "\U0001f4ad";
- public const string THREE = "\U00000033\U000020e3";
- public const string THREE_BUTTON_MOUSE = "\U0001f5b1";
- public const string THUMBDOWN = "\U0001f44e";
- public const string THUMBDOWN_SKIN_TONE1 = "\U0001f44e\U0001f3fb";
- public const string THUMBDOWN_SKIN_TONE2 = "\U0001f44e\U0001f3fc";
- public const string THUMBDOWN_SKIN_TONE3 = "\U0001f44e\U0001f3fd";
- public const string THUMBDOWN_SKIN_TONE4 = "\U0001f44e\U0001f3fe";
- public const string THUMBDOWN_SKIN_TONE5 = "\U0001f44e\U0001f3ff";
- public const string THUMBSDOWN = "\U0001f44e";
- public const string THUMBSDOWN_SKIN_TONE1 = "\U0001f44e\U0001f3fb";
- public const string THUMBSDOWN_SKIN_TONE2 = "\U0001f44e\U0001f3fc";
- public const string THUMBSDOWN_SKIN_TONE3 = "\U0001f44e\U0001f3fd";
- public const string THUMBSDOWN_SKIN_TONE4 = "\U0001f44e\U0001f3fe";
- public const string THUMBSDOWN_SKIN_TONE5 = "\U0001f44e\U0001f3ff";
- public const string THUMBSUP = "\U0001f44d";
- public const string THUMBSUP_SKIN_TONE1 = "\U0001f44d\U0001f3fb";
- public const string THUMBSUP_SKIN_TONE2 = "\U0001f44d\U0001f3fc";
- public const string THUMBSUP_SKIN_TONE3 = "\U0001f44d\U0001f3fd";
- public const string THUMBSUP_SKIN_TONE4 = "\U0001f44d\U0001f3fe";
- public const string THUMBSUP_SKIN_TONE5 = "\U0001f44d\U0001f3ff";
- public const string THUMBUP = "\U0001f44d";
- public const string THUMBUP_SKIN_TONE1 = "\U0001f44d\U0001f3fb";
- public const string THUMBUP_SKIN_TONE2 = "\U0001f44d\U0001f3fc";
- public const string THUMBUP_SKIN_TONE3 = "\U0001f44d\U0001f3fd";
- public const string THUMBUP_SKIN_TONE4 = "\U0001f44d\U0001f3fe";
- public const string THUMBUP_SKIN_TONE5 = "\U0001f44d\U0001f3ff";
- public const string THUNDER_CLOUD_AND_RAIN = "\U000026c8";
- public const string THUNDER_CLOUD_RAIN = "\U000026c8";
- public const string TICKET = "\U0001f3ab";
- public const string TICKETS = "\U0001f39f";
- public const string TIGER = "\U0001f42f";
- public const string TIGER2 = "\U0001f405";
- public const string TIMER = "\U000023f2";
- public const string TIMER_CLOCK = "\U000023f2";
- public const string TIRED_FACE = "\U0001f62b";
- public const string TM = "\U00002122";
- public const string TOILET = "\U0001f6bd";
- public const string TOKYO_TOWER = "\U0001f5fc";
- public const string TOMATO = "\U0001f345";
- public const string TONGUE = "\U0001f445";
- public const string TOOLS = "\U0001f6e0";
- public const string TOP = "\U0001f51d";
- public const string TOPHAT = "\U0001f3a9";
- public const string TRACKBALL = "\U0001f5b2";
- public const string TRACK_NEXT = "\U000023ed";
- public const string TRACK_PREVIOUS = "\U000023ee";
- public const string TRACTOR = "\U0001f69c";
- public const string TRAFFIC_LIGHT = "\U0001f6a5";
- public const string TRAIN = "\U0001f68b";
- public const string TRAIN2 = "\U0001f686";
- public const string TRAM = "\U0001f68a";
- public const string TRIANGULAR_FLAG_ON_POST = "\U0001f6a9";
- public const string TRIANGULAR_RULER = "\U0001f4d0";
- public const string TRIDENT = "\U0001f531";
- public const string TRIUMPH = "\U0001f624";
- public const string TROLLEYBUS = "\U0001f68e";
- public const string TROPHY = "\U0001f3c6";
- public const string TROPICAL_DRINK = "\U0001f379";
- public const string TROPICAL_FISH = "\U0001f420";
- public const string TRUCK = "\U0001f69a";
- public const string TRUMPET = "\U0001f3ba";
- public const string TULIP = "\U0001f337";
- public const string TUMBLER_GLASS = "\U0001f943";
- public const string TURKEY = "\U0001f983";
- public const string TURTLE = "\U0001f422";
- public const string TV = "\U0001f4fa";
- public const string TWISTED_RIGHTWARDS_ARROWS = "\U0001f500";
- public const string TWO = "\U00000032\U000020e3";
- public const string TWO_HEARTS = "\U0001f495";
- public const string TWO_MEN_HOLDING_HANDS = "\U0001f46c";
- public const string TWO_WOMEN_HOLDING_HANDS = "\U0001f46d";
- public const string U5272 = "\U0001f239";
- public const string U5408 = "\U0001f234";
- public const string U55_B6 = "\U0001f23a";
- public const string U6307 = "\U0001f22f";
- public const string U6708 = "\U0001f237";
- public const string U6709 = "\U0001f236";
- public const string U6_E80 = "\U0001f235";
- public const string U7121 = "\U0001f21a";
- public const string U7533 = "\U0001f238";
- public const string U7981 = "\U0001f232";
- public const string U7_A7_A = "\U0001f233";
- public const string UMBRELLA = "\U00002614";
- public const string UMBRELLA2 = "\U00002602";
- public const string UMBRELLA_ON_GROUND = "\U000026f1";
- public const string UNAMUSED = "\U0001f612";
- public const string UNDERAGE = "\U0001f51e";
- public const string UNICORN = "\U0001f984";
- public const string UNICORN_FACE = "\U0001f984";
- public const string UNLOCK = "\U0001f513";
- public const string UP = "\U0001f199";
- public const string UPSIDE_DOWN = "\U0001f643";
- public const string UPSIDE_DOWN_FACE = "\U0001f643";
- public const string URN = "\U000026b1";
- public const string V = "\U0000270c";
- public const string VERTICAL_TRAFFIC_LIGHT = "\U0001f6a6";
- public const string VHS = "\U0001f4fc";
- public const string VIBRATION_MODE = "\U0001f4f3";
- public const string VIDEO_CAMERA = "\U0001f4f9";
- public const string VIDEO_GAME = "\U0001f3ae";
- public const string VIOLIN = "\U0001f3bb";
- public const string VIRGO = "\U0000264d";
- public const string VOLCANO = "\U0001f30b";
- public const string VOLLEYBALL = "\U0001f3d0";
- public const string VS = "\U0001f19a";
- public const string V_SKIN_TONE1 = "\U0000270c\U0001f3fb";
- public const string V_SKIN_TONE2 = "\U0000270c\U0001f3fc";
- public const string V_SKIN_TONE3 = "\U0000270c\U0001f3fd";
- public const string V_SKIN_TONE4 = "\U0000270c\U0001f3fe";
- public const string V_SKIN_TONE5 = "\U0000270c\U0001f3ff";
- public const string VULCAN = "\U0001f596";
- public const string VULCAN_SKIN_TONE1 = "\U0001f596\U0001f3fb";
- public const string VULCAN_SKIN_TONE2 = "\U0001f596\U0001f3fc";
- public const string VULCAN_SKIN_TONE3 = "\U0001f596\U0001f3fd";
- public const string VULCAN_SKIN_TONE4 = "\U0001f596\U0001f3fe";
- public const string VULCAN_SKIN_TONE5 = "\U0001f596\U0001f3ff";
- public const string WALKING = "\U0001f6b6";
- public const string WALKING_SKIN_TONE1 = "\U0001f6b6\U0001f3fb";
- public const string WALKING_SKIN_TONE2 = "\U0001f6b6\U0001f3fc";
- public const string WALKING_SKIN_TONE3 = "\U0001f6b6\U0001f3fd";
- public const string WALKING_SKIN_TONE4 = "\U0001f6b6\U0001f3fe";
- public const string WALKING_SKIN_TONE5 = "\U0001f6b6\U0001f3ff";
- public const string WANING_CRESCENT_MOON = "\U0001f318";
- public const string WANING_GIBBOUS_MOON = "\U0001f316";
- public const string WARNING = "\U000026a0";
- public const string WASTEBASKET = "\U0001f5d1";
- public const string WATCH = "\U0000231a";
- public const string WATER_BUFFALO = "\U0001f403";
- public const string WATERMELON = "\U0001f349";
- public const string WATER_POLO = "\U0001f93d";
- public const string WATER_POLO_SKIN_TONE1 = "\U0001f93d\U0001f3fb";
- public const string WATER_POLO_SKIN_TONE2 = "\U0001f93d\U0001f3fc";
- public const string WATER_POLO_SKIN_TONE3 = "\U0001f93d\U0001f3fd";
- public const string WATER_POLO_SKIN_TONE4 = "\U0001f93d\U0001f3fe";
- public const string WATER_POLO_SKIN_TONE5 = "\U0001f93d\U0001f3ff";
- public const string WAVE = "\U0001f44b";
- public const string WAVE_SKIN_TONE1 = "\U0001f44b\U0001f3fb";
- public const string WAVE_SKIN_TONE2 = "\U0001f44b\U0001f3fc";
- public const string WAVE_SKIN_TONE3 = "\U0001f44b\U0001f3fd";
- public const string WAVE_SKIN_TONE4 = "\U0001f44b\U0001f3fe";
- public const string WAVE_SKIN_TONE5 = "\U0001f44b\U0001f3ff";
- public const string WAVY_DASH = "\U00003030";
- public const string WAXING_CRESCENT_MOON = "\U0001f312";
- public const string WAXING_GIBBOUS_MOON = "\U0001f314";
- public const string WC = "\U0001f6be";
- public const string WEARY = "\U0001f629";
- public const string WEDDING = "\U0001f492";
- public const string WEIGHT_LIFTER = "\U0001f3cb";
- public const string WEIGHT_LIFTER_SKIN_TONE1 = "\U0001f3cb\U0001f3fb";
- public const string WEIGHT_LIFTER_SKIN_TONE2 = "\U0001f3cb\U0001f3fc";
- public const string WEIGHT_LIFTER_SKIN_TONE3 = "\U0001f3cb\U0001f3fd";
- public const string WEIGHT_LIFTER_SKIN_TONE4 = "\U0001f3cb\U0001f3fe";
- public const string WEIGHT_LIFTER_SKIN_TONE5 = "\U0001f3cb\U0001f3ff";
- public const string WHALE = "\U0001f433";
- public const string WHALE2 = "\U0001f40b";
- public const string WHEELCHAIR = "\U0000267f";
- public const string WHEEL_OF_DHARMA = "\U00002638";
- public const string WHISKY = "\U0001f943";
- public const string WHITE_CHECK_MARK = "\U00002705";
- public const string WHITE_CIRCLE = "\U000026aa";
- public const string WHITE_FLOWER = "\U0001f4ae";
- public const string WHITE_FROWNING_FACE = "\U00002639";
- public const string WHITE_LARGE_SQUARE = "\U00002b1c";
- public const string WHITE_MEDIUM_SMALL_SQUARE = "\U000025fd";
- public const string WHITE_MEDIUM_SQUARE = "\U000025fb";
- public const string WHITE_SMALL_SQUARE = "\U000025ab";
- public const string WHITE_SQUARE_BUTTON = "\U0001f533";
- public const string WHITE_SUN_BEHIND_CLOUD = "\U0001f325";
- public const string WHITE_SUN_BEHIND_CLOUD_WITH_RAIN = "\U0001f326";
- public const string WHITE_SUN_CLOUD = "\U0001f325";
- public const string WHITE_SUN_RAIN_CLOUD = "\U0001f326";
- public const string WHITE_SUN_SMALL_CLOUD = "\U0001f324";
- public const string WHITE_SUN_WITH_SMALL_CLOUD = "\U0001f324";
- public const string WILTED_FLOWER = "\U0001f940";
- public const string WILTED_ROSE = "\U0001f940";
- public const string WIND_BLOWING_FACE = "\U0001f32c";
- public const string WIND_CHIME = "\U0001f390";
- public const string WINE_GLASS = "\U0001f377";
- public const string WINK = "\U0001f609";
- public const string WOLF = "\U0001f43a";
- public const string WOMAN = "\U0001f469";
- public const string WOMANS_CLOTHES = "\U0001f45a";
- public const string WOMANS_HAT = "\U0001f452";
- public const string WOMAN_SKIN_TONE1 = "\U0001f469\U0001f3fb";
- public const string WOMAN_SKIN_TONE2 = "\U0001f469\U0001f3fc";
- public const string WOMAN_SKIN_TONE3 = "\U0001f469\U0001f3fd";
- public const string WOMAN_SKIN_TONE4 = "\U0001f469\U0001f3fe";
- public const string WOMAN_SKIN_TONE5 = "\U0001f469\U0001f3ff";
- public const string WOMENS = "\U0001f6ba";
- public const string WORLD_MAP = "\U0001f5fa";
- public const string WORRIED = "\U0001f61f";
- public const string WORSHIP_SYMBOL = "\U0001f6d0";
- public const string WRENCH = "\U0001f527";
- public const string WRESTLERS = "\U0001f93c";
- public const string WRESTLING = "\U0001f93c";
- public const string WRITING_HAND = "\U0000270d";
- public const string WRITING_HAND_SKIN_TONE1 = "\U0000270d\U0001f3fb";
- public const string WRITING_HAND_SKIN_TONE2 = "\U0000270d\U0001f3fc";
- public const string WRITING_HAND_SKIN_TONE3 = "\U0000270d\U0001f3fd";
- public const string WRITING_HAND_SKIN_TONE4 = "\U0000270d\U0001f3fe";
- public const string WRITING_HAND_SKIN_TONE5 = "\U0000270d\U0001f3ff";
- public const string X = "\U0000274c";
- public const string YELLOW_HEART = "\U0001f49b";
- public const string YEN = "\U0001f4b4";
- public const string YIN_YANG = "\U0000262f";
- public const string YUM = "\U0001f60b";
- public const string ZAP = "\U000026a1";
- public const string ZERO = "\U00000030\U000020e3";
- public const string ZIPPER_MOUTH = "\U0001f910";
- public const string ZIPPER_MOUTH_FACE = "\U0001f910";
- public const string ZZZ = "\U0001f4a4";
+ public const string _1 = "\U0001f44e";
+ public const string _100 = "\U0001f4af";
+ public const string _1234 = "\U0001f522";
+ public const string _1SkinTone1 = "\U0001f44e\U0001f3fb";
+ public const string _1SkinTone2 = "\U0001f44e\U0001f3fc";
+ public const string _1SkinTone3 = "\U0001f44e\U0001f3fd";
+ public const string _1SkinTone4 = "\U0001f44e\U0001f3fe";
+ public const string _1SkinTone5 = "\U0001f44e\U0001f3ff";
+ public const string _8ball = "\U0001f3b1";
+ public const string A = "\U0001f170";
+ public const string AB = "\U0001f18e";
+ public const string ABC = "\U0001f524";
+ public const string ABCD = "\U0001f521";
+ public const string ACCEPT = "\U0001f251";
+ public const string ADMISSION_TICKETS = "\U0001f39f";
+ public const string AERIAL_TRAMWAY = "\U0001f6a1";
+ public const string AIRPLANE = "\U00002708";
+ public const string AIRPLANE_ARRIVING = "\U0001f6ec";
+ public const string AIRPLANE_DEPARTURE = "\U0001f6eb";
+ public const string AIRPLANE_SMALL = "\U0001f6e9";
+ public const string ALARM_CLOCK = "\U000023f0";
+ public const string ALEMBIC = "\U00002697";
+ public const string ALIEN = "\U0001f47d";
+ public const string AMBULANCE = "\U0001f691";
+ public const string AMPHORA = "\U0001f3fa";
+ public const string ANCHOR = "\U00002693";
+ public const string ANGEL = "\U0001f47c";
+ public const string ANGEL_SKIN_TONE1 = "\U0001f47c\U0001f3fb";
+ public const string ANGEL_SKIN_TONE2 = "\U0001f47c\U0001f3fc";
+ public const string ANGEL_SKIN_TONE3 = "\U0001f47c\U0001f3fd";
+ public const string ANGEL_SKIN_TONE4 = "\U0001f47c\U0001f3fe";
+ public const string ANGEL_SKIN_TONE5 = "\U0001f47c\U0001f3ff";
+ public const string ANGER = "\U0001f4a2";
+ public const string ANGER_RIGHT = "\U0001f5ef";
+ public const string ANGRY = "\U0001f620";
+ public const string ANGUISHED = "\U0001f627";
+ public const string ANT = "\U0001f41c";
+ public const string APPLE = "\U0001f34e";
+ public const string AQUARIUS = "\U00002652";
+ public const string ARCHERY = "\U0001f3f9";
+ public const string ARIES = "\U00002648";
+ public const string ARROW_BACKWARD = "\U000025c0";
+ public const string ARROW_DOUBLE_DOWN = "\U000023ec";
+ public const string ARROW_DOUBLE_UP = "\U000023eb";
+ public const string ARROW_DOWN = "\U00002b07";
+ public const string ARROW_DOWN_SMALL = "\U0001f53d";
+ public const string ARROW_FORWARD = "\U000025b6";
+ public const string ARROW_HEADING_DOWN = "\U00002935";
+ public const string ARROW_HEADING_UP = "\U00002934";
+ public const string ARROW_LEFT = "\U00002b05";
+ public const string ARROW_LOWER_LEFT = "\U00002199";
+ public const string ARROW_LOWER_RIGHT = "\U00002198";
+ public const string ARROW_RIGHT = "\U000027a1";
+ public const string ARROW_RIGHT_HOOK = "\U000021aa";
+ public const string ARROWS_CLOCKWISE = "\U0001f503";
+ public const string ARROWS_COUNTERCLOCKWISE = "\U0001f504";
+ public const string ARROW_UP = "\U00002b06";
+ public const string ARROW_UP_DOWN = "\U00002195";
+ public const string ARROW_UPPER_LEFT = "\U00002196";
+ public const string ARROW_UPPER_RIGHT = "\U00002197";
+ public const string ARROW_UP_SMALL = "\U0001f53c";
+ public const string ART = "\U0001f3a8";
+ public const string ARTICULATED_LORRY = "\U0001f69b";
+ public const string ASTERISK = "\U0000002a\U000020e3";
+ public const string ASTONISHED = "\U0001f632";
+ public const string ATHLETIC_SHOE = "\U0001f45f";
+ public const string ATM = "\U0001f3e7";
+ public const string ATOM = "\U0000269b";
+ public const string ATOM_SYMBOL = "\U0000269b";
+ public const string AVOCADO = "\U0001f951";
+ public const string B = "\U0001f171";
+ public const string BABY = "\U0001f476";
+ public const string BABY_BOTTLE = "\U0001f37c";
+ public const string BABY_CHICK = "\U0001f424";
+ public const string BABY_SKIN_TONE1 = "\U0001f476\U0001f3fb";
+ public const string BABY_SKIN_TONE2 = "\U0001f476\U0001f3fc";
+ public const string BABY_SKIN_TONE3 = "\U0001f476\U0001f3fd";
+ public const string BABY_SKIN_TONE4 = "\U0001f476\U0001f3fe";
+ public const string BABY_SKIN_TONE5 = "\U0001f476\U0001f3ff";
+ public const string BABY_SYMBOL = "\U0001f6bc";
+ public const string BACK = "\U0001f519";
+ public const string BACK_OF_HAND = "\U0001f91a";
+ public const string BACK_OF_HAND_SKIN_TONE1 = "\U0001f91a\U0001f3fb";
+ public const string BACK_OF_HAND_SKIN_TONE2 = "\U0001f91a\U0001f3fc";
+ public const string BACK_OF_HAND_SKIN_TONE3 = "\U0001f91a\U0001f3fd";
+ public const string BACK_OF_HAND_SKIN_TONE4 = "\U0001f91a\U0001f3fe";
+ public const string BACK_OF_HAND_SKIN_TONE5 = "\U0001f91a\U0001f3ff";
+ public const string BACON = "\U0001f953";
+ public const string BADMINTON = "\U0001f3f8";
+ public const string BAGGAGE_CLAIM = "\U0001f6c4";
+ public const string BAGUETTE_BREAD = "\U0001f956";
+ public const string BALLOON = "\U0001f388";
+ public const string BALLOT_BOX = "\U0001f5f3";
+ public const string BALLOT_BOX_WITH_BALLOT = "\U0001f5f3";
+ public const string BALLOT_BOX_WITH_CHECK = "\U00002611";
+ public const string BAMBOO = "\U0001f38d";
+ public const string BANANA = "\U0001f34c";
+ public const string BANGBANG = "\U0000203c";
+ public const string BANK = "\U0001f3e6";
+ public const string BARBER = "\U0001f488";
+ public const string BAR_CHART = "\U0001f4ca";
+ public const string BASEBALL = "\U000026be";
+ public const string BASKETBALL = "\U0001f3c0";
+ public const string BASKETBALL_PLAYER = "\U000026f9";
+ public const string BASKETBALL_PLAYER_SKIN_TONE1 = "\U000026f9\U0001f3fb";
+ public const string BASKETBALL_PLAYER_SKIN_TONE2 = "\U000026f9\U0001f3fc";
+ public const string BASKETBALL_PLAYER_SKIN_TONE3 = "\U000026f9\U0001f3fd";
+ public const string BASKETBALL_PLAYER_SKIN_TONE4 = "\U000026f9\U0001f3fe";
+ public const string BASKETBALL_PLAYER_SKIN_TONE5 = "\U000026f9\U0001f3ff";
+ public const string BAT = "\U0001f987";
+ public const string BATH = "\U0001f6c0";
+ public const string BATH_SKIN_TONE1 = "\U0001f6c0\U0001f3fb";
+ public const string BATH_SKIN_TONE2 = "\U0001f6c0\U0001f3fc";
+ public const string BATH_SKIN_TONE3 = "\U0001f6c0\U0001f3fd";
+ public const string BATH_SKIN_TONE4 = "\U0001f6c0\U0001f3fe";
+ public const string BATH_SKIN_TONE5 = "\U0001f6c0\U0001f3ff";
+ public const string BATHTUB = "\U0001f6c1";
+ public const string BATTERY = "\U0001f50b";
+ public const string BEACH = "\U0001f3d6";
+ public const string BEACH_UMBRELLA = "\U000026f1";
+ public const string BEACH_WITH_UMBRELLA = "\U0001f3d6";
+ public const string BEAR = "\U0001f43b";
+ public const string BED = "\U0001f6cf";
+ public const string BEE = "\U0001f41d";
+ public const string BEER = "\U0001f37a";
+ public const string BEERS = "\U0001f37b";
+ public const string BEETLE = "\U0001f41e";
+ public const string BEGINNER = "\U0001f530";
+ public const string BELL = "\U0001f514";
+ public const string BELLHOP = "\U0001f6ce";
+ public const string BELLHOP_BELL = "\U0001f6ce";
+ public const string BENTO = "\U0001f371";
+ public const string BICYCLIST = "\U0001f6b4";
+ public const string BICYCLIST_SKIN_TONE1 = "\U0001f6b4\U0001f3fb";
+ public const string BICYCLIST_SKIN_TONE2 = "\U0001f6b4\U0001f3fc";
+ public const string BICYCLIST_SKIN_TONE3 = "\U0001f6b4\U0001f3fd";
+ public const string BICYCLIST_SKIN_TONE4 = "\U0001f6b4\U0001f3fe";
+ public const string BICYCLIST_SKIN_TONE5 = "\U0001f6b4\U0001f3ff";
+ public const string BIKE = "\U0001f6b2";
+ public const string BIKINI = "\U0001f459";
+ public const string BIOHAZARD = "\U00002623";
+ public const string BIOHAZARD_SIGN = "\U00002623";
+ public const string BIRD = "\U0001f426";
+ public const string BIRTHDAY = "\U0001f382";
+ public const string BLACK_CIRCLE = "\U000026ab";
+ public const string BLACK_HEART = "\U0001f5a4";
+ public const string BLACK_JOKER = "\U0001f0cf";
+ public const string BLACK_LARGE_SQUARE = "\U00002b1b";
+ public const string BLACK_MEDIUM_SMALL_SQUARE = "\U000025fe";
+ public const string BLACK_MEDIUM_SQUARE = "\U000025fc";
+ public const string BLACK_NIB = "\U00002712";
+ public const string BLACK_SMALL_SQUARE = "\U000025aa";
+ public const string BLACK_SQUARE_BUTTON = "\U0001f532";
+ public const string BLOSSOM = "\U0001f33c";
+ public const string BLOWFISH = "\U0001f421";
+ public const string BLUE_BOOK = "\U0001f4d8";
+ public const string BLUE_CAR = "\U0001f699";
+ public const string BLUE_HEART = "\U0001f499";
+ public const string BLUSH = "\U0001f60a";
+ public const string BOAR = "\U0001f417";
+ public const string BOMB = "\U0001f4a3";
+ public const string BOOK = "\U0001f4d6";
+ public const string BOOKMARK = "\U0001f516";
+ public const string BOOKMARK_TABS = "\U0001f4d1";
+ public const string BOOKS = "\U0001f4da";
+ public const string BOOM = "\U0001f4a5";
+ public const string BOOT = "\U0001f462";
+ public const string BOTTLE_WITH_POPPING_CORK = "\U0001f37e";
+ public const string BOUQUET = "\U0001f490";
+ public const string BOW = "\U0001f647";
+ public const string BOW_AND_ARROW = "\U0001f3f9";
+ public const string BOWLING = "\U0001f3b3";
+ public const string BOW_SKIN_TONE1 = "\U0001f647\U0001f3fb";
+ public const string BOW_SKIN_TONE2 = "\U0001f647\U0001f3fc";
+ public const string BOW_SKIN_TONE3 = "\U0001f647\U0001f3fd";
+ public const string BOW_SKIN_TONE4 = "\U0001f647\U0001f3fe";
+ public const string BOW_SKIN_TONE5 = "\U0001f647\U0001f3ff";
+ public const string BOXING_GLOVE = "\U0001f94a";
+ public const string BOXING_GLOVES = "\U0001f94a";
+ public const string BOY = "\U0001f466";
+ public const string BOY_SKIN_TONE1 = "\U0001f466\U0001f3fb";
+ public const string BOY_SKIN_TONE2 = "\U0001f466\U0001f3fc";
+ public const string BOY_SKIN_TONE3 = "\U0001f466\U0001f3fd";
+ public const string BOY_SKIN_TONE4 = "\U0001f466\U0001f3fe";
+ public const string BOY_SKIN_TONE5 = "\U0001f466\U0001f3ff";
+ public const string BREAD = "\U0001f35e";
+ public const string BRIDE_WITH_VEIL = "\U0001f470";
+ public const string BRIDE_WITH_VEIL_SKIN_TONE1 = "\U0001f470\U0001f3fb";
+ public const string BRIDE_WITH_VEIL_SKIN_TONE2 = "\U0001f470\U0001f3fc";
+ public const string BRIDE_WITH_VEIL_SKIN_TONE3 = "\U0001f470\U0001f3fd";
+ public const string BRIDE_WITH_VEIL_SKIN_TONE4 = "\U0001f470\U0001f3fe";
+ public const string BRIDE_WITH_VEIL_SKIN_TONE5 = "\U0001f470\U0001f3ff";
+ public const string BRIDGE_AT_NIGHT = "\U0001f309";
+ public const string BRIEFCASE = "\U0001f4bc";
+ public const string BROKEN_HEART = "\U0001f494";
+ public const string BUG = "\U0001f41b";
+ public const string BUILDING_CONSTRUCTION = "\U0001f3d7";
+ public const string BULB = "\U0001f4a1";
+ public const string BULLETTRAIN_FRONT = "\U0001f685";
+ public const string BULLETTRAIN_SIDE = "\U0001f684";
+ public const string BURRITO = "\U0001f32f";
+ public const string BUS = "\U0001f68c";
+ public const string BUSSTOP = "\U0001f68f";
+ public const string BUST_IN_SILHOUETTE = "\U0001f464";
+ public const string BUSTS_IN_SILHOUETTE = "\U0001f465";
+ public const string BUTTERFLY = "\U0001f98b";
+ public const string CACTUS = "\U0001f335";
+ public const string CAKE = "\U0001f370";
+ public const string CALENDAR = "\U0001f4c6";
+ public const string CALENDAR_SPIRAL = "\U0001f5d3";
+ public const string CALLING = "\U0001f4f2";
+ public const string CALL_ME = "\U0001f919";
+ public const string CALL_ME_HAND = "\U0001f919";
+ public const string CALL_ME_HAND_SKIN_TONE1 = "\U0001f919\U0001f3fb";
+ public const string CALL_ME_HAND_SKIN_TONE2 = "\U0001f919\U0001f3fc";
+ public const string CALL_ME_HAND_SKIN_TONE3 = "\U0001f919\U0001f3fd";
+ public const string CALL_ME_HAND_SKIN_TONE4 = "\U0001f919\U0001f3fe";
+ public const string CALL_ME_HAND_SKIN_TONE5 = "\U0001f919\U0001f3ff";
+ public const string CALL_ME_SKIN_TONE1 = "\U0001f919\U0001f3fb";
+ public const string CALL_ME_SKIN_TONE2 = "\U0001f919\U0001f3fc";
+ public const string CALL_ME_SKIN_TONE3 = "\U0001f919\U0001f3fd";
+ public const string CALL_ME_SKIN_TONE4 = "\U0001f919\U0001f3fe";
+ public const string CALL_ME_SKIN_TONE5 = "\U0001f919\U0001f3ff";
+ public const string CAMEL = "\U0001f42b";
+ public const string CAMERA = "\U0001f4f7";
+ public const string CAMERA_WITH_FLASH = "\U0001f4f8";
+ public const string CAMPING = "\U0001f3d5";
+ public const string CANCER = "\U0000264b";
+ public const string CANDLE = "\U0001f56f";
+ public const string CANDY = "\U0001f36c";
+ public const string CANOE = "\U0001f6f6";
+ public const string CAPITAL_ABCD = "\U0001f520";
+ public const string CAPRICORN = "\U00002651";
+ public const string CARD_BOX = "\U0001f5c3";
+ public const string CARD_FILE_BOX = "\U0001f5c3";
+ public const string CARD_INDEX = "\U0001f4c7";
+ public const string CARD_INDEX_DIVIDERS = "\U0001f5c2";
+ public const string CAROUSEL_HORSE = "\U0001f3a0";
+ public const string CARROT = "\U0001f955";
+ public const string CARTWHEEL = "\U0001f938";
+ public const string CARTWHEEL_SKIN_TONE1 = "\U0001f938\U0001f3fb";
+ public const string CARTWHEEL_SKIN_TONE2 = "\U0001f938\U0001f3fc";
+ public const string CARTWHEEL_SKIN_TONE3 = "\U0001f938\U0001f3fd";
+ public const string CARTWHEEL_SKIN_TONE4 = "\U0001f938\U0001f3fe";
+ public const string CARTWHEEL_SKIN_TONE5 = "\U0001f938\U0001f3ff";
+ public const string CAT = "\U0001f431";
+ public const string CAT2 = "\U0001f408";
+ public const string CD = "\U0001f4bf";
+ public const string CHAINS = "\U000026d3";
+ public const string CHAMPAGNE = "\U0001f37e";
+ public const string CHAMPAGNE_GLASS = "\U0001f942";
+ public const string CHART = "\U0001f4b9";
+ public const string CHART_WITH_DOWNWARDS_TREND = "\U0001f4c9";
+ public const string CHART_WITH_UPWARDS_TREND = "\U0001f4c8";
+ public const string CHECKERED_FLAG = "\U0001f3c1";
+ public const string CHEESE = "\U0001f9c0";
+ public const string CHEESE_WEDGE = "\U0001f9c0";
+ public const string CHERRIES = "\U0001f352";
+ public const string CHERRY_BLOSSOM = "\U0001f338";
+ public const string CHESTNUT = "\U0001f330";
+ public const string CHICKEN = "\U0001f414";
+ public const string CHILDREN_CROSSING = "\U0001f6b8";
+ public const string CHIPMUNK = "\U0001f43f";
+ public const string CHOCOLATE_BAR = "\U0001f36b";
+ public const string CHRISTMAS_TREE = "\U0001f384";
+ public const string CHURCH = "\U000026ea";
+ public const string CINEMA = "\U0001f3a6";
+ public const string CIRCUS_TENT = "\U0001f3aa";
+ public const string CITY_DUSK = "\U0001f306";
+ public const string CITYSCAPE = "\U0001f3d9";
+ public const string CITY_SUNRISE = "\U0001f307";
+ public const string CITY_SUNSET = "\U0001f307";
+ public const string CL = "\U0001f191";
+ public const string CLAP = "\U0001f44f";
+ public const string CLAPPER = "\U0001f3ac";
+ public const string CLAP_SKIN_TONE1 = "\U0001f44f\U0001f3fb";
+ public const string CLAP_SKIN_TONE2 = "\U0001f44f\U0001f3fc";
+ public const string CLAP_SKIN_TONE3 = "\U0001f44f\U0001f3fd";
+ public const string CLAP_SKIN_TONE4 = "\U0001f44f\U0001f3fe";
+ public const string CLAP_SKIN_TONE5 = "\U0001f44f\U0001f3ff";
+ public const string CLASSICAL_BUILDING = "\U0001f3db";
+ public const string CLINKING_GLASS = "\U0001f942";
+ public const string CLIPBOARD = "\U0001f4cb";
+ public const string CLOCK = "\U0001f570";
+ public const string CLOCK1 = "\U0001f550";
+ public const string CLOCK10 = "\U0001f559";
+ public const string CLOCK1030 = "\U0001f565";
+ public const string CLOCK11 = "\U0001f55a";
+ public const string CLOCK1130 = "\U0001f566";
+ public const string CLOCK12 = "\U0001f55b";
+ public const string CLOCK1230 = "\U0001f567";
+ public const string CLOCK130 = "\U0001f55c";
+ public const string CLOCK2 = "\U0001f551";
+ public const string CLOCK230 = "\U0001f55d";
+ public const string CLOCK3 = "\U0001f552";
+ public const string CLOCK330 = "\U0001f55e";
+ public const string CLOCK4 = "\U0001f553";
+ public const string CLOCK430 = "\U0001f55f";
+ public const string CLOCK5 = "\U0001f554";
+ public const string CLOCK530 = "\U0001f560";
+ public const string CLOCK6 = "\U0001f555";
+ public const string CLOCK630 = "\U0001f561";
+ public const string CLOCK7 = "\U0001f556";
+ public const string CLOCK730 = "\U0001f562";
+ public const string CLOCK8 = "\U0001f557";
+ public const string CLOCK830 = "\U0001f563";
+ public const string CLOCK9 = "\U0001f558";
+ public const string CLOCK930 = "\U0001f564";
+ public const string CLOSED_BOOK = "\U0001f4d5";
+ public const string CLOSED_LOCK_WITH_KEY = "\U0001f510";
+ public const string CLOSED_UMBRELLA = "\U0001f302";
+ public const string CLOUD = "\U00002601";
+ public const string CLOUD_LIGHTNING = "\U0001f329";
+ public const string CLOUD_RAIN = "\U0001f327";
+ public const string CLOUD_SNOW = "\U0001f328";
+ public const string CLOUD_TORNADO = "\U0001f32a";
+ public const string CLOUD_WITH_LIGHTNING = "\U0001f329";
+ public const string CLOUD_WITH_RAIN = "\U0001f327";
+ public const string CLOUD_WITH_SNOW = "\U0001f328";
+ public const string CLOUD_WITH_TORNADO = "\U0001f32a";
+ public const string CLOWN = "\U0001f921";
+ public const string CLOWN_FACE = "\U0001f921";
+ public const string CLUBS = "\U00002663";
+ public const string COCKTAIL = "\U0001f378";
+ public const string COFFEE = "\U00002615";
+ public const string COFFIN = "\U000026b0";
+ public const string COLD_SWEAT = "\U0001f630";
+ public const string COMET = "\U00002604";
+ public const string COMPRESSION = "\U0001f5dc";
+ public const string COMPUTER = "\U0001f4bb";
+ public const string CONFETTI_BALL = "\U0001f38a";
+ public const string CONFOUNDED = "\U0001f616";
+ public const string CONFUSED = "\U0001f615";
+ public const string CONGRATULATIONS = "\U00003297";
+ public const string CONSTRUCTION = "\U0001f6a7";
+ public const string CONSTRUCTION_SITE = "\U0001f3d7";
+ public const string CONSTRUCTION_WORKER = "\U0001f477";
+ public const string CONSTRUCTION_WORKER_SKIN_TONE1 = "\U0001f477\U0001f3fb";
+ public const string CONSTRUCTION_WORKER_SKIN_TONE2 = "\U0001f477\U0001f3fc";
+ public const string CONSTRUCTION_WORKER_SKIN_TONE3 = "\U0001f477\U0001f3fd";
+ public const string CONSTRUCTION_WORKER_SKIN_TONE4 = "\U0001f477\U0001f3fe";
+ public const string CONSTRUCTION_WORKER_SKIN_TONE5 = "\U0001f477\U0001f3ff";
+ public const string CONTROL_KNOBS = "\U0001f39b";
+ public const string CONVENIENCE_STORE = "\U0001f3ea";
+ public const string COOKIE = "\U0001f36a";
+ public const string COOKING = "\U0001f373";
+ public const string COOL = "\U0001f192";
+ public const string COP = "\U0001f46e";
+ public const string COP_SKIN_TONE1 = "\U0001f46e\U0001f3fb";
+ public const string COP_SKIN_TONE2 = "\U0001f46e\U0001f3fc";
+ public const string COP_SKIN_TONE3 = "\U0001f46e\U0001f3fd";
+ public const string COP_SKIN_TONE4 = "\U0001f46e\U0001f3fe";
+ public const string COP_SKIN_TONE5 = "\U0001f46e\U0001f3ff";
+ public const string COPYRIGHT = "\U000000a9";
+ public const string CORN = "\U0001f33d";
+ public const string COUCH = "\U0001f6cb";
+ public const string COUCH_AND_LAMP = "\U0001f6cb";
+ public const string COUPLE = "\U0001f46b";
+ public const string COUPLEKISS = "\U0001f48f";
+ public const string COUPLEKISS_MM = "\U0001f468\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f48b\U0000200d\U0001f468";
+ public const string COUPLEKISS_WW = "\U0001f469\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f48b\U0000200d\U0001f469";
+ public const string COUPLE_MM = "\U0001f468\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f468";
+ public const string COUPLE_WITH_HEART = "\U0001f491";
+ public const string COUPLE_WITH_HEART_MM = "\U0001f468\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f468";
+ public const string COUPLE_WITH_HEART_WW = "\U0001f469\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f469";
+ public const string COUPLE_WW = "\U0001f469\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f469";
+ public const string COW = "\U0001f42e";
+ public const string COW2 = "\U0001f404";
+ public const string COWBOY = "\U0001f920";
+ public const string CRAB = "\U0001f980";
+ public const string CRAYON = "\U0001f58d";
+ public const string CREDIT_CARD = "\U0001f4b3";
+ public const string CRESCENT_MOON = "\U0001f319";
+ public const string CRICKET = "\U0001f3cf";
+ public const string CRICKET_BAT_BALL = "\U0001f3cf";
+ public const string CROCODILE = "\U0001f40a";
+ public const string CROISSANT = "\U0001f950";
+ public const string CROSS = "\U0000271d";
+ public const string CROSSED_FLAGS = "\U0001f38c";
+ public const string CROSSED_SWORDS = "\U00002694";
+ public const string CROWN = "\U0001f451";
+ public const string CRUISE_SHIP = "\U0001f6f3";
+ public const string CRY = "\U0001f622";
+ public const string CRYING_CAT_FACE = "\U0001f63f";
+ public const string CRYSTAL_BALL = "\U0001f52e";
+ public const string CUCUMBER = "\U0001f952";
+ public const string CUPID = "\U0001f498";
+ public const string CURLY_LOOP = "\U000027b0";
+ public const string CURRENCY_EXCHANGE = "\U0001f4b1";
+ public const string CURRY = "\U0001f35b";
+ public const string CUSTARD = "\U0001f36e";
+ public const string CUSTOMS = "\U0001f6c3";
+ public const string CYCLONE = "\U0001f300";
+ public const string DAGGER = "\U0001f5e1";
+ public const string DAGGER_KNIFE = "\U0001f5e1";
+ public const string DANCER = "\U0001f483";
+ public const string DANCERS = "\U0001f46f";
+ public const string DANCER_SKIN_TONE1 = "\U0001f483\U0001f3fb";
+ public const string DANCER_SKIN_TONE2 = "\U0001f483\U0001f3fc";
+ public const string DANCER_SKIN_TONE3 = "\U0001f483\U0001f3fd";
+ public const string DANCER_SKIN_TONE4 = "\U0001f483\U0001f3fe";
+ public const string DANCER_SKIN_TONE5 = "\U0001f483\U0001f3ff";
+ public const string DANGO = "\U0001f361";
+ public const string DARK_SUNGLASSES = "\U0001f576";
+ public const string DART = "\U0001f3af";
+ public const string DASH = "\U0001f4a8";
+ public const string DATE = "\U0001f4c5";
+ public const string DECIDUOUS_TREE = "\U0001f333";
+ public const string DEER = "\U0001f98c";
+ public const string DEPARTMENT_STORE = "\U0001f3ec";
+ public const string DERELICT_HOUSE_BUILDING = "\U0001f3da";
+ public const string DESERT = "\U0001f3dc";
+ public const string DESERT_ISLAND = "\U0001f3dd";
+ public const string DESKTOP = "\U0001f5a5";
+ public const string DESKTOP_COMPUTER = "\U0001f5a5";
+ public const string DIAMONDS = "\U00002666";
+ public const string DIAMOND_SHAPE_WITH_A_DOT_INSIDE = "\U0001f4a0";
+ public const string DISAPPOINTED = "\U0001f61e";
+ public const string DISAPPOINTED_RELIEVED = "\U0001f625";
+ public const string DIVIDERS = "\U0001f5c2";
+ public const string DIZZY = "\U0001f4ab";
+ public const string DIZZY_FACE = "\U0001f635";
+ public const string DOG = "\U0001f436";
+ public const string DOG2 = "\U0001f415";
+ public const string DOLLAR = "\U0001f4b5";
+ public const string DOLLS = "\U0001f38e";
+ public const string DOLPHIN = "\U0001f42c";
+ public const string DO_NOT_LITTER = "\U0001f6af";
+ public const string DOOR = "\U0001f6aa";
+ public const string DOUBLE_VERTICAL_BAR = "\U000023f8";
+ public const string DOUGHNUT = "\U0001f369";
+ public const string DOVE = "\U0001f54a";
+ public const string DOVE_OF_PEACE = "\U0001f54a";
+ public const string DRAGON = "\U0001f409";
+ public const string DRAGON_FACE = "\U0001f432";
+ public const string DRESS = "\U0001f457";
+ public const string DROMEDARY_CAMEL = "\U0001f42a";
+ public const string DROOL = "\U0001f924";
+ public const string DROOLING_FACE = "\U0001f924";
+ public const string DROPLET = "\U0001f4a7";
+ public const string DRUM = "\U0001f941";
+ public const string DRUM_WITH_DRUMSTICKS = "\U0001f941";
+ public const string DUCK = "\U0001f986";
+ public const string DVD = "\U0001f4c0";
+ public const string EAGLE = "\U0001f985";
+ public const string EAR = "\U0001f442";
+ public const string EAR_OF_RICE = "\U0001f33e";
+ public const string EAR_SKIN_TONE1 = "\U0001f442\U0001f3fb";
+ public const string EAR_SKIN_TONE2 = "\U0001f442\U0001f3fc";
+ public const string EAR_SKIN_TONE3 = "\U0001f442\U0001f3fd";
+ public const string EAR_SKIN_TONE4 = "\U0001f442\U0001f3fe";
+ public const string EAR_SKIN_TONE5 = "\U0001f442\U0001f3ff";
+ public const string EARTH_AFRICA = "\U0001f30d";
+ public const string EARTH_AMERICAS = "\U0001f30e";
+ public const string EARTH_ASIA = "\U0001f30f";
+ public const string EGG = "\U0001f95a";
+ public const string EGGPLANT = "\U0001f346";
+ public const string EIGHT = "\U00000038\U000020e3";
+ public const string EIGHT_POINTED_BLACK_STAR = "\U00002734";
+ public const string EIGHT_SPOKED_ASTERISK = "\U00002733";
+ public const string EJECT = "\U000023cf";
+ public const string EJECT_SYMBOL = "\U000023cf";
+ public const string ELECTRIC_PLUG = "\U0001f50c";
+ public const string ELEPHANT = "\U0001f418";
+ public const string E_MAIL = "\U0001f4e7";
+ public const string EMAIL = "\U0001f4e7";
+ public const string END = "\U0001f51a";
+ public const string ENVELOPE = "\U00002709";
+ public const string ENVELOPE_WITH_ARROW = "\U0001f4e9";
+ public const string EURO = "\U0001f4b6";
+ public const string EUROPEAN_CASTLE = "\U0001f3f0";
+ public const string EUROPEAN_POST_OFFICE = "\U0001f3e4";
+ public const string EVERGREEN_TREE = "\U0001f332";
+ public const string EXCLAMATION = "\U00002757";
+ public const string EXPECTING_WOMAN = "\U0001f930";
+ public const string EXPECTING_WOMAN_SKIN_TONE1 = "\U0001f930\U0001f3fb";
+ public const string EXPECTING_WOMAN_SKIN_TONE2 = "\U0001f930\U0001f3fc";
+ public const string EXPECTING_WOMAN_SKIN_TONE3 = "\U0001f930\U0001f3fd";
+ public const string EXPECTING_WOMAN_SKIN_TONE4 = "\U0001f930\U0001f3fe";
+ public const string EXPECTING_WOMAN_SKIN_TONE5 = "\U0001f930\U0001f3ff";
+ public const string EXPRESSIONLESS = "\U0001f611";
+ public const string EYE = "\U0001f441";
+ public const string EYEGLASSES = "\U0001f453";
+ public const string EYE_IN_SPEECH_BUBBLE = "\U0001f441\U0000200d\U0001f5e8";
+ public const string EYES = "\U0001f440";
+ public const string FACE_PALM = "\U0001f926";
+ public const string FACEPALM = "\U0001f926";
+ public const string FACE_PALM_SKIN_TONE1 = "\U0001f926\U0001f3fb";
+ public const string FACEPALM_SKIN_TONE1 = "\U0001f926\U0001f3fb";
+ public const string FACE_PALM_SKIN_TONE2 = "\U0001f926\U0001f3fc";
+ public const string FACEPALM_SKIN_TONE2 = "\U0001f926\U0001f3fc";
+ public const string FACE_PALM_SKIN_TONE3 = "\U0001f926\U0001f3fd";
+ public const string FACEPALM_SKIN_TONE3 = "\U0001f926\U0001f3fd";
+ public const string FACE_PALM_SKIN_TONE4 = "\U0001f926\U0001f3fe";
+ public const string FACEPALM_SKIN_TONE4 = "\U0001f926\U0001f3fe";
+ public const string FACE_PALM_SKIN_TONE5 = "\U0001f926\U0001f3ff";
+ public const string FACEPALM_SKIN_TONE5 = "\U0001f926\U0001f3ff";
+ public const string FACE_WITH_COWBOY_HAT = "\U0001f920";
+ public const string FACE_WITH_HEAD_BANDAGE = "\U0001f915";
+ public const string FACE_WITH_ROLLING_EYES = "\U0001f644";
+ public const string FACE_WITH_THERMOMETER = "\U0001f912";
+ public const string FACTORY = "\U0001f3ed";
+ public const string FALLEN_LEAF = "\U0001f342";
+ public const string FAMILY = "\U0001f46a";
+ public const string FAMILY_MMB = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f466";
+ public const string FAMILY_MMBB = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f466\U0000200d\U0001f466";
+ public const string FAMILY_MMG = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f467";
+ public const string FAMILY_MMGB = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f467\U0000200d\U0001f466";
+ public const string FAMILY_MMGG = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f467\U0000200d\U0001f467";
+ public const string FAMILY_MWBB = "\U0001f468\U0000200d\U0001f469\U0000200d\U0001f466\U0000200d\U0001f466";
+ public const string FAMILY_MWG = "\U0001f468\U0000200d\U0001f469\U0000200d\U0001f467";
+ public const string FAMILY_MWGB = "\U0001f468\U0000200d\U0001f469\U0000200d\U0001f467\U0000200d\U0001f466";
+ public const string FAMILY_MWGG = "\U0001f468\U0000200d\U0001f469\U0000200d\U0001f467\U0000200d\U0001f467";
+ public const string FAMILY_WWB = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f466";
+ public const string FAMILY_WWBB = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f466\U0000200d\U0001f466";
+ public const string FAMILY_WWG = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f467";
+ public const string FAMILY_WWGB = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f467\U0000200d\U0001f466";
+ public const string FAMILY_WWGG = "\U0001f469\U0000200d\U0001f469\U0000200d\U0001f467\U0000200d\U0001f467";
+ public const string FAST_FORWARD = "\U000023e9";
+ public const string FAX = "\U0001f4e0";
+ public const string FEARFUL = "\U0001f628";
+ public const string FEET = "\U0001f43e";
+ public const string FENCER = "\U0001f93a";
+ public const string FENCING = "\U0001f93a";
+ public const string FERRIS_WHEEL = "\U0001f3a1";
+ public const string FERRY = "\U000026f4";
+ public const string FIELD_HOCKEY = "\U0001f3d1";
+ public const string FILE_CABINET = "\U0001f5c4";
+ public const string FILE_FOLDER = "\U0001f4c1";
+ public const string FILM_FRAMES = "\U0001f39e";
+ public const string FILM_PROJECTOR = "\U0001f4fd";
+ public const string FINGERS_CROSSED = "\U0001f91e";
+ public const string FINGERS_CROSSED_SKIN_TONE1 = "\U0001f91e\U0001f3fb";
+ public const string FINGERS_CROSSED_SKIN_TONE2 = "\U0001f91e\U0001f3fc";
+ public const string FINGERS_CROSSED_SKIN_TONE3 = "\U0001f91e\U0001f3fd";
+ public const string FINGERS_CROSSED_SKIN_TONE4 = "\U0001f91e\U0001f3fe";
+ public const string FINGERS_CROSSED_SKIN_TONE5 = "\U0001f91e\U0001f3ff";
+ public const string FIRE = "\U0001f525";
+ public const string FIRE_ENGINE = "\U0001f692";
+ public const string FIREWORKS = "\U0001f386";
+ public const string FIRST_PLACE = "\U0001f947";
+ public const string FIRST_PLACE_MEDAL = "\U0001f947";
+ public const string FIRST_QUARTER_MOON = "\U0001f313";
+ public const string FIRST_QUARTER_MOON_WITH_FACE = "\U0001f31b";
+ public const string FISH = "\U0001f41f";
+ public const string FISH_CAKE = "\U0001f365";
+ public const string FISHING_POLE_AND_FISH = "\U0001f3a3";
+ public const string FIST = "\U0000270a";
+ public const string FIST_SKIN_TONE1 = "\U0000270a\U0001f3fb";
+ public const string FIST_SKIN_TONE2 = "\U0000270a\U0001f3fc";
+ public const string FIST_SKIN_TONE3 = "\U0000270a\U0001f3fd";
+ public const string FIST_SKIN_TONE4 = "\U0000270a\U0001f3fe";
+ public const string FIST_SKIN_TONE5 = "\U0000270a\U0001f3ff";
+ public const string FIVE = "\U00000035\U000020e3";
+ public const string FLAG_AC = "\U0001f1e6\U0001f1e8";
+ public const string FLAG_AD = "\U0001f1e6\U0001f1e9";
+ public const string FLAG_AE = "\U0001f1e6\U0001f1ea";
+ public const string FLAG_AF = "\U0001f1e6\U0001f1eb";
+ public const string FLAG_AG = "\U0001f1e6\U0001f1ec";
+ public const string FLAG_AI = "\U0001f1e6\U0001f1ee";
+ public const string FLAG_AL = "\U0001f1e6\U0001f1f1";
+ public const string FLAG_AM = "\U0001f1e6\U0001f1f2";
+ public const string FLAG_AO = "\U0001f1e6\U0001f1f4";
+ public const string FLAG_AQ = "\U0001f1e6\U0001f1f6";
+ public const string FLAG_AR = "\U0001f1e6\U0001f1f7";
+ public const string FLAG_AS = "\U0001f1e6\U0001f1f8";
+ public const string FLAG_AT = "\U0001f1e6\U0001f1f9";
+ public const string FLAG_AU = "\U0001f1e6\U0001f1fa";
+ public const string FLAG_AW = "\U0001f1e6\U0001f1fc";
+ public const string FLAG_AX = "\U0001f1e6\U0001f1fd";
+ public const string FLAG_AZ = "\U0001f1e6\U0001f1ff";
+ public const string FLAG_BA = "\U0001f1e7\U0001f1e6";
+ public const string FLAG_BB = "\U0001f1e7\U0001f1e7";
+ public const string FLAG_BD = "\U0001f1e7\U0001f1e9";
+ public const string FLAG_BE = "\U0001f1e7\U0001f1ea";
+ public const string FLAG_BF = "\U0001f1e7\U0001f1eb";
+ public const string FLAG_BG = "\U0001f1e7\U0001f1ec";
+ public const string FLAG_BH = "\U0001f1e7\U0001f1ed";
+ public const string FLAG_BI = "\U0001f1e7\U0001f1ee";
+ public const string FLAG_BJ = "\U0001f1e7\U0001f1ef";
+ public const string FLAG_BL = "\U0001f1e7\U0001f1f1";
+ public const string FLAG_BLACK = "\U0001f3f4";
+ public const string FLAG_BM = "\U0001f1e7\U0001f1f2";
+ public const string FLAG_BN = "\U0001f1e7\U0001f1f3";
+ public const string FLAG_BO = "\U0001f1e7\U0001f1f4";
+ public const string FLAG_BQ = "\U0001f1e7\U0001f1f6";
+ public const string FLAG_BR = "\U0001f1e7\U0001f1f7";
+ public const string FLAG_BS = "\U0001f1e7\U0001f1f8";
+ public const string FLAG_BT = "\U0001f1e7\U0001f1f9";
+ public const string FLAG_BV = "\U0001f1e7\U0001f1fb";
+ public const string FLAG_BW = "\U0001f1e7\U0001f1fc";
+ public const string FLAG_BY = "\U0001f1e7\U0001f1fe";
+ public const string FLAG_BZ = "\U0001f1e7\U0001f1ff";
+ public const string FLAG_CA = "\U0001f1e8\U0001f1e6";
+ public const string FLAG_CC = "\U0001f1e8\U0001f1e8";
+ public const string FLAG_CD = "\U0001f1e8\U0001f1e9";
+ public const string FLAG_CF = "\U0001f1e8\U0001f1eb";
+ public const string FLAG_CG = "\U0001f1e8\U0001f1ec";
+ public const string FLAG_CH = "\U0001f1e8\U0001f1ed";
+ public const string FLAG_CI = "\U0001f1e8\U0001f1ee";
+ public const string FLAG_CK = "\U0001f1e8\U0001f1f0";
+ public const string FLAG_CL = "\U0001f1e8\U0001f1f1";
+ public const string FLAG_CM = "\U0001f1e8\U0001f1f2";
+ public const string FLAG_CN = "\U0001f1e8\U0001f1f3";
+ public const string FLAG_CO = "\U0001f1e8\U0001f1f4";
+ public const string FLAG_CP = "\U0001f1e8\U0001f1f5";
+ public const string FLAG_CR = "\U0001f1e8\U0001f1f7";
+ public const string FLAG_CU = "\U0001f1e8\U0001f1fa";
+ public const string FLAG_CV = "\U0001f1e8\U0001f1fb";
+ public const string FLAG_CW = "\U0001f1e8\U0001f1fc";
+ public const string FLAG_CX = "\U0001f1e8\U0001f1fd";
+ public const string FLAG_CY = "\U0001f1e8\U0001f1fe";
+ public const string FLAG_CZ = "\U0001f1e8\U0001f1ff";
+ public const string FLAG_DE = "\U0001f1e9\U0001f1ea";
+ public const string FLAG_DG = "\U0001f1e9\U0001f1ec";
+ public const string FLAG_DJ = "\U0001f1e9\U0001f1ef";
+ public const string FLAG_DK = "\U0001f1e9\U0001f1f0";
+ public const string FLAG_DM = "\U0001f1e9\U0001f1f2";
+ public const string FLAG_DO = "\U0001f1e9\U0001f1f4";
+ public const string FLAG_DZ = "\U0001f1e9\U0001f1ff";
+ public const string FLAG_EA = "\U0001f1ea\U0001f1e6";
+ public const string FLAG_EC = "\U0001f1ea\U0001f1e8";
+ public const string FLAG_EE = "\U0001f1ea\U0001f1ea";
+ public const string FLAG_EG = "\U0001f1ea\U0001f1ec";
+ public const string FLAG_EH = "\U0001f1ea\U0001f1ed";
+ public const string FLAG_ER = "\U0001f1ea\U0001f1f7";
+ public const string FLAG_ES = "\U0001f1ea\U0001f1f8";
+ public const string FLAG_ET = "\U0001f1ea\U0001f1f9";
+ public const string FLAG_EU = "\U0001f1ea\U0001f1fa";
+ public const string FLAG_FI = "\U0001f1eb\U0001f1ee";
+ public const string FLAG_FJ = "\U0001f1eb\U0001f1ef";
+ public const string FLAG_FK = "\U0001f1eb\U0001f1f0";
+ public const string FLAG_FM = "\U0001f1eb\U0001f1f2";
+ public const string FLAG_FO = "\U0001f1eb\U0001f1f4";
+ public const string FLAG_FR = "\U0001f1eb\U0001f1f7";
+ public const string FLAG_GA = "\U0001f1ec\U0001f1e6";
+ public const string FLAG_GB = "\U0001f1ec\U0001f1e7";
+ public const string FLAG_GD = "\U0001f1ec\U0001f1e9";
+ public const string FLAG_GE = "\U0001f1ec\U0001f1ea";
+ public const string FLAG_GF = "\U0001f1ec\U0001f1eb";
+ public const string FLAG_GG = "\U0001f1ec\U0001f1ec";
+ public const string FLAG_GH = "\U0001f1ec\U0001f1ed";
+ public const string FLAG_GI = "\U0001f1ec\U0001f1ee";
+ public const string FLAG_GL = "\U0001f1ec\U0001f1f1";
+ public const string FLAG_GM = "\U0001f1ec\U0001f1f2";
+ public const string FLAG_GN = "\U0001f1ec\U0001f1f3";
+ public const string FLAG_GP = "\U0001f1ec\U0001f1f5";
+ public const string FLAG_GQ = "\U0001f1ec\U0001f1f6";
+ public const string FLAG_GR = "\U0001f1ec\U0001f1f7";
+ public const string FLAG_GS = "\U0001f1ec\U0001f1f8";
+ public const string FLAG_GT = "\U0001f1ec\U0001f1f9";
+ public const string FLAG_GU = "\U0001f1ec\U0001f1fa";
+ public const string FLAG_GW = "\U0001f1ec\U0001f1fc";
+ public const string FLAG_GY = "\U0001f1ec\U0001f1fe";
+ public const string FLAG_HK = "\U0001f1ed\U0001f1f0";
+ public const string FLAG_HM = "\U0001f1ed\U0001f1f2";
+ public const string FLAG_HN = "\U0001f1ed\U0001f1f3";
+ public const string FLAG_HR = "\U0001f1ed\U0001f1f7";
+ public const string FLAG_HT = "\U0001f1ed\U0001f1f9";
+ public const string FLAG_HU = "\U0001f1ed\U0001f1fa";
+ public const string FLAG_IC = "\U0001f1ee\U0001f1e8";
+ public const string FLAG_ID = "\U0001f1ee\U0001f1e9";
+ public const string FLAG_IE = "\U0001f1ee\U0001f1ea";
+ public const string FLAG_IL = "\U0001f1ee\U0001f1f1";
+ public const string FLAG_IM = "\U0001f1ee\U0001f1f2";
+ public const string FLAG_IN = "\U0001f1ee\U0001f1f3";
+ public const string FLAG_IO = "\U0001f1ee\U0001f1f4";
+ public const string FLAG_IQ = "\U0001f1ee\U0001f1f6";
+ public const string FLAG_IR = "\U0001f1ee\U0001f1f7";
+ public const string FLAG_IS = "\U0001f1ee\U0001f1f8";
+ public const string FLAG_IT = "\U0001f1ee\U0001f1f9";
+ public const string FLAG_JE = "\U0001f1ef\U0001f1ea";
+ public const string FLAG_JM = "\U0001f1ef\U0001f1f2";
+ public const string FLAG_JO = "\U0001f1ef\U0001f1f4";
+ public const string FLAG_JP = "\U0001f1ef\U0001f1f5";
+ public const string FLAG_KE = "\U0001f1f0\U0001f1ea";
+ public const string FLAG_KG = "\U0001f1f0\U0001f1ec";
+ public const string FLAG_KH = "\U0001f1f0\U0001f1ed";
+ public const string FLAG_KI = "\U0001f1f0\U0001f1ee";
+ public const string FLAG_KM = "\U0001f1f0\U0001f1f2";
+ public const string FLAG_KN = "\U0001f1f0\U0001f1f3";
+ public const string FLAG_KP = "\U0001f1f0\U0001f1f5";
+ public const string FLAG_KR = "\U0001f1f0\U0001f1f7";
+ public const string FLAG_KW = "\U0001f1f0\U0001f1fc";
+ public const string FLAG_KY = "\U0001f1f0\U0001f1fe";
+ public const string FLAG_KZ = "\U0001f1f0\U0001f1ff";
+ public const string FLAG_LA = "\U0001f1f1\U0001f1e6";
+ public const string FLAG_LB = "\U0001f1f1\U0001f1e7";
+ public const string FLAG_LC = "\U0001f1f1\U0001f1e8";
+ public const string FLAG_LI = "\U0001f1f1\U0001f1ee";
+ public const string FLAG_LK = "\U0001f1f1\U0001f1f0";
+ public const string FLAG_LR = "\U0001f1f1\U0001f1f7";
+ public const string FLAG_LS = "\U0001f1f1\U0001f1f8";
+ public const string FLAG_LT = "\U0001f1f1\U0001f1f9";
+ public const string FLAG_LU = "\U0001f1f1\U0001f1fa";
+ public const string FLAG_LV = "\U0001f1f1\U0001f1fb";
+ public const string FLAG_LY = "\U0001f1f1\U0001f1fe";
+ public const string FLAG_MA = "\U0001f1f2\U0001f1e6";
+ public const string FLAG_MC = "\U0001f1f2\U0001f1e8";
+ public const string FLAG_MD = "\U0001f1f2\U0001f1e9";
+ public const string FLAG_ME = "\U0001f1f2\U0001f1ea";
+ public const string FLAG_MF = "\U0001f1f2\U0001f1eb";
+ public const string FLAG_MG = "\U0001f1f2\U0001f1ec";
+ public const string FLAG_MH = "\U0001f1f2\U0001f1ed";
+ public const string FLAG_MK = "\U0001f1f2\U0001f1f0";
+ public const string FLAG_ML = "\U0001f1f2\U0001f1f1";
+ public const string FLAG_MM = "\U0001f1f2\U0001f1f2";
+ public const string FLAG_MN = "\U0001f1f2\U0001f1f3";
+ public const string FLAG_MO = "\U0001f1f2\U0001f1f4";
+ public const string FLAG_MP = "\U0001f1f2\U0001f1f5";
+ public const string FLAG_MQ = "\U0001f1f2\U0001f1f6";
+ public const string FLAG_MR = "\U0001f1f2\U0001f1f7";
+ public const string FLAG_MS = "\U0001f1f2\U0001f1f8";
+ public const string FLAG_MT = "\U0001f1f2\U0001f1f9";
+ public const string FLAG_MU = "\U0001f1f2\U0001f1fa";
+ public const string FLAG_MV = "\U0001f1f2\U0001f1fb";
+ public const string FLAG_MW = "\U0001f1f2\U0001f1fc";
+ public const string FLAG_MX = "\U0001f1f2\U0001f1fd";
+ public const string FLAG_MY = "\U0001f1f2\U0001f1fe";
+ public const string FLAG_MZ = "\U0001f1f2\U0001f1ff";
+ public const string FLAG_NA = "\U0001f1f3\U0001f1e6";
+ public const string FLAG_NC = "\U0001f1f3\U0001f1e8";
+ public const string FLAG_NE = "\U0001f1f3\U0001f1ea";
+ public const string FLAG_NF = "\U0001f1f3\U0001f1eb";
+ public const string FLAG_NG = "\U0001f1f3\U0001f1ec";
+ public const string FLAG_NI = "\U0001f1f3\U0001f1ee";
+ public const string FLAG_NL = "\U0001f1f3\U0001f1f1";
+ public const string FLAG_NO = "\U0001f1f3\U0001f1f4";
+ public const string FLAG_NP = "\U0001f1f3\U0001f1f5";
+ public const string FLAG_NR = "\U0001f1f3\U0001f1f7";
+ public const string FLAG_NU = "\U0001f1f3\U0001f1fa";
+ public const string FLAG_NZ = "\U0001f1f3\U0001f1ff";
+ public const string FLAG_OM = "\U0001f1f4\U0001f1f2";
+ public const string FLAG_PA = "\U0001f1f5\U0001f1e6";
+ public const string FLAG_PE = "\U0001f1f5\U0001f1ea";
+ public const string FLAG_PF = "\U0001f1f5\U0001f1eb";
+ public const string FLAG_PG = "\U0001f1f5\U0001f1ec";
+ public const string FLAG_PH = "\U0001f1f5\U0001f1ed";
+ public const string FLAG_PK = "\U0001f1f5\U0001f1f0";
+ public const string FLAG_PL = "\U0001f1f5\U0001f1f1";
+ public const string FLAG_PM = "\U0001f1f5\U0001f1f2";
+ public const string FLAG_PN = "\U0001f1f5\U0001f1f3";
+ public const string FLAG_PR = "\U0001f1f5\U0001f1f7";
+ public const string FLAG_PS = "\U0001f1f5\U0001f1f8";
+ public const string FLAG_PT = "\U0001f1f5\U0001f1f9";
+ public const string FLAG_PW = "\U0001f1f5\U0001f1fc";
+ public const string FLAG_PY = "\U0001f1f5\U0001f1fe";
+ public const string FLAG_QA = "\U0001f1f6\U0001f1e6";
+ public const string FLAG_RE = "\U0001f1f7\U0001f1ea";
+ public const string FLAG_RO = "\U0001f1f7\U0001f1f4";
+ public const string FLAG_RS = "\U0001f1f7\U0001f1f8";
+ public const string FLAG_RU = "\U0001f1f7\U0001f1fa";
+ public const string FLAG_RW = "\U0001f1f7\U0001f1fc";
+ public const string FLAGS = "\U0001f38f";
+ public const string FLAG_SA = "\U0001f1f8\U0001f1e6";
+ public const string FLAG_SB = "\U0001f1f8\U0001f1e7";
+ public const string FLAG_SC = "\U0001f1f8\U0001f1e8";
+ public const string FLAG_SD = "\U0001f1f8\U0001f1e9";
+ public const string FLAG_SE = "\U0001f1f8\U0001f1ea";
+ public const string FLAG_SG = "\U0001f1f8\U0001f1ec";
+ public const string FLAG_SH = "\U0001f1f8\U0001f1ed";
+ public const string FLAG_SI = "\U0001f1f8\U0001f1ee";
+ public const string FLAG_SJ = "\U0001f1f8\U0001f1ef";
+ public const string FLAG_SK = "\U0001f1f8\U0001f1f0";
+ public const string FLAG_SL = "\U0001f1f8\U0001f1f1";
+ public const string FLAG_SM = "\U0001f1f8\U0001f1f2";
+ public const string FLAG_SN = "\U0001f1f8\U0001f1f3";
+ public const string FLAG_SO = "\U0001f1f8\U0001f1f4";
+ public const string FLAG_SR = "\U0001f1f8\U0001f1f7";
+ public const string FLAG_SS = "\U0001f1f8\U0001f1f8";
+ public const string FLAG_ST = "\U0001f1f8\U0001f1f9";
+ public const string FLAG_SV = "\U0001f1f8\U0001f1fb";
+ public const string FLAG_SX = "\U0001f1f8\U0001f1fd";
+ public const string FLAG_SY = "\U0001f1f8\U0001f1fe";
+ public const string FLAG_SZ = "\U0001f1f8\U0001f1ff";
+ public const string FLAG_TA = "\U0001f1f9\U0001f1e6";
+ public const string FLAG_TC = "\U0001f1f9\U0001f1e8";
+ public const string FLAG_TD = "\U0001f1f9\U0001f1e9";
+ public const string FLAG_TF = "\U0001f1f9\U0001f1eb";
+ public const string FLAG_TG = "\U0001f1f9\U0001f1ec";
+ public const string FLAG_TH = "\U0001f1f9\U0001f1ed";
+ public const string FLAG_TJ = "\U0001f1f9\U0001f1ef";
+ public const string FLAG_TK = "\U0001f1f9\U0001f1f0";
+ public const string FLAG_TL = "\U0001f1f9\U0001f1f1";
+ public const string FLAG_TM = "\U0001f1f9\U0001f1f2";
+ public const string FLAG_TN = "\U0001f1f9\U0001f1f3";
+ public const string FLAG_TO = "\U0001f1f9\U0001f1f4";
+ public const string FLAG_TR = "\U0001f1f9\U0001f1f7";
+ public const string FLAG_TT = "\U0001f1f9\U0001f1f9";
+ public const string FLAG_TV = "\U0001f1f9\U0001f1fb";
+ public const string FLAG_TW = "\U0001f1f9\U0001f1fc";
+ public const string FLAG_TZ = "\U0001f1f9\U0001f1ff";
+ public const string FLAG_UA = "\U0001f1fa\U0001f1e6";
+ public const string FLAG_UG = "\U0001f1fa\U0001f1ec";
+ public const string FLAG_UM = "\U0001f1fa\U0001f1f2";
+ public const string FLAG_US = "\U0001f1fa\U0001f1f8";
+ public const string FLAG_UY = "\U0001f1fa\U0001f1fe";
+ public const string FLAG_UZ = "\U0001f1fa\U0001f1ff";
+ public const string FLAG_VA = "\U0001f1fb\U0001f1e6";
+ public const string FLAG_VC = "\U0001f1fb\U0001f1e8";
+ public const string FLAG_VE = "\U0001f1fb\U0001f1ea";
+ public const string FLAG_VG = "\U0001f1fb\U0001f1ec";
+ public const string FLAG_VI = "\U0001f1fb\U0001f1ee";
+ public const string FLAG_VN = "\U0001f1fb\U0001f1f3";
+ public const string FLAG_VU = "\U0001f1fb\U0001f1fa";
+ public const string FLAG_WF = "\U0001f1fc\U0001f1eb";
+ public const string FLAG_WHITE = "\U0001f3f3";
+ public const string FLAG_WS = "\U0001f1fc\U0001f1f8";
+ public const string FLAG_XK = "\U0001f1fd\U0001f1f0";
+ public const string FLAG_YE = "\U0001f1fe\U0001f1ea";
+ public const string FLAG_YT = "\U0001f1fe\U0001f1f9";
+ public const string FLAG_ZA = "\U0001f1ff\U0001f1e6";
+ public const string FLAG_ZM = "\U0001f1ff\U0001f1f2";
+ public const string FLAG_ZW = "\U0001f1ff\U0001f1fc";
+ public const string FLAME = "\U0001f525";
+ public const string FLAN = "\U0001f36e";
+ public const string FLASHLIGHT = "\U0001f526";
+ public const string FLEUR_DE_LIS = "\U0000269c";
+ public const string FLOPPY_DISK = "\U0001f4be";
+ public const string FLOWER_PLAYING_CARDS = "\U0001f3b4";
+ public const string FLUSHED = "\U0001f633";
+ public const string FOG = "\U0001f32b";
+ public const string FOGGY = "\U0001f301";
+ public const string FOOTBALL = "\U0001f3c8";
+ public const string FOOTPRINTS = "\U0001f463";
+ public const string FORK_AND_KNIFE = "\U0001f374";
+ public const string FORK_AND_KNIFE_WITH_PLATE = "\U0001f37d";
+ public const string FORK_KNIFE_PLATE = "\U0001f37d";
+ public const string FOUNTAIN = "\U000026f2";
+ public const string FOUR = "\U00000034\U000020e3";
+ public const string FOUR_LEAF_CLOVER = "\U0001f340";
+ public const string FOX = "\U0001f98a";
+ public const string FOX_FACE = "\U0001f98a";
+ public const string FRAME_PHOTO = "\U0001f5bc";
+ public const string FRAME_WITH_PICTURE = "\U0001f5bc";
+ public const string FREE = "\U0001f193";
+ public const string FRENCH_BREAD = "\U0001f956";
+ public const string FRIED_SHRIMP = "\U0001f364";
+ public const string FRIES = "\U0001f35f";
+ public const string FROG = "\U0001f438";
+ public const string FROWNING = "\U0001f626";
+ public const string FROWNING2 = "\U00002639";
+ public const string FUELPUMP = "\U000026fd";
+ public const string FULL_MOON = "\U0001f315";
+ public const string FULL_MOON_WITH_FACE = "\U0001f31d";
+ public const string FUNERAL_URN = "\U000026b1";
+ public const string GAME_DIE = "\U0001f3b2";
+ public const string GAY_PRIDE_FLAG = "\U0001f3f3\U0000fe0f\U0000200d\U0001f308";
+ public const string GEAR = "\U00002699";
+ public const string GEM = "\U0001f48e";
+ public const string GEMINI = "\U0000264a";
+ public const string GHOST = "\U0001f47b";
+ public const string GIFT = "\U0001f381";
+ public const string GIFT_HEART = "\U0001f49d";
+ public const string GIRL = "\U0001f467";
+ public const string GIRL_SKIN_TONE1 = "\U0001f467\U0001f3fb";
+ public const string GIRL_SKIN_TONE2 = "\U0001f467\U0001f3fc";
+ public const string GIRL_SKIN_TONE3 = "\U0001f467\U0001f3fd";
+ public const string GIRL_SKIN_TONE4 = "\U0001f467\U0001f3fe";
+ public const string GIRL_SKIN_TONE5 = "\U0001f467\U0001f3ff";
+ public const string GLASS_OF_MILK = "\U0001f95b";
+ public const string GLOBE_WITH_MERIDIANS = "\U0001f310";
+ public const string GOAL = "\U0001f945";
+ public const string GOAL_NET = "\U0001f945";
+ public const string GOAT = "\U0001f410";
+ public const string GOLF = "\U000026f3";
+ public const string GOLFER = "\U0001f3cc";
+ public const string GOLFER_SKIN_TONE1 = "\U0001f3cc\U0001f3fb";
+ public const string GOLFER_SKIN_TONE2 = "\U0001f3cc\U0001f3fc";
+ public const string GOLFER_SKIN_TONE3 = "\U0001f3cc\U0001f3fd";
+ public const string GOLFER_SKIN_TONE4 = "\U0001f3cc\U0001f3fe";
+ public const string GOLFER_SKIN_TONE5 = "\U0001f3cc\U0001f3ff";
+ public const string GORILLA = "\U0001f98d";
+ public const string GRANDMA = "\U0001f475";
+ public const string GRANDMA_SKIN_TONE1 = "\U0001f475\U0001f3fb";
+ public const string GRANDMA_SKIN_TONE2 = "\U0001f475\U0001f3fc";
+ public const string GRANDMA_SKIN_TONE3 = "\U0001f475\U0001f3fd";
+ public const string GRANDMA_SKIN_TONE4 = "\U0001f475\U0001f3fe";
+ public const string GRANDMA_SKIN_TONE5 = "\U0001f475\U0001f3ff";
+ public const string GRAPES = "\U0001f347";
+ public const string GREEN_APPLE = "\U0001f34f";
+ public const string GREEN_BOOK = "\U0001f4d7";
+ public const string GREEN_HEART = "\U0001f49a";
+ public const string GREEN_SALAD = "\U0001f957";
+ public const string GREY_EXCLAMATION = "\U00002755";
+ public const string GREY_QUESTION = "\U00002754";
+ public const string GRIMACING = "\U0001f62c";
+ public const string GRIN = "\U0001f601";
+ public const string GRINNING = "\U0001f600";
+ public const string GUARDSMAN = "\U0001f482";
+ public const string GUARDSMAN_SKIN_TONE1 = "\U0001f482\U0001f3fb";
+ public const string GUARDSMAN_SKIN_TONE2 = "\U0001f482\U0001f3fc";
+ public const string GUARDSMAN_SKIN_TONE3 = "\U0001f482\U0001f3fd";
+ public const string GUARDSMAN_SKIN_TONE4 = "\U0001f482\U0001f3fe";
+ public const string GUARDSMAN_SKIN_TONE5 = "\U0001f482\U0001f3ff";
+ public const string GUITAR = "\U0001f3b8";
+ public const string GUN = "\U0001f52b";
+ public const string HAIRCUT = "\U0001f487";
+ public const string HAIRCUT_SKIN_TONE1 = "\U0001f487\U0001f3fb";
+ public const string HAIRCUT_SKIN_TONE2 = "\U0001f487\U0001f3fc";
+ public const string HAIRCUT_SKIN_TONE3 = "\U0001f487\U0001f3fd";
+ public const string HAIRCUT_SKIN_TONE4 = "\U0001f487\U0001f3fe";
+ public const string HAIRCUT_SKIN_TONE5 = "\U0001f487\U0001f3ff";
+ public const string HAMBURGER = "\U0001f354";
+ public const string HAMMER = "\U0001f528";
+ public const string HAMMER_AND_PICK = "\U00002692";
+ public const string HAMMER_AND_WRENCH = "\U0001f6e0";
+ public const string HAMMER_PICK = "\U00002692";
+ public const string HAMSTER = "\U0001f439";
+ public const string HANDBAG = "\U0001f45c";
+ public const string HANDBALL = "\U0001f93e";
+ public const string HANDBALL_SKIN_TONE1 = "\U0001f93e\U0001f3fb";
+ public const string HANDBALL_SKIN_TONE2 = "\U0001f93e\U0001f3fc";
+ public const string HANDBALL_SKIN_TONE3 = "\U0001f93e\U0001f3fd";
+ public const string HANDBALL_SKIN_TONE4 = "\U0001f93e\U0001f3fe";
+ public const string HANDBALL_SKIN_TONE5 = "\U0001f93e\U0001f3ff";
+ public const string HANDSHAKE = "\U0001f91d";
+ public const string HAND_SPLAYED = "\U0001f590";
+ public const string HAND_SPLAYED_SKIN_TONE1 = "\U0001f590\U0001f3fb";
+ public const string HAND_SPLAYED_SKIN_TONE2 = "\U0001f590\U0001f3fc";
+ public const string HAND_SPLAYED_SKIN_TONE3 = "\U0001f590\U0001f3fd";
+ public const string HAND_SPLAYED_SKIN_TONE4 = "\U0001f590\U0001f3fe";
+ public const string HAND_SPLAYED_SKIN_TONE5 = "\U0001f590\U0001f3ff";
+ public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED = "\U0001f91e";
+ public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE1 = "\U0001f91e\U0001f3fb";
+ public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE2 = "\U0001f91e\U0001f3fc";
+ public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE3 = "\U0001f91e\U0001f3fd";
+ public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE4 = "\U0001f91e\U0001f3fe";
+ public const string HAND_WITH_INDEX_AND_MIDDLE_FINGER_CROSSED_SKIN_TONE5 = "\U0001f91e\U0001f3ff";
+ public const string HANKEY = "\U0001f4a9";
+ public const string HASH = "\U00000023\U000020e3";
+ public const string HATCHED_CHICK = "\U0001f425";
+ public const string HATCHING_CHICK = "\U0001f423";
+ public const string HEAD_BANDAGE = "\U0001f915";
+ public const string HEADPHONES = "\U0001f3a7";
+ public const string HEAR_NO_EVIL = "\U0001f649";
+ public const string HEART = "\U00002764";
+ public const string HEARTBEAT = "\U0001f493";
+ public const string HEART_DECORATION = "\U0001f49f";
+ public const string HEART_EXCLAMATION = "\U00002763";
+ public const string HEART_EYES = "\U0001f60d";
+ public const string HEART_EYES_CAT = "\U0001f63b";
+ public const string HEARTPULSE = "\U0001f497";
+ public const string HEARTS = "\U00002665";
+ public const string HEAVY_CHECK_MARK = "\U00002714";
+ public const string HEAVY_DIVISION_SIGN = "\U00002797";
+ public const string HEAVY_DOLLAR_SIGN = "\U0001f4b2";
+ public const string HEAVY_HEART_EXCLAMATION_MARK_ORNAMENT = "\U00002763";
+ public const string HEAVY_MINUS_SIGN = "\U00002796";
+ public const string HEAVY_MULTIPLICATION_X = "\U00002716";
+ public const string HEAVY_PLUS_SIGN = "\U00002795";
+ public const string HELICOPTER = "\U0001f681";
+ public const string HELMET_WITH_CROSS = "\U000026d1";
+ public const string HELMET_WITH_WHITE_CROSS = "\U000026d1";
+ public const string HERB = "\U0001f33f";
+ public const string HIBISCUS = "\U0001f33a";
+ public const string HIGH_BRIGHTNESS = "\U0001f506";
+ public const string HIGH_HEEL = "\U0001f460";
+ public const string HOCKEY = "\U0001f3d2";
+ public const string HOLE = "\U0001f573";
+ public const string HOMES = "\U0001f3d8";
+ public const string HONEY_POT = "\U0001f36f";
+ public const string HORSE = "\U0001f434";
+ public const string HORSE_RACING = "\U0001f3c7";
+ public const string HORSE_RACING_SKIN_TONE1 = "\U0001f3c7\U0001f3fb";
+ public const string HORSE_RACING_SKIN_TONE2 = "\U0001f3c7\U0001f3fc";
+ public const string HORSE_RACING_SKIN_TONE3 = "\U0001f3c7\U0001f3fd";
+ public const string HORSE_RACING_SKIN_TONE4 = "\U0001f3c7\U0001f3fe";
+ public const string HORSE_RACING_SKIN_TONE5 = "\U0001f3c7\U0001f3ff";
+ public const string HOSPITAL = "\U0001f3e5";
+ public const string HOTDOG = "\U0001f32d";
+ public const string HOT_DOG = "\U0001f32d";
+ public const string HOTEL = "\U0001f3e8";
+ public const string HOT_PEPPER = "\U0001f336";
+ public const string HOTSPRINGS = "\U00002668";
+ public const string HOURGLASS = "\U0000231b";
+ public const string HOURGLASS_FLOWING_SAND = "\U000023f3";
+ public const string HOUSE = "\U0001f3e0";
+ public const string HOUSE_ABANDONED = "\U0001f3da";
+ public const string HOUSE_BUILDINGS = "\U0001f3d8";
+ public const string HOUSE_WITH_GARDEN = "\U0001f3e1";
+ public const string HUGGING = "\U0001f917";
+ public const string HUGGING_FACE = "\U0001f917";
+ public const string HUSHED = "\U0001f62f";
+ public const string ICECREAM = "\U0001f366";
+ public const string ICE_CREAM = "\U0001f368";
+ public const string ICE_SKATE = "\U000026f8";
+ public const string ID = "\U0001f194";
+ public const string IDEOGRAPH_ADVANTAGE = "\U0001f250";
+ public const string IMP = "\U0001f47f";
+ public const string INBOX_TRAY = "\U0001f4e5";
+ public const string INCOMING_ENVELOPE = "\U0001f4e8";
+ public const string INFORMATION_DESK_PERSON = "\U0001f481";
+ public const string INFORMATION_DESK_PERSON_SKIN_TONE1 = "\U0001f481\U0001f3fb";
+ public const string INFORMATION_DESK_PERSON_SKIN_TONE2 = "\U0001f481\U0001f3fc";
+ public const string INFORMATION_DESK_PERSON_SKIN_TONE3 = "\U0001f481\U0001f3fd";
+ public const string INFORMATION_DESK_PERSON_SKIN_TONE4 = "\U0001f481\U0001f3fe";
+ public const string INFORMATION_DESK_PERSON_SKIN_TONE5 = "\U0001f481\U0001f3ff";
+ public const string INFORMATION_SOURCE = "\U00002139";
+ public const string INNOCENT = "\U0001f607";
+ public const string INTERROBANG = "\U00002049";
+ public const string IPHONE = "\U0001f4f1";
+ public const string ISLAND = "\U0001f3dd";
+ public const string IZAKAYA_LANTERN = "\U0001f3ee";
+ public const string JACK_O_LANTERN = "\U0001f383";
+ public const string JAPAN = "\U0001f5fe";
+ public const string JAPANESE_CASTLE = "\U0001f3ef";
+ public const string JAPANESE_GOBLIN = "\U0001f47a";
+ public const string JAPANESE_OGRE = "\U0001f479";
+ public const string JEANS = "\U0001f456";
+ public const string JOY = "\U0001f602";
+ public const string JOY_CAT = "\U0001f639";
+ public const string JOYSTICK = "\U0001f579";
+ public const string JUGGLER = "\U0001f939";
+ public const string JUGGLER_SKIN_TONE1 = "\U0001f939\U0001f3fb";
+ public const string JUGGLER_SKIN_TONE2 = "\U0001f939\U0001f3fc";
+ public const string JUGGLER_SKIN_TONE3 = "\U0001f939\U0001f3fd";
+ public const string JUGGLER_SKIN_TONE4 = "\U0001f939\U0001f3fe";
+ public const string JUGGLER_SKIN_TONE5 = "\U0001f939\U0001f3ff";
+ public const string JUGGLING = "\U0001f939";
+ public const string JUGGLING_SKIN_TONE1 = "\U0001f939\U0001f3fb";
+ public const string JUGGLING_SKIN_TONE2 = "\U0001f939\U0001f3fc";
+ public const string JUGGLING_SKIN_TONE3 = "\U0001f939\U0001f3fd";
+ public const string JUGGLING_SKIN_TONE4 = "\U0001f939\U0001f3fe";
+ public const string JUGGLING_SKIN_TONE5 = "\U0001f939\U0001f3ff";
+ public const string KAABA = "\U0001f54b";
+ public const string KARATE_UNIFORM = "\U0001f94b";
+ public const string KAYAK = "\U0001f6f6";
+ public const string KEY = "\U0001f511";
+ public const string KEY2 = "\U0001f5dd";
+ public const string KEYBOARD = "\U00002328";
+ public const string KEYCAP_ASTERISK = "\U0000002a\U000020e3";
+ public const string KEYCAP_TEN = "\U0001f51f";
+ public const string KIMONO = "\U0001f458";
+ public const string KISS = "\U0001f48b";
+ public const string KISSING = "\U0001f617";
+ public const string KISSING_CAT = "\U0001f63d";
+ public const string KISSING_CLOSED_EYES = "\U0001f61a";
+ public const string KISSING_HEART = "\U0001f618";
+ public const string KISSING_SMILING_EYES = "\U0001f619";
+ public const string KISS_MM = "\U0001f468\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f48b\U0000200d\U0001f468";
+ public const string KISS_WW = "\U0001f469\U0000200d\U00002764\U0000fe0f\U0000200d\U0001f48b\U0000200d\U0001f469";
+ public const string KIWI = "\U0001f95d";
+ public const string KIWIFRUIT = "\U0001f95d";
+ public const string KNIFE = "\U0001f52a";
+ public const string KOALA = "\U0001f428";
+ public const string KOKO = "\U0001f201";
+ public const string LABEL = "\U0001f3f7";
+ public const string LARGE_BLUE_CIRCLE = "\U0001f535";
+ public const string LARGE_BLUE_DIAMOND = "\U0001f537";
+ public const string LARGE_ORANGE_DIAMOND = "\U0001f536";
+ public const string LAST_QUARTER_MOON = "\U0001f317";
+ public const string LAST_QUARTER_MOON_WITH_FACE = "\U0001f31c";
+ public const string LATIN_CROSS = "\U0000271d";
+ public const string LAUGHING = "\U0001f606";
+ public const string LEAVES = "\U0001f343";
+ public const string LEDGER = "\U0001f4d2";
+ public const string LEFT_FACING_FIST = "\U0001f91b";
+ public const string LEFT_FACING_FIST_SKIN_TONE1 = "\U0001f91b\U0001f3fb";
+ public const string LEFT_FACING_FIST_SKIN_TONE2 = "\U0001f91b\U0001f3fc";
+ public const string LEFT_FACING_FIST_SKIN_TONE3 = "\U0001f91b\U0001f3fd";
+ public const string LEFT_FACING_FIST_SKIN_TONE4 = "\U0001f91b\U0001f3fe";
+ public const string LEFT_FACING_FIST_SKIN_TONE5 = "\U0001f91b\U0001f3ff";
+ public const string LEFT_FIST = "\U0001f91b";
+ public const string LEFT_FIST_SKIN_TONE1 = "\U0001f91b\U0001f3fb";
+ public const string LEFT_FIST_SKIN_TONE2 = "\U0001f91b\U0001f3fc";
+ public const string LEFT_FIST_SKIN_TONE3 = "\U0001f91b\U0001f3fd";
+ public const string LEFT_FIST_SKIN_TONE4 = "\U0001f91b\U0001f3fe";
+ public const string LEFT_FIST_SKIN_TONE5 = "\U0001f91b\U0001f3ff";
+ public const string LEFT_LUGGAGE = "\U0001f6c5";
+ public const string LEFT_RIGHT_ARROW = "\U00002194";
+ public const string LEFT_SPEECH_BUBBLE = "\U0001f5e8";
+ public const string LEFTWARDS_ARROW_WITH_HOOK = "\U000021a9";
+ public const string LEMON = "\U0001f34b";
+ public const string LEO = "\U0000264c";
+ public const string LEOPARD = "\U0001f406";
+ public const string LEVEL_SLIDER = "\U0001f39a";
+ public const string LEVITATE = "\U0001f574";
+ public const string LEVITATE_SKIN_TONE1 = "\U0001f574\U0001f3fb";
+ public const string LEVITATE_SKIN_TONE2 = "\U0001f574\U0001f3fc";
+ public const string LEVITATE_SKIN_TONE3 = "\U0001f574\U0001f3fd";
+ public const string LEVITATE_SKIN_TONE4 = "\U0001f574\U0001f3fe";
+ public const string LEVITATE_SKIN_TONE5 = "\U0001f574\U0001f3ff";
+ public const string LIAR = "\U0001f925";
+ public const string LIBRA = "\U0000264e";
+ public const string LIFTER = "\U0001f3cb";
+ public const string LIFTER_SKIN_TONE1 = "\U0001f3cb\U0001f3fb";
+ public const string LIFTER_SKIN_TONE2 = "\U0001f3cb\U0001f3fc";
+ public const string LIFTER_SKIN_TONE3 = "\U0001f3cb\U0001f3fd";
+ public const string LIFTER_SKIN_TONE4 = "\U0001f3cb\U0001f3fe";
+ public const string LIFTER_SKIN_TONE5 = "\U0001f3cb\U0001f3ff";
+ public const string LIGHT_RAIL = "\U0001f688";
+ public const string LINK = "\U0001f517";
+ public const string LINKED_PAPERCLIPS = "\U0001f587";
+ public const string LION = "\U0001f981";
+ public const string LION_FACE = "\U0001f981";
+ public const string LIPS = "\U0001f444";
+ public const string LIPSTICK = "\U0001f484";
+ public const string LIZARD = "\U0001f98e";
+ public const string LOCK = "\U0001f512";
+ public const string LOCK_WITH_INK_PEN = "\U0001f50f";
+ public const string LOLLIPOP = "\U0001f36d";
+ public const string LOOP = "\U000027bf";
+ public const string LOUD_SOUND = "\U0001f50a";
+ public const string LOUDSPEAKER = "\U0001f4e2";
+ public const string LOVE_HOTEL = "\U0001f3e9";
+ public const string LOVE_LETTER = "\U0001f48c";
+ public const string LOW_BRIGHTNESS = "\U0001f505";
+ public const string LOWER_LEFT_BALLPOINT_PEN = "\U0001f58a";
+ public const string LOWER_LEFT_CRAYON = "\U0001f58d";
+ public const string LOWER_LEFT_FOUNTAIN_PEN = "\U0001f58b";
+ public const string LOWER_LEFT_PAINTBRUSH = "\U0001f58c";
+ public const string LYING_FACE = "\U0001f925";
+ public const string M = "\U000024c2";
+ public const string MAG = "\U0001f50d";
+ public const string MAG_RIGHT = "\U0001f50e";
+ public const string MAHJONG = "\U0001f004";
+ public const string MAILBOX = "\U0001f4eb";
+ public const string MAILBOX_CLOSED = "\U0001f4ea";
+ public const string MAILBOX_WITH_MAIL = "\U0001f4ec";
+ public const string MAILBOX_WITH_NO_MAIL = "\U0001f4ed";
+ public const string MALE_DANCER = "\U0001f57a";
+ public const string MALE_DANCER_SKIN_TONE1 = "\U0001f57a\U0001f3fb";
+ public const string MALE_DANCER_SKIN_TONE2 = "\U0001f57a\U0001f3fc";
+ public const string MALE_DANCER_SKIN_TONE3 = "\U0001f57a\U0001f3fd";
+ public const string MALE_DANCER_SKIN_TONE4 = "\U0001f57a\U0001f3fe";
+ public const string MALE_DANCER_SKIN_TONE5 = "\U0001f57a\U0001f3ff";
+ public const string MAN = "\U0001f468";
+ public const string MAN_DANCING = "\U0001f57a";
+ public const string MAN_DANCING_SKIN_TONE1 = "\U0001f57a\U0001f3fb";
+ public const string MAN_DANCING_SKIN_TONE2 = "\U0001f57a\U0001f3fc";
+ public const string MAN_DANCING_SKIN_TONE3 = "\U0001f57a\U0001f3fd";
+ public const string MAN_DANCING_SKIN_TONE4 = "\U0001f57a\U0001f3fe";
+ public const string MAN_DANCING_SKIN_TONE5 = "\U0001f57a\U0001f3ff";
+ public const string MAN_IN_BUSINESS_SUIT_LEVITATING = "\U0001f574";
+ public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE1 = "\U0001f574\U0001f3fb";
+ public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE2 = "\U0001f574\U0001f3fc";
+ public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE3 = "\U0001f574\U0001f3fd";
+ public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE4 = "\U0001f574\U0001f3fe";
+ public const string MAN_IN_BUSINESS_SUIT_LEVITATING_SKIN_TONE5 = "\U0001f574\U0001f3ff";
+ public const string MAN_IN_TUXEDO = "\U0001f935";
+ public const string MAN_IN_TUXEDO_SKIN_TONE1 = "\U0001f935\U0001f3fb";
+ public const string MAN_IN_TUXEDO_SKIN_TONE2 = "\U0001f935\U0001f3fc";
+ public const string MAN_IN_TUXEDO_SKIN_TONE3 = "\U0001f935\U0001f3fd";
+ public const string MAN_IN_TUXEDO_SKIN_TONE4 = "\U0001f935\U0001f3fe";
+ public const string MAN_IN_TUXEDO_SKIN_TONE5 = "\U0001f935\U0001f3ff";
+ public const string MAN_SKIN_TONE1 = "\U0001f468\U0001f3fb";
+ public const string MAN_SKIN_TONE2 = "\U0001f468\U0001f3fc";
+ public const string MAN_SKIN_TONE3 = "\U0001f468\U0001f3fd";
+ public const string MAN_SKIN_TONE4 = "\U0001f468\U0001f3fe";
+ public const string MAN_SKIN_TONE5 = "\U0001f468\U0001f3ff";
+ public const string MANS_SHOE = "\U0001f45e";
+ public const string MANTLEPIECE_CLOCK = "\U0001f570";
+ public const string MAN_WITH_GUA_PI_MAO = "\U0001f472";
+ public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE1 = "\U0001f472\U0001f3fb";
+ public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE2 = "\U0001f472\U0001f3fc";
+ public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE3 = "\U0001f472\U0001f3fd";
+ public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE4 = "\U0001f472\U0001f3fe";
+ public const string MAN_WITH_GUA_PI_MAO_SKIN_TONE5 = "\U0001f472\U0001f3ff";
+ public const string MAN_WITH_TURBAN = "\U0001f473";
+ public const string MAN_WITH_TURBAN_SKIN_TONE1 = "\U0001f473\U0001f3fb";
+ public const string MAN_WITH_TURBAN_SKIN_TONE2 = "\U0001f473\U0001f3fc";
+ public const string MAN_WITH_TURBAN_SKIN_TONE3 = "\U0001f473\U0001f3fd";
+ public const string MAN_WITH_TURBAN_SKIN_TONE4 = "\U0001f473\U0001f3fe";
+ public const string MAN_WITH_TURBAN_SKIN_TONE5 = "\U0001f473\U0001f3ff";
+ public const string MAP = "\U0001f5fa";
+ public const string MAPLE_LEAF = "\U0001f341";
+ public const string MARTIAL_ARTS_UNIFORM = "\U0001f94b";
+ public const string MASK = "\U0001f637";
+ public const string MASSAGE = "\U0001f486";
+ public const string MASSAGE_SKIN_TONE1 = "\U0001f486\U0001f3fb";
+ public const string MASSAGE_SKIN_TONE2 = "\U0001f486\U0001f3fc";
+ public const string MASSAGE_SKIN_TONE3 = "\U0001f486\U0001f3fd";
+ public const string MASSAGE_SKIN_TONE4 = "\U0001f486\U0001f3fe";
+ public const string MASSAGE_SKIN_TONE5 = "\U0001f486\U0001f3ff";
+ public const string MEAT_ON_BONE = "\U0001f356";
+ public const string MEDAL = "\U0001f3c5";
+ public const string MEGA = "\U0001f4e3";
+ public const string MELON = "\U0001f348";
+ public const string MENORAH = "\U0001f54e";
+ public const string MENS = "\U0001f6b9";
+ public const string METAL = "\U0001f918";
+ public const string METAL_SKIN_TONE1 = "\U0001f918\U0001f3fb";
+ public const string METAL_SKIN_TONE2 = "\U0001f918\U0001f3fc";
+ public const string METAL_SKIN_TONE3 = "\U0001f918\U0001f3fd";
+ public const string METAL_SKIN_TONE4 = "\U0001f918\U0001f3fe";
+ public const string METAL_SKIN_TONE5 = "\U0001f918\U0001f3ff";
+ public const string METRO = "\U0001f687";
+ public const string MICROPHONE = "\U0001f3a4";
+ public const string MICROPHONE2 = "\U0001f399";
+ public const string MICROSCOPE = "\U0001f52c";
+ public const string MIDDLE_FINGER = "\U0001f595";
+ public const string MIDDLE_FINGER_SKIN_TONE1 = "\U0001f595\U0001f3fb";
+ public const string MIDDLE_FINGER_SKIN_TONE2 = "\U0001f595\U0001f3fc";
+ public const string MIDDLE_FINGER_SKIN_TONE3 = "\U0001f595\U0001f3fd";
+ public const string MIDDLE_FINGER_SKIN_TONE4 = "\U0001f595\U0001f3fe";
+ public const string MIDDLE_FINGER_SKIN_TONE5 = "\U0001f595\U0001f3ff";
+ public const string MILITARY_MEDAL = "\U0001f396";
+ public const string MILK = "\U0001f95b";
+ public const string MILKY_WAY = "\U0001f30c";
+ public const string MINIBUS = "\U0001f690";
+ public const string MINIDISC = "\U0001f4bd";
+ public const string MOBILE_PHONE_OFF = "\U0001f4f4";
+ public const string MONEYBAG = "\U0001f4b0";
+ public const string MONEY_MOUTH = "\U0001f911";
+ public const string MONEY_MOUTH_FACE = "\U0001f911";
+ public const string MONEY_WITH_WINGS = "\U0001f4b8";
+ public const string MONKEY = "\U0001f412";
+ public const string MONKEY_FACE = "\U0001f435";
+ public const string MONORAIL = "\U0001f69d";
+ public const string MORTAR_BOARD = "\U0001f393";
+ public const string MOSQUE = "\U0001f54c";
+ public const string MOTHER_CHRISTMAS = "\U0001f936";
+ public const string MOTHER_CHRISTMAS_SKIN_TONE1 = "\U0001f936\U0001f3fb";
+ public const string MOTHER_CHRISTMAS_SKIN_TONE2 = "\U0001f936\U0001f3fc";
+ public const string MOTHER_CHRISTMAS_SKIN_TONE3 = "\U0001f936\U0001f3fd";
+ public const string MOTHER_CHRISTMAS_SKIN_TONE4 = "\U0001f936\U0001f3fe";
+ public const string MOTHER_CHRISTMAS_SKIN_TONE5 = "\U0001f936\U0001f3ff";
+ public const string MOTORBIKE = "\U0001f6f5";
+ public const string MOTORBOAT = "\U0001f6e5";
+ public const string MOTORCYCLE = "\U0001f3cd";
+ public const string MOTOR_SCOOTER = "\U0001f6f5";
+ public const string MOTORWAY = "\U0001f6e3";
+ public const string MOUNTAIN = "\U000026f0";
+ public const string MOUNTAIN_BICYCLIST = "\U0001f6b5";
+ public const string MOUNTAIN_BICYCLIST_SKIN_TONE1 = "\U0001f6b5\U0001f3fb";
+ public const string MOUNTAIN_BICYCLIST_SKIN_TONE2 = "\U0001f6b5\U0001f3fc";
+ public const string MOUNTAIN_BICYCLIST_SKIN_TONE3 = "\U0001f6b5\U0001f3fd";
+ public const string MOUNTAIN_BICYCLIST_SKIN_TONE4 = "\U0001f6b5\U0001f3fe";
+ public const string MOUNTAIN_BICYCLIST_SKIN_TONE5 = "\U0001f6b5\U0001f3ff";
+ public const string MOUNTAIN_CABLEWAY = "\U0001f6a0";
+ public const string MOUNTAIN_RAILWAY = "\U0001f69e";
+ public const string MOUNTAIN_SNOW = "\U0001f3d4";
+ public const string MOUNT_FUJI = "\U0001f5fb";
+ public const string MOUSE = "\U0001f42d";
+ public const string MOUSE2 = "\U0001f401";
+ public const string MOUSE_THREE_BUTTON = "\U0001f5b1";
+ public const string MOVIE_CAMERA = "\U0001f3a5";
+ public const string MOYAI = "\U0001f5ff";
+ public const string MRS_CLAUS = "\U0001f936";
+ public const string MRS_CLAUS_SKIN_TONE1 = "\U0001f936\U0001f3fb";
+ public const string MRS_CLAUS_SKIN_TONE2 = "\U0001f936\U0001f3fc";
+ public const string MRS_CLAUS_SKIN_TONE3 = "\U0001f936\U0001f3fd";
+ public const string MRS_CLAUS_SKIN_TONE4 = "\U0001f936\U0001f3fe";
+ public const string MRS_CLAUS_SKIN_TONE5 = "\U0001f936\U0001f3ff";
+ public const string MUSCLE = "\U0001f4aa";
+ public const string MUSCLE_SKIN_TONE1 = "\U0001f4aa\U0001f3fb";
+ public const string MUSCLE_SKIN_TONE2 = "\U0001f4aa\U0001f3fc";
+ public const string MUSCLE_SKIN_TONE3 = "\U0001f4aa\U0001f3fd";
+ public const string MUSCLE_SKIN_TONE4 = "\U0001f4aa\U0001f3fe";
+ public const string MUSCLE_SKIN_TONE5 = "\U0001f4aa\U0001f3ff";
+ public const string MUSHROOM = "\U0001f344";
+ public const string MUSICAL_KEYBOARD = "\U0001f3b9";
+ public const string MUSICAL_NOTE = "\U0001f3b5";
+ public const string MUSICAL_SCORE = "\U0001f3bc";
+ public const string MUTE = "\U0001f507";
+ public const string NAIL_CARE = "\U0001f485";
+ public const string NAIL_CARE_SKIN_TONE1 = "\U0001f485\U0001f3fb";
+ public const string NAIL_CARE_SKIN_TONE2 = "\U0001f485\U0001f3fc";
+ public const string NAIL_CARE_SKIN_TONE3 = "\U0001f485\U0001f3fd";
+ public const string NAIL_CARE_SKIN_TONE4 = "\U0001f485\U0001f3fe";
+ public const string NAIL_CARE_SKIN_TONE5 = "\U0001f485\U0001f3ff";
+ public const string NAME_BADGE = "\U0001f4db";
+ public const string NATIONAL_PARK = "\U0001f3de";
+ public const string NAUSEATED_FACE = "\U0001f922";
+ public const string NECKTIE = "\U0001f454";
+ public const string NEGATIVE_SQUARED_CROSS_MARK = "\U0000274e";
+ public const string NERD = "\U0001f913";
+ public const string NERD_FACE = "\U0001f913";
+ public const string NEUTRAL_FACE = "\U0001f610";
+ public const string NEW = "\U0001f195";
+ public const string NEW_MOON = "\U0001f311";
+ public const string NEW_MOON_WITH_FACE = "\U0001f31a";
+ public const string NEWSPAPER = "\U0001f4f0";
+ public const string NEWSPAPER2 = "\U0001f5de";
+ public const string NEXT_TRACK = "\U000023ed";
+ public const string NG = "\U0001f196";
+ public const string NIGHT_WITH_STARS = "\U0001f303";
+ public const string NINE = "\U00000039\U000020e3";
+ public const string NO_BELL = "\U0001f515";
+ public const string NO_BICYCLES = "\U0001f6b3";
+ public const string NO_ENTRY = "\U000026d4";
+ public const string NO_ENTRY_SIGN = "\U0001f6ab";
+ public const string NO_GOOD = "\U0001f645";
+ public const string NO_GOOD_SKIN_TONE1 = "\U0001f645\U0001f3fb";
+ public const string NO_GOOD_SKIN_TONE2 = "\U0001f645\U0001f3fc";
+ public const string NO_GOOD_SKIN_TONE3 = "\U0001f645\U0001f3fd";
+ public const string NO_GOOD_SKIN_TONE4 = "\U0001f645\U0001f3fe";
+ public const string NO_GOOD_SKIN_TONE5 = "\U0001f645\U0001f3ff";
+ public const string NO_MOBILE_PHONES = "\U0001f4f5";
+ public const string NO_MOUTH = "\U0001f636";
+ public const string NON_POTABLE_WATER = "\U0001f6b1";
+ public const string NO_PEDESTRIANS = "\U0001f6b7";
+ public const string NOSE = "\U0001f443";
+ public const string NOSE_SKIN_TONE1 = "\U0001f443\U0001f3fb";
+ public const string NOSE_SKIN_TONE2 = "\U0001f443\U0001f3fc";
+ public const string NOSE_SKIN_TONE3 = "\U0001f443\U0001f3fd";
+ public const string NOSE_SKIN_TONE4 = "\U0001f443\U0001f3fe";
+ public const string NOSE_SKIN_TONE5 = "\U0001f443\U0001f3ff";
+ public const string NO_SMOKING = "\U0001f6ad";
+ public const string NOTEBOOK = "\U0001f4d3";
+ public const string NOTEBOOK_WITH_DECORATIVE_COVER = "\U0001f4d4";
+ public const string NOTEPAD_SPIRAL = "\U0001f5d2";
+ public const string NOTES = "\U0001f3b6";
+ public const string NUT_AND_BOLT = "\U0001f529";
+ public const string O = "\U00002b55";
+ public const string O2 = "\U0001f17e";
+ public const string OCEAN = "\U0001f30a";
+ public const string OCTAGONAL_SIGN = "\U0001f6d1";
+ public const string OCTOPUS = "\U0001f419";
+ public const string ODEN = "\U0001f362";
+ public const string OFFICE = "\U0001f3e2";
+ public const string OIL = "\U0001f6e2";
+ public const string OIL_DRUM = "\U0001f6e2";
+ public const string OK = "\U0001f197";
+ public const string OK_HAND = "\U0001f44c";
+ public const string OK_HAND_SKIN_TONE1 = "\U0001f44c\U0001f3fb";
+ public const string OK_HAND_SKIN_TONE2 = "\U0001f44c\U0001f3fc";
+ public const string OK_HAND_SKIN_TONE3 = "\U0001f44c\U0001f3fd";
+ public const string OK_HAND_SKIN_TONE4 = "\U0001f44c\U0001f3fe";
+ public const string OK_HAND_SKIN_TONE5 = "\U0001f44c\U0001f3ff";
+ public const string OK_WOMAN = "\U0001f646";
+ public const string OK_WOMAN_SKIN_TONE1 = "\U0001f646\U0001f3fb";
+ public const string OK_WOMAN_SKIN_TONE2 = "\U0001f646\U0001f3fc";
+ public const string OK_WOMAN_SKIN_TONE3 = "\U0001f646\U0001f3fd";
+ public const string OK_WOMAN_SKIN_TONE4 = "\U0001f646\U0001f3fe";
+ public const string OK_WOMAN_SKIN_TONE5 = "\U0001f646\U0001f3ff";
+ public const string OLDER_MAN = "\U0001f474";
+ public const string OLDER_MAN_SKIN_TONE1 = "\U0001f474\U0001f3fb";
+ public const string OLDER_MAN_SKIN_TONE2 = "\U0001f474\U0001f3fc";
+ public const string OLDER_MAN_SKIN_TONE3 = "\U0001f474\U0001f3fd";
+ public const string OLDER_MAN_SKIN_TONE4 = "\U0001f474\U0001f3fe";
+ public const string OLDER_MAN_SKIN_TONE5 = "\U0001f474\U0001f3ff";
+ public const string OLDER_WOMAN = "\U0001f475";
+ public const string OLDER_WOMAN_SKIN_TONE1 = "\U0001f475\U0001f3fb";
+ public const string OLDER_WOMAN_SKIN_TONE2 = "\U0001f475\U0001f3fc";
+ public const string OLDER_WOMAN_SKIN_TONE3 = "\U0001f475\U0001f3fd";
+ public const string OLDER_WOMAN_SKIN_TONE4 = "\U0001f475\U0001f3fe";
+ public const string OLDER_WOMAN_SKIN_TONE5 = "\U0001f475\U0001f3ff";
+ public const string OLD_KEY = "\U0001f5dd";
+ public const string OM_SYMBOL = "\U0001f549";
+ public const string ON = "\U0001f51b";
+ public const string ONCOMING_AUTOMOBILE = "\U0001f698";
+ public const string ONCOMING_BUS = "\U0001f68d";
+ public const string ONCOMING_POLICE_CAR = "\U0001f694";
+ public const string ONCOMING_TAXI = "\U0001f696";
+ public const string ONE = "\U00000031\U000020e3";
+ public const string OPEN_FILE_FOLDER = "\U0001f4c2";
+ public const string OPEN_HANDS = "\U0001f450";
+ public const string OPEN_HANDS_SKIN_TONE1 = "\U0001f450\U0001f3fb";
+ public const string OPEN_HANDS_SKIN_TONE2 = "\U0001f450\U0001f3fc";
+ public const string OPEN_HANDS_SKIN_TONE3 = "\U0001f450\U0001f3fd";
+ public const string OPEN_HANDS_SKIN_TONE4 = "\U0001f450\U0001f3fe";
+ public const string OPEN_HANDS_SKIN_TONE5 = "\U0001f450\U0001f3ff";
+ public const string OPEN_MOUTH = "\U0001f62e";
+ public const string OPHIUCHUS = "\U000026ce";
+ public const string ORANGE_BOOK = "\U0001f4d9";
+ public const string ORTHODOX_CROSS = "\U00002626";
+ public const string OUTBOX_TRAY = "\U0001f4e4";
+ public const string OWL = "\U0001f989";
+ public const string OX = "\U0001f402";
+ public const string PACKAGE = "\U0001f4e6";
+ public const string PAELLA = "\U0001f958";
+ public const string PAGE_FACING_UP = "\U0001f4c4";
+ public const string PAGER = "\U0001f4df";
+ public const string PAGE_WITH_CURL = "\U0001f4c3";
+ public const string PAINTBRUSH = "\U0001f58c";
+ public const string PALM_TREE = "\U0001f334";
+ public const string PANCAKES = "\U0001f95e";
+ public const string PANDA_FACE = "\U0001f43c";
+ public const string PAPERCLIP = "\U0001f4ce";
+ public const string PAPERCLIPS = "\U0001f587";
+ public const string PARK = "\U0001f3de";
+ public const string PARKING = "\U0001f17f";
+ public const string PART_ALTERNATION_MARK = "\U0000303d";
+ public const string PARTLY_SUNNY = "\U000026c5";
+ public const string PASSENGER_SHIP = "\U0001f6f3";
+ public const string PASSPORT_CONTROL = "\U0001f6c2";
+ public const string PAUSE_BUTTON = "\U000023f8";
+ public const string PAW_PRINTS = "\U0001f43e";
+ public const string PEACE = "\U0000262e";
+ public const string PEACE_SYMBOL = "\U0000262e";
+ public const string PEACH = "\U0001f351";
+ public const string PEANUTS = "\U0001f95c";
+ public const string PEAR = "\U0001f350";
+ public const string PEN_BALLPOINT = "\U0001f58a";
+ public const string PENCIL = "\U0001f4dd";
+ public const string PENCIL2 = "\U0000270f";
+ public const string PEN_FOUNTAIN = "\U0001f58b";
+ public const string PENGUIN = "\U0001f427";
+ public const string PENSIVE = "\U0001f614";
+ public const string PERFORMING_ARTS = "\U0001f3ad";
+ public const string PERSEVERE = "\U0001f623";
+ public const string PERSON_DOING_CARTWHEEL = "\U0001f938";
+ public const string PERSON_DOING_CARTWHEEL_SKIN_TONE1 = "\U0001f938\U0001f3fb";
+ public const string PERSON_DOING_CARTWHEEL_SKIN_TONE2 = "\U0001f938\U0001f3fc";
+ public const string PERSON_DOING_CARTWHEEL_SKIN_TONE3 = "\U0001f938\U0001f3fd";
+ public const string PERSON_DOING_CARTWHEEL_SKIN_TONE4 = "\U0001f938\U0001f3fe";
+ public const string PERSON_DOING_CARTWHEEL_SKIN_TONE5 = "\U0001f938\U0001f3ff";
+ public const string PERSON_FROWNING = "\U0001f64d";
+ public const string PERSON_FROWNING_SKIN_TONE1 = "\U0001f64d\U0001f3fb";
+ public const string PERSON_FROWNING_SKIN_TONE2 = "\U0001f64d\U0001f3fc";
+ public const string PERSON_FROWNING_SKIN_TONE3 = "\U0001f64d\U0001f3fd";
+ public const string PERSON_FROWNING_SKIN_TONE4 = "\U0001f64d\U0001f3fe";
+ public const string PERSON_FROWNING_SKIN_TONE5 = "\U0001f64d\U0001f3ff";
+ public const string PERSON_WITH_BALL = "\U000026f9";
+ public const string PERSON_WITH_BALL_SKIN_TONE1 = "\U000026f9\U0001f3fb";
+ public const string PERSON_WITH_BALL_SKIN_TONE2 = "\U000026f9\U0001f3fc";
+ public const string PERSON_WITH_BALL_SKIN_TONE3 = "\U000026f9\U0001f3fd";
+ public const string PERSON_WITH_BALL_SKIN_TONE4 = "\U000026f9\U0001f3fe";
+ public const string PERSON_WITH_BALL_SKIN_TONE5 = "\U000026f9\U0001f3ff";
+ public const string PERSON_WITH_BLOND_HAIR = "\U0001f471";
+ public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE1 = "\U0001f471\U0001f3fb";
+ public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE2 = "\U0001f471\U0001f3fc";
+ public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE3 = "\U0001f471\U0001f3fd";
+ public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE4 = "\U0001f471\U0001f3fe";
+ public const string PERSON_WITH_BLOND_HAIR_SKIN_TONE5 = "\U0001f471\U0001f3ff";
+ public const string PERSON_WITH_POUTING_FACE = "\U0001f64e";
+ public const string PERSON_WITH_POUTING_FACE_SKIN_TONE1 = "\U0001f64e\U0001f3fb";
+ public const string PERSON_WITH_POUTING_FACE_SKIN_TONE2 = "\U0001f64e\U0001f3fc";
+ public const string PERSON_WITH_POUTING_FACE_SKIN_TONE3 = "\U0001f64e\U0001f3fd";
+ public const string PERSON_WITH_POUTING_FACE_SKIN_TONE4 = "\U0001f64e\U0001f3fe";
+ public const string PERSON_WITH_POUTING_FACE_SKIN_TONE5 = "\U0001f64e\U0001f3ff";
+ public const string PICK = "\U000026cf";
+ public const string PIG = "\U0001f437";
+ public const string PIG2 = "\U0001f416";
+ public const string PIG_NOSE = "\U0001f43d";
+ public const string PILL = "\U0001f48a";
+ public const string PINEAPPLE = "\U0001f34d";
+ public const string PING_PONG = "\U0001f3d3";
+ public const string PISCES = "\U00002653";
+ public const string PIZZA = "\U0001f355";
+ public const string PLACE_OF_WORSHIP = "\U0001f6d0";
+ public const string PLAY_PAUSE = "\U000023ef";
+ public const string PLUS1 = "\U0001f44d";
+ public const string PLUS1_SKIN_TONE1 = "\U0001f44d\U0001f3fb";
+ public const string PLUS1_SKIN_TONE2 = "\U0001f44d\U0001f3fc";
+ public const string PLUS1_SKIN_TONE3 = "\U0001f44d\U0001f3fd";
+ public const string PLUS1_SKIN_TONE4 = "\U0001f44d\U0001f3fe";
+ public const string PLUS1_SKIN_TONE5 = "\U0001f44d\U0001f3ff";
+ public const string POINT_DOWN = "\U0001f447";
+ public const string POINT_DOWN_SKIN_TONE1 = "\U0001f447\U0001f3fb";
+ public const string POINT_DOWN_SKIN_TONE2 = "\U0001f447\U0001f3fc";
+ public const string POINT_DOWN_SKIN_TONE3 = "\U0001f447\U0001f3fd";
+ public const string POINT_DOWN_SKIN_TONE4 = "\U0001f447\U0001f3fe";
+ public const string POINT_DOWN_SKIN_TONE5 = "\U0001f447\U0001f3ff";
+ public const string POINT_LEFT = "\U0001f448";
+ public const string POINT_LEFT_SKIN_TONE1 = "\U0001f448\U0001f3fb";
+ public const string POINT_LEFT_SKIN_TONE2 = "\U0001f448\U0001f3fc";
+ public const string POINT_LEFT_SKIN_TONE3 = "\U0001f448\U0001f3fd";
+ public const string POINT_LEFT_SKIN_TONE4 = "\U0001f448\U0001f3fe";
+ public const string POINT_LEFT_SKIN_TONE5 = "\U0001f448\U0001f3ff";
+ public const string POINT_RIGHT = "\U0001f449";
+ public const string POINT_RIGHT_SKIN_TONE1 = "\U0001f449\U0001f3fb";
+ public const string POINT_RIGHT_SKIN_TONE2 = "\U0001f449\U0001f3fc";
+ public const string POINT_RIGHT_SKIN_TONE3 = "\U0001f449\U0001f3fd";
+ public const string POINT_RIGHT_SKIN_TONE4 = "\U0001f449\U0001f3fe";
+ public const string POINT_RIGHT_SKIN_TONE5 = "\U0001f449\U0001f3ff";
+ public const string POINT_UP = "\U0000261d";
+ public const string POINT_UP2 = "\U0001f446";
+ public const string POINT_UP2_SKIN_TONE1 = "\U0001f446\U0001f3fb";
+ public const string POINT_UP2_SKIN_TONE2 = "\U0001f446\U0001f3fc";
+ public const string POINT_UP2_SKIN_TONE3 = "\U0001f446\U0001f3fd";
+ public const string POINT_UP2_SKIN_TONE4 = "\U0001f446\U0001f3fe";
+ public const string POINT_UP2_SKIN_TONE5 = "\U0001f446\U0001f3ff";
+ public const string POINT_UP_SKIN_TONE1 = "\U0000261d\U0001f3fb";
+ public const string POINT_UP_SKIN_TONE2 = "\U0000261d\U0001f3fc";
+ public const string POINT_UP_SKIN_TONE3 = "\U0000261d\U0001f3fd";
+ public const string POINT_UP_SKIN_TONE4 = "\U0000261d\U0001f3fe";
+ public const string POINT_UP_SKIN_TONE5 = "\U0000261d\U0001f3ff";
+ public const string POLICE_CAR = "\U0001f693";
+ public const string POO = "\U0001f4a9";
+ public const string POODLE = "\U0001f429";
+ public const string POOP = "\U0001f4a9";
+ public const string POPCORN = "\U0001f37f";
+ public const string POSTAL_HORN = "\U0001f4ef";
+ public const string POSTBOX = "\U0001f4ee";
+ public const string POST_OFFICE = "\U0001f3e3";
+ public const string POTABLE_WATER = "\U0001f6b0";
+ public const string POTATO = "\U0001f954";
+ public const string POUCH = "\U0001f45d";
+ public const string POULTRY_LEG = "\U0001f357";
+ public const string POUND = "\U0001f4b7";
+ public const string POUTING_CAT = "\U0001f63e";
+ public const string PRAY = "\U0001f64f";
+ public const string PRAYER_BEADS = "\U0001f4ff";
+ public const string PRAY_SKIN_TONE1 = "\U0001f64f\U0001f3fb";
+ public const string PRAY_SKIN_TONE2 = "\U0001f64f\U0001f3fc";
+ public const string PRAY_SKIN_TONE3 = "\U0001f64f\U0001f3fd";
+ public const string PRAY_SKIN_TONE4 = "\U0001f64f\U0001f3fe";
+ public const string PRAY_SKIN_TONE5 = "\U0001f64f\U0001f3ff";
+ public const string PREGNANT_WOMAN = "\U0001f930";
+ public const string PREGNANT_WOMAN_SKIN_TONE1 = "\U0001f930\U0001f3fb";
+ public const string PREGNANT_WOMAN_SKIN_TONE2 = "\U0001f930\U0001f3fc";
+ public const string PREGNANT_WOMAN_SKIN_TONE3 = "\U0001f930\U0001f3fd";
+ public const string PREGNANT_WOMAN_SKIN_TONE4 = "\U0001f930\U0001f3fe";
+ public const string PREGNANT_WOMAN_SKIN_TONE5 = "\U0001f930\U0001f3ff";
+ public const string PREVIOUS_TRACK = "\U000023ee";
+ public const string PRINCE = "\U0001f934";
+ public const string PRINCE_SKIN_TONE1 = "\U0001f934\U0001f3fb";
+ public const string PRINCE_SKIN_TONE2 = "\U0001f934\U0001f3fc";
+ public const string PRINCE_SKIN_TONE3 = "\U0001f934\U0001f3fd";
+ public const string PRINCE_SKIN_TONE4 = "\U0001f934\U0001f3fe";
+ public const string PRINCE_SKIN_TONE5 = "\U0001f934\U0001f3ff";
+ public const string PRINCESS = "\U0001f478";
+ public const string PRINCESS_SKIN_TONE1 = "\U0001f478\U0001f3fb";
+ public const string PRINCESS_SKIN_TONE2 = "\U0001f478\U0001f3fc";
+ public const string PRINCESS_SKIN_TONE3 = "\U0001f478\U0001f3fd";
+ public const string PRINCESS_SKIN_TONE4 = "\U0001f478\U0001f3fe";
+ public const string PRINCESS_SKIN_TONE5 = "\U0001f478\U0001f3ff";
+ public const string PRINTER = "\U0001f5a8";
+ public const string PROJECTOR = "\U0001f4fd";
+ public const string PUDDING = "\U0001f36e";
+ public const string PUNCH = "\U0001f44a";
+ public const string PUNCH_SKIN_TONE1 = "\U0001f44a\U0001f3fb";
+ public const string PUNCH_SKIN_TONE2 = "\U0001f44a\U0001f3fc";
+ public const string PUNCH_SKIN_TONE3 = "\U0001f44a\U0001f3fd";
+ public const string PUNCH_SKIN_TONE4 = "\U0001f44a\U0001f3fe";
+ public const string PUNCH_SKIN_TONE5 = "\U0001f44a\U0001f3ff";
+ public const string PURPLE_HEART = "\U0001f49c";
+ public const string PURSE = "\U0001f45b";
+ public const string PUSHPIN = "\U0001f4cc";
+ public const string PUT_LITTER_IN_ITS_PLACE = "\U0001f6ae";
+ public const string QUESTION = "\U00002753";
+ public const string RABBIT = "\U0001f430";
+ public const string RABBIT2 = "\U0001f407";
+ public const string RACE_CAR = "\U0001f3ce";
+ public const string RACEHORSE = "\U0001f40e";
+ public const string RACING_CAR = "\U0001f3ce";
+ public const string RACING_MOTORCYCLE = "\U0001f3cd";
+ public const string RADIO = "\U0001f4fb";
+ public const string RADIOACTIVE = "\U00002622";
+ public const string RADIOACTIVE_SIGN = "\U00002622";
+ public const string RADIO_BUTTON = "\U0001f518";
+ public const string RAGE = "\U0001f621";
+ public const string RAILROAD_TRACK = "\U0001f6e4";
+ public const string RAILWAY_CAR = "\U0001f683";
+ public const string RAILWAY_TRACK = "\U0001f6e4";
+ public const string RAINBOW = "\U0001f308";
+ public const string RAINBOW_FLAG = "\U0001f3f3\U0000fe0f\U0000200d\U0001f308";
+ public const string RAISED_BACK_OF_HAND = "\U0001f91a";
+ public const string RAISED_BACK_OF_HAND_SKIN_TONE1 = "\U0001f91a\U0001f3fb";
+ public const string RAISED_BACK_OF_HAND_SKIN_TONE2 = "\U0001f91a\U0001f3fc";
+ public const string RAISED_BACK_OF_HAND_SKIN_TONE3 = "\U0001f91a\U0001f3fd";
+ public const string RAISED_BACK_OF_HAND_SKIN_TONE4 = "\U0001f91a\U0001f3fe";
+ public const string RAISED_BACK_OF_HAND_SKIN_TONE5 = "\U0001f91a\U0001f3ff";
+ public const string RAISED_HAND = "\U0000270b";
+ public const string RAISED_HANDS = "\U0001f64c";
+ public const string RAISED_HAND_SKIN_TONE1 = "\U0000270b\U0001f3fb";
+ public const string RAISED_HAND_SKIN_TONE2 = "\U0000270b\U0001f3fc";
+ public const string RAISED_HAND_SKIN_TONE3 = "\U0000270b\U0001f3fd";
+ public const string RAISED_HAND_SKIN_TONE4 = "\U0000270b\U0001f3fe";
+ public const string RAISED_HAND_SKIN_TONE5 = "\U0000270b\U0001f3ff";
+ public const string RAISED_HANDS_SKIN_TONE1 = "\U0001f64c\U0001f3fb";
+ public const string RAISED_HANDS_SKIN_TONE2 = "\U0001f64c\U0001f3fc";
+ public const string RAISED_HANDS_SKIN_TONE3 = "\U0001f64c\U0001f3fd";
+ public const string RAISED_HANDS_SKIN_TONE4 = "\U0001f64c\U0001f3fe";
+ public const string RAISED_HANDS_SKIN_TONE5 = "\U0001f64c\U0001f3ff";
+ public const string RAISED_HAND_WITH_FINGERS_SPLAYED = "\U0001f590";
+ public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE1 = "\U0001f590\U0001f3fb";
+ public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE2 = "\U0001f590\U0001f3fc";
+ public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE3 = "\U0001f590\U0001f3fd";
+ public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE4 = "\U0001f590\U0001f3fe";
+ public const string RAISED_HAND_WITH_FINGERS_SPLAYED_SKIN_TONE5 = "\U0001f590\U0001f3ff";
+ public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS = "\U0001f596";
+ public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE1 = "\U0001f596\U0001f3fb";
+ public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE2 = "\U0001f596\U0001f3fc";
+ public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE3 = "\U0001f596\U0001f3fd";
+ public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE4 = "\U0001f596\U0001f3fe";
+ public const string RAISED_HAND_WITH_PART_BETWEEN_MIDDLE_AND_RING_FINGERS_SKIN_TONE5 = "\U0001f596\U0001f3ff";
+ public const string RAISING_HAND = "\U0001f64b";
+ public const string RAISING_HAND_SKIN_TONE1 = "\U0001f64b\U0001f3fb";
+ public const string RAISING_HAND_SKIN_TONE2 = "\U0001f64b\U0001f3fc";
+ public const string RAISING_HAND_SKIN_TONE3 = "\U0001f64b\U0001f3fd";
+ public const string RAISING_HAND_SKIN_TONE4 = "\U0001f64b\U0001f3fe";
+ public const string RAISING_HAND_SKIN_TONE5 = "\U0001f64b\U0001f3ff";
+ public const string RAM = "\U0001f40f";
+ public const string RAMEN = "\U0001f35c";
+ public const string RAT = "\U0001f400";
+ public const string RECORD_BUTTON = "\U000023fa";
+ public const string RECYCLE = "\U0000267b";
+ public const string RED_CAR = "\U0001f697";
+ public const string RED_CIRCLE = "\U0001f534";
+ public const string REGIONAL_INDICATOR_A = "\U0001f1e6";
+ public const string REGIONAL_INDICATOR_B = "\U0001f1e7";
+ public const string REGIONAL_INDICATOR_C = "\U0001f1e8";
+ public const string REGIONAL_INDICATOR_D = "\U0001f1e9";
+ public const string REGIONAL_INDICATOR_E = "\U0001f1ea";
+ public const string REGIONAL_INDICATOR_F = "\U0001f1eb";
+ public const string REGIONAL_INDICATOR_G = "\U0001f1ec";
+ public const string REGIONAL_INDICATOR_H = "\U0001f1ed";
+ public const string REGIONAL_INDICATOR_I = "\U0001f1ee";
+ public const string REGIONAL_INDICATOR_J = "\U0001f1ef";
+ public const string REGIONAL_INDICATOR_K = "\U0001f1f0";
+ public const string REGIONAL_INDICATOR_L = "\U0001f1f1";
+ public const string REGIONAL_INDICATOR_M = "\U0001f1f2";
+ public const string REGIONAL_INDICATOR_N = "\U0001f1f3";
+ public const string REGIONAL_INDICATOR_O = "\U0001f1f4";
+ public const string REGIONAL_INDICATOR_P = "\U0001f1f5";
+ public const string REGIONAL_INDICATOR_Q = "\U0001f1f6";
+ public const string REGIONAL_INDICATOR_R = "\U0001f1f7";
+ public const string REGIONAL_INDICATOR_S = "\U0001f1f8";
+ public const string REGIONAL_INDICATOR_T = "\U0001f1f9";
+ public const string REGIONAL_INDICATOR_U = "\U0001f1fa";
+ public const string REGIONAL_INDICATOR_V = "\U0001f1fb";
+ public const string REGIONAL_INDICATOR_W = "\U0001f1fc";
+ public const string REGIONAL_INDICATOR_X = "\U0001f1fd";
+ public const string REGIONAL_INDICATOR_Y = "\U0001f1fe";
+ public const string REGIONAL_INDICATOR_Z = "\U0001f1ff";
+ public const string REGISTERED = "\U000000ae";
+ public const string RELAXED = "\U0000263a";
+ public const string RELIEVED = "\U0001f60c";
+ public const string REMINDER_RIBBON = "\U0001f397";
+ public const string REPEAT = "\U0001f501";
+ public const string REPEAT_ONE = "\U0001f502";
+ public const string RESTROOM = "\U0001f6bb";
+ public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED = "\U0001f595";
+ public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE1 = "\U0001f595\U0001f3fb";
+ public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE2 = "\U0001f595\U0001f3fc";
+ public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE3 = "\U0001f595\U0001f3fd";
+ public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE4 = "\U0001f595\U0001f3fe";
+ public const string REVERSED_HAND_WITH_MIDDLE_FINGER_EXTENDED_SKIN_TONE5 = "\U0001f595\U0001f3ff";
+ public const string REVOLVING_HEARTS = "\U0001f49e";
+ public const string REWIND = "\U000023ea";
+ public const string RHINO = "\U0001f98f";
+ public const string RHINOCEROS = "\U0001f98f";
+ public const string RIBBON = "\U0001f380";
+ public const string RICE = "\U0001f35a";
+ public const string RICE_BALL = "\U0001f359";
+ public const string RICE_CRACKER = "\U0001f358";
+ public const string RICE_SCENE = "\U0001f391";
+ public const string RIGHT_ANGER_BUBBLE = "\U0001f5ef";
+ public const string RIGHT_FACING_FIST = "\U0001f91c";
+ public const string RIGHT_FACING_FIST_SKIN_TONE1 = "\U0001f91c\U0001f3fb";
+ public const string RIGHT_FACING_FIST_SKIN_TONE2 = "\U0001f91c\U0001f3fc";
+ public const string RIGHT_FACING_FIST_SKIN_TONE3 = "\U0001f91c\U0001f3fd";
+ public const string RIGHT_FACING_FIST_SKIN_TONE4 = "\U0001f91c\U0001f3fe";
+ public const string RIGHT_FACING_FIST_SKIN_TONE5 = "\U0001f91c\U0001f3ff";
+ public const string RIGHT_FIST = "\U0001f91c";
+ public const string RIGHT_FIST_SKIN_TONE1 = "\U0001f91c\U0001f3fb";
+ public const string RIGHT_FIST_SKIN_TONE2 = "\U0001f91c\U0001f3fc";
+ public const string RIGHT_FIST_SKIN_TONE3 = "\U0001f91c\U0001f3fd";
+ public const string RIGHT_FIST_SKIN_TONE4 = "\U0001f91c\U0001f3fe";
+ public const string RIGHT_FIST_SKIN_TONE5 = "\U0001f91c\U0001f3ff";
+ public const string RING = "\U0001f48d";
+ public const string ROBOT = "\U0001f916";
+ public const string ROBOT_FACE = "\U0001f916";
+ public const string ROCKET = "\U0001f680";
+ public const string ROFL = "\U0001f923";
+ public const string ROLLED_UP_NEWSPAPER = "\U0001f5de";
+ public const string ROLLER_COASTER = "\U0001f3a2";
+ public const string ROLLING_EYES = "\U0001f644";
+ public const string ROLLING_ON_THE_FLOOR_LAUGHING = "\U0001f923";
+ public const string ROOSTER = "\U0001f413";
+ public const string ROSE = "\U0001f339";
+ public const string ROSETTE = "\U0001f3f5";
+ public const string ROTATING_LIGHT = "\U0001f6a8";
+ public const string ROUND_PUSHPIN = "\U0001f4cd";
+ public const string ROWBOAT = "\U0001f6a3";
+ public const string ROWBOAT_SKIN_TONE1 = "\U0001f6a3\U0001f3fb";
+ public const string ROWBOAT_SKIN_TONE2 = "\U0001f6a3\U0001f3fc";
+ public const string ROWBOAT_SKIN_TONE3 = "\U0001f6a3\U0001f3fd";
+ public const string ROWBOAT_SKIN_TONE4 = "\U0001f6a3\U0001f3fe";
+ public const string ROWBOAT_SKIN_TONE5 = "\U0001f6a3\U0001f3ff";
+ public const string RUGBY_FOOTBALL = "\U0001f3c9";
+ public const string RUNNER = "\U0001f3c3";
+ public const string RUNNER_SKIN_TONE1 = "\U0001f3c3\U0001f3fb";
+ public const string RUNNER_SKIN_TONE2 = "\U0001f3c3\U0001f3fc";
+ public const string RUNNER_SKIN_TONE3 = "\U0001f3c3\U0001f3fd";
+ public const string RUNNER_SKIN_TONE4 = "\U0001f3c3\U0001f3fe";
+ public const string RUNNER_SKIN_TONE5 = "\U0001f3c3\U0001f3ff";
+ public const string RUNNING_SHIRT_WITH_SASH = "\U0001f3bd";
+ public const string SA = "\U0001f202";
+ public const string SAGITTARIUS = "\U00002650";
+ public const string SAILBOAT = "\U000026f5";
+ public const string SAKE = "\U0001f376";
+ public const string SALAD = "\U0001f957";
+ public const string SANDAL = "\U0001f461";
+ public const string SANTA = "\U0001f385";
+ public const string SANTA_SKIN_TONE1 = "\U0001f385\U0001f3fb";
+ public const string SANTA_SKIN_TONE2 = "\U0001f385\U0001f3fc";
+ public const string SANTA_SKIN_TONE3 = "\U0001f385\U0001f3fd";
+ public const string SANTA_SKIN_TONE4 = "\U0001f385\U0001f3fe";
+ public const string SANTA_SKIN_TONE5 = "\U0001f385\U0001f3ff";
+ public const string SATELLITE = "\U0001f4e1";
+ public const string SATELLITE_ORBITAL = "\U0001f6f0";
+ public const string SATISFIED = "\U0001f606";
+ public const string SAXOPHONE = "\U0001f3b7";
+ public const string SCALES = "\U00002696";
+ public const string SCHOOL = "\U0001f3eb";
+ public const string SCHOOL_SATCHEL = "\U0001f392";
+ public const string SCISSORS = "\U00002702";
+ public const string SCOOTER = "\U0001f6f4";
+ public const string SCORPION = "\U0001f982";
+ public const string SCORPIUS = "\U0000264f";
+ public const string SCREAM = "\U0001f631";
+ public const string SCREAM_CAT = "\U0001f640";
+ public const string SCROLL = "\U0001f4dc";
+ public const string SEAT = "\U0001f4ba";
+ public const string SECOND_PLACE = "\U0001f948";
+ public const string SECOND_PLACE_MEDAL = "\U0001f948";
+ public const string SECRET = "\U00003299";
+ public const string SEEDLING = "\U0001f331";
+ public const string SEE_NO_EVIL = "\U0001f648";
+ public const string SELFIE = "\U0001f933";
+ public const string SELFIE_SKIN_TONE1 = "\U0001f933\U0001f3fb";
+ public const string SELFIE_SKIN_TONE2 = "\U0001f933\U0001f3fc";
+ public const string SELFIE_SKIN_TONE3 = "\U0001f933\U0001f3fd";
+ public const string SELFIE_SKIN_TONE4 = "\U0001f933\U0001f3fe";
+ public const string SELFIE_SKIN_TONE5 = "\U0001f933\U0001f3ff";
+ public const string SEVEN = "\U00000037\U000020e3";
+ public const string SHAKING_HANDS = "\U0001f91d";
+ public const string SHALLOW_PAN_OF_FOOD = "\U0001f958";
+ public const string SHAMROCK = "\U00002618";
+ public const string SHARK = "\U0001f988";
+ public const string SHAVED_ICE = "\U0001f367";
+ public const string SHEEP = "\U0001f411";
+ public const string SHELL = "\U0001f41a";
+ public const string SHELLED_PEANUT = "\U0001f95c";
+ public const string SHIELD = "\U0001f6e1";
+ public const string SHINTO_SHRINE = "\U000026e9";
+ public const string SHIP = "\U0001f6a2";
+ public const string SHIRT = "\U0001f455";
+ public const string SHIT = "\U0001f4a9";
+ public const string SHOPPING_BAGS = "\U0001f6cd";
+ public const string SHOPPING_CART = "\U0001f6d2";
+ public const string SHOPPING_TROLLEY = "\U0001f6d2";
+ public const string SHOWER = "\U0001f6bf";
+ public const string SHRIMP = "\U0001f990";
+ public const string SHRUG = "\U0001f937";
+ public const string SHRUG_SKIN_TONE1 = "\U0001f937\U0001f3fb";
+ public const string SHRUG_SKIN_TONE2 = "\U0001f937\U0001f3fc";
+ public const string SHRUG_SKIN_TONE3 = "\U0001f937\U0001f3fd";
+ public const string SHRUG_SKIN_TONE4 = "\U0001f937\U0001f3fe";
+ public const string SHRUG_SKIN_TONE5 = "\U0001f937\U0001f3ff";
+ public const string SICK = "\U0001f922";
+ public const string SIGNAL_STRENGTH = "\U0001f4f6";
+ public const string SIGN_OF_THE_HORNS = "\U0001f918";
+ public const string SIGN_OF_THE_HORNS_SKIN_TONE1 = "\U0001f918\U0001f3fb";
+ public const string SIGN_OF_THE_HORNS_SKIN_TONE2 = "\U0001f918\U0001f3fc";
+ public const string SIGN_OF_THE_HORNS_SKIN_TONE3 = "\U0001f918\U0001f3fd";
+ public const string SIGN_OF_THE_HORNS_SKIN_TONE4 = "\U0001f918\U0001f3fe";
+ public const string SIGN_OF_THE_HORNS_SKIN_TONE5 = "\U0001f918\U0001f3ff";
+ public const string SIX = "\U00000036\U000020e3";
+ public const string SIX_POINTED_STAR = "\U0001f52f";
+ public const string SKELETON = "\U0001f480";
+ public const string SKI = "\U0001f3bf";
+ public const string SKIER = "\U000026f7";
+ public const string SKIER_SKIN_TONE1 = "\U000026f7\U0001f3fb";
+ public const string SKIER_SKIN_TONE2 = "\U000026f7\U0001f3fc";
+ public const string SKIER_SKIN_TONE3 = "\U000026f7\U0001f3fd";
+ public const string SKIER_SKIN_TONE4 = "\U000026f7\U0001f3fe";
+ public const string SKIER_SKIN_TONE5 = "\U000026f7\U0001f3ff";
+ public const string SKULL = "\U0001f480";
+ public const string SKULL_AND_CROSSBONES = "\U00002620";
+ public const string SKULL_CROSSBONES = "\U00002620";
+ public const string SLEEPING = "\U0001f634";
+ public const string SLEEPING_ACCOMMODATION = "\U0001f6cc";
+ public const string SLEEPING_ACCOMMODATION_SKIN_TONE1 = "\U0001f6cc\U0001f3fb";
+ public const string SLEEPING_ACCOMMODATION_SKIN_TONE2 = "\U0001f6cc\U0001f3fc";
+ public const string SLEEPING_ACCOMMODATION_SKIN_TONE3 = "\U0001f6cc\U0001f3fd";
+ public const string SLEEPING_ACCOMMODATION_SKIN_TONE4 = "\U0001f6cc\U0001f3fe";
+ public const string SLEEPING_ACCOMMODATION_SKIN_TONE5 = "\U0001f6cc\U0001f3ff";
+ public const string SLEEPY = "\U0001f62a";
+ public const string SLEUTH_OR_SPY = "\U0001f575";
+ public const string SLEUTH_OR_SPY_SKIN_TONE1 = "\U0001f575\U0001f3fb";
+ public const string SLEUTH_OR_SPY_SKIN_TONE2 = "\U0001f575\U0001f3fc";
+ public const string SLEUTH_OR_SPY_SKIN_TONE3 = "\U0001f575\U0001f3fd";
+ public const string SLEUTH_OR_SPY_SKIN_TONE4 = "\U0001f575\U0001f3fe";
+ public const string SLEUTH_OR_SPY_SKIN_TONE5 = "\U0001f575\U0001f3ff";
+ public const string SLIGHT_FROWN = "\U0001f641";
+ public const string SLIGHTLY_FROWNING_FACE = "\U0001f641";
+ public const string SLIGHTLY_SMILING_FACE = "\U0001f642";
+ public const string SLIGHT_SMILE = "\U0001f642";
+ public const string SLOT_MACHINE = "\U0001f3b0";
+ public const string SMALL_AIRPLANE = "\U0001f6e9";
+ public const string SMALL_BLUE_DIAMOND = "\U0001f539";
+ public const string SMALL_ORANGE_DIAMOND = "\U0001f538";
+ public const string SMALL_RED_TRIANGLE = "\U0001f53a";
+ public const string SMALL_RED_TRIANGLE_DOWN = "\U0001f53b";
+ public const string SMILE = "\U0001f604";
+ public const string SMILE_CAT = "\U0001f638";
+ public const string SMILEY = "\U0001f603";
+ public const string SMILEY_CAT = "\U0001f63a";
+ public const string SMILING_IMP = "\U0001f608";
+ public const string SMIRK = "\U0001f60f";
+ public const string SMIRK_CAT = "\U0001f63c";
+ public const string SMOKING = "\U0001f6ac";
+ public const string SNAIL = "\U0001f40c";
+ public const string SNAKE = "\U0001f40d";
+ public const string SNEEZE = "\U0001f927";
+ public const string SNEEZING_FACE = "\U0001f927";
+ public const string SNOWBOARDER = "\U0001f3c2";
+ public const string SNOWBOARDER_SKIN_TONE1 = "\U0001f3c2\U0001f3fb";
+ public const string SNOWBOARDER_SKIN_TONE2 = "\U0001f3c2\U0001f3fc";
+ public const string SNOWBOARDER_SKIN_TONE3 = "\U0001f3c2\U0001f3fd";
+ public const string SNOWBOARDER_SKIN_TONE4 = "\U0001f3c2\U0001f3fe";
+ public const string SNOWBOARDER_SKIN_TONE5 = "\U0001f3c2\U0001f3ff";
+ public const string SNOW_CAPPED_MOUNTAIN = "\U0001f3d4";
+ public const string SNOWFLAKE = "\U00002744";
+ public const string SNOWMAN = "\U000026c4";
+ public const string SNOWMAN2 = "\U00002603";
+ public const string SOB = "\U0001f62d";
+ public const string SOCCER = "\U000026bd";
+ public const string SOON = "\U0001f51c";
+ public const string SOS = "\U0001f198";
+ public const string SOUND = "\U0001f509";
+ public const string SPACE_INVADER = "\U0001f47e";
+ public const string SPADES = "\U00002660";
+ public const string SPAGHETTI = "\U0001f35d";
+ public const string SPARKLE = "\U00002747";
+ public const string SPARKLER = "\U0001f387";
+ public const string SPARKLES = "\U00002728";
+ public const string SPARKLING_HEART = "\U0001f496";
+ public const string SPEAKER = "\U0001f508";
+ public const string SPEAKING_HEAD = "\U0001f5e3";
+ public const string SPEAKING_HEAD_IN_SILHOUETTE = "\U0001f5e3";
+ public const string SPEAK_NO_EVIL = "\U0001f64a";
+ public const string SPEECH_BALLOON = "\U0001f4ac";
+ public const string SPEECH_LEFT = "\U0001f5e8";
+ public const string SPEEDBOAT = "\U0001f6a4";
+ public const string SPIDER = "\U0001f577";
+ public const string SPIDER_WEB = "\U0001f578";
+ public const string SPIRAL_CALENDAR_PAD = "\U0001f5d3";
+ public const string SPIRAL_NOTE_PAD = "\U0001f5d2";
+ public const string SPOON = "\U0001f944";
+ public const string SPORTS_MEDAL = "\U0001f3c5";
+ public const string SPY = "\U0001f575";
+ public const string SPY_SKIN_TONE1 = "\U0001f575\U0001f3fb";
+ public const string SPY_SKIN_TONE2 = "\U0001f575\U0001f3fc";
+ public const string SPY_SKIN_TONE3 = "\U0001f575\U0001f3fd";
+ public const string SPY_SKIN_TONE4 = "\U0001f575\U0001f3fe";
+ public const string SPY_SKIN_TONE5 = "\U0001f575\U0001f3ff";
+ public const string SQUID = "\U0001f991";
+ public const string STADIUM = "\U0001f3df";
+ public const string STAR = "\U00002b50";
+ public const string STAR2 = "\U0001f31f";
+ public const string STAR_AND_CRESCENT = "\U0000262a";
+ public const string STAR_OF_DAVID = "\U00002721";
+ public const string STARS = "\U0001f320";
+ public const string STATION = "\U0001f689";
+ public const string STATUE_OF_LIBERTY = "\U0001f5fd";
+ public const string STEAM_LOCOMOTIVE = "\U0001f682";
+ public const string STEW = "\U0001f372";
+ public const string STOP_BUTTON = "\U000023f9";
+ public const string STOP_SIGN = "\U0001f6d1";
+ public const string STOPWATCH = "\U000023f1";
+ public const string STRAIGHT_RULER = "\U0001f4cf";
+ public const string STRAWBERRY = "\U0001f353";
+ public const string STUCK_OUT_TONGUE = "\U0001f61b";
+ public const string STUCK_OUT_TONGUE_CLOSED_EYES = "\U0001f61d";
+ public const string STUCK_OUT_TONGUE_WINKING_EYE = "\U0001f61c";
+ public const string STUDIO_MICROPHONE = "\U0001f399";
+ public const string STUFFED_FLATBREAD = "\U0001f959";
+ public const string STUFFED_PITA = "\U0001f959";
+ public const string SUNFLOWER = "\U0001f33b";
+ public const string SUNGLASSES = "\U0001f60e";
+ public const string SUNNY = "\U00002600";
+ public const string SUNRISE = "\U0001f305";
+ public const string SUNRISE_OVER_MOUNTAINS = "\U0001f304";
+ public const string SUN_WITH_FACE = "\U0001f31e";
+ public const string SURFER = "\U0001f3c4";
+ public const string SURFER_SKIN_TONE1 = "\U0001f3c4\U0001f3fb";
+ public const string SURFER_SKIN_TONE2 = "\U0001f3c4\U0001f3fc";
+ public const string SURFER_SKIN_TONE3 = "\U0001f3c4\U0001f3fd";
+ public const string SURFER_SKIN_TONE4 = "\U0001f3c4\U0001f3fe";
+ public const string SURFER_SKIN_TONE5 = "\U0001f3c4\U0001f3ff";
+ public const string SUSHI = "\U0001f363";
+ public const string SUSPENSION_RAILWAY = "\U0001f69f";
+ public const string SWEAT = "\U0001f613";
+ public const string SWEAT_DROPS = "\U0001f4a6";
+ public const string SWEAT_SMILE = "\U0001f605";
+ public const string SWEET_POTATO = "\U0001f360";
+ public const string SWIMMER = "\U0001f3ca";
+ public const string SWIMMER_SKIN_TONE1 = "\U0001f3ca\U0001f3fb";
+ public const string SWIMMER_SKIN_TONE2 = "\U0001f3ca\U0001f3fc";
+ public const string SWIMMER_SKIN_TONE3 = "\U0001f3ca\U0001f3fd";
+ public const string SWIMMER_SKIN_TONE4 = "\U0001f3ca\U0001f3fe";
+ public const string SWIMMER_SKIN_TONE5 = "\U0001f3ca\U0001f3ff";
+ public const string SYMBOLS = "\U0001f523";
+ public const string SYNAGOGUE = "\U0001f54d";
+ public const string SYRINGE = "\U0001f489";
+ public const string TABLE_TENNIS = "\U0001f3d3";
+ public const string TACO = "\U0001f32e";
+ public const string TADA = "\U0001f389";
+ public const string TANABATA_TREE = "\U0001f38b";
+ public const string TANGERINE = "\U0001f34a";
+ public const string TAURUS = "\U00002649";
+ public const string TAXI = "\U0001f695";
+ public const string TEA = "\U0001f375";
+ public const string TELEPHONE = "\U0000260e";
+ public const string TELEPHONE_RECEIVER = "\U0001f4de";
+ public const string TELESCOPE = "\U0001f52d";
+ public const string TENNIS = "\U0001f3be";
+ public const string TENT = "\U000026fa";
+ public const string THERMOMETER = "\U0001f321";
+ public const string THERMOMETER_FACE = "\U0001f912";
+ public const string THINKING = "\U0001f914";
+ public const string THINKING_FACE = "\U0001f914";
+ public const string THIRD_PLACE = "\U0001f949";
+ public const string THIRD_PLACE_MEDAL = "\U0001f949";
+ public const string THOUGHT_BALLOON = "\U0001f4ad";
+ public const string THREE = "\U00000033\U000020e3";
+ public const string THREE_BUTTON_MOUSE = "\U0001f5b1";
+ public const string THUMBDOWN = "\U0001f44e";
+ public const string THUMBDOWN_SKIN_TONE1 = "\U0001f44e\U0001f3fb";
+ public const string THUMBDOWN_SKIN_TONE2 = "\U0001f44e\U0001f3fc";
+ public const string THUMBDOWN_SKIN_TONE3 = "\U0001f44e\U0001f3fd";
+ public const string THUMBDOWN_SKIN_TONE4 = "\U0001f44e\U0001f3fe";
+ public const string THUMBDOWN_SKIN_TONE5 = "\U0001f44e\U0001f3ff";
+ public const string THUMBSDOWN = "\U0001f44e";
+ public const string THUMBSDOWN_SKIN_TONE1 = "\U0001f44e\U0001f3fb";
+ public const string THUMBSDOWN_SKIN_TONE2 = "\U0001f44e\U0001f3fc";
+ public const string THUMBSDOWN_SKIN_TONE3 = "\U0001f44e\U0001f3fd";
+ public const string THUMBSDOWN_SKIN_TONE4 = "\U0001f44e\U0001f3fe";
+ public const string THUMBSDOWN_SKIN_TONE5 = "\U0001f44e\U0001f3ff";
+ public const string THUMBSUP = "\U0001f44d";
+ public const string THUMBSUP_SKIN_TONE1 = "\U0001f44d\U0001f3fb";
+ public const string THUMBSUP_SKIN_TONE2 = "\U0001f44d\U0001f3fc";
+ public const string THUMBSUP_SKIN_TONE3 = "\U0001f44d\U0001f3fd";
+ public const string THUMBSUP_SKIN_TONE4 = "\U0001f44d\U0001f3fe";
+ public const string THUMBSUP_SKIN_TONE5 = "\U0001f44d\U0001f3ff";
+ public const string THUMBUP = "\U0001f44d";
+ public const string THUMBUP_SKIN_TONE1 = "\U0001f44d\U0001f3fb";
+ public const string THUMBUP_SKIN_TONE2 = "\U0001f44d\U0001f3fc";
+ public const string THUMBUP_SKIN_TONE3 = "\U0001f44d\U0001f3fd";
+ public const string THUMBUP_SKIN_TONE4 = "\U0001f44d\U0001f3fe";
+ public const string THUMBUP_SKIN_TONE5 = "\U0001f44d\U0001f3ff";
+ public const string THUNDER_CLOUD_AND_RAIN = "\U000026c8";
+ public const string THUNDER_CLOUD_RAIN = "\U000026c8";
+ public const string TICKET = "\U0001f3ab";
+ public const string TICKETS = "\U0001f39f";
+ public const string TIGER = "\U0001f42f";
+ public const string TIGER2 = "\U0001f405";
+ public const string TIMER = "\U000023f2";
+ public const string TIMER_CLOCK = "\U000023f2";
+ public const string TIRED_FACE = "\U0001f62b";
+ public const string TM = "\U00002122";
+ public const string TOILET = "\U0001f6bd";
+ public const string TOKYO_TOWER = "\U0001f5fc";
+ public const string TOMATO = "\U0001f345";
+ public const string TONGUE = "\U0001f445";
+ public const string TOOLS = "\U0001f6e0";
+ public const string TOP = "\U0001f51d";
+ public const string TOPHAT = "\U0001f3a9";
+ public const string TRACKBALL = "\U0001f5b2";
+ public const string TRACK_NEXT = "\U000023ed";
+ public const string TRACK_PREVIOUS = "\U000023ee";
+ public const string TRACTOR = "\U0001f69c";
+ public const string TRAFFIC_LIGHT = "\U0001f6a5";
+ public const string TRAIN = "\U0001f68b";
+ public const string TRAIN2 = "\U0001f686";
+ public const string TRAM = "\U0001f68a";
+ public const string TRIANGULAR_FLAG_ON_POST = "\U0001f6a9";
+ public const string TRIANGULAR_RULER = "\U0001f4d0";
+ public const string TRIDENT = "\U0001f531";
+ public const string TRIUMPH = "\U0001f624";
+ public const string TROLLEYBUS = "\U0001f68e";
+ public const string TROPHY = "\U0001f3c6";
+ public const string TROPICAL_DRINK = "\U0001f379";
+ public const string TROPICAL_FISH = "\U0001f420";
+ public const string TRUCK = "\U0001f69a";
+ public const string TRUMPET = "\U0001f3ba";
+ public const string TULIP = "\U0001f337";
+ public const string TUMBLER_GLASS = "\U0001f943";
+ public const string TURKEY = "\U0001f983";
+ public const string TURTLE = "\U0001f422";
+ public const string TV = "\U0001f4fa";
+ public const string TWISTED_RIGHTWARDS_ARROWS = "\U0001f500";
+ public const string TWO = "\U00000032\U000020e3";
+ public const string TWO_HEARTS = "\U0001f495";
+ public const string TWO_MEN_HOLDING_HANDS = "\U0001f46c";
+ public const string TWO_WOMEN_HOLDING_HANDS = "\U0001f46d";
+ public const string U5272 = "\U0001f239";
+ public const string U5408 = "\U0001f234";
+ public const string U55_B6 = "\U0001f23a";
+ public const string U6307 = "\U0001f22f";
+ public const string U6708 = "\U0001f237";
+ public const string U6709 = "\U0001f236";
+ public const string U6_E80 = "\U0001f235";
+ public const string U7121 = "\U0001f21a";
+ public const string U7533 = "\U0001f238";
+ public const string U7981 = "\U0001f232";
+ public const string U7_A7_A = "\U0001f233";
+ public const string UMBRELLA = "\U00002614";
+ public const string UMBRELLA2 = "\U00002602";
+ public const string UMBRELLA_ON_GROUND = "\U000026f1";
+ public const string UNAMUSED = "\U0001f612";
+ public const string UNDERAGE = "\U0001f51e";
+ public const string UNICORN = "\U0001f984";
+ public const string UNICORN_FACE = "\U0001f984";
+ public const string UNLOCK = "\U0001f513";
+ public const string UP = "\U0001f199";
+ public const string UPSIDE_DOWN = "\U0001f643";
+ public const string UPSIDE_DOWN_FACE = "\U0001f643";
+ public const string URN = "\U000026b1";
+ public const string V = "\U0000270c";
+ public const string VERTICAL_TRAFFIC_LIGHT = "\U0001f6a6";
+ public const string VHS = "\U0001f4fc";
+ public const string VIBRATION_MODE = "\U0001f4f3";
+ public const string VIDEO_CAMERA = "\U0001f4f9";
+ public const string VIDEO_GAME = "\U0001f3ae";
+ public const string VIOLIN = "\U0001f3bb";
+ public const string VIRGO = "\U0000264d";
+ public const string VOLCANO = "\U0001f30b";
+ public const string VOLLEYBALL = "\U0001f3d0";
+ public const string VS = "\U0001f19a";
+ public const string V_SKIN_TONE1 = "\U0000270c\U0001f3fb";
+ public const string V_SKIN_TONE2 = "\U0000270c\U0001f3fc";
+ public const string V_SKIN_TONE3 = "\U0000270c\U0001f3fd";
+ public const string V_SKIN_TONE4 = "\U0000270c\U0001f3fe";
+ public const string V_SKIN_TONE5 = "\U0000270c\U0001f3ff";
+ public const string VULCAN = "\U0001f596";
+ public const string VULCAN_SKIN_TONE1 = "\U0001f596\U0001f3fb";
+ public const string VULCAN_SKIN_TONE2 = "\U0001f596\U0001f3fc";
+ public const string VULCAN_SKIN_TONE3 = "\U0001f596\U0001f3fd";
+ public const string VULCAN_SKIN_TONE4 = "\U0001f596\U0001f3fe";
+ public const string VULCAN_SKIN_TONE5 = "\U0001f596\U0001f3ff";
+ public const string WALKING = "\U0001f6b6";
+ public const string WALKING_SKIN_TONE1 = "\U0001f6b6\U0001f3fb";
+ public const string WALKING_SKIN_TONE2 = "\U0001f6b6\U0001f3fc";
+ public const string WALKING_SKIN_TONE3 = "\U0001f6b6\U0001f3fd";
+ public const string WALKING_SKIN_TONE4 = "\U0001f6b6\U0001f3fe";
+ public const string WALKING_SKIN_TONE5 = "\U0001f6b6\U0001f3ff";
+ public const string WANING_CRESCENT_MOON = "\U0001f318";
+ public const string WANING_GIBBOUS_MOON = "\U0001f316";
+ public const string WARNING = "\U000026a0";
+ public const string WASTEBASKET = "\U0001f5d1";
+ public const string WATCH = "\U0000231a";
+ public const string WATER_BUFFALO = "\U0001f403";
+ public const string WATERMELON = "\U0001f349";
+ public const string WATER_POLO = "\U0001f93d";
+ public const string WATER_POLO_SKIN_TONE1 = "\U0001f93d\U0001f3fb";
+ public const string WATER_POLO_SKIN_TONE2 = "\U0001f93d\U0001f3fc";
+ public const string WATER_POLO_SKIN_TONE3 = "\U0001f93d\U0001f3fd";
+ public const string WATER_POLO_SKIN_TONE4 = "\U0001f93d\U0001f3fe";
+ public const string WATER_POLO_SKIN_TONE5 = "\U0001f93d\U0001f3ff";
+ public const string WAVE = "\U0001f44b";
+ public const string WAVE_SKIN_TONE1 = "\U0001f44b\U0001f3fb";
+ public const string WAVE_SKIN_TONE2 = "\U0001f44b\U0001f3fc";
+ public const string WAVE_SKIN_TONE3 = "\U0001f44b\U0001f3fd";
+ public const string WAVE_SKIN_TONE4 = "\U0001f44b\U0001f3fe";
+ public const string WAVE_SKIN_TONE5 = "\U0001f44b\U0001f3ff";
+ public const string WAVY_DASH = "\U00003030";
+ public const string WAXING_CRESCENT_MOON = "\U0001f312";
+ public const string WAXING_GIBBOUS_MOON = "\U0001f314";
+ public const string WC = "\U0001f6be";
+ public const string WEARY = "\U0001f629";
+ public const string WEDDING = "\U0001f492";
+ public const string WEIGHT_LIFTER = "\U0001f3cb";
+ public const string WEIGHT_LIFTER_SKIN_TONE1 = "\U0001f3cb\U0001f3fb";
+ public const string WEIGHT_LIFTER_SKIN_TONE2 = "\U0001f3cb\U0001f3fc";
+ public const string WEIGHT_LIFTER_SKIN_TONE3 = "\U0001f3cb\U0001f3fd";
+ public const string WEIGHT_LIFTER_SKIN_TONE4 = "\U0001f3cb\U0001f3fe";
+ public const string WEIGHT_LIFTER_SKIN_TONE5 = "\U0001f3cb\U0001f3ff";
+ public const string WHALE = "\U0001f433";
+ public const string WHALE2 = "\U0001f40b";
+ public const string WHEELCHAIR = "\U0000267f";
+ public const string WHEEL_OF_DHARMA = "\U00002638";
+ public const string WHISKY = "\U0001f943";
+ public const string WHITE_CHECK_MARK = "\U00002705";
+ public const string WHITE_CIRCLE = "\U000026aa";
+ public const string WHITE_FLOWER = "\U0001f4ae";
+ public const string WHITE_FROWNING_FACE = "\U00002639";
+ public const string WHITE_LARGE_SQUARE = "\U00002b1c";
+ public const string WHITE_MEDIUM_SMALL_SQUARE = "\U000025fd";
+ public const string WHITE_MEDIUM_SQUARE = "\U000025fb";
+ public const string WHITE_SMALL_SQUARE = "\U000025ab";
+ public const string WHITE_SQUARE_BUTTON = "\U0001f533";
+ public const string WHITE_SUN_BEHIND_CLOUD = "\U0001f325";
+ public const string WHITE_SUN_BEHIND_CLOUD_WITH_RAIN = "\U0001f326";
+ public const string WHITE_SUN_CLOUD = "\U0001f325";
+ public const string WHITE_SUN_RAIN_CLOUD = "\U0001f326";
+ public const string WHITE_SUN_SMALL_CLOUD = "\U0001f324";
+ public const string WHITE_SUN_WITH_SMALL_CLOUD = "\U0001f324";
+ public const string WILTED_FLOWER = "\U0001f940";
+ public const string WILTED_ROSE = "\U0001f940";
+ public const string WIND_BLOWING_FACE = "\U0001f32c";
+ public const string WIND_CHIME = "\U0001f390";
+ public const string WINE_GLASS = "\U0001f377";
+ public const string WINK = "\U0001f609";
+ public const string WOLF = "\U0001f43a";
+ public const string WOMAN = "\U0001f469";
+ public const string WOMANS_CLOTHES = "\U0001f45a";
+ public const string WOMANS_HAT = "\U0001f452";
+ public const string WOMAN_SKIN_TONE1 = "\U0001f469\U0001f3fb";
+ public const string WOMAN_SKIN_TONE2 = "\U0001f469\U0001f3fc";
+ public const string WOMAN_SKIN_TONE3 = "\U0001f469\U0001f3fd";
+ public const string WOMAN_SKIN_TONE4 = "\U0001f469\U0001f3fe";
+ public const string WOMAN_SKIN_TONE5 = "\U0001f469\U0001f3ff";
+ public const string WOMENS = "\U0001f6ba";
+ public const string WORLD_MAP = "\U0001f5fa";
+ public const string WORRIED = "\U0001f61f";
+ public const string WORSHIP_SYMBOL = "\U0001f6d0";
+ public const string WRENCH = "\U0001f527";
+ public const string WRESTLERS = "\U0001f93c";
+ public const string WRESTLING = "\U0001f93c";
+ public const string WRITING_HAND = "\U0000270d";
+ public const string WRITING_HAND_SKIN_TONE1 = "\U0000270d\U0001f3fb";
+ public const string WRITING_HAND_SKIN_TONE2 = "\U0000270d\U0001f3fc";
+ public const string WRITING_HAND_SKIN_TONE3 = "\U0000270d\U0001f3fd";
+ public const string WRITING_HAND_SKIN_TONE4 = "\U0000270d\U0001f3fe";
+ public const string WRITING_HAND_SKIN_TONE5 = "\U0000270d\U0001f3ff";
+ public const string X = "\U0000274c";
+ public const string YELLOW_HEART = "\U0001f49b";
+ public const string YEN = "\U0001f4b4";
+ public const string YIN_YANG = "\U0000262f";
+ public const string YUM = "\U0001f60b";
+ public const string ZAP = "\U000026a1";
+ public const string ZERO = "\U00000030\U000020e3";
+ public const string ZIPPER_MOUTH = "\U0001f910";
+ public const string ZIPPER_MOUTH_FACE = "\U0001f910";
+ public const string ZZZ = "\U0001f4a4";
#pragma warning restore IDE1006
#pragma warning restore ConstFieldDocumentationHeader
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordAuditLogObjects.cs b/DisCatSharp/Entities/Guild/DiscordAuditLogObjects.cs
index d443ca9dc..de54101cc 100644
--- a/DisCatSharp/Entities/Guild/DiscordAuditLogObjects.cs
+++ b/DisCatSharp/Entities/Guild/DiscordAuditLogObjects.cs
@@ -1,1144 +1,1145 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an audit log entry.
-/// </summary>
-public abstract class DiscordAuditLogEntry : SnowflakeObject
-{
- /// <summary>
- /// Gets the entry's action type.
- /// </summary>
- public AuditLogActionType ActionType { get; internal set; }
-
- /// <summary>
- /// Gets the user responsible for the action.
- /// </summary>
- public DiscordUser UserResponsible { get; internal set; }
-
- /// <summary>
- /// Gets the reason defined in the action.
- /// </summary>
- public string Reason { get; internal set; }
-
- /// <summary>
- /// Gets the category under which the action falls.
- /// </summary>
- public AuditLogActionCategory ActionCategory { get; internal set; }
-}
-
-/// <summary>
-/// Represents a description of how a property changed.
-/// </summary>
-/// <typeparam name="T">Type of the changed property.</typeparam>
-public sealed class PropertyChange<T>
-{
- /// <summary>
- /// The property's value before it was changed.
- /// </summary>
- public T Before { get; internal set; }
-
- /// <summary>
- /// The property's value after it was changed.
- /// </summary>
- public T After { get; internal set; }
-}
-
-/// <summary>
-/// Represents a audit log guild entry.
-/// </summary>
-public sealed class DiscordAuditLogGuildEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected guild.
- /// </summary>
- public DiscordGuild Target { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.Name"/>
- /// </summary>
- public PropertyChange<string> NameChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.Owner"/>
- /// </summary>
- public PropertyChange<DiscordMember> OwnerChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.IconUrl"/>
- /// </summary>
- public PropertyChange<string> IconChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.VerificationLevel"/>
- /// </summary>
- public PropertyChange<VerificationLevel> VerificationLevelChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.AfkChannel"/>
- /// </summary>
- public PropertyChange<DiscordChannel> AfkChannelChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.SystemChannelFlags"/>
- /// </summary>
- public PropertyChange<SystemChannelFlags> SystemChannelFlagsChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.WidgetChannel"/>
- /// </summary>
- public PropertyChange<DiscordChannel> WidgetChannelChange { get; internal set; }
-
- [Obsolete("Use properly named WidgetChannelChange")]
- public PropertyChange<DiscordChannel> EmbedChannelChange => this.WidgetChannelChange;
-
- /// <summary>
- /// <see cref="DiscordGuild.RulesChannel"/>
- /// </summary>
- public PropertyChange<DiscordChannel> RulesChannelChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.PublicUpdatesChannel"/>
- /// </summary>
- public PropertyChange<DiscordChannel> PublicUpdatesChannelChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.DefaultMessageNotifications"/>
- /// </summary>
- public PropertyChange<DefaultMessageNotifications> NotificationSettingsChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.SystemChannel"/>
- /// </summary>
- public PropertyChange<DiscordChannel> SystemChannelChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.ExplicitContentFilter"/>
- /// </summary>
- public PropertyChange<ExplicitContentFilter> ExplicitContentFilterChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.MfaLevel"/>
- /// </summary>
- public PropertyChange<MfaLevel> MfaLevelChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.SplashUrl"/>
- /// </summary>
- public PropertyChange<string> SplashChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.VoiceRegion"/>
- /// </summary>
- public PropertyChange<string> RegionChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.VanityUrlCode"/>
- /// </summary>
- public PropertyChange<string> VanityUrlCodeChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordGuild.PremiumProgressBarEnabled"/>
- /// </summary>
- public PropertyChange<bool> PremiumProgressBarChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogGuildEntry"/> class.
- /// </summary>
- internal DiscordAuditLogGuildEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log channel entry.
-/// </summary>
-public sealed class DiscordAuditLogChannelEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected channel.
- /// </summary>
- public DiscordChannel Target { get; internal set; }
-
- /// <summary>
- /// Gets the description of channel's name change.
- /// </summary>
- public PropertyChange<string> NameChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of channel's type change.
- /// </summary>
- public PropertyChange<ChannelType?> TypeChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of channel's nsfw flag change.
- /// </summary>
- public PropertyChange<bool?> NsfwChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordChannel.RtcRegionId"/>
- /// </summary>
- public PropertyChange<string> RtcRegionIdChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of channel's bitrate change.
- /// </summary>
- public PropertyChange<int?> BitrateChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of channel permission overwrites' change.
- /// </summary>
- public PropertyChange<IReadOnlyList<DiscordOverwrite>> OverwriteChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of channel's topic change.
- /// </summary>
- public PropertyChange<string> TopicChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordChannel.UserLimit"/>
- /// </summary>
- public PropertyChange<int?> UserLimitChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of channel's slow mode timeout change.
- /// </summary>
- public PropertyChange<int?> PerUserRateLimitChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordChannel.DefaultAutoArchiveDuration"/>
- /// </summary>
- public PropertyChange<ThreadAutoArchiveDuration?> DefaultAutoArchiveDurationChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogChannelEntry"/> class.
- /// </summary>
- internal DiscordAuditLogChannelEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log overwrite entry.
-/// </summary>
-public sealed class DiscordAuditLogOverwriteEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected overwrite.
- /// </summary>
- public DiscordOverwrite Target { get; internal set; }
-
- /// <summary>
- /// Gets the channel for which the overwrite was changed.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the description of overwrite's allow value change.
- /// </summary>
- public PropertyChange<Permissions?> AllowChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of overwrite's deny value change.
- /// </summary>
- public PropertyChange<Permissions?> DenyChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of overwrite's type change.
- /// </summary>
- public PropertyChange<OverwriteType?> TypeChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of overwrite's target id change.
- /// </summary>
- public PropertyChange<ulong?> TargetIdChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogOverwriteEntry"/> class.
- /// </summary>
- internal DiscordAuditLogOverwriteEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log kick entry.
-/// </summary>
-public sealed class DiscordAuditLogKickEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the kicked member.
- /// </summary>
- public DiscordMember Target { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogKickEntry"/> class.
- /// </summary>
- internal DiscordAuditLogKickEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log prune entry.
-/// </summary>
-public sealed class DiscordAuditLogPruneEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the number inactivity days after which members were pruned.
- /// </summary>
- public int Days { get; internal set; }
-
- /// <summary>
- /// Gets the number of members pruned.
- /// </summary>
- public int Toll { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogPruneEntry"/> class.
- /// </summary>
- internal DiscordAuditLogPruneEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log ban entry.
-/// </summary>
-public sealed class DiscordAuditLogBanEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the banned member.
- /// </summary>
- public DiscordMember Target { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogBanEntry"/> class.
- /// </summary>
- internal DiscordAuditLogBanEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log member update entry.
-/// </summary>
-public sealed class DiscordAuditLogMemberUpdateEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected member.
- /// </summary>
- public DiscordMember Target { get; internal set; }
-
- /// <summary>
- /// Gets the description of member's nickname change.
- /// </summary>
- public PropertyChange<string> NicknameChange { get; internal set; }
-
- /// <summary>
- /// Gets the roles that were removed from the member.
- /// </summary>
- public IReadOnlyList<DiscordRole> RemovedRoles { get; internal set; }
-
- /// <summary>
- /// Gets the roles that were added to the member.
- /// </summary>
- public IReadOnlyList<DiscordRole> AddedRoles { get; internal set; }
-
- /// <summary>
- /// Gets the description of member's mute status change.
- /// </summary>
- public PropertyChange<bool?> MuteChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of member's deaf status change.
- /// </summary>
- public PropertyChange<bool?> DeafenChange { get; internal set; }
-
- /// <summary>
- /// Get's the timeout change.
- /// </summary>
- public PropertyChange<DateTime?> CommunicationDisabledUntilChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogMemberUpdateEntry"/> class.
- /// </summary>
- internal DiscordAuditLogMemberUpdateEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log role update entry.
-/// </summary>
-public sealed class DiscordAuditLogRoleUpdateEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected role.
- /// </summary>
- public DiscordRole Target { get; internal set; }
-
- /// <summary>
- /// Gets the description of role's name change.
- /// </summary>
- public PropertyChange<string> NameChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of role's color change.
- /// </summary>
- public PropertyChange<int?> ColorChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of role's permission set change.
- /// </summary>
- public PropertyChange<Permissions?> PermissionChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of the role's position change.
- /// </summary>
- public PropertyChange<int?> PositionChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of the role's mentionability change.
- /// </summary>
- public PropertyChange<bool?> MentionableChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of the role's hoist status change.
- /// </summary>
- public PropertyChange<bool?> HoistChange { get; internal set; }
-
- /// <summary>
- /// <see cref="DiscordRole.IconHash"/>
- /// </summary>
- public PropertyChange<string> IconHashChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogRoleUpdateEntry"/> class.
- /// </summary>
- internal DiscordAuditLogRoleUpdateEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log invite entry.
-/// </summary>
-public sealed class DiscordAuditLogInviteEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected invite.
- /// </summary>
- public DiscordInvite Target { get; internal set; }
-
- /// <summary>
- /// Gets the description of invite's max age change.
- /// </summary>
- public PropertyChange<int?> MaxAgeChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of invite's code change.
- /// </summary>
- public PropertyChange<string> CodeChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of invite's temporariness change.
- /// </summary>
- public PropertyChange<bool?> TemporaryChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of invite's inviting member change.
- /// </summary>
- public PropertyChange<DiscordMember> InviterChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of invite's target channel change.
- /// </summary>
- public PropertyChange<DiscordChannel> ChannelChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of invite's use count change.
- /// </summary>
- public PropertyChange<int?> UsesChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of invite's max use count change.
- /// </summary>
- public PropertyChange<int?> MaxUsesChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogInviteEntry"/> class.
- /// </summary>
- internal DiscordAuditLogInviteEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log webhook entry.
-/// </summary>
-public sealed class DiscordAuditLogWebhookEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected webhook.
- /// </summary>
- public DiscordWebhook Target { get; internal set; }
-
- /// <summary>
- /// Undocumented.
- /// </summary>
- public PropertyChange<ulong?> IdChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of webhook's name change.
- /// </summary>
- public PropertyChange<string> NameChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of webhook's target channel change.
- /// </summary>
- public PropertyChange<DiscordChannel> ChannelChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of webhook's type change.
- /// </summary>
- public PropertyChange<int?> TypeChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of webhook's avatar change.
- /// </summary>
- public PropertyChange<string> AvatarHashChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogWebhookEntry"/> class.
- /// </summary>
- internal DiscordAuditLogWebhookEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log emoji entry.
-/// </summary>
-public sealed class DiscordAuditLogEmojiEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected emoji.
- /// </summary>
- public DiscordEmoji Target { get; internal set; }
-
- /// <summary>
- /// Gets the description of emoji's name change.
- /// </summary>
- public PropertyChange<string> NameChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogEmojiEntry"/> class.
- /// </summary>
- internal DiscordAuditLogEmojiEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log sticker entry.
-/// </summary>
-public sealed class DiscordAuditLogStickerEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected sticker.
- /// </summary>
- public DiscordSticker Target { get; internal set; }
-
- /// <summary>
- /// Gets the description of sticker's name change.
- /// </summary>
- public PropertyChange<string> NameChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of sticker's description change.
- /// </summary>
- public PropertyChange<string> DescriptionChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of sticker's tags change.
- /// </summary>
- public PropertyChange<string> TagsChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of sticker's tags change.
- /// </summary>
- public PropertyChange<string> AssetChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of sticker's guild id change.
- /// </summary>
- public PropertyChange<ulong?> GuildIdChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of sticker's availability change.
- /// </summary>
- public PropertyChange<bool?> AvailabilityChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of sticker's id change.
- /// </summary>
- public PropertyChange<ulong?> IdChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of sticker's type change.
- /// </summary>
- public PropertyChange<StickerType?> TypeChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of sticker's format change.
- /// </summary>
- public PropertyChange<StickerFormat?> FormatChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogStickerEntry"/> class.
- /// </summary>
- internal DiscordAuditLogStickerEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log message entry.
-/// </summary>
-public sealed class DiscordAuditLogMessageEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected message. Note that more often than not, this will only have ID specified.
- /// </summary>
- public DiscordMessage Target { get; internal set; }
-
- /// <summary>
- /// Gets the channel in which the action occurred.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the number of messages that were affected.
- /// </summary>
- public int? MessageCount { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogMessageEntry"/> class.
- /// </summary>
- internal DiscordAuditLogMessageEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log message pin entry.
-/// </summary>
-public sealed class DiscordAuditLogMessagePinEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected message's user.
- /// </summary>
- public DiscordUser Target { get; internal set; }
-
- /// <summary>
- /// Gets the channel the message is in.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the message the pin action was for.
- /// </summary>
- public DiscordMessage Message { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogMessagePinEntry"/> class.
- /// </summary>
- internal DiscordAuditLogMessagePinEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log bot add entry.
-/// </summary>
-public sealed class DiscordAuditLogBotAddEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the bot that has been added to the guild.
- /// </summary>
- public DiscordUser TargetBot { get; internal set; }
-}
-
-/// <summary>
-/// Represents a audit log member move entry.
-/// </summary>
-public sealed class DiscordAuditLogMemberMoveEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the channel the members were moved in.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the amount of users that were moved out from the voice channel.
- /// </summary>
- public int UserCount { get; internal set; }
-}
-
-/// <summary>
-/// Represents a audit log member disconnect entry.
-/// </summary>
-public sealed class DiscordAuditLogMemberDisconnectEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the amount of users that were disconnected from the voice channel.
- /// </summary>
- public int UserCount { get; internal set; }
-}
-
-/// <summary>
-/// Represents a audit log integration entry.
-/// </summary>
-public sealed class DiscordAuditLogIntegrationEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// The type of integration.
- /// </summary>
- public PropertyChange<string> Type { get; internal set; }
-
- /// <summary>
- /// Gets the description of emoticons' change.
- /// </summary>
- public PropertyChange<bool?> EnableEmoticons { get; internal set; }
-
- /// <summary>
- /// Gets the description of expire grace period's change.
- /// </summary>
- public PropertyChange<int?> ExpireGracePeriod { get; internal set; }
-
- /// <summary>
- /// Gets the description of expire behavior change.
- /// </summary>
- public PropertyChange<int?> ExpireBehavior { get; internal set; }
-}
-
-/// <summary>
-/// Represents a audit log stage entry.
-/// </summary>
-public sealed class DiscordAuditLogStageEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected stage instance
- /// </summary>
- public DiscordStageInstance Target { get; internal set; }
-
- /// <summary>
- /// Gets the description of stage instance's topic change.
- /// </summary>
- public PropertyChange<string> TopicChange { get; internal set; }
-
- /// <summary>
- /// Gets the description of stage instance's privacy level change.
- /// </summary>
- public PropertyChange<StagePrivacyLevel?> PrivacyLevelChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogStageEntry"/> class.
- /// </summary>
- internal DiscordAuditLogStageEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log event entry.
-/// </summary>
-public sealed class DiscordAuditLogGuildScheduledEventEntry : DiscordAuditLogEntry
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the affected event
- /// </summary>
- public DiscordScheduledEvent Target { get; internal set; }
-
- /// <summary>
- /// Gets the channel change.
- /// </summary>
- public PropertyChange<ulong?> ChannelIdChange { get; internal set; }
+ /// Represents an audit log entry.
+ /// </summary>
+ public abstract class DiscordAuditLogEntry : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the entry's action type.
+ /// </summary>
+ public AuditLogActionType ActionType { get; internal set; }
+
+ /// <summary>
+ /// Gets the user responsible for the action.
+ /// </summary>
+ public DiscordUser UserResponsible { get; internal set; }
+
+ /// <summary>
+ /// Gets the reason defined in the action.
+ /// </summary>
+ public string Reason { get; internal set; }
+
+ /// <summary>
+ /// Gets the category under which the action falls.
+ /// </summary>
+ public AuditLogActionCategory ActionCategory { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents a description of how a property changed.
+ /// </summary>
+ /// <typeparam name="T">Type of the changed property.</typeparam>
+ public sealed class PropertyChange<T>
+ {
+ /// <summary>
+ /// The property's value before it was changed.
+ /// </summary>
+ public T Before { get; internal set; }
+
+ /// <summary>
+ /// The property's value after it was changed.
+ /// </summary>
+ public T After { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log guild entry.
+ /// </summary>
+ public sealed class DiscordAuditLogGuildEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected guild.
+ /// </summary>
+ public DiscordGuild Target { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.Name"/>
+ /// </summary>
+ public PropertyChange<string> NameChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.Owner"/>
+ /// </summary>
+ public PropertyChange<DiscordMember> OwnerChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.IconUrl"/>
+ /// </summary>
+ public PropertyChange<string> IconChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.VerificationLevel"/>
+ /// </summary>
+ public PropertyChange<VerificationLevel> VerificationLevelChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.AfkChannel"/>
+ /// </summary>
+ public PropertyChange<DiscordChannel> AfkChannelChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.SystemChannelFlags"/>
+ /// </summary>
+ public PropertyChange<SystemChannelFlags> SystemChannelFlagsChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.WidgetChannel"/>
+ /// </summary>
+ public PropertyChange<DiscordChannel> WidgetChannelChange { get; internal set; }
+
+ [Obsolete("Use properly named WidgetChannelChange")]
+ public PropertyChange<DiscordChannel> EmbedChannelChange => this.WidgetChannelChange;
+
+ /// <summary>
+ /// <see cref="DiscordGuild.RulesChannel"/>
+ /// </summary>
+ public PropertyChange<DiscordChannel> RulesChannelChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.PublicUpdatesChannel"/>
+ /// </summary>
+ public PropertyChange<DiscordChannel> PublicUpdatesChannelChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.DefaultMessageNotifications"/>
+ /// </summary>
+ public PropertyChange<DefaultMessageNotifications> NotificationSettingsChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.SystemChannel"/>
+ /// </summary>
+ public PropertyChange<DiscordChannel> SystemChannelChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.ExplicitContentFilter"/>
+ /// </summary>
+ public PropertyChange<ExplicitContentFilter> ExplicitContentFilterChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.MfaLevel"/>
+ /// </summary>
+ public PropertyChange<MfaLevel> MfaLevelChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.SplashUrl"/>
+ /// </summary>
+ public PropertyChange<string> SplashChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.VoiceRegion"/>
+ /// </summary>
+ public PropertyChange<string> RegionChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.VanityUrlCode"/>
+ /// </summary>
+ public PropertyChange<string> VanityUrlCodeChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordGuild.PremiumProgressBarEnabled"/>
+ /// </summary>
+ public PropertyChange<bool> PremiumProgressBarChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogGuildEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogGuildEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log channel entry.
+ /// </summary>
+ public sealed class DiscordAuditLogChannelEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected channel.
+ /// </summary>
+ public DiscordChannel Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of channel's name change.
+ /// </summary>
+ public PropertyChange<string> NameChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of channel's type change.
+ /// </summary>
+ public PropertyChange<ChannelType?> TypeChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of channel's nsfw flag change.
+ /// </summary>
+ public PropertyChange<bool?> NsfwChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordChannel.RtcRegionId"/>
+ /// </summary>
+ public PropertyChange<string> RtcRegionIdChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of channel's bitrate change.
+ /// </summary>
+ public PropertyChange<int?> BitrateChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of channel permission overwrites' change.
+ /// </summary>
+ public PropertyChange<IReadOnlyList<DiscordOverwrite>> OverwriteChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of channel's topic change.
+ /// </summary>
+ public PropertyChange<string> TopicChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordChannel.UserLimit"/>
+ /// </summary>
+ public PropertyChange<int?> UserLimitChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of channel's slow mode timeout change.
+ /// </summary>
+ public PropertyChange<int?> PerUserRateLimitChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordChannel.DefaultAutoArchiveDuration"/>
+ /// </summary>
+ public PropertyChange<ThreadAutoArchiveDuration?> DefaultAutoArchiveDurationChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogChannelEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogChannelEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log overwrite entry.
+ /// </summary>
+ public sealed class DiscordAuditLogOverwriteEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected overwrite.
+ /// </summary>
+ public DiscordOverwrite Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the channel for which the overwrite was changed.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of overwrite's allow value change.
+ /// </summary>
+ public PropertyChange<Permissions?> AllowChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of overwrite's deny value change.
+ /// </summary>
+ public PropertyChange<Permissions?> DenyChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of overwrite's type change.
+ /// </summary>
+ public PropertyChange<OverwriteType?> TypeChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of overwrite's target id change.
+ /// </summary>
+ public PropertyChange<ulong?> TargetIdChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogOverwriteEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogOverwriteEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log kick entry.
+ /// </summary>
+ public sealed class DiscordAuditLogKickEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the kicked member.
+ /// </summary>
+ public DiscordMember Target { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogKickEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogKickEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log prune entry.
+ /// </summary>
+ public sealed class DiscordAuditLogPruneEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the number inactivity days after which members were pruned.
+ /// </summary>
+ public int Days { get; internal set; }
+
+ /// <summary>
+ /// Gets the number of members pruned.
+ /// </summary>
+ public int Toll { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogPruneEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogPruneEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log ban entry.
+ /// </summary>
+ public sealed class DiscordAuditLogBanEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the banned member.
+ /// </summary>
+ public DiscordMember Target { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogBanEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogBanEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log member update entry.
+ /// </summary>
+ public sealed class DiscordAuditLogMemberUpdateEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected member.
+ /// </summary>
+ public DiscordMember Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of member's nickname change.
+ /// </summary>
+ public PropertyChange<string> NicknameChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the roles that were removed from the member.
+ /// </summary>
+ public IReadOnlyList<DiscordRole> RemovedRoles { get; internal set; }
+
+ /// <summary>
+ /// Gets the roles that were added to the member.
+ /// </summary>
+ public IReadOnlyList<DiscordRole> AddedRoles { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of member's mute status change.
+ /// </summary>
+ public PropertyChange<bool?> MuteChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of member's deaf status change.
+ /// </summary>
+ public PropertyChange<bool?> DeafenChange { get; internal set; }
+
+ /// <summary>
+ /// Get's the timeout change.
+ /// </summary>
+ public PropertyChange<DateTime?> CommunicationDisabledUntilChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogMemberUpdateEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogMemberUpdateEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log role update entry.
+ /// </summary>
+ public sealed class DiscordAuditLogRoleUpdateEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected role.
+ /// </summary>
+ public DiscordRole Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of role's name change.
+ /// </summary>
+ public PropertyChange<string> NameChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of role's color change.
+ /// </summary>
+ public PropertyChange<int?> ColorChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of role's permission set change.
+ /// </summary>
+ public PropertyChange<Permissions?> PermissionChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of the role's position change.
+ /// </summary>
+ public PropertyChange<int?> PositionChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of the role's mentionability change.
+ /// </summary>
+ public PropertyChange<bool?> MentionableChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of the role's hoist status change.
+ /// </summary>
+ public PropertyChange<bool?> HoistChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordRole.IconHash"/>
+ /// </summary>
+ public PropertyChange<string> IconHashChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogRoleUpdateEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogRoleUpdateEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log invite entry.
+ /// </summary>
+ public sealed class DiscordAuditLogInviteEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected invite.
+ /// </summary>
+ public DiscordInvite Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of invite's max age change.
+ /// </summary>
+ public PropertyChange<int?> MaxAgeChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of invite's code change.
+ /// </summary>
+ public PropertyChange<string> CodeChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of invite's temporariness change.
+ /// </summary>
+ public PropertyChange<bool?> TemporaryChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of invite's inviting member change.
+ /// </summary>
+ public PropertyChange<DiscordMember> InviterChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of invite's target channel change.
+ /// </summary>
+ public PropertyChange<DiscordChannel> ChannelChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of invite's use count change.
+ /// </summary>
+ public PropertyChange<int?> UsesChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of invite's max use count change.
+ /// </summary>
+ public PropertyChange<int?> MaxUsesChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogInviteEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogInviteEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log webhook entry.
+ /// </summary>
+ public sealed class DiscordAuditLogWebhookEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected webhook.
+ /// </summary>
+ public DiscordWebhook Target { get; internal set; }
+
+ /// <summary>
+ /// Undocumented.
+ /// </summary>
+ public PropertyChange<ulong?> IdChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of webhook's name change.
+ /// </summary>
+ public PropertyChange<string> NameChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of webhook's target channel change.
+ /// </summary>
+ public PropertyChange<DiscordChannel> ChannelChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of webhook's type change.
+ /// </summary>
+ public PropertyChange<int?> TypeChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of webhook's avatar change.
+ /// </summary>
+ public PropertyChange<string> AvatarHashChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogWebhookEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogWebhookEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log emoji entry.
+ /// </summary>
+ public sealed class DiscordAuditLogEmojiEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected emoji.
+ /// </summary>
+ public DiscordEmoji Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of emoji's name change.
+ /// </summary>
+ public PropertyChange<string> NameChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogEmojiEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogEmojiEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log sticker entry.
+ /// </summary>
+ public sealed class DiscordAuditLogStickerEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected sticker.
+ /// </summary>
+ public DiscordSticker Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of sticker's name change.
+ /// </summary>
+ public PropertyChange<string> NameChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of sticker's description change.
+ /// </summary>
+ public PropertyChange<string> DescriptionChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of sticker's tags change.
+ /// </summary>
+ public PropertyChange<string> TagsChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of sticker's tags change.
+ /// </summary>
+ public PropertyChange<string> AssetChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of sticker's guild id change.
+ /// </summary>
+ public PropertyChange<ulong?> GuildIdChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of sticker's availability change.
+ /// </summary>
+ public PropertyChange<bool?> AvailabilityChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of sticker's id change.
+ /// </summary>
+ public PropertyChange<ulong?> IdChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of sticker's type change.
+ /// </summary>
+ public PropertyChange<StickerType?> TypeChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of sticker's format change.
+ /// </summary>
+ public PropertyChange<StickerFormat?> FormatChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogStickerEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogStickerEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log message entry.
+ /// </summary>
+ public sealed class DiscordAuditLogMessageEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected message. Note that more often than not, this will only have ID specified.
+ /// </summary>
+ public DiscordMessage Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the channel in which the action occurred.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the number of messages that were affected.
+ /// </summary>
+ public int? MessageCount { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogMessageEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogMessageEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log message pin entry.
+ /// </summary>
+ public sealed class DiscordAuditLogMessagePinEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected message's user.
+ /// </summary>
+ public DiscordUser Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the channel the message is in.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the message the pin action was for.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogMessagePinEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogMessagePinEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log bot add entry.
+ /// </summary>
+ public sealed class DiscordAuditLogBotAddEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the bot that has been added to the guild.
+ /// </summary>
+ public DiscordUser TargetBot { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log member move entry.
+ /// </summary>
+ public sealed class DiscordAuditLogMemberMoveEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the channel the members were moved in.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the amount of users that were moved out from the voice channel.
+ /// </summary>
+ public int UserCount { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log member disconnect entry.
+ /// </summary>
+ public sealed class DiscordAuditLogMemberDisconnectEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the amount of users that were disconnected from the voice channel.
+ /// </summary>
+ public int UserCount { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log integration entry.
+ /// </summary>
+ public sealed class DiscordAuditLogIntegrationEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// The type of integration.
+ /// </summary>
+ public PropertyChange<string> Type { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of emoticons' change.
+ /// </summary>
+ public PropertyChange<bool?> EnableEmoticons { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of expire grace period's change.
+ /// </summary>
+ public PropertyChange<int?> ExpireGracePeriod { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of expire behavior change.
+ /// </summary>
+ public PropertyChange<int?> ExpireBehavior { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log stage entry.
+ /// </summary>
+ public sealed class DiscordAuditLogStageEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected stage instance
+ /// </summary>
+ public DiscordStageInstance Target { get; internal set; }
- /// <summary>
- /// <see cref="DiscordScheduledEvent.Name"/>
- /// </summary>
- public PropertyChange<string> NameChange { get; internal set; }
+ /// <summary>
+ /// Gets the description of stage instance's topic change.
+ /// </summary>
+ public PropertyChange<string> TopicChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of stage instance's privacy level change.
+ /// </summary>
+ public PropertyChange<StagePrivacyLevel?> PrivacyLevelChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogStageEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogStageEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log event entry.
+ /// </summary>
+ public sealed class DiscordAuditLogGuildScheduledEventEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected event
+ /// </summary>
+ public DiscordScheduledEvent Target { get; internal set; }
- /// <summary>
- /// Gets the description change.
- /// </summary>
- public PropertyChange<string> DescriptionChange { get; internal set; }
+ /// <summary>
+ /// Gets the channel change.
+ /// </summary>
+ public PropertyChange<ulong?> ChannelIdChange { get; internal set; }
+
+ /// <summary>
+ /// <see cref="DiscordScheduledEvent.Name"/>
+ /// </summary>
+ public PropertyChange<string> NameChange { get; internal set; }
- /* Will be added https://github.com/discord/discord-api-docs/pull/3586#issuecomment-969137241
+ /// <summary>
+ /// Gets the description change.
+ /// </summary>
+ public PropertyChange<string> DescriptionChange { get; internal set; }
+
+ /* Will be added https://github.com/discord/discord-api-docs/pull/3586#issuecomment-969137241
public PropertyChange<> ScheduledStartTimeChange { get; internal set; }
public PropertyChange<> ScheduledEndTimeChange { get; internal set; }
*/
- /// <summary>
- /// Gets the location change.
- /// </summary>
- public PropertyChange<string> LocationChange { get; internal set; }
+ /// <summary>
+ /// Gets the location change.
+ /// </summary>
+ public PropertyChange<string> LocationChange { get; internal set; }
- /// <summary>
- /// Gets the privacy level change.
- /// </summary>
- public PropertyChange<ScheduledEventPrivacyLevel?> PrivacyLevelChange { get; internal set; }
+ /// <summary>
+ /// Gets the privacy level change.
+ /// </summary>
+ public PropertyChange<ScheduledEventPrivacyLevel?> PrivacyLevelChange { get; internal set; }
- /// <summary>
- /// Gets the status change.
- /// </summary>
- public PropertyChange<ScheduledEventStatus?> StatusChange { get; internal set; }
+ /// <summary>
+ /// Gets the status change.
+ /// </summary>
+ public PropertyChange<ScheduledEventStatus?> StatusChange { get; internal set; }
- /// <summary>
- /// Gets the entity type change.
- /// </summary>
- public PropertyChange<ScheduledEventEntityType?> EntityTypeChange { get; internal set; }
+ /// <summary>
+ /// Gets the entity type change.
+ /// </summary>
+ public PropertyChange<ScheduledEventEntityType?> EntityTypeChange { get; internal set; }
- /*/// <summary>
+ /*/// <summary>
/// Gets the sku ids change.
/// </summary>
public PropertyChange<IReadOnlyList<ulong>> SkuIdsChange { get; internal set; }*/
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogGuildScheduledEventEntry"/> class.
- /// </summary>
- internal DiscordAuditLogGuildScheduledEventEntry() { }
-}
-
-/// <summary>
-/// Represents a audit log thread entry.
-/// </summary>
-public sealed class DiscordAuditLogThreadEntry : DiscordAuditLogEntry
-{
- /// <summary>
- /// Gets the affected thread
- /// </summary>
- public DiscordThreadChannel Target { get; internal set; }
-
- /// <summary>
- /// Gets the name of the thread.
- /// </summary>
- public PropertyChange<string> NameChange { get; internal set; }
-
- /// <summary>
- /// Gets the type of the thread.
- /// </summary>
- public PropertyChange<ChannelType?> TypeChange { get; internal set; }
-
- /// <summary>
- /// Gets the archived state of the thread.
- /// </summary>
- public PropertyChange<bool?> ArchivedChange { get; internal set; }
-
- /// <summary>
- /// Gets the locked state of the thread.
- /// </summary>
- public PropertyChange<bool?> LockedChange { get; internal set; }
-
- /// <summary>
- /// Gets the invitable state of the thread.
- /// </summary>
- public PropertyChange<bool?> InvitableChange { get; internal set; }
-
- /// <summary>
- /// Gets the new auto archive duration of the thread.
- /// </summary>
- public PropertyChange<ThreadAutoArchiveDuration?> AutoArchiveDurationChange { get; internal set; }
-
- /// <summary>
- /// Gets the new ratelimit of the thread.
- /// </summary>
- public PropertyChange<int?> PerUserRateLimitChange { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAuditLogThreadEntry"/> class.
- /// </summary>
- internal DiscordAuditLogThreadEntry() { }
-}
-
-/// <summary>
-/// Indicates audit log action category.
-/// </summary>
-public enum AuditLogActionCategory
-{
- /// <summary>
- /// Indicates that this action resulted in creation or addition of an object.
- /// </summary>
- Create,
-
- /// <summary>
- /// Indicates that this action resulted in update of an object.
- /// </summary>
- Update,
-
- /// <summary>
- /// Indicates that this action resulted in deletion or removal of an object.
- /// </summary>
- Delete,
-
- /// <summary>
- /// Indicates that this action resulted in something else than creation, addition, update, deletion, or removal of an object.
- /// </summary>
- Other
-}
-
-// below is taken from
-// https://github.com/Rapptz/discord.py/blob/rewrite/discord/enums.py#L125
-
-/// <summary>
-/// Represents type of the action that was taken in given audit log event.
-/// </summary>
-public enum AuditLogActionType
-{
- /// <summary>
- /// Indicates an invalid action type.
- /// </summary>
- Invalid = 0,
-
- /// <summary>
- /// Indicates that the guild was updated.
- /// </summary>
- GuildUpdate = 1,
-
- /// <summary>
- /// Indicates that the channel was created.
- /// </summary>
- ChannelCreate = 10,
-
- /// <summary>
- /// Indicates that the channel was updated.
- /// </summary>
- ChannelUpdate = 11,
-
- /// <summary>
- /// Indicates that the channel was deleted.
- /// </summary>
- ChannelDelete = 12,
-
- /// <summary>
- /// Indicates that the channel permission overwrite was created.
- /// </summary>
- OverwriteCreate = 13,
-
- /// <summary>
- /// Indicates that the channel permission overwrite was updated.
- /// </summary>
- OverwriteUpdate = 14,
-
- /// <summary>
- /// Indicates that the channel permission overwrite was deleted.
- /// </summary>
- OverwriteDelete = 15,
-
- /// <summary>
- /// Indicates that the user was kicked.
- /// </summary>
- Kick = 20,
-
- /// <summary>
- /// Indicates that users were pruned.
- /// </summary>
- Prune = 21,
-
- /// <summary>
- /// Indicates that the user was banned.
- /// </summary>
- Ban = 22,
-
- /// <summary>
- /// Indicates that the user was unbanned.
- /// </summary>
- Unban = 23,
-
- /// <summary>
- /// Indicates that the member was updated.
- /// </summary>
- MemberUpdate = 24,
-
- /// <summary>
- /// Indicates that the member's roles were updated.
- /// </summary>
- MemberRoleUpdate = 25,
-
- /// <summary>
- /// Indicates that the member has moved to another voice channel.
- /// </summary>
- MemberMove = 26,
-
- /// <summary>
- /// Indicates that the member has disconnected from a voice channel.
- /// </summary>
- MemberDisconnect = 27,
-
- /// <summary>
- /// Indicates that a bot was added to the guild.
- /// </summary>
- BotAdd = 28,
-
- /// <summary>
- /// Indicates that the role was created.
- /// </summary>
- RoleCreate = 30,
-
- /// <summary>
- /// Indicates that the role was updated.
- /// </summary>
- RoleUpdate = 31,
-
- /// <summary>
- /// Indicates that the role was deleted.
- /// </summary>
- RoleDelete = 32,
-
- /// <summary>
- /// Indicates that the invite was created.
- /// </summary>
- InviteCreate = 40,
-
- /// <summary>
- /// Indicates that the invite was updated.
- /// </summary>
- InviteUpdate = 41,
-
- /// <summary>
- /// Indicates that the invite was deleted.
- /// </summary>
- InviteDelete = 42,
-
- /// <summary>
- /// Indicates that the webhook was created.
- /// </summary>
- WebhookCreate = 50,
-
- /// <summary>
- /// Indicates that the webook was updated.
- /// </summary>
- WebhookUpdate = 51,
-
- /// <summary>
- /// Indicates that the webhook was deleted.
- /// </summary>
- WebhookDelete = 52,
-
- /// <summary>
- /// Indicates that an emoji was created.
- /// </summary>
- EmojiCreate = 60,
-
- /// <summary>
- /// Indicates that an emoji was updated.
- /// </summary>
- EmojiUpdate = 61,
-
- /// <summary>
- /// Indicates that an emoji was deleted.
- /// </summary>
- EmojiDelete = 62,
-
- /// <summary>
- /// Indicates that the message was deleted.
- /// </summary>
- MessageDelete = 72,
-
- /// <summary>
- /// Indicates that messages were bulk-deleted.
- /// </summary>
- MessageBulkDelete = 73,
-
- /// <summary>
- /// Indicates that a message was pinned.
- /// </summary>
- MessagePin = 74,
-
- /// <summary>
- /// Indicates that a message was unpinned.
- /// </summary>
- MessageUnpin = 75,
-
- /// <summary>
- /// Indicates that an integration was created.
- /// </summary>
- IntegrationCreate = 80,
-
- /// <summary>
- /// Indicates that an integration was updated.
- /// </summary>
- IntegrationUpdate = 81,
-
- /// <summary>
- /// Indicates that an integration was deleted.
- /// </summary>
- IntegrationDelete = 82,
-
- /// <summary>
- /// Indicates that an stage instance was created.
- /// </summary>
- StageInstanceCreate = 83,
-
- /// <summary>
- /// Indicates that an stage instance was updated.
- /// </summary>
- StageInstanceUpdate = 84,
-
- /// <summary>
- /// Indicates that an stage instance was deleted.
- /// </summary>
- StageInstanceDelete = 85,
-
- /// <summary>
- /// Indicates that an sticker was created.
- /// </summary>
- StickerCreate = 90,
-
- /// <summary>
- /// Indicates that an sticker was updated.
- /// </summary>
- StickerUpdate = 91,
-
- /// <summary>
- /// Indicates that an sticker was deleted.
- /// </summary>
- StickerDelete = 92,
-
- /// <summary>
- /// Indicates that an event was created.
- /// </summary>
- GuildScheduledEventCreate = 100,
-
- /// <summary>
- /// Indicates that an event was updated.
- /// </summary>
- GuildScheduledEventUpdate = 101,
-
- /// <summary>
- /// Indicates that an event was deleted.
- /// </summary>
- GuildScheduledEventDelete = 102,
-
- /// <summary>
- /// Indicates that an thread was created.
- /// </summary>
- ThreadCreate = 110,
-
- /// <summary>
- /// Indicates that an thread was updated.
- /// </summary>
- ThreadUpdate = 111,
-
- /// <summary>
- /// Indicates that an thread was deleted.
- /// </summary>
- ThreadDelete = 112,
-
- ApplicationCommandPermissionUpdate = 121,
-
- AutoModerationRuleCreate = 140,
-
- AutoModerationRuleUpdate = 141,
-
- AutoModerationRuleDelete = 142,
-
- AutoModerationBlockMessage = 143
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogGuildScheduledEventEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogGuildScheduledEventEntry() { }
+ }
+
+ /// <summary>
+ /// Represents a audit log thread entry.
+ /// </summary>
+ public sealed class DiscordAuditLogThreadEntry : DiscordAuditLogEntry
+ {
+ /// <summary>
+ /// Gets the affected thread
+ /// </summary>
+ public DiscordThreadChannel Target { get; internal set; }
+
+ /// <summary>
+ /// Gets the name of the thread.
+ /// </summary>
+ public PropertyChange<string> NameChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the type of the thread.
+ /// </summary>
+ public PropertyChange<ChannelType?> TypeChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the archived state of the thread.
+ /// </summary>
+ public PropertyChange<bool?> ArchivedChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the locked state of the thread.
+ /// </summary>
+ public PropertyChange<bool?> LockedChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the invitable state of the thread.
+ /// </summary>
+ public PropertyChange<bool?> InvitableChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the new auto archive duration of the thread.
+ /// </summary>
+ public PropertyChange<ThreadAutoArchiveDuration?> AutoArchiveDurationChange { get; internal set; }
+
+ /// <summary>
+ /// Gets the new ratelimit of the thread.
+ /// </summary>
+ public PropertyChange<int?> PerUserRateLimitChange { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAuditLogThreadEntry"/> class.
+ /// </summary>
+ internal DiscordAuditLogThreadEntry() { }
+ }
+
+ /// <summary>
+ /// Indicates audit log action category.
+ /// </summary>
+ public enum AuditLogActionCategory
+ {
+ /// <summary>
+ /// Indicates that this action resulted in creation or addition of an object.
+ /// </summary>
+ Create,
+
+ /// <summary>
+ /// Indicates that this action resulted in update of an object.
+ /// </summary>
+ Update,
+
+ /// <summary>
+ /// Indicates that this action resulted in deletion or removal of an object.
+ /// </summary>
+ Delete,
+
+ /// <summary>
+ /// Indicates that this action resulted in something else than creation, addition, update, deletion, or removal of an object.
+ /// </summary>
+ Other
+ }
+
+ // below is taken from
+ // https://github.com/Rapptz/discord.py/blob/rewrite/discord/enums.py#L125
+
+ /// <summary>
+ /// Represents type of the action that was taken in given audit log event.
+ /// </summary>
+ public enum AuditLogActionType
+ {
+ /// <summary>
+ /// Indicates an invalid action type.
+ /// </summary>
+ Invalid = 0,
+
+ /// <summary>
+ /// Indicates that the guild was updated.
+ /// </summary>
+ GuildUpdate = 1,
+
+ /// <summary>
+ /// Indicates that the channel was created.
+ /// </summary>
+ ChannelCreate = 10,
+
+ /// <summary>
+ /// Indicates that the channel was updated.
+ /// </summary>
+ ChannelUpdate = 11,
+
+ /// <summary>
+ /// Indicates that the channel was deleted.
+ /// </summary>
+ ChannelDelete = 12,
+
+ /// <summary>
+ /// Indicates that the channel permission overwrite was created.
+ /// </summary>
+ OverwriteCreate = 13,
+
+ /// <summary>
+ /// Indicates that the channel permission overwrite was updated.
+ /// </summary>
+ OverwriteUpdate = 14,
+
+ /// <summary>
+ /// Indicates that the channel permission overwrite was deleted.
+ /// </summary>
+ OverwriteDelete = 15,
+
+ /// <summary>
+ /// Indicates that the user was kicked.
+ /// </summary>
+ Kick = 20,
+
+ /// <summary>
+ /// Indicates that users were pruned.
+ /// </summary>
+ Prune = 21,
+
+ /// <summary>
+ /// Indicates that the user was banned.
+ /// </summary>
+ Ban = 22,
+
+ /// <summary>
+ /// Indicates that the user was unbanned.
+ /// </summary>
+ Unban = 23,
+
+ /// <summary>
+ /// Indicates that the member was updated.
+ /// </summary>
+ MemberUpdate = 24,
+
+ /// <summary>
+ /// Indicates that the member's roles were updated.
+ /// </summary>
+ MemberRoleUpdate = 25,
+
+ /// <summary>
+ /// Indicates that the member has moved to another voice channel.
+ /// </summary>
+ MemberMove = 26,
+
+ /// <summary>
+ /// Indicates that the member has disconnected from a voice channel.
+ /// </summary>
+ MemberDisconnect = 27,
+
+ /// <summary>
+ /// Indicates that a bot was added to the guild.
+ /// </summary>
+ BotAdd = 28,
+
+ /// <summary>
+ /// Indicates that the role was created.
+ /// </summary>
+ RoleCreate = 30,
+
+ /// <summary>
+ /// Indicates that the role was updated.
+ /// </summary>
+ RoleUpdate = 31,
+
+ /// <summary>
+ /// Indicates that the role was deleted.
+ /// </summary>
+ RoleDelete = 32,
+
+ /// <summary>
+ /// Indicates that the invite was created.
+ /// </summary>
+ InviteCreate = 40,
+
+ /// <summary>
+ /// Indicates that the invite was updated.
+ /// </summary>
+ InviteUpdate = 41,
+
+ /// <summary>
+ /// Indicates that the invite was deleted.
+ /// </summary>
+ InviteDelete = 42,
+
+ /// <summary>
+ /// Indicates that the webhook was created.
+ /// </summary>
+ WebhookCreate = 50,
+
+ /// <summary>
+ /// Indicates that the webook was updated.
+ /// </summary>
+ WebhookUpdate = 51,
+
+ /// <summary>
+ /// Indicates that the webhook was deleted.
+ /// </summary>
+ WebhookDelete = 52,
+
+ /// <summary>
+ /// Indicates that an emoji was created.
+ /// </summary>
+ EmojiCreate = 60,
+
+ /// <summary>
+ /// Indicates that an emoji was updated.
+ /// </summary>
+ EmojiUpdate = 61,
+
+ /// <summary>
+ /// Indicates that an emoji was deleted.
+ /// </summary>
+ EmojiDelete = 62,
+
+ /// <summary>
+ /// Indicates that the message was deleted.
+ /// </summary>
+ MessageDelete = 72,
+
+ /// <summary>
+ /// Indicates that messages were bulk-deleted.
+ /// </summary>
+ MessageBulkDelete = 73,
+
+ /// <summary>
+ /// Indicates that a message was pinned.
+ /// </summary>
+ MessagePin = 74,
+
+ /// <summary>
+ /// Indicates that a message was unpinned.
+ /// </summary>
+ MessageUnpin = 75,
+
+ /// <summary>
+ /// Indicates that an integration was created.
+ /// </summary>
+ IntegrationCreate = 80,
+
+ /// <summary>
+ /// Indicates that an integration was updated.
+ /// </summary>
+ IntegrationUpdate = 81,
+
+ /// <summary>
+ /// Indicates that an integration was deleted.
+ /// </summary>
+ IntegrationDelete = 82,
+
+ /// <summary>
+ /// Indicates that an stage instance was created.
+ /// </summary>
+ StageInstanceCreate = 83,
+
+ /// <summary>
+ /// Indicates that an stage instance was updated.
+ /// </summary>
+ StageInstanceUpdate = 84,
+
+ /// <summary>
+ /// Indicates that an stage instance was deleted.
+ /// </summary>
+ StageInstanceDelete = 85,
+
+ /// <summary>
+ /// Indicates that an sticker was created.
+ /// </summary>
+ StickerCreate = 90,
+
+ /// <summary>
+ /// Indicates that an sticker was updated.
+ /// </summary>
+ StickerUpdate = 91,
+
+ /// <summary>
+ /// Indicates that an sticker was deleted.
+ /// </summary>
+ StickerDelete = 92,
+
+ /// <summary>
+ /// Indicates that an event was created.
+ /// </summary>
+ GuildScheduledEventCreate = 100,
+
+ /// <summary>
+ /// Indicates that an event was updated.
+ /// </summary>
+ GuildScheduledEventUpdate = 101,
+
+ /// <summary>
+ /// Indicates that an event was deleted.
+ /// </summary>
+ GuildScheduledEventDelete = 102,
+
+ /// <summary>
+ /// Indicates that an thread was created.
+ /// </summary>
+ ThreadCreate = 110,
+
+ /// <summary>
+ /// Indicates that an thread was updated.
+ /// </summary>
+ ThreadUpdate = 111,
+
+ /// <summary>
+ /// Indicates that an thread was deleted.
+ /// </summary>
+ ThreadDelete = 112,
+
+ ApplicationCommandPermissionUpdate = 121,
+
+ AutoModerationRuleCreate = 140,
+
+ AutoModerationRuleUpdate = 141,
+
+ AutoModerationRuleDelete = 142,
+
+ AutoModerationBlockMessage = 143
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordBan.cs b/DisCatSharp/Entities/Guild/DiscordBan.cs
index fac19dfae..40b83849a 100644
--- a/DisCatSharp/Entities/Guild/DiscordBan.cs
+++ b/DisCatSharp/Entities/Guild/DiscordBan.cs
@@ -1,57 +1,58 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Net.Abstractions;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord ban
-/// </summary>
-public class DiscordBan
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the reason for the ban
+ /// Represents a Discord ban
/// </summary>
- [JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)]
- public string Reason { get; internal set; }
+ public class DiscordBan
+ {
+ /// <summary>
+ /// Gets the reason for the ban
+ /// </summary>
+ [JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)]
+ public string Reason { get; internal set; }
- /// <summary>
- /// Gets the banned user
- /// </summary>
- [JsonIgnore]
- public DiscordUser User { get; internal set; }
+ /// <summary>
+ /// Gets the banned user
+ /// </summary>
+ [JsonIgnore]
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the raw user.
- /// </summary>
- [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
- internal TransportUser RawUser { get; set; }
+ /// <summary>
+ /// Gets the raw user.
+ /// </summary>
+ [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
+ internal TransportUser RawUser { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordBan"/> class.
- /// </summary>
- internal DiscordBan()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordBan"/> class.
+ /// </summary>
+ internal DiscordBan()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuild.AuditLog.cs b/DisCatSharp/Entities/Guild/DiscordGuild.AuditLog.cs
index 6a8b98d62..8e2917e4e 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuild.AuditLog.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuild.AuditLog.cs
@@ -1,1293 +1,1294 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Entities;
-
-public partial class DiscordGuild {
- // TODO: Rework audit logs!
- /// <summary>
- /// Gets audit log entries for this guild.
- /// </summary>
- /// <param name="limit">Maximum number of entries to fetch.</param>
- /// <param name="byMember">Filter by member responsible.</param>
- /// <param name="actionType">Filter by action type.</param>
- /// <returns>A collection of requested audit log entries.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ViewAuditLog"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<IReadOnlyList<DiscordAuditLogEntry>> GetAuditLogsAsync(int? limit = null, DiscordMember byMember = null, AuditLogActionType? actionType = null)
- {
- var alrs = new List<AuditLog>();
- int ac = 1, tc = 0, rmn = 100;
- var last = 0ul;
- while (ac > 0)
+namespace DisCatSharp.Entities
+{
+ public partial class DiscordGuild {
+ // TODO: Rework audit logs!
+ /// <summary>
+ /// Gets audit log entries for this guild.
+ /// </summary>
+ /// <param name="limit">Maximum number of entries to fetch.</param>
+ /// <param name="byMember">Filter by member responsible.</param>
+ /// <param name="actionType">Filter by action type.</param>
+ /// <returns>A collection of requested audit log entries.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ViewAuditLog"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<IReadOnlyList<DiscordAuditLogEntry>> GetAuditLogsAsync(int? limit = null, DiscordMember byMember = null, AuditLogActionType? actionType = null)
{
- rmn = limit != null ? limit.Value - tc : 100;
- rmn = Math.Min(100, rmn);
- if (rmn <= 0) break;
-
- var alr = await this.Discord.ApiClient.GetAuditLogsAsync(this.Id, rmn, null, last == 0 ? null : (ulong?)last, byMember?.Id, (int?)actionType).ConfigureAwait(false);
- ac = alr.Entries.Count();
- tc += ac;
- if (ac > 0)
+ var alrs = new List<AuditLog>();
+ int ac = 1, tc = 0, rmn = 100;
+ var last = 0ul;
+ while (ac > 0)
{
- last = alr.Entries.Last().Id;
- alrs.Add(alr);
+ rmn = limit != null ? limit.Value - tc : 100;
+ rmn = Math.Min(100, rmn);
+ if (rmn <= 0) break;
+
+ var alr = await this.Discord.ApiClient.GetAuditLogsAsync(this.Id, rmn, null, last == 0 ? null : (ulong?)last, byMember?.Id, (int?)actionType).ConfigureAwait(false);
+ ac = alr.Entries.Count();
+ tc += ac;
+ if (ac > 0)
+ {
+ last = alr.Entries.Last().Id;
+ alrs.Add(alr);
+ }
}
- }
- var amr = alrs.SelectMany(xa => xa.Users)
- .GroupBy(xu => xu.Id)
- .Select(xgu => xgu.First());
+ var amr = alrs.SelectMany(xa => xa.Users)
+ .GroupBy(xu => xu.Id)
+ .Select(xgu => xgu.First());
- foreach (var xau in amr)
- {
- if (this.Discord.UserCache.ContainsKey(xau.Id))
- continue;
-
- var xtu = new TransportUser
- {
- Id = xau.Id,
- Username = xau.Username,
- Discriminator = xau.Discriminator,
- AvatarHash = xau.AvatarHash
- };
- var xu = new DiscordUser(xtu) { Discord = this.Discord };
- xu = this.Discord.UserCache.AddOrUpdate(xu.Id, xu, (id, old) =>
+ foreach (var xau in amr)
{
- old.Username = xu.Username;
- old.Discriminator = xu.Discriminator;
- old.AvatarHash = xu.AvatarHash;
- return old;
- });
- }
+ if (this.Discord.UserCache.ContainsKey(xau.Id))
+ continue;
- var atgse = alrs.SelectMany(xa => xa.ScheduledEvents)
- .GroupBy(xse => xse.Id)
- .Select(xgse => xgse.First());
+ var xtu = new TransportUser
+ {
+ Id = xau.Id,
+ Username = xau.Username,
+ Discriminator = xau.Discriminator,
+ AvatarHash = xau.AvatarHash
+ };
+ var xu = new DiscordUser(xtu) { Discord = this.Discord };
+ xu = this.Discord.UserCache.AddOrUpdate(xu.Id, xu, (id, old) =>
+ {
+ old.Username = xu.Username;
+ old.Discriminator = xu.Discriminator;
+ old.AvatarHash = xu.AvatarHash;
+ return old;
+ });
+ }
- var ath = alrs.SelectMany(xa => xa.Threads)
- .GroupBy(xt => xt.Id)
- .Select(xgt => xgt.First());
+ var atgse = alrs.SelectMany(xa => xa.ScheduledEvents)
+ .GroupBy(xse => xse.Id)
+ .Select(xgse => xgse.First());
- var aig = alrs.SelectMany(xa => xa.Integrations)
- .GroupBy(xi => xi.Id)
- .Select(xgi => xgi.First());
+ var ath = alrs.SelectMany(xa => xa.Threads)
+ .GroupBy(xt => xt.Id)
+ .Select(xgt => xgt.First());
- var ahr = alrs.SelectMany(xa => xa.Webhooks)
- .GroupBy(xh => xh.Id)
- .Select(xgh => xgh.First());
+ var aig = alrs.SelectMany(xa => xa.Integrations)
+ .GroupBy(xi => xi.Id)
+ .Select(xgi => xgi.First());
- var ams = amr.Select(xau => this.MembersInternal != null && this.MembersInternal.TryGetValue(xau.Id, out var member) ? member : new DiscordMember { Discord = this.Discord, Id = xau.Id, GuildId = this.Id });
- var amd = ams.ToDictionary(xm => xm.Id, xm => xm);
+ var ahr = alrs.SelectMany(xa => xa.Webhooks)
+ .GroupBy(xh => xh.Id)
+ .Select(xgh => xgh.First());
+
+ var ams = amr.Select(xau => this.MembersInternal != null && this.MembersInternal.TryGetValue(xau.Id, out var member) ? member : new DiscordMember { Discord = this.Discord, Id = xau.Id, GuildId = this.Id });
+ var amd = ams.ToDictionary(xm => xm.Id, xm => xm);
#pragma warning disable CS0219
- Dictionary<ulong, DiscordThreadChannel> dtc = null;
- Dictionary<ulong, DiscordIntegration> di = null;
- Dictionary<ulong, DiscordScheduledEvent> dse = null;
+ Dictionary<ulong, DiscordThreadChannel> dtc = null;
+ Dictionary<ulong, DiscordIntegration> di = null;
+ Dictionary<ulong, DiscordScheduledEvent> dse = null;
#pragma warning restore
- Dictionary<ulong, DiscordWebhook> ahd = null;
- if (ahr.Any())
- {
- var whr = await this.GetWebhooksAsync().ConfigureAwait(false);
- var whs = whr.ToDictionary(xh => xh.Id, xh => xh);
+ Dictionary<ulong, DiscordWebhook> ahd = null;
+ if (ahr.Any())
+ {
+ var whr = await this.GetWebhooksAsync().ConfigureAwait(false);
+ var whs = whr.ToDictionary(xh => xh.Id, xh => xh);
- var amh = ahr.Select(xah => whs.TryGetValue(xah.Id, out var webhook) ? webhook : new DiscordWebhook { Discord = this.Discord, Name = xah.Name, Id = xah.Id, AvatarHash = xah.AvatarHash, ChannelId = xah.ChannelId, GuildId = xah.GuildId, Token = xah.Token });
- ahd = amh.ToDictionary(xh => xh.Id, xh => xh);
- }
+ var amh = ahr.Select(xah => whs.TryGetValue(xah.Id, out var webhook) ? webhook : new DiscordWebhook { Discord = this.Discord, Name = xah.Name, Id = xah.Id, AvatarHash = xah.AvatarHash, ChannelId = xah.ChannelId, GuildId = xah.GuildId, Token = xah.Token });
+ ahd = amh.ToDictionary(xh => xh.Id, xh => xh);
+ }
- var acs = alrs.SelectMany(xa => xa.Entries).OrderByDescending(xa => xa.Id);
- var entries = new List<DiscordAuditLogEntry>();
- foreach (var xac in acs)
- {
- DiscordAuditLogEntry entry = null;
- ulong t1, t2;
- int t3, t4;
- long t5, t6;
- bool p1, p2;
- switch (xac.ActionType)
+ var acs = alrs.SelectMany(xa => xa.Entries).OrderByDescending(xa => xa.Id);
+ var entries = new List<DiscordAuditLogEntry>();
+ foreach (var xac in acs)
{
- case AuditLogActionType.Invalid:
- break;
-
- case AuditLogActionType.GuildUpdate:
- entry = new DiscordAuditLogGuildEntry
- {
- Target = this
- };
+ DiscordAuditLogEntry entry = null;
+ ulong t1, t2;
+ int t3, t4;
+ long t5, t6;
+ bool p1, p2;
+ switch (xac.ActionType)
+ {
+ case AuditLogActionType.Invalid:
+ break;
- var entrygld = entry as DiscordAuditLogGuildEntry;
- foreach (var xc in xac.Changes)
- {
- PropertyChange<DiscordChannel> GetChannelChange()
+ case AuditLogActionType.GuildUpdate:
+ entry = new DiscordAuditLogGuildEntry
{
- ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
-
- return new PropertyChange<DiscordChannel>
- {
- Before = this.GetChannel(t1) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id },
- After = this.GetChannel(t2) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id }
- };
- }
+ Target = this
+ };
- switch (xc.Key.ToLowerInvariant())
+ var entrygld = entry as DiscordAuditLogGuildEntry;
+ foreach (var xc in xac.Changes)
{
- case "name":
- entrygld.NameChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "owner_id":
- entrygld.OwnerChange = new PropertyChange<DiscordMember>
- {
- Before = this.MembersInternal != null && this.MembersInternal.TryGetValue(xc.OldValueUlong, out var oldMember) ? oldMember : await this.GetMemberAsync(xc.OldValueUlong).ConfigureAwait(false),
- After = this.MembersInternal != null && this.MembersInternal.TryGetValue(xc.NewValueUlong, out var newMember) ? newMember : await this.GetMemberAsync(xc.NewValueUlong).ConfigureAwait(false)
- };
- break;
-
- case "icon_hash":
- entrygld.IconChange = new PropertyChange<string>
- {
- Before = xc.OldValueString != null ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id}/{xc.OldValueString}.webp" : null,
- After = xc.OldValueString != null ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id}/{xc.NewValueString}.webp" : null
- };
- break;
-
- case "verification_level":
- entrygld.VerificationLevelChange = new PropertyChange<VerificationLevel>
- {
- Before = (VerificationLevel)(long)xc.OldValue,
- After = (VerificationLevel)(long)xc.NewValue
- };
- break;
-
- case "afk_channel_id":
- entrygld.AfkChannelChange = GetChannelChange();
- break;
-
- case "system_channel_flags":
- entrygld.SystemChannelFlagsChange = new PropertyChange<SystemChannelFlags>()
- {
- Before = (SystemChannelFlags)(long)xc.OldValue,
- After = (SystemChannelFlags)(long)xc.NewValue
- };
- break;
-
- case "widget_channel_id":
- entrygld.WidgetChannelChange = GetChannelChange();
- break;
-
- case "rules_channel_id":
- entrygld.RulesChannelChange = GetChannelChange();
- break;
-
- case "public_updates_channel_id":
- entrygld.PublicUpdatesChannelChange = GetChannelChange();
- break;
-
- case "splash_hash":
- entrygld.SplashChange = new PropertyChange<string>
- {
- Before = xc.OldValueString != null ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id}/{xc.OldValueString}.webp?size=2048" : null,
- After = xc.NewValueString != null ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id}/{xc.NewValueString}.webp?size=2048" : null
- };
- break;
-
- case "default_message_notifications":
- entrygld.NotificationSettingsChange = new PropertyChange<DefaultMessageNotifications>
- {
- Before = (DefaultMessageNotifications)(long)xc.OldValue,
- After = (DefaultMessageNotifications)(long)xc.NewValue
- };
- break;
-
- case "system_channel_id":
- entrygld.SystemChannelChange = GetChannelChange();
- break;
-
- case "explicit_content_filter":
- entrygld.ExplicitContentFilterChange = new PropertyChange<ExplicitContentFilter>
- {
- Before = (ExplicitContentFilter)(long)xc.OldValue,
- After = (ExplicitContentFilter)(long)xc.NewValue
- };
- break;
-
- case "mfa_level":
- entrygld.MfaLevelChange = new PropertyChange<MfaLevel>
- {
- Before = (MfaLevel)(long)xc.OldValue,
- After = (MfaLevel)(long)xc.NewValue
- };
- break;
-
- case "region":
- entrygld.RegionChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "vanity_url_code":
- entrygld.VanityUrlCodeChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
+ PropertyChange<DiscordChannel> GetChannelChange()
+ {
+ ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
- case "premium_progress_bar_enabled":
- entrygld.PremiumProgressBarChange = new PropertyChange<bool>
+ return new PropertyChange<DiscordChannel>
{
- Before = (bool)xc.OldValue,
- After = (bool)xc.NewValue
+ Before = this.GetChannel(t1) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id },
+ After = this.GetChannel(t2) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id }
};
- break;
+ }
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in guild update: {0} - this should be reported to library developers", xc.Key);
- break;
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "name":
+ entrygld.NameChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "owner_id":
+ entrygld.OwnerChange = new PropertyChange<DiscordMember>
+ {
+ Before = this.MembersInternal != null && this.MembersInternal.TryGetValue(xc.OldValueUlong, out var oldMember) ? oldMember : await this.GetMemberAsync(xc.OldValueUlong).ConfigureAwait(false),
+ After = this.MembersInternal != null && this.MembersInternal.TryGetValue(xc.NewValueUlong, out var newMember) ? newMember : await this.GetMemberAsync(xc.NewValueUlong).ConfigureAwait(false)
+ };
+ break;
+
+ case "icon_hash":
+ entrygld.IconChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString != null ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id}/{xc.OldValueString}.webp" : null,
+ After = xc.OldValueString != null ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id}/{xc.NewValueString}.webp" : null
+ };
+ break;
+
+ case "verification_level":
+ entrygld.VerificationLevelChange = new PropertyChange<VerificationLevel>
+ {
+ Before = (VerificationLevel)(long)xc.OldValue,
+ After = (VerificationLevel)(long)xc.NewValue
+ };
+ break;
+
+ case "afk_channel_id":
+ entrygld.AfkChannelChange = GetChannelChange();
+ break;
+
+ case "system_channel_flags":
+ entrygld.SystemChannelFlagsChange = new PropertyChange<SystemChannelFlags>()
+ {
+ Before = (SystemChannelFlags)(long)xc.OldValue,
+ After = (SystemChannelFlags)(long)xc.NewValue
+ };
+ break;
+
+ case "widget_channel_id":
+ entrygld.WidgetChannelChange = GetChannelChange();
+ break;
+
+ case "rules_channel_id":
+ entrygld.RulesChannelChange = GetChannelChange();
+ break;
+
+ case "public_updates_channel_id":
+ entrygld.PublicUpdatesChannelChange = GetChannelChange();
+ break;
+
+ case "splash_hash":
+ entrygld.SplashChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString != null ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id}/{xc.OldValueString}.webp?size=2048" : null,
+ After = xc.NewValueString != null ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id}/{xc.NewValueString}.webp?size=2048" : null
+ };
+ break;
+
+ case "default_message_notifications":
+ entrygld.NotificationSettingsChange = new PropertyChange<DefaultMessageNotifications>
+ {
+ Before = (DefaultMessageNotifications)(long)xc.OldValue,
+ After = (DefaultMessageNotifications)(long)xc.NewValue
+ };
+ break;
+
+ case "system_channel_id":
+ entrygld.SystemChannelChange = GetChannelChange();
+ break;
+
+ case "explicit_content_filter":
+ entrygld.ExplicitContentFilterChange = new PropertyChange<ExplicitContentFilter>
+ {
+ Before = (ExplicitContentFilter)(long)xc.OldValue,
+ After = (ExplicitContentFilter)(long)xc.NewValue
+ };
+ break;
+
+ case "mfa_level":
+ entrygld.MfaLevelChange = new PropertyChange<MfaLevel>
+ {
+ Before = (MfaLevel)(long)xc.OldValue,
+ After = (MfaLevel)(long)xc.NewValue
+ };
+ break;
+
+ case "region":
+ entrygld.RegionChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "vanity_url_code":
+ entrygld.VanityUrlCodeChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "premium_progress_bar_enabled":
+ entrygld.PremiumProgressBarChange = new PropertyChange<bool>
+ {
+ Before = (bool)xc.OldValue,
+ After = (bool)xc.NewValue
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in guild update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
}
- }
- break;
-
- case AuditLogActionType.ChannelCreate:
- case AuditLogActionType.ChannelDelete:
- case AuditLogActionType.ChannelUpdate:
- entry = new DiscordAuditLogChannelEntry
- {
- Target = this.GetChannel(xac.TargetId.Value) ?? new DiscordChannel { Id = xac.TargetId.Value, Discord = this.Discord, GuildId = this.Id }
- };
+ break;
- var entrychn = entry as DiscordAuditLogChannelEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ case AuditLogActionType.ChannelCreate:
+ case AuditLogActionType.ChannelDelete:
+ case AuditLogActionType.ChannelUpdate:
+ entry = new DiscordAuditLogChannelEntry
{
- case "name":
- entrychn.NameChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "type":
- p1 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- p2 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
-
- entrychn.TypeChange = new PropertyChange<ChannelType?>
- {
- Before = p1 ? (ChannelType?)t1 : null,
- After = p2 ? (ChannelType?)t2 : null
- };
- break;
-
- case "permission_overwrites":
- var olds = xc.OldValues?.OfType<JObject>()
- ?.Select(xjo => xjo.ToObject<DiscordOverwrite>())
- ?.Select(xo => { xo.Discord = this.Discord; return xo; });
-
- var news = xc.NewValues?.OfType<JObject>()
- ?.Select(xjo => xjo.ToObject<DiscordOverwrite>())
- ?.Select(xo => { xo.Discord = this.Discord; return xo; });
+ Target = this.GetChannel(xac.TargetId.Value) ?? new DiscordChannel { Id = xac.TargetId.Value, Discord = this.Discord, GuildId = this.Id }
+ };
- entrychn.OverwriteChange = new PropertyChange<IReadOnlyList<DiscordOverwrite>>
- {
- Before = olds != null ? new ReadOnlyCollection<DiscordOverwrite>(new List<DiscordOverwrite>(olds)) : null,
- After = news != null ? new ReadOnlyCollection<DiscordOverwrite>(new List<DiscordOverwrite>(news)) : null
- };
- break;
-
- case "topic":
- entrychn.TopicChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "nsfw":
- entrychn.NsfwChange = new PropertyChange<bool?>
- {
- Before = (bool?)xc.OldValue,
- After = (bool?)xc.NewValue
- };
- break;
-
- case "rtc_region":
- entrychn.RtcRegionIdChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "bitrate":
- entrychn.BitrateChange = new PropertyChange<int?>
- {
- Before = (int?)(long?)xc.OldValue,
- After = (int?)(long?)xc.NewValue
- };
- break;
-
- case "user_limit":
- entrychn.UserLimitChange = new PropertyChange<int?>
- {
- Before = (int?)(long?)xc.OldValue,
- After = (int?)(long?)xc.NewValue
- };
- break;
-
- case "rate_limit_per_user":
- entrychn.PerUserRateLimitChange = new PropertyChange<int?>
- {
- Before = (int?)(long?)xc.OldValue,
- After = (int?)(long?)xc.NewValue
- };
- break;
-
- case "default_auto_archive_duration":
- entrychn.DefaultAutoArchiveDurationChange = new PropertyChange<ThreadAutoArchiveDuration?>
- {
- Before = (ThreadAutoArchiveDuration?)(long?)xc.OldValue,
- After = (ThreadAutoArchiveDuration?)(long?)xc.NewValue
- };
- break;
-
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in channel update: {0} - this should be reported to library developers", xc.Key);
- break;
+ var entrychn = entry as DiscordAuditLogChannelEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "name":
+ entrychn.NameChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "type":
+ p1 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ p2 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+
+ entrychn.TypeChange = new PropertyChange<ChannelType?>
+ {
+ Before = p1 ? (ChannelType?)t1 : null,
+ After = p2 ? (ChannelType?)t2 : null
+ };
+ break;
+
+ case "permission_overwrites":
+ var olds = xc.OldValues?.OfType<JObject>()
+ ?.Select(xjo => xjo.ToObject<DiscordOverwrite>())
+ ?.Select(xo => { xo.Discord = this.Discord; return xo; });
+
+ var news = xc.NewValues?.OfType<JObject>()
+ ?.Select(xjo => xjo.ToObject<DiscordOverwrite>())
+ ?.Select(xo => { xo.Discord = this.Discord; return xo; });
+
+ entrychn.OverwriteChange = new PropertyChange<IReadOnlyList<DiscordOverwrite>>
+ {
+ Before = olds != null ? new ReadOnlyCollection<DiscordOverwrite>(new List<DiscordOverwrite>(olds)) : null,
+ After = news != null ? new ReadOnlyCollection<DiscordOverwrite>(new List<DiscordOverwrite>(news)) : null
+ };
+ break;
+
+ case "topic":
+ entrychn.TopicChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "nsfw":
+ entrychn.NsfwChange = new PropertyChange<bool?>
+ {
+ Before = (bool?)xc.OldValue,
+ After = (bool?)xc.NewValue
+ };
+ break;
+
+ case "rtc_region":
+ entrychn.RtcRegionIdChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "bitrate":
+ entrychn.BitrateChange = new PropertyChange<int?>
+ {
+ Before = (int?)(long?)xc.OldValue,
+ After = (int?)(long?)xc.NewValue
+ };
+ break;
+
+ case "user_limit":
+ entrychn.UserLimitChange = new PropertyChange<int?>
+ {
+ Before = (int?)(long?)xc.OldValue,
+ After = (int?)(long?)xc.NewValue
+ };
+ break;
+
+ case "rate_limit_per_user":
+ entrychn.PerUserRateLimitChange = new PropertyChange<int?>
+ {
+ Before = (int?)(long?)xc.OldValue,
+ After = (int?)(long?)xc.NewValue
+ };
+ break;
+
+ case "default_auto_archive_duration":
+ entrychn.DefaultAutoArchiveDurationChange = new PropertyChange<ThreadAutoArchiveDuration?>
+ {
+ Before = (ThreadAutoArchiveDuration?)(long?)xc.OldValue,
+ After = (ThreadAutoArchiveDuration?)(long?)xc.NewValue
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in channel update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
}
- }
- break;
-
- case AuditLogActionType.OverwriteCreate:
- case AuditLogActionType.OverwriteDelete:
- case AuditLogActionType.OverwriteUpdate:
- entry = new DiscordAuditLogOverwriteEntry
- {
- Target = this.GetChannel(xac.TargetId.Value)?.PermissionOverwrites.FirstOrDefault(xo => xo.Id == xac.Options.Id),
- Channel = this.GetChannel(xac.TargetId.Value)
- };
+ break;
- var entryovr = entry as DiscordAuditLogOverwriteEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ case AuditLogActionType.OverwriteCreate:
+ case AuditLogActionType.OverwriteDelete:
+ case AuditLogActionType.OverwriteUpdate:
+ entry = new DiscordAuditLogOverwriteEntry
{
- case "deny":
- p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- p2 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
-
- entryovr.DenyChange = new PropertyChange<Permissions?>
- {
- Before = p1 ? (Permissions?)t1 : null,
- After = p2 ? (Permissions?)t2 : null
- };
- break;
-
- case "allow":
- p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- p2 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
-
- entryovr.AllowChange = new PropertyChange<Permissions?>
- {
- Before = p1 ? (Permissions?)t1 : null,
- After = p2 ? (Permissions?)t2 : null
- };
- break;
+ Target = this.GetChannel(xac.TargetId.Value)?.PermissionOverwrites.FirstOrDefault(xo => xo.Id == xac.Options.Id),
+ Channel = this.GetChannel(xac.TargetId.Value)
+ };
- case "type":
- entryovr.TypeChange = new PropertyChange<OverwriteType?>
- {
- Before = xc.OldValue != null ? (OverwriteType)(long)xc.OldValue : null,
- After = xc.NewValue != null ? (OverwriteType)(long)xc.NewValue : null
- };
- break;
-
- case "id":
- p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
-
- entryovr.TargetIdChange = new PropertyChange<ulong?>
- {
- Before = p1 ? (ulong?)t1 : null,
- After = p2 ? (ulong?)t2 : null
- };
- break;
-
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in overwrite update: {0} - this should be reported to library developers", xc.Key);
- break;
+ var entryovr = entry as DiscordAuditLogOverwriteEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "deny":
+ p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ p2 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+
+ entryovr.DenyChange = new PropertyChange<Permissions?>
+ {
+ Before = p1 ? (Permissions?)t1 : null,
+ After = p2 ? (Permissions?)t2 : null
+ };
+ break;
+
+ case "allow":
+ p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ p2 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+
+ entryovr.AllowChange = new PropertyChange<Permissions?>
+ {
+ Before = p1 ? (Permissions?)t1 : null,
+ After = p2 ? (Permissions?)t2 : null
+ };
+ break;
+
+ case "type":
+ entryovr.TypeChange = new PropertyChange<OverwriteType?>
+ {
+ Before = xc.OldValue != null ? (OverwriteType)(long)xc.OldValue : null,
+ After = xc.NewValue != null ? (OverwriteType)(long)xc.NewValue : null
+ };
+ break;
+
+ case "id":
+ p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+
+ entryovr.TargetIdChange = new PropertyChange<ulong?>
+ {
+ Before = p1 ? (ulong?)t1 : null,
+ After = p2 ? (ulong?)t2 : null
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in overwrite update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
}
- }
- break;
-
- case AuditLogActionType.Kick:
- entry = new DiscordAuditLogKickEntry
- {
- Target = amd.TryGetValue(xac.TargetId.Value, out var kickMember) ? kickMember : new DiscordMember { Id = xac.TargetId.Value, Discord = this.Discord, GuildId = this.Id }
- };
- break;
-
- case AuditLogActionType.Prune:
- entry = new DiscordAuditLogPruneEntry
- {
- Days = xac.Options.DeleteMemberDays,
- Toll = xac.Options.MembersRemoved
- };
- break;
-
- case AuditLogActionType.Ban:
- case AuditLogActionType.Unban:
- entry = new DiscordAuditLogBanEntry
- {
- Target = amd.TryGetValue(xac.TargetId.Value, out var unbanMember) ? unbanMember : new DiscordMember { Id = xac.TargetId.Value, Discord = this.Discord, GuildId = this.Id }
- };
- break;
-
- case AuditLogActionType.MemberUpdate:
- case AuditLogActionType.MemberRoleUpdate:
- entry = new DiscordAuditLogMemberUpdateEntry
- {
- Target = amd.TryGetValue(xac.TargetId.Value, out var roleUpdMember) ? roleUpdMember : new DiscordMember { Id = xac.TargetId.Value, Discord = this.Discord, GuildId = this.Id }
- };
+ break;
- var entrymbu = entry as DiscordAuditLogMemberUpdateEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ case AuditLogActionType.Kick:
+ entry = new DiscordAuditLogKickEntry
{
- case "nick":
- entrymbu.NicknameChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "deaf":
- entrymbu.DeafenChange = new PropertyChange<bool?>
- {
- Before = (bool?)xc.OldValue,
- After = (bool?)xc.NewValue
- };
- break;
+ Target = amd.TryGetValue(xac.TargetId.Value, out var kickMember) ? kickMember : new DiscordMember { Id = xac.TargetId.Value, Discord = this.Discord, GuildId = this.Id }
+ };
+ break;
- case "mute":
- entrymbu.MuteChange = new PropertyChange<bool?>
- {
- Before = (bool?)xc.OldValue,
- After = (bool?)xc.NewValue
- };
- break;
- case "communication_disabled_until":
- entrymbu.CommunicationDisabledUntilChange = new PropertyChange<DateTime?>
- {
- Before = (DateTime?)xc.OldValue,
- After = (DateTime?)xc.NewValue
- };
- break;
+ case AuditLogActionType.Prune:
+ entry = new DiscordAuditLogPruneEntry
+ {
+ Days = xac.Options.DeleteMemberDays,
+ Toll = xac.Options.MembersRemoved
+ };
+ break;
- case "$add":
- entrymbu.AddedRoles = new ReadOnlyCollection<DiscordRole>(xc.NewValues.Select(xo => (ulong)xo["id"]).Select(this.GetRole).ToList());
- break;
+ case AuditLogActionType.Ban:
+ case AuditLogActionType.Unban:
+ entry = new DiscordAuditLogBanEntry
+ {
+ Target = amd.TryGetValue(xac.TargetId.Value, out var unbanMember) ? unbanMember : new DiscordMember { Id = xac.TargetId.Value, Discord = this.Discord, GuildId = this.Id }
+ };
+ break;
- case "$remove":
- entrymbu.RemovedRoles = new ReadOnlyCollection<DiscordRole>(xc.NewValues.Select(xo => (ulong)xo["id"]).Select(this.GetRole).ToList());
- break;
+ case AuditLogActionType.MemberUpdate:
+ case AuditLogActionType.MemberRoleUpdate:
+ entry = new DiscordAuditLogMemberUpdateEntry
+ {
+ Target = amd.TryGetValue(xac.TargetId.Value, out var roleUpdMember) ? roleUpdMember : new DiscordMember { Id = xac.TargetId.Value, Discord = this.Discord, GuildId = this.Id }
+ };
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in member update: {0} - this should be reported to library developers", xc.Key);
- break;
+ var entrymbu = entry as DiscordAuditLogMemberUpdateEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "nick":
+ entrymbu.NicknameChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "deaf":
+ entrymbu.DeafenChange = new PropertyChange<bool?>
+ {
+ Before = (bool?)xc.OldValue,
+ After = (bool?)xc.NewValue
+ };
+ break;
+
+ case "mute":
+ entrymbu.MuteChange = new PropertyChange<bool?>
+ {
+ Before = (bool?)xc.OldValue,
+ After = (bool?)xc.NewValue
+ };
+ break;
+ case "communication_disabled_until":
+ entrymbu.CommunicationDisabledUntilChange = new PropertyChange<DateTime?>
+ {
+ Before = (DateTime?)xc.OldValue,
+ After = (DateTime?)xc.NewValue
+ };
+ break;
+
+ case "$add":
+ entrymbu.AddedRoles = new ReadOnlyCollection<DiscordRole>(xc.NewValues.Select(xo => (ulong)xo["id"]).Select(this.GetRole).ToList());
+ break;
+
+ case "$remove":
+ entrymbu.RemovedRoles = new ReadOnlyCollection<DiscordRole>(xc.NewValues.Select(xo => (ulong)xo["id"]).Select(this.GetRole).ToList());
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in member update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
}
- }
- break;
-
- case AuditLogActionType.RoleCreate:
- case AuditLogActionType.RoleDelete:
- case AuditLogActionType.RoleUpdate:
- entry = new DiscordAuditLogRoleUpdateEntry
- {
- Target = this.GetRole(xac.TargetId.Value) ?? new DiscordRole { Id = xac.TargetId.Value, Discord = this.Discord }
- };
+ break;
- var entryrol = entry as DiscordAuditLogRoleUpdateEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ case AuditLogActionType.RoleCreate:
+ case AuditLogActionType.RoleDelete:
+ case AuditLogActionType.RoleUpdate:
+ entry = new DiscordAuditLogRoleUpdateEntry
{
- case "name":
- entryrol.NameChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "color":
- p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
- p2 = int.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
-
- entryrol.ColorChange = new PropertyChange<int?>
- {
- Before = p1 ? (int?)t3 : null,
- After = p2 ? (int?)t4 : null
- };
- break;
-
- case "permissions":
- entryrol.PermissionChange = new PropertyChange<Permissions?>
- {
- Before = xc.OldValue != null ? (Permissions?)long.Parse((string)xc.OldValue) : null,
- After = xc.NewValue != null ? (Permissions?)long.Parse((string)xc.NewValue) : null
- };
- break;
-
- case "position":
- entryrol.PositionChange = new PropertyChange<int?>
- {
- Before = xc.OldValue != null ? (int?)(long)xc.OldValue : null,
- After = xc.NewValue != null ? (int?)(long)xc.NewValue : null,
- };
- break;
-
- case "mentionable":
- entryrol.MentionableChange = new PropertyChange<bool?>
- {
- Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
- After = xc.NewValue != null ? (bool?)xc.NewValue : null
- };
- break;
-
- case "hoist":
- entryrol.HoistChange = new PropertyChange<bool?>
- {
- Before = (bool?)xc.OldValue,
- After = (bool?)xc.NewValue
- };
- break;
-
- case "icon_hash":
- entryrol.IconHashChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
+ Target = this.GetRole(xac.TargetId.Value) ?? new DiscordRole { Id = xac.TargetId.Value, Discord = this.Discord }
+ };
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in role update: {0} - this should be reported to library developers", xc.Key);
- break;
+ var entryrol = entry as DiscordAuditLogRoleUpdateEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "name":
+ entryrol.NameChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "color":
+ p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
+ p2 = int.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
+
+ entryrol.ColorChange = new PropertyChange<int?>
+ {
+ Before = p1 ? (int?)t3 : null,
+ After = p2 ? (int?)t4 : null
+ };
+ break;
+
+ case "permissions":
+ entryrol.PermissionChange = new PropertyChange<Permissions?>
+ {
+ Before = xc.OldValue != null ? (Permissions?)long.Parse((string)xc.OldValue) : null,
+ After = xc.NewValue != null ? (Permissions?)long.Parse((string)xc.NewValue) : null
+ };
+ break;
+
+ case "position":
+ entryrol.PositionChange = new PropertyChange<int?>
+ {
+ Before = xc.OldValue != null ? (int?)(long)xc.OldValue : null,
+ After = xc.NewValue != null ? (int?)(long)xc.NewValue : null,
+ };
+ break;
+
+ case "mentionable":
+ entryrol.MentionableChange = new PropertyChange<bool?>
+ {
+ Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
+ After = xc.NewValue != null ? (bool?)xc.NewValue : null
+ };
+ break;
+
+ case "hoist":
+ entryrol.HoistChange = new PropertyChange<bool?>
+ {
+ Before = (bool?)xc.OldValue,
+ After = (bool?)xc.NewValue
+ };
+ break;
+
+ case "icon_hash":
+ entryrol.IconHashChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in role update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
}
- }
- break;
+ break;
- case AuditLogActionType.InviteCreate:
- case AuditLogActionType.InviteDelete:
- case AuditLogActionType.InviteUpdate:
- entry = new DiscordAuditLogInviteEntry();
+ case AuditLogActionType.InviteCreate:
+ case AuditLogActionType.InviteDelete:
+ case AuditLogActionType.InviteUpdate:
+ entry = new DiscordAuditLogInviteEntry();
- var inv = new DiscordInvite
- {
- Discord = this.Discord,
- Guild = new DiscordInviteGuild
+ var inv = new DiscordInvite
{
Discord = this.Discord,
- Id = this.Id,
- Name = this.Name,
- SplashHash = this.SplashHash
- }
- };
-
- var entryinv = entry as DiscordAuditLogInviteEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ Guild = new DiscordInviteGuild
+ {
+ Discord = this.Discord,
+ Id = this.Id,
+ Name = this.Name,
+ SplashHash = this.SplashHash
+ }
+ };
+
+ var entryinv = entry as DiscordAuditLogInviteEntry;
+ foreach (var xc in xac.Changes)
{
- case "max_age":
- p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
- p2 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
-
- entryinv.MaxAgeChange = new PropertyChange<int?>
- {
- Before = p1 ? (int?)t3 : null,
- After = p2 ? (int?)t4 : null
- };
- break;
-
- case "code":
- inv.Code = xc.OldValueString ?? xc.NewValueString;
-
- entryinv.CodeChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "temporary":
- entryinv.TemporaryChange = new PropertyChange<bool?>
- {
- Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
- After = xc.NewValue != null ? (bool?)xc.NewValue : null
- };
- break;
-
- case "inviter_id":
- p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
-
- entryinv.InviterChange = new PropertyChange<DiscordMember>
- {
- Before = amd.TryGetValue(t1, out var propBeforeMember) ? propBeforeMember : new DiscordMember { Id = t1, Discord = this.Discord, GuildId = this.Id },
- After = amd.TryGetValue(t2, out var propAfterMember) ? propAfterMember : new DiscordMember { Id = t1, Discord = this.Discord, GuildId = this.Id },
- };
- break;
-
- case "channel_id":
- p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
-
- entryinv.ChannelChange = new PropertyChange<DiscordChannel>
- {
- Before = p1 ? this.GetChannel(t1) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id } : null,
- After = p2 ? this.GetChannel(t2) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id } : null
- };
-
- var ch = entryinv.ChannelChange.Before ?? entryinv.ChannelChange.After;
- var cht = ch?.Type;
- inv.Channel = new DiscordInviteChannel
- {
- Discord = this.Discord,
- Id = p1 ? t1 : t2,
- Name = ch?.Name,
- Type = cht != null ? cht.Value : ChannelType.Unknown
- };
- break;
-
- case "uses":
- p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
- p2 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
-
- entryinv.UsesChange = new PropertyChange<int?>
- {
- Before = p1 ? (int?)t3 : null,
- After = p2 ? (int?)t4 : null
- };
- break;
-
- case "max_uses":
- p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
- p2 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "max_age":
+ p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
+ p2 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
+
+ entryinv.MaxAgeChange = new PropertyChange<int?>
+ {
+ Before = p1 ? (int?)t3 : null,
+ After = p2 ? (int?)t4 : null
+ };
+ break;
+
+ case "code":
+ inv.Code = xc.OldValueString ?? xc.NewValueString;
+
+ entryinv.CodeChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "temporary":
+ entryinv.TemporaryChange = new PropertyChange<bool?>
+ {
+ Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
+ After = xc.NewValue != null ? (bool?)xc.NewValue : null
+ };
+ break;
+
+ case "inviter_id":
+ p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+
+ entryinv.InviterChange = new PropertyChange<DiscordMember>
+ {
+ Before = amd.TryGetValue(t1, out var propBeforeMember) ? propBeforeMember : new DiscordMember { Id = t1, Discord = this.Discord, GuildId = this.Id },
+ After = amd.TryGetValue(t2, out var propAfterMember) ? propAfterMember : new DiscordMember { Id = t1, Discord = this.Discord, GuildId = this.Id },
+ };
+ break;
+
+ case "channel_id":
+ p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+
+ entryinv.ChannelChange = new PropertyChange<DiscordChannel>
+ {
+ Before = p1 ? this.GetChannel(t1) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id } : null,
+ After = p2 ? this.GetChannel(t2) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id } : null
+ };
+
+ var ch = entryinv.ChannelChange.Before ?? entryinv.ChannelChange.After;
+ var cht = ch?.Type;
+ inv.Channel = new DiscordInviteChannel
+ {
+ Discord = this.Discord,
+ Id = p1 ? t1 : t2,
+ Name = ch?.Name,
+ Type = cht != null ? cht.Value : ChannelType.Unknown
+ };
+ break;
+
+ case "uses":
+ p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
+ p2 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
+
+ entryinv.UsesChange = new PropertyChange<int?>
+ {
+ Before = p1 ? (int?)t3 : null,
+ After = p2 ? (int?)t4 : null
+ };
+ break;
+
+ case "max_uses":
+ p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
+ p2 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
+
+ entryinv.MaxUsesChange = new PropertyChange<int?>
+ {
+ Before = p1 ? (int?)t3 : null,
+ After = p2 ? (int?)t4 : null
+ };
+ break;
+
+ // TODO: Add changes for target application
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in invite update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
+ }
- entryinv.MaxUsesChange = new PropertyChange<int?>
- {
- Before = p1 ? (int?)t3 : null,
- After = p2 ? (int?)t4 : null
- };
- break;
+ entryinv.Target = inv;
+ break;
- // TODO: Add changes for target application
+ case AuditLogActionType.WebhookCreate:
+ case AuditLogActionType.WebhookDelete:
+ case AuditLogActionType.WebhookUpdate:
+ entry = new DiscordAuditLogWebhookEntry
+ {
+ Target = ahd.TryGetValue(xac.TargetId.Value, out var webhook) ? webhook : new DiscordWebhook { Id = xac.TargetId.Value, Discord = this.Discord }
+ };
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in invite update: {0} - this should be reported to library developers", xc.Key);
- break;
+ var entrywhk = entry as DiscordAuditLogWebhookEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "application_id": // ???
+ p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+
+ entrywhk.IdChange = new PropertyChange<ulong?>
+ {
+ Before = p1 ? t1 : null,
+ After = p2 ? t2 : null
+ };
+ break;
+
+ case "name":
+ entrywhk.NameChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "channel_id":
+ p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+
+ entrywhk.ChannelChange = new PropertyChange<DiscordChannel>
+ {
+ Before = p1 ? this.GetChannel(t1) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id } : null,
+ After = p2 ? this.GetChannel(t2) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id } : null
+ };
+ break;
+
+ case "type": // ???
+ p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
+ p2 = int.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
+
+ entrywhk.TypeChange = new PropertyChange<int?>
+ {
+ Before = p1 ? (int?)t3 : null,
+ After = p2 ? (int?)t4 : null
+ };
+ break;
+
+ case "avatar_hash":
+ entrywhk.AvatarHashChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in webhook update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
}
- }
+ break;
- entryinv.Target = inv;
- break;
+ case AuditLogActionType.EmojiCreate:
+ case AuditLogActionType.EmojiDelete:
+ case AuditLogActionType.EmojiUpdate:
+ entry = new DiscordAuditLogEmojiEntry
+ {
+ Target = this.EmojisInternal.TryGetValue(xac.TargetId.Value, out var target) ? target : new DiscordEmoji { Id = xac.TargetId.Value, Discord = this.Discord }
+ };
- case AuditLogActionType.WebhookCreate:
- case AuditLogActionType.WebhookDelete:
- case AuditLogActionType.WebhookUpdate:
- entry = new DiscordAuditLogWebhookEntry
- {
- Target = ahd.TryGetValue(xac.TargetId.Value, out var webhook) ? webhook : new DiscordWebhook { Id = xac.TargetId.Value, Discord = this.Discord }
- };
+ var entryemo = entry as DiscordAuditLogEmojiEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "name":
+ entryemo.NameChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in emote update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
+ }
+ break;
- var entrywhk = entry as DiscordAuditLogWebhookEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ case AuditLogActionType.StageInstanceCreate:
+ case AuditLogActionType.StageInstanceDelete:
+ case AuditLogActionType.StageInstanceUpdate:
+ entry = new DiscordAuditLogStageEntry
{
- case "application_id": // ???
- p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+ Target = this.StageInstancesInternal.TryGetValue(xac.TargetId.Value, out var stage) ? stage : new DiscordStageInstance { Id = xac.TargetId.Value, Discord = this.Discord }
+ };
- entrywhk.IdChange = new PropertyChange<ulong?>
- {
- Before = p1 ? t1 : null,
- After = p2 ? t2 : null
- };
- break;
+ var entrysta = entry as DiscordAuditLogStageEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "topic":
+ entrysta.TopicChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+ case "privacy_level":
+ entrysta.PrivacyLevelChange = new PropertyChange<StagePrivacyLevel?>
+ {
+ Before = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5) ? (StagePrivacyLevel?)t5 : null,
+ After = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6) ? (StagePrivacyLevel?)t6 : null,
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in stage instance update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
+ }
+ break;
- case "name":
- entrywhk.NameChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
+ case AuditLogActionType.StickerCreate:
+ case AuditLogActionType.StickerDelete:
+ case AuditLogActionType.StickerUpdate:
+ entry = new DiscordAuditLogStickerEntry
+ {
+ Target = this.StickersInternal.TryGetValue(xac.TargetId.Value, out var sticker) ? sticker : new DiscordSticker { Id = xac.TargetId.Value, Discord = this.Discord }
+ };
- case "channel_id":
- p1 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- p2 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+ var entrysti = entry as DiscordAuditLogStickerEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "name":
+ entrysti.NameChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+ case "description":
+ entrysti.DescriptionChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+ case "tags":
+ entrysti.TagsChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+ case "guild_id":
+ entrysti.GuildIdChange = new PropertyChange<ulong?>
+ {
+ Before = ulong.TryParse(xc.OldValueString, out var ogid) ? ogid : null,
+ After = ulong.TryParse(xc.NewValueString, out var ngid) ? ngid : null
+ };
+ break;
+ case "available":
+ entrysti.AvailabilityChange = new PropertyChange<bool?>
+ {
+ Before = (bool?)xc.OldValue,
+ After = (bool?)xc.NewValue,
+ };
+ break;
+ case "asset":
+ entrysti.AssetChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+ case "id":
+ entrysti.IdChange = new PropertyChange<ulong?>
+ {
+ Before = ulong.TryParse(xc.OldValueString, out var oid) ? oid : null,
+ After = ulong.TryParse(xc.NewValueString, out var nid) ? nid : null
+ };
+ break;
+ case "type":
+ p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
+ p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
+ entrysti.TypeChange = new PropertyChange<StickerType?>
+ {
+ Before = p1 ? (StickerType?)t5 : null,
+ After = p2 ? (StickerType?)t6 : null
+ };
+ break;
+ case "format_type":
+ p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
+ p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
+ entrysti.FormatChange = new PropertyChange<StickerFormat?>
+ {
+ Before = p1 ? (StickerFormat?)t5 : null,
+ After = p2 ? (StickerFormat?)t6 : null
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in sticker update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
+ }
+ break;
- entrywhk.ChannelChange = new PropertyChange<DiscordChannel>
- {
- Before = p1 ? this.GetChannel(t1) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id } : null,
- After = p2 ? this.GetChannel(t2) ?? new DiscordChannel { Id = t1, Discord = this.Discord, GuildId = this.Id } : null
- };
- break;
- case "type": // ???
- p1 = int.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t3);
- p2 = int.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t4);
- entrywhk.TypeChange = new PropertyChange<int?>
- {
- Before = p1 ? (int?)t3 : null,
- After = p2 ? (int?)t4 : null
- };
- break;
+ case AuditLogActionType.MessageDelete:
+ case AuditLogActionType.MessageBulkDelete:
+ {
+ entry = new DiscordAuditLogMessageEntry();
- case "avatar_hash":
- entrywhk.AvatarHashChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
+ var entrymsg = entry as DiscordAuditLogMessageEntry;
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in webhook update: {0} - this should be reported to library developers", xc.Key);
- break;
+ if (xac.Options != null)
+ {
+ entrymsg.Channel = this.GetChannel(xac.Options.ChannelId) ?? new DiscordChannel { Id = xac.Options.ChannelId, Discord = this.Discord, GuildId = this.Id };
+ entrymsg.MessageCount = xac.Options.Count;
}
- }
- break;
-
- case AuditLogActionType.EmojiCreate:
- case AuditLogActionType.EmojiDelete:
- case AuditLogActionType.EmojiUpdate:
- entry = new DiscordAuditLogEmojiEntry
- {
- Target = this.EmojisInternal.TryGetValue(xac.TargetId.Value, out var target) ? target : new DiscordEmoji { Id = xac.TargetId.Value, Discord = this.Discord }
- };
- var entryemo = entry as DiscordAuditLogEmojiEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ if (entrymsg.Channel != null)
{
- case "name":
- entryemo.NameChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in emote update: {0} - this should be reported to library developers", xc.Key);
- break;
+ entrymsg.Target = this.Discord is DiscordClient dc
+ && dc.MessageCache != null
+ && dc.MessageCache.TryGet(xm => xm.Id == xac.TargetId.Value && xm.ChannelId == entrymsg.Channel.Id, out var msg)
+ ? msg
+ : new DiscordMessage { Discord = this.Discord, Id = xac.TargetId.Value };
}
+ break;
}
- break;
- case AuditLogActionType.StageInstanceCreate:
- case AuditLogActionType.StageInstanceDelete:
- case AuditLogActionType.StageInstanceUpdate:
- entry = new DiscordAuditLogStageEntry
+ case AuditLogActionType.MessagePin:
+ case AuditLogActionType.MessageUnpin:
{
- Target = this.StageInstancesInternal.TryGetValue(xac.TargetId.Value, out var stage) ? stage : new DiscordStageInstance { Id = xac.TargetId.Value, Discord = this.Discord }
- };
+ entry = new DiscordAuditLogMessagePinEntry();
- var entrysta = entry as DiscordAuditLogStageEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
- {
- case "topic":
- entrysta.TopicChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
- case "privacy_level":
- entrysta.PrivacyLevelChange = new PropertyChange<StagePrivacyLevel?>
- {
- Before = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5) ? (StagePrivacyLevel?)t5 : null,
- After = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6) ? (StagePrivacyLevel?)t6 : null,
- };
- break;
+ var entrypin = entry as DiscordAuditLogMessagePinEntry;
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in stage instance update: {0} - this should be reported to library developers", xc.Key);
- break;
+ if (this.Discord is not DiscordClient dc)
+ {
+ break;
}
- }
- break;
- case AuditLogActionType.StickerCreate:
- case AuditLogActionType.StickerDelete:
- case AuditLogActionType.StickerUpdate:
- entry = new DiscordAuditLogStickerEntry
- {
- Target = this.StickersInternal.TryGetValue(xac.TargetId.Value, out var sticker) ? sticker : new DiscordSticker { Id = xac.TargetId.Value, Discord = this.Discord }
- };
-
- var entrysti = entry as DiscordAuditLogStickerEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ if (xac.Options != null)
{
- case "name":
- entrysti.NameChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
- case "description":
- entrysti.DescriptionChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
- case "tags":
- entrysti.TagsChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
- case "guild_id":
- entrysti.GuildIdChange = new PropertyChange<ulong?>
- {
- Before = ulong.TryParse(xc.OldValueString, out var ogid) ? ogid : null,
- After = ulong.TryParse(xc.NewValueString, out var ngid) ? ngid : null
- };
- break;
- case "available":
- entrysti.AvailabilityChange = new PropertyChange<bool?>
- {
- Before = (bool?)xc.OldValue,
- After = (bool?)xc.NewValue,
- };
- break;
- case "asset":
- entrysti.AssetChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
- case "id":
- entrysti.IdChange = new PropertyChange<ulong?>
- {
- Before = ulong.TryParse(xc.OldValueString, out var oid) ? oid : null,
- After = ulong.TryParse(xc.NewValueString, out var nid) ? nid : null
- };
- break;
- case "type":
- p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
- p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
- entrysti.TypeChange = new PropertyChange<StickerType?>
- {
- Before = p1 ? (StickerType?)t5 : null,
- After = p2 ? (StickerType?)t6 : null
- };
- break;
- case "format_type":
- p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
- p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
- entrysti.FormatChange = new PropertyChange<StickerFormat?>
- {
- Before = p1 ? (StickerFormat?)t5 : null,
- After = p2 ? (StickerFormat?)t6 : null
- };
- break;
+ DiscordMessage message = default;
+ dc.MessageCache?.TryGet(x => x.Id == xac.Options.MessageId && x.ChannelId == xac.Options.ChannelId, out message);
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in sticker update: {0} - this should be reported to library developers", xc.Key);
- break;
+ entrypin.Channel = this.GetChannel(xac.Options.ChannelId) ?? new DiscordChannel { Id = xac.Options.ChannelId, Discord = this.Discord, GuildId = this.Id };
+ entrypin.Message = message ?? new DiscordMessage { Id = xac.Options.MessageId, Discord = this.Discord };
}
- }
- break;
+ if (xac.TargetId.HasValue)
+ {
+ dc.UserCache.TryGetValue(xac.TargetId.Value, out var user);
+ entrypin.Target = user ?? new DiscordUser { Id = user.Id, Discord = this.Discord };
+ }
-
- case AuditLogActionType.MessageDelete:
- case AuditLogActionType.MessageBulkDelete:
- {
- entry = new DiscordAuditLogMessageEntry();
-
- var entrymsg = entry as DiscordAuditLogMessageEntry;
-
- if (xac.Options != null)
- {
- entrymsg.Channel = this.GetChannel(xac.Options.ChannelId) ?? new DiscordChannel { Id = xac.Options.ChannelId, Discord = this.Discord, GuildId = this.Id };
- entrymsg.MessageCount = xac.Options.Count;
- }
-
- if (entrymsg.Channel != null)
- {
- entrymsg.Target = this.Discord is DiscordClient dc
- && dc.MessageCache != null
- && dc.MessageCache.TryGet(xm => xm.Id == xac.TargetId.Value && xm.ChannelId == entrymsg.Channel.Id, out var msg)
- ? msg
- : new DiscordMessage { Discord = this.Discord, Id = xac.TargetId.Value };
- }
- break;
- }
-
- case AuditLogActionType.MessagePin:
- case AuditLogActionType.MessageUnpin:
- {
- entry = new DiscordAuditLogMessagePinEntry();
-
- var entrypin = entry as DiscordAuditLogMessagePinEntry;
-
- if (this.Discord is not DiscordClient dc)
- {
break;
}
- if (xac.Options != null)
+ case AuditLogActionType.BotAdd:
{
- DiscordMessage message = default;
- dc.MessageCache?.TryGet(x => x.Id == xac.Options.MessageId && x.ChannelId == xac.Options.ChannelId, out message);
-
- entrypin.Channel = this.GetChannel(xac.Options.ChannelId) ?? new DiscordChannel { Id = xac.Options.ChannelId, Discord = this.Discord, GuildId = this.Id };
- entrypin.Message = message ?? new DiscordMessage { Id = xac.Options.MessageId, Discord = this.Discord };
- }
-
- if (xac.TargetId.HasValue)
- {
- dc.UserCache.TryGetValue(xac.TargetId.Value, out var user);
- entrypin.Target = user ?? new DiscordUser { Id = user.Id, Discord = this.Discord };
- }
+ entry = new DiscordAuditLogBotAddEntry();
- break;
- }
+ if (!(this.Discord is DiscordClient dc && xac.TargetId.HasValue))
+ {
+ break;
+ }
- case AuditLogActionType.BotAdd:
- {
- entry = new DiscordAuditLogBotAddEntry();
+ dc.UserCache.TryGetValue(xac.TargetId.Value, out var bot);
+ (entry as DiscordAuditLogBotAddEntry).TargetBot = bot ?? new DiscordUser { Id = xac.TargetId.Value, Discord = this.Discord };
- if (!(this.Discord is DiscordClient dc && xac.TargetId.HasValue))
- {
break;
}
- dc.UserCache.TryGetValue(xac.TargetId.Value, out var bot);
- (entry as DiscordAuditLogBotAddEntry).TargetBot = bot ?? new DiscordUser { Id = xac.TargetId.Value, Discord = this.Discord };
+ case AuditLogActionType.MemberMove:
+ entry = new DiscordAuditLogMemberMoveEntry();
- break;
- }
+ if (xac.Options == null)
+ {
+ break;
+ }
- case AuditLogActionType.MemberMove:
- entry = new DiscordAuditLogMemberMoveEntry();
+ var moveentry = entry as DiscordAuditLogMemberMoveEntry;
- if (xac.Options == null)
- {
+ moveentry.UserCount = xac.Options.Count;
+ moveentry.Channel = this.GetChannel(xac.Options.ChannelId) ?? new DiscordChannel { Id = xac.Options.ChannelId, Discord = this.Discord, GuildId = this.Id };
break;
- }
-
- var moveentry = entry as DiscordAuditLogMemberMoveEntry;
-
- moveentry.UserCount = xac.Options.Count;
- moveentry.Channel = this.GetChannel(xac.Options.ChannelId) ?? new DiscordChannel { Id = xac.Options.ChannelId, Discord = this.Discord, GuildId = this.Id };
- break;
- case AuditLogActionType.MemberDisconnect:
- entry = new DiscordAuditLogMemberDisconnectEntry
- {
- UserCount = xac.Options?.Count ?? 0
- };
- break;
+ case AuditLogActionType.MemberDisconnect:
+ entry = new DiscordAuditLogMemberDisconnectEntry
+ {
+ UserCount = xac.Options?.Count ?? 0
+ };
+ break;
- case AuditLogActionType.IntegrationCreate:
- case AuditLogActionType.IntegrationDelete:
- case AuditLogActionType.IntegrationUpdate:
- entry = new DiscordAuditLogIntegrationEntry();
+ case AuditLogActionType.IntegrationCreate:
+ case AuditLogActionType.IntegrationDelete:
+ case AuditLogActionType.IntegrationUpdate:
+ entry = new DiscordAuditLogIntegrationEntry();
- var integentry = entry as DiscordAuditLogIntegrationEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ var integentry = entry as DiscordAuditLogIntegrationEntry;
+ foreach (var xc in xac.Changes)
{
- case "type":
- integentry.Type = new PropertyChange<string>()
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
- case "enable_emoticons":
- integentry.EnableEmoticons = new PropertyChange<bool?>
- {
- Before = (bool?)xc.OldValue,
- After = (bool?)xc.NewValue
- };
- break;
- case "expire_behavior":
- integentry.ExpireBehavior = new PropertyChange<int?>
- {
- Before = (int?)xc.OldValue,
- After = (int?)xc.NewValue
- };
- break;
- case "expire_grace_period":
- integentry.ExpireBehavior = new PropertyChange<int?>
- {
- Before = (int?)xc.OldValue,
- After = (int?)xc.NewValue
- };
- break;
-
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in integration update: {0} - this should be reported to library developers", xc.Key);
- break;
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "type":
+ integentry.Type = new PropertyChange<string>()
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+ case "enable_emoticons":
+ integentry.EnableEmoticons = new PropertyChange<bool?>
+ {
+ Before = (bool?)xc.OldValue,
+ After = (bool?)xc.NewValue
+ };
+ break;
+ case "expire_behavior":
+ integentry.ExpireBehavior = new PropertyChange<int?>
+ {
+ Before = (int?)xc.OldValue,
+ After = (int?)xc.NewValue
+ };
+ break;
+ case "expire_grace_period":
+ integentry.ExpireBehavior = new PropertyChange<int?>
+ {
+ Before = (int?)xc.OldValue,
+ After = (int?)xc.NewValue
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in integration update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
}
- }
- break;
-
- case AuditLogActionType.ThreadCreate:
- case AuditLogActionType.ThreadDelete:
- case AuditLogActionType.ThreadUpdate:
- entry = new DiscordAuditLogThreadEntry
- {
- Target = this.ThreadsInternal.TryGetValue(xac.TargetId.Value, out var thread) ? thread : new DiscordThreadChannel { Id = xac.TargetId.Value, Discord = this.Discord }
- };
+ break;
- var entrythr = entry as DiscordAuditLogThreadEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ case AuditLogActionType.ThreadCreate:
+ case AuditLogActionType.ThreadDelete:
+ case AuditLogActionType.ThreadUpdate:
+ entry = new DiscordAuditLogThreadEntry
{
- case "name":
- entrythr.NameChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "type":
- p1 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
- p2 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
-
- entrythr.TypeChange = new PropertyChange<ChannelType?>
- {
- Before = p1 ? (ChannelType?)t1 : null,
- After = p2 ? (ChannelType?)t2 : null
- };
- break;
-
- case "archived":
- entrythr.ArchivedChange = new PropertyChange<bool?>
- {
- Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
- After = xc.NewValue != null ? (bool?)xc.NewValue : null
- };
- break;
-
- case "locked":
- entrythr.LockedChange = new PropertyChange<bool?>
- {
- Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
- After = xc.NewValue != null ? (bool?)xc.NewValue : null
- };
- break;
+ Target = this.ThreadsInternal.TryGetValue(xac.TargetId.Value, out var thread) ? thread : new DiscordThreadChannel { Id = xac.TargetId.Value, Discord = this.Discord }
+ };
- case "invitable":
- entrythr.InvitableChange = new PropertyChange<bool?>
- {
- Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
- After = xc.NewValue != null ? (bool?)xc.NewValue : null
- };
- break;
-
- case "auto_archive_duration":
- p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
- p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
-
- entrythr.AutoArchiveDurationChange = new PropertyChange<ThreadAutoArchiveDuration?>
- {
- Before = p1 ? (ThreadAutoArchiveDuration?)t5 : null,
- After = p2 ? (ThreadAutoArchiveDuration?)t6 : null
- };
- break;
-
- case "rate_limit_per_user":
- entrythr.PerUserRateLimitChange = new PropertyChange<int?>
- {
- Before = (int?)(long?)xc.OldValue,
- After = (int?)(long?)xc.NewValue
- };
- break;
-
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in thread update: {0} - this should be reported to library developers", xc.Key);
- break;
+ var entrythr = entry as DiscordAuditLogThreadEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "name":
+ entrythr.NameChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "type":
+ p1 = ulong.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t1);
+ p2 = ulong.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t2);
+
+ entrythr.TypeChange = new PropertyChange<ChannelType?>
+ {
+ Before = p1 ? (ChannelType?)t1 : null,
+ After = p2 ? (ChannelType?)t2 : null
+ };
+ break;
+
+ case "archived":
+ entrythr.ArchivedChange = new PropertyChange<bool?>
+ {
+ Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
+ After = xc.NewValue != null ? (bool?)xc.NewValue : null
+ };
+ break;
+
+ case "locked":
+ entrythr.LockedChange = new PropertyChange<bool?>
+ {
+ Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
+ After = xc.NewValue != null ? (bool?)xc.NewValue : null
+ };
+ break;
+
+ case "invitable":
+ entrythr.InvitableChange = new PropertyChange<bool?>
+ {
+ Before = xc.OldValue != null ? (bool?)xc.OldValue : null,
+ After = xc.NewValue != null ? (bool?)xc.NewValue : null
+ };
+ break;
+
+ case "auto_archive_duration":
+ p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
+ p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
+
+ entrythr.AutoArchiveDurationChange = new PropertyChange<ThreadAutoArchiveDuration?>
+ {
+ Before = p1 ? (ThreadAutoArchiveDuration?)t5 : null,
+ After = p2 ? (ThreadAutoArchiveDuration?)t6 : null
+ };
+ break;
+
+ case "rate_limit_per_user":
+ entrythr.PerUserRateLimitChange = new PropertyChange<int?>
+ {
+ Before = (int?)(long?)xc.OldValue,
+ After = (int?)(long?)xc.NewValue
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in thread update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
}
- }
- break;
-
+ break;
- case AuditLogActionType.GuildScheduledEventCreate:
- case AuditLogActionType.GuildScheduledEventDelete:
- case AuditLogActionType.GuildScheduledEventUpdate:
- entry = new DiscordAuditLogGuildScheduledEventEntry
- {
- Target = this.ScheduledEventsInternal.TryGetValue(xac.TargetId.Value, out var scheduledEvent) ? scheduledEvent : new DiscordScheduledEvent { Id = xac.TargetId.Value, Discord = this.Discord }
- };
- var entryse = entry as DiscordAuditLogGuildScheduledEventEntry;
- foreach (var xc in xac.Changes)
- {
- switch (xc.Key.ToLowerInvariant())
+ case AuditLogActionType.GuildScheduledEventCreate:
+ case AuditLogActionType.GuildScheduledEventDelete:
+ case AuditLogActionType.GuildScheduledEventUpdate:
+ entry = new DiscordAuditLogGuildScheduledEventEntry
{
- case "channel_id":
- entryse.ChannelIdChange = new PropertyChange<ulong?>
- {
- Before = ulong.TryParse(xc.OldValueString, out var ogid) ? ogid : null,
- After = ulong.TryParse(xc.NewValueString, out var ngid) ? ngid : null
- };
- break;
-
- case "name":
- entryse.NameChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "description":
- entryse.DescriptionChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
+ Target = this.ScheduledEventsInternal.TryGetValue(xac.TargetId.Value, out var scheduledEvent) ? scheduledEvent : new DiscordScheduledEvent { Id = xac.TargetId.Value, Discord = this.Discord }
+ };
- case "location":
- entryse.LocationChange = new PropertyChange<string>
- {
- Before = xc.OldValueString,
- After = xc.NewValueString
- };
- break;
-
- case "privacy_level":
- p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
- p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
-
- entryse.PrivacyLevelChange = new PropertyChange<ScheduledEventPrivacyLevel?>
- {
- Before = p1 ? (ScheduledEventPrivacyLevel?)t5 : null,
- After = p2 ? (ScheduledEventPrivacyLevel?)t6 : null
- };
- break;
-
- case "entity_type":
- p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
- p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
-
- entryse.EntityTypeChange = new PropertyChange<ScheduledEventEntityType?>
- {
- Before = p1 ? (ScheduledEventEntityType?)t5 : null,
- After = p2 ? (ScheduledEventEntityType?)t6 : null
- };
- break;
-
- case "status":
- p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
- p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
+ var entryse = entry as DiscordAuditLogGuildScheduledEventEntry;
+ foreach (var xc in xac.Changes)
+ {
+ switch (xc.Key.ToLowerInvariant())
+ {
+ case "channel_id":
+ entryse.ChannelIdChange = new PropertyChange<ulong?>
+ {
+ Before = ulong.TryParse(xc.OldValueString, out var ogid) ? ogid : null,
+ After = ulong.TryParse(xc.NewValueString, out var ngid) ? ngid : null
+ };
+ break;
+
+ case "name":
+ entryse.NameChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "description":
+ entryse.DescriptionChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "location":
+ entryse.LocationChange = new PropertyChange<string>
+ {
+ Before = xc.OldValueString,
+ After = xc.NewValueString
+ };
+ break;
+
+ case "privacy_level":
+ p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
+ p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
+
+ entryse.PrivacyLevelChange = new PropertyChange<ScheduledEventPrivacyLevel?>
+ {
+ Before = p1 ? (ScheduledEventPrivacyLevel?)t5 : null,
+ After = p2 ? (ScheduledEventPrivacyLevel?)t6 : null
+ };
+ break;
+
+ case "entity_type":
+ p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
+ p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
+
+ entryse.EntityTypeChange = new PropertyChange<ScheduledEventEntityType?>
+ {
+ Before = p1 ? (ScheduledEventEntityType?)t5 : null,
+ After = p2 ? (ScheduledEventEntityType?)t6 : null
+ };
+ break;
+
+ case "status":
+ p1 = long.TryParse(xc.OldValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t5);
+ p2 = long.TryParse(xc.NewValue as string, NumberStyles.Integer, CultureInfo.InvariantCulture, out t6);
+
+ entryse.StatusChange = new PropertyChange<ScheduledEventStatus?>
+ {
+ Before = p1 ? (ScheduledEventStatus?)t5 : null,
+ After = p2 ? (ScheduledEventStatus?)t6 : null
+ };
+ break;
+
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in scheduled event update: {0} - this should be reported to library developers", xc.Key);
+ break;
+ }
+ }
+ break;
- entryse.StatusChange = new PropertyChange<ScheduledEventStatus?>
- {
- Before = p1 ? (ScheduledEventStatus?)t5 : null,
- After = p2 ? (ScheduledEventStatus?)t6 : null
- };
- break;
+ default:
+ this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown audit log action type: {0} - this should be reported to library developers", (int)xac.ActionType);
+ break;
+ }
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown key in scheduled event update: {0} - this should be reported to library developers", xc.Key);
- break;
- }
- }
- break;
+ if (entry == null)
+ continue;
- default:
- this.Discord.Logger.LogWarning(LoggerEvents.AuditLog, "Unknown audit log action type: {0} - this should be reported to library developers", (int)xac.ActionType);
- break;
+ entry.ActionCategory = xac.ActionType switch
+ {
+ AuditLogActionType.ChannelCreate or AuditLogActionType.EmojiCreate or AuditLogActionType.InviteCreate or AuditLogActionType.OverwriteCreate or AuditLogActionType.RoleCreate or AuditLogActionType.WebhookCreate or AuditLogActionType.IntegrationCreate or AuditLogActionType.StickerCreate or AuditLogActionType.StageInstanceCreate or AuditLogActionType.ThreadCreate or AuditLogActionType.GuildScheduledEventCreate => AuditLogActionCategory.Create,
+ AuditLogActionType.ChannelDelete or AuditLogActionType.EmojiDelete or AuditLogActionType.InviteDelete or AuditLogActionType.MessageDelete or AuditLogActionType.MessageBulkDelete or AuditLogActionType.OverwriteDelete or AuditLogActionType.RoleDelete or AuditLogActionType.WebhookDelete or AuditLogActionType.IntegrationDelete or AuditLogActionType.StickerDelete or AuditLogActionType.StageInstanceDelete or AuditLogActionType.ThreadDelete or AuditLogActionType.GuildScheduledEventDelete => AuditLogActionCategory.Delete,
+ AuditLogActionType.ChannelUpdate or AuditLogActionType.EmojiUpdate or AuditLogActionType.InviteUpdate or AuditLogActionType.MemberRoleUpdate or AuditLogActionType.MemberUpdate or AuditLogActionType.OverwriteUpdate or AuditLogActionType.RoleUpdate or AuditLogActionType.WebhookUpdate or AuditLogActionType.IntegrationUpdate or AuditLogActionType.StickerUpdate or AuditLogActionType.StageInstanceUpdate or AuditLogActionType.ThreadUpdate or AuditLogActionType.GuildScheduledEventUpdate => AuditLogActionCategory.Update,
+ _ => AuditLogActionCategory.Other,
+ };
+ entry.Discord = this.Discord;
+ entry.ActionType = xac.ActionType;
+ entry.Id = xac.Id;
+ entry.Reason = xac.Reason;
+ entry.UserResponsible = amd[xac.UserId];
+ entries.Add(entry);
}
- if (entry == null)
- continue;
-
- entry.ActionCategory = xac.ActionType switch
- {
- AuditLogActionType.ChannelCreate or AuditLogActionType.EmojiCreate or AuditLogActionType.InviteCreate or AuditLogActionType.OverwriteCreate or AuditLogActionType.RoleCreate or AuditLogActionType.WebhookCreate or AuditLogActionType.IntegrationCreate or AuditLogActionType.StickerCreate or AuditLogActionType.StageInstanceCreate or AuditLogActionType.ThreadCreate or AuditLogActionType.GuildScheduledEventCreate => AuditLogActionCategory.Create,
- AuditLogActionType.ChannelDelete or AuditLogActionType.EmojiDelete or AuditLogActionType.InviteDelete or AuditLogActionType.MessageDelete or AuditLogActionType.MessageBulkDelete or AuditLogActionType.OverwriteDelete or AuditLogActionType.RoleDelete or AuditLogActionType.WebhookDelete or AuditLogActionType.IntegrationDelete or AuditLogActionType.StickerDelete or AuditLogActionType.StageInstanceDelete or AuditLogActionType.ThreadDelete or AuditLogActionType.GuildScheduledEventDelete => AuditLogActionCategory.Delete,
- AuditLogActionType.ChannelUpdate or AuditLogActionType.EmojiUpdate or AuditLogActionType.InviteUpdate or AuditLogActionType.MemberRoleUpdate or AuditLogActionType.MemberUpdate or AuditLogActionType.OverwriteUpdate or AuditLogActionType.RoleUpdate or AuditLogActionType.WebhookUpdate or AuditLogActionType.IntegrationUpdate or AuditLogActionType.StickerUpdate or AuditLogActionType.StageInstanceUpdate or AuditLogActionType.ThreadUpdate or AuditLogActionType.GuildScheduledEventUpdate => AuditLogActionCategory.Update,
- _ => AuditLogActionCategory.Other,
- };
- entry.Discord = this.Discord;
- entry.ActionType = xac.ActionType;
- entry.Id = xac.Id;
- entry.Reason = xac.Reason;
- entry.UserResponsible = amd[xac.UserId];
- entries.Add(entry);
+ return new ReadOnlyCollection<DiscordAuditLogEntry>(entries);
}
-
- return new ReadOnlyCollection<DiscordAuditLogEntry>(entries);
}
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuild.Features.cs b/DisCatSharp/Entities/Guild/DiscordGuild.Features.cs
index bd4c78fb7..5af404179 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuild.Features.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuild.Features.cs
@@ -1,300 +1,302 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents the guild features.
-/// </summary>
-public class GuildFeatures
+namespace DisCatSharp.Entities
{
+
/// <summary>
- /// Guild has access to set an animated guild icon.
- /// </summary>
- public bool CanSetAnimatedIcon { get; }
-
- /// <summary>
- /// Guild has access to set a guild banner image.
- /// </summary>
- public bool CanSetBanner { get; }
-
- /// <summary>
- /// Guild has access to use commerce features (i.e. create store channels)
- /// </summary>
- public bool CanCreateStoreChannels { get; }
-
- /// <summary>
- /// Guild can enable Welcome Screen, Membership Screening, Stage Channels, News Channels and receives community updates.
- /// Furthermore the guild can apply as a partner and for the discovery (if the prerequisites are given).
- /// <see cref="ChannelType.Stage"/> and <see cref="ChannelType.News"/> is usable.
- /// </summary>
- public bool HasCommunityEnabled { get; }
-
- /// <summary>
- /// Guild is able to be discovered in the discovery.
- /// </summary>
- public bool IsDiscoverable { get; }
-
- /// <summary>
- /// Guild is able to be featured in the discovery.
- /// </summary>
- public bool IsFeatureable { get; }
-
- /// <summary>
- /// Guild has access to set an invite splash background.
- /// </summary>
- public bool CanSetInviteSplash { get; }
-
- /// <summary>
- /// Guild has enabled Membership Screening.
- /// </summary>
- public bool HasMembershipScreeningEnabled { get; }
-
- /// <summary>
- /// Guild has access to create news channels.
- /// <see cref="ChannelType.News"/> is usable.
- /// </summary>
- public bool CanCreateNewsChannels { get; }
-
- /// <summary>
- /// Guild is partnered.
- /// </summary>
- public bool IsPartnered { get; }
-
- /// <summary>
- /// Guild has increased custom emoji slots.
+ /// Represents the guild features.
/// </summary>
- public bool CanUploadMoreEmojis { get; }
-
- /// <summary>
- /// Guild can be previewed before joining via Membership Screening or the discovery.
- /// </summary>
- public bool HasPreviewEnabled { get; }
-
- /// <summary>
- /// Guild has access to set a vanity URL.
- /// </summary>
- public bool CanSetVanityUrl { get; }
-
- /// <summary>
- /// Guild is verified.
- /// </summary>
- public bool IsVerified { get; }
-
- /// <summary>
- /// Guild has access to set 384kbps bitrate in voice (previously VIP voice servers).
- /// </summary>
- public bool CanAccessVipRegions { get; }
-
- /// <summary>
- /// Guild has enabled the welcome screen.
- /// </summary>
- public bool HasWelcomeScreenEnabled { get; }
-
- /// <summary>
- /// Guild has enabled ticketed events.
- /// </summary>
- public bool HasTicketedEventsEnabled { get; }
-
- /// <summary>
- /// Guild has enabled monetization.
- /// </summary>
- public bool HasMonetizationEnabled { get; }
-
- /// <summary>
- /// Guild has increased custom sticker slots.
- /// </summary>
- public bool CanUploadMoreStickers { get; }
-
- /// <summary>
- /// Guild has access to the three day archive time for threads.
- /// Needs Premium Tier 1 (<see cref="PremiumTier.TierOne"/>).
- /// </summary>
- public bool CanSetThreadArchiveDurationThreeDays { get; }
-
- /// <summary>
- /// Guild has access to the seven day archive time for threads.
- /// Needs Premium Tier 2 (<see cref="PremiumTier.TierTwo"/>).
- /// </summary>
- public bool CanSetThreadArchiveDurationSevenDays { get; }
-
- /// <summary>
- /// Guild has access to create private threads.
- /// Needs Premium Tier 2 (<see cref="PremiumTier.TierTwo"/>).
- /// </summary>
- public bool CanCreatePrivateThreads { get; }
-
- /// <summary>
- /// Guild is a hub.
- /// <see cref="ChannelType.GuildDirectory"/> is usable.
- /// </summary>
- public bool IsHub { get; }
-
- /// <summary>
- /// Guild is in a hub.
- /// https://github.com/discord/discord-api-docs/pull/3757/commits/4932d92c9d0c783861bc715bf7ebbabb15114e34
- /// </summary>
- public bool HasDirectoryEntry { get; }
-
- /// <summary>
- /// Guild is linked to a hub.
- /// </summary>
- public bool IsLinkedToHub { get; }
-
- /// <summary>
- /// Guild has full access to threads.
- /// Old Feature.
- /// </summary>
- public bool HasThreadTestingEnabled { get; }
-
- /// <summary>
- /// Guild has access to threads.
- /// </summary>
- public bool HasThreadsEnabled { get; }
-
- /// <summary>
- /// Guild can set role icons.
- /// </summary>
- public bool CanSetRoleIcons { get; }
-
- /// <summary>
- /// Guild has the new thread permissions.
- /// Old Feature.
- /// </summary>
- public bool HasNewThreadPermissions { get; }
-
- /// <summary>
- /// Guild can set thread default auto archive duration.
- /// Old Feature.
- /// </summary>
- public bool CanSetThreadDefaultAutoArchiveDuration { get; }
-
- /// <summary>
- /// Guild has enabled role subsriptions.
- /// </summary>
- public bool HasRoleSubscriptionsEnabled { get; }
-
- /// <summary>
- /// Guild role subsriptions as purchaseable.
- /// </summary>
- public bool RoleSubscriptionsIsAvaiableForPurchase { get; }
-
- /// <summary>
- /// Guild has premium tier 3 override.
- /// </summary>
- public bool PremiumTierThreeOverride { get; }
-
- /// <summary>
- /// Guild has access to text in voice.
- /// Restricted to <see cref="IsStaffOnly"/>.
- /// </summary>
- public bool TextInVoiceEnabled { get; }
-
- /// <summary>
- /// Guild can set an animated banner.
- /// Needs Premium Tier 3 (<see cref="PremiumTier.TierThree"/>).
- /// </summary>
- public bool CanSetAnimatedBanner { get; }
-
- /// <summary>
- /// Guild can set an animated banner.
- /// Needs Premium Tier 3 (<see cref="PremiumTier.TierThree"/>).
- /// </summary>
- public bool CanSetChannelBanner { get; }
-
- /// <summary>
- /// Allows members to customize their avatar, banner and bio for that server.
- /// </summary>
- public bool HasMemberProfiles { get; }
-
- /// <summary>
- /// Guild is restricted to users with the <see cref="UserFlags.Staff"/> badge.
- /// </summary>
- public bool IsStaffOnly { get; }
-
- /// <summary>
- /// Guild can use and setup the experimental auto moderation feature.
- /// </summary>
- public bool CanSetupAutoModeration { get; }
-
- /// <summary>
- /// String of guild features.
- /// </summary>
- public string FeatureString { get; }
-
- /// <summary>
- /// Checks the guild features and constructs a new <see cref="GuildFeatures"/> object.
- /// </summary>
- /// <param name="guild">Guild to check</param>
- public GuildFeatures(DiscordGuild guild)
+ public class GuildFeatures
{
- this.CanSetAnimatedIcon = guild.RawFeatures.Contains("ANIMATED_ICON");
- this.CanSetAnimatedBanner = guild.RawFeatures.Contains("ANIMATED_BANNER");
- this.CanSetBanner = guild.RawFeatures.Contains("BANNER");
- this.CanSetChannelBanner = guild.RawFeatures.Contains("CHANNEL_BANNER");
- this.CanCreateStoreChannels = guild.RawFeatures.Contains("COMMERCE");
- this.HasCommunityEnabled = guild.RawFeatures.Contains("COMMUNITY");
- this.IsDiscoverable = !guild.RawFeatures.Contains("DISCOVERABLE_DISABLED") && guild.RawFeatures.Contains("DISCOVERABLE");
- this.IsFeatureable = guild.RawFeatures.Contains("FEATURABLE");
- this.CanSetInviteSplash = guild.RawFeatures.Contains("INVITE_SPLASH");
- this.HasMembershipScreeningEnabled = guild.RawFeatures.Contains("MEMBER_VERIFICATION_GATE_ENABLED");
- this.CanCreateNewsChannels = guild.RawFeatures.Contains("NEWS");
- this.IsPartnered = guild.RawFeatures.Contains("PARTNERED");
- this.CanUploadMoreEmojis = guild.RawFeatures.Contains("MORE_EMOJI");
- this.HasPreviewEnabled = guild.RawFeatures.Contains("PREVIEW_ENABLED");
- this.CanSetVanityUrl = guild.RawFeatures.Contains("VANITY_URL");
- this.IsVerified = guild.RawFeatures.Contains("VERIFIED");
- this.CanAccessVipRegions = guild.RawFeatures.Contains("VIP_REGIONS");
- this.HasWelcomeScreenEnabled = guild.RawFeatures.Contains("WELCOME_SCREEN_ENABLED");
- this.HasTicketedEventsEnabled = guild.RawFeatures.Contains("TICKETED_EVENTS_ENABLED");
- this.HasMonetizationEnabled = guild.RawFeatures.Contains("MONETIZATION_ENABLED");
- this.CanUploadMoreStickers = guild.RawFeatures.Contains("MORE_STICKERS");
- this.CanSetThreadArchiveDurationThreeDays = guild.RawFeatures.Contains("THREE_DAY_THREAD_ARCHIVE");
- this.CanSetThreadArchiveDurationSevenDays = guild.RawFeatures.Contains("SEVEN_DAY_THREAD_ARCHIVE");
- this.CanCreatePrivateThreads = guild.RawFeatures.Contains("PRIVATE_THREADS");
- this.IsHub = guild.RawFeatures.Contains("HUB");
- this.HasThreadTestingEnabled = guild.RawFeatures.Contains("THREADS_ENABLED_TESTING");
- this.HasThreadsEnabled = guild.RawFeatures.Contains("THREADS_ENABLED");
- this.CanSetRoleIcons = guild.RawFeatures.Contains("ROLE_ICONS");
- this.HasNewThreadPermissions = guild.RawFeatures.Contains("NEW_THREAD_PERMISSIONS");
- this.HasRoleSubscriptionsEnabled = guild.RawFeatures.Contains("ROLE_SUBSCRIPTIONS_ENABLED");
- this.PremiumTierThreeOverride = guild.RawFeatures.Contains("PREMIUM_TIER_3_OVERRIDE");
- this.CanSetThreadDefaultAutoArchiveDuration = guild.RawFeatures.Contains("THREAD_DEFAULT_AUTO_ARCHIVE_DURATION");
- this.TextInVoiceEnabled = guild.RawFeatures.Contains("TEXT_IN_VOICE_ENABLED");
- this.HasDirectoryEntry = guild.RawFeatures.Contains("HAS_DIRECTORY_ENTRY");
- this.IsLinkedToHub = guild.RawFeatures.Contains("LINKED_TO_HUB");
- this.HasMemberProfiles = guild.RawFeatures.Contains("MEMBER_PROFILES");
- this.IsStaffOnly = guild.RawFeatures.Contains("INTERNAL_EMPLOYEE_ONLY");
- this.RoleSubscriptionsIsAvaiableForPurchase = guild.RawFeatures.Contains("ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE");
- this.CanSetupAutoModeration = guild.RawFeatures.Contains("AUTO_MODERATION");
-
- var features = guild.RawFeatures.Any() ? "" : "None";
- foreach (var feature in guild.RawFeatures)
+ /// <summary>
+ /// Guild has access to set an animated guild icon.
+ /// </summary>
+ public bool CanSetAnimatedIcon { get; }
+
+ /// <summary>
+ /// Guild has access to set a guild banner image.
+ /// </summary>
+ public bool CanSetBanner { get; }
+
+ /// <summary>
+ /// Guild has access to use commerce features (i.e. create store channels)
+ /// </summary>
+ public bool CanCreateStoreChannels { get; }
+
+ /// <summary>
+ /// Guild can enable Welcome Screen, Membership Screening, Stage Channels, News Channels and receives community updates.
+ /// Furthermore the guild can apply as a partner and for the discovery (if the prerequisites are given).
+ /// <see cref="ChannelType.Stage"/> and <see cref="ChannelType.News"/> is usable.
+ /// </summary>
+ public bool HasCommunityEnabled { get; }
+
+ /// <summary>
+ /// Guild is able to be discovered in the discovery.
+ /// </summary>
+ public bool IsDiscoverable { get; }
+
+ /// <summary>
+ /// Guild is able to be featured in the discovery.
+ /// </summary>
+ public bool IsFeatureable { get; }
+
+ /// <summary>
+ /// Guild has access to set an invite splash background.
+ /// </summary>
+ public bool CanSetInviteSplash { get; }
+
+ /// <summary>
+ /// Guild has enabled Membership Screening.
+ /// </summary>
+ public bool HasMembershipScreeningEnabled { get; }
+
+ /// <summary>
+ /// Guild has access to create news channels.
+ /// <see cref="ChannelType.News"/> is usable.
+ /// </summary>
+ public bool CanCreateNewsChannels { get; }
+
+ /// <summary>
+ /// Guild is partnered.
+ /// </summary>
+ public bool IsPartnered { get; }
+
+ /// <summary>
+ /// Guild has increased custom emoji slots.
+ /// </summary>
+ public bool CanUploadMoreEmojis { get; }
+
+ /// <summary>
+ /// Guild can be previewed before joining via Membership Screening or the discovery.
+ /// </summary>
+ public bool HasPreviewEnabled { get; }
+
+ /// <summary>
+ /// Guild has access to set a vanity URL.
+ /// </summary>
+ public bool CanSetVanityUrl { get; }
+
+ /// <summary>
+ /// Guild is verified.
+ /// </summary>
+ public bool IsVerified { get; }
+
+ /// <summary>
+ /// Guild has access to set 384kbps bitrate in voice (previously VIP voice servers).
+ /// </summary>
+ public bool CanAccessVipRegions { get; }
+
+ /// <summary>
+ /// Guild has enabled the welcome screen.
+ /// </summary>
+ public bool HasWelcomeScreenEnabled { get; }
+
+ /// <summary>
+ /// Guild has enabled ticketed events.
+ /// </summary>
+ public bool HasTicketedEventsEnabled { get; }
+
+ /// <summary>
+ /// Guild has enabled monetization.
+ /// </summary>
+ public bool HasMonetizationEnabled { get; }
+
+ /// <summary>
+ /// Guild has increased custom sticker slots.
+ /// </summary>
+ public bool CanUploadMoreStickers { get; }
+
+ /// <summary>
+ /// Guild has access to the three day archive time for threads.
+ /// Needs Premium Tier 1 (<see cref="PremiumTier.TierOne"/>).
+ /// </summary>
+ public bool CanSetThreadArchiveDurationThreeDays { get; }
+
+ /// <summary>
+ /// Guild has access to the seven day archive time for threads.
+ /// Needs Premium Tier 2 (<see cref="PremiumTier.TierTwo"/>).
+ /// </summary>
+ public bool CanSetThreadArchiveDurationSevenDays { get; }
+
+ /// <summary>
+ /// Guild has access to create private threads.
+ /// Needs Premium Tier 2 (<see cref="PremiumTier.TierTwo"/>).
+ /// </summary>
+ public bool CanCreatePrivateThreads { get; }
+
+ /// <summary>
+ /// Guild is a hub.
+ /// <see cref="ChannelType.GuildDirectory"/> is usable.
+ /// </summary>
+ public bool IsHub { get; }
+
+ /// <summary>
+ /// Guild is in a hub.
+ /// https://github.com/discord/discord-api-docs/pull/3757/commits/4932d92c9d0c783861bc715bf7ebbabb15114e34
+ /// </summary>
+ public bool HasDirectoryEntry { get; }
+
+ /// <summary>
+ /// Guild is linked to a hub.
+ /// </summary>
+ public bool IsLinkedToHub { get; }
+
+ /// <summary>
+ /// Guild has full access to threads.
+ /// Old Feature.
+ /// </summary>
+ public bool HasThreadTestingEnabled { get; }
+
+ /// <summary>
+ /// Guild has access to threads.
+ /// </summary>
+ public bool HasThreadsEnabled { get; }
+
+ /// <summary>
+ /// Guild can set role icons.
+ /// </summary>
+ public bool CanSetRoleIcons { get; }
+
+ /// <summary>
+ /// Guild has the new thread permissions.
+ /// Old Feature.
+ /// </summary>
+ public bool HasNewThreadPermissions { get; }
+
+ /// <summary>
+ /// Guild can set thread default auto archive duration.
+ /// Old Feature.
+ /// </summary>
+ public bool CanSetThreadDefaultAutoArchiveDuration { get; }
+
+ /// <summary>
+ /// Guild has enabled role subsriptions.
+ /// </summary>
+ public bool HasRoleSubscriptionsEnabled { get; }
+
+ /// <summary>
+ /// Guild role subsriptions as purchaseable.
+ /// </summary>
+ public bool RoleSubscriptionsIsAvaiableForPurchase { get; }
+
+ /// <summary>
+ /// Guild has premium tier 3 override.
+ /// </summary>
+ public bool PremiumTierThreeOverride { get; }
+
+ /// <summary>
+ /// Guild has access to text in voice.
+ /// Restricted to <see cref="IsStaffOnly"/>.
+ /// </summary>
+ public bool TextInVoiceEnabled { get; }
+
+ /// <summary>
+ /// Guild can set an animated banner.
+ /// Needs Premium Tier 3 (<see cref="PremiumTier.TierThree"/>).
+ /// </summary>
+ public bool CanSetAnimatedBanner { get; }
+
+ /// <summary>
+ /// Guild can set an animated banner.
+ /// Needs Premium Tier 3 (<see cref="PremiumTier.TierThree"/>).
+ /// </summary>
+ public bool CanSetChannelBanner { get; }
+
+ /// <summary>
+ /// Allows members to customize their avatar, banner and bio for that server.
+ /// </summary>
+ public bool HasMemberProfiles { get; }
+
+ /// <summary>
+ /// Guild is restricted to users with the <see cref="UserFlags.Staff"/> badge.
+ /// </summary>
+ public bool IsStaffOnly { get; }
+
+ /// <summary>
+ /// Guild can use and setup the experimental auto moderation feature.
+ /// </summary>
+ public bool CanSetupAutoModeration { get; }
+
+ /// <summary>
+ /// String of guild features.
+ /// </summary>
+ public string FeatureString { get; }
+
+ /// <summary>
+ /// Checks the guild features and constructs a new <see cref="GuildFeatures"/> object.
+ /// </summary>
+ /// <param name="guild">Guild to check</param>
+ public GuildFeatures(DiscordGuild guild)
{
- features += feature + " ";
- }
- this.FeatureString = features;
+ this.CanSetAnimatedIcon = guild.RawFeatures.Contains("ANIMATED_ICON");
+ this.CanSetAnimatedBanner = guild.RawFeatures.Contains("ANIMATED_BANNER");
+ this.CanSetBanner = guild.RawFeatures.Contains("BANNER");
+ this.CanSetChannelBanner = guild.RawFeatures.Contains("CHANNEL_BANNER");
+ this.CanCreateStoreChannels = guild.RawFeatures.Contains("COMMERCE");
+ this.HasCommunityEnabled = guild.RawFeatures.Contains("COMMUNITY");
+ this.IsDiscoverable = !guild.RawFeatures.Contains("DISCOVERABLE_DISABLED") && guild.RawFeatures.Contains("DISCOVERABLE");
+ this.IsFeatureable = guild.RawFeatures.Contains("FEATURABLE");
+ this.CanSetInviteSplash = guild.RawFeatures.Contains("INVITE_SPLASH");
+ this.HasMembershipScreeningEnabled = guild.RawFeatures.Contains("MEMBER_VERIFICATION_GATE_ENABLED");
+ this.CanCreateNewsChannels = guild.RawFeatures.Contains("NEWS");
+ this.IsPartnered = guild.RawFeatures.Contains("PARTNERED");
+ this.CanUploadMoreEmojis = guild.RawFeatures.Contains("MORE_EMOJI");
+ this.HasPreviewEnabled = guild.RawFeatures.Contains("PREVIEW_ENABLED");
+ this.CanSetVanityUrl = guild.RawFeatures.Contains("VANITY_URL");
+ this.IsVerified = guild.RawFeatures.Contains("VERIFIED");
+ this.CanAccessVipRegions = guild.RawFeatures.Contains("VIP_REGIONS");
+ this.HasWelcomeScreenEnabled = guild.RawFeatures.Contains("WELCOME_SCREEN_ENABLED");
+ this.HasTicketedEventsEnabled = guild.RawFeatures.Contains("TICKETED_EVENTS_ENABLED");
+ this.HasMonetizationEnabled = guild.RawFeatures.Contains("MONETIZATION_ENABLED");
+ this.CanUploadMoreStickers = guild.RawFeatures.Contains("MORE_STICKERS");
+ this.CanSetThreadArchiveDurationThreeDays = guild.RawFeatures.Contains("THREE_DAY_THREAD_ARCHIVE");
+ this.CanSetThreadArchiveDurationSevenDays = guild.RawFeatures.Contains("SEVEN_DAY_THREAD_ARCHIVE");
+ this.CanCreatePrivateThreads = guild.RawFeatures.Contains("PRIVATE_THREADS");
+ this.IsHub = guild.RawFeatures.Contains("HUB");
+ this.HasThreadTestingEnabled = guild.RawFeatures.Contains("THREADS_ENABLED_TESTING");
+ this.HasThreadsEnabled = guild.RawFeatures.Contains("THREADS_ENABLED");
+ this.CanSetRoleIcons = guild.RawFeatures.Contains("ROLE_ICONS");
+ this.HasNewThreadPermissions = guild.RawFeatures.Contains("NEW_THREAD_PERMISSIONS");
+ this.HasRoleSubscriptionsEnabled = guild.RawFeatures.Contains("ROLE_SUBSCRIPTIONS_ENABLED");
+ this.PremiumTierThreeOverride = guild.RawFeatures.Contains("PREMIUM_TIER_3_OVERRIDE");
+ this.CanSetThreadDefaultAutoArchiveDuration = guild.RawFeatures.Contains("THREAD_DEFAULT_AUTO_ARCHIVE_DURATION");
+ this.TextInVoiceEnabled = guild.RawFeatures.Contains("TEXT_IN_VOICE_ENABLED");
+ this.HasDirectoryEntry = guild.RawFeatures.Contains("HAS_DIRECTORY_ENTRY");
+ this.IsLinkedToHub = guild.RawFeatures.Contains("LINKED_TO_HUB");
+ this.HasMemberProfiles = guild.RawFeatures.Contains("MEMBER_PROFILES");
+ this.IsStaffOnly = guild.RawFeatures.Contains("INTERNAL_EMPLOYEE_ONLY");
+ this.RoleSubscriptionsIsAvaiableForPurchase = guild.RawFeatures.Contains("ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE");
+ this.CanSetupAutoModeration = guild.RawFeatures.Contains("AUTO_MODERATION");
+
+ var features = guild.RawFeatures.Any() ? "" : "None";
+ foreach (var feature in guild.RawFeatures)
+ {
+ features += feature + " ";
+ }
+ this.FeatureString = features;
+ }
}
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuild.cs b/DisCatSharp/Entities/Guild/DiscordGuild.cs
index c503807f5..38f3e1d6a 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuild.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuild.cs
@@ -1,2076 +1,2077 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Models;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord guild.
-/// </summary>
-public partial class DiscordGuild : SnowflakeObject, IEquatable<DiscordGuild>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the guild's name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the guild icon's hash.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
- public string IconHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild icon's url.
- /// </summary>
- [JsonIgnore]
- public string IconUrl
- => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.{(this.IconHash.StartsWith("a_") ? "gif" : "png")}?size=1024" : null;
-
- /// <summary>
- /// Gets the guild splash's hash.
- /// </summary>
- [JsonProperty("splash", NullValueHandling = NullValueHandling.Ignore)]
- public string SplashHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild splash's url.
- /// </summary>
- [JsonIgnore]
- public string SplashUrl
- => !string.IsNullOrWhiteSpace(this.SplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.SplashHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets the guild discovery splash's hash.
- /// </summary>
- [JsonProperty("discovery_splash", NullValueHandling = NullValueHandling.Ignore)]
- public string DiscoverySplashHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild discovery splash's url.
- /// </summary>
- [JsonIgnore]
- public string DiscoverySplashUrl
- => !string.IsNullOrWhiteSpace(this.DiscoverySplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILD_DISCOVERY_SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.DiscoverySplashHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets the preferred locale of this guild.
- /// <para>This is used for server discovery, interactions and notices from Discord. Defaults to en-US.</para>
- /// </summary>
- [JsonProperty("preferred_locale", NullValueHandling = NullValueHandling.Ignore)]
- public string PreferredLocale { get; internal set; }
-
- /// <summary>
- /// Gets the ID of the guild's owner.
- /// </summary>
- [JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong OwnerId { get; internal set; }
-
- /// <summary>
- /// Gets the guild's owner.
- /// </summary>
- [JsonIgnore]
- public DiscordMember Owner
- => this.Members.TryGetValue(this.OwnerId, out var owner)
- ? owner
- : this.Discord.ApiClient.GetGuildMemberAsync(this.Id, this.OwnerId).ConfigureAwait(false).GetAwaiter().GetResult();
-
- /// <summary>
- /// Gets permissions for the user in the guild (does not include channel overrides)
- /// </summary>
- [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions? Permissions { get; set; }
-
- /// <summary>
- /// Gets the guild's voice region ID.
- /// </summary>
- [JsonProperty("region", NullValueHandling = NullValueHandling.Ignore)]
- internal string VoiceRegionId { get; set; }
-
- /// <summary>
- /// Gets the guild's voice region.
- /// </summary>
- [JsonIgnore]
- public DiscordVoiceRegion VoiceRegion
- => this.Discord.VoiceRegions[this.VoiceRegionId];
-
- /// <summary>
- /// Gets the guild's AFK voice channel ID.
- /// </summary>
- [JsonProperty("afk_channel_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong AfkChannelId { get; set; }
-
- /// <summary>
- /// Gets the guild's AFK voice channel.
- /// </summary>
- [JsonIgnore]
- public DiscordChannel AfkChannel
- => this.GetChannel(this.AfkChannelId);
-
- /// <summary>
- /// List of <see cref="DisCatSharp.Entities.DiscordApplicationCommand"/>.
- /// Null if DisCatSharp.ApplicationCommands is not used or no guild commands are registered.
- /// </summary>
- [JsonIgnore]
- public ReadOnlyCollection<DiscordApplicationCommand> RegisteredApplicationCommands
- => new(this.InternalRegisteredApplicationCommands);
- [JsonIgnore]
- internal List<DiscordApplicationCommand> InternalRegisteredApplicationCommands { get; set; } = null;
-
- /// <summary>
- /// Gets the guild's AFK timeout.
- /// </summary>
- [JsonProperty("afk_timeout", NullValueHandling = NullValueHandling.Ignore)]
- public int AfkTimeout { get; internal set; }
-
- /// <summary>
- /// Gets the guild's verification level.
- /// </summary>
- [JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
- public VerificationLevel VerificationLevel { get; internal set; }
-
- /// <summary>
- /// Gets the guild's default notification settings.
- /// </summary>
- [JsonProperty("default_message_notifications", NullValueHandling = NullValueHandling.Ignore)]
- public DefaultMessageNotifications DefaultMessageNotifications { get; internal set; }
-
- /// <summary>
- /// Gets the guild's explicit content filter settings.
- /// </summary>
- [JsonProperty("explicit_content_filter")]
- public ExplicitContentFilter ExplicitContentFilter { get; internal set; }
-
- /// <summary>
- /// Gets the guild's nsfw level.
- /// </summary>
- [JsonProperty("nsfw_level")]
- public NsfwLevel NsfwLevel { get; internal set; }
-
- /// <summary>
- /// Gets the system channel id.
- /// </summary>
- [JsonProperty("system_channel_id", NullValueHandling = NullValueHandling.Include)]
- internal ulong? SystemChannelId { get; set; }
-
- /// <summary>
- /// Gets the channel where system messages (such as boost and welcome messages) are sent.
- /// </summary>
- [JsonIgnore]
- public DiscordChannel SystemChannel => this.SystemChannelId.HasValue
- ? this.GetChannel(this.SystemChannelId.Value)
- : null;
-
- /// <summary>
- /// Gets the settings for this guild's system channel.
- /// </summary>
- [JsonProperty("system_channel_flags")]
- public SystemChannelFlags SystemChannelFlags { get; internal set; }
-
- /// <summary>
- /// Gets whether this guild's widget is enabled.
- /// </summary>
- [JsonProperty("widget_enabled", NullValueHandling = NullValueHandling.Ignore)]
- public bool? WidgetEnabled { get; internal set; }
-
- /// <summary>
- /// Gets the widget channel id.
- /// </summary>
- [JsonProperty("widget_channel_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong? WidgetChannelId { get; set; }
-
- /// <summary>
- /// Gets the widget channel for this guild.
- /// </summary>
- [JsonIgnore]
- public DiscordChannel WidgetChannel => this.WidgetChannelId.HasValue
- ? this.GetChannel(this.WidgetChannelId.Value)
- : null;
-
- /// <summary>
- /// Gets the rules channel id.
- /// </summary>
- [JsonProperty("rules_channel_id")]
- internal ulong? RulesChannelId { get; set; }
-
- /// <summary>
- /// Gets the rules channel for this guild.
- /// <para>This is only available if the guild is considered "discoverable".</para>
- /// </summary>
- [JsonIgnore]
- public DiscordChannel RulesChannel => this.RulesChannelId.HasValue
- ? this.GetChannel(this.RulesChannelId.Value)
- : null;
-
- /// <summary>
- /// Gets the public updates channel id.
- /// </summary>
- [JsonProperty("public_updates_channel_id")]
- internal ulong? PublicUpdatesChannelId { get; set; }
-
- /// <summary>
- /// Gets the public updates channel (where admins and moderators receive messages from Discord) for this guild.
- /// <para>This is only available if the guild is considered "discoverable".</para>
- /// </summary>
- [JsonIgnore]
- public DiscordChannel PublicUpdatesChannel => this.PublicUpdatesChannelId.HasValue
- ? this.GetChannel(this.PublicUpdatesChannelId.Value)
- : null;
-
- /// <summary>
- /// Gets the application id of this guild if it is bot created.
- /// </summary>
- [JsonProperty("application_id")]
- public ulong? ApplicationId { get; internal set; }
-
- /// <summary>
- /// Gets a collection of this guild's roles.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordRole> Roles => new ReadOnlyConcurrentDictionary<ulong, DiscordRole>(this.RolesInternal);
-
- [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordRole> RolesInternal;
-
- /// <summary>
- /// Gets a collection of this guild's stickers.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordSticker> Stickers => new ReadOnlyConcurrentDictionary<ulong, DiscordSticker>(this.StickersInternal);
-
- [JsonProperty("stickers", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordSticker> StickersInternal;
-
- /// <summary>
- /// Gets a collection of this guild's emojis.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordEmoji> Emojis => new ReadOnlyConcurrentDictionary<ulong, DiscordEmoji>(this.EmojisInternal);
-
- [JsonProperty("emojis", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordEmoji> EmojisInternal;
-
- /// <summary>
- /// Gets a collection of this guild's features.
- /// </summary>
- [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<string> RawFeatures { get; internal set; }
-
- /// <summary>
- /// Gets the guild's features.
- /// </summary>
- [JsonIgnore]
- public GuildFeatures Features => new(this);
-
- /// <summary>
- /// Gets the required multi-factor authentication level for this guild.
- /// </summary>
- [JsonProperty("mfa_level", NullValueHandling = NullValueHandling.Ignore)]
- public MfaLevel MfaLevel { get; internal set; }
-
- /// <summary>
- /// Gets this guild's join date.
- /// </summary>
- [JsonProperty("joined_at", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset JoinedAt { get; internal set; }
-
- /// <summary>
- /// Gets whether this guild is considered to be a large guild.
- /// </summary>
- [JsonProperty("large", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsLarge { get; internal set; }
-
- /// <summary>
- /// Gets whether this guild is unavailable.
- /// </summary>
- [JsonProperty("unavailable", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsUnavailable { get; internal set; }
-
- /// <summary>
- /// Gets the total number of members in this guild.
- /// </summary>
- [JsonProperty("member_count", NullValueHandling = NullValueHandling.Ignore)]
- public int MemberCount { get; internal set; }
-
- /// <summary>
- /// Gets the maximum amount of members allowed for this guild.
- /// </summary>
- [JsonProperty("max_members")]
- public int? MaxMembers { get; internal set; }
-
- /// <summary>
- /// Gets the maximum amount of presences allowed for this guild.
- /// </summary>
- [JsonProperty("max_presences")]
- public int? MaxPresences { get; internal set; }
-
- /// <summary>
- /// Gets the approximate number of members in this guild, when using <see cref="DiscordClient.GetGuildAsync(ulong, bool?)"/> and having withCounts set to true.
- /// </summary>
- [JsonProperty("approximate_member_count", NullValueHandling = NullValueHandling.Ignore)]
- public int? ApproximateMemberCount { get; internal set; }
-
- /// <summary>
- /// Gets the approximate number of presences in this guild, when using <see cref="DiscordClient.GetGuildAsync(ulong, bool?)"/> and having withCounts set to true.
- /// </summary>
- [JsonProperty("approximate_presence_count", NullValueHandling = NullValueHandling.Ignore)]
- public int? ApproximatePresenceCount { get; internal set; }
-
- /// <summary>
- /// Gets the maximum amount of users allowed per video channel.
- /// </summary>
- [JsonProperty("max_video_channel_users", NullValueHandling = NullValueHandling.Ignore)]
- public int? MaxVideoChannelUsers { get; internal set; }
-
- /// <summary>
- /// Gets a dictionary of all the voice states for this guilds. The key for this dictionary is the ID of the user
- /// the voice state corresponds to.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordVoiceState> VoiceStates => new ReadOnlyConcurrentDictionary<ulong, DiscordVoiceState>(this.VoiceStatesInternal);
-
- [JsonProperty("voice_states", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordVoiceState> VoiceStatesInternal;
-
- /// <summary>
- /// Gets a dictionary of all the members that belong to this guild. The dictionary's key is the member ID.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordMember> Members => new ReadOnlyConcurrentDictionary<ulong, DiscordMember>(this.MembersInternal);
-
- [JsonProperty("members", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordMember> MembersInternal;
-
- /// <summary>
- /// Gets a dictionary of all the channels associated with this guild. The dictionary's key is the channel ID.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordChannel> Channels => new ReadOnlyConcurrentDictionary<ulong, DiscordChannel>(this.ChannelsInternal);
-
- [JsonProperty("channels", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordChannel> ChannelsInternal;
-
- internal ConcurrentDictionary<string, DiscordInvite> Invites;
-
- /// <summary>
- /// Gets a dictionary of all the active threads associated with this guild the user has permission to view. The dictionary's key is the channel ID.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordThreadChannel> Threads { get; internal set; }
-
- [JsonProperty("threads", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordThreadChannel> ThreadsInternal = new();
-
- /// <summary>
- /// Gets a dictionary of all active stage instances. The dictionary's key is the stage ID.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordStageInstance> StageInstances { get; internal set; }
-
- [JsonProperty("stage_instances", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordStageInstance> StageInstancesInternal = new();
-
- /// <summary>
- /// Gets a dictionary of all scheduled events.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordScheduledEvent> ScheduledEvents { get; internal set; }
-
- [JsonProperty("guild_scheduled_events", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordScheduledEvent> ScheduledEventsInternal = new();
-
- /// <summary>
- /// Gets the guild member for current user.
- /// </summary>
- [JsonIgnore]
- public DiscordMember CurrentMember
- => this._currentMemberLazy.Value;
-
- [JsonIgnore]
- private readonly Lazy<DiscordMember> _currentMemberLazy;
-
- /// <summary>
- /// Gets the @everyone role for this guild.
- /// </summary>
- [JsonIgnore]
- public DiscordRole EveryoneRole
- => this.GetRole(this.Id);
-
- [JsonIgnore]
- internal bool IsOwnerInternal;
-
- /// <summary>
- /// Gets whether the current user is the guild's owner.
- /// </summary>
- [JsonProperty("owner", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsOwner
- {
- get => this.IsOwnerInternal || this.OwnerId == this.Discord.CurrentUser.Id;
- internal set => this.IsOwnerInternal = value;
- }
-
- /// <summary>
- /// Gets the vanity URL code for this guild, when applicable.
- /// </summary>
- [JsonProperty("vanity_url_code")]
- public string VanityUrlCode { get; internal set; }
-
- /// <summary>
- /// Gets the guild description, when applicable.
- /// </summary>
- [JsonProperty("description")]
- public string Description { get; internal set; }
-
- /// <summary>
- /// Gets this guild's banner hash, when applicable.
- /// </summary>
- [JsonProperty("banner")]
- public string BannerHash { get; internal set; }
-
- /// <summary>
- /// Gets this guild's banner in url form.
- /// </summary>
- [JsonIgnore]
- public string BannerUrl
- => !string.IsNullOrWhiteSpace(this.BannerHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Uri}{Endpoints.BANNERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.BannerHash}.{(this.BannerHash.StartsWith("a_") ? "gif" : "png")}" : null;
-
- /// <summary>
- /// Whether this guild has the community feature enabled.
- /// </summary>
- [JsonIgnore]
- public bool IsCommunity => this.Features.HasCommunityEnabled;
-
- /// <summary>
- /// Whether this guild has enabled the welcome screen.
- /// </summary>
- [JsonIgnore]
- public bool HasWelcomeScreen => this.Features.HasWelcomeScreenEnabled;
-
- /// <summary>
- /// Whether this guild has enabled membership screening.
- /// </summary>
- [JsonIgnore]
- public bool HasMemberVerificationGate => this.Features.HasMembershipScreeningEnabled;
-
- /// <summary>
- /// Gets this guild's premium tier (Nitro boosting).
- /// </summary>
- [JsonProperty("premium_tier")]
- public PremiumTier PremiumTier { get; internal set; }
-
- /// <summary>
- /// Gets the amount of members that boosted this guild.
- /// </summary>
- [JsonProperty("premium_subscription_count", NullValueHandling = NullValueHandling.Ignore)]
- public int? PremiumSubscriptionCount { get; internal set; }
-
- /// <summary>
- /// Whether the premium progress bar is enabled.
- /// </summary>
- [JsonProperty("premium_progress_bar_enabled", NullValueHandling = NullValueHandling.Ignore)]
- public bool PremiumProgressBarEnabled { get; internal set; }
-
- /// <summary>
- /// Gets whether this guild is designated as NSFW.
- /// </summary>
- [JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsNsfw { get; internal set; }
-
- /// <summary>
- /// Gets this guild's hub type, if applicable.
- /// </summary>
- [JsonProperty("hub_type", NullValueHandling = NullValueHandling.Ignore)]
- public HubType HubType { get; internal set; }
-
- /// <summary>
- /// Gets a dictionary of all by position ordered channels associated with this guild. The dictionary's key is the channel ID.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordChannel> OrderedChannels => new ReadOnlyDictionary<ulong, DiscordChannel>(this.InternalSortChannels());
-
- /// <summary>
- /// Sorts the channels.
- /// </summary>
- private Dictionary<ulong, DiscordChannel> InternalSortChannels()
- {
- Dictionary<ulong, DiscordChannel> keyValuePairs = new();
- var ochannels = this.GetOrderedChannels();
- foreach (var ochan in ochannels)
- {
- if (ochan.Key != 0)
- keyValuePairs.Add(ochan.Key, this.GetChannel(ochan.Key));
- foreach (var chan in ochan.Value)
- keyValuePairs.Add(chan.Id, chan);
- }
- return keyValuePairs;
- }
-
- /// <summary>
- /// Gets an ordered <see cref="DiscordChannel"/> list out of the channel cache.
- /// Returns a Dictionary where the key is an ulong and can be mapped to <see cref="ChannelType.Category"/> <see cref="DiscordChannel"/>s.
- /// Ignore the 0 key here, because that indicates that this is the "has no category" list.
- /// Each value contains a ordered list of text/news and voice/stage channels as <see cref="DiscordChannel"/>.
- /// </summary>
- /// <returns>A ordered list of categories with its channels</returns>
- public Dictionary<ulong, List<DiscordChannel>> GetOrderedChannels()
- {
- IReadOnlyList<DiscordChannel> rawChannels = this.ChannelsInternal.Values.ToList();
-
- Dictionary<ulong, List<DiscordChannel>> orderedChannels = new()
- {
- { 0, new List<DiscordChannel>() }
- };
-
- foreach (var channel in rawChannels.Where(c => c.Type == ChannelType.Category).OrderBy(c => c.Position))
- {
- orderedChannels.Add(channel.Id, new List<DiscordChannel>());
- }
-
- foreach (var channel in rawChannels.Where(c => c.ParentId.HasValue && (c.Type == ChannelType.Text || c.Type == ChannelType.News)).OrderBy(c => c.Position))
- {
- orderedChannels[channel.ParentId.Value].Add(channel);
- }
- foreach (var channel in rawChannels.Where(c => c.ParentId.HasValue && (c.Type == ChannelType.Voice || c.Type == ChannelType.Stage)).OrderBy(c => c.Position))
- {
- orderedChannels[channel.ParentId.Value].Add(channel);
- }
-
- foreach (var channel in rawChannels.Where(c => !c.ParentId.HasValue && c.Type != ChannelType.Category && (c.Type == ChannelType.Text || c.Type == ChannelType.News)).OrderBy(c => c.Position))
- {
- orderedChannels[0].Add(channel);
- }
- foreach (var channel in rawChannels.Where(c => !c.ParentId.HasValue && c.Type != ChannelType.Category && (c.Type == ChannelType.Voice || c.Type == ChannelType.Stage)).OrderBy(c => c.Position))
- {
- orderedChannels[0].Add(channel);
- }
-
- return orderedChannels;
- }
-
- /// <summary>
- /// Gets an ordered <see cref="DiscordChannel"/> list.
- /// Returns a Dictionary where the key is an ulong and can be mapped to <see cref="ChannelType.Category"/> <see cref="DiscordChannel"/>s.
- /// Ignore the 0 key here, because that indicates that this is the "has no category" list.
- /// Each value contains a ordered list of text/news and voice/stage channels as <see cref="DiscordChannel"/>.
+ /// Represents a Discord guild.
/// </summary>
- /// <returns>A ordered list of categories with its channels</returns>
- public async Task<Dictionary<ulong, List<DiscordChannel>>> GetOrderedChannelsAsync()
+ public partial class DiscordGuild : SnowflakeObject, IEquatable<DiscordGuild>
{
- var rawChannels = await this.Discord.ApiClient.GetGuildChannelsAsync(this.Id);
-
- Dictionary<ulong, List<DiscordChannel>> orderedChannels = new()
- {
- { 0, new List<DiscordChannel>() }
- };
-
- foreach (var channel in rawChannels.Where(c => c.Type == ChannelType.Category).OrderBy(c => c.Position))
- {
- orderedChannels.Add(channel.Id, new List<DiscordChannel>());
- }
-
- foreach (var channel in rawChannels.Where(c => c.ParentId.HasValue && (c.Type == ChannelType.Text || c.Type == ChannelType.News)).OrderBy(c => c.Position))
- {
- orderedChannels[channel.ParentId.Value].Add(channel);
- }
- foreach (var channel in rawChannels.Where(c => c.ParentId.HasValue && (c.Type == ChannelType.Voice || c.Type == ChannelType.Stage)).OrderBy(c => c.Position))
- {
- orderedChannels[channel.ParentId.Value].Add(channel);
- }
-
- foreach (var channel in rawChannels.Where(c => !c.ParentId.HasValue && c.Type != ChannelType.Category && (c.Type == ChannelType.Text || c.Type == ChannelType.News)).OrderBy(c => c.Position))
- {
- orderedChannels[0].Add(channel);
- }
- foreach (var channel in rawChannels.Where(c => !c.ParentId.HasValue && c.Type != ChannelType.Category && (c.Type == ChannelType.Voice || c.Type == ChannelType.Stage)).OrderBy(c => c.Position))
+ /// <summary>
+ /// Gets the guild's name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild icon's hash.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
+ public string IconHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild icon's url.
+ /// </summary>
+ [JsonIgnore]
+ public string IconUrl
+ => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.{(this.IconHash.StartsWith("a_") ? "gif" : "png")}?size=1024" : null;
+
+ /// <summary>
+ /// Gets the guild splash's hash.
+ /// </summary>
+ [JsonProperty("splash", NullValueHandling = NullValueHandling.Ignore)]
+ public string SplashHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild splash's url.
+ /// </summary>
+ [JsonIgnore]
+ public string SplashUrl
+ => !string.IsNullOrWhiteSpace(this.SplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.SplashHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets the guild discovery splash's hash.
+ /// </summary>
+ [JsonProperty("discovery_splash", NullValueHandling = NullValueHandling.Ignore)]
+ public string DiscoverySplashHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild discovery splash's url.
+ /// </summary>
+ [JsonIgnore]
+ public string DiscoverySplashUrl
+ => !string.IsNullOrWhiteSpace(this.DiscoverySplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILD_DISCOVERY_SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.DiscoverySplashHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets the preferred locale of this guild.
+ /// <para>This is used for server discovery, interactions and notices from Discord. Defaults to en-US.</para>
+ /// </summary>
+ [JsonProperty("preferred_locale", NullValueHandling = NullValueHandling.Ignore)]
+ public string PreferredLocale { get; internal set; }
+
+ /// <summary>
+ /// Gets the ID of the guild's owner.
+ /// </summary>
+ [JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong OwnerId { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild's owner.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordMember Owner
+ => this.Members.TryGetValue(this.OwnerId, out var owner)
+ ? owner
+ : this.Discord.ApiClient.GetGuildMemberAsync(this.Id, this.OwnerId).ConfigureAwait(false).GetAwaiter().GetResult();
+
+ /// <summary>
+ /// Gets permissions for the user in the guild (does not include channel overrides)
+ /// </summary>
+ [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions? Permissions { get; set; }
+
+ /// <summary>
+ /// Gets the guild's voice region ID.
+ /// </summary>
+ [JsonProperty("region", NullValueHandling = NullValueHandling.Ignore)]
+ internal string VoiceRegionId { get; set; }
+
+ /// <summary>
+ /// Gets the guild's voice region.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordVoiceRegion VoiceRegion
+ => this.Discord.VoiceRegions[this.VoiceRegionId];
+
+ /// <summary>
+ /// Gets the guild's AFK voice channel ID.
+ /// </summary>
+ [JsonProperty("afk_channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong AfkChannelId { get; set; }
+
+ /// <summary>
+ /// Gets the guild's AFK voice channel.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel AfkChannel
+ => this.GetChannel(this.AfkChannelId);
+
+ /// <summary>
+ /// List of <see cref="DisCatSharp.Entities.DiscordApplicationCommand"/>.
+ /// Null if DisCatSharp.ApplicationCommands is not used or no guild commands are registered.
+ /// </summary>
+ [JsonIgnore]
+ public ReadOnlyCollection<DiscordApplicationCommand> RegisteredApplicationCommands
+ => new(this.InternalRegisteredApplicationCommands);
+ [JsonIgnore]
+ internal List<DiscordApplicationCommand> InternalRegisteredApplicationCommands { get; set; } = null;
+
+ /// <summary>
+ /// Gets the guild's AFK timeout.
+ /// </summary>
+ [JsonProperty("afk_timeout", NullValueHandling = NullValueHandling.Ignore)]
+ public int AfkTimeout { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild's verification level.
+ /// </summary>
+ [JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
+ public VerificationLevel VerificationLevel { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild's default notification settings.
+ /// </summary>
+ [JsonProperty("default_message_notifications", NullValueHandling = NullValueHandling.Ignore)]
+ public DefaultMessageNotifications DefaultMessageNotifications { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild's explicit content filter settings.
+ /// </summary>
+ [JsonProperty("explicit_content_filter")]
+ public ExplicitContentFilter ExplicitContentFilter { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild's nsfw level.
+ /// </summary>
+ [JsonProperty("nsfw_level")]
+ public NsfwLevel NsfwLevel { get; internal set; }
+
+ /// <summary>
+ /// Gets the system channel id.
+ /// </summary>
+ [JsonProperty("system_channel_id", NullValueHandling = NullValueHandling.Include)]
+ internal ulong? SystemChannelId { get; set; }
+
+ /// <summary>
+ /// Gets the channel where system messages (such as boost and welcome messages) are sent.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel SystemChannel => this.SystemChannelId.HasValue
+ ? this.GetChannel(this.SystemChannelId.Value)
+ : null;
+
+ /// <summary>
+ /// Gets the settings for this guild's system channel.
+ /// </summary>
+ [JsonProperty("system_channel_flags")]
+ public SystemChannelFlags SystemChannelFlags { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this guild's widget is enabled.
+ /// </summary>
+ [JsonProperty("widget_enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? WidgetEnabled { get; internal set; }
+
+ /// <summary>
+ /// Gets the widget channel id.
+ /// </summary>
+ [JsonProperty("widget_channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong? WidgetChannelId { get; set; }
+
+ /// <summary>
+ /// Gets the widget channel for this guild.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel WidgetChannel => this.WidgetChannelId.HasValue
+ ? this.GetChannel(this.WidgetChannelId.Value)
+ : null;
+
+ /// <summary>
+ /// Gets the rules channel id.
+ /// </summary>
+ [JsonProperty("rules_channel_id")]
+ internal ulong? RulesChannelId { get; set; }
+
+ /// <summary>
+ /// Gets the rules channel for this guild.
+ /// <para>This is only available if the guild is considered "discoverable".</para>
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel RulesChannel => this.RulesChannelId.HasValue
+ ? this.GetChannel(this.RulesChannelId.Value)
+ : null;
+
+ /// <summary>
+ /// Gets the public updates channel id.
+ /// </summary>
+ [JsonProperty("public_updates_channel_id")]
+ internal ulong? PublicUpdatesChannelId { get; set; }
+
+ /// <summary>
+ /// Gets the public updates channel (where admins and moderators receive messages from Discord) for this guild.
+ /// <para>This is only available if the guild is considered "discoverable".</para>
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel PublicUpdatesChannel => this.PublicUpdatesChannelId.HasValue
+ ? this.GetChannel(this.PublicUpdatesChannelId.Value)
+ : null;
+
+ /// <summary>
+ /// Gets the application id of this guild if it is bot created.
+ /// </summary>
+ [JsonProperty("application_id")]
+ public ulong? ApplicationId { get; internal set; }
+
+ /// <summary>
+ /// Gets a collection of this guild's roles.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordRole> Roles => new ReadOnlyConcurrentDictionary<ulong, DiscordRole>(this.RolesInternal);
+
+ [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordRole> RolesInternal;
+
+ /// <summary>
+ /// Gets a collection of this guild's stickers.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordSticker> Stickers => new ReadOnlyConcurrentDictionary<ulong, DiscordSticker>(this.StickersInternal);
+
+ [JsonProperty("stickers", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordSticker> StickersInternal;
+
+ /// <summary>
+ /// Gets a collection of this guild's emojis.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordEmoji> Emojis => new ReadOnlyConcurrentDictionary<ulong, DiscordEmoji>(this.EmojisInternal);
+
+ [JsonProperty("emojis", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordEmoji> EmojisInternal;
+
+ /// <summary>
+ /// Gets a collection of this guild's features.
+ /// </summary>
+ [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<string> RawFeatures { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild's features.
+ /// </summary>
+ [JsonIgnore]
+ public GuildFeatures Features => new(this);
+
+ /// <summary>
+ /// Gets the required multi-factor authentication level for this guild.
+ /// </summary>
+ [JsonProperty("mfa_level", NullValueHandling = NullValueHandling.Ignore)]
+ public MfaLevel MfaLevel { get; internal set; }
+
+ /// <summary>
+ /// Gets this guild's join date.
+ /// </summary>
+ [JsonProperty("joined_at", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset JoinedAt { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this guild is considered to be a large guild.
+ /// </summary>
+ [JsonProperty("large", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsLarge { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this guild is unavailable.
+ /// </summary>
+ [JsonProperty("unavailable", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsUnavailable { get; internal set; }
+
+ /// <summary>
+ /// Gets the total number of members in this guild.
+ /// </summary>
+ [JsonProperty("member_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int MemberCount { get; internal set; }
+
+ /// <summary>
+ /// Gets the maximum amount of members allowed for this guild.
+ /// </summary>
+ [JsonProperty("max_members")]
+ public int? MaxMembers { get; internal set; }
+
+ /// <summary>
+ /// Gets the maximum amount of presences allowed for this guild.
+ /// </summary>
+ [JsonProperty("max_presences")]
+ public int? MaxPresences { get; internal set; }
+
+ /// <summary>
+ /// Gets the approximate number of members in this guild, when using <see cref="DiscordClient.GetGuildAsync(ulong, bool?)"/> and having withCounts set to true.
+ /// </summary>
+ [JsonProperty("approximate_member_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int? ApproximateMemberCount { get; internal set; }
+
+ /// <summary>
+ /// Gets the approximate number of presences in this guild, when using <see cref="DiscordClient.GetGuildAsync(ulong, bool?)"/> and having withCounts set to true.
+ /// </summary>
+ [JsonProperty("approximate_presence_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int? ApproximatePresenceCount { get; internal set; }
+
+ /// <summary>
+ /// Gets the maximum amount of users allowed per video channel.
+ /// </summary>
+ [JsonProperty("max_video_channel_users", NullValueHandling = NullValueHandling.Ignore)]
+ public int? MaxVideoChannelUsers { get; internal set; }
+
+ /// <summary>
+ /// Gets a dictionary of all the voice states for this guilds. The key for this dictionary is the ID of the user
+ /// the voice state corresponds to.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordVoiceState> VoiceStates => new ReadOnlyConcurrentDictionary<ulong, DiscordVoiceState>(this.VoiceStatesInternal);
+
+ [JsonProperty("voice_states", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordVoiceState> VoiceStatesInternal;
+
+ /// <summary>
+ /// Gets a dictionary of all the members that belong to this guild. The dictionary's key is the member ID.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordMember> Members => new ReadOnlyConcurrentDictionary<ulong, DiscordMember>(this.MembersInternal);
+
+ [JsonProperty("members", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordMember> MembersInternal;
+
+ /// <summary>
+ /// Gets a dictionary of all the channels associated with this guild. The dictionary's key is the channel ID.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordChannel> Channels => new ReadOnlyConcurrentDictionary<ulong, DiscordChannel>(this.ChannelsInternal);
+
+ [JsonProperty("channels", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordChannel> ChannelsInternal;
+
+ internal ConcurrentDictionary<string, DiscordInvite> Invites;
+
+ /// <summary>
+ /// Gets a dictionary of all the active threads associated with this guild the user has permission to view. The dictionary's key is the channel ID.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordThreadChannel> Threads { get; internal set; }
+
+ [JsonProperty("threads", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordThreadChannel> ThreadsInternal = new();
+
+ /// <summary>
+ /// Gets a dictionary of all active stage instances. The dictionary's key is the stage ID.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordStageInstance> StageInstances { get; internal set; }
+
+ [JsonProperty("stage_instances", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordStageInstance> StageInstancesInternal = new();
+
+ /// <summary>
+ /// Gets a dictionary of all scheduled events.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordScheduledEvent> ScheduledEvents { get; internal set; }
+
+ [JsonProperty("guild_scheduled_events", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordScheduledEvent> ScheduledEventsInternal = new();
+
+ /// <summary>
+ /// Gets the guild member for current user.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordMember CurrentMember
+ => this._currentMemberLazy.Value;
+
+ [JsonIgnore]
+ private readonly Lazy<DiscordMember> _currentMemberLazy;
+
+ /// <summary>
+ /// Gets the @everyone role for this guild.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordRole EveryoneRole
+ => this.GetRole(this.Id);
+
+ [JsonIgnore]
+ internal bool IsOwnerInternal;
+
+ /// <summary>
+ /// Gets whether the current user is the guild's owner.
+ /// </summary>
+ [JsonProperty("owner", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsOwner
{
- orderedChannels[0].Add(channel);
+ get => this.IsOwnerInternal || this.OwnerId == this.Discord.CurrentUser.Id;
+ internal set => this.IsOwnerInternal = value;
}
- return orderedChannels;
- }
-
- /// <summary>
- /// Whether it is synced.
- /// </summary>
- [JsonIgnore]
- internal bool IsSynced { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordGuild"/> class.
- /// </summary>
- internal DiscordGuild()
- {
- this._currentMemberLazy = new Lazy<DiscordMember>(() => this.MembersInternal != null && this.MembersInternal.TryGetValue(this.Discord.CurrentUser.Id, out var member) ? member : null);
- this.Invites = new ConcurrentDictionary<string, DiscordInvite>();
- this.Threads = new ReadOnlyConcurrentDictionary<ulong, DiscordThreadChannel>(this.ThreadsInternal);
- this.StageInstances = new ReadOnlyConcurrentDictionary<ulong, DiscordStageInstance>(this.StageInstancesInternal);
- this.ScheduledEvents = new ReadOnlyConcurrentDictionary<ulong, DiscordScheduledEvent>(this.ScheduledEventsInternal);
- }
-
- #region Guild Methods
-
- /// <summary>
- /// Searches the current guild for members who's display name start with the specified name.
- /// </summary>
- /// <param name="name">The name to search for.</param>
- /// <param name="limit">The maximum amount of members to return. Max 1000. Defaults to 1.</param>
- /// <returns>The members found, if any.</returns>
- public Task<IReadOnlyList<DiscordMember>> SearchMembersAsync(string name, int? limit = 1)
- => this.Discord.ApiClient.SearchMembersAsync(this.Id, name, limit);
-
- /// <summary>
- /// Adds a new member to this guild
- /// </summary>
- /// <param name="user">User to add</param>
- /// <param name="accessToken">User's access token (OAuth2)</param>
- /// <param name="nickname">new nickname</param>
- /// <param name="roles">new roles</param>
- /// <param name="muted">whether this user has to be muted</param>
- /// <param name="deaf">whether this user has to be deafened</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreateInstantInvite" /> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the <paramref name="user"/> or <paramref name="accessToken"/> is not found.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task AddMemberAsync(DiscordUser user, string accessToken, string nickname = null, IEnumerable<DiscordRole> roles = null,
- bool muted = false, bool deaf = false)
- => this.Discord.ApiClient.AddGuildMemberAsync(this.Id, user.Id, accessToken, nickname, roles, muted, deaf);
-
- /// <summary>
- /// Deletes this guild. Requires the caller to be the owner of the guild.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client is not the owner of the guild.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteAsync()
- => this.Discord.ApiClient.DeleteGuildAsync(this.Id);
-
- /// <summary>
- /// Modifies this guild.
- /// </summary>
- /// <param name="action">Action to perform on this guild..</param>
- /// <returns>The modified guild object.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordGuild> ModifyAsync(Action<GuildEditModel> action)
- {
- var mdl = new GuildEditModel();
- action(mdl);
-
- var afkChannelId = mdl.PublicUpdatesChannel
- .MapOrNull<ulong?>(c => c.Type != ChannelType.Voice
- ? throw new ArgumentException("AFK channel needs to be a text channel.")
- : c.Id);
-
- static Optional<ulong?> ChannelToId(Optional<DiscordChannel> ch, string name)
- => ch.MapOrNull<ulong?>(c => c.Type != ChannelType.Text && c.Type != ChannelType.News
- ? throw new ArgumentException($"{name} channel needs to be a text channel.")
- : c.Id);
-
- var rulesChannelId = ChannelToId(mdl.RulesChannel, "Rules");
- var publicUpdatesChannelId = ChannelToId(mdl.PublicUpdatesChannel, "Public updates");
- var systemChannelId = ChannelToId(mdl.SystemChannel, "System");
-
- var iconb64 = ImageTool.Base64FromStream(mdl.Icon);
- var splashb64 = ImageTool.Base64FromStream(mdl.Splash);
- var bannerb64 = ImageTool.Base64FromStream(mdl.Banner);
- var discoverySplash64 = ImageTool.Base64FromStream(mdl.DiscoverySplash);
-
- return await this.Discord.ApiClient.ModifyGuildAsync(this.Id, mdl.Name,
- mdl.VerificationLevel, mdl.DefaultMessageNotifications, mdl.MfaLevel, mdl.ExplicitContentFilter,
- afkChannelId, mdl.AfkTimeout, iconb64, mdl.Owner.Map(e => e.Id), splashb64,
- systemChannelId, mdl.SystemChannelFlags, publicUpdatesChannelId, rulesChannelId,
- mdl.Description, bannerb64, discoverySplash64, mdl.PreferredLocale, mdl.PremiumProgressBarEnabled, mdl.AuditLogReason).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Modifies the community settings async.
- /// This sets <see cref="VerificationLevel.High"/> if not highest and <see cref="ExplicitContentFilter.AllMembers"/>.
- /// </summary>
- /// <param name="enabled">If true, enable <see cref="GuildFeatures.HasCommunityEnabled"/>.</param>
- /// <param name="rulesChannel">The rules channel.</param>
- /// <param name="publicUpdatesChannel">The public updates channel.</param>
- /// <param name="preferredLocale">The preferred locale. Defaults to en-US.</param>
- /// <param name="description">The description.</param>
- /// <param name="defaultMessageNotifications">The default message notifications. Defaults to <see cref="DefaultMessageNotifications.MentionsOnly"/></param>
- /// <param name="reason">The auditlog reason.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordGuild> ModifyCommunitySettingsAsync(bool enabled, DiscordChannel rulesChannel = null, DiscordChannel publicUpdatesChannel = null, string preferredLocale = "en-US", string description = null, DefaultMessageNotifications defaultMessageNotifications = DefaultMessageNotifications.MentionsOnly, string reason = null)
- {
- var verificationLevel = this.VerificationLevel;
- if (this.VerificationLevel != VerificationLevel.Highest)
+ /// <summary>
+ /// Gets the vanity URL code for this guild, when applicable.
+ /// </summary>
+ [JsonProperty("vanity_url_code")]
+ public string VanityUrlCode { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild description, when applicable.
+ /// </summary>
+ [JsonProperty("description")]
+ public string Description { get; internal set; }
+
+ /// <summary>
+ /// Gets this guild's banner hash, when applicable.
+ /// </summary>
+ [JsonProperty("banner")]
+ public string BannerHash { get; internal set; }
+
+ /// <summary>
+ /// Gets this guild's banner in url form.
+ /// </summary>
+ [JsonIgnore]
+ public string BannerUrl
+ => !string.IsNullOrWhiteSpace(this.BannerHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Uri}{Endpoints.BANNERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.BannerHash}.{(this.BannerHash.StartsWith("a_") ? "gif" : "png")}" : null;
+
+ /// <summary>
+ /// Whether this guild has the community feature enabled.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsCommunity => this.Features.HasCommunityEnabled;
+
+ /// <summary>
+ /// Whether this guild has enabled the welcome screen.
+ /// </summary>
+ [JsonIgnore]
+ public bool HasWelcomeScreen => this.Features.HasWelcomeScreenEnabled;
+
+ /// <summary>
+ /// Whether this guild has enabled membership screening.
+ /// </summary>
+ [JsonIgnore]
+ public bool HasMemberVerificationGate => this.Features.HasMembershipScreeningEnabled;
+
+ /// <summary>
+ /// Gets this guild's premium tier (Nitro boosting).
+ /// </summary>
+ [JsonProperty("premium_tier")]
+ public PremiumTier PremiumTier { get; internal set; }
+
+ /// <summary>
+ /// Gets the amount of members that boosted this guild.
+ /// </summary>
+ [JsonProperty("premium_subscription_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int? PremiumSubscriptionCount { get; internal set; }
+
+ /// <summary>
+ /// Whether the premium progress bar is enabled.
+ /// </summary>
+ [JsonProperty("premium_progress_bar_enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public bool PremiumProgressBarEnabled { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this guild is designated as NSFW.
+ /// </summary>
+ [JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsNsfw { get; internal set; }
+
+ /// <summary>
+ /// Gets this guild's hub type, if applicable.
+ /// </summary>
+ [JsonProperty("hub_type", NullValueHandling = NullValueHandling.Ignore)]
+ public HubType HubType { get; internal set; }
+
+ /// <summary>
+ /// Gets a dictionary of all by position ordered channels associated with this guild. The dictionary's key is the channel ID.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordChannel> OrderedChannels => new ReadOnlyDictionary<ulong, DiscordChannel>(this.InternalSortChannels());
+
+ /// <summary>
+ /// Sorts the channels.
+ /// </summary>
+ private Dictionary<ulong, DiscordChannel> InternalSortChannels()
{
- verificationLevel = VerificationLevel.High;
+ Dictionary<ulong, DiscordChannel> keyValuePairs = new();
+ var ochannels = this.GetOrderedChannels();
+ foreach (var ochan in ochannels)
+ {
+ if (ochan.Key != 0)
+ keyValuePairs.Add(ochan.Key, this.GetChannel(ochan.Key));
+ foreach (var chan in ochan.Value)
+ keyValuePairs.Add(chan.Id, chan);
+ }
+ return keyValuePairs;
}
- var explicitContentFilter = ExplicitContentFilter.AllMembers;
-
- static Optional<ulong?> ChannelToId(DiscordChannel ch, string name)
- => ch == null ? null :
- ch.Type != ChannelType.Text && ch.Type != ChannelType.News
- ? throw new ArgumentException($"{name} channel needs to be a text channel.")
- : ch.Id;
-
- var rulesChannelId = ChannelToId(rulesChannel, "Rules");
- var publicUpdatesChannelId = ChannelToId(publicUpdatesChannel, "Public updates");
-
- List<string> features = new();
- var rfeatures = this.RawFeatures.ToList();
- if (this.RawFeatures.Contains("COMMUNITY") && enabled)
+ /// <summary>
+ /// Gets an ordered <see cref="DiscordChannel"/> list out of the channel cache.
+ /// Returns a Dictionary where the key is an ulong and can be mapped to <see cref="ChannelType.Category"/> <see cref="DiscordChannel"/>s.
+ /// Ignore the 0 key here, because that indicates that this is the "has no category" list.
+ /// Each value contains a ordered list of text/news and voice/stage channels as <see cref="DiscordChannel"/>.
+ /// </summary>
+ /// <returns>A ordered list of categories with its channels</returns>
+ public Dictionary<ulong, List<DiscordChannel>> GetOrderedChannels()
{
- features = rfeatures;
- }
- else if (!this.RawFeatures.Contains("COMMUNITY") && enabled)
- {
- rfeatures.Add("COMMUNITY");
- features = rfeatures;
- }
- else if (this.RawFeatures.Contains("COMMUNITY") && !enabled)
- {
- rfeatures.Remove("COMMUNITY");
- features = rfeatures;
- }
- else if (!this.RawFeatures.Contains("COMMUNITY") && !enabled)
- {
- features = rfeatures;
- }
-
- return await this.Discord.ApiClient.ModifyGuildCommunitySettingsAsync(this.Id, features, rulesChannelId, publicUpdatesChannelId, preferredLocale, description, defaultMessageNotifications, explicitContentFilter, verificationLevel, reason).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Timeout a specified member in this guild.
- /// </summary>
- /// <param name="memberId">Member to timeout.</param>
- /// <param name="until">The datetime offset to time out the user. Up to 28 days.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task TimeoutAsync(ulong memberId, DateTimeOffset until, string reason = null)
- => until.Subtract(DateTimeOffset.UtcNow).Days > 28
- ? throw new ArgumentException("Timeout can not be longer than 28 days")
- : this.Discord.ApiClient.ModifyTimeoutAsync(this.Id, memberId, until, reason);
-
- /// <summary>
- /// Timeout a specified member in this guild.
- /// </summary>
- /// <param name="memberId">Member to timeout.</param>
- /// <param name="until">The timespan to time out the user. Up to 28 days.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task TimeoutAsync(ulong memberId, TimeSpan until, string reason = null)
- => this.TimeoutAsync(memberId, DateTimeOffset.UtcNow + until, reason);
-
- /// <summary>
- /// Timeout a specified member in this guild.
- /// </summary>
- /// <param name="memberId">Member to timeout.</param>
- /// <param name="until">The datetime to time out the user. Up to 28 days.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task TimeoutAsync(ulong memberId, DateTime until, string reason = null)
- => this.TimeoutAsync(memberId, until.ToUniversalTime() - DateTime.UtcNow, reason);
-
- /// <summary>
- /// Removes the timeout from a specified member in this guild.
- /// </summary>
- /// <param name="memberId">Member to remove the timeout from.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task RemoveTimeoutAsync(ulong memberId, string reason = null)
- => this.Discord.ApiClient.ModifyTimeoutAsync(this.Id, memberId, null, reason);
+ IReadOnlyList<DiscordChannel> rawChannels = this.ChannelsInternal.Values.ToList();
- /// <summary>
- /// Bans a specified member from this guild.
- /// </summary>
- /// <param name="member">Member to ban.</param>
- /// <param name="deleteMessageDays">How many days to remove messages from.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task BanMemberAsync(DiscordMember member, int deleteMessageDays = 0, string reason = null)
- => this.Discord.ApiClient.CreateGuildBanAsync(this.Id, member.Id, deleteMessageDays, reason);
-
- /// <summary>
- /// Bans a specified user by ID. This doesn't require the user to be in this guild.
- /// </summary>
- /// <param name="userId">ID of the user to ban.</param>
- /// <param name="deleteMessageDays">How many days to remove messages from.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task BanMemberAsync(ulong userId, int deleteMessageDays = 0, string reason = null)
- => this.Discord.ApiClient.CreateGuildBanAsync(this.Id, userId, deleteMessageDays, reason);
-
- /// <summary>
- /// Unbans a user from this guild.
- /// </summary>
- /// <param name="user">User to unban.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the user does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task UnbanMemberAsync(DiscordUser user, string reason = null)
- => this.Discord.ApiClient.RemoveGuildBanAsync(this.Id, user.Id, reason);
-
- /// <summary>
- /// Unbans a user by ID.
- /// </summary>
- /// <param name="userId">ID of the user to unban.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the user does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task UnbanMemberAsync(ulong userId, string reason = null)
- => this.Discord.ApiClient.RemoveGuildBanAsync(this.Id, userId, reason);
-
- /// <summary>
- /// Leaves this guild.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task LeaveAsync()
- => this.Discord.ApiClient.LeaveGuildAsync(this.Id);
-
- /// <summary>
- /// Gets the bans for this guild, allowing for pagination.
- /// </summary>
- /// <param name="limit">Maximum number of bans to fetch. Max 1000. Defaults to 1000.</param>
- /// <param name="before">The Id of the user before which to fetch the bans. Overrides <paramref name="after"/> if both are present.</param>
- /// <param name="after">The Id of the user after which to fetch the bans.</param>
- /// <returns>Collection of bans in this guild in ascending order by user id.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordBan>> GetBansAsync(int? limit = null, ulong? before = null, ulong? after = null)
- => this.Discord.ApiClient.GetGuildBansAsync(this.Id, limit, before, after);
-
- /// <summary>
- /// Gets a ban for a specific user.
- /// </summary>
- /// <param name="userId">The Id of the user to get the ban for.</param>
- /// <returns>The requested ban object.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the specified user is not banned.</exception>
- public Task<DiscordBan> GetBanAsync(ulong userId)
- => this.Discord.ApiClient.GetGuildBanAsync(this.Id, userId);
-
- /// <summary>
- /// Gets a ban for a specific user.
- /// </summary>
- /// <param name="user">The user to get the ban for.</param>
- /// <returns>The requested ban object.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the specified user is not banned.</exception>
- public Task<DiscordBan> GetBanAsync(DiscordUser user)
- => this.GetBanAsync(user.Id);
-
- #region Sheduled Events
-
- /// <summary>
- /// Creates a scheduled event.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="scheduledStartTime">The scheduled start time.</param>
- /// <param name="scheduledEndTime">The scheduled end time.</param>
- /// <param name="channel">The channel.</param>
- /// <param name="metadata">The metadata.</param>
- /// <param name="description">The description.</param>
- /// <param name="type">The type.</param>
- /// <param name="coverImage">The cover image.</param>
- /// <param name="reason">The reason.</param>
- /// <returns>A scheduled event.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordScheduledEvent> CreateScheduledEventAsync(string name, DateTimeOffset scheduledStartTime, DateTimeOffset? scheduledEndTime = null, DiscordChannel channel = null, DiscordScheduledEventEntityMetadata metadata = null, string description = null, ScheduledEventEntityType type = ScheduledEventEntityType.StageInstance, Optional<Stream> coverImage = default, string reason = null)
- {
- var coverb64 = ImageTool.Base64FromStream(coverImage);
- return await this.Discord.ApiClient.CreateGuildScheduledEventAsync(this.Id, type == ScheduledEventEntityType.External ? null : channel?.Id, type == ScheduledEventEntityType.External ? metadata : null, name, scheduledStartTime, scheduledEndTime.HasValue && type == ScheduledEventEntityType.External ? scheduledEndTime.Value : null, description, type, coverb64, reason);
- }
-
- /// <summary>
- /// Creates a scheduled event with type <see cref="ScheduledEventEntityType.External"/>.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="scheduledStartTime">The scheduled start time.</param>
- /// <param name="scheduledEndTime">The scheduled end time.</param>
- /// <param name="location">The location of the external event.</param>
- /// <param name="description">The description.</param>
- /// <param name="coverImage">The cover image.</param>
- /// <param name="reason">The reason.</param>
- /// <returns>A scheduled event.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordScheduledEvent> CreateExternalScheduledEventAsync(string name, DateTimeOffset scheduledStartTime, DateTimeOffset scheduledEndTime, string location, string description = null, Optional<Stream> coverImage = default, string reason = null)
- {
- var coverb64 = ImageTool.Base64FromStream(coverImage);
- return await this.Discord.ApiClient.CreateGuildScheduledEventAsync(this.Id, null, new DiscordScheduledEventEntityMetadata(location), name, scheduledStartTime, scheduledEndTime, description, ScheduledEventEntityType.External, coverb64, reason);
- }
-
-
- /// <summary>
- /// Gets a specific scheduled events.
- /// </summary>
- /// <param name="scheduledEventId">The Id of the event to get.</param>
- /// <param name="withUserCount">Whether to include user count.</param>
- /// <returns>A scheduled event.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordScheduledEvent> GetScheduledEventAsync(ulong scheduledEventId, bool? withUserCount = null)
- => this.ScheduledEventsInternal.TryGetValue(scheduledEventId, out var ev) ? ev : await this.Discord.ApiClient.GetGuildScheduledEventAsync(this.Id, scheduledEventId, withUserCount);
-
- /// <summary>
- /// Gets a specific scheduled events.
- /// </summary>
- /// <param name="scheduledEvent">The event to get.</param>
- /// <param name="withUserCount">Whether to include user count.</param>
- /// <returns>A sheduled event.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordScheduledEvent> GetScheduledEventAsync(DiscordScheduledEvent scheduledEvent, bool? withUserCount = null)
- => await this.GetScheduledEventAsync(scheduledEvent.Id, withUserCount);
-
- /// <summary>
- /// Gets the guilds scheduled events.
- /// </summary>
- /// <param name="withUserCount">Whether to include user count.</param>
- /// <returns>A list of the guilds scheduled events.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<IReadOnlyDictionary<ulong, DiscordScheduledEvent>> GetScheduledEventsAsync(bool? withUserCount = null)
- => await this.Discord.ApiClient.ListGuildScheduledEventsAsync(this.Id, withUserCount);
- #endregion
-
- /// <summary>
- /// Creates a new text channel in this guild.
- /// </summary>
- /// <param name="name">Name of the new channel.</param>
- /// <param name="parent">Category to put this channel in.</param>
- /// <param name="topic">Topic of the channel.</param>
- /// <param name="overwrites">Permission overwrites for this channel.</param>
- /// <param name="nsfw">Whether the channel is to be flagged as not safe for work.</param>
- /// <param name="perUserRateLimit">Slow mode timeout for users.</param>
- /// <param name="defaultAutoArchiveDuration">The default auto archive duration for new threads.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>The newly-created channel.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordChannel> CreateTextChannelAsync(string name, DiscordChannel parent = null, Optional<string> topic = default, IEnumerable<DiscordOverwriteBuilder> overwrites = null, bool? nsfw = null, Optional<int?> perUserRateLimit = default, ThreadAutoArchiveDuration defaultAutoArchiveDuration = ThreadAutoArchiveDuration.OneDay, string reason = null)
- => this.CreateChannelAsync(name, ChannelType.Text, parent, topic, null, null, overwrites, nsfw, perUserRateLimit, null, defaultAutoArchiveDuration, reason);
-
- /// <summary>
- /// Creates a new channel category in this guild.
- /// </summary>
- /// <param name="name">Name of the new category.</param>
- /// <param name="overwrites">Permission overwrites for this category.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>The newly-created channel category.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordChannel> CreateChannelCategoryAsync(string name, IEnumerable<DiscordOverwriteBuilder> overwrites = null, string reason = null)
- => this.CreateChannelAsync(name, ChannelType.Category, null, Optional.None, null, null, overwrites, null, Optional.None, null, null, reason);
-
- /// <summary>
- /// Creates a new stage channel in this guild.
- /// </summary>
- /// <param name="name">Name of the new stage channel.</param>
- /// <param name="overwrites">Permission overwrites for this stage channel.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>The newly-created stage channel.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/>.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.NotSupportedException">Thrown when the guilds has not enabled community.</exception>
- public Task<DiscordChannel> CreateStageChannelAsync(string name, IEnumerable<DiscordOverwriteBuilder> overwrites = null, string reason = null)
- => this.Features.HasCommunityEnabled ? this.CreateChannelAsync(name, ChannelType.Stage, null, Optional.None, null, null, overwrites, null, Optional.None, null, null, reason) : throw new NotSupportedException("Guild has not enabled community. Can not create a stage channel.");
-
- /// <summary>
- /// Creates a new news channel in this guild.
- /// </summary>
- /// <param name="name">Name of the new news channel.</param>
- /// <param name="overwrites">Permission overwrites for this news channel.</param>
- /// <param name="defaultAutoArchiveDuration">The default auto archive duration for new threads.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>The newly-created news channel.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/>.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.NotSupportedException">Thrown when the guilds has not enabled community.</exception>
- public Task<DiscordChannel> CreateNewsChannelAsync(string name, IEnumerable<DiscordOverwriteBuilder> overwrites = null, string reason = null, ThreadAutoArchiveDuration defaultAutoArchiveDuration = ThreadAutoArchiveDuration.OneDay)
- => this.Features.HasCommunityEnabled ? this.CreateChannelAsync(name, ChannelType.News, null, Optional.None, null, null, overwrites, null, Optional.None, null, defaultAutoArchiveDuration, reason) : throw new NotSupportedException("Guild has not enabled community. Can not create a news channel.");
-
- /// <summary>
- /// Creates a new voice channel in this guild.
- /// </summary>
- /// <param name="name">Name of the new channel.</param>
- /// <param name="parent">Category to put this channel in.</param>
- /// <param name="bitrate">Bitrate of the channel.</param>
- /// <param name="userLimit">Maximum number of users in the channel.</param>
- /// <param name="overwrites">Permission overwrites for this channel.</param>
- /// <param name="qualityMode">Video quality mode of the channel.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>The newly-created channel.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordChannel> CreateVoiceChannelAsync(string name, DiscordChannel parent = null, int? bitrate = null, int? userLimit = null, IEnumerable<DiscordOverwriteBuilder> overwrites = null, VideoQualityMode? qualityMode = null, string reason = null)
- => this.CreateChannelAsync(name, ChannelType.Voice, parent, Optional.None, bitrate, userLimit, overwrites, null, Optional.None, qualityMode, null, reason);
-
- /// <summary>
- /// Creates a new channel in this guild.
- /// </summary>
- /// <param name="name">Name of the new channel.</param>
- /// <param name="type">Type of the new channel.</param>
- /// <param name="parent">Category to put this channel in.</param>
- /// <param name="topic">Topic of the channel.</param>
- /// <param name="bitrate">Bitrate of the channel. Applies to voice only.</param>
- /// <param name="userLimit">Maximum number of users in the channel. Applies to voice only.</param>
- /// <param name="overwrites">Permission overwrites for this channel.</param>
- /// <param name="nsfw">Whether the channel is to be flagged as not safe for work. Applies to text only.</param>
- /// <param name="perUserRateLimit">Slow mode timeout for users.</param>
- /// <param name="qualityMode">Video quality mode of the channel. Applies to voice only.</param>
- /// <param name="defaultAutoArchiveDuration">The default auto archive duration for new threads.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>The newly-created channel.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordChannel> CreateChannelAsync(string name, ChannelType type, DiscordChannel parent = null, Optional<string> topic = default, int? bitrate = null, int? userLimit = null, IEnumerable<DiscordOverwriteBuilder> overwrites = null, bool? nsfw = null, Optional<int?> perUserRateLimit = default, VideoQualityMode? qualityMode = null, ThreadAutoArchiveDuration? defaultAutoArchiveDuration = null, string reason = null) =>
- // technically you can create news/store channels but not always
- type != ChannelType.Text && type != ChannelType.Voice && type != ChannelType.Category && type != ChannelType.News && type != ChannelType.Store && type != ChannelType.Stage
- ? throw new ArgumentException("Channel type must be text, voice, stage, or category.", nameof(type))
- : type == ChannelType.Category && parent != null
- ? throw new ArgumentException("Cannot specify parent of a channel category.", nameof(parent))
- : this.Discord.ApiClient.CreateGuildChannelAsync(this.Id, name, type, parent?.Id, topic, bitrate, userLimit, overwrites, nsfw, perUserRateLimit, qualityMode, defaultAutoArchiveDuration, reason);
-
- /// <summary>
- /// Gets active threads. Can contain more threads.
- /// If the result's value 'HasMore' is true, you need to recall this function to get older threads.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordThreadResult> GetActiveThreadsAsync()
- => this.Discord.ApiClient.GetActiveThreadsAsync(this.Id);
-
- /// <summary>
- /// <para>Deletes all channels in this guild.</para>
- /// <para>Note that this is irreversible. Use carefully!</para>
- /// </summary>
- /// <returns></returns>
- public Task DeleteAllChannelsAsync()
- {
- var tasks = this.Channels.Values.Select(xc => xc.DeleteAsync());
- return Task.WhenAll(tasks);
- }
-
- /// <summary>
- /// Estimates the number of users to be pruned.
- /// </summary>
- /// <param name="days">Minimum number of inactivity days required for users to be pruned. Defaults to 7.</param>
- /// <param name="includedRoles">The roles to be included in the prune.</param>
- /// <returns>Number of users that will be pruned.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.KickMembers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<int> GetPruneCountAsync(int days = 7, IEnumerable<DiscordRole> includedRoles = null)
- {
- if (includedRoles != null)
- {
- includedRoles = includedRoles.Where(r => r != null);
- var roleCount = includedRoles.Count();
- var roleArr = includedRoles.ToArray();
- var rawRoleIds = new List<ulong>();
+ Dictionary<ulong, List<DiscordChannel>> orderedChannels = new()
+ {
+ { 0, new List<DiscordChannel>() }
+ };
- for (var i = 0; i < roleCount; i++)
+ foreach (var channel in rawChannels.Where(c => c.Type == ChannelType.Category).OrderBy(c => c.Position))
{
- if (this.RolesInternal.ContainsKey(roleArr[i].Id))
- rawRoleIds.Add(roleArr[i].Id);
+ orderedChannels.Add(channel.Id, new List<DiscordChannel>());
}
- return this.Discord.ApiClient.GetGuildPruneCountAsync(this.Id, days, rawRoleIds);
- }
-
- return this.Discord.ApiClient.GetGuildPruneCountAsync(this.Id, days, null);
- }
-
- /// <summary>
- /// Prunes inactive users from this guild.
- /// </summary>
- /// <param name="days">Minimum number of inactivity days required for users to be pruned. Defaults to 7.</param>
- /// <param name="computePruneCount">Whether to return the prune count after this method completes. This is discouraged for larger guilds.</param>
- /// <param name="includedRoles">The roles to be included in the prune.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>Number of users pruned.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<int?> PruneAsync(int days = 7, bool computePruneCount = true, IEnumerable<DiscordRole> includedRoles = null, string reason = null)
- {
- if (includedRoles != null)
- {
- includedRoles = includedRoles.Where(r => r != null);
- var roleCount = includedRoles.Count();
- var roleArr = includedRoles.ToArray();
- var rawRoleIds = new List<ulong>();
-
- for (var i = 0; i < roleCount; i++)
+ foreach (var channel in rawChannels.Where(c => c.ParentId.HasValue && (c.Type == ChannelType.Text || c.Type == ChannelType.News)).OrderBy(c => c.Position))
{
- if (this.RolesInternal.ContainsKey(roleArr[i].Id))
- rawRoleIds.Add(roleArr[i].Id);
+ orderedChannels[channel.ParentId.Value].Add(channel);
}
-
- return this.Discord.ApiClient.BeginGuildPruneAsync(this.Id, days, computePruneCount, rawRoleIds, reason);
- }
-
- return this.Discord.ApiClient.BeginGuildPruneAsync(this.Id, days, computePruneCount, null, reason);
- }
-
- /// <summary>
- /// Gets integrations attached to this guild.
- /// </summary>
- /// <returns>Collection of integrations attached to this guild.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordIntegration>> GetIntegrationsAsync()
- => this.Discord.ApiClient.GetGuildIntegrationsAsync(this.Id);
-
- /// <summary>
- /// Attaches an integration from current user to this guild.
- /// </summary>
- /// <param name="integration">Integration to attach.</param>
- /// <returns>The integration after being attached to the guild.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordIntegration> AttachUserIntegrationAsync(DiscordIntegration integration)
- => this.Discord.ApiClient.CreateGuildIntegrationAsync(this.Id, integration.Type, integration.Id);
-
- /// <summary>
- /// Modifies an integration in this guild.
- /// </summary>
- /// <param name="integration">Integration to modify.</param>
- /// <param name="expireBehaviour">Number of days after which the integration expires.</param>
- /// <param name="expireGracePeriod">Length of grace period which allows for renewing the integration.</param>
- /// <param name="enableEmoticons">Whether emotes should be synced from this integration.</param>
- /// <returns>The modified integration.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordIntegration> ModifyIntegrationAsync(DiscordIntegration integration, int expireBehaviour, int expireGracePeriod, bool enableEmoticons)
- => this.Discord.ApiClient.ModifyGuildIntegrationAsync(this.Id, integration.Id, expireBehaviour, expireGracePeriod, enableEmoticons);
-
- /// <summary>
- /// Removes an integration from this guild.
- /// </summary>
- /// <param name="integration">Integration to remove.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteIntegrationAsync(DiscordIntegration integration)
- => this.Discord.ApiClient.DeleteGuildIntegrationAsync(this.Id, integration);
-
- /// <summary>
- /// Forces re-synchronization of an integration for this guild.
- /// </summary>
- /// <param name="integration">Integration to synchronize.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task SyncIntegrationAsync(DiscordIntegration integration)
- => this.Discord.ApiClient.SyncGuildIntegrationAsync(this.Id, integration.Id);
-
- /// <summary>
- /// Gets the voice regions for this guild.
- /// </summary>
- /// <returns>Voice regions available for this guild.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<IReadOnlyList<DiscordVoiceRegion>> ListVoiceRegionsAsync()
- {
- var vrs = await this.Discord.ApiClient.GetGuildVoiceRegionsAsync(this.Id).ConfigureAwait(false);
- foreach (var xvr in vrs)
- this.Discord.InternalVoiceRegions.TryAdd(xvr.Id, xvr);
-
- return vrs;
- }
-
- /// <summary>
- /// Gets an invite from this guild from an invite code.
- /// </summary>
- /// <param name="code">The invite code</param>
- /// <returns>An invite, or null if not in cache.</returns>
- public DiscordInvite GetInvite(string code)
- => this.Invites.TryGetValue(code, out var invite) ? invite : null;
-
- /// <summary>
- /// Gets all the invites created for all the channels in this guild.
- /// </summary>
- /// <returns>A collection of invites.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<IReadOnlyList<DiscordInvite>> GetInvitesAsync()
- {
- var res = await this.Discord.ApiClient.GetGuildInvitesAsync(this.Id).ConfigureAwait(false);
-
- var intents = this.Discord.Configuration.Intents;
-
- if (!intents.HasIntent(DiscordIntents.GuildInvites))
- {
- for (var i = 0; i < res.Count; i++)
- this.Invites[res[i].Code] = res[i];
- }
-
- return res;
- }
-
- /// <summary>
- /// Gets the vanity invite for this guild.
- /// </summary>
- /// <returns>A partial vanity invite.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordInvite> GetVanityInviteAsync()
- => this.Discord.ApiClient.GetGuildVanityUrlAsync(this.Id);
-
- /// <summary>
- /// Gets all the webhooks created for all the channels in this guild.
- /// </summary>
- /// <returns>A collection of webhooks this guild has.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordWebhook>> GetWebhooksAsync()
- => this.Discord.ApiClient.GetGuildWebhooksAsync(this.Id);
-
- /// <summary>
- /// Gets this guild's widget image.
- /// </summary>
- /// <param name="bannerType">The format of the widget.</param>
- /// <returns>The URL of the widget image.</returns>
- public string GetWidgetImage(WidgetType bannerType = WidgetType.Shield)
- {
- var param = bannerType switch
- {
- WidgetType.Banner1 => "banner1",
- WidgetType.Banner2 => "banner2",
- WidgetType.Banner3 => "banner3",
- WidgetType.Banner4 => "banner4",
- _ => "shield",
- };
- return $"{Endpoints.BASE_URI}{Endpoints.GUILDS}/{this.Id}{Endpoints.WIDGET_PNG}?style={param}";
- }
-
- /// <summary>
- /// Gets a member of this guild by their user ID.
- /// </summary>
- /// <param name="userId">ID of the member to get.</param>
- /// <returns>The requested member.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMember> GetMemberAsync(ulong userId)
- {
- if (this.MembersInternal != null && this.MembersInternal.TryGetValue(userId, out var mbr))
- return mbr;
-
- mbr = await this.Discord.ApiClient.GetGuildMemberAsync(this.Id, userId).ConfigureAwait(false);
-
- var intents = this.Discord.Configuration.Intents;
-
- if (intents.HasIntent(DiscordIntents.GuildMembers))
- {
- if (this.MembersInternal != null)
+ foreach (var channel in rawChannels.Where(c => c.ParentId.HasValue && (c.Type == ChannelType.Voice || c.Type == ChannelType.Stage)).OrderBy(c => c.Position))
{
- this.MembersInternal[userId] = mbr;
+ orderedChannels[channel.ParentId.Value].Add(channel);
}
- }
- return mbr;
- }
+ foreach (var channel in rawChannels.Where(c => !c.ParentId.HasValue && c.Type != ChannelType.Category && (c.Type == ChannelType.Text || c.Type == ChannelType.News)).OrderBy(c => c.Position))
+ {
+ orderedChannels[0].Add(channel);
+ }
+ foreach (var channel in rawChannels.Where(c => !c.ParentId.HasValue && c.Type != ChannelType.Category && (c.Type == ChannelType.Voice || c.Type == ChannelType.Stage)).OrderBy(c => c.Position))
+ {
+ orderedChannels[0].Add(channel);
+ }
- /// <summary>
- /// Retrieves a full list of members from Discord. This method will bypass cache.
- /// </summary>
- /// <returns>A collection of all members in this guild.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<IReadOnlyCollection<DiscordMember>> GetAllMembersAsync()
- {
- var recmbr = new HashSet<DiscordMember>();
+ return orderedChannels;
+ }
- var recd = 1000;
- var last = 0ul;
- while (recd > 0)
+ /// <summary>
+ /// Gets an ordered <see cref="DiscordChannel"/> list.
+ /// Returns a Dictionary where the key is an ulong and can be mapped to <see cref="ChannelType.Category"/> <see cref="DiscordChannel"/>s.
+ /// Ignore the 0 key here, because that indicates that this is the "has no category" list.
+ /// Each value contains a ordered list of text/news and voice/stage channels as <see cref="DiscordChannel"/>.
+ /// </summary>
+ /// <returns>A ordered list of categories with its channels</returns>
+ public async Task<Dictionary<ulong, List<DiscordChannel>>> GetOrderedChannelsAsync()
{
- var tms = await this.Discord.ApiClient.ListGuildMembersAsync(this.Id, 1000, last == 0 ? null : (ulong?)last).ConfigureAwait(false);
- recd = tms.Count;
+ var rawChannels = await this.Discord.ApiClient.GetGuildChannelsAsync(this.Id);
- foreach (var xtm in tms)
+ Dictionary<ulong, List<DiscordChannel>> orderedChannels = new()
{
- var usr = new DiscordUser(xtm.User) { Discord = this.Discord };
+ { 0, new List<DiscordChannel>() }
+ };
- usr = this.Discord.UserCache.AddOrUpdate(xtm.User.Id, usr, (id, old) =>
- {
- old.Username = usr.Username;
- old.Discord = usr.Discord;
- old.AvatarHash = usr.AvatarHash;
+ foreach (var channel in rawChannels.Where(c => c.Type == ChannelType.Category).OrderBy(c => c.Position))
+ {
+ orderedChannels.Add(channel.Id, new List<DiscordChannel>());
+ }
- return old;
- });
+ foreach (var channel in rawChannels.Where(c => c.ParentId.HasValue && (c.Type == ChannelType.Text || c.Type == ChannelType.News)).OrderBy(c => c.Position))
+ {
+ orderedChannels[channel.ParentId.Value].Add(channel);
+ }
+ foreach (var channel in rawChannels.Where(c => c.ParentId.HasValue && (c.Type == ChannelType.Voice || c.Type == ChannelType.Stage)).OrderBy(c => c.Position))
+ {
+ orderedChannels[channel.ParentId.Value].Add(channel);
+ }
- recmbr.Add(new DiscordMember(xtm) { Discord = this.Discord, GuildId = this.Id });
+ foreach (var channel in rawChannels.Where(c => !c.ParentId.HasValue && c.Type != ChannelType.Category && (c.Type == ChannelType.Text || c.Type == ChannelType.News)).OrderBy(c => c.Position))
+ {
+ orderedChannels[0].Add(channel);
+ }
+ foreach (var channel in rawChannels.Where(c => !c.ParentId.HasValue && c.Type != ChannelType.Category && (c.Type == ChannelType.Voice || c.Type == ChannelType.Stage)).OrderBy(c => c.Position))
+ {
+ orderedChannels[0].Add(channel);
}
- var tm = tms.LastOrDefault();
- last = tm?.User.Id ?? 0;
+ return orderedChannels;
}
- return new ReadOnlySet<DiscordMember>(recmbr);
- }
-
- /// <summary>
- /// Requests that Discord send a list of guild members based on the specified arguments. This method will fire the <see cref="DiscordClient.GuildMembersChunked"/> event.
- /// <para>If no arguments aside from <paramref name="presences"/> and <paramref name="nonce"/> are specified, this will request all guild members.</para>
- /// </summary>
- /// <param name="query">Filters the returned members based on what the username starts with. Either this or <paramref name="userIds"/> must not be null.
- /// The <paramref name="limit"/> must also be greater than 0 if this is specified.</param>
- /// <param name="limit">Total number of members to request. This must be greater than 0 if <paramref name="query"/> is specified.</param>
- /// <param name="presences">Whether to include the <see cref="DisCatSharp.EventArgs.GuildMembersChunkEventArgs.Presences"/> associated with the fetched members.</param>
- /// <param name="userIds">Whether to limit the request to the specified user ids. Either this or <paramref name="query"/> must not be null.</param>
- /// <param name="nonce">The unique string to identify the response.</param>
- public async Task RequestMembersAsync(string query = "", int limit = 0, bool? presences = null, IEnumerable<ulong> userIds = null, string nonce = null)
- {
- if (this.Discord is not DiscordClient client)
- throw new InvalidOperationException("This operation is only valid for regular Discord clients.");
-
- if (query == null && userIds == null)
- throw new ArgumentException("The query and user IDs cannot both be null.");
+ /// <summary>
+ /// Whether it is synced.
+ /// </summary>
+ [JsonIgnore]
+ internal bool IsSynced { get; set; }
- if (query != null && userIds != null)
- query = null;
-
- var grgm = new GatewayRequestGuildMembers(this)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordGuild"/> class.
+ /// </summary>
+ internal DiscordGuild()
{
- Query = query,
- Limit = limit >= 0 ? limit : 0,
- Presences = presences,
- UserIds = userIds,
- Nonce = nonce
- };
-
- var payload = new GatewayPayload
+ this._currentMemberLazy = new Lazy<DiscordMember>(() => this.MembersInternal != null && this.MembersInternal.TryGetValue(this.Discord.CurrentUser.Id, out var member) ? member : null);
+ this.Invites = new ConcurrentDictionary<string, DiscordInvite>();
+ this.Threads = new ReadOnlyConcurrentDictionary<ulong, DiscordThreadChannel>(this.ThreadsInternal);
+ this.StageInstances = new ReadOnlyConcurrentDictionary<ulong, DiscordStageInstance>(this.StageInstancesInternal);
+ this.ScheduledEvents = new ReadOnlyConcurrentDictionary<ulong, DiscordScheduledEvent>(this.ScheduledEventsInternal);
+ }
+
+ #region Guild Methods
+
+ /// <summary>
+ /// Searches the current guild for members who's display name start with the specified name.
+ /// </summary>
+ /// <param name="name">The name to search for.</param>
+ /// <param name="limit">The maximum amount of members to return. Max 1000. Defaults to 1.</param>
+ /// <returns>The members found, if any.</returns>
+ public Task<IReadOnlyList<DiscordMember>> SearchMembersAsync(string name, int? limit = 1)
+ => this.Discord.ApiClient.SearchMembersAsync(this.Id, name, limit);
+
+ /// <summary>
+ /// Adds a new member to this guild
+ /// </summary>
+ /// <param name="user">User to add</param>
+ /// <param name="accessToken">User's access token (OAuth2)</param>
+ /// <param name="nickname">new nickname</param>
+ /// <param name="roles">new roles</param>
+ /// <param name="muted">whether this user has to be muted</param>
+ /// <param name="deaf">whether this user has to be deafened</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreateInstantInvite" /> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the <paramref name="user"/> or <paramref name="accessToken"/> is not found.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task AddMemberAsync(DiscordUser user, string accessToken, string nickname = null, IEnumerable<DiscordRole> roles = null,
+ bool muted = false, bool deaf = false)
+ => this.Discord.ApiClient.AddGuildMemberAsync(this.Id, user.Id, accessToken, nickname, roles, muted, deaf);
+
+ /// <summary>
+ /// Deletes this guild. Requires the caller to be the owner of the guild.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client is not the owner of the guild.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteAsync()
+ => this.Discord.ApiClient.DeleteGuildAsync(this.Id);
+
+ /// <summary>
+ /// Modifies this guild.
+ /// </summary>
+ /// <param name="action">Action to perform on this guild..</param>
+ /// <returns>The modified guild object.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordGuild> ModifyAsync(Action<GuildEditModel> action)
{
- OpCode = GatewayOpCode.RequestGuildMembers,
- Data = grgm
- };
+ var mdl = new GuildEditModel();
+ action(mdl);
- var payloadStr = JsonConvert.SerializeObject(payload, Formatting.None);
- await client.WsSendAsync(payloadStr).ConfigureAwait(false);
- }
+ var afkChannelId = mdl.PublicUpdatesChannel
+ .MapOrNull<ulong?>(c => c.Type != ChannelType.Voice
+ ? throw new ArgumentException("AFK channel needs to be a text channel.")
+ : c.Id);
- /// <summary>
- /// Gets all the channels this guild has.
- /// </summary>
- /// <returns>A collection of this guild's channels.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordChannel>> GetChannelsAsync()
- => this.Discord.ApiClient.GetGuildChannelsAsync(this.Id);
+ static Optional<ulong?> ChannelToId(Optional<DiscordChannel> ch, string name)
+ => ch.MapOrNull<ulong?>(c => c.Type != ChannelType.Text && c.Type != ChannelType.News
+ ? throw new ArgumentException($"{name} channel needs to be a text channel.")
+ : c.Id);
+
+ var rulesChannelId = ChannelToId(mdl.RulesChannel, "Rules");
+ var publicUpdatesChannelId = ChannelToId(mdl.PublicUpdatesChannel, "Public updates");
+ var systemChannelId = ChannelToId(mdl.SystemChannel, "System");
+
+ var iconb64 = ImageTool.Base64FromStream(mdl.Icon);
+ var splashb64 = ImageTool.Base64FromStream(mdl.Splash);
+ var bannerb64 = ImageTool.Base64FromStream(mdl.Banner);
+ var discoverySplash64 = ImageTool.Base64FromStream(mdl.DiscoverySplash);
+
+ return await this.Discord.ApiClient.ModifyGuildAsync(this.Id, mdl.Name,
+ mdl.VerificationLevel, mdl.DefaultMessageNotifications, mdl.MfaLevel, mdl.ExplicitContentFilter,
+ afkChannelId, mdl.AfkTimeout, iconb64, mdl.Owner.Map(e => e.Id), splashb64,
+ systemChannelId, mdl.SystemChannelFlags, publicUpdatesChannelId, rulesChannelId,
+ mdl.Description, bannerb64, discoverySplash64, mdl.PreferredLocale, mdl.PremiumProgressBarEnabled, mdl.AuditLogReason).ConfigureAwait(false);
+ }
- /// <summary>
- /// Creates a new role in this guild.
- /// </summary>
- /// <param name="name">Name of the role.</param>
- /// <param name="permissions">Permissions for the role.</param>
- /// <param name="color">Color for the role.</param>
- /// <param name="hoist">Whether the role is to be hoisted.</param>
- /// <param name="mentionable">Whether the role is to be mentionable.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>The newly-created role.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordRole> CreateRoleAsync(string name = null, Permissions? permissions = null, DiscordColor? color = null, bool? hoist = null, bool? mentionable = null, string reason = null)
- => this.Discord.ApiClient.CreateGuildRoleAsync(this.Id, name, permissions, color?.Value, hoist, mentionable, reason);
+ /// <summary>
+ /// Modifies the community settings async.
+ /// This sets <see cref="VerificationLevel.High"/> if not highest and <see cref="ExplicitContentFilter.AllMembers"/>.
+ /// </summary>
+ /// <param name="enabled">If true, enable <see cref="GuildFeatures.HasCommunityEnabled"/>.</param>
+ /// <param name="rulesChannel">The rules channel.</param>
+ /// <param name="publicUpdatesChannel">The public updates channel.</param>
+ /// <param name="preferredLocale">The preferred locale. Defaults to en-US.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="defaultMessageNotifications">The default message notifications. Defaults to <see cref="DefaultMessageNotifications.MentionsOnly"/></param>
+ /// <param name="reason">The auditlog reason.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordGuild> ModifyCommunitySettingsAsync(bool enabled, DiscordChannel rulesChannel = null, DiscordChannel publicUpdatesChannel = null, string preferredLocale = "en-US", string description = null, DefaultMessageNotifications defaultMessageNotifications = DefaultMessageNotifications.MentionsOnly, string reason = null)
+ {
+ var verificationLevel = this.VerificationLevel;
+ if (this.VerificationLevel != VerificationLevel.Highest)
+ {
+ verificationLevel = VerificationLevel.High;
+ }
- /// <summary>
- /// Gets a role from this guild by its ID.
- /// </summary>
- /// <param name="id">ID of the role to get.</param>
- /// <returns>Requested role.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public DiscordRole GetRole(ulong id)
- => this.RolesInternal.TryGetValue(id, out var role) ? role : null;
+ var explicitContentFilter = ExplicitContentFilter.AllMembers;
- /// <summary>
- /// Gets a channel from this guild by its ID.
- /// </summary>
- /// <param name="id">ID of the channel to get.</param>
- /// <returns>Requested channel.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public DiscordChannel GetChannel(ulong id)
- => this.ChannelsInternal != null && this.ChannelsInternal.TryGetValue(id, out var channel) ? channel : null;
+ static Optional<ulong?> ChannelToId(DiscordChannel ch, string name)
+ => ch == null ? null :
+ ch.Type != ChannelType.Text && ch.Type != ChannelType.News
+ ? throw new ArgumentException($"{name} channel needs to be a text channel.")
+ : ch.Id;
- /// <summary>
- /// Gets a thread from this guild by its ID.
- /// </summary>
- /// <param name="id">ID of the thread to get.</param>
- /// <returns>Requested thread.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public DiscordThreadChannel GetThread(ulong id)
- => this.ThreadsInternal != null && this.ThreadsInternal.TryGetValue(id, out var thread) ? thread : null;
+ var rulesChannelId = ChannelToId(rulesChannel, "Rules");
+ var publicUpdatesChannelId = ChannelToId(publicUpdatesChannel, "Public updates");
- /// <summary>
- /// Gets all of this guild's custom emojis.
- /// </summary>
- /// <returns>All of this guild's custom emojis.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordGuildEmoji>> GetEmojisAsync()
- => this.Discord.ApiClient.GetGuildEmojisAsync(this.Id);
+ List<string> features = new();
+ var rfeatures = this.RawFeatures.ToList();
+ if (this.RawFeatures.Contains("COMMUNITY") && enabled)
+ {
+ features = rfeatures;
+ }
+ else if (!this.RawFeatures.Contains("COMMUNITY") && enabled)
+ {
+ rfeatures.Add("COMMUNITY");
+ features = rfeatures;
+ }
+ else if (this.RawFeatures.Contains("COMMUNITY") && !enabled)
+ {
+ rfeatures.Remove("COMMUNITY");
+ features = rfeatures;
+ }
+ else if (!this.RawFeatures.Contains("COMMUNITY") && !enabled)
+ {
+ features = rfeatures;
+ }
- /// <summary>
- /// Gets this guild's specified custom emoji.
- /// </summary>
- /// <param name="id">ID of the emoji to get.</param>
- /// <returns>The requested custom emoji.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildEmoji> GetEmojiAsync(ulong id)
- => this.Discord.ApiClient.GetGuildEmojiAsync(this.Id, id);
+ return await this.Discord.ApiClient.ModifyGuildCommunitySettingsAsync(this.Id, features, rulesChannelId, publicUpdatesChannelId, preferredLocale, description, defaultMessageNotifications, explicitContentFilter, verificationLevel, reason).ConfigureAwait(false);
+ }
- /// <summary>
- /// Creates a new custom emoji for this guild.
- /// </summary>
- /// <param name="name">Name of the new emoji.</param>
- /// <param name="image">Image to use as the emoji.</param>
- /// <param name="roles">Roles for which the emoji will be available. This works only if your application is whitelisted as integration.</param>
- /// <param name="reason">Reason for audit log.</param>
- /// <returns>The newly-created emoji.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildEmoji> CreateEmojiAsync(string name, Stream image, IEnumerable<DiscordRole> roles = null, string reason = null)
- {
- if (string.IsNullOrWhiteSpace(name))
- throw new ArgumentNullException(nameof(name));
+ /// <summary>
+ /// Timeout a specified member in this guild.
+ /// </summary>
+ /// <param name="memberId">Member to timeout.</param>
+ /// <param name="until">The datetime offset to time out the user. Up to 28 days.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task TimeoutAsync(ulong memberId, DateTimeOffset until, string reason = null)
+ => until.Subtract(DateTimeOffset.UtcNow).Days > 28
+ ? throw new ArgumentException("Timeout can not be longer than 28 days")
+ : this.Discord.ApiClient.ModifyTimeoutAsync(this.Id, memberId, until, reason);
+
+ /// <summary>
+ /// Timeout a specified member in this guild.
+ /// </summary>
+ /// <param name="memberId">Member to timeout.</param>
+ /// <param name="until">The timespan to time out the user. Up to 28 days.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task TimeoutAsync(ulong memberId, TimeSpan until, string reason = null)
+ => this.TimeoutAsync(memberId, DateTimeOffset.UtcNow + until, reason);
+
+ /// <summary>
+ /// Timeout a specified member in this guild.
+ /// </summary>
+ /// <param name="memberId">Member to timeout.</param>
+ /// <param name="until">The datetime to time out the user. Up to 28 days.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task TimeoutAsync(ulong memberId, DateTime until, string reason = null)
+ => this.TimeoutAsync(memberId, until.ToUniversalTime() - DateTime.UtcNow, reason);
+
+ /// <summary>
+ /// Removes the timeout from a specified member in this guild.
+ /// </summary>
+ /// <param name="memberId">Member to remove the timeout from.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task RemoveTimeoutAsync(ulong memberId, string reason = null)
+ => this.Discord.ApiClient.ModifyTimeoutAsync(this.Id, memberId, null, reason);
+
+ /// <summary>
+ /// Bans a specified member from this guild.
+ /// </summary>
+ /// <param name="member">Member to ban.</param>
+ /// <param name="deleteMessageDays">How many days to remove messages from.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task BanMemberAsync(DiscordMember member, int deleteMessageDays = 0, string reason = null)
+ => this.Discord.ApiClient.CreateGuildBanAsync(this.Id, member.Id, deleteMessageDays, reason);
+
+ /// <summary>
+ /// Bans a specified user by ID. This doesn't require the user to be in this guild.
+ /// </summary>
+ /// <param name="userId">ID of the user to ban.</param>
+ /// <param name="deleteMessageDays">How many days to remove messages from.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task BanMemberAsync(ulong userId, int deleteMessageDays = 0, string reason = null)
+ => this.Discord.ApiClient.CreateGuildBanAsync(this.Id, userId, deleteMessageDays, reason);
+
+ /// <summary>
+ /// Unbans a user from this guild.
+ /// </summary>
+ /// <param name="user">User to unban.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the user does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task UnbanMemberAsync(DiscordUser user, string reason = null)
+ => this.Discord.ApiClient.RemoveGuildBanAsync(this.Id, user.Id, reason);
+
+ /// <summary>
+ /// Unbans a user by ID.
+ /// </summary>
+ /// <param name="userId">ID of the user to unban.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the user does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task UnbanMemberAsync(ulong userId, string reason = null)
+ => this.Discord.ApiClient.RemoveGuildBanAsync(this.Id, userId, reason);
+
+ /// <summary>
+ /// Leaves this guild.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task LeaveAsync()
+ => this.Discord.ApiClient.LeaveGuildAsync(this.Id);
+
+ /// <summary>
+ /// Gets the bans for this guild, allowing for pagination.
+ /// </summary>
+ /// <param name="limit">Maximum number of bans to fetch. Max 1000. Defaults to 1000.</param>
+ /// <param name="before">The Id of the user before which to fetch the bans. Overrides <paramref name="after"/> if both are present.</param>
+ /// <param name="after">The Id of the user after which to fetch the bans.</param>
+ /// <returns>Collection of bans in this guild in ascending order by user id.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordBan>> GetBansAsync(int? limit = null, ulong? before = null, ulong? after = null)
+ => this.Discord.ApiClient.GetGuildBansAsync(this.Id, limit, before, after);
+
+ /// <summary>
+ /// Gets a ban for a specific user.
+ /// </summary>
+ /// <param name="userId">The Id of the user to get the ban for.</param>
+ /// <returns>The requested ban object.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the specified user is not banned.</exception>
+ public Task<DiscordBan> GetBanAsync(ulong userId)
+ => this.Discord.ApiClient.GetGuildBanAsync(this.Id, userId);
+
+ /// <summary>
+ /// Gets a ban for a specific user.
+ /// </summary>
+ /// <param name="user">The user to get the ban for.</param>
+ /// <returns>The requested ban object.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the specified user is not banned.</exception>
+ public Task<DiscordBan> GetBanAsync(DiscordUser user)
+ => this.GetBanAsync(user.Id);
+
+ #region Sheduled Events
+
+ /// <summary>
+ /// Creates a scheduled event.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="scheduledStartTime">The scheduled start time.</param>
+ /// <param name="scheduledEndTime">The scheduled end time.</param>
+ /// <param name="channel">The channel.</param>
+ /// <param name="metadata">The metadata.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="coverImage">The cover image.</param>
+ /// <param name="reason">The reason.</param>
+ /// <returns>A scheduled event.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordScheduledEvent> CreateScheduledEventAsync(string name, DateTimeOffset scheduledStartTime, DateTimeOffset? scheduledEndTime = null, DiscordChannel channel = null, DiscordScheduledEventEntityMetadata metadata = null, string description = null, ScheduledEventEntityType type = ScheduledEventEntityType.StageInstance, Optional<Stream> coverImage = default, string reason = null)
+ {
+ var coverb64 = ImageTool.Base64FromStream(coverImage);
+ return await this.Discord.ApiClient.CreateGuildScheduledEventAsync(this.Id, type == ScheduledEventEntityType.External ? null : channel?.Id, type == ScheduledEventEntityType.External ? metadata : null, name, scheduledStartTime, scheduledEndTime.HasValue && type == ScheduledEventEntityType.External ? scheduledEndTime.Value : null, description, type, coverb64, reason);
+ }
- name = name.Trim();
- if (name.Length < 2 || name.Length > 50)
- throw new ArgumentException("Emoji name needs to be between 2 and 50 characters long.");
+ /// <summary>
+ /// Creates a scheduled event with type <see cref="ScheduledEventEntityType.External"/>.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="scheduledStartTime">The scheduled start time.</param>
+ /// <param name="scheduledEndTime">The scheduled end time.</param>
+ /// <param name="location">The location of the external event.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="coverImage">The cover image.</param>
+ /// <param name="reason">The reason.</param>
+ /// <returns>A scheduled event.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordScheduledEvent> CreateExternalScheduledEventAsync(string name, DateTimeOffset scheduledStartTime, DateTimeOffset scheduledEndTime, string location, string description = null, Optional<Stream> coverImage = default, string reason = null)
+ {
+ var coverb64 = ImageTool.Base64FromStream(coverImage);
+ return await this.Discord.ApiClient.CreateGuildScheduledEventAsync(this.Id, null, new DiscordScheduledEventEntityMetadata(location), name, scheduledStartTime, scheduledEndTime, description, ScheduledEventEntityType.External, coverb64, reason);
+ }
- if (image == null)
- throw new ArgumentNullException(nameof(image));
- var image64 = ImageTool.Base64FromStream(image);
+ /// <summary>
+ /// Gets a specific scheduled events.
+ /// </summary>
+ /// <param name="scheduledEventId">The Id of the event to get.</param>
+ /// <param name="withUserCount">Whether to include user count.</param>
+ /// <returns>A scheduled event.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordScheduledEvent> GetScheduledEventAsync(ulong scheduledEventId, bool? withUserCount = null)
+ => this.ScheduledEventsInternal.TryGetValue(scheduledEventId, out var ev) ? ev : await this.Discord.ApiClient.GetGuildScheduledEventAsync(this.Id, scheduledEventId, withUserCount);
+
+ /// <summary>
+ /// Gets a specific scheduled events.
+ /// </summary>
+ /// <param name="scheduledEvent">The event to get.</param>
+ /// <param name="withUserCount">Whether to include user count.</param>
+ /// <returns>A sheduled event.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordScheduledEvent> GetScheduledEventAsync(DiscordScheduledEvent scheduledEvent, bool? withUserCount = null)
+ => await this.GetScheduledEventAsync(scheduledEvent.Id, withUserCount);
+
+ /// <summary>
+ /// Gets the guilds scheduled events.
+ /// </summary>
+ /// <param name="withUserCount">Whether to include user count.</param>
+ /// <returns>A list of the guilds scheduled events.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<IReadOnlyDictionary<ulong, DiscordScheduledEvent>> GetScheduledEventsAsync(bool? withUserCount = null)
+ => await this.Discord.ApiClient.ListGuildScheduledEventsAsync(this.Id, withUserCount);
+ #endregion
+
+ /// <summary>
+ /// Creates a new text channel in this guild.
+ /// </summary>
+ /// <param name="name">Name of the new channel.</param>
+ /// <param name="parent">Category to put this channel in.</param>
+ /// <param name="topic">Topic of the channel.</param>
+ /// <param name="overwrites">Permission overwrites for this channel.</param>
+ /// <param name="nsfw">Whether the channel is to be flagged as not safe for work.</param>
+ /// <param name="perUserRateLimit">Slow mode timeout for users.</param>
+ /// <param name="defaultAutoArchiveDuration">The default auto archive duration for new threads.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>The newly-created channel.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordChannel> CreateTextChannelAsync(string name, DiscordChannel parent = null, Optional<string> topic = default, IEnumerable<DiscordOverwriteBuilder> overwrites = null, bool? nsfw = null, Optional<int?> perUserRateLimit = default, ThreadAutoArchiveDuration defaultAutoArchiveDuration = ThreadAutoArchiveDuration.OneDay, string reason = null)
+ => this.CreateChannelAsync(name, ChannelType.Text, parent, topic, null, null, overwrites, nsfw, perUserRateLimit, null, defaultAutoArchiveDuration, reason);
+
+ /// <summary>
+ /// Creates a new channel category in this guild.
+ /// </summary>
+ /// <param name="name">Name of the new category.</param>
+ /// <param name="overwrites">Permission overwrites for this category.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>The newly-created channel category.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordChannel> CreateChannelCategoryAsync(string name, IEnumerable<DiscordOverwriteBuilder> overwrites = null, string reason = null)
+ => this.CreateChannelAsync(name, ChannelType.Category, null, Optional.None, null, null, overwrites, null, Optional.None, null, null, reason);
+
+ /// <summary>
+ /// Creates a new stage channel in this guild.
+ /// </summary>
+ /// <param name="name">Name of the new stage channel.</param>
+ /// <param name="overwrites">Permission overwrites for this stage channel.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>The newly-created stage channel.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/>.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.NotSupportedException">Thrown when the guilds has not enabled community.</exception>
+ public Task<DiscordChannel> CreateStageChannelAsync(string name, IEnumerable<DiscordOverwriteBuilder> overwrites = null, string reason = null)
+ => this.Features.HasCommunityEnabled ? this.CreateChannelAsync(name, ChannelType.Stage, null, Optional.None, null, null, overwrites, null, Optional.None, null, null, reason) : throw new NotSupportedException("Guild has not enabled community. Can not create a stage channel.");
+
+ /// <summary>
+ /// Creates a new news channel in this guild.
+ /// </summary>
+ /// <param name="name">Name of the new news channel.</param>
+ /// <param name="overwrites">Permission overwrites for this news channel.</param>
+ /// <param name="defaultAutoArchiveDuration">The default auto archive duration for new threads.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>The newly-created news channel.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/>.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.NotSupportedException">Thrown when the guilds has not enabled community.</exception>
+ public Task<DiscordChannel> CreateNewsChannelAsync(string name, IEnumerable<DiscordOverwriteBuilder> overwrites = null, string reason = null, ThreadAutoArchiveDuration defaultAutoArchiveDuration = ThreadAutoArchiveDuration.OneDay)
+ => this.Features.HasCommunityEnabled ? this.CreateChannelAsync(name, ChannelType.News, null, Optional.None, null, null, overwrites, null, Optional.None, null, defaultAutoArchiveDuration, reason) : throw new NotSupportedException("Guild has not enabled community. Can not create a news channel.");
+
+ /// <summary>
+ /// Creates a new voice channel in this guild.
+ /// </summary>
+ /// <param name="name">Name of the new channel.</param>
+ /// <param name="parent">Category to put this channel in.</param>
+ /// <param name="bitrate">Bitrate of the channel.</param>
+ /// <param name="userLimit">Maximum number of users in the channel.</param>
+ /// <param name="overwrites">Permission overwrites for this channel.</param>
+ /// <param name="qualityMode">Video quality mode of the channel.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>The newly-created channel.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordChannel> CreateVoiceChannelAsync(string name, DiscordChannel parent = null, int? bitrate = null, int? userLimit = null, IEnumerable<DiscordOverwriteBuilder> overwrites = null, VideoQualityMode? qualityMode = null, string reason = null)
+ => this.CreateChannelAsync(name, ChannelType.Voice, parent, Optional.None, bitrate, userLimit, overwrites, null, Optional.None, qualityMode, null, reason);
+
+ /// <summary>
+ /// Creates a new channel in this guild.
+ /// </summary>
+ /// <param name="name">Name of the new channel.</param>
+ /// <param name="type">Type of the new channel.</param>
+ /// <param name="parent">Category to put this channel in.</param>
+ /// <param name="topic">Topic of the channel.</param>
+ /// <param name="bitrate">Bitrate of the channel. Applies to voice only.</param>
+ /// <param name="userLimit">Maximum number of users in the channel. Applies to voice only.</param>
+ /// <param name="overwrites">Permission overwrites for this channel.</param>
+ /// <param name="nsfw">Whether the channel is to be flagged as not safe for work. Applies to text only.</param>
+ /// <param name="perUserRateLimit">Slow mode timeout for users.</param>
+ /// <param name="qualityMode">Video quality mode of the channel. Applies to voice only.</param>
+ /// <param name="defaultAutoArchiveDuration">The default auto archive duration for new threads.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>The newly-created channel.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordChannel> CreateChannelAsync(string name, ChannelType type, DiscordChannel parent = null, Optional<string> topic = default, int? bitrate = null, int? userLimit = null, IEnumerable<DiscordOverwriteBuilder> overwrites = null, bool? nsfw = null, Optional<int?> perUserRateLimit = default, VideoQualityMode? qualityMode = null, ThreadAutoArchiveDuration? defaultAutoArchiveDuration = null, string reason = null) =>
+ // technically you can create news/store channels but not always
+ type != ChannelType.Text && type != ChannelType.Voice && type != ChannelType.Category && type != ChannelType.News && type != ChannelType.Store && type != ChannelType.Stage
+ ? throw new ArgumentException("Channel type must be text, voice, stage, or category.", nameof(type))
+ : type == ChannelType.Category && parent != null
+ ? throw new ArgumentException("Cannot specify parent of a channel category.", nameof(parent))
+ : this.Discord.ApiClient.CreateGuildChannelAsync(this.Id, name, type, parent?.Id, topic, bitrate, userLimit, overwrites, nsfw, perUserRateLimit, qualityMode, defaultAutoArchiveDuration, reason);
+
+ /// <summary>
+ /// Gets active threads. Can contain more threads.
+ /// If the result's value 'HasMore' is true, you need to recall this function to get older threads.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordThreadResult> GetActiveThreadsAsync()
+ => this.Discord.ApiClient.GetActiveThreadsAsync(this.Id);
+
+ /// <summary>
+ /// <para>Deletes all channels in this guild.</para>
+ /// <para>Note that this is irreversible. Use carefully!</para>
+ /// </summary>
+ /// <returns></returns>
+ public Task DeleteAllChannelsAsync()
+ {
+ var tasks = this.Channels.Values.Select(xc => xc.DeleteAsync());
+ return Task.WhenAll(tasks);
+ }
- return this.Discord.ApiClient.CreateGuildEmojiAsync(this.Id, name, image64, roles?.Select(xr => xr.Id), reason);
- }
+ /// <summary>
+ /// Estimates the number of users to be pruned.
+ /// </summary>
+ /// <param name="days">Minimum number of inactivity days required for users to be pruned. Defaults to 7.</param>
+ /// <param name="includedRoles">The roles to be included in the prune.</param>
+ /// <returns>Number of users that will be pruned.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.KickMembers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<int> GetPruneCountAsync(int days = 7, IEnumerable<DiscordRole> includedRoles = null)
+ {
+ if (includedRoles != null)
+ {
+ includedRoles = includedRoles.Where(r => r != null);
+ var roleCount = includedRoles.Count();
+ var roleArr = includedRoles.ToArray();
+ var rawRoleIds = new List<ulong>();
- /// <summary>
- /// Modifies a this guild's custom emoji.
- /// </summary>
- /// <param name="emoji">Emoji to modify.</param>
- /// <param name="name">New name for the emoji.</param>
- /// <param name="roles">Roles for which the emoji will be available. This works only if your application is whitelisted as integration.</param>
- /// <param name="reason">Reason for audit log.</param>
- /// <returns>The modified emoji.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildEmoji> ModifyEmojiAsync(DiscordGuildEmoji emoji, string name, IEnumerable<DiscordRole> roles = null, string reason = null)
- {
- if (emoji == null)
- throw new ArgumentNullException(nameof(emoji));
+ for (var i = 0; i < roleCount; i++)
+ {
+ if (this.RolesInternal.ContainsKey(roleArr[i].Id))
+ rawRoleIds.Add(roleArr[i].Id);
+ }
- if (emoji.Guild.Id != this.Id)
- throw new ArgumentException("This emoji does not belong to this guild.");
+ return this.Discord.ApiClient.GetGuildPruneCountAsync(this.Id, days, rawRoleIds);
+ }
- if (string.IsNullOrWhiteSpace(name))
- throw new ArgumentNullException(nameof(name));
+ return this.Discord.ApiClient.GetGuildPruneCountAsync(this.Id, days, null);
+ }
- name = name.Trim();
- return name.Length < 2 || name.Length > 50
- ? throw new ArgumentException("Emoji name needs to be between 2 and 50 characters long.")
- : this.Discord.ApiClient.ModifyGuildEmojiAsync(this.Id, emoji.Id, name, roles?.Select(xr => xr.Id), reason);
- }
+ /// <summary>
+ /// Prunes inactive users from this guild.
+ /// </summary>
+ /// <param name="days">Minimum number of inactivity days required for users to be pruned. Defaults to 7.</param>
+ /// <param name="computePruneCount">Whether to return the prune count after this method completes. This is discouraged for larger guilds.</param>
+ /// <param name="includedRoles">The roles to be included in the prune.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>Number of users pruned.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<int?> PruneAsync(int days = 7, bool computePruneCount = true, IEnumerable<DiscordRole> includedRoles = null, string reason = null)
+ {
+ if (includedRoles != null)
+ {
+ includedRoles = includedRoles.Where(r => r != null);
+ var roleCount = includedRoles.Count();
+ var roleArr = includedRoles.ToArray();
+ var rawRoleIds = new List<ulong>();
- /// <summary>
- /// Deletes this guild's custom emoji.
- /// </summary>
- /// <param name="emoji">Emoji to delete.</param>
- /// <param name="reason">Reason for audit log.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteEmojiAsync(DiscordGuildEmoji emoji, string reason = null) =>
- emoji == null
- ? throw new ArgumentNullException(nameof(emoji))
- : emoji.Guild.Id != this.Id
- ? throw new ArgumentException("This emoji does not belong to this guild.")
- : this.Discord.ApiClient.DeleteGuildEmojiAsync(this.Id, emoji.Id, reason);
+ for (var i = 0; i < roleCount; i++)
+ {
+ if (this.RolesInternal.ContainsKey(roleArr[i].Id))
+ rawRoleIds.Add(roleArr[i].Id);
+ }
- /// <summary>
- /// Gets all of this guild's custom stickers.
- /// </summary>
- /// <returns>All of this guild's custom stickers.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<IReadOnlyList<DiscordSticker>> GetStickersAsync()
- {
- var stickers = await this.Discord.ApiClient.GetGuildStickersAsync(this.Id);
+ return this.Discord.ApiClient.BeginGuildPruneAsync(this.Id, days, computePruneCount, rawRoleIds, reason);
+ }
- foreach (var xstr in stickers)
- {
- this.StickersInternal.AddOrUpdate(xstr.Id, xstr, (id, old) =>
- {
- old.Name = xstr.Name;
- old.Description = xstr.Description;
- old.InternalTags = xstr.InternalTags;
- return old;
- });
+ return this.Discord.ApiClient.BeginGuildPruneAsync(this.Id, days, computePruneCount, null, reason);
}
- return stickers;
- }
+ /// <summary>
+ /// Gets integrations attached to this guild.
+ /// </summary>
+ /// <returns>Collection of integrations attached to this guild.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordIntegration>> GetIntegrationsAsync()
+ => this.Discord.ApiClient.GetGuildIntegrationsAsync(this.Id);
+
+ /// <summary>
+ /// Attaches an integration from current user to this guild.
+ /// </summary>
+ /// <param name="integration">Integration to attach.</param>
+ /// <returns>The integration after being attached to the guild.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordIntegration> AttachUserIntegrationAsync(DiscordIntegration integration)
+ => this.Discord.ApiClient.CreateGuildIntegrationAsync(this.Id, integration.Type, integration.Id);
+
+ /// <summary>
+ /// Modifies an integration in this guild.
+ /// </summary>
+ /// <param name="integration">Integration to modify.</param>
+ /// <param name="expireBehaviour">Number of days after which the integration expires.</param>
+ /// <param name="expireGracePeriod">Length of grace period which allows for renewing the integration.</param>
+ /// <param name="enableEmoticons">Whether emotes should be synced from this integration.</param>
+ /// <returns>The modified integration.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordIntegration> ModifyIntegrationAsync(DiscordIntegration integration, int expireBehaviour, int expireGracePeriod, bool enableEmoticons)
+ => this.Discord.ApiClient.ModifyGuildIntegrationAsync(this.Id, integration.Id, expireBehaviour, expireGracePeriod, enableEmoticons);
+
+ /// <summary>
+ /// Removes an integration from this guild.
+ /// </summary>
+ /// <param name="integration">Integration to remove.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteIntegrationAsync(DiscordIntegration integration)
+ => this.Discord.ApiClient.DeleteGuildIntegrationAsync(this.Id, integration);
+
+ /// <summary>
+ /// Forces re-synchronization of an integration for this guild.
+ /// </summary>
+ /// <param name="integration">Integration to synchronize.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the guild does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task SyncIntegrationAsync(DiscordIntegration integration)
+ => this.Discord.ApiClient.SyncGuildIntegrationAsync(this.Id, integration.Id);
+
+ /// <summary>
+ /// Gets the voice regions for this guild.
+ /// </summary>
+ /// <returns>Voice regions available for this guild.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<IReadOnlyList<DiscordVoiceRegion>> ListVoiceRegionsAsync()
+ {
+ var vrs = await this.Discord.ApiClient.GetGuildVoiceRegionsAsync(this.Id).ConfigureAwait(false);
+ foreach (var xvr in vrs)
+ this.Discord.InternalVoiceRegions.TryAdd(xvr.Id, xvr);
- /// <summary>
- /// Gets a sticker
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
- public Task<DiscordSticker> GetStickerAsync(ulong stickerId)
- => this.Discord.ApiClient.GetGuildStickerAsync(this.Id, stickerId);
+ return vrs;
+ }
- /// <summary>
- /// Creates a sticker
- /// </summary>
- /// <param name="name">The name of the sticker.</param>
- /// <param name="description">The optional description of the sticker.</param>
- /// <param name="emoji">The emoji to associate the sticker with.</param>
- /// <param name="format">The file format the sticker is written in.</param>
- /// <param name="file">The sticker.</param>
- /// <param name="reason">Audit log reason</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordSticker> CreateStickerAsync(string name, string description, DiscordEmoji emoji, Stream file, StickerFormat format, string reason = null)
- {
- var fileExt = format switch
+ /// <summary>
+ /// Gets an invite from this guild from an invite code.
+ /// </summary>
+ /// <param name="code">The invite code</param>
+ /// <returns>An invite, or null if not in cache.</returns>
+ public DiscordInvite GetInvite(string code)
+ => this.Invites.TryGetValue(code, out var invite) ? invite : null;
+
+ /// <summary>
+ /// Gets all the invites created for all the channels in this guild.
+ /// </summary>
+ /// <returns>A collection of invites.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<IReadOnlyList<DiscordInvite>> GetInvitesAsync()
{
- StickerFormat.Png => "png",
- StickerFormat.Apng => "png",
- StickerFormat.Lottie => "json",
- _ => throw new InvalidOperationException("This format is not supported.")
- };
+ var res = await this.Discord.ApiClient.GetGuildInvitesAsync(this.Id).ConfigureAwait(false);
- var contentType = format switch
- {
- StickerFormat.Png => "image/png",
- StickerFormat.Apng => "image/png",
- StickerFormat.Lottie => "application/json",
- _ => throw new InvalidOperationException("This format is not supported.")
- };
-
- return emoji.Id is not 0
- ? throw new InvalidOperationException("Only unicode emoji can be used for stickers.")
- : name.Length < 2 || name.Length > 30
- ? throw new ArgumentOutOfRangeException(nameof(name), "Sticker name needs to be between 2 and 30 characters long.")
- : description.Length < 1 || description.Length > 100
- ? throw new ArgumentOutOfRangeException(nameof(description), "Sticker description needs to be between 1 and 100 characters long.")
- : this.Discord.ApiClient.CreateGuildStickerAsync(this.Id, name, description, emoji.GetDiscordName().Replace(":", ""), new DiscordMessageFile("sticker", file, null, fileExt, contentType), reason);
- }
+ var intents = this.Discord.Configuration.Intents;
- /// <summary>
- /// Modifies a sticker
- /// </summary>
- /// <param name="sticker">The id of the sticker to modify</param>
- /// <param name="name">The name of the sticker</param>
- /// <param name="description">The description of the sticker</param>
- /// <param name="emoji">The emoji to associate with this sticker.</param>
- /// <param name="reason">Audit log reason</param>
- /// <returns>A sticker object</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
- public async Task<DiscordSticker> ModifyStickerAsync(ulong sticker, Optional<string> name, Optional<string> description, Optional<DiscordEmoji> emoji, string reason = null)
- {
- if (!this.StickersInternal.TryGetValue(sticker, out var stickerobj) || stickerobj.Guild.Id != this.Id)
- throw new ArgumentException("This sticker does not belong to this guild.");
- if (name.HasValue && (name.Value.Length < 2 || name.Value.Length > 30))
- throw new ArgumentException("Sticker name needs to be between 2 and 30 characters long.");
- if (description.HasValue && (description.Value.Length < 1 || description.Value.Length > 100))
- throw new ArgumentException("Sticker description needs to be between 1 and 100 characters long.");
- if (emoji.HasValue && emoji.Value.Id > 0)
- throw new ArgumentException("Only unicode emojis can be used with stickers.");
+ if (!intents.HasIntent(DiscordIntents.GuildInvites))
+ {
+ for (var i = 0; i < res.Count; i++)
+ this.Invites[res[i].Code] = res[i];
+ }
- string uemoji = null;
- if (emoji.HasValue)
- uemoji = emoji.Value.GetDiscordName().Replace(":", "");
+ return res;
+ }
- var usticker = await this.Discord.ApiClient.ModifyGuildStickerAsync(this.Id, sticker, name, description, uemoji, reason).ConfigureAwait(false);
+ /// <summary>
+ /// Gets the vanity invite for this guild.
+ /// </summary>
+ /// <returns>A partial vanity invite.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordInvite> GetVanityInviteAsync()
+ => this.Discord.ApiClient.GetGuildVanityUrlAsync(this.Id);
+
+ /// <summary>
+ /// Gets all the webhooks created for all the channels in this guild.
+ /// </summary>
+ /// <returns>A collection of webhooks this guild has.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordWebhook>> GetWebhooksAsync()
+ => this.Discord.ApiClient.GetGuildWebhooksAsync(this.Id);
+
+ /// <summary>
+ /// Gets this guild's widget image.
+ /// </summary>
+ /// <param name="bannerType">The format of the widget.</param>
+ /// <returns>The URL of the widget image.</returns>
+ public string GetWidgetImage(WidgetType bannerType = WidgetType.Shield)
+ {
+ var param = bannerType switch
+ {
+ WidgetType.Banner1 => "banner1",
+ WidgetType.Banner2 => "banner2",
+ WidgetType.Banner3 => "banner3",
+ WidgetType.Banner4 => "banner4",
+ _ => "shield",
+ };
+ return $"{Endpoints.BASE_URI}{Endpoints.GUILDS}/{this.Id}{Endpoints.WIDGET_PNG}?style={param}";
+ }
+ /// <summary>
+ /// Gets a member of this guild by their user ID.
+ /// </summary>
+ /// <param name="userId">ID of the member to get.</param>
+ /// <returns>The requested member.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMember> GetMemberAsync(ulong userId)
+ {
+ if (this.MembersInternal != null && this.MembersInternal.TryGetValue(userId, out var mbr))
+ return mbr;
- if (this.StickersInternal.TryGetValue(usticker.Id, out var old))
- this.StickersInternal.TryUpdate(usticker.Id, usticker, old);
+ mbr = await this.Discord.ApiClient.GetGuildMemberAsync(this.Id, userId).ConfigureAwait(false);
- return usticker;
- }
+ var intents = this.Discord.Configuration.Intents;
- /// <summary>
- /// Modifies a sticker
- /// </summary>
- /// <param name="sticker">The sticker to modify</param>
- /// <param name="name">The name of the sticker</param>
- /// <param name="description">The description of the sticker</param>
- /// <param name="emoji">The emoji to associate with this sticker.</param>
- /// <param name="reason">Audit log reason</param>
- /// <returns>A sticker object</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
- public Task<DiscordSticker> ModifyStickerAsync(DiscordSticker sticker, Optional<string> name, Optional<string> description, Optional<DiscordEmoji> emoji, string reason = null)
- => this.ModifyStickerAsync(sticker.Id, name, description, emoji, reason);
+ if (intents.HasIntent(DiscordIntents.GuildMembers))
+ {
+ if (this.MembersInternal != null)
+ {
+ this.MembersInternal[userId] = mbr;
+ }
+ }
- /// <summary>
- /// Deletes a sticker
- /// </summary>
- /// <param name="sticker">Id of sticker to delete</param>
- /// <param name="reason">Audit log reason</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
- public Task DeleteStickerAsync(ulong sticker, string reason = null) =>
- !this.StickersInternal.TryGetValue(sticker, out var stickerobj)
- ? throw new ArgumentNullException(nameof(sticker))
- : stickerobj.Guild.Id != this.Id
- ? throw new ArgumentException("This sticker does not belong to this guild.")
- : this.Discord.ApiClient.DeleteGuildStickerAsync(this.Id, sticker, reason);
+ return mbr;
+ }
- /// <summary>
- /// Deletes a sticker
- /// </summary>
- /// <param name="sticker">Sticker to delete</param>
- /// <param name="reason">Audit log reason</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
- public Task DeleteStickerAsync(DiscordSticker sticker, string reason = null)
- => this.DeleteStickerAsync(sticker.Id, reason);
+ /// <summary>
+ /// Retrieves a full list of members from Discord. This method will bypass cache.
+ /// </summary>
+ /// <returns>A collection of all members in this guild.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<IReadOnlyCollection<DiscordMember>> GetAllMembersAsync()
+ {
+ var recmbr = new HashSet<DiscordMember>();
- /// <summary>
- /// <para>Gets the default channel for this guild.</para>
- /// <para>Default channel is the first channel current member can see.</para>
- /// </summary>
- /// <returns>This member's default guild.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public DiscordChannel GetDefaultChannel() =>
- this.ChannelsInternal?.Values.Where(xc => xc.Type == ChannelType.Text)
- .OrderBy(xc => xc.Position)
- .FirstOrDefault(xc => (xc.PermissionsFor(this.CurrentMember) & DisCatSharp.Permissions.AccessChannels) == DisCatSharp.Permissions.AccessChannels);
+ var recd = 1000;
+ var last = 0ul;
+ while (recd > 0)
+ {
+ var tms = await this.Discord.ApiClient.ListGuildMembersAsync(this.Id, 1000, last == 0 ? null : (ulong?)last).ConfigureAwait(false);
+ recd = tms.Count;
- /// <summary>
- /// Gets the guild's widget
- /// </summary>
- /// <returns>The guild's widget</returns>
- public Task<DiscordWidget> GetWidgetAsync()
- => this.Discord.ApiClient.GetGuildWidgetAsync(this.Id);
+ foreach (var xtm in tms)
+ {
+ var usr = new DiscordUser(xtm.User) { Discord = this.Discord };
- /// <summary>
- /// Gets the guild's widget settings
- /// </summary>
- /// <returns>The guild's widget settings</returns>
- public Task<DiscordWidgetSettings> GetWidgetSettingsAsync()
- => this.Discord.ApiClient.GetGuildWidgetSettingsAsync(this.Id);
+ usr = this.Discord.UserCache.AddOrUpdate(xtm.User.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discord = usr.Discord;
+ old.AvatarHash = usr.AvatarHash;
- /// <summary>
- /// Modifies the guild's widget settings
- /// </summary>
- /// <param name="isEnabled">If the widget is enabled or not</param>
- /// <param name="channel">Widget channel</param>
- /// <param name="reason">Reason the widget settings were modified</param>
- /// <returns>The newly modified widget settings</returns>
- public Task<DiscordWidgetSettings> ModifyWidgetSettingsAsync(bool? isEnabled = null, DiscordChannel channel = null, string reason = null)
- => this.Discord.ApiClient.ModifyGuildWidgetSettingsAsync(this.Id, isEnabled, channel?.Id, reason);
+ return old;
+ });
- /// <summary>
- /// Gets all of this guild's templates.
- /// </summary>
- /// <returns>All of the guild's templates.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordGuildTemplate>> GetTemplatesAsync()
- => this.Discord.ApiClient.GetGuildTemplatesAsync(this.Id);
+ recmbr.Add(new DiscordMember(xtm) { Discord = this.Discord, GuildId = this.Id });
+ }
- /// <summary>
- /// Creates a guild template.
- /// </summary>
- /// <param name="name">Name of the template.</param>
- /// <param name="description">Description of the template.</param>
- /// <returns>The template created.</returns>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Throws when a template already exists for the guild or a null parameter is provided for the name.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildTemplate> CreateTemplateAsync(string name, string description = null)
- => this.Discord.ApiClient.CreateGuildTemplateAsync(this.Id, name, description);
+ var tm = tms.LastOrDefault();
+ last = tm?.User.Id ?? 0;
+ }
- /// <summary>
- /// Syncs the template to the current guild's state.
- /// </summary>
- /// <param name="code">The code of the template to sync.</param>
- /// <returns>The template synced.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Throws when the template for the code cannot be found</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildTemplate> SyncTemplateAsync(string code)
- => this.Discord.ApiClient.SyncGuildTemplateAsync(this.Id, code);
+ return new ReadOnlySet<DiscordMember>(recmbr);
+ }
- /// <summary>
- /// Modifies the template's metadata.
- /// </summary>
- /// <param name="code">The template's code.</param>
- /// <param name="name">Name of the template.</param>
- /// <param name="description">Description of the template.</param>
- /// <returns>The template modified.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Throws when the template for the code cannot be found</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildTemplate> ModifyTemplateAsync(string code, string name = null, string description = null)
- => this.Discord.ApiClient.ModifyGuildTemplateAsync(this.Id, code, name, description);
+ /// <summary>
+ /// Requests that Discord send a list of guild members based on the specified arguments. This method will fire the <see cref="DiscordClient.GuildMembersChunked"/> event.
+ /// <para>If no arguments aside from <paramref name="presences"/> and <paramref name="nonce"/> are specified, this will request all guild members.</para>
+ /// </summary>
+ /// <param name="query">Filters the returned members based on what the username starts with. Either this or <paramref name="userIds"/> must not be null.
+ /// The <paramref name="limit"/> must also be greater than 0 if this is specified.</param>
+ /// <param name="limit">Total number of members to request. This must be greater than 0 if <paramref name="query"/> is specified.</param>
+ /// <param name="presences">Whether to include the <see cref="DisCatSharp.EventArgs.GuildMembersChunkEventArgs.Presences"/> associated with the fetched members.</param>
+ /// <param name="userIds">Whether to limit the request to the specified user ids. Either this or <paramref name="query"/> must not be null.</param>
+ /// <param name="nonce">The unique string to identify the response.</param>
+ public async Task RequestMembersAsync(string query = "", int limit = 0, bool? presences = null, IEnumerable<ulong> userIds = null, string nonce = null)
+ {
+ if (this.Discord is not DiscordClient client)
+ throw new InvalidOperationException("This operation is only valid for regular Discord clients.");
- /// <summary>
- /// Deletes the template.
- /// </summary>
- /// <param name="code">The code of the template to delete.</param>
- /// <returns>The deleted template.</returns>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Throws when the template for the code cannot be found</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildTemplate> DeleteTemplateAsync(string code)
- => this.Discord.ApiClient.DeleteGuildTemplateAsync(this.Id, code);
+ if (query == null && userIds == null)
+ throw new ArgumentException("The query and user IDs cannot both be null.");
- /// <summary>
- /// Gets this guild's membership screening form.
- /// </summary>
- /// <returns>This guild's membership screening form.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildMembershipScreening> GetMembershipScreeningFormAsync()
- => this.Discord.ApiClient.GetGuildMembershipScreeningFormAsync(this.Id);
+ if (query != null && userIds != null)
+ query = null;
- /// <summary>
- /// Modifies this guild's membership screening form.
- /// </summary>
- /// <param name="action">Action to perform</param>
- /// <returns>The modified screening form.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client doesn't have the <see cref="Permissions.ManageGuild"/> permission, or community is not enabled on this guild.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordGuildMembershipScreening> ModifyMembershipScreeningFormAsync(Action<MembershipScreeningEditModel> action)
- {
- var mdl = new MembershipScreeningEditModel();
- action(mdl);
- return await this.Discord.ApiClient.ModifyGuildMembershipScreeningFormAsync(this.Id, mdl.Enabled, mdl.Fields, mdl.Description);
- }
+ var grgm = new GatewayRequestGuildMembers(this)
+ {
+ Query = query,
+ Limit = limit >= 0 ? limit : 0,
+ Presences = presences,
+ UserIds = userIds,
+ Nonce = nonce
+ };
+
+ var payload = new GatewayPayload
+ {
+ OpCode = GatewayOpCode.RequestGuildMembers,
+ Data = grgm
+ };
- /// <summary>
- /// Gets all the application commands in this guild.
- /// </summary>
- /// <returns>A list of application commands in this guild.</returns>
- public Task<IReadOnlyList<DiscordApplicationCommand>> GetApplicationCommandsAsync() =>
- this.Discord.ApiClient.GetGuildApplicationCommandsAsync(this.Discord.CurrentApplication.Id, this.Id);
+ var payloadStr = JsonConvert.SerializeObject(payload, Formatting.None);
+ await client.WsSendAsync(payloadStr).ConfigureAwait(false);
+ }
- /// <summary>
- /// Overwrites the existing application commands in this guild. New commands are automatically created and missing commands are automatically delete
- /// </summary>
- /// <param name="commands">The list of commands to overwrite with.</param>
- /// <returns>The list of guild commands</returns>
- public Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteApplicationCommandsAsync(IEnumerable<DiscordApplicationCommand> commands) =>
- this.Discord.ApiClient.BulkOverwriteGuildApplicationCommandsAsync(this.Discord.CurrentApplication.Id, this.Id, commands);
+ /// <summary>
+ /// Gets all the channels this guild has.
+ /// </summary>
+ /// <returns>A collection of this guild's channels.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordChannel>> GetChannelsAsync()
+ => this.Discord.ApiClient.GetGuildChannelsAsync(this.Id);
+
+ /// <summary>
+ /// Creates a new role in this guild.
+ /// </summary>
+ /// <param name="name">Name of the role.</param>
+ /// <param name="permissions">Permissions for the role.</param>
+ /// <param name="color">Color for the role.</param>
+ /// <param name="hoist">Whether the role is to be hoisted.</param>
+ /// <param name="mentionable">Whether the role is to be mentionable.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>The newly-created role.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordRole> CreateRoleAsync(string name = null, Permissions? permissions = null, DiscordColor? color = null, bool? hoist = null, bool? mentionable = null, string reason = null)
+ => this.Discord.ApiClient.CreateGuildRoleAsync(this.Id, name, permissions, color?.Value, hoist, mentionable, reason);
+
+ /// <summary>
+ /// Gets a role from this guild by its ID.
+ /// </summary>
+ /// <param name="id">ID of the role to get.</param>
+ /// <returns>Requested role.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public DiscordRole GetRole(ulong id)
+ => this.RolesInternal.TryGetValue(id, out var role) ? role : null;
+
+ /// <summary>
+ /// Gets a channel from this guild by its ID.
+ /// </summary>
+ /// <param name="id">ID of the channel to get.</param>
+ /// <returns>Requested channel.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public DiscordChannel GetChannel(ulong id)
+ => this.ChannelsInternal != null && this.ChannelsInternal.TryGetValue(id, out var channel) ? channel : null;
+
+ /// <summary>
+ /// Gets a thread from this guild by its ID.
+ /// </summary>
+ /// <param name="id">ID of the thread to get.</param>
+ /// <returns>Requested thread.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public DiscordThreadChannel GetThread(ulong id)
+ => this.ThreadsInternal != null && this.ThreadsInternal.TryGetValue(id, out var thread) ? thread : null;
+
+ /// <summary>
+ /// Gets all of this guild's custom emojis.
+ /// </summary>
+ /// <returns>All of this guild's custom emojis.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordGuildEmoji>> GetEmojisAsync()
+ => this.Discord.ApiClient.GetGuildEmojisAsync(this.Id);
+
+ /// <summary>
+ /// Gets this guild's specified custom emoji.
+ /// </summary>
+ /// <param name="id">ID of the emoji to get.</param>
+ /// <returns>The requested custom emoji.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildEmoji> GetEmojiAsync(ulong id)
+ => this.Discord.ApiClient.GetGuildEmojiAsync(this.Id, id);
+
+ /// <summary>
+ /// Creates a new custom emoji for this guild.
+ /// </summary>
+ /// <param name="name">Name of the new emoji.</param>
+ /// <param name="image">Image to use as the emoji.</param>
+ /// <param name="roles">Roles for which the emoji will be available. This works only if your application is whitelisted as integration.</param>
+ /// <param name="reason">Reason for audit log.</param>
+ /// <returns>The newly-created emoji.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildEmoji> CreateEmojiAsync(string name, Stream image, IEnumerable<DiscordRole> roles = null, string reason = null)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentNullException(nameof(name));
- /// <summary>
- /// Creates or overwrites a application command in this guild.
- /// </summary>
- /// <param name="command">The command to create.</param>
- /// <returns>The created command.</returns>
- public Task<DiscordApplicationCommand> CreateApplicationCommandAsync(DiscordApplicationCommand command) =>
- this.Discord.ApiClient.CreateGuildApplicationCommandAsync(this.Discord.CurrentApplication.Id, this.Id, command);
+ name = name.Trim();
+ if (name.Length < 2 || name.Length > 50)
+ throw new ArgumentException("Emoji name needs to be between 2 and 50 characters long.");
- /// <summary>
- /// Edits a application command in this guild.
- /// </summary>
- /// <param name="commandId">The id of the command to edit.</param>
- /// <param name="action">Action to perform.</param>
- /// <returns>The edit command.</returns>
- public async Task<DiscordApplicationCommand> EditApplicationCommandAsync(ulong commandId, Action<ApplicationCommandEditModel> action)
- {
- var mdl = new ApplicationCommandEditModel();
- action(mdl);
- return await this.Discord.ApiClient.EditGuildApplicationCommandAsync(this.Discord.CurrentApplication.Id, this.Id, commandId, mdl.Name, mdl.Description, mdl.Options, mdl.NameLocalizations, mdl.DescriptionLocalizations, mdl.DefaultMemberPermissions, mdl.DmPermission).ConfigureAwait(false);
- }
+ if (image == null)
+ throw new ArgumentNullException(nameof(image));
- /// <summary>
- /// Gets this guild's welcome screen.
- /// </summary>
- /// <returns>This guild's welcome screen object.</returns>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildWelcomeScreen> GetWelcomeScreenAsync() =>
- this.Discord.ApiClient.GetGuildWelcomeScreenAsync(this.Id);
+ var image64 = ImageTool.Base64FromStream(image);
- /// <summary>
- /// Modifies this guild's welcome screen.
- /// </summary>
- /// <param name="action">Action to perform.</param>
- /// <returns>The modified welcome screen.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client doesn't have the <see cref="Permissions.ManageGuild"/> permission, or community is not enabled on this guild.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordGuildWelcomeScreen> ModifyWelcomeScreenAsync(Action<WelcomeScreenEditModel> action)
- {
- var mdl = new WelcomeScreenEditModel();
- action(mdl);
- return await this.Discord.ApiClient.ModifyGuildWelcomeScreenAsync(this.Id, mdl.Enabled, mdl.WelcomeChannels, mdl.Description).ConfigureAwait(false);
- }
- #endregion
+ return this.Discord.ApiClient.CreateGuildEmojiAsync(this.Id, name, image64, roles?.Select(xr => xr.Id), reason);
+ }
- /// <summary>
- /// Returns a string representation of this guild.
- /// </summary>
- /// <returns>String representation of this guild.</returns>
- public override string ToString()
- => $"Guild {this.Id}; {this.Name}";
+ /// <summary>
+ /// Modifies a this guild's custom emoji.
+ /// </summary>
+ /// <param name="emoji">Emoji to modify.</param>
+ /// <param name="name">New name for the emoji.</param>
+ /// <param name="roles">Roles for which the emoji will be available. This works only if your application is whitelisted as integration.</param>
+ /// <param name="reason">Reason for audit log.</param>
+ /// <returns>The modified emoji.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildEmoji> ModifyEmojiAsync(DiscordGuildEmoji emoji, string name, IEnumerable<DiscordRole> roles = null, string reason = null)
+ {
+ if (emoji == null)
+ throw new ArgumentNullException(nameof(emoji));
- /// <summary>
- /// Checks whether this <see cref="DiscordGuild"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordGuild"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordGuild);
+ if (emoji.Guild.Id != this.Id)
+ throw new ArgumentException("This emoji does not belong to this guild.");
- /// <summary>
- /// Checks whether this <see cref="DiscordGuild"/> is equal to another <see cref="DiscordGuild"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordGuild"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordGuild"/> is equal to this <see cref="DiscordGuild"/>.</returns>
- public bool Equals(DiscordGuild e)
- => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+ if (string.IsNullOrWhiteSpace(name))
+ throw new ArgumentNullException(nameof(name));
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordGuild"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordGuild"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
+ name = name.Trim();
+ return name.Length < 2 || name.Length > 50
+ ? throw new ArgumentException("Emoji name needs to be between 2 and 50 characters long.")
+ : this.Discord.ApiClient.ModifyGuildEmojiAsync(this.Id, emoji.Id, name, roles?.Select(xr => xr.Id), reason);
+ }
- /// <summary>
- /// Gets whether the two <see cref="DiscordGuild"/> objects are equal.
- /// </summary>
- /// <param name="e1">First guild to compare.</param>
- /// <param name="e2">Second guild to compare.</param>
- /// <returns>Whether the two guilds are equal.</returns>
- public static bool operator ==(DiscordGuild e1, DiscordGuild e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ /// <summary>
+ /// Deletes this guild's custom emoji.
+ /// </summary>
+ /// <param name="emoji">Emoji to delete.</param>
+ /// <param name="reason">Reason for audit log.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteEmojiAsync(DiscordGuildEmoji emoji, string reason = null) =>
+ emoji == null
+ ? throw new ArgumentNullException(nameof(emoji))
+ : emoji.Guild.Id != this.Id
+ ? throw new ArgumentException("This emoji does not belong to this guild.")
+ : this.Discord.ApiClient.DeleteGuildEmojiAsync(this.Id, emoji.Id, reason);
+
+ /// <summary>
+ /// Gets all of this guild's custom stickers.
+ /// </summary>
+ /// <returns>All of this guild's custom stickers.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<IReadOnlyList<DiscordSticker>> GetStickersAsync()
+ {
+ var stickers = await this.Discord.ApiClient.GetGuildStickersAsync(this.Id);
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
- }
+ foreach (var xstr in stickers)
+ {
+ this.StickersInternal.AddOrUpdate(xstr.Id, xstr, (id, old) =>
+ {
+ old.Name = xstr.Name;
+ old.Description = xstr.Description;
+ old.InternalTags = xstr.InternalTags;
+ return old;
+ });
+ }
- /// <summary>
- /// Gets whether the two <see cref="DiscordGuild"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First guild to compare.</param>
- /// <param name="e2">Second guild to compare.</param>
- /// <returns>Whether the two guilds are not equal.</returns>
- public static bool operator !=(DiscordGuild e1, DiscordGuild e2)
- => !(e1 == e2);
+ return stickers;
+ }
-}
+ /// <summary>
+ /// Gets a sticker
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
+ public Task<DiscordSticker> GetStickerAsync(ulong stickerId)
+ => this.Discord.ApiClient.GetGuildStickerAsync(this.Id, stickerId);
+
+ /// <summary>
+ /// Creates a sticker
+ /// </summary>
+ /// <param name="name">The name of the sticker.</param>
+ /// <param name="description">The optional description of the sticker.</param>
+ /// <param name="emoji">The emoji to associate the sticker with.</param>
+ /// <param name="format">The file format the sticker is written in.</param>
+ /// <param name="file">The sticker.</param>
+ /// <param name="reason">Audit log reason</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordSticker> CreateStickerAsync(string name, string description, DiscordEmoji emoji, Stream file, StickerFormat format, string reason = null)
+ {
+ var fileExt = format switch
+ {
+ StickerFormat.Png => "png",
+ StickerFormat.Apng => "png",
+ StickerFormat.Lottie => "json",
+ _ => throw new InvalidOperationException("This format is not supported.")
+ };
-/// <summary>
-/// Represents guild verification level.
-/// </summary>
-public enum VerificationLevel : int
-{
- /// <summary>
- /// No verification. Anyone can join and chat right away.
- /// </summary>
- None = 0,
+ var contentType = format switch
+ {
+ StickerFormat.Png => "image/png",
+ StickerFormat.Apng => "image/png",
+ StickerFormat.Lottie => "application/json",
+ _ => throw new InvalidOperationException("This format is not supported.")
+ };
+
+ return emoji.Id is not 0
+ ? throw new InvalidOperationException("Only unicode emoji can be used for stickers.")
+ : name.Length < 2 || name.Length > 30
+ ? throw new ArgumentOutOfRangeException(nameof(name), "Sticker name needs to be between 2 and 30 characters long.")
+ : description.Length < 1 || description.Length > 100
+ ? throw new ArgumentOutOfRangeException(nameof(description), "Sticker description needs to be between 1 and 100 characters long.")
+ : this.Discord.ApiClient.CreateGuildStickerAsync(this.Id, name, description, emoji.GetDiscordName().Replace(":", ""), new DiscordMessageFile("sticker", file, null, fileExt, contentType), reason);
+ }
- /// <summary>
- /// Low verification level. Users are required to have a verified email attached to their account in order to be able to chat.
- /// </summary>
- Low = 1,
+ /// <summary>
+ /// Modifies a sticker
+ /// </summary>
+ /// <param name="sticker">The id of the sticker to modify</param>
+ /// <param name="name">The name of the sticker</param>
+ /// <param name="description">The description of the sticker</param>
+ /// <param name="emoji">The emoji to associate with this sticker.</param>
+ /// <param name="reason">Audit log reason</param>
+ /// <returns>A sticker object</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
+ public async Task<DiscordSticker> ModifyStickerAsync(ulong sticker, Optional<string> name, Optional<string> description, Optional<DiscordEmoji> emoji, string reason = null)
+ {
+ if (!this.StickersInternal.TryGetValue(sticker, out var stickerobj) || stickerobj.Guild.Id != this.Id)
+ throw new ArgumentException("This sticker does not belong to this guild.");
+ if (name.HasValue && (name.Value.Length < 2 || name.Value.Length > 30))
+ throw new ArgumentException("Sticker name needs to be between 2 and 30 characters long.");
+ if (description.HasValue && (description.Value.Length < 1 || description.Value.Length > 100))
+ throw new ArgumentException("Sticker description needs to be between 1 and 100 characters long.");
+ if (emoji.HasValue && emoji.Value.Id > 0)
+ throw new ArgumentException("Only unicode emojis can be used with stickers.");
- /// <summary>
- /// Medium verification level. Users are required to have a verified email attached to their account, and account age need to be at least 5 minutes in order to be able to chat.
- /// </summary>
- Medium = 2,
+ string uemoji = null;
+ if (emoji.HasValue)
+ uemoji = emoji.Value.GetDiscordName().Replace(":", "");
- /// <summary>
- /// High verification level. Users are required to have a verified email attached to their account, account age need to be at least 5 minutes, and they need to be in the server for at least 10 minutes in order to be able to chat.
- /// </summary>
- High = 3,
+ var usticker = await this.Discord.ApiClient.ModifyGuildStickerAsync(this.Id, sticker, name, description, uemoji, reason).ConfigureAwait(false);
- /// <summary>
- /// Highest verification level. Users are required to have a verified phone number attached to their account.
- /// </summary>
- Highest = 4
-}
-/// <summary>
-/// Represents default notification level for a guild.
-/// </summary>
-public enum DefaultMessageNotifications : int
-{
- /// <summary>
- /// All messages will trigger push notifications.
- /// </summary>
- AllMessages = 0,
+ if (this.StickersInternal.TryGetValue(usticker.Id, out var old))
+ this.StickersInternal.TryUpdate(usticker.Id, usticker, old);
- /// <summary>
- /// Only messages that mention the user (or a role he's in) will trigger push notifications.
- /// </summary>
- MentionsOnly = 1
-}
+ return usticker;
+ }
-/// <summary>
-/// Represents multi-factor authentication level required by a guild to use administrator functionality.
-/// </summary>
-public enum MfaLevel : int
-{
- /// <summary>
- /// Multi-factor authentication is not required to use administrator functionality.
- /// </summary>
- Disabled = 0,
+ /// <summary>
+ /// Modifies a sticker
+ /// </summary>
+ /// <param name="sticker">The sticker to modify</param>
+ /// <param name="name">The name of the sticker</param>
+ /// <param name="description">The description of the sticker</param>
+ /// <param name="emoji">The emoji to associate with this sticker.</param>
+ /// <param name="reason">Audit log reason</param>
+ /// <returns>A sticker object</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
+ public Task<DiscordSticker> ModifyStickerAsync(DiscordSticker sticker, Optional<string> name, Optional<string> description, Optional<DiscordEmoji> emoji, string reason = null)
+ => this.ModifyStickerAsync(sticker.Id, name, description, emoji, reason);
+
+ /// <summary>
+ /// Deletes a sticker
+ /// </summary>
+ /// <param name="sticker">Id of sticker to delete</param>
+ /// <param name="reason">Audit log reason</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
+ public Task DeleteStickerAsync(ulong sticker, string reason = null) =>
+ !this.StickersInternal.TryGetValue(sticker, out var stickerobj)
+ ? throw new ArgumentNullException(nameof(sticker))
+ : stickerobj.Guild.Id != this.Id
+ ? throw new ArgumentException("This sticker does not belong to this guild.")
+ : this.Discord.ApiClient.DeleteGuildStickerAsync(this.Id, sticker, reason);
+
+ /// <summary>
+ /// Deletes a sticker
+ /// </summary>
+ /// <param name="sticker">Sticker to delete</param>
+ /// <param name="reason">Audit log reason</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
+ public Task DeleteStickerAsync(DiscordSticker sticker, string reason = null)
+ => this.DeleteStickerAsync(sticker.Id, reason);
+
+ /// <summary>
+ /// <para>Gets the default channel for this guild.</para>
+ /// <para>Default channel is the first channel current member can see.</para>
+ /// </summary>
+ /// <returns>This member's default guild.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public DiscordChannel GetDefaultChannel() =>
+ this.ChannelsInternal?.Values.Where(xc => xc.Type == ChannelType.Text)
+ .OrderBy(xc => xc.Position)
+ .FirstOrDefault(xc => (xc.PermissionsFor(this.CurrentMember) & DisCatSharp.Permissions.AccessChannels) == DisCatSharp.Permissions.AccessChannels);
+
+ /// <summary>
+ /// Gets the guild's widget
+ /// </summary>
+ /// <returns>The guild's widget</returns>
+ public Task<DiscordWidget> GetWidgetAsync()
+ => this.Discord.ApiClient.GetGuildWidgetAsync(this.Id);
+
+ /// <summary>
+ /// Gets the guild's widget settings
+ /// </summary>
+ /// <returns>The guild's widget settings</returns>
+ public Task<DiscordWidgetSettings> GetWidgetSettingsAsync()
+ => this.Discord.ApiClient.GetGuildWidgetSettingsAsync(this.Id);
+
+ /// <summary>
+ /// Modifies the guild's widget settings
+ /// </summary>
+ /// <param name="isEnabled">If the widget is enabled or not</param>
+ /// <param name="channel">Widget channel</param>
+ /// <param name="reason">Reason the widget settings were modified</param>
+ /// <returns>The newly modified widget settings</returns>
+ public Task<DiscordWidgetSettings> ModifyWidgetSettingsAsync(bool? isEnabled = null, DiscordChannel channel = null, string reason = null)
+ => this.Discord.ApiClient.ModifyGuildWidgetSettingsAsync(this.Id, isEnabled, channel?.Id, reason);
+
+ /// <summary>
+ /// Gets all of this guild's templates.
+ /// </summary>
+ /// <returns>All of the guild's templates.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordGuildTemplate>> GetTemplatesAsync()
+ => this.Discord.ApiClient.GetGuildTemplatesAsync(this.Id);
+
+ /// <summary>
+ /// Creates a guild template.
+ /// </summary>
+ /// <param name="name">Name of the template.</param>
+ /// <param name="description">Description of the template.</param>
+ /// <returns>The template created.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Throws when a template already exists for the guild or a null parameter is provided for the name.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildTemplate> CreateTemplateAsync(string name, string description = null)
+ => this.Discord.ApiClient.CreateGuildTemplateAsync(this.Id, name, description);
+
+ /// <summary>
+ /// Syncs the template to the current guild's state.
+ /// </summary>
+ /// <param name="code">The code of the template to sync.</param>
+ /// <returns>The template synced.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Throws when the template for the code cannot be found</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildTemplate> SyncTemplateAsync(string code)
+ => this.Discord.ApiClient.SyncGuildTemplateAsync(this.Id, code);
+
+ /// <summary>
+ /// Modifies the template's metadata.
+ /// </summary>
+ /// <param name="code">The template's code.</param>
+ /// <param name="name">Name of the template.</param>
+ /// <param name="description">Description of the template.</param>
+ /// <returns>The template modified.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Throws when the template for the code cannot be found</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildTemplate> ModifyTemplateAsync(string code, string name = null, string description = null)
+ => this.Discord.ApiClient.ModifyGuildTemplateAsync(this.Id, code, name, description);
+
+ /// <summary>
+ /// Deletes the template.
+ /// </summary>
+ /// <param name="code">The code of the template to delete.</param>
+ /// <returns>The deleted template.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Throws when the template for the code cannot be found</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Throws when the client does not have the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildTemplate> DeleteTemplateAsync(string code)
+ => this.Discord.ApiClient.DeleteGuildTemplateAsync(this.Id, code);
+
+ /// <summary>
+ /// Gets this guild's membership screening form.
+ /// </summary>
+ /// <returns>This guild's membership screening form.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildMembershipScreening> GetMembershipScreeningFormAsync()
+ => this.Discord.ApiClient.GetGuildMembershipScreeningFormAsync(this.Id);
+
+ /// <summary>
+ /// Modifies this guild's membership screening form.
+ /// </summary>
+ /// <param name="action">Action to perform</param>
+ /// <returns>The modified screening form.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client doesn't have the <see cref="Permissions.ManageGuild"/> permission, or community is not enabled on this guild.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordGuildMembershipScreening> ModifyMembershipScreeningFormAsync(Action<MembershipScreeningEditModel> action)
+ {
+ var mdl = new MembershipScreeningEditModel();
+ action(mdl);
+ return await this.Discord.ApiClient.ModifyGuildMembershipScreeningFormAsync(this.Id, mdl.Enabled, mdl.Fields, mdl.Description);
+ }
- /// <summary>
- /// Multi-factor authentication is required to use administrator functionality.
- /// </summary>
- Enabled = 1
-}
+ /// <summary>
+ /// Gets all the application commands in this guild.
+ /// </summary>
+ /// <returns>A list of application commands in this guild.</returns>
+ public Task<IReadOnlyList<DiscordApplicationCommand>> GetApplicationCommandsAsync() =>
+ this.Discord.ApiClient.GetGuildApplicationCommandsAsync(this.Discord.CurrentApplication.Id, this.Id);
+
+ /// <summary>
+ /// Overwrites the existing application commands in this guild. New commands are automatically created and missing commands are automatically delete
+ /// </summary>
+ /// <param name="commands">The list of commands to overwrite with.</param>
+ /// <returns>The list of guild commands</returns>
+ public Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteApplicationCommandsAsync(IEnumerable<DiscordApplicationCommand> commands) =>
+ this.Discord.ApiClient.BulkOverwriteGuildApplicationCommandsAsync(this.Discord.CurrentApplication.Id, this.Id, commands);
+
+ /// <summary>
+ /// Creates or overwrites a application command in this guild.
+ /// </summary>
+ /// <param name="command">The command to create.</param>
+ /// <returns>The created command.</returns>
+ public Task<DiscordApplicationCommand> CreateApplicationCommandAsync(DiscordApplicationCommand command) =>
+ this.Discord.ApiClient.CreateGuildApplicationCommandAsync(this.Discord.CurrentApplication.Id, this.Id, command);
+
+ /// <summary>
+ /// Edits a application command in this guild.
+ /// </summary>
+ /// <param name="commandId">The id of the command to edit.</param>
+ /// <param name="action">Action to perform.</param>
+ /// <returns>The edit command.</returns>
+ public async Task<DiscordApplicationCommand> EditApplicationCommandAsync(ulong commandId, Action<ApplicationCommandEditModel> action)
+ {
+ var mdl = new ApplicationCommandEditModel();
+ action(mdl);
+ return await this.Discord.ApiClient.EditGuildApplicationCommandAsync(this.Discord.CurrentApplication.Id, this.Id, commandId, mdl.Name, mdl.Description, mdl.Options, mdl.NameLocalizations, mdl.DescriptionLocalizations, mdl.DefaultMemberPermissions, mdl.DmPermission).ConfigureAwait(false);
+ }
-/// <summary>
-/// Represents the value of explicit content filter in a guild.
-/// </summary>
-public enum ExplicitContentFilter : int
-{
- /// <summary>
- /// Explicit content filter is disabled.
- /// </summary>
- Disabled = 0,
+ /// <summary>
+ /// Gets this guild's welcome screen.
+ /// </summary>
+ /// <returns>This guild's welcome screen object.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildWelcomeScreen> GetWelcomeScreenAsync() =>
+ this.Discord.ApiClient.GetGuildWelcomeScreenAsync(this.Id);
+
+ /// <summary>
+ /// Modifies this guild's welcome screen.
+ /// </summary>
+ /// <param name="action">Action to perform.</param>
+ /// <returns>The modified welcome screen.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client doesn't have the <see cref="Permissions.ManageGuild"/> permission, or community is not enabled on this guild.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordGuildWelcomeScreen> ModifyWelcomeScreenAsync(Action<WelcomeScreenEditModel> action)
+ {
+ var mdl = new WelcomeScreenEditModel();
+ action(mdl);
+ return await this.Discord.ApiClient.ModifyGuildWelcomeScreenAsync(this.Id, mdl.Enabled, mdl.WelcomeChannels, mdl.Description).ConfigureAwait(false);
+ }
+ #endregion
+
+ /// <summary>
+ /// Returns a string representation of this guild.
+ /// </summary>
+ /// <returns>String representation of this guild.</returns>
+ public override string ToString()
+ => $"Guild {this.Id}; {this.Name}";
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordGuild"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordGuild"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordGuild);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordGuild"/> is equal to another <see cref="DiscordGuild"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordGuild"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordGuild"/> is equal to this <see cref="DiscordGuild"/>.</returns>
+ public bool Equals(DiscordGuild e)
+ => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordGuild"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordGuild"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordGuild"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First guild to compare.</param>
+ /// <param name="e2">Second guild to compare.</param>
+ /// <returns>Whether the two guilds are equal.</returns>
+ public static bool operator ==(DiscordGuild e1, DiscordGuild e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- /// <summary>
- /// Only messages from members without any roles are scanned.
- /// </summary>
- MembersWithoutRoles = 1,
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
- /// <summary>
- /// Messages from all members are scanned.
- /// </summary>
- AllMembers = 2
-}
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordGuild"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First guild to compare.</param>
+ /// <param name="e2">Second guild to compare.</param>
+ /// <returns>Whether the two guilds are not equal.</returns>
+ public static bool operator !=(DiscordGuild e1, DiscordGuild e2)
+ => !(e1 == e2);
+
+ }
-/// <summary>
-/// Represents the formats for a guild widget.
-/// </summary>
-public enum WidgetType : int
-{
/// <summary>
- /// The widget is represented in shield format.
- /// <para>This is the default widget type.</para>
+ /// Represents guild verification level.
/// </summary>
- Shield = 0,
+ public enum VerificationLevel : int
+ {
+ /// <summary>
+ /// No verification. Anyone can join and chat right away.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// Low verification level. Users are required to have a verified email attached to their account in order to be able to chat.
+ /// </summary>
+ Low = 1,
+
+ /// <summary>
+ /// Medium verification level. Users are required to have a verified email attached to their account, and account age need to be at least 5 minutes in order to be able to chat.
+ /// </summary>
+ Medium = 2,
+
+ /// <summary>
+ /// High verification level. Users are required to have a verified email attached to their account, account age need to be at least 5 minutes, and they need to be in the server for at least 10 minutes in order to be able to chat.
+ /// </summary>
+ High = 3,
+
+ /// <summary>
+ /// Highest verification level. Users are required to have a verified phone number attached to their account.
+ /// </summary>
+ Highest = 4
+ }
/// <summary>
- /// The widget is represented as the first banner type.
+ /// Represents default notification level for a guild.
/// </summary>
- Banner1 = 1,
+ public enum DefaultMessageNotifications : int
+ {
+ /// <summary>
+ /// All messages will trigger push notifications.
+ /// </summary>
+ AllMessages = 0,
+
+ /// <summary>
+ /// Only messages that mention the user (or a role he's in) will trigger push notifications.
+ /// </summary>
+ MentionsOnly = 1
+ }
/// <summary>
- /// The widget is represented as the second banner type.
+ /// Represents multi-factor authentication level required by a guild to use administrator functionality.
/// </summary>
- Banner2 = 2,
+ public enum MfaLevel : int
+ {
+ /// <summary>
+ /// Multi-factor authentication is not required to use administrator functionality.
+ /// </summary>
+ Disabled = 0,
+
+ /// <summary>
+ /// Multi-factor authentication is required to use administrator functionality.
+ /// </summary>
+ Enabled = 1
+ }
/// <summary>
- /// The widget is represented as the third banner type.
+ /// Represents the value of explicit content filter in a guild.
/// </summary>
- Banner3 = 3,
+ public enum ExplicitContentFilter : int
+ {
+ /// <summary>
+ /// Explicit content filter is disabled.
+ /// </summary>
+ Disabled = 0,
+
+ /// <summary>
+ /// Only messages from members without any roles are scanned.
+ /// </summary>
+ MembersWithoutRoles = 1,
+
+ /// <summary>
+ /// Messages from all members are scanned.
+ /// </summary>
+ AllMembers = 2
+ }
/// <summary>
- /// The widget is represented in the fourth banner type.
+ /// Represents the formats for a guild widget.
/// </summary>
- Banner4 = 4
+ public enum WidgetType : int
+ {
+ /// <summary>
+ /// The widget is represented in shield format.
+ /// <para>This is the default widget type.</para>
+ /// </summary>
+ Shield = 0,
+
+ /// <summary>
+ /// The widget is represented as the first banner type.
+ /// </summary>
+ Banner1 = 1,
+
+ /// <summary>
+ /// The widget is represented as the second banner type.
+ /// </summary>
+ Banner2 = 2,
+
+ /// <summary>
+ /// The widget is represented as the third banner type.
+ /// </summary>
+ Banner3 = 3,
+
+ /// <summary>
+ /// The widget is represented in the fourth banner type.
+ /// </summary>
+ Banner4 = 4
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuildEmoji.cs b/DisCatSharp/Entities/Guild/DiscordGuildEmoji.cs
index cde206710..1075dc9fe 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuildEmoji.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuildEmoji.cs
@@ -1,77 +1,78 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a guild emoji.
-/// </summary>
-public sealed class DiscordGuildEmoji : DiscordEmoji
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the user that created this emoji.
+ /// Represents a guild emoji.
/// </summary>
- [JsonIgnore]
- public DiscordUser User { get; internal set; }
+ public sealed class DiscordGuildEmoji : DiscordEmoji
+ {
+ /// <summary>
+ /// Gets the user that created this emoji.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the guild to which this emoji belongs.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild to which this emoji belongs.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordGuildEmoji"/> class.
- /// </summary>
- internal DiscordGuildEmoji()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordGuildEmoji"/> class.
+ /// </summary>
+ internal DiscordGuildEmoji()
+ { }
- /// <summary>
- /// Modifies this emoji.
- /// </summary>
- /// <param name="name">New name for this emoji.</param>
- /// <param name="roles">Roles for which this emoji will be available. This works only if your application is whitelisted as integration.</param>
- /// <param name="reason">Reason for audit log.</param>
- /// <returns>The modified emoji.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordGuildEmoji> ModifyAsync(string name, IEnumerable<DiscordRole> roles = null, string reason = null)
- => this.Guild.ModifyEmojiAsync(this, name, roles, reason);
+ /// <summary>
+ /// Modifies this emoji.
+ /// </summary>
+ /// <param name="name">New name for this emoji.</param>
+ /// <param name="roles">Roles for which this emoji will be available. This works only if your application is whitelisted as integration.</param>
+ /// <param name="reason">Reason for audit log.</param>
+ /// <returns>The modified emoji.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordGuildEmoji> ModifyAsync(string name, IEnumerable<DiscordRole> roles = null, string reason = null)
+ => this.Guild.ModifyEmojiAsync(this, name, roles, reason);
- /// <summary>
- /// Deletes this emoji.
- /// </summary>
- /// <param name="reason">Reason for audit log.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteAsync(string reason = null)
- => this.Guild.DeleteEmojiAsync(this, reason);
+ /// <summary>
+ /// Deletes this emoji.
+ /// </summary>
+ /// <param name="reason">Reason for audit log.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteAsync(string reason = null)
+ => this.Guild.DeleteEmojiAsync(this, reason);
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuildMembershipScreening.cs b/DisCatSharp/Entities/Guild/DiscordGuildMembershipScreening.cs
index 7ac7f7ca4..91ba55a21 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuildMembershipScreening.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuildMembershipScreening.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a guild's membership screening form.
-/// </summary>
-public class DiscordGuildMembershipScreening
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets when the fields were last updated.
+ /// Represents a guild's membership screening form.
/// </summary>
- [JsonProperty("version")]
- public DateTimeOffset Version { get; internal set; }
+ public class DiscordGuildMembershipScreening
+ {
+ /// <summary>
+ /// Gets when the fields were last updated.
+ /// </summary>
+ [JsonProperty("version")]
+ public DateTimeOffset Version { get; internal set; }
- /// <summary>
- /// Gets the steps in the screening form.
- /// </summary>
- [JsonProperty("form_fields")]
- public IReadOnlyList<DiscordGuildMembershipScreeningField> Fields { get; internal set; }
+ /// <summary>
+ /// Gets the steps in the screening form.
+ /// </summary>
+ [JsonProperty("form_fields")]
+ public IReadOnlyList<DiscordGuildMembershipScreeningField> Fields { get; internal set; }
- /// <summary>
- /// Gets the server description shown in the screening form.
- /// </summary>
- [JsonProperty("description")]
- public string Description { get; internal set; }
+ /// <summary>
+ /// Gets the server description shown in the screening form.
+ /// </summary>
+ [JsonProperty("description")]
+ public string Description { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuildMembershipScreeningField.cs b/DisCatSharp/Entities/Guild/DiscordGuildMembershipScreeningField.cs
index 57e46588f..00f743bc9 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuildMembershipScreeningField.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuildMembershipScreeningField.cs
@@ -1,78 +1,79 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a field in a guild's membership screening form
-/// </summary>
-public class DiscordGuildMembershipScreeningField
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the type of the field.
+ /// Represents a field in a guild's membership screening form
/// </summary>
- [JsonProperty("field_type", NullValueHandling = NullValueHandling.Ignore)]
- public MembershipScreeningFieldType Type { get; internal set; }
+ public class DiscordGuildMembershipScreeningField
+ {
+ /// <summary>
+ /// Gets the type of the field.
+ /// </summary>
+ [JsonProperty("field_type", NullValueHandling = NullValueHandling.Ignore)]
+ public MembershipScreeningFieldType Type { get; internal set; }
- /// <summary>
- /// Gets the title of the field.
- /// </summary>
- [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
- public string Label { get; internal set; }
+ /// <summary>
+ /// Gets the title of the field.
+ /// </summary>
+ [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
+ public string Label { get; internal set; }
- /// <summary>
- /// Gets the list of rules
- /// </summary>
- [JsonProperty("values", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<string> Values { get; internal set; }
+ /// <summary>
+ /// Gets the list of rules
+ /// </summary>
+ [JsonProperty("values", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<string> Values { get; internal set; }
- /// <summary>
- /// Gets whether the user has to fill out this field
- /// </summary>
- [JsonProperty("required", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsRequired { get; internal set; }
+ /// <summary>
+ /// Gets whether the user has to fill out this field
+ /// </summary>
+ [JsonProperty("required", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsRequired { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordGuildMembershipScreeningField"/> class.
- /// </summary>
- /// <param name="type">The type.</param>
- /// <param name="label">The label.</param>
- /// <param name="values">The values.</param>
- /// <param name="required">If true, required.</param>
- public DiscordGuildMembershipScreeningField(MembershipScreeningFieldType type, string label, IEnumerable<string> values, bool required = true)
- {
- this.Type = type;
- this.Label = label;
- this.Values = values.ToList();
- this.IsRequired = required;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordGuildMembershipScreeningField"/> class.
+ /// </summary>
+ /// <param name="type">The type.</param>
+ /// <param name="label">The label.</param>
+ /// <param name="values">The values.</param>
+ /// <param name="required">If true, required.</param>
+ public DiscordGuildMembershipScreeningField(MembershipScreeningFieldType type, string label, IEnumerable<string> values, bool required = true)
+ {
+ this.Type = type;
+ this.Label = label;
+ this.Values = values.ToList();
+ this.IsRequired = required;
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordGuildMembershipScreeningField"/> class.
- /// </summary>
- internal DiscordGuildMembershipScreeningField() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordGuildMembershipScreeningField"/> class.
+ /// </summary>
+ internal DiscordGuildMembershipScreeningField() { }
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuildPreview.cs b/DisCatSharp/Entities/Guild/DiscordGuildPreview.cs
index bc36610b6..ad3f3de6e 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuildPreview.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuildPreview.cs
@@ -1,136 +1,137 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents the guild preview.
-/// </summary>
-public class DiscordGuildPreview : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the guild name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the guild icon's hash.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
- public string IconHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild icon's url.
- /// </summary>
- [JsonIgnore]
- public string IconUrl
- => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.{(this.IconHash.StartsWith("a_") ? "gif" : "png")}?size=1024" : null;
-
- /// <summary>
- /// Gets the guild splash's hash.
- /// </summary>
- [JsonProperty("splash", NullValueHandling = NullValueHandling.Ignore)]
- public string SplashHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild splash's url.
- /// </summary>
- [JsonIgnore]
- public string SplashUrl
- => !string.IsNullOrWhiteSpace(this.SplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.SplashHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets the guild discovery splash's hash.
- /// </summary>
- [JsonProperty("discovery_splash", NullValueHandling = NullValueHandling.Ignore)]
- public string DiscoverySplashHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild discovery splash's url.
- /// </summary>
- [JsonIgnore]
- public string DiscoverySplashUrl
- => !string.IsNullOrWhiteSpace(this.DiscoverySplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILD_DISCOVERY_SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.DiscoverySplashHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets a collection of this guild's emojis.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordEmoji> Emojis => new ReadOnlyConcurrentDictionary<ulong, DiscordEmoji>(this.EmojisInternal);
-
- [JsonProperty("emojis", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordEmoji> EmojisInternal;
-
- /// <summary>
- /// Gets a collection of this guild's features.
- /// </summary>
- [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<string> Features { get; internal set; }
-
- /// <summary>
- /// Gets the approximate member count.
- /// </summary>
- [JsonProperty("approximate_member_count")]
- public int ApproximateMemberCount { get; internal set; }
-
- /// <summary>
- /// Gets the approximate presence count.
- /// </summary>
- [JsonProperty("approximate_presence_count")]
- public int ApproximatePresenceCount { get; internal set; }
-
- /// <summary>
- /// Gets the description for the guild, if the guild is discoverable.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
-
- /// <summary>
- /// Gets the system channel flags for the guild.
- /// </summary>
- [JsonProperty("system_channel_flags", NullValueHandling = NullValueHandling.Ignore)]
- public SystemChannelFlags SystemChannelFlags { get; internal set; }
-
- /// <summary>
- /// Gets this hub type for the guild, if the guild is a hub.
- /// </summary>
- [JsonProperty("hub_type", NullValueHandling = NullValueHandling.Ignore)]
- public HubType HubType { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordGuildPreview"/> class.
+ /// Represents the guild preview.
/// </summary>
- internal DiscordGuildPreview()
- { }
+ public class DiscordGuildPreview : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the guild name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild icon's hash.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
+ public string IconHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild icon's url.
+ /// </summary>
+ [JsonIgnore]
+ public string IconUrl
+ => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.{(this.IconHash.StartsWith("a_") ? "gif" : "png")}?size=1024" : null;
+
+ /// <summary>
+ /// Gets the guild splash's hash.
+ /// </summary>
+ [JsonProperty("splash", NullValueHandling = NullValueHandling.Ignore)]
+ public string SplashHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild splash's url.
+ /// </summary>
+ [JsonIgnore]
+ public string SplashUrl
+ => !string.IsNullOrWhiteSpace(this.SplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.SplashHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets the guild discovery splash's hash.
+ /// </summary>
+ [JsonProperty("discovery_splash", NullValueHandling = NullValueHandling.Ignore)]
+ public string DiscoverySplashHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild discovery splash's url.
+ /// </summary>
+ [JsonIgnore]
+ public string DiscoverySplashUrl
+ => !string.IsNullOrWhiteSpace(this.DiscoverySplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILD_DISCOVERY_SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.DiscoverySplashHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets a collection of this guild's emojis.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordEmoji> Emojis => new ReadOnlyConcurrentDictionary<ulong, DiscordEmoji>(this.EmojisInternal);
+
+ [JsonProperty("emojis", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordEmoji> EmojisInternal;
+
+ /// <summary>
+ /// Gets a collection of this guild's features.
+ /// </summary>
+ [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<string> Features { get; internal set; }
+
+ /// <summary>
+ /// Gets the approximate member count.
+ /// </summary>
+ [JsonProperty("approximate_member_count")]
+ public int ApproximateMemberCount { get; internal set; }
+
+ /// <summary>
+ /// Gets the approximate presence count.
+ /// </summary>
+ [JsonProperty("approximate_presence_count")]
+ public int ApproximatePresenceCount { get; internal set; }
+
+ /// <summary>
+ /// Gets the description for the guild, if the guild is discoverable.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
+
+ /// <summary>
+ /// Gets the system channel flags for the guild.
+ /// </summary>
+ [JsonProperty("system_channel_flags", NullValueHandling = NullValueHandling.Ignore)]
+ public SystemChannelFlags SystemChannelFlags { get; internal set; }
+
+ /// <summary>
+ /// Gets this hub type for the guild, if the guild is a hub.
+ /// </summary>
+ [JsonProperty("hub_type", NullValueHandling = NullValueHandling.Ignore)]
+ public HubType HubType { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordGuildPreview"/> class.
+ /// </summary>
+ internal DiscordGuildPreview()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuildTemplate.cs b/DisCatSharp/Entities/Guild/DiscordGuildTemplate.cs
index f8e764360..c6d4d3d7d 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuildTemplate.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuildTemplate.cs
@@ -1,107 +1,108 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a guild template.
-/// </summary>
-public class DiscordGuildTemplate
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the template code.
+ /// Represents a guild template.
/// </summary>
- [JsonProperty("code", NullValueHandling = NullValueHandling.Ignore)]
- public string Code { get; internal set; }
+ public class DiscordGuildTemplate
+ {
+ /// <summary>
+ /// Gets the template code.
+ /// </summary>
+ [JsonProperty("code", NullValueHandling = NullValueHandling.Ignore)]
+ public string Code { get; internal set; }
- /// <summary>
- /// Gets the name of the template.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
+ /// <summary>
+ /// Gets the name of the template.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the description of the template.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
+ /// <summary>
+ /// Gets the description of the template.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
- /// <summary>
- /// Gets the number of times the template has been used.
- /// </summary>
- [JsonProperty("usage_count", NullValueHandling = NullValueHandling.Ignore)]
- public int UsageCount { get; internal set; }
+ /// <summary>
+ /// Gets the number of times the template has been used.
+ /// </summary>
+ [JsonProperty("usage_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int UsageCount { get; internal set; }
- /// <summary>
- /// Gets the ID of the creator of the template.
- /// </summary>
- [JsonProperty("creator_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong CreatorId { get; internal set; }
+ /// <summary>
+ /// Gets the ID of the creator of the template.
+ /// </summary>
+ [JsonProperty("creator_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong CreatorId { get; internal set; }
- /// <summary>
- /// Gets the creator of the template.
- /// </summary>
- [JsonProperty("creator", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUser Creator { get; internal set; }
+ /// <summary>
+ /// Gets the creator of the template.
+ /// </summary>
+ [JsonProperty("creator", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUser Creator { get; internal set; }
- /// <summary>
- /// Date the template was created.
- /// </summary>
- [JsonProperty("created_at", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset CreatedAt { get; internal set; }
+ /// <summary>
+ /// Date the template was created.
+ /// </summary>
+ [JsonProperty("created_at", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset CreatedAt { get; internal set; }
- /// <summary>
- /// Date the template was updated.
- /// </summary>
- [JsonProperty("updated_at", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset UpdatedAt { get; internal set; }
+ /// <summary>
+ /// Date the template was updated.
+ /// </summary>
+ [JsonProperty("updated_at", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset UpdatedAt { get; internal set; }
- /// <summary>
- /// Gets the ID of the source guild.
- /// </summary>
- [JsonProperty("source_guild_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong SourceGuildId { get; internal set; }
+ /// <summary>
+ /// Gets the ID of the source guild.
+ /// </summary>
+ [JsonProperty("source_guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong SourceGuildId { get; internal set; }
- /// <summary>
- /// Gets the source guild.
- /// </summary>
- [JsonProperty("serialized_source_guild", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordGuild SourceGuild { get; internal set; }
+ /// <summary>
+ /// Gets the source guild.
+ /// </summary>
+ [JsonProperty("serialized_source_guild", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordGuild SourceGuild { get; internal set; }
- /// <summary>
- /// Gets whether the template has unsynced changes.
- /// </summary>
- [JsonProperty("is_dirty", NullValueHandling = NullValueHandling.Ignore)]
- public bool? IsDirty { get; internal set; }
+ /// <summary>
+ /// Gets whether the template has unsynced changes.
+ /// </summary>
+ [JsonProperty("is_dirty", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsDirty { get; internal set; }
- /// <summary>
- /// Gets whether the template has unsynced changes.
- /// </summary>
- /// <remarks><see cref="IsDirty"/></remarks>
- [JsonIgnore]
- public bool? IsUnsynced
- => this.IsDirty;
+ /// <summary>
+ /// Gets whether the template has unsynced changes.
+ /// </summary>
+ /// <remarks><see cref="IsDirty"/></remarks>
+ [JsonIgnore]
+ public bool? IsUnsynced
+ => this.IsDirty;
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuildWelcomeScreen.cs b/DisCatSharp/Entities/Guild/DiscordGuildWelcomeScreen.cs
index 9d5ebe97c..52c1357d9 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuildWelcomeScreen.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuildWelcomeScreen.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord welcome screen object.
-/// </summary>
-public class DiscordGuildWelcomeScreen
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the server description shown in the welcome screen.
+ /// Represents a discord welcome screen object.
/// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
+ public class DiscordGuildWelcomeScreen
+ {
+ /// <summary>
+ /// Gets the server description shown in the welcome screen.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
- /// <summary>
- /// Gets the channels shown in the welcome screen.
- /// </summary>
- [JsonProperty("welcome_channels", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<DiscordGuildWelcomeScreenChannel> WelcomeChannels { get; internal set; }
+ /// <summary>
+ /// Gets the channels shown in the welcome screen.
+ /// </summary>
+ [JsonProperty("welcome_channels", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<DiscordGuildWelcomeScreenChannel> WelcomeChannels { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Guild/DiscordGuildWelcomeScreenChannel.cs b/DisCatSharp/Entities/Guild/DiscordGuildWelcomeScreenChannel.cs
index 997241bd1..1210f84cf 100644
--- a/DisCatSharp/Entities/Guild/DiscordGuildWelcomeScreenChannel.cs
+++ b/DisCatSharp/Entities/Guild/DiscordGuildWelcomeScreenChannel.cs
@@ -1,74 +1,75 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a channel in a welcome screen
-/// </summary>
-public class DiscordGuildWelcomeScreenChannel
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the id of the channel.
+ /// Represents a channel in a welcome screen
/// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; internal set; }
+ public class DiscordGuildWelcomeScreenChannel
+ {
+ /// <summary>
+ /// Gets the id of the channel.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; internal set; }
- /// <summary>
- /// Gets the description shown for the channel.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
+ /// <summary>
+ /// Gets the description shown for the channel.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
- /// <summary>
- /// Gets the emoji id if the emoji is custom, when applicable.
- /// </summary>
- [JsonProperty("emoji_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? EmojiId { get; internal set; }
+ /// <summary>
+ /// Gets the emoji id if the emoji is custom, when applicable.
+ /// </summary>
+ [JsonProperty("emoji_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? EmojiId { get; internal set; }
- /// <summary>
- /// Gets the name of the emoji if custom or the unicode character if standard, when applicable.
- /// </summary>
- [JsonProperty("emoji_name", NullValueHandling = NullValueHandling.Ignore)]
- public string EmojiName { get; internal set; }
+ /// <summary>
+ /// Gets the name of the emoji if custom or the unicode character if standard, when applicable.
+ /// </summary>
+ [JsonProperty("emoji_name", NullValueHandling = NullValueHandling.Ignore)]
+ public string EmojiName { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordGuildWelcomeScreenChannel"/> class.
- /// </summary>
- /// <param name="channelId">The channel id.</param>
- /// <param name="description">The description.</param>
- /// <param name="emoji">The emoji.</param>
- public DiscordGuildWelcomeScreenChannel(ulong channelId, string description, DiscordEmoji emoji = null)
- {
- this.ChannelId = channelId;
- this.Description = description;
- if (emoji != null)
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordGuildWelcomeScreenChannel"/> class.
+ /// </summary>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="emoji">The emoji.</param>
+ public DiscordGuildWelcomeScreenChannel(ulong channelId, string description, DiscordEmoji emoji = null)
{
- if (emoji.Id == 0)
- this.EmojiName = emoji.Name;
- else
- this.EmojiId = emoji.Id;
+ this.ChannelId = channelId;
+ this.Description = description;
+ if (emoji != null)
+ {
+ if (emoji.Id == 0)
+ this.EmojiName = emoji.Name;
+ else
+ this.EmojiId = emoji.Id;
+ }
}
}
}
diff --git a/DisCatSharp/Entities/Guild/DiscordMember.cs b/DisCatSharp/Entities/Guild/DiscordMember.cs
index 288403a43..6e5278253 100644
--- a/DisCatSharp/Entities/Guild/DiscordMember.cs
+++ b/DisCatSharp/Entities/Guild/DiscordMember.cs
@@ -1,784 +1,785 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Models;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord guild member.
-/// </summary>
-public class DiscordMember : DiscordUser, IEquatable<DiscordMember>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordMember"/> class.
- /// </summary>
- internal DiscordMember()
- {
- this._roleIdsLazy = new Lazy<IReadOnlyList<ulong>>(() => new ReadOnlyCollection<ulong>(this.RoleIdsInternal));
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordMember"/> class.
- /// </summary>
- /// <param name="user">The user.</param>
- internal DiscordMember(DiscordUser user)
- {
- this.Discord = user.Discord;
-
- this.Id = user.Id;
-
- this.RoleIdsInternal = new List<ulong>();
- this._roleIdsLazy = new Lazy<IReadOnlyList<ulong>>(() => new ReadOnlyCollection<ulong>(this.RoleIdsInternal));
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordMember"/> class.
+ /// Represents a Discord guild member.
/// </summary>
- /// <param name="mbr">The mbr.</param>
- internal DiscordMember(TransportMember mbr)
+ public class DiscordMember : DiscordUser, IEquatable<DiscordMember>
{
- this.Id = mbr.User.Id;
- this.IsDeafened = mbr.IsDeafened;
- this.IsMuted = mbr.IsMuted;
- this.JoinedAt = mbr.JoinedAt;
- this.Nickname = mbr.Nickname;
- this.PremiumSince = mbr.PremiumSince;
- this.IsPending = mbr.IsPending;
- this.GuildAvatarHash = mbr.GuildAvatarHash;
- this.GuildBannerHash = mbr.GuildBannerHash;
- this.GuildBio = mbr.GuildBio;
- this.CommunicationDisabledUntil = mbr.CommunicationDisabledUntil;
- this.AvatarHashInternal = mbr.AvatarHash;
- this.RoleIdsInternal = mbr.Roles ?? new List<ulong>();
- this._roleIdsLazy = new Lazy<IReadOnlyList<ulong>>(() => new ReadOnlyCollection<ulong>(this.RoleIdsInternal));
- this.MemberFlags = mbr.MemberFlags;
- }
-
- /// <summary>
- /// Gets the members avatar hash.
- /// </summary>
- [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
- public virtual string GuildAvatarHash { get; internal set; }
-
- /// <summary>
- /// Gets the members avatar URL.
- /// </summary>
- [JsonIgnore]
- public string GuildAvatarUrl
- => string.IsNullOrWhiteSpace(this.GuildAvatarHash) ? this.User.AvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.GuildId.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.GuildAvatarHash}.{(this.GuildAvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
-
- /// <summary>
- /// Gets the members banner hash.
- /// </summary>
- [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
- public virtual string GuildBannerHash { get; internal set; }
-
- /// <summary>
- /// Gets the members banner URL.
- /// </summary>
- [JsonIgnore]
- public string GuildBannerUrl
- => string.IsNullOrWhiteSpace(this.GuildBannerHash) ? this.User.BannerUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.GuildId.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.BANNERS}/{this.GuildBannerHash}.{(this.GuildBannerHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
-
- /// <summary>
- /// The color of this member's banner. Mutually exclusive with <see cref="GuildBannerHash"/>.
- /// </summary>
- [JsonIgnore]
- public override DiscordColor? BannerColor => this.User.BannerColor;
-
- /// <summary>
- /// Gets this member's nickname.
- /// </summary>
- [JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
- public string Nickname { get; internal set; }
-
- /// <summary>
- /// Gets the members guild bio.
- /// This is not available to bots tho.
- /// </summary>
- [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
- public string GuildBio { get; internal set; }
-
- /// <summary>
- /// Gets the members flags.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public MemberFlags MemberFlags { get; internal set; }
-
- [JsonIgnore]
- internal string AvatarHashInternal;
-
- /// <summary>
- /// Gets this member's display name.
- /// </summary>
- [JsonIgnore]
- public string DisplayName
- => this.Nickname ?? this.Username;
-
- /// <summary>
- /// List of role ids
- /// </summary>
- [JsonIgnore]
- internal IReadOnlyList<ulong> RoleIds
- => this._roleIdsLazy.Value;
-
- [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
- internal List<ulong> RoleIdsInternal;
- [JsonIgnore]
- private readonly Lazy<IReadOnlyList<ulong>> _roleIdsLazy;
-
- /// <summary>
- /// Gets the list of roles associated with this member.
- /// </summary>
- [JsonIgnore]
- public IEnumerable<DiscordRole> Roles
- => this.RoleIds.Select(id => this.Guild.GetRole(id)).Where(x => x != null);
-
- /// <summary>
- /// Gets the color associated with this user's top color-giving role, otherwise 0 (no color).
- /// </summary>
- [JsonIgnore]
- public DiscordColor Color
- {
- get
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMember"/> class.
+ /// </summary>
+ internal DiscordMember()
{
- var role = this.Roles.OrderByDescending(xr => xr.Position).FirstOrDefault(xr => xr.Color.Value != 0);
- return role != null ? role.Color : new DiscordColor();
+ this._roleIdsLazy = new Lazy<IReadOnlyList<ulong>>(() => new ReadOnlyCollection<ulong>(this.RoleIdsInternal));
}
- }
-
- /// <summary>
- /// Date the user joined the guild
- /// </summary>
- [JsonProperty("joined_at", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset JoinedAt { get; internal set; }
-
- /// <summary>
- /// Date the user started boosting this server
- /// </summary>
- [JsonProperty("premium_since", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset? PremiumSince { get; internal set; }
-
- /// <summary>
- /// Date until the can communicate again.
- /// </summary>
- [JsonProperty("communication_disabled_until", NullValueHandling = NullValueHandling.Include)]
- public DateTime? CommunicationDisabledUntil { get; internal set; }
-
- /// <summary>
- /// If the user is deafened
- /// </summary>
- [JsonProperty("is_deafened", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsDeafened { get; internal set; }
-
- /// <summary>
- /// If the user is muted
- /// </summary>
- [JsonProperty("is_muted", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsMuted { get; internal set; }
-
- /// <summary>
- /// Whether the user has not passed the guild's Membership Screening requirements yet.
- /// </summary>
- [JsonProperty("pending", NullValueHandling = NullValueHandling.Ignore)]
- public bool? IsPending { get; internal set; }
-
- /// <summary>
- /// Gets this member's voice state.
- /// </summary>
- [JsonIgnore]
- public DiscordVoiceState VoiceState
- => this.Discord.Guilds[this.GuildId].VoiceStates.TryGetValue(this.Id, out var voiceState) ? voiceState : null;
-
- [JsonIgnore]
- internal ulong GuildId = 0;
-
- /// <summary>
- /// Gets the guild of which this member is a part of.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild
- => this.Discord.Guilds[this.GuildId];
-
- /// <summary>
- /// Gets whether this member is the Guild owner.
- /// </summary>
- [JsonIgnore]
- public bool IsOwner
- => this.Id == this.Guild.OwnerId;
-
- /// <summary>
- /// Gets the member's position in the role hierarchy, which is the member's highest role's position. Returns <see cref="int.MaxValue"/> for the guild's owner.
- /// </summary>
- [JsonIgnore]
- public int Hierarchy
- => this.IsOwner ? int.MaxValue : this.RoleIds.Count == 0 ? 0 : this.Roles.Max(x => x.Position);
-
- /// <summary>
- /// Gets the permissions for the current member.
- /// </summary>
- [JsonIgnore]
- public Permissions Permissions => this.GetPermissions();
-
- #region Overridden user properties
-
- /// <summary>
- /// Gets the user.
- /// </summary>
- [JsonIgnore]
- internal DiscordUser User
- => this.Discord.UserCache[this.Id];
-
- /// <summary>
- /// Gets this member's username.
- /// </summary>
- [JsonIgnore]
- public override string Username
- {
- get => this.User.Username;
- internal set => this.User.Username = value;
- }
-
- /// <summary>
- /// Gets the member's 4-digit discriminator.
- /// </summary>
- [JsonIgnore]
- public override string Discriminator
- {
- get => this.User.Discriminator;
- internal set => this.User.Discriminator = value;
- }
-
- /// <summary>
- /// Gets the member's avatar hash.
- /// </summary>
- [JsonIgnore]
- public override string AvatarHash
- {
- get => this.User.AvatarHash;
- internal set => this.User.AvatarHash = value;
- }
-
- /// <summary>
- /// Gets the member's banner hash.
- /// </summary>
- [JsonIgnore]
- public override string BannerHash
- {
- get => this.User.BannerHash;
- internal set => this.User.BannerHash = value;
- }
-
- /// <summary>
- /// Gets whether the member is a bot.
- /// </summary>
- [JsonIgnore]
- public override bool IsBot
- {
- get => this.User.IsBot;
- internal set => this.User.IsBot = value;
- }
-
- /// <summary>
- /// Gets the member's email address.
- /// <para>This is only present in OAuth.</para>
- /// </summary>
- [JsonIgnore]
- public override string Email
- {
- get => this.User.Email;
- internal set => this.User.Email = value;
- }
-
- /// <summary>
- /// Gets whether the member has multi-factor authentication enabled.
- /// </summary>
- [JsonIgnore]
- public override bool? MfaEnabled
- {
- get => this.User.MfaEnabled;
- internal set => this.User.MfaEnabled = value;
- }
-
- /// <summary>
- /// Gets whether the member is verified.
- /// <para>This is only present in OAuth.</para>
- /// </summary>
- [JsonIgnore]
- public override bool? Verified
- {
- get => this.User.Verified;
- internal set => this.User.Verified = value;
- }
-
- /// <summary>
- /// Gets the member's chosen language
- /// </summary>
- [JsonIgnore]
- public override string Locale
- {
- get => this.User.Locale;
- internal set => this.User.Locale = value;
- }
-
- /// <summary>
- /// Gets the user's flags.
- /// </summary>
- [JsonIgnore]
- public override UserFlags? OAuthFlags
- {
- get => this.User.OAuthFlags;
- internal set => this.User.OAuthFlags = value;
- }
- /// <summary>
- /// Gets the member's flags for OAuth.
- /// </summary>
- [JsonIgnore]
- public override UserFlags? Flags
- {
- get => this.User.Flags;
- internal set => this.User.Flags = value;
- }
-
- #endregion
-
- /// <summary>
- /// Creates a direct message channel to this member.
- /// </summary>
- /// <returns>Direct message channel to this member.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordDmChannel> CreateDmChannelAsync()
- => this.Discord.ApiClient.CreateDmAsync(this.Id);
-
- /// <summary>
- /// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
- /// </summary>
- /// <param name="content">Content of the message to send.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> SendMessageAsync(string content)
- {
- if (this.IsBot && this.Discord.CurrentUser.IsBot)
- throw new ArgumentException("Bots cannot DM each other.");
-
- var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
- return await chn.SendMessageAsync(content).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
- /// </summary>
- /// <param name="embed">Embed to attach to the message.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> SendMessageAsync(DiscordEmbed embed)
- {
- if (this.IsBot && this.Discord.CurrentUser.IsBot)
- throw new ArgumentException("Bots cannot DM each other.");
-
- var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
- return await chn.SendMessageAsync(embed).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
- /// </summary>
- /// <param name="content">Content of the message to send.</param>
- /// <param name="embed">Embed to attach to the message.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> SendMessageAsync(string content, DiscordEmbed embed)
- {
- if (this.IsBot && this.Discord.CurrentUser.IsBot)
- throw new ArgumentException("Bots cannot DM each other.");
-
- var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
- return await chn.SendMessageAsync(content, embed).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMember"/> class.
+ /// </summary>
+ /// <param name="user">The user.</param>
+ internal DiscordMember(DiscordUser user)
+ {
+ this.Discord = user.Discord;
- /// <summary>
- /// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
- /// </summary>
- /// <param name="message">Builder to with the message.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> SendMessageAsync(DiscordMessageBuilder message)
- {
- if (this.IsBot && this.Discord.CurrentUser.IsBot)
- throw new ArgumentException("Bots cannot DM each other.");
+ this.Id = user.Id;
- var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
- return await chn.SendMessageAsync(message).ConfigureAwait(false);
- }
+ this.RoleIdsInternal = new List<ulong>();
+ this._roleIdsLazy = new Lazy<IReadOnlyList<ulong>>(() => new ReadOnlyCollection<ulong>(this.RoleIdsInternal));
+ }
- /// <summary>
- /// Sets this member's voice mute status.
- /// </summary>
- /// <param name="mute">Whether the member is to be muted.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MuteMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task SetMuteAsync(bool mute, string reason = null)
- => this.Discord.ApiClient.ModifyGuildMemberAsync(this.GuildId, this.Id, default, default, mute, default, default, reason);
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMember"/> class.
+ /// </summary>
+ /// <param name="mbr">The mbr.</param>
+ internal DiscordMember(TransportMember mbr)
+ {
+ this.Id = mbr.User.Id;
+ this.IsDeafened = mbr.IsDeafened;
+ this.IsMuted = mbr.IsMuted;
+ this.JoinedAt = mbr.JoinedAt;
+ this.Nickname = mbr.Nickname;
+ this.PremiumSince = mbr.PremiumSince;
+ this.IsPending = mbr.IsPending;
+ this.GuildAvatarHash = mbr.GuildAvatarHash;
+ this.GuildBannerHash = mbr.GuildBannerHash;
+ this.GuildBio = mbr.GuildBio;
+ this.CommunicationDisabledUntil = mbr.CommunicationDisabledUntil;
+ this.AvatarHashInternal = mbr.AvatarHash;
+ this.RoleIdsInternal = mbr.Roles ?? new List<ulong>();
+ this._roleIdsLazy = new Lazy<IReadOnlyList<ulong>>(() => new ReadOnlyCollection<ulong>(this.RoleIdsInternal));
+ this.MemberFlags = mbr.MemberFlags;
+ }
- /// <summary>
- /// Sets this member's voice deaf status.
- /// </summary>
- /// <param name="deaf">Whether the member is to be deafened.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.DeafenMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task SetDeafAsync(bool deaf, string reason = null)
- => this.Discord.ApiClient.ModifyGuildMemberAsync(this.GuildId, this.Id, default, default, default, deaf, default, reason);
+ /// <summary>
+ /// Gets the members avatar hash.
+ /// </summary>
+ [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string GuildAvatarHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the members avatar URL.
+ /// </summary>
+ [JsonIgnore]
+ public string GuildAvatarUrl
+ => string.IsNullOrWhiteSpace(this.GuildAvatarHash) ? this.User.AvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.GuildId.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.GuildAvatarHash}.{(this.GuildAvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
+
+ /// <summary>
+ /// Gets the members banner hash.
+ /// </summary>
+ [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string GuildBannerHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the members banner URL.
+ /// </summary>
+ [JsonIgnore]
+ public string GuildBannerUrl
+ => string.IsNullOrWhiteSpace(this.GuildBannerHash) ? this.User.BannerUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.GuildId.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.BANNERS}/{this.GuildBannerHash}.{(this.GuildBannerHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
+
+ /// <summary>
+ /// The color of this member's banner. Mutually exclusive with <see cref="GuildBannerHash"/>.
+ /// </summary>
+ [JsonIgnore]
+ public override DiscordColor? BannerColor => this.User.BannerColor;
+
+ /// <summary>
+ /// Gets this member's nickname.
+ /// </summary>
+ [JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
+ public string Nickname { get; internal set; }
+
+ /// <summary>
+ /// Gets the members guild bio.
+ /// This is not available to bots tho.
+ /// </summary>
+ [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
+ public string GuildBio { get; internal set; }
+
+ /// <summary>
+ /// Gets the members flags.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public MemberFlags MemberFlags { get; internal set; }
+
+ [JsonIgnore]
+ internal string AvatarHashInternal;
+
+ /// <summary>
+ /// Gets this member's display name.
+ /// </summary>
+ [JsonIgnore]
+ public string DisplayName
+ => this.Nickname ?? this.Username;
+
+ /// <summary>
+ /// List of role ids
+ /// </summary>
+ [JsonIgnore]
+ internal IReadOnlyList<ulong> RoleIds
+ => this._roleIdsLazy.Value;
+
+ [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
+ internal List<ulong> RoleIdsInternal;
+ [JsonIgnore]
+ private readonly Lazy<IReadOnlyList<ulong>> _roleIdsLazy;
+
+ /// <summary>
+ /// Gets the list of roles associated with this member.
+ /// </summary>
+ [JsonIgnore]
+ public IEnumerable<DiscordRole> Roles
+ => this.RoleIds.Select(id => this.Guild.GetRole(id)).Where(x => x != null);
+
+ /// <summary>
+ /// Gets the color associated with this user's top color-giving role, otherwise 0 (no color).
+ /// </summary>
+ [JsonIgnore]
+ public DiscordColor Color
+ {
+ get
+ {
+ var role = this.Roles.OrderByDescending(xr => xr.Position).FirstOrDefault(xr => xr.Color.Value != 0);
+ return role != null ? role.Color : new DiscordColor();
+ }
+ }
- /// <summary>
- /// Modifies this member.
- /// </summary>
- /// <param name="action">Action to perform on this member.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageNicknames"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task ModifyAsync(Action<MemberEditModel> action)
- {
- var mdl = new MemberEditModel();
- action(mdl);
+ /// <summary>
+ /// Date the user joined the guild
+ /// </summary>
+ [JsonProperty("joined_at", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset JoinedAt { get; internal set; }
+
+ /// <summary>
+ /// Date the user started boosting this server
+ /// </summary>
+ [JsonProperty("premium_since", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset? PremiumSince { get; internal set; }
+
+ /// <summary>
+ /// Date until the can communicate again.
+ /// </summary>
+ [JsonProperty("communication_disabled_until", NullValueHandling = NullValueHandling.Include)]
+ public DateTime? CommunicationDisabledUntil { get; internal set; }
+
+ /// <summary>
+ /// If the user is deafened
+ /// </summary>
+ [JsonProperty("is_deafened", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsDeafened { get; internal set; }
+
+ /// <summary>
+ /// If the user is muted
+ /// </summary>
+ [JsonProperty("is_muted", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsMuted { get; internal set; }
+
+ /// <summary>
+ /// Whether the user has not passed the guild's Membership Screening requirements yet.
+ /// </summary>
+ [JsonProperty("pending", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsPending { get; internal set; }
+
+ /// <summary>
+ /// Gets this member's voice state.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordVoiceState VoiceState
+ => this.Discord.Guilds[this.GuildId].VoiceStates.TryGetValue(this.Id, out var voiceState) ? voiceState : null;
+
+ [JsonIgnore]
+ internal ulong GuildId = 0;
+
+ /// <summary>
+ /// Gets the guild of which this member is a part of.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild
+ => this.Discord.Guilds[this.GuildId];
+
+ /// <summary>
+ /// Gets whether this member is the Guild owner.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsOwner
+ => this.Id == this.Guild.OwnerId;
+
+ /// <summary>
+ /// Gets the member's position in the role hierarchy, which is the member's highest role's position. Returns <see cref="int.MaxValue"/> for the guild's owner.
+ /// </summary>
+ [JsonIgnore]
+ public int Hierarchy
+ => this.IsOwner ? int.MaxValue : this.RoleIds.Count == 0 ? 0 : this.Roles.Max(x => x.Position);
+
+ /// <summary>
+ /// Gets the permissions for the current member.
+ /// </summary>
+ [JsonIgnore]
+ public Permissions Permissions => this.GetPermissions();
+
+ #region Overridden user properties
+
+ /// <summary>
+ /// Gets the user.
+ /// </summary>
+ [JsonIgnore]
+ internal DiscordUser User
+ => this.Discord.UserCache[this.Id];
+
+ /// <summary>
+ /// Gets this member's username.
+ /// </summary>
+ [JsonIgnore]
+ public override string Username
+ {
+ get => this.User.Username;
+ internal set => this.User.Username = value;
+ }
- if (mdl.VoiceChannel.HasValue && mdl.VoiceChannel.Value != null && mdl.VoiceChannel.Value.Type != ChannelType.Voice && mdl.VoiceChannel.Value.Type != ChannelType.Stage)
- throw new ArgumentException("Given channel is not a voice or stage channel.", nameof(mdl.VoiceChannel));
+ /// <summary>
+ /// Gets the member's 4-digit discriminator.
+ /// </summary>
+ [JsonIgnore]
+ public override string Discriminator
+ {
+ get => this.User.Discriminator;
+ internal set => this.User.Discriminator = value;
+ }
- if (mdl.Nickname.HasValue && this.Discord.CurrentUser.Id == this.Id)
+ /// <summary>
+ /// Gets the member's avatar hash.
+ /// </summary>
+ [JsonIgnore]
+ public override string AvatarHash
{
- await this.Discord.ApiClient.ModifyCurrentMemberNicknameAsync(this.Guild.Id, mdl.Nickname.Value,
- mdl.AuditLogReason).ConfigureAwait(false);
+ get => this.User.AvatarHash;
+ internal set => this.User.AvatarHash = value;
+ }
- await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, Optional.None,
- mdl.Roles.Map(e => e.Select(xr => xr.Id)), mdl.Muted, mdl.Deafened,
- mdl.VoiceChannel.Map(e => e?.Id), mdl.AuditLogReason).ConfigureAwait(false);
+ /// <summary>
+ /// Gets the member's banner hash.
+ /// </summary>
+ [JsonIgnore]
+ public override string BannerHash
+ {
+ get => this.User.BannerHash;
+ internal set => this.User.BannerHash = value;
}
- else
+
+ /// <summary>
+ /// Gets whether the member is a bot.
+ /// </summary>
+ [JsonIgnore]
+ public override bool IsBot
{
- await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, mdl.Nickname,
- mdl.Roles.Map(e => e.Select(xr => xr.Id)), mdl.Muted, mdl.Deafened,
- mdl.VoiceChannel.Map(e => e?.Id), mdl.AuditLogReason).ConfigureAwait(false);
+ get => this.User.IsBot;
+ internal set => this.User.IsBot = value;
}
- }
- /// <summary>
- /// Adds a timeout to a member.
- /// </summary>
- /// <param name="until">The datetime offset to time out the user. Up to 28 days.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task TimeoutAsync(DateTimeOffset until, string reason = null)
- => until.Subtract(DateTimeOffset.UtcNow).Days > 28 ? throw new ArgumentException("Timeout can not be longer than 28 days") : this.Discord.ApiClient.ModifyTimeoutAsync(this.Guild.Id, this.Id, until, reason);
+ /// <summary>
+ /// Gets the member's email address.
+ /// <para>This is only present in OAuth.</para>
+ /// </summary>
+ [JsonIgnore]
+ public override string Email
+ {
+ get => this.User.Email;
+ internal set => this.User.Email = value;
+ }
- /// <summary>
- /// Adds a timeout to a member.
- /// </summary>
- /// <param name="until">The timespan to time out the user. Up to 28 days.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task TimeoutAsync(TimeSpan until, string reason = null)
- => this.TimeoutAsync(DateTimeOffset.UtcNow + until, reason);
+ /// <summary>
+ /// Gets whether the member has multi-factor authentication enabled.
+ /// </summary>
+ [JsonIgnore]
+ public override bool? MfaEnabled
+ {
+ get => this.User.MfaEnabled;
+ internal set => this.User.MfaEnabled = value;
+ }
- /// <summary>
- /// Adds a timeout to a member.
- /// </summary>
- /// <param name="until">The datetime to time out the user. Up to 28 days.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task TimeoutAsync(DateTime until, string reason = null)
- => this.TimeoutAsync(until.ToUniversalTime() - DateTime.UtcNow, reason);
+ /// <summary>
+ /// Gets whether the member is verified.
+ /// <para>This is only present in OAuth.</para>
+ /// </summary>
+ [JsonIgnore]
+ public override bool? Verified
+ {
+ get => this.User.Verified;
+ internal set => this.User.Verified = value;
+ }
- /// <summary>
- /// Removes the timeout from a member.
- /// </summary>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task RemoveTimeoutAsync(string reason = null) => this.Discord.ApiClient.ModifyTimeoutAsync(this.Guild.Id, this.Id, null, reason);
+ /// <summary>
+ /// Gets the member's chosen language
+ /// </summary>
+ [JsonIgnore]
+ public override string Locale
+ {
+ get => this.User.Locale;
+ internal set => this.User.Locale = value;
+ }
- /// <summary>
- /// Grants a role to the member.
- /// </summary>
- /// <param name="role">Role to grant.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task GrantRoleAsync(DiscordRole role, string reason = null)
- => this.Discord.ApiClient.AddGuildMemberRoleAsync(this.Guild.Id, this.Id, role.Id, reason);
+ /// <summary>
+ /// Gets the user's flags.
+ /// </summary>
+ [JsonIgnore]
+ public override UserFlags? OAuthFlags
+ {
+ get => this.User.OAuthFlags;
+ internal set => this.User.OAuthFlags = value;
+ }
- /// <summary>
- /// Revokes a role from a member.
- /// </summary>
- /// <param name="role">Role to revoke.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task RevokeRoleAsync(DiscordRole role, string reason = null)
- => this.Discord.ApiClient.RemoveGuildMemberRoleAsync(this.Guild.Id, this.Id, role.Id, reason);
+ /// <summary>
+ /// Gets the member's flags for OAuth.
+ /// </summary>
+ [JsonIgnore]
+ public override UserFlags? Flags
+ {
+ get => this.User.Flags;
+ internal set => this.User.Flags = value;
+ }
- /// <summary>
- /// Sets the member's roles to ones specified.
- /// </summary>
- /// <param name="roles">Roles to set.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ReplaceRolesAsync(IEnumerable<DiscordRole> roles, string reason = null)
- => this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, default,
- Optional.Some(roles.Select(xr => xr.Id)), default, default, default, reason);
+ #endregion
+
+ /// <summary>
+ /// Creates a direct message channel to this member.
+ /// </summary>
+ /// <returns>Direct message channel to this member.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordDmChannel> CreateDmChannelAsync()
+ => this.Discord.ApiClient.CreateDmAsync(this.Id);
+
+ /// <summary>
+ /// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
+ /// </summary>
+ /// <param name="content">Content of the message to send.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> SendMessageAsync(string content)
+ {
+ if (this.IsBot && this.Discord.CurrentUser.IsBot)
+ throw new ArgumentException("Bots cannot DM each other.");
- /// <summary>
- /// Bans this member from their guild.
- /// </summary>
- /// <param name="deleteMessageDays">How many days to remove messages from.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task BanAsync(int deleteMessageDays = 0, string reason = null)
- => this.Guild.BanMemberAsync(this, deleteMessageDays, reason);
+ var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
+ return await chn.SendMessageAsync(content).ConfigureAwait(false);
+ }
- /// <summary>
- /// Unbans this member from their guild.
- /// </summary>
- /// <exception cref = "Exceptions.UnauthorizedException" > Thrown when the client does not have the<see cref="Permissions.BanMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task UnbanAsync(string reason = null) => this.Guild.UnbanMemberAsync(this, reason);
+ /// <summary>
+ /// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
+ /// </summary>
+ /// <param name="embed">Embed to attach to the message.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> SendMessageAsync(DiscordEmbed embed)
+ {
+ if (this.IsBot && this.Discord.CurrentUser.IsBot)
+ throw new ArgumentException("Bots cannot DM each other.");
- /// <summary>
- /// Kicks this member from their guild.
- /// </summary>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns></returns>
- /// <remarks>[alias="KickAsync"]</remarks>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.KickMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task RemoveAsync(string reason = null)
- => this.Discord.ApiClient.RemoveGuildMemberAsync(this.GuildId, this.Id, reason);
+ var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
+ return await chn.SendMessageAsync(embed).ConfigureAwait(false);
+ }
- /// <summary>
- /// Moves this member to the specified voice channel
- /// </summary>
- /// <param name="channel"></param>
+ /// <summary>
+ /// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
+ /// </summary>
+ /// <param name="content">Content of the message to send.</param>
+ /// <param name="embed">Embed to attach to the message.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> SendMessageAsync(string content, DiscordEmbed embed)
+ {
+ if (this.IsBot && this.Discord.CurrentUser.IsBot)
+ throw new ArgumentException("Bots cannot DM each other.");
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MoveMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task PlaceInAsync(DiscordChannel channel)
- => channel.PlaceMemberAsync(this);
+ var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
+ return await chn.SendMessageAsync(content, embed).ConfigureAwait(false);
+ }
- /// <summary>
- /// Updates the member's suppress state in a stage channel.
- /// </summary>
- /// <param name="channel">The channel the member is currently in.</param>
- /// <param name="suppress">Toggles the member's suppress state.</param>
- /// <exception cref="System.ArgumentException">Thrown when the channel in not a voice channel.</exception>
- public async Task UpdateVoiceStateAsync(DiscordChannel channel, bool? suppress)
- {
- if (channel.Type != ChannelType.Stage)
- throw new ArgumentException("Voice state can only be updated in a stage channel.");
+ /// <summary>
+ /// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
+ /// </summary>
+ /// <param name="message">Builder to with the message.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> SendMessageAsync(DiscordMessageBuilder message)
+ {
+ if (this.IsBot && this.Discord.CurrentUser.IsBot)
+ throw new ArgumentException("Bots cannot DM each other.");
- await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, channel.Id, suppress).ConfigureAwait(false);
- }
+ var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
+ return await chn.SendMessageAsync(message).ConfigureAwait(false);
+ }
- /// <summary>
- /// Makes the user a speaker.
- /// </summary>
- /// <exception cref="System.ArgumentException">Thrown when the user is not inside an stage channel.</exception>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MuteMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task MakeSpeakerAsync()
- {
- var vs = this.VoiceState;
- if (vs == null || vs.Channel.Type != ChannelType.Stage)
- throw new ArgumentException("Voice state can only be updated when the user is inside an stage channel.");
+ /// <summary>
+ /// Sets this member's voice mute status.
+ /// </summary>
+ /// <param name="mute">Whether the member is to be muted.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MuteMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task SetMuteAsync(bool mute, string reason = null)
+ => this.Discord.ApiClient.ModifyGuildMemberAsync(this.GuildId, this.Id, default, default, mute, default, default, reason);
+
+ /// <summary>
+ /// Sets this member's voice deaf status.
+ /// </summary>
+ /// <param name="deaf">Whether the member is to be deafened.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.DeafenMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task SetDeafAsync(bool deaf, string reason = null)
+ => this.Discord.ApiClient.ModifyGuildMemberAsync(this.GuildId, this.Id, default, default, default, deaf, default, reason);
+
+ /// <summary>
+ /// Modifies this member.
+ /// </summary>
+ /// <param name="action">Action to perform on this member.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageNicknames"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task ModifyAsync(Action<MemberEditModel> action)
+ {
+ var mdl = new MemberEditModel();
+ action(mdl);
+
+ if (mdl.VoiceChannel.HasValue && mdl.VoiceChannel.Value != null && mdl.VoiceChannel.Value.Type != ChannelType.Voice && mdl.VoiceChannel.Value.Type != ChannelType.Stage)
+ throw new ArgumentException("Given channel is not a voice or stage channel.", nameof(mdl.VoiceChannel));
+
+ if (mdl.Nickname.HasValue && this.Discord.CurrentUser.Id == this.Id)
+ {
+ await this.Discord.ApiClient.ModifyCurrentMemberNicknameAsync(this.Guild.Id, mdl.Nickname.Value,
+ mdl.AuditLogReason).ConfigureAwait(false);
+
+ await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, Optional.None,
+ mdl.Roles.Map(e => e.Select(xr => xr.Id)), mdl.Muted, mdl.Deafened,
+ mdl.VoiceChannel.Map(e => e?.Id), mdl.AuditLogReason).ConfigureAwait(false);
+ }
+ else
+ {
+ await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, mdl.Nickname,
+ mdl.Roles.Map(e => e.Select(xr => xr.Id)), mdl.Muted, mdl.Deafened,
+ mdl.VoiceChannel.Map(e => e?.Id), mdl.AuditLogReason).ConfigureAwait(false);
+ }
+ }
- await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, vs.Channel.Id, false).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Adds a timeout to a member.
+ /// </summary>
+ /// <param name="until">The datetime offset to time out the user. Up to 28 days.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task TimeoutAsync(DateTimeOffset until, string reason = null)
+ => until.Subtract(DateTimeOffset.UtcNow).Days > 28 ? throw new ArgumentException("Timeout can not be longer than 28 days") : this.Discord.ApiClient.ModifyTimeoutAsync(this.Guild.Id, this.Id, until, reason);
+
+ /// <summary>
+ /// Adds a timeout to a member.
+ /// </summary>
+ /// <param name="until">The timespan to time out the user. Up to 28 days.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task TimeoutAsync(TimeSpan until, string reason = null)
+ => this.TimeoutAsync(DateTimeOffset.UtcNow + until, reason);
+
+ /// <summary>
+ /// Adds a timeout to a member.
+ /// </summary>
+ /// <param name="until">The datetime to time out the user. Up to 28 days.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task TimeoutAsync(DateTime until, string reason = null)
+ => this.TimeoutAsync(until.ToUniversalTime() - DateTime.UtcNow, reason);
+
+ /// <summary>
+ /// Removes the timeout from a member.
+ /// </summary>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ModerateMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task RemoveTimeoutAsync(string reason = null) => this.Discord.ApiClient.ModifyTimeoutAsync(this.Guild.Id, this.Id, null, reason);
+
+ /// <summary>
+ /// Grants a role to the member.
+ /// </summary>
+ /// <param name="role">Role to grant.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task GrantRoleAsync(DiscordRole role, string reason = null)
+ => this.Discord.ApiClient.AddGuildMemberRoleAsync(this.Guild.Id, this.Id, role.Id, reason);
+
+ /// <summary>
+ /// Revokes a role from a member.
+ /// </summary>
+ /// <param name="role">Role to revoke.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task RevokeRoleAsync(DiscordRole role, string reason = null)
+ => this.Discord.ApiClient.RemoveGuildMemberRoleAsync(this.Guild.Id, this.Id, role.Id, reason);
+
+ /// <summary>
+ /// Sets the member's roles to ones specified.
+ /// </summary>
+ /// <param name="roles">Roles to set.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ReplaceRolesAsync(IEnumerable<DiscordRole> roles, string reason = null)
+ => this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, default,
+ Optional.Some(roles.Select(xr => xr.Id)), default, default, default, reason);
+
+ /// <summary>
+ /// Bans this member from their guild.
+ /// </summary>
+ /// <param name="deleteMessageDays">How many days to remove messages from.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task BanAsync(int deleteMessageDays = 0, string reason = null)
+ => this.Guild.BanMemberAsync(this, deleteMessageDays, reason);
+
+ /// <summary>
+ /// Unbans this member from their guild.
+ /// </summary>
+ /// <exception cref = "Exceptions.UnauthorizedException" > Thrown when the client does not have the<see cref="Permissions.BanMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task UnbanAsync(string reason = null) => this.Guild.UnbanMemberAsync(this, reason);
+
+ /// <summary>
+ /// Kicks this member from their guild.
+ /// </summary>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns></returns>
+ /// <remarks>[alias="KickAsync"]</remarks>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.KickMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task RemoveAsync(string reason = null)
+ => this.Discord.ApiClient.RemoveGuildMemberAsync(this.GuildId, this.Id, reason);
+
+ /// <summary>
+ /// Moves this member to the specified voice channel
+ /// </summary>
+ /// <param name="channel"></param>
+
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MoveMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task PlaceInAsync(DiscordChannel channel)
+ => channel.PlaceMemberAsync(this);
+
+ /// <summary>
+ /// Updates the member's suppress state in a stage channel.
+ /// </summary>
+ /// <param name="channel">The channel the member is currently in.</param>
+ /// <param name="suppress">Toggles the member's suppress state.</param>
+ /// <exception cref="System.ArgumentException">Thrown when the channel in not a voice channel.</exception>
+ public async Task UpdateVoiceStateAsync(DiscordChannel channel, bool? suppress)
+ {
+ if (channel.Type != ChannelType.Stage)
+ throw new ArgumentException("Voice state can only be updated in a stage channel.");
- /// <summary>
- /// Moves the user to audience.
- /// </summary>
- /// <exception cref="System.ArgumentException">Thrown when the user is not inside an stage channel.</exception>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MuteMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task MoveToAudienceAsync()
- {
- var vs = this.VoiceState;
- if (vs == null || vs.Channel.Type != ChannelType.Stage)
- throw new ArgumentException("Voice state can only be updated when the user is inside an stage channel.");
+ await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, channel.Id, suppress).ConfigureAwait(false);
+ }
- await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, vs.Channel.Id, true).ConfigureAwait(false);
- }
+ /// <summary>
+ /// Makes the user a speaker.
+ /// </summary>
+ /// <exception cref="System.ArgumentException">Thrown when the user is not inside an stage channel.</exception>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MuteMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task MakeSpeakerAsync()
+ {
+ var vs = this.VoiceState;
+ if (vs == null || vs.Channel.Type != ChannelType.Stage)
+ throw new ArgumentException("Voice state can only be updated when the user is inside an stage channel.");
- /// <summary>
- /// Calculates permissions in a given channel for this member.
- /// </summary>
- /// <param name="channel">Channel to calculate permissions for.</param>
- /// <returns>Calculated permissions for this member in the channel.</returns>
- public Permissions PermissionsIn(DiscordChannel channel)
- => channel.PermissionsFor(this);
+ await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, vs.Channel.Id, false).ConfigureAwait(false);
+ }
- /// <summary>
- /// Get's the current member's roles based on the sum of the permissions of their given roles.
- /// </summary>
- private Permissions GetPermissions()
- {
- if (this.Guild.OwnerId == this.Id)
- return PermissionMethods.FullPerms;
+ /// <summary>
+ /// Moves the user to audience.
+ /// </summary>
+ /// <exception cref="System.ArgumentException">Thrown when the user is not inside an stage channel.</exception>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.MuteMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task MoveToAudienceAsync()
+ {
+ var vs = this.VoiceState;
+ if (vs == null || vs.Channel.Type != ChannelType.Stage)
+ throw new ArgumentException("Voice state can only be updated when the user is inside an stage channel.");
- Permissions perms;
+ await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, vs.Channel.Id, true).ConfigureAwait(false);
+ }
- // assign @everyone permissions
- var everyoneRole = this.Guild.EveryoneRole;
- perms = everyoneRole.Permissions;
+ /// <summary>
+ /// Calculates permissions in a given channel for this member.
+ /// </summary>
+ /// <param name="channel">Channel to calculate permissions for.</param>
+ /// <returns>Calculated permissions for this member in the channel.</returns>
+ public Permissions PermissionsIn(DiscordChannel channel)
+ => channel.PermissionsFor(this);
+
+ /// <summary>
+ /// Get's the current member's roles based on the sum of the permissions of their given roles.
+ /// </summary>
+ private Permissions GetPermissions()
+ {
+ if (this.Guild.OwnerId == this.Id)
+ return PermissionMethods.FullPerms;
- // assign permissions from member's roles (in order)
- perms |= this.Roles.Aggregate(Permissions.None, (c, role) => c | role.Permissions);
+ Permissions perms;
- // Administrator grants all permissions and cannot be overridden
- return (perms & Permissions.Administrator) == Permissions.Administrator ? PermissionMethods.FullPerms : perms;
- }
+ // assign @everyone permissions
+ var everyoneRole = this.Guild.EveryoneRole;
+ perms = everyoneRole.Permissions;
- /// <summary>
- /// Returns a string representation of this member.
- /// </summary>
- /// <returns>String representation of this member.</returns>
- public override string ToString()
- => $"Member {this.Id}; {this.Username}#{this.Discriminator} ({this.DisplayName})";
+ // assign permissions from member's roles (in order)
+ perms |= this.Roles.Aggregate(Permissions.None, (c, role) => c | role.Permissions);
- /// <summary>
- /// Checks whether this <see cref="DiscordMember"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordMember"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordMember);
+ // Administrator grants all permissions and cannot be overridden
+ return (perms & Permissions.Administrator) == Permissions.Administrator ? PermissionMethods.FullPerms : perms;
+ }
- /// <summary>
- /// Checks whether this <see cref="DiscordMember"/> is equal to another <see cref="DiscordMember"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordMember"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordMember"/> is equal to this <see cref="DiscordMember"/>.</returns>
- public bool Equals(DiscordMember e)
- => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.GuildId == e.GuildId));
+ /// <summary>
+ /// Returns a string representation of this member.
+ /// </summary>
+ /// <returns>String representation of this member.</returns>
+ public override string ToString()
+ => $"Member {this.Id}; {this.Username}#{this.Discriminator} ({this.DisplayName})";
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordMember"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordMember"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordMember);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordMember"/> is equal to another <see cref="DiscordMember"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordMember"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordMember"/> is equal to this <see cref="DiscordMember"/>.</returns>
+ public bool Equals(DiscordMember e)
+ => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.GuildId == e.GuildId));
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordMember"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordMember"/>.</returns>
+ public override int GetHashCode()
+ {
+ var hash = 13;
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordMember"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordMember"/>.</returns>
- public override int GetHashCode()
- {
- var hash = 13;
+ hash = (hash * 7) + this.Id.GetHashCode();
+ hash = (hash * 7) + this.GuildId.GetHashCode();
- hash = (hash * 7) + this.Id.GetHashCode();
- hash = (hash * 7) + this.GuildId.GetHashCode();
+ return hash;
+ }
- return hash;
- }
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordMember"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First member to compare.</param>
+ /// <param name="e2">Second member to compare.</param>
+ /// <returns>Whether the two members are equal.</returns>
+ public static bool operator ==(DiscordMember e1, DiscordMember e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- /// <summary>
- /// Gets whether the two <see cref="DiscordMember"/> objects are equal.
- /// </summary>
- /// <param name="e1">First member to compare.</param>
- /// <param name="e2">Second member to compare.</param>
- /// <returns>Whether the two members are equal.</returns>
- public static bool operator ==(DiscordMember e1, DiscordMember e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || (e1.Id == e2.Id && e1.GuildId == e2.GuildId));
+ }
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || (e1.Id == e2.Id && e1.GuildId == e2.GuildId));
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordMember"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First member to compare.</param>
+ /// <param name="e2">Second member to compare.</param>
+ /// <returns>Whether the two members are not equal.</returns>
+ public static bool operator !=(DiscordMember e1, DiscordMember e2)
+ => !(e1 == e2);
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordMember"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First member to compare.</param>
- /// <param name="e2">Second member to compare.</param>
- /// <returns>Whether the two members are not equal.</returns>
- public static bool operator !=(DiscordMember e1, DiscordMember e2)
- => !(e1 == e2);
}
diff --git a/DisCatSharp/Entities/Guild/DiscordRole.cs b/DisCatSharp/Entities/Guild/DiscordRole.cs
index 8bb6aaa92..df94d8bcb 100644
--- a/DisCatSharp/Entities/Guild/DiscordRole.cs
+++ b/DisCatSharp/Entities/Guild/DiscordRole.cs
@@ -1,283 +1,284 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Models;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord role, to which users can be assigned.
-/// </summary>
-public class DiscordRole : SnowflakeObject, IEquatable<DiscordRole>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of this role.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the color of this role.
- /// </summary>
- [JsonIgnore]
- public DiscordColor Color
- => new(this.ColorInternal);
-
- [JsonProperty("color", NullValueHandling = NullValueHandling.Ignore)]
- internal int ColorInternal;
-
- /// <summary>
- /// Gets whether this role is hoisted.
- /// </summary>
- [JsonProperty("hoist", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsHoisted { get; internal set; }
-
- /// <summary>
- /// Gets the position of this role in the role hierarchy.
+ /// Represents a discord role, to which users can be assigned.
/// </summary>
- [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
- public int Position { get; internal set; }
-
- /// <summary>
- /// Gets the permissions set for this role.
- /// </summary>
- [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions Permissions { get; internal set; }
-
- /// <summary>
- /// Gets whether this role is managed by an integration.
- /// </summary>
- [JsonProperty("managed", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsManaged { get; internal set; }
-
- /// <summary>
- /// Gets whether this role is mentionable.
- /// </summary>
- [JsonProperty("mentionable", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsMentionable { get; internal set; }
-
- /// <summary>
- /// Gets the tags this role has.
- /// </summary>
- [JsonProperty("tags", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordRoleTags Tags { get; internal set; }
-
- /// <summary>
- /// Gets the role icon's hash.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
- public string IconHash { get; internal set; }
-
- /// <summary>
- /// Gets the role icon's url.
- /// </summary>
- [JsonIgnore]
- public string IconUrl
- => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ROLE_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png?size=64" : null;
-
- /// <summary>
- /// Gets the role unicode_emoji.
- /// </summary>
- [JsonProperty("unicode_emoji", NullValueHandling = NullValueHandling.Ignore)]
- internal string UnicodeEmojiString;
-
- /// <summary>
- /// Gets the unicode emoji.
- /// </summary>
- public DiscordEmoji UnicodeEmoji
- => this.UnicodeEmojiString != null ? DiscordEmoji.FromName(this.Discord, $":{this.UnicodeEmojiString}:", false) : null;
-
- [JsonIgnore]
- internal ulong GuildId = 0;
-
- /// <summary>
- /// Gets the role flags.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public RoleFlags Flags { get; internal set; }
-
- /// <summary>
- /// Gets a mention string for this role. If the role is mentionable, this string will mention all the users that belong to this role.
- /// </summary>
- public string Mention
- => Formatter.Mention(this);
-
- #region Methods
-
- /// <summary>
- /// Modifies this role's position.
- /// </summary>
- /// <param name="position">New position</param>
- /// <param name="reason">Reason why we moved it</param>
- /// <returns></returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ModifyPositionAsync(int position, string reason = null)
+ public class DiscordRole : SnowflakeObject, IEquatable<DiscordRole>
{
- var roles = this.Discord.Guilds[this.GuildId].Roles.Values.OrderByDescending(xr => xr.Position).ToArray();
- var pmds = new RestGuildRoleReorderPayload[roles.Length];
- for (var i = 0; i < roles.Length; i++)
+ /// <summary>
+ /// Gets the name of this role.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the color of this role.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordColor Color
+ => new(this.ColorInternal);
+
+ [JsonProperty("color", NullValueHandling = NullValueHandling.Ignore)]
+ internal int ColorInternal;
+
+ /// <summary>
+ /// Gets whether this role is hoisted.
+ /// </summary>
+ [JsonProperty("hoist", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsHoisted { get; internal set; }
+
+ /// <summary>
+ /// Gets the position of this role in the role hierarchy.
+ /// </summary>
+ [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
+ public int Position { get; internal set; }
+
+ /// <summary>
+ /// Gets the permissions set for this role.
+ /// </summary>
+ [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions Permissions { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this role is managed by an integration.
+ /// </summary>
+ [JsonProperty("managed", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsManaged { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this role is mentionable.
+ /// </summary>
+ [JsonProperty("mentionable", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsMentionable { get; internal set; }
+
+ /// <summary>
+ /// Gets the tags this role has.
+ /// </summary>
+ [JsonProperty("tags", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordRoleTags Tags { get; internal set; }
+
+ /// <summary>
+ /// Gets the role icon's hash.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
+ public string IconHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the role icon's url.
+ /// </summary>
+ [JsonIgnore]
+ public string IconUrl
+ => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ROLE_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png?size=64" : null;
+
+ /// <summary>
+ /// Gets the role unicode_emoji.
+ /// </summary>
+ [JsonProperty("unicode_emoji", NullValueHandling = NullValueHandling.Ignore)]
+ internal string UnicodeEmojiString;
+
+ /// <summary>
+ /// Gets the unicode emoji.
+ /// </summary>
+ public DiscordEmoji UnicodeEmoji
+ => this.UnicodeEmojiString != null ? DiscordEmoji.FromName(this.Discord, $":{this.UnicodeEmojiString}:", false) : null;
+
+ [JsonIgnore]
+ internal ulong GuildId = 0;
+
+ /// <summary>
+ /// Gets the role flags.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public RoleFlags Flags { get; internal set; }
+
+ /// <summary>
+ /// Gets a mention string for this role. If the role is mentionable, this string will mention all the users that belong to this role.
+ /// </summary>
+ public string Mention
+ => Formatter.Mention(this);
+
+ #region Methods
+
+ /// <summary>
+ /// Modifies this role's position.
+ /// </summary>
+ /// <param name="position">New position</param>
+ /// <param name="reason">Reason why we moved it</param>
+ /// <returns></returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ModifyPositionAsync(int position, string reason = null)
{
- pmds[i] = new RestGuildRoleReorderPayload { RoleId = roles[i].Id };
+ var roles = this.Discord.Guilds[this.GuildId].Roles.Values.OrderByDescending(xr => xr.Position).ToArray();
+ var pmds = new RestGuildRoleReorderPayload[roles.Length];
+ for (var i = 0; i < roles.Length; i++)
+ {
+ pmds[i] = new RestGuildRoleReorderPayload { RoleId = roles[i].Id };
- pmds[i].Position = roles[i].Id == this.Id ? position : roles[i].Position <= position ? roles[i].Position - 1 : roles[i].Position;
+ pmds[i].Position = roles[i].Id == this.Id ? position : roles[i].Position <= position ? roles[i].Position - 1 : roles[i].Position;
+ }
+
+ return this.Discord.ApiClient.ModifyGuildRolePositionAsync(this.GuildId, pmds, reason);
}
- return this.Discord.ApiClient.ModifyGuildRolePositionAsync(this.GuildId, pmds, reason);
- }
+ /// <summary>
+ /// Updates this role.
+ /// </summary>
+ /// <param name="name">New role name.</param>
+ /// <param name="permissions">New role permissions.</param>
+ /// <param name="color">New role color.</param>
+ /// <param name="hoist">New role hoist.</param>
+ /// <param name="mentionable">Whether this role is mentionable.</param>
+ /// <param name="reason">Audit log reason.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ModifyAsync(string name = null, Permissions? permissions = null, DiscordColor? color = null, bool? hoist = null, bool? mentionable = null, string reason = null)
+ => this.Discord.ApiClient.ModifyGuildRoleAsync(this.GuildId, this.Id, name, permissions, color?.Value, hoist, mentionable, null, null, reason);
+
+ /// <summary>
+ /// Updates this role.
+ /// </summary>
+ /// <param name="action">The action.</param>
+ /// <exception cref = "Exceptions.UnauthorizedException" > Thrown when the client does not have the<see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ModifyAsync(Action<RoleEditModel> action)
+ {
+ var mdl = new RoleEditModel();
+ action(mdl);
- /// <summary>
- /// Updates this role.
- /// </summary>
- /// <param name="name">New role name.</param>
- /// <param name="permissions">New role permissions.</param>
- /// <param name="color">New role color.</param>
- /// <param name="hoist">New role hoist.</param>
- /// <param name="mentionable">Whether this role is mentionable.</param>
- /// <param name="reason">Audit log reason.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ModifyAsync(string name = null, Permissions? permissions = null, DiscordColor? color = null, bool? hoist = null, bool? mentionable = null, string reason = null)
- => this.Discord.ApiClient.ModifyGuildRoleAsync(this.GuildId, this.Id, name, permissions, color?.Value, hoist, mentionable, null, null, reason);
+ var canContinue = true;
+ if (mdl.Icon.HasValue || mdl.UnicodeEmoji.HasValue)
+ canContinue = this.Discord.Guilds[this.GuildId].Features.CanSetRoleIcons;
- /// <summary>
- /// Updates this role.
- /// </summary>
- /// <param name="action">The action.</param>
- /// <exception cref = "Exceptions.UnauthorizedException" > Thrown when the client does not have the<see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ModifyAsync(Action<RoleEditModel> action)
- {
- var mdl = new RoleEditModel();
- action(mdl);
+ var iconb64 = ImageTool.Base64FromStream(mdl.Icon);
- var canContinue = true;
- if (mdl.Icon.HasValue || mdl.UnicodeEmoji.HasValue)
- canContinue = this.Discord.Guilds[this.GuildId].Features.CanSetRoleIcons;
+ var emoji = mdl.UnicodeEmoji
+ .MapOrNull(e => e.Id == 0
+ ? e.Name
+ : throw new ArgumentException("Emoji must be unicode"));
- var iconb64 = ImageTool.Base64FromStream(mdl.Icon);
-
- var emoji = mdl.UnicodeEmoji
- .MapOrNull(e => e.Id == 0
- ? e.Name
- : throw new ArgumentException("Emoji must be unicode"));
+ return canContinue ? this.Discord.ApiClient.ModifyGuildRoleAsync(this.GuildId, this.Id, mdl.Name, mdl.Permissions, mdl.Color?.Value, mdl.Hoist, mdl.Mentionable, iconb64, emoji, mdl.AuditLogReason) : throw new NotSupportedException($"Cannot modify role icon. Guild needs boost tier two.");
+ }
- return canContinue ? this.Discord.ApiClient.ModifyGuildRoleAsync(this.GuildId, this.Id, mdl.Name, mdl.Permissions, mdl.Color?.Value, mdl.Hoist, mdl.Mentionable, iconb64, emoji, mdl.AuditLogReason) : throw new NotSupportedException($"Cannot modify role icon. Guild needs boost tier two.");
+ /// <summary>
+ /// Deletes this role.
+ /// </summary>
+ /// <param name="reason">Reason as to why this role has been deleted.</param>
+ /// <returns></returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteAsync(string reason = null)
+ => this.Discord.ApiClient.DeleteRoleAsync(this.GuildId, this.Id, reason);
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordRole"/> class.
+ /// </summary>
+ internal DiscordRole()
+ { }
+
+ /// <summary>
+ /// Checks whether this role has specific permissions.
+ /// </summary>
+ /// <param name="permission">Permissions to check for.</param>
+ /// <returns>Whether the permissions are allowed or not.</returns>
+ public PermissionLevel CheckPermission(Permissions permission)
+ => (this.Permissions & permission) != 0 ? PermissionLevel.Allowed : PermissionLevel.Unset;
+
+ /// <summary>
+ /// Returns a string representation of this role.
+ /// </summary>
+ /// <returns>String representation of this role.</returns>
+ public override string ToString()
+ => $"Role {this.Id}; {this.Name}";
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordRole"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordRole"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordRole);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordRole"/> is equal to another <see cref="DiscordRole"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordRole"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordRole"/> is equal to this <see cref="DiscordRole"/>.</returns>
+ public bool Equals(DiscordRole e)
+ => e switch
+ {
+ null => false,
+ _ => ReferenceEquals(this, e) || this.Id == e.Id
+ };
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordRole"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordRole"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordRole"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First role to compare.</param>
+ /// <param name="e2">Second role to compare.</param>
+ /// <returns>Whether the two roles are equal.</returns>
+ public static bool operator ==(DiscordRole e1, DiscordRole e2)
+ => e1 is null == e2 is null
+ && ((e1 is null && e2 is null) || e1.Id == e2.Id);
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordRole"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First role to compare.</param>
+ /// <param name="e2">Second role to compare.</param>
+ /// <returns>Whether the two roles are not equal.</returns>
+ public static bool operator !=(DiscordRole e1, DiscordRole e2)
+ => !(e1 == e2);
}
-
- /// <summary>
- /// Deletes this role.
- /// </summary>
- /// <param name="reason">Reason as to why this role has been deleted.</param>
- /// <returns></returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageRoles"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the role does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteAsync(string reason = null)
- => this.Discord.ApiClient.DeleteRoleAsync(this.GuildId, this.Id, reason);
-
- #endregion
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordRole"/> class.
- /// </summary>
- internal DiscordRole()
- { }
-
- /// <summary>
- /// Checks whether this role has specific permissions.
- /// </summary>
- /// <param name="permission">Permissions to check for.</param>
- /// <returns>Whether the permissions are allowed or not.</returns>
- public PermissionLevel CheckPermission(Permissions permission)
- => (this.Permissions & permission) != 0 ? PermissionLevel.Allowed : PermissionLevel.Unset;
-
- /// <summary>
- /// Returns a string representation of this role.
- /// </summary>
- /// <returns>String representation of this role.</returns>
- public override string ToString()
- => $"Role {this.Id}; {this.Name}";
-
- /// <summary>
- /// Checks whether this <see cref="DiscordRole"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordRole"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordRole);
-
- /// <summary>
- /// Checks whether this <see cref="DiscordRole"/> is equal to another <see cref="DiscordRole"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordRole"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordRole"/> is equal to this <see cref="DiscordRole"/>.</returns>
- public bool Equals(DiscordRole e)
- => e switch
- {
- null => false,
- _ => ReferenceEquals(this, e) || this.Id == e.Id
- };
-
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordRole"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordRole"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordRole"/> objects are equal.
- /// </summary>
- /// <param name="e1">First role to compare.</param>
- /// <param name="e2">Second role to compare.</param>
- /// <returns>Whether the two roles are equal.</returns>
- public static bool operator ==(DiscordRole e1, DiscordRole e2)
- => e1 is null == e2 is null
- && ((e1 is null && e2 is null) || e1.Id == e2.Id);
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordRole"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First role to compare.</param>
- /// <param name="e2">Second role to compare.</param>
- /// <returns>Whether the two roles are not equal.</returns>
- public static bool operator !=(DiscordRole e1, DiscordRole e2)
- => !(e1 == e2);
}
diff --git a/DisCatSharp/Entities/Guild/DiscordRoleTags.cs b/DisCatSharp/Entities/Guild/DiscordRoleTags.cs
index 6d3c30c29..b0eb438cc 100644
--- a/DisCatSharp/Entities/Guild/DiscordRoleTags.cs
+++ b/DisCatSharp/Entities/Guild/DiscordRoleTags.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord role tags.
-/// </summary>
-public class DiscordRoleTags
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the id of the bot this role belongs to.
+ /// Represents a discord role tags.
/// </summary>
- [JsonProperty("bot_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? BotId { get; internal set; }
+ public class DiscordRoleTags
+ {
+ /// <summary>
+ /// Gets the id of the bot this role belongs to.
+ /// </summary>
+ [JsonProperty("bot_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? BotId { get; internal set; }
- /// <summary>
- /// Gets the id of the integration this role belongs to.
- /// </summary>
- [JsonProperty("integration_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? IntegrationId { get; internal set; }
+ /// <summary>
+ /// Gets the id of the integration this role belongs to.
+ /// </summary>
+ [JsonProperty("integration_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? IntegrationId { get; internal set; }
- /// <summary>
- /// Gets whether this is the guild's premium subscriber role.
- /// </summary>
- [JsonIgnore]
- public bool IsPremiumSubscriber
- => this.PremiumSubscriber.HasValue && this.PremiumSubscriber.Value is null;
+ /// <summary>
+ /// Gets whether this is the guild's premium subscriber role.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsPremiumSubscriber
+ => this.PremiumSubscriber.HasValue && this.PremiumSubscriber.Value is null;
- [JsonProperty("premium_subscriber", NullValueHandling = NullValueHandling.Include)]
- internal Optional<bool?> PremiumSubscriber = false;
+ [JsonProperty("premium_subscriber", NullValueHandling = NullValueHandling.Include)]
+ internal Optional<bool?> PremiumSubscriber = false;
+ }
}
diff --git a/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEvent.cs b/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEvent.cs
index ac14114d3..90da21c13 100644
--- a/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEvent.cs
+++ b/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEvent.cs
@@ -1,327 +1,328 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Models;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an scheduled event.
-/// </summary>
-public class DiscordScheduledEvent : SnowflakeObject, IEquatable<DiscordScheduledEvent>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the guild id of the associated scheduled event.
- /// </summary>
- [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong GuildId { get; internal set; }
-
- /// <summary>
- /// Gets the guild to which this scheduled event belongs.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild
- => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) ? guild : null;
-
- /// <summary>
- /// Gets the associated channel.
- /// </summary>
- [JsonIgnore]
- public Task<DiscordChannel> Channel
- => this.ChannelId.HasValue ? this.Discord.ApiClient.GetChannelAsync(this.ChannelId.Value) : null;
-
- /// <summary>
- /// Gets id of the associated channel id.
- /// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? ChannelId { get; internal set; }
-
- /// <summary>
- /// Gets the ID of the user that created the scheduled event.
+ /// Represents an scheduled event.
/// </summary>
- [JsonProperty("creator_id")]
- public ulong CreatorId { get; internal set; }
-
- /// <summary>
- /// Gets the user that created the scheduled event.
- /// </summary>
- [JsonProperty("creator")]
- public DiscordUser Creator { get; internal set; }
-
- /// <summary>
- /// Gets the member that created the scheduled event.
- /// </summary>
- [JsonIgnore]
- public DiscordMember CreatorMember
- => this.Guild.MembersInternal.TryGetValue(this.CreatorId, out var owner)
- ? owner
- : this.Discord.ApiClient.GetGuildMemberAsync(this.GuildId, this.CreatorId).Result;
-
- /// <summary>
- /// Gets the name of the scheduled event.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the description of the scheduled event.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
+ public class DiscordScheduledEvent : SnowflakeObject, IEquatable<DiscordScheduledEvent>
+ {
+ /// <summary>
+ /// Gets the guild id of the associated scheduled event.
+ /// </summary>
+ [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong GuildId { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild to which this scheduled event belongs.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild
+ => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) ? guild : null;
+
+ /// <summary>
+ /// Gets the associated channel.
+ /// </summary>
+ [JsonIgnore]
+ public Task<DiscordChannel> Channel
+ => this.ChannelId.HasValue ? this.Discord.ApiClient.GetChannelAsync(this.ChannelId.Value) : null;
+
+ /// <summary>
+ /// Gets id of the associated channel id.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? ChannelId { get; internal set; }
+
+ /// <summary>
+ /// Gets the ID of the user that created the scheduled event.
+ /// </summary>
+ [JsonProperty("creator_id")]
+ public ulong CreatorId { get; internal set; }
+
+ /// <summary>
+ /// Gets the user that created the scheduled event.
+ /// </summary>
+ [JsonProperty("creator")]
+ public DiscordUser Creator { get; internal set; }
+
+ /// <summary>
+ /// Gets the member that created the scheduled event.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordMember CreatorMember
+ => this.Guild.MembersInternal.TryGetValue(this.CreatorId, out var owner)
+ ? owner
+ : this.Discord.ApiClient.GetGuildMemberAsync(this.GuildId, this.CreatorId).Result;
+
+ /// <summary>
+ /// Gets the name of the scheduled event.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the description of the scheduled event.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
/// <summary>
/// Gets this event's cover hash, when applicable.
/// </summary>
[JsonProperty("image", NullValueHandling = NullValueHandling.Include)]
public string CoverImageHash { get; internal set; }
/// <summary>
/// Gets this event's cover in url form.
/// </summary>
[JsonIgnore]
public string CoverImageUrl
=> !string.IsNullOrWhiteSpace(this.CoverImageHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Uri}{Endpoints.GUILD_EVENTS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.CoverImageHash}.png" : null;
- /// <summary>
- /// Gets the scheduled start time of the scheduled event.
- /// </summary>
- [JsonIgnore]
- public DateTimeOffset? ScheduledStartTime
- => !string.IsNullOrWhiteSpace(this.ScheduledStartTimeRaw) && DateTimeOffset.TryParse(this.ScheduledStartTimeRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
- dto : null;
-
- /// <summary>
- /// Gets the scheduled start time of the scheduled event as raw string.
- /// </summary>
- [JsonProperty("scheduled_start_time", NullValueHandling = NullValueHandling.Ignore)]
- internal string ScheduledStartTimeRaw { get; set; }
-
- /// <summary>
- /// Gets the scheduled end time of the scheduled event.
- /// </summary>
- [JsonIgnore]
- public DateTimeOffset? ScheduledEndTime
- => !string.IsNullOrWhiteSpace(this.ScheduledEndTimeRaw) && DateTimeOffset.TryParse(this.ScheduledEndTimeRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
- dto : null;
-
- /// <summary>
- /// Gets the scheduled end time of the scheduled event as raw string.
- /// </summary>
- [JsonProperty("scheduled_end_time", NullValueHandling = NullValueHandling.Ignore)]
- internal string ScheduledEndTimeRaw { get; set; }
-
- /// <summary>
- /// Gets the privacy level of the scheduled event.
- /// </summary>
- [JsonProperty("privacy_level", NullValueHandling = NullValueHandling.Ignore)]
- internal ScheduledEventPrivacyLevel PrivacyLevel { get; set; }
-
- /// <summary>
- /// Gets the status of the scheduled event.
- /// </summary>
- [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
- public ScheduledEventStatus Status { get; internal set; }
-
- /// <summary>
- /// Gets the entity type.
- /// </summary>
- [JsonProperty("entity_type", NullValueHandling = NullValueHandling.Ignore)]
- public ScheduledEventEntityType EntityType { get; internal set; }
-
- /// <summary>
- /// Gets id of the entity.
- /// </summary>
- [JsonProperty("entity_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? EntityId { get; internal set; }
-
- /// <summary>
- /// Gets metadata of the entity.
- /// </summary>
- [JsonProperty("entity_metadata", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordScheduledEventEntityMetadata EntityMetadata { get; internal set; }
-
- /* This isn't used.
+ /// <summary>
+ /// Gets the scheduled start time of the scheduled event.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset? ScheduledStartTime
+ => !string.IsNullOrWhiteSpace(this.ScheduledStartTimeRaw) && DateTimeOffset.TryParse(this.ScheduledStartTimeRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
+ dto : null;
+
+ /// <summary>
+ /// Gets the scheduled start time of the scheduled event as raw string.
+ /// </summary>
+ [JsonProperty("scheduled_start_time", NullValueHandling = NullValueHandling.Ignore)]
+ internal string ScheduledStartTimeRaw { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled end time of the scheduled event.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset? ScheduledEndTime
+ => !string.IsNullOrWhiteSpace(this.ScheduledEndTimeRaw) && DateTimeOffset.TryParse(this.ScheduledEndTimeRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
+ dto : null;
+
+ /// <summary>
+ /// Gets the scheduled end time of the scheduled event as raw string.
+ /// </summary>
+ [JsonProperty("scheduled_end_time", NullValueHandling = NullValueHandling.Ignore)]
+ internal string ScheduledEndTimeRaw { get; set; }
+
+ /// <summary>
+ /// Gets the privacy level of the scheduled event.
+ /// </summary>
+ [JsonProperty("privacy_level", NullValueHandling = NullValueHandling.Ignore)]
+ internal ScheduledEventPrivacyLevel PrivacyLevel { get; set; }
+
+ /// <summary>
+ /// Gets the status of the scheduled event.
+ /// </summary>
+ [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
+ public ScheduledEventStatus Status { get; internal set; }
+
+ /// <summary>
+ /// Gets the entity type.
+ /// </summary>
+ [JsonProperty("entity_type", NullValueHandling = NullValueHandling.Ignore)]
+ public ScheduledEventEntityType EntityType { get; internal set; }
+
+ /// <summary>
+ /// Gets id of the entity.
+ /// </summary>
+ [JsonProperty("entity_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? EntityId { get; internal set; }
+
+ /// <summary>
+ /// Gets metadata of the entity.
+ /// </summary>
+ [JsonProperty("entity_metadata", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordScheduledEventEntityMetadata EntityMetadata { get; internal set; }
+
+ /* This isn't used.
* See https://github.com/discord/discord-api-docs/pull/3586#issuecomment-969066061.
* Was originally for paid stages.
/// <summary>
/// Gets the sku ids of the scheduled event.
/// </summary>
[JsonProperty("sku_ids", NullValueHandling = NullValueHandling.Ignore)]
public IReadOnlyList<ulong> SkuIds { get; internal set; }*/
- /// <summary>
- /// Gets the total number of users subscribed to the scheduled event.
- /// </summary>
- [JsonProperty("user_count", NullValueHandling = NullValueHandling.Ignore)]
- public int UserCount { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordScheduledEvent"/> class.
- /// </summary>
- internal DiscordScheduledEvent()
- { }
-
- #region Methods
-
- /// <summary>
- /// Modifies the current scheduled event.
- /// </summary>
- /// <param name="action">Action to perform on this thread</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task ModifyAsync(Action<ScheduledEventEditModel> action)
- {
- var mdl = new ScheduledEventEditModel();
- action(mdl);
-
- Optional<ulong?> channelId = null;
- if (this.EntityType == ScheduledEventEntityType.External || mdl.EntityType != ScheduledEventEntityType.External)
- channelId = mdl.Channel
- .MapOrNull<ulong?>(c => c.Type != ChannelType.Voice && c.Type != ChannelType.Stage
- ? throw new ArgumentException("Channel needs to be a voice or stage channel.")
- : c.Id);
-
- var coverb64 = ImageTool.Base64FromStream(mdl.CoverImage);
-
- var scheduledEndTime = Optional<DateTimeOffset>.None;
- if (mdl.ScheduledEndTime.HasValue && mdl.EntityType.HasValue ? mdl.EntityType == ScheduledEventEntityType.External : this.EntityType == ScheduledEventEntityType.External)
- scheduledEndTime = mdl.ScheduledEndTime.Value;
-
- await this.Discord.ApiClient.ModifyGuildScheduledEventAsync(this.GuildId, this.Id, channelId, this.EntityType == ScheduledEventEntityType.External ? new DiscordScheduledEventEntityMetadata(mdl.Location.Value) : null, mdl.Name, mdl.ScheduledStartTime, scheduledEndTime, mdl.Description, mdl.EntityType, mdl.Status, coverb64, mdl.AuditLogReason);
- }
-
- /// <summary>
- /// Starts the current scheduled event.
- /// </summary>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordScheduledEvent> StartAsync(string reason = null)
- => this.Status == ScheduledEventStatus.Scheduled ? await this.Discord.ApiClient.ModifyGuildScheduledEventStatusAsync(this.GuildId, this.Id, ScheduledEventStatus.Active, reason) : throw new InvalidOperationException("You can only start scheduled events");
-
- /// <summary>
- /// Cancels the current scheduled event.
- /// </summary>
- /// <param name="reason">The audit log reason.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordScheduledEvent> CancelAsync(string reason = null)
- => this.Status == ScheduledEventStatus.Scheduled ? await this.Discord.ApiClient.ModifyGuildScheduledEventStatusAsync(this.GuildId, this.Id, ScheduledEventStatus.Canceled, reason) : throw new InvalidOperationException("You can only cancel scheduled events");
-
- /// <summary>
- /// Ends the current scheduled event.
- /// </summary>
- /// <param name="reason">The audit log reason.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordScheduledEvent> EndAsync(string reason = null)
- => this.Status == ScheduledEventStatus.Active ? await this.Discord.ApiClient.ModifyGuildScheduledEventStatusAsync(this.GuildId, this.Id, ScheduledEventStatus.Completed, reason) : throw new InvalidOperationException("You can only stop active events");
-
- /// <summary>
- /// Gets a list of users RSVP'd to the scheduled event.
- /// </summary>
- /// <param name="limit">The limit how many users to receive from the event. Defaults to 100. Max 100.</param>
- /// <param name="before">Get results of <see cref="DiscordScheduledEventUser"/> before the given snowflake.</param>
- /// <param name="after">Get results of <see cref="DiscordScheduledEventUser"/> after the given snowflake.</param>
- /// <param name="withMember">Whether to include guild member data.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the correct permissions.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<IReadOnlyDictionary<ulong, DiscordScheduledEventUser>> GetUsersAsync(int? limit = null, ulong? before = null, ulong? after = null, bool? withMember = null)
- => await this.Discord.ApiClient.GetGuildScheduledEventRspvUsersAsync(this.GuildId, this.Id, limit, before, after, withMember);
-
- /// <summary>
- /// Deletes a scheduled event.
- /// </summary>
- /// <param name="reason">The audit log reason.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task DeleteAsync(string reason = null)
- => await this.Discord.ApiClient.DeleteGuildScheduledEventAsync(this.GuildId, this.Id, reason);
-
- #endregion
-
- /// <summary>
- /// Checks whether this <see cref="DiscordScheduledEvent"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordScheduledEvent"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordScheduledEvent);
-
- /// <summary>
- /// Checks whether this <see cref="DiscordScheduledEvent"/> is equal to another <see cref="DiscordScheduledEvent"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordScheduledEvent"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordScheduledEvent"/> is equal to this <see cref="DiscordScheduledEvent"/>.</returns>
- public bool Equals(DiscordScheduledEvent e)
- => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
-
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordScheduledEvent"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordScheduledEvent"/>.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordScheduledEvent"/> objects are equal.
- /// </summary>
- /// <param name="e1">First event to compare.</param>
- /// <param name="e2">Second event to compare.</param>
- /// <returns>Whether the two events are equal.</returns>
- public static bool operator ==(DiscordScheduledEvent e1, DiscordScheduledEvent e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
-
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ /// <summary>
+ /// Gets the total number of users subscribed to the scheduled event.
+ /// </summary>
+ [JsonProperty("user_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int UserCount { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordScheduledEvent"/> class.
+ /// </summary>
+ internal DiscordScheduledEvent()
+ { }
+
+ #region Methods
+
+ /// <summary>
+ /// Modifies the current scheduled event.
+ /// </summary>
+ /// <param name="action">Action to perform on this thread</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task ModifyAsync(Action<ScheduledEventEditModel> action)
+ {
+ var mdl = new ScheduledEventEditModel();
+ action(mdl);
+
+ Optional<ulong?> channelId = null;
+ if (this.EntityType == ScheduledEventEntityType.External || mdl.EntityType != ScheduledEventEntityType.External)
+ channelId = mdl.Channel
+ .MapOrNull<ulong?>(c => c.Type != ChannelType.Voice && c.Type != ChannelType.Stage
+ ? throw new ArgumentException("Channel needs to be a voice or stage channel.")
+ : c.Id);
+
+ var coverb64 = ImageTool.Base64FromStream(mdl.CoverImage);
+
+ var scheduledEndTime = Optional<DateTimeOffset>.None;
+ if (mdl.ScheduledEndTime.HasValue && mdl.EntityType.HasValue ? mdl.EntityType == ScheduledEventEntityType.External : this.EntityType == ScheduledEventEntityType.External)
+ scheduledEndTime = mdl.ScheduledEndTime.Value;
+
+ await this.Discord.ApiClient.ModifyGuildScheduledEventAsync(this.GuildId, this.Id, channelId, this.EntityType == ScheduledEventEntityType.External ? new DiscordScheduledEventEntityMetadata(mdl.Location.Value) : null, mdl.Name, mdl.ScheduledStartTime, scheduledEndTime, mdl.Description, mdl.EntityType, mdl.Status, coverb64, mdl.AuditLogReason);
+ }
+
+ /// <summary>
+ /// Starts the current scheduled event.
+ /// </summary>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordScheduledEvent> StartAsync(string reason = null)
+ => this.Status == ScheduledEventStatus.Scheduled ? await this.Discord.ApiClient.ModifyGuildScheduledEventStatusAsync(this.GuildId, this.Id, ScheduledEventStatus.Active, reason) : throw new InvalidOperationException("You can only start scheduled events");
+
+ /// <summary>
+ /// Cancels the current scheduled event.
+ /// </summary>
+ /// <param name="reason">The audit log reason.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordScheduledEvent> CancelAsync(string reason = null)
+ => this.Status == ScheduledEventStatus.Scheduled ? await this.Discord.ApiClient.ModifyGuildScheduledEventStatusAsync(this.GuildId, this.Id, ScheduledEventStatus.Canceled, reason) : throw new InvalidOperationException("You can only cancel scheduled events");
+
+ /// <summary>
+ /// Ends the current scheduled event.
+ /// </summary>
+ /// <param name="reason">The audit log reason.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordScheduledEvent> EndAsync(string reason = null)
+ => this.Status == ScheduledEventStatus.Active ? await this.Discord.ApiClient.ModifyGuildScheduledEventStatusAsync(this.GuildId, this.Id, ScheduledEventStatus.Completed, reason) : throw new InvalidOperationException("You can only stop active events");
+
+ /// <summary>
+ /// Gets a list of users RSVP'd to the scheduled event.
+ /// </summary>
+ /// <param name="limit">The limit how many users to receive from the event. Defaults to 100. Max 100.</param>
+ /// <param name="before">Get results of <see cref="DiscordScheduledEventUser"/> before the given snowflake.</param>
+ /// <param name="after">Get results of <see cref="DiscordScheduledEventUser"/> after the given snowflake.</param>
+ /// <param name="withMember">Whether to include guild member data.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the correct permissions.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<IReadOnlyDictionary<ulong, DiscordScheduledEventUser>> GetUsersAsync(int? limit = null, ulong? before = null, ulong? after = null, bool? withMember = null)
+ => await this.Discord.ApiClient.GetGuildScheduledEventRspvUsersAsync(this.GuildId, this.Id, limit, before, after, withMember);
+
+ /// <summary>
+ /// Deletes a scheduled event.
+ /// </summary>
+ /// <param name="reason">The audit log reason.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEvents"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the event does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task DeleteAsync(string reason = null)
+ => await this.Discord.ApiClient.DeleteGuildScheduledEventAsync(this.GuildId, this.Id, reason);
+
+ #endregion
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordScheduledEvent"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordScheduledEvent"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordScheduledEvent);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordScheduledEvent"/> is equal to another <see cref="DiscordScheduledEvent"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordScheduledEvent"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordScheduledEvent"/> is equal to this <see cref="DiscordScheduledEvent"/>.</returns>
+ public bool Equals(DiscordScheduledEvent e)
+ => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordScheduledEvent"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordScheduledEvent"/>.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordScheduledEvent"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First event to compare.</param>
+ /// <param name="e2">Second event to compare.</param>
+ /// <returns>Whether the two events are equal.</returns>
+ public static bool operator ==(DiscordScheduledEvent e1, DiscordScheduledEvent e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
+
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordScheduledEvent"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First event to compare.</param>
+ /// <param name="e2">Second event to compare.</param>
+ /// <returns>Whether the two events are not equal.</returns>
+ public static bool operator !=(DiscordScheduledEvent e1, DiscordScheduledEvent e2)
+ => !(e1 == e2);
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordScheduledEvent"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First event to compare.</param>
- /// <param name="e2">Second event to compare.</param>
- /// <returns>Whether the two events are not equal.</returns>
- public static bool operator !=(DiscordScheduledEvent e1, DiscordScheduledEvent e2)
- => !(e1 == e2);
}
diff --git a/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEventEntityMetadata.cs b/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEventEntityMetadata.cs
index f23eebca9..cd277280a 100644
--- a/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEventEntityMetadata.cs
+++ b/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEventEntityMetadata.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an scheduled event.
-/// </summary>
-public class DiscordScheduledEventEntityMetadata
+namespace DisCatSharp.Entities
{
/// <summary>
- /// External location if event type is <see cref="ScheduledEventEntityType.External"/>.
+ /// Represents an scheduled event.
/// </summary>
- [JsonProperty("location", NullValueHandling = NullValueHandling.Ignore)]
- public string Location { get; internal set; }
+ public class DiscordScheduledEventEntityMetadata
+ {
+ /// <summary>
+ /// External location if event type is <see cref="ScheduledEventEntityType.External"/>.
+ /// </summary>
+ [JsonProperty("location", NullValueHandling = NullValueHandling.Ignore)]
+ public string Location { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordScheduledEventEntityMetadata"/> class.
- /// </summary>
- internal DiscordScheduledEventEntityMetadata()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordScheduledEventEntityMetadata"/> class.
+ /// </summary>
+ internal DiscordScheduledEventEntityMetadata()
+ { }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordScheduledEventEntityMetadata"/> class.
- /// </summary>
- /// <param name="location">The location.</param>
- public DiscordScheduledEventEntityMetadata(string location)
- {
- this.Location = location;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordScheduledEventEntityMetadata"/> class.
+ /// </summary>
+ /// <param name="location">The location.</param>
+ public DiscordScheduledEventEntityMetadata(string location)
+ {
+ this.Location = location;
+ }
}
}
diff --git a/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEventUser.cs b/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEventUser.cs
index 0efd60990..253ccd671 100644
--- a/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEventUser.cs
+++ b/DisCatSharp/Entities/Guild/ScheduledEvent/DiscordScheduledEventUser.cs
@@ -1,123 +1,124 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// The discord scheduled event user.
-/// </summary>
-public class DiscordScheduledEventUser : IEquatable<DiscordScheduledEventUser>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the client instance this object is tied to.
- /// </summary>
- [JsonIgnore]
- internal BaseDiscordClient Discord { get; set; }
-
- /// <summary>
- /// Gets the user.
- /// </summary>
- [JsonProperty("user")]
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Gets the member.
- /// Only applicable when requested with `with_member`.
- /// </summary>
- [JsonProperty("member", NullValueHandling = NullValueHandling.Ignore)]
- internal DiscordMember Member { get; set; }
-
- /// <summary>
- /// Gets the scheduled event.
+ /// The discord scheduled event user.
/// </summary>
- [JsonIgnore]
- public DiscordScheduledEvent ScheduledEvent
- => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) && guild.ScheduledEvents.TryGetValue(this.EventId, out var scheduledEvent) ? scheduledEvent : null;
-
- /// <summary>
- /// Gets or sets the event id.
- /// </summary>
- [JsonProperty("guild_scheduled_event_id")]
- internal ulong EventId { get; set; }
-
- /// <summary>
- /// Gets or sets the guild id.
- /// </summary>
- [JsonIgnore]
- internal ulong GuildId { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordScheduledEventUser"/> class.
- /// </summary>
- internal DiscordScheduledEventUser()
-{ }
-
- /// <summary>
- /// Checks whether this <see cref="DiscordScheduledEventUser"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordScheduledEventUser"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordScheduledEventUser);
-
- /// <summary>
- /// Checks whether this <see cref="DiscordScheduledEventUser"/> is equal to another <see cref="DiscordScheduledEventUser"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordScheduledEventUser"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordScheduledEventUser"/> is equal to this <see cref="DiscordScheduledEventUser"/>.</returns>
- public bool Equals(DiscordScheduledEventUser e)
- => e is not null && (ReferenceEquals(this, e) || HashCode.Combine(this.User.Id, this.EventId) == HashCode.Combine(e.User.Id, e.EventId));
-
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordScheduledEventUser"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordScheduledEventUser"/>.</returns>
- public override int GetHashCode()
- => HashCode.Combine(this.User.Id, this.EventId);
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordScheduledEventUser"/> objects are equal.
- /// </summary>
- /// <param name="e1">First event to compare.</param>
- /// <param name="e2">Second event to compare.</param>
- /// <returns>Whether the two events are equal.</returns>
- public static bool operator ==(DiscordScheduledEventUser e1, DiscordScheduledEventUser e2)
+ public class DiscordScheduledEventUser : IEquatable<DiscordScheduledEventUser>
{
- var o1 = e1 as object;
- var o2 = e2 as object;
-
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || HashCode.Combine(e1.User.Id, e1.EventId) == HashCode.Combine(e2.User.Id, e2.EventId));
+ /// <summary>
+ /// Gets the client instance this object is tied to.
+ /// </summary>
+ [JsonIgnore]
+ internal BaseDiscordClient Discord { get; set; }
+
+ /// <summary>
+ /// Gets the user.
+ /// </summary>
+ [JsonProperty("user")]
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the member.
+ /// Only applicable when requested with `with_member`.
+ /// </summary>
+ [JsonProperty("member", NullValueHandling = NullValueHandling.Ignore)]
+ internal DiscordMember Member { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordScheduledEvent ScheduledEvent
+ => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) && guild.ScheduledEvents.TryGetValue(this.EventId, out var scheduledEvent) ? scheduledEvent : null;
+
+ /// <summary>
+ /// Gets or sets the event id.
+ /// </summary>
+ [JsonProperty("guild_scheduled_event_id")]
+ internal ulong EventId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the guild id.
+ /// </summary>
+ [JsonIgnore]
+ internal ulong GuildId { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordScheduledEventUser"/> class.
+ /// </summary>
+ internal DiscordScheduledEventUser()
+ { }
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordScheduledEventUser"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordScheduledEventUser"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordScheduledEventUser);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordScheduledEventUser"/> is equal to another <see cref="DiscordScheduledEventUser"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordScheduledEventUser"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordScheduledEventUser"/> is equal to this <see cref="DiscordScheduledEventUser"/>.</returns>
+ public bool Equals(DiscordScheduledEventUser e)
+ => e is not null && (ReferenceEquals(this, e) || HashCode.Combine(this.User.Id, this.EventId) == HashCode.Combine(e.User.Id, e.EventId));
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordScheduledEventUser"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordScheduledEventUser"/>.</returns>
+ public override int GetHashCode()
+ => HashCode.Combine(this.User.Id, this.EventId);
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordScheduledEventUser"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First event to compare.</param>
+ /// <param name="e2">Second event to compare.</param>
+ /// <returns>Whether the two events are equal.</returns>
+ public static bool operator ==(DiscordScheduledEventUser e1, DiscordScheduledEventUser e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
+
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || HashCode.Combine(e1.User.Id, e1.EventId) == HashCode.Combine(e2.User.Id, e2.EventId));
+ }
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordScheduledEventUser"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First event to compare.</param>
+ /// <param name="e2">Second event to compare.</param>
+ /// <returns>Whether the two events are not equal.</returns>
+ public static bool operator !=(DiscordScheduledEventUser e1, DiscordScheduledEventUser e2)
+ => !(e1 == e2);
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordScheduledEventUser"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First event to compare.</param>
- /// <param name="e2">Second event to compare.</param>
- /// <returns>Whether the two events are not equal.</returns>
- public static bool operator !=(DiscordScheduledEventUser e1, DiscordScheduledEventUser e2)
- => !(e1 == e2);
}
diff --git a/DisCatSharp/Entities/Integration/DiscordIntegration.cs b/DisCatSharp/Entities/Integration/DiscordIntegration.cs
index e9c1031ce..92d4c2c6f 100644
--- a/DisCatSharp/Entities/Integration/DiscordIntegration.cs
+++ b/DisCatSharp/Entities/Integration/DiscordIntegration.cs
@@ -1,99 +1,100 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord integration. These appear on the profile as linked 3rd party accounts.
-/// </summary>
-public class DiscordIntegration : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the integration name.
+ /// Represents a Discord integration. These appear on the profile as linked 3rd party accounts.
/// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
+ public class DiscordIntegration : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the integration name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the integration type.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public string Type { get; internal set; }
+ /// <summary>
+ /// Gets the integration type.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public string Type { get; internal set; }
- /// <summary>
- /// Gets whether this integration is enabled.
- /// </summary>
- [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsEnabled { get; internal set; }
+ /// <summary>
+ /// Gets whether this integration is enabled.
+ /// </summary>
+ [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsEnabled { get; internal set; }
- /// <summary>
- /// Gets whether this integration is syncing.
- /// </summary>
- [JsonProperty("syncing", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsSyncing { get; internal set; }
+ /// <summary>
+ /// Gets whether this integration is syncing.
+ /// </summary>
+ [JsonProperty("syncing", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsSyncing { get; internal set; }
- /// <summary>
- /// Gets ID of the role this integration uses for subscribers.
- /// </summary>
- [JsonProperty("role_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong RoleId { get; internal set; }
+ /// <summary>
+ /// Gets ID of the role this integration uses for subscribers.
+ /// </summary>
+ [JsonProperty("role_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong RoleId { get; internal set; }
- /// <summary>
- /// Gets the expiration behaviour.
- /// </summary>
- [JsonProperty("expire_behavior", NullValueHandling = NullValueHandling.Ignore)]
- public int ExpireBehavior { get; internal set; }
+ /// <summary>
+ /// Gets the expiration behaviour.
+ /// </summary>
+ [JsonProperty("expire_behavior", NullValueHandling = NullValueHandling.Ignore)]
+ public int ExpireBehavior { get; internal set; }
- /// <summary>
- /// Gets the grace period before expiring subscribers.
- /// </summary>
- [JsonProperty("expire_grace_period", NullValueHandling = NullValueHandling.Ignore)]
- public int ExpireGracePeriod { get; internal set; }
+ /// <summary>
+ /// Gets the grace period before expiring subscribers.
+ /// </summary>
+ [JsonProperty("expire_grace_period", NullValueHandling = NullValueHandling.Ignore)]
+ public int ExpireGracePeriod { get; internal set; }
- /// <summary>
- /// Gets the user that owns this integration.
- /// </summary>
- [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUser User { get; internal set; }
+ /// <summary>
+ /// Gets the user that owns this integration.
+ /// </summary>
+ [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the 3rd party service account for this integration.
- /// </summary>
- [JsonProperty("account", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordIntegrationAccount Account { get; internal set; }
+ /// <summary>
+ /// Gets the 3rd party service account for this integration.
+ /// </summary>
+ [JsonProperty("account", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordIntegrationAccount Account { get; internal set; }
- /// <summary>
- /// Gets the date and time this integration was last synced.
- /// </summary>
- [JsonProperty("synced_at", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset SyncedAt { get; internal set; }
+ /// <summary>
+ /// Gets the date and time this integration was last synced.
+ /// </summary>
+ [JsonProperty("synced_at", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset SyncedAt { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordIntegration"/> class.
- /// </summary>
- internal DiscordIntegration()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordIntegration"/> class.
+ /// </summary>
+ internal DiscordIntegration()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Integration/DiscordIntegrationAccount.cs b/DisCatSharp/Entities/Integration/DiscordIntegrationAccount.cs
index 38a780870..a938f2b8d 100644
--- a/DisCatSharp/Entities/Integration/DiscordIntegrationAccount.cs
+++ b/DisCatSharp/Entities/Integration/DiscordIntegrationAccount.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord integration account.
-/// </summary>
-public class DiscordIntegrationAccount
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the ID of the account.
+ /// Represents a Discord integration account.
/// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public string Id { get; internal set; }
+ public class DiscordIntegrationAccount
+ {
+ /// <summary>
+ /// Gets the ID of the account.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public string Id { get; internal set; }
- /// <summary>
- /// Gets the name of the account.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
+ /// <summary>
+ /// Gets the name of the account.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordIntegrationAccount"/> class.
- /// </summary>
- internal DiscordIntegrationAccount()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordIntegrationAccount"/> class.
+ /// </summary>
+ internal DiscordIntegrationAccount()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Interaction/Components/Button/DiscordButtonComponent.cs b/DisCatSharp/Entities/Interaction/Components/Button/DiscordButtonComponent.cs
index 4c3d74aea..53e54557d 100644
--- a/DisCatSharp/Entities/Interaction/Components/Button/DiscordButtonComponent.cs
+++ b/DisCatSharp/Entities/Interaction/Components/Button/DiscordButtonComponent.cs
@@ -1,127 +1,129 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a button that can be pressed. Fires <see cref="DisCatSharp.DiscordClient.ComponentInteractionCreated"/> event when pressed.
-/// </summary>
-public sealed class DiscordButtonComponent : DiscordComponent
+namespace DisCatSharp.Entities
{
- /// <summary>
- /// The style of the button.
- /// </summary>
- [JsonProperty("style", NullValueHandling = NullValueHandling.Ignore)]
- public ButtonStyle Style { get; internal set; }
/// <summary>
- /// The text to apply to the button. If this is not specified <see cref="Emoji"/> becomes required.
+ /// Represents a button that can be pressed. Fires <see cref="DisCatSharp.DiscordClient.ComponentInteractionCreated"/> event when pressed.
/// </summary>
- [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
- public string Label { get; internal set; }
+ public sealed class DiscordButtonComponent : DiscordComponent
+ {
+ /// <summary>
+ /// The style of the button.
+ /// </summary>
+ [JsonProperty("style", NullValueHandling = NullValueHandling.Ignore)]
+ public ButtonStyle Style { get; internal set; }
- /// <summary>
- /// Whether this button can be pressed.
- /// </summary>
- [JsonProperty("disabled", NullValueHandling = NullValueHandling.Ignore)]
- public bool Disabled { get; internal set; }
+ /// <summary>
+ /// The text to apply to the button. If this is not specified <see cref="Emoji"/> becomes required.
+ /// </summary>
+ [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
+ public string Label { get; internal set; }
- /// <summary>
- /// The emoji to add to the button. Can be used in conjunction with a label, or as standalone. Must be added if label is not specified.
- /// </summary>
- [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordComponentEmoji Emoji { get; internal set; }
+ /// <summary>
+ /// Whether this button can be pressed.
+ /// </summary>
+ [JsonProperty("disabled", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Disabled { get; internal set; }
- /// <summary>
- /// Enables this component if it was disabled before.
- /// </summary>
- /// <returns>The current component.</returns>
- public DiscordButtonComponent Enable()
- {
- this.Disabled = false;
- return this;
- }
+ /// <summary>
+ /// The emoji to add to the button. Can be used in conjunction with a label, or as standalone. Must be added if label is not specified.
+ /// </summary>
+ [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordComponentEmoji Emoji { get; internal set; }
- /// <summary>
- /// Disables this component.
- /// </summary>
- /// <returns>The current component.</returns>
- public DiscordButtonComponent Disable()
- {
- this.Disabled = true;
- return this;
- }
+ /// <summary>
+ /// Enables this component if it was disabled before.
+ /// </summary>
+ /// <returns>The current component.</returns>
+ public DiscordButtonComponent Enable()
+ {
+ this.Disabled = false;
+ return this;
+ }
- /// <summary>
- /// Constructs a new <see cref="DiscordButtonComponent"/>.
- /// </summary>
- public DiscordButtonComponent()
- {
- this.Type = ComponentType.Button;
- }
+ /// <summary>
+ /// Disables this component.
+ /// </summary>
+ /// <returns>The current component.</returns>
+ public DiscordButtonComponent Disable()
+ {
+ this.Disabled = true;
+ return this;
+ }
- /// <summary>
- /// Constructs a new button based on another button.
- /// </summary>
- /// <param name="other">The button to copy.</param>
- public DiscordButtonComponent(DiscordButtonComponent other) : this()
- {
- this.CustomId = other.CustomId;
- this.Style = other.Style;
- this.Label = other.Label;
- this.Disabled = other.Disabled;
- this.Emoji = other.Emoji;
- }
+ /// <summary>
+ /// Constructs a new <see cref="DiscordButtonComponent"/>.
+ /// </summary>
+ public DiscordButtonComponent()
+ {
+ this.Type = ComponentType.Button;
+ }
- /// <summary>
- /// Constructs a new button with the specified options.
- /// </summary>
- /// <param name="style">The style/color of the button.</param>
- /// <param name="customId">The Id to assign to the button. This is sent back when a user presses it.</param>
- /// <param name="label">The text to display on the button, up to 80 characters. Can be left blank if <paramref name="emoji"/>is set.</param>
- /// <param name="disabled">Whether this button should be initialized as being disabled. User sees a greyed out button that cannot be interacted with.</param>
- /// <param name="emoji">The emoji to add to the button. This is required if <paramref name="label"/> is empty or null.</param>
- /// <exception cref="ArgumentException">Is thrown when neither the <paramref name="emoji"/> nor the <paramref name="label"/> is set.</exception>
- public DiscordButtonComponent(ButtonStyle style, string customId = null, string label = null, bool disabled = false, DiscordComponentEmoji emoji = null)
- {
- this.Style = style;
- this.CustomId = customId ?? Guid.NewGuid().ToString();
- this.Disabled = disabled;
- if (emoji != null)
- {
- this.Label = label;
- this.Emoji = emoji;
+ /// <summary>
+ /// Constructs a new button based on another button.
+ /// </summary>
+ /// <param name="other">The button to copy.</param>
+ public DiscordButtonComponent(DiscordButtonComponent other) : this()
+ {
+ this.CustomId = other.CustomId;
+ this.Style = other.Style;
+ this.Label = other.Label;
+ this.Disabled = other.Disabled;
+ this.Emoji = other.Emoji;
}
- else
+
+ /// <summary>
+ /// Constructs a new button with the specified options.
+ /// </summary>
+ /// <param name="style">The style/color of the button.</param>
+ /// <param name="customId">The Id to assign to the button. This is sent back when a user presses it.</param>
+ /// <param name="label">The text to display on the button, up to 80 characters. Can be left blank if <paramref name="emoji"/>is set.</param>
+ /// <param name="disabled">Whether this button should be initialized as being disabled. User sees a greyed out button that cannot be interacted with.</param>
+ /// <param name="emoji">The emoji to add to the button. This is required if <paramref name="label"/> is empty or null.</param>
+ /// <exception cref="ArgumentException">Is thrown when neither the <paramref name="emoji"/> nor the <paramref name="label"/> is set.</exception>
+ public DiscordButtonComponent(ButtonStyle style, string customId = null, string label = null, bool disabled = false, DiscordComponentEmoji emoji = null)
+ {
+ this.Style = style;
+ this.CustomId = customId ?? Guid.NewGuid().ToString();
+ this.Disabled = disabled;
+ if (emoji != null)
+ {
+ this.Label = label;
+ this.Emoji = emoji;
+ }
+ else
{
- this.Label = label ?? throw new ArgumentException("Label can only be null if emoji is set.");
- this.Emoji = null;
+ this.Label = label ?? throw new ArgumentException("Label can only be null if emoji is set.");
+ this.Emoji = null;
+ }
+ this.Type = ComponentType.Button;
}
- this.Type = ComponentType.Button;
}
}
diff --git a/DisCatSharp/Entities/Interaction/Components/Button/DiscordLinkButtonComponent.cs b/DisCatSharp/Entities/Interaction/Components/Button/DiscordLinkButtonComponent.cs
index 3bb70fa34..88106e78f 100644
--- a/DisCatSharp/Entities/Interaction/Components/Button/DiscordLinkButtonComponent.cs
+++ b/DisCatSharp/Entities/Interaction/Components/Button/DiscordLinkButtonComponent.cs
@@ -1,106 +1,107 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a link button. Clicking a link button does not send an interaction.
-/// </summary>
-public class DiscordLinkButtonComponent : DiscordComponent
+namespace DisCatSharp.Entities
{
/// <summary>
- /// The url to open when pressing this button.
+ /// Represents a link button. Clicking a link button does not send an interaction.
/// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public string Url { get; set; }
+ public class DiscordLinkButtonComponent : DiscordComponent
+ {
+ /// <summary>
+ /// The url to open when pressing this button.
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public string Url { get; set; }
- /// <summary>
- /// The text to add to this button. If this is not specified, <see cref="Emoji"/> must be.
- /// </summary>
- [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
- public string Label { get; set; }
+ /// <summary>
+ /// The text to add to this button. If this is not specified, <see cref="Emoji"/> must be.
+ /// </summary>
+ [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
+ public string Label { get; set; }
- /// <summary>
- /// Whether this button can be pressed.
- /// </summary>
- [JsonProperty("disabled", NullValueHandling = NullValueHandling.Ignore)]
- public bool Disabled { get; set; }
+ /// <summary>
+ /// Whether this button can be pressed.
+ /// </summary>
+ [JsonProperty("disabled", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Disabled { get; set; }
- /// <summary>
- /// The emoji to add to the button. Can be used in conjunction with a label, or as standalone. Must be added if label is not specified.
- /// </summary>
- [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordComponentEmoji Emoji { get; set; }
+ /// <summary>
+ /// The emoji to add to the button. Can be used in conjunction with a label, or as standalone. Must be added if label is not specified.
+ /// </summary>
+ [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordComponentEmoji Emoji { get; set; }
- /// <summary>
- /// Gets the style.
- /// </summary>
- [JsonProperty("style", NullValueHandling = NullValueHandling.Ignore)]
- internal int Style { get; set; } = 5; // Link = 5; Discord throws 400 otherwise //
+ /// <summary>
+ /// Gets the style.
+ /// </summary>
+ [JsonProperty("style", NullValueHandling = NullValueHandling.Ignore)]
+ internal int Style { get; set; } = 5; // Link = 5; Discord throws 400 otherwise //
- /// <summary>
- /// Enables this component if it was disabled before.
- /// </summary>
- /// <returns>The current component.</returns>
- public DiscordLinkButtonComponent Enable()
- {
- this.Disabled = false;
- return this;
- }
+ /// <summary>
+ /// Enables this component if it was disabled before.
+ /// </summary>
+ /// <returns>The current component.</returns>
+ public DiscordLinkButtonComponent Enable()
+ {
+ this.Disabled = false;
+ return this;
+ }
- /// <summary>
- /// Disables this component.
- /// </summary>
- /// <returns>The current component.</returns>
- public DiscordLinkButtonComponent Disable()
- {
- this.Disabled = true;
- return this;
- }
+ /// <summary>
+ /// Disables this component.
+ /// </summary>
+ /// <returns>The current component.</returns>
+ public DiscordLinkButtonComponent Disable()
+ {
+ this.Disabled = true;
+ return this;
+ }
- /// <summary>
- /// Constructs a new <see cref="DiscordLinkButtonComponent"/>. This type of button does not send back and interaction when pressed.
- /// </summary>
- /// <param name="url">The url to set the button to.</param>
- /// <param name="label">The text to display on the button. Can be left blank if <paramref name="emoji"/> is set.</param>
- /// <param name="disabled">Whether or not this button can be pressed.</param>
- /// <param name="emoji">The emoji to set with this button. This is required if <paramref name="label"/> is null or empty.</param>
- public DiscordLinkButtonComponent(string url, string label, bool disabled = false, DiscordComponentEmoji emoji = null) : this()
- {
- this.Url = url;
- this.Label = label;
- this.Disabled = disabled;
- this.Emoji = emoji;
- }
+ /// <summary>
+ /// Constructs a new <see cref="DiscordLinkButtonComponent"/>. This type of button does not send back and interaction when pressed.
+ /// </summary>
+ /// <param name="url">The url to set the button to.</param>
+ /// <param name="label">The text to display on the button. Can be left blank if <paramref name="emoji"/> is set.</param>
+ /// <param name="disabled">Whether or not this button can be pressed.</param>
+ /// <param name="emoji">The emoji to set with this button. This is required if <paramref name="label"/> is null or empty.</param>
+ public DiscordLinkButtonComponent(string url, string label, bool disabled = false, DiscordComponentEmoji emoji = null) : this()
+ {
+ this.Url = url;
+ this.Label = label;
+ this.Disabled = disabled;
+ this.Emoji = emoji;
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordLinkButtonComponent"/> class.
- /// </summary>
- public DiscordLinkButtonComponent()
- {
- this.Type = ComponentType.Button;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordLinkButtonComponent"/> class.
+ /// </summary>
+ public DiscordLinkButtonComponent()
+ {
+ this.Type = ComponentType.Button;
+ }
}
}
diff --git a/DisCatSharp/Entities/Interaction/Components/DiscordActionRowComponent.cs b/DisCatSharp/Entities/Interaction/Components/DiscordActionRowComponent.cs
index 65b9b12a5..29ccd5ee5 100644
--- a/DisCatSharp/Entities/Interaction/Components/DiscordActionRowComponent.cs
+++ b/DisCatSharp/Entities/Interaction/Components/DiscordActionRowComponent.cs
@@ -1,67 +1,68 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Linq;
using DisCatSharp.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a row of components. Action rows can have up to five components.
-/// </summary>
-public sealed class DiscordActionRowComponent : DiscordComponent
+namespace DisCatSharp.Entities
{
/// <summary>
- /// The components contained within the action row.
+ /// Represents a row of components. Action rows can have up to five components.
/// </summary>
- [JsonIgnore]
- public IReadOnlyCollection<DiscordComponent> Components
+ public sealed class DiscordActionRowComponent : DiscordComponent
{
- get => this._components ?? new List<DiscordComponent>();
- set => this._components = new List<DiscordComponent>(value);
- }
+ /// <summary>
+ /// The components contained within the action row.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyCollection<DiscordComponent> Components
+ {
+ get => this._components ?? new List<DiscordComponent>();
+ set => this._components = new List<DiscordComponent>(value);
+ }
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- private List<DiscordComponent> _components;
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ private List<DiscordComponent> _components;
- /// <summary>
- /// Constructs a new <see cref="DiscordActionRowComponent"/>.
- /// </summary>
- /// <param name="components">List of components</param>
- public DiscordActionRowComponent(IEnumerable<DiscordComponent> components)
- : this()
- {
- this.Components = components.ToList().AsReadOnly();
- }
+ /// <summary>
+ /// Constructs a new <see cref="DiscordActionRowComponent"/>.
+ /// </summary>
+ /// <param name="components">List of components</param>
+ public DiscordActionRowComponent(IEnumerable<DiscordComponent> components)
+ : this()
+ {
+ this.Components = components.ToList().AsReadOnly();
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordActionRowComponent"/> class.
- /// </summary>
- internal DiscordActionRowComponent()
- {
- this.Type = ComponentType.ActionRow;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordActionRowComponent"/> class.
+ /// </summary>
+ internal DiscordActionRowComponent()
+ {
+ this.Type = ComponentType.ActionRow;
+ }
}
}
diff --git a/DisCatSharp/Entities/Interaction/Components/DiscordActionRowComponentResult.cs b/DisCatSharp/Entities/Interaction/Components/DiscordActionRowComponentResult.cs
index d6cfe87b6..877b8b01c 100644
--- a/DisCatSharp/Entities/Interaction/Components/DiscordActionRowComponentResult.cs
+++ b/DisCatSharp/Entities/Interaction/Components/DiscordActionRowComponentResult.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a <see cref="DiscordActionRowComponentResult"/> resolved from a <see cref="DisCatSharp.Enums.ApplicationCommandType.ModalSubmit"/>.
-/// </summary>
-public sealed class DiscordActionRowComponentResult
+namespace DisCatSharp.Entities
{
/// <summary>
- /// The type of component this represents.
+ /// Represents a <see cref="DiscordActionRowComponentResult"/> resolved from a <see cref="DisCatSharp.Enums.ApplicationCommandType.ModalSubmit"/>.
/// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public ComponentType Type { get; internal set; }
+ public sealed class DiscordActionRowComponentResult
+ {
+ /// <summary>
+ /// The type of component this represents.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public ComponentType Type { get; internal set; }
- /// <summary>
- /// The components contained within the resolved action row.
- /// </summary>
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public List<DiscordComponentResult> Components { get; internal set; }
+ /// <summary>
+ /// The components contained within the resolved action row.
+ /// </summary>
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public List<DiscordComponentResult> Components { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordActionRowComponentResult"/> class.
- /// </summary>
- internal DiscordActionRowComponentResult()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordActionRowComponentResult"/> class.
+ /// </summary>
+ internal DiscordActionRowComponentResult()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Interaction/Components/DiscordComponent.cs b/DisCatSharp/Entities/Interaction/Components/DiscordComponent.cs
index 7e3f0e9cf..3bb0dd9cf 100644
--- a/DisCatSharp/Entities/Interaction/Components/DiscordComponent.cs
+++ b/DisCatSharp/Entities/Interaction/Components/DiscordComponent.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Enums;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// A component to attach to a message.
-/// </summary>
-[JsonConverter(typeof(DiscordComponentJsonConverter))]
-public class DiscordComponent
+namespace DisCatSharp.Entities
{
/// <summary>
- /// The type of component this represents.
+ /// A component to attach to a message.
/// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public ComponentType Type { get; internal set; }
+ [JsonConverter(typeof(DiscordComponentJsonConverter))]
+ public class DiscordComponent
+ {
+ /// <summary>
+ /// The type of component this represents.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public ComponentType Type { get; internal set; }
- /// <summary>
- /// The Id of this component, if applicable. Not applicable on ActionRow(s) and link buttons.
- /// </summary>
- [JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
- public string CustomId { get; internal set; }
+ /// <summary>
+ /// The Id of this component, if applicable. Not applicable on ActionRow(s) and link buttons.
+ /// </summary>
+ [JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
+ public string CustomId { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordComponent"/> class.
- /// </summary>
- internal DiscordComponent()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordComponent"/> class.
+ /// </summary>
+ internal DiscordComponent()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Interaction/Components/DiscordComponentResult.cs b/DisCatSharp/Entities/Interaction/Components/DiscordComponentResult.cs
index a0329d977..bead8fbda 100644
--- a/DisCatSharp/Entities/Interaction/Components/DiscordComponentResult.cs
+++ b/DisCatSharp/Entities/Interaction/Components/DiscordComponentResult.cs
@@ -1,57 +1,58 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a <see cref="DiscordComponentResult"/> resolved within an <see cref="DiscordActionRowComponentResult"/>.
-/// </summary>
-public sealed class DiscordComponentResult
+namespace DisCatSharp.Entities
{
/// <summary>
- /// The type of component this represents.
+ /// Represents a <see cref="DiscordComponentResult"/> resolved within an <see cref="DiscordActionRowComponentResult"/>.
/// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public ComponentType Type { get; internal set; }
+ public sealed class DiscordComponentResult
+ {
+ /// <summary>
+ /// The type of component this represents.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public ComponentType Type { get; internal set; }
- /// <summary>
- /// The Id of this component, if applicable. Not applicable on ActionRow(s) and link buttons.
- /// </summary>
- [JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
- public string CustomId { get; internal set; }
+ /// <summary>
+ /// The Id of this component, if applicable. Not applicable on ActionRow(s) and link buttons.
+ /// </summary>
+ [JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
+ public string CustomId { get; internal set; }
- /// <summary>
- /// The Id of this component, if applicable. Not applicable on ActionRow(s) and link buttons.
- /// </summary>
- [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
- public string Value { get; internal set; }
+ /// <summary>
+ /// The Id of this component, if applicable. Not applicable on ActionRow(s) and link buttons.
+ /// </summary>
+ [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
+ public string Value { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordComponentResult"/> class.
- /// </summary>
- internal DiscordComponentResult()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordComponentResult"/> class.
+ /// </summary>
+ internal DiscordComponentResult()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Interaction/Components/DiscordEmojiComponent.cs b/DisCatSharp/Entities/Interaction/Components/DiscordEmojiComponent.cs
index 786115696..745b54eec 100644
--- a/DisCatSharp/Entities/Interaction/Components/DiscordEmojiComponent.cs
+++ b/DisCatSharp/Entities/Interaction/Components/DiscordEmojiComponent.cs
@@ -1,80 +1,81 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an emoji to add to a component.
-/// </summary>
-public sealed class DiscordComponentEmoji
+namespace DisCatSharp.Entities
{
/// <summary>
- /// The Id of the emoji to use.
+ /// Represents an emoji to add to a component.
/// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong Id { get; set; }
+ public sealed class DiscordComponentEmoji
+ {
+ /// <summary>
+ /// The Id of the emoji to use.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong Id { get; set; }
- /// <summary>
- /// The name of the emoji to use. Ignored if <see cref="Id"/> is set.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; set; }
+ /// <summary>
+ /// The name of the emoji to use. Ignored if <see cref="Id"/> is set.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
- /// <summary>
- /// Constructs a new component emoji to add to a <see cref="DiscordComponent"/>.
- /// </summary>
- public DiscordComponentEmoji() { }
+ /// <summary>
+ /// Constructs a new component emoji to add to a <see cref="DiscordComponent"/>.
+ /// </summary>
+ public DiscordComponentEmoji() { }
- /// <summary>
- /// Constructs a new component emoji from an emoji Id.
- /// </summary>
- /// <param name="id">The Id of the emoji to use. Any valid emoji Id can be passed.</param>
- public DiscordComponentEmoji(ulong id)
- {
- this.Id = id;
- }
+ /// <summary>
+ /// Constructs a new component emoji from an emoji Id.
+ /// </summary>
+ /// <param name="id">The Id of the emoji to use. Any valid emoji Id can be passed.</param>
+ public DiscordComponentEmoji(ulong id)
+ {
+ this.Id = id;
+ }
- /// <summary>
- /// Constructs a new component emoji from unicode.
- /// </summary>
- /// <param name="name">The unicode emoji to set.</param>
- public DiscordComponentEmoji(string name)
- {
- if (!DiscordEmoji.IsValidUnicode(name))
- throw new ArgumentException("Only unicode emojis can be passed.");
- this.Name = name;
- }
+ /// <summary>
+ /// Constructs a new component emoji from unicode.
+ /// </summary>
+ /// <param name="name">The unicode emoji to set.</param>
+ public DiscordComponentEmoji(string name)
+ {
+ if (!DiscordEmoji.IsValidUnicode(name))
+ throw new ArgumentException("Only unicode emojis can be passed.");
+ this.Name = name;
+ }
- /// <summary>
- /// Constructs a new component emoji from an existing <see cref="DiscordEmoji"/>.
- /// </summary>
- /// <param name="emoji">The emoji to use.</param>
- public DiscordComponentEmoji(DiscordEmoji emoji)
- {
- this.Id = emoji.Id;
- this.Name = emoji.Name;
+ /// <summary>
+ /// Constructs a new component emoji from an existing <see cref="DiscordEmoji"/>.
+ /// </summary>
+ /// <param name="emoji">The emoji to use.</param>
+ public DiscordComponentEmoji(DiscordEmoji emoji)
+ {
+ this.Id = emoji.Id;
+ this.Name = emoji.Name;
+ }
}
}
diff --git a/DisCatSharp/Entities/Interaction/Components/Select/DiscordSelectComponent.cs b/DisCatSharp/Entities/Interaction/Components/Select/DiscordSelectComponent.cs
index c5cedb1c1..47a0c4be7 100644
--- a/DisCatSharp/Entities/Interaction/Components/Select/DiscordSelectComponent.cs
+++ b/DisCatSharp/Entities/Interaction/Components/Select/DiscordSelectComponent.cs
@@ -1,114 +1,115 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using DisCatSharp.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// A select menu with multiple options to choose from.
-/// </summary>
-public sealed class DiscordSelectComponent : DiscordComponent
+namespace DisCatSharp.Entities
{
/// <summary>
- /// The options to pick from on this component.
+ /// A select menu with multiple options to choose from.
/// </summary>
- [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<DiscordSelectComponentOption> Options { get; internal set; } = Array.Empty<DiscordSelectComponentOption>();
+ public sealed class DiscordSelectComponent : DiscordComponent
+ {
+ /// <summary>
+ /// The options to pick from on this component.
+ /// </summary>
+ [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<DiscordSelectComponentOption> Options { get; internal set; } = Array.Empty<DiscordSelectComponentOption>();
- /// <summary>
- /// The text to show when no option is selected.
- /// </summary>
- [JsonProperty("placeholder", NullValueHandling = NullValueHandling.Ignore)]
- public string Placeholder { get; internal set; }
+ /// <summary>
+ /// The text to show when no option is selected.
+ /// </summary>
+ [JsonProperty("placeholder", NullValueHandling = NullValueHandling.Ignore)]
+ public string Placeholder { get; internal set; }
- /// <summary>
- /// The minimum amount of options that can be selected. Must be less than or equal to <see cref="MaximumSelectedValues"/>. Defaults to one.
- /// </summary>
- [JsonProperty("min_values", NullValueHandling = NullValueHandling.Ignore)]
- public int? MinimumSelectedValues { get; internal set; } = 1;
+ /// <summary>
+ /// The minimum amount of options that can be selected. Must be less than or equal to <see cref="MaximumSelectedValues"/>. Defaults to one.
+ /// </summary>
+ [JsonProperty("min_values", NullValueHandling = NullValueHandling.Ignore)]
+ public int? MinimumSelectedValues { get; internal set; } = 1;
- /// <summary>
- /// The maximum amount of options that can be selected. Must be greater than or equal to zero or <see cref="MinimumSelectedValues"/>. Defaults to one.
- /// </summary>
- [JsonProperty("max_values", NullValueHandling = NullValueHandling.Ignore)]
- public int? MaximumSelectedValues { get; internal set; } = 1;
+ /// <summary>
+ /// The maximum amount of options that can be selected. Must be greater than or equal to zero or <see cref="MinimumSelectedValues"/>. Defaults to one.
+ /// </summary>
+ [JsonProperty("max_values", NullValueHandling = NullValueHandling.Ignore)]
+ public int? MaximumSelectedValues { get; internal set; } = 1;
- /// <summary>
- /// Whether this select can be used.
- /// </summary>
- [JsonProperty("disabled", NullValueHandling = NullValueHandling.Ignore)]
- public bool Disabled { get; internal set; }
+ /// <summary>
+ /// Whether this select can be used.
+ /// </summary>
+ [JsonProperty("disabled", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Disabled { get; internal set; }
- /// <summary>
- /// Enables this component if it was disabled before.
- /// </summary>
- /// <returns>The current component.</returns>
- public DiscordSelectComponent Enable()
- {
- this.Disabled = false;
- return this;
- }
+ /// <summary>
+ /// Enables this component if it was disabled before.
+ /// </summary>
+ /// <returns>The current component.</returns>
+ public DiscordSelectComponent Enable()
+ {
+ this.Disabled = false;
+ return this;
+ }
- /// <summary>
- /// Disables this component.
- /// </summary>
- /// <returns>The current component.</returns>
- public DiscordSelectComponent Disable()
- {
- this.Disabled = true;
- return this;
- }
+ /// <summary>
+ /// Disables this component.
+ /// </summary>
+ /// <returns>The current component.</returns>
+ public DiscordSelectComponent Disable()
+ {
+ this.Disabled = true;
+ return this;
+ }
- /// <summary>
- /// Constructs a new <see cref="DiscordSelectComponent"/>.
- /// </summary>
- /// <param name="customId">The Id to assign to the button. This is sent back when a user presses it.</param>
- /// <param name="options">Array of options</param>
- /// <param name="placeholder">Text to show if no option is selected.</param>
- /// <param name="minOptions">Minimum count of selectable options.</param>
- /// <param name="maxOptions">Maximum count of selectable options.</param>
- /// <param name="disabled">Whether this button should be initialized as being disabled. User sees a greyed out button that cannot be interacted with.</param>
- public DiscordSelectComponent(string customId, string placeholder, IEnumerable<DiscordSelectComponentOption> options, int minOptions = 1, int maxOptions = 1, bool disabled = false) : this()
- {
- this.CustomId = customId;
- this.Disabled = disabled;
- this.Options = options.ToArray();
- this.Placeholder = placeholder;
- this.MinimumSelectedValues = minOptions;
- this.MaximumSelectedValues = maxOptions;
- }
+ /// <summary>
+ /// Constructs a new <see cref="DiscordSelectComponent"/>.
+ /// </summary>
+ /// <param name="customId">The Id to assign to the button. This is sent back when a user presses it.</param>
+ /// <param name="options">Array of options</param>
+ /// <param name="placeholder">Text to show if no option is selected.</param>
+ /// <param name="minOptions">Minimum count of selectable options.</param>
+ /// <param name="maxOptions">Maximum count of selectable options.</param>
+ /// <param name="disabled">Whether this button should be initialized as being disabled. User sees a greyed out button that cannot be interacted with.</param>
+ public DiscordSelectComponent(string customId, string placeholder, IEnumerable<DiscordSelectComponentOption> options, int minOptions = 1, int maxOptions = 1, bool disabled = false) : this()
+ {
+ this.CustomId = customId;
+ this.Disabled = disabled;
+ this.Options = options.ToArray();
+ this.Placeholder = placeholder;
+ this.MinimumSelectedValues = minOptions;
+ this.MaximumSelectedValues = maxOptions;
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordSelectComponent"/> class.
- /// </summary>
- public DiscordSelectComponent()
- {
- this.Type = ComponentType.Select;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordSelectComponent"/> class.
+ /// </summary>
+ public DiscordSelectComponent()
+ {
+ this.Type = ComponentType.Select;
+ }
}
}
diff --git a/DisCatSharp/Entities/Interaction/Components/Select/DiscordSelectComponentOption.cs b/DisCatSharp/Entities/Interaction/Components/Select/DiscordSelectComponentOption.cs
index 14f2a2c8d..f2592c913 100644
--- a/DisCatSharp/Entities/Interaction/Components/Select/DiscordSelectComponentOption.cs
+++ b/DisCatSharp/Entities/Interaction/Components/Select/DiscordSelectComponentOption.cs
@@ -1,87 +1,88 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents options for <see cref="DiscordSelectComponent"/>.
-/// </summary>
-public sealed class DiscordSelectComponentOption
+namespace DisCatSharp.Entities
{
/// <summary>
- /// The label to add. This is required.
+ /// Represents options for <see cref="DiscordSelectComponent"/>.
/// </summary>
- [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
- public string Label { get; internal set; }
+ public sealed class DiscordSelectComponentOption
+ {
+ /// <summary>
+ /// The label to add. This is required.
+ /// </summary>
+ [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
+ public string Label { get; internal set; }
- /// <summary>
- /// The value of this option. Akin to the Custom Id of components.
- /// </summary>
- [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
- public string Value { get; internal set; }
+ /// <summary>
+ /// The value of this option. Akin to the Custom Id of components.
+ /// </summary>
+ [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
+ public string Value { get; internal set; }
- /// <summary>
- /// Whether this option is default. If true, this option will be pre-selected. Defaults to false.
- /// </summary>
- [JsonProperty("default", NullValueHandling = NullValueHandling.Ignore)]
- public bool Default { get; internal set; } // false //
+ /// <summary>
+ /// Whether this option is default. If true, this option will be pre-selected. Defaults to false.
+ /// </summary>
+ [JsonProperty("default", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Default { get; internal set; } // false //
- /// <summary>
- /// The description of this option. This is optional.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
+ /// <summary>
+ /// The description of this option. This is optional.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
- /// <summary>
- /// The emoji of this option. This is optional.
- /// </summary>
- [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordComponentEmoji Emoji { get; internal set; }
+ /// <summary>
+ /// The emoji of this option. This is optional.
+ /// </summary>
+ [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordComponentEmoji Emoji { get; internal set; }
- /// <summary>
- /// Constructs a new <see cref="DiscordSelectComponentOption"/>.
- /// </summary>
- /// <param name="label">The label of this option.</param>
- /// <param name="value">The value of this option.</param>
- /// <param name="description">Description of the option.</param>
- /// <param name="isDefault">Whether this option is default. If true, this option will be pre-selected.</param>
- /// <param name="emoji">The emoji to set with this option.</param>
- public DiscordSelectComponentOption(string label, string value, string description = null, bool isDefault = false, DiscordComponentEmoji emoji = null)
- {
- if (label.Length > 100)
- throw new NotSupportedException("Select label can't be longer then 100 chars.");
- if (value.Length > 100)
- throw new NotSupportedException("Select value can't be longer then 100 chars.");
- if (description != null && description.Length > 100)
- throw new NotSupportedException("Select description can't be longer then 100 chars.");
+ /// <summary>
+ /// Constructs a new <see cref="DiscordSelectComponentOption"/>.
+ /// </summary>
+ /// <param name="label">The label of this option.</param>
+ /// <param name="value">The value of this option.</param>
+ /// <param name="description">Description of the option.</param>
+ /// <param name="isDefault">Whether this option is default. If true, this option will be pre-selected.</param>
+ /// <param name="emoji">The emoji to set with this option.</param>
+ public DiscordSelectComponentOption(string label, string value, string description = null, bool isDefault = false, DiscordComponentEmoji emoji = null)
+ {
+ if (label.Length > 100)
+ throw new NotSupportedException("Select label can't be longer then 100 chars.");
+ if (value.Length > 100)
+ throw new NotSupportedException("Select value can't be longer then 100 chars.");
+ if (description != null && description.Length > 100)
+ throw new NotSupportedException("Select description can't be longer then 100 chars.");
- this.Label = label;
- this.Value = value;
- this.Description = description;
- this.Default = isDefault;
- this.Emoji = emoji;
+ this.Label = label;
+ this.Value = value;
+ this.Description = description;
+ this.Default = isDefault;
+ this.Emoji = emoji;
+ }
}
}
diff --git a/DisCatSharp/Entities/Interaction/Components/Text/DiscordTextComponent.cs b/DisCatSharp/Entities/Interaction/Components/Text/DiscordTextComponent.cs
index 2decacf89..d89993e86 100644
--- a/DisCatSharp/Entities/Interaction/Components/Text/DiscordTextComponent.cs
+++ b/DisCatSharp/Entities/Interaction/Components/Text/DiscordTextComponent.cs
@@ -1,157 +1,159 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a text component that can be submitted. Fires <see cref="DisCatSharp.DiscordClient.ComponentInteractionCreated"/> event when submitted.
-/// </summary>
-public sealed class DiscordTextComponent : DiscordComponent
+namespace DisCatSharp.Entities
{
- /// <summary>
- /// The style of the text component.
- /// </summary>
- [JsonProperty("style", NullValueHandling = NullValueHandling.Ignore)]
- public TextComponentStyle Style { get; internal set; }
-
- /// <summary>
- /// The text description to apply to the text component.
- /// </summary>
- [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
- public string Label { get; internal set; }
-
- /// <summary>
- /// The placeholder for the text component.
- /// </summary>
- [JsonProperty("placeholder", NullValueHandling = NullValueHandling.Ignore)]
- public string Placeholder { get; internal set; }
-
- /// <summary>
- /// The pre-filled value for the text component.
- /// </summary>
- [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
- public string Value { get; internal set; }
/// <summary>
- /// The minimal length of text input.
- /// Defaults to 0.
+ /// Represents a text component that can be submitted. Fires <see cref="DisCatSharp.DiscordClient.ComponentInteractionCreated"/> event when submitted.
/// </summary>
- [JsonProperty("min_length", NullValueHandling = NullValueHandling.Ignore)]
- public int? MinLength { get; internal set; } = 0;
-
- /// <summary>
- /// The maximal length of text input.
- /// </summary>
- [JsonProperty("max_length", NullValueHandling = NullValueHandling.Ignore)]
- public int? MaxLength { get; internal set; }
-
- // NOTE: Probably will be introduced in future
- /*/// <summary>
+ public sealed class DiscordTextComponent : DiscordComponent
+ {
+ /// <summary>
+ /// The style of the text component.
+ /// </summary>
+ [JsonProperty("style", NullValueHandling = NullValueHandling.Ignore)]
+ public TextComponentStyle Style { get; internal set; }
+
+ /// <summary>
+ /// The text description to apply to the text component.
+ /// </summary>
+ [JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
+ public string Label { get; internal set; }
+
+ /// <summary>
+ /// The placeholder for the text component.
+ /// </summary>
+ [JsonProperty("placeholder", NullValueHandling = NullValueHandling.Ignore)]
+ public string Placeholder { get; internal set; }
+
+ /// <summary>
+ /// The pre-filled value for the text component.
+ /// </summary>
+ [JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
+ public string Value { get; internal set; }
+
+ /// <summary>
+ /// The minimal length of text input.
+ /// Defaults to 0.
+ /// </summary>
+ [JsonProperty("min_length", NullValueHandling = NullValueHandling.Ignore)]
+ public int? MinLength { get; internal set; } = 0;
+
+ /// <summary>
+ /// The maximal length of text input.
+ /// </summary>
+ [JsonProperty("max_length", NullValueHandling = NullValueHandling.Ignore)]
+ public int? MaxLength { get; internal set; }
+
+ // NOTE: Probably will be introduced in future
+ /*/// <summary>
/// Whether this text component can be used.
/// </summary>
[JsonProperty("disabled", NullValueHandling = NullValueHandling.Ignore)]
public bool Disabled { get; internal set; }*/
- /// <summary>
- /// Whether this text component is required.
- /// Defaults to true.
- /// </summary>
- [JsonProperty("required", NullValueHandling = NullValueHandling.Ignore)]
- public bool Required { get; internal set; }
+ /// <summary>
+ /// Whether this text component is required.
+ /// Defaults to true.
+ /// </summary>
+ [JsonProperty("required", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Required { get; internal set; }
- /*/// <summary>
+ /*/// <summary>
/// Enables this component if it was disabled before.
/// </summary>
/// <returns>The current component.</returns>
public DiscordTextComponent Enable()
{
this.Disabled = false;
return this;
}
/// <summary>
/// Disables this component.
/// </summary>
/// <returns>The current component.</returns>
public DiscordTextComponent Disable()
{
this.Disabled = true;
return this;
}*/
- /// <summary>
- /// Constructs a new <see cref="DiscordTextComponent"/>.
- /// </summary>
- internal DiscordTextComponent()
- {
- this.Type = ComponentType.InputText;
- }
-
- /// <summary>
- /// Constucts a new text component based on another text component.
- /// </summary>
- /// <param name="other">The button to copy.</param>
- public DiscordTextComponent(DiscordTextComponent other) : this()
- {
- this.CustomId = other.CustomId;
- this.Style = other.Style;
- this.Label = other.Label;
- //this.Disabled = other.Disabled;
- this.MinLength = other.MinLength;
- this.MaxLength = other.MaxLength;
- this.Placeholder = other.Placeholder;
- this.Required = other.Required;
- this.Value = other.Value;
- }
-
- /// <summary>
- /// Constructs a new text component field with the specified options.
- /// </summary>
- /// <param name="style">The style of the text component.</param>
- /// <param name="customId">The Id to assign to the text component. This is sent back when a user presses it.</param>
- /// <param name="label">The text to display before the text component, up to 80 characters. Required, but set to null to avoid breaking change.</param>
- /// <param name="placeholder">The placeholder for the text input.</param>
- /// <param name="minLength">The minimal length of text input.</param>
- /// <param name="maxLength">The maximal length of text input.</param>
- /// <param name="required">Whether this text component should be required.</param>
- /// <param name="defaultValue">Pre-filled value for text field.</param>
- /// <exception cref="ArgumentException">Is thrown when no label is set.</exception>
- public DiscordTextComponent(TextComponentStyle style, string customId = null, string label = null, string placeholder = null, int? minLength = null, int? maxLength = null, bool required = true, string defaultValue = null)
- {
- this.Style = style;
- this.Label = label ?? throw new ArgumentException("A label is required.");
- this.CustomId = customId ?? Guid.NewGuid().ToString();
- this.MinLength = minLength;
- this.MaxLength = maxLength;
- this.Placeholder = placeholder;
- //this.Disabled = disabled;
- this.Required = required;
- this.Value = defaultValue;
- this.Type = ComponentType.InputText;
+ /// <summary>
+ /// Constructs a new <see cref="DiscordTextComponent"/>.
+ /// </summary>
+ internal DiscordTextComponent()
+ {
+ this.Type = ComponentType.InputText;
+ }
+
+ /// <summary>
+ /// Constucts a new text component based on another text component.
+ /// </summary>
+ /// <param name="other">The button to copy.</param>
+ public DiscordTextComponent(DiscordTextComponent other) : this()
+ {
+ this.CustomId = other.CustomId;
+ this.Style = other.Style;
+ this.Label = other.Label;
+ //this.Disabled = other.Disabled;
+ this.MinLength = other.MinLength;
+ this.MaxLength = other.MaxLength;
+ this.Placeholder = other.Placeholder;
+ this.Required = other.Required;
+ this.Value = other.Value;
+ }
+
+ /// <summary>
+ /// Constructs a new text component field with the specified options.
+ /// </summary>
+ /// <param name="style">The style of the text component.</param>
+ /// <param name="customId">The Id to assign to the text component. This is sent back when a user presses it.</param>
+ /// <param name="label">The text to display before the text component, up to 80 characters. Required, but set to null to avoid breaking change.</param>
+ /// <param name="placeholder">The placeholder for the text input.</param>
+ /// <param name="minLength">The minimal length of text input.</param>
+ /// <param name="maxLength">The maximal length of text input.</param>
+ /// <param name="required">Whether this text component should be required.</param>
+ /// <param name="defaultValue">Pre-filled value for text field.</param>
+ /// <exception cref="ArgumentException">Is thrown when no label is set.</exception>
+ public DiscordTextComponent(TextComponentStyle style, string customId = null, string label = null, string placeholder = null, int? minLength = null, int? maxLength = null, bool required = true, string defaultValue = null)
+ {
+ this.Style = style;
+ this.Label = label ?? throw new ArgumentException("A label is required.");
+ this.CustomId = customId ?? Guid.NewGuid().ToString();
+ this.MinLength = minLength;
+ this.MaxLength = maxLength;
+ this.Placeholder = placeholder;
+ //this.Disabled = disabled;
+ this.Required = required;
+ this.Value = defaultValue;
+ this.Type = ComponentType.InputText;
+ }
}
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordFollowupMessageBuilder.cs b/DisCatSharp/Entities/Interaction/DiscordFollowupMessageBuilder.cs
index f43e8a54c..52455f129 100644
--- a/DisCatSharp/Entities/Interaction/DiscordFollowupMessageBuilder.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordFollowupMessageBuilder.cs
@@ -1,313 +1,314 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using System.Linq;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Constructs a followup message to an interaction.
-/// </summary>
-public sealed class DiscordFollowupMessageBuilder
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Whether this followup message is text-to-speech.
- /// </summary>
- public bool IsTts { get; set; }
-
- /// <summary>
- /// Whether this followup message should be ephemeral.
+ /// Constructs a followup message to an interaction.
/// </summary>
- public bool IsEphemeral { get; set; }
-
- /// <summary>
- /// Indicates this message is ephemeral.
- /// </summary>
- internal int? Flags
- => this.IsEphemeral ? 64 : null;
-
- /// <summary>
- /// Message to send on followup message.
- /// </summary>
- public string Content
+ public sealed class DiscordFollowupMessageBuilder
{
- get => this._content;
- set
+ /// <summary>
+ /// Whether this followup message is text-to-speech.
+ /// </summary>
+ public bool IsTts { get; set; }
+
+ /// <summary>
+ /// Whether this followup message should be ephemeral.
+ /// </summary>
+ public bool IsEphemeral { get; set; }
+
+ /// <summary>
+ /// Indicates this message is ephemeral.
+ /// </summary>
+ internal int? Flags
+ => this.IsEphemeral ? 64 : null;
+
+ /// <summary>
+ /// Message to send on followup message.
+ /// </summary>
+ public string Content
{
- if (value != null && value.Length > 2000)
- throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
- this._content = value;
+ get => this._content;
+ set
+ {
+ if (value != null && value.Length > 2000)
+ throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
+ this._content = value;
+ }
}
- }
- private string _content;
-
- /// <summary>
- /// Embeds to send on followup message.
- /// </summary>
- public IReadOnlyList<DiscordEmbed> Embeds => this._embeds;
- private readonly List<DiscordEmbed> _embeds = new();
-
- /// <summary>
- /// Files to send on this followup message.
- /// </summary>
- public IReadOnlyList<DiscordMessageFile> Files => this._files;
- private readonly List<DiscordMessageFile> _files = new();
-
- /// <summary>
- /// Components to send on this followup message.
- /// </summary>
- public IReadOnlyList<DiscordActionRowComponent> Components => this._components;
- private readonly List<DiscordActionRowComponent> _components = new();
+ private string _content;
+
+ /// <summary>
+ /// Embeds to send on followup message.
+ /// </summary>
+ public IReadOnlyList<DiscordEmbed> Embeds => this._embeds;
+ private readonly List<DiscordEmbed> _embeds = new();
+
+ /// <summary>
+ /// Files to send on this followup message.
+ /// </summary>
+ public IReadOnlyList<DiscordMessageFile> Files => this._files;
+ private readonly List<DiscordMessageFile> _files = new();
+
+ /// <summary>
+ /// Components to send on this followup message.
+ /// </summary>
+ public IReadOnlyList<DiscordActionRowComponent> Components => this._components;
+ private readonly List<DiscordActionRowComponent> _components = new();
+
+ /// <summary>
+ /// Mentions to send on this followup message.
+ /// </summary>
+ public IReadOnlyList<IMention> Mentions => this._mentions;
+ private readonly List<IMention> _mentions = new();
+
+
+ /// <summary>
+ /// Appends a collection of components to the message.
+ /// </summary>
+ /// <param name="components">The collection of components to add.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ /// <exception cref="System.ArgumentException"><paramref name="components"/> contained more than 5 components.</exception>
+ public DiscordFollowupMessageBuilder AddComponents(params DiscordComponent[] components)
+ => this.AddComponents((IEnumerable<DiscordComponent>)components);
+
+ /// <summary>
+ /// Appends several rows of components to the message
+ /// </summary>
+ /// <param name="components">The rows of components to add, holding up to five each.</param>
+ /// <returns></returns>
+ public DiscordFollowupMessageBuilder AddComponents(IEnumerable<DiscordActionRowComponent> components)
+ {
+ var ara = components.ToArray();
- /// <summary>
- /// Mentions to send on this followup message.
- /// </summary>
- public IReadOnlyList<IMention> Mentions => this._mentions;
- private readonly List<IMention> _mentions = new();
+ if (ara.Length + this._components.Count > 5)
+ throw new ArgumentException("ActionRow count exceeds maximum of five.");
+ foreach (var ar in ara)
+ this._components.Add(ar);
- /// <summary>
- /// Appends a collection of components to the message.
- /// </summary>
- /// <param name="components">The collection of components to add.</param>
- /// <returns>The builder to chain calls with.</returns>
- /// <exception cref="System.ArgumentException"><paramref name="components"/> contained more than 5 components.</exception>
- public DiscordFollowupMessageBuilder AddComponents(params DiscordComponent[] components)
- => this.AddComponents((IEnumerable<DiscordComponent>)components);
+ return this;
+ }
- /// <summary>
- /// Appends several rows of components to the message
- /// </summary>
- /// <param name="components">The rows of components to add, holding up to five each.</param>
- /// <returns></returns>
- public DiscordFollowupMessageBuilder AddComponents(IEnumerable<DiscordActionRowComponent> components)
- {
- var ara = components.ToArray();
+ /// <summary>
+ /// Appends a collection of components to the message.
+ /// </summary>
+ /// <param name="components">The collection of components to add.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ /// <exception cref="System.ArgumentException"><paramref name="components"/> contained more than 5 components.</exception>
+ public DiscordFollowupMessageBuilder AddComponents(IEnumerable<DiscordComponent> components)
+ {
+ var compArr = components.ToArray();
+ var count = compArr.Length;
- if (ara.Length + this._components.Count > 5)
- throw new ArgumentException("ActionRow count exceeds maximum of five.");
+ if (count > 5)
+ throw new ArgumentException("Cannot add more than 5 components per action row!");
- foreach (var ar in ara)
- this._components.Add(ar);
+ var arc = new DiscordActionRowComponent(compArr);
+ this._components.Add(arc);
+ return this;
+ }
+ /// <summary>
+ /// Indicates if the followup message must use text-to-speech.
+ /// </summary>
+ /// <param name="tts">Text-to-speech</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordFollowupMessageBuilder WithTts(bool tts)
+ {
+ this.IsTts = tts;
+ return this;
+ }
- return this;
- }
+ /// <summary>
+ /// Sets the message to send with the followup message..
+ /// </summary>
+ /// <param name="content">Message to send.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordFollowupMessageBuilder WithContent(string content)
+ {
+ this.Content = content;
+ return this;
+ }
- /// <summary>
- /// Appends a collection of components to the message.
- /// </summary>
- /// <param name="components">The collection of components to add.</param>
- /// <returns>The builder to chain calls with.</returns>
- /// <exception cref="System.ArgumentException"><paramref name="components"/> contained more than 5 components.</exception>
- public DiscordFollowupMessageBuilder AddComponents(IEnumerable<DiscordComponent> components)
- {
- var compArr = components.ToArray();
- var count = compArr.Length;
+ /// <summary>
+ /// Adds an embed to the followup message.
+ /// </summary>
+ /// <param name="embed">Embed to add.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordFollowupMessageBuilder AddEmbed(DiscordEmbed embed)
+ {
+ this._embeds.Add(embed);
+ return this;
+ }
- if (count > 5)
- throw new ArgumentException("Cannot add more than 5 components per action row!");
+ /// <summary>
+ /// Adds the given embeds to the followup message.
+ /// </summary>
+ /// <param name="embeds">Embeds to add.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordFollowupMessageBuilder AddEmbeds(IEnumerable<DiscordEmbed> embeds)
+ {
+ this._embeds.AddRange(embeds);
+ return this;
+ }
- var arc = new DiscordActionRowComponent(compArr);
- this._components.Add(arc);
- return this;
- }
- /// <summary>
- /// Indicates if the followup message must use text-to-speech.
- /// </summary>
- /// <param name="tts">Text-to-speech</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordFollowupMessageBuilder WithTts(bool tts)
- {
- this.IsTts = tts;
- return this;
- }
+ /// <summary>
+ /// Adds a file to the followup message.
+ /// </summary>
+ /// <param name="filename">Name of the file.</param>
+ /// <param name="data">File data.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <param name="description">Description of the file.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordFollowupMessageBuilder AddFile(string filename, Stream data, bool resetStreamPosition = false, string description = null)
+ {
+ if (this.Files.Count >= 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
- /// <summary>
- /// Sets the message to send with the followup message..
- /// </summary>
- /// <param name="content">Message to send.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordFollowupMessageBuilder WithContent(string content)
- {
- this.Content = content;
- return this;
- }
+ if (this._files.Any(x => x.FileName == filename))
+ throw new ArgumentException("A File with that filename already exists");
- /// <summary>
- /// Adds an embed to the followup message.
- /// </summary>
- /// <param name="embed">Embed to add.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordFollowupMessageBuilder AddEmbed(DiscordEmbed embed)
- {
- this._embeds.Add(embed);
- return this;
- }
+ if (resetStreamPosition)
+ this._files.Add(new DiscordMessageFile(filename, data, data.Position, description: description));
+ else
+ this._files.Add(new DiscordMessageFile(filename, data, null, description: description));
- /// <summary>
- /// Adds the given embeds to the followup message.
- /// </summary>
- /// <param name="embeds">Embeds to add.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordFollowupMessageBuilder AddEmbeds(IEnumerable<DiscordEmbed> embeds)
- {
- this._embeds.AddRange(embeds);
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Adds a file to the followup message.
- /// </summary>
- /// <param name="filename">Name of the file.</param>
- /// <param name="data">File data.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <param name="description">Description of the file.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordFollowupMessageBuilder AddFile(string filename, Stream data, bool resetStreamPosition = false, string description = null)
- {
- if (this.Files.Count >= 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ /// <summary>
+ /// Sets if the message has files to be sent.
+ /// </summary>
+ /// <param name="stream">The Stream to the file.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <param name="description">Description of the file.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordFollowupMessageBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string description = null)
+ {
+ if (this.Files.Count >= 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (this._files.Any(x => x.FileName == filename))
- throw new ArgumentException("A File with that filename already exists");
+ if (this._files.Any(x => x.FileName == stream.Name))
+ throw new ArgumentException("A File with that filename already exists");
- if (resetStreamPosition)
- this._files.Add(new DiscordMessageFile(filename, data, data.Position, description: description));
- else
- this._files.Add(new DiscordMessageFile(filename, data, null, description: description));
+ if (resetStreamPosition)
+ this._files.Add(new DiscordMessageFile(stream.Name, stream, stream.Position, description: description));
+ else
+ this._files.Add(new DiscordMessageFile(stream.Name, stream, null, description: description));
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Sets if the message has files to be sent.
- /// </summary>
- /// <param name="stream">The Stream to the file.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <param name="description">Description of the file.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordFollowupMessageBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string description = null)
- {
- if (this.Files.Count >= 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ /// <summary>
+ /// Adds the given files to the followup message.
+ /// </summary>
+ /// <param name="files">Dictionary of file name and file data.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordFollowupMessageBuilder AddFiles(Dictionary<string, Stream> files, bool resetStreamPosition = false)
+ {
+ if (this.Files.Count + files.Count > 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (this._files.Any(x => x.FileName == stream.Name))
- throw new ArgumentException("A File with that filename already exists");
+ foreach (var file in files)
+ {
+ if (this._files.Any(x => x.FileName == file.Key))
+ throw new ArgumentException("A File with that filename already exists");
- if (resetStreamPosition)
- this._files.Add(new DiscordMessageFile(stream.Name, stream, stream.Position, description: description));
- else
- this._files.Add(new DiscordMessageFile(stream.Name, stream, null, description: description));
+ if (resetStreamPosition)
+ this._files.Add(new DiscordMessageFile(file.Key, file.Value, file.Value.Position));
+ else
+ this._files.Add(new DiscordMessageFile(file.Key, file.Value, null));
+ }
- return this;
- }
- /// <summary>
- /// Adds the given files to the followup message.
- /// </summary>
- /// <param name="files">Dictionary of file name and file data.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordFollowupMessageBuilder AddFiles(Dictionary<string, Stream> files, bool resetStreamPosition = false)
- {
- if (this.Files.Count + files.Count > 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ return this;
+ }
- foreach (var file in files)
+ /// <summary>
+ /// Adds the mention to the mentions to parse, etc. with the followup message.
+ /// </summary>
+ /// <param name="mention">Mention to add.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordFollowupMessageBuilder AddMention(IMention mention)
{
- if (this._files.Any(x => x.FileName == file.Key))
- throw new ArgumentException("A File with that filename already exists");
-
- if (resetStreamPosition)
- this._files.Add(new DiscordMessageFile(file.Key, file.Value, file.Value.Position));
- else
- this._files.Add(new DiscordMessageFile(file.Key, file.Value, null));
+ this._mentions.Add(mention);
+ return this;
}
+ /// <summary>
+ /// Adds the mentions to the mentions to parse, etc. with the followup message.
+ /// </summary>
+ /// <param name="mentions">Mentions to add.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordFollowupMessageBuilder AddMentions(IEnumerable<IMention> mentions)
+ {
+ this._mentions.AddRange(mentions);
+ return this;
+ }
- return this;
- }
-
- /// <summary>
- /// Adds the mention to the mentions to parse, etc. with the followup message.
- /// </summary>
- /// <param name="mention">Mention to add.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordFollowupMessageBuilder AddMention(IMention mention)
- {
- this._mentions.Add(mention);
- return this;
- }
-
- /// <summary>
- /// Adds the mentions to the mentions to parse, etc. with the followup message.
- /// </summary>
- /// <param name="mentions">Mentions to add.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordFollowupMessageBuilder AddMentions(IEnumerable<IMention> mentions)
- {
- this._mentions.AddRange(mentions);
- return this;
- }
-
- /// <summary>
- /// Sets the followup message to be ephemeral.
- /// </summary>
- /// <param name="ephemeral">Whether the followup should be ephemeral. Defaults to true.</param>
- public DiscordFollowupMessageBuilder AsEphemeral(bool ephemeral = true)
- {
- this.IsEphemeral = ephemeral;
- return this;
- }
+ /// <summary>
+ /// Sets the followup message to be ephemeral.
+ /// </summary>
+ /// <param name="ephemeral">Whether the followup should be ephemeral. Defaults to true.</param>
+ public DiscordFollowupMessageBuilder AsEphemeral(bool ephemeral = true)
+ {
+ this.IsEphemeral = ephemeral;
+ return this;
+ }
- /// <summary>
- /// Clears all message components on this builder.
- /// </summary>
- public void ClearComponents()
- => this._components.Clear();
+ /// <summary>
+ /// Clears all message components on this builder.
+ /// </summary>
+ public void ClearComponents()
+ => this._components.Clear();
- /// <summary>
- /// Allows for clearing the Followup Message builder so that it can be used again to send a new message.
- /// </summary>
- public void Clear()
- {
- this.Content = "";
- this._embeds.Clear();
- this.IsTts = false;
- this._mentions.Clear();
- this._files.Clear();
- this.IsEphemeral = false;
- this._components.Clear();
- }
+ /// <summary>
+ /// Allows for clearing the Followup Message builder so that it can be used again to send a new message.
+ /// </summary>
+ public void Clear()
+ {
+ this.Content = "";
+ this._embeds.Clear();
+ this.IsTts = false;
+ this._mentions.Clear();
+ this._files.Clear();
+ this.IsEphemeral = false;
+ this._components.Clear();
+ }
- /// <summary>
- /// Validates the builder.
- /// </summary>
- internal void Validate()
- {
- if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && !this.Embeds.Any())
- throw new ArgumentException("You must specify content, an embed, or at least one file.");
+ /// <summary>
+ /// Validates the builder.
+ /// </summary>
+ internal void Validate()
+ {
+ if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && !this.Embeds.Any())
+ throw new ArgumentException("You must specify content, an embed, or at least one file.");
+ }
}
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteraction.cs b/DisCatSharp/Entities/Interaction/DiscordInteraction.cs
index f4227a536..6eecf3a95 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteraction.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteraction.cs
@@ -1,219 +1,220 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an interaction that was invoked.
-/// </summary>
-public sealed class DiscordInteraction : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the type of interaction invoked.
- /// </summary>
- [JsonProperty("type")]
- public InteractionType Type { get; internal set; }
-
- /// <summary>
- /// Gets the command data for this interaction.
- /// </summary>
- [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordInteractionData Data { get; internal set; }
-
- /// <summary>
- /// Gets the Id of the guild that invoked this interaction.
- /// </summary>
- [JsonIgnore]
- public ulong? GuildId { get; internal set; }
-
- /// <summary>
- /// Gets the guild that invoked this interaction.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild
- => (this.Discord as DiscordClient).InternalGetCachedGuild(this.GuildId);
-
- /// <summary>
- /// Gets the Id of the channel that invoked this interaction.
- /// </summary>
- [JsonIgnore]
- public ulong ChannelId { get; internal set; }
-
- /// <summary>
- /// Gets the channel that invoked this interaction.
- /// </summary>
- [JsonIgnore]
- public DiscordChannel Channel
- => (this.Discord as DiscordClient).InternalGetCachedChannel(this.ChannelId) ?? (DiscordChannel)(this.Discord as DiscordClient).InternalGetCachedThread(this.ChannelId) ?? (this.Guild == null ? new DiscordDmChannel { Id = this.ChannelId, Type = ChannelType.Private, Discord = this.Discord } : new DiscordChannel() { Id = this.ChannelId, Discord = this.Discord });
-
- /// <summary>
- /// Gets the user that invoked this interaction.
- /// <para>This can be cast to a <see cref="DisCatSharp.Entities.DiscordMember"/> if created in a guild.</para>
- /// </summary>
- [JsonIgnore]
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Gets the continuation token for responding to this interaction.
- /// </summary>
- [JsonProperty("token")]
- public string Token { get; internal set; }
-
- /// <summary>
- /// Gets the version number for this interaction type.
- /// </summary>
- [JsonProperty("version")]
- public int Version { get; internal set; }
-
- /// <summary>
- /// Gets the ID of the application that created this interaction.
- /// </summary>
- [JsonProperty("application_id")]
- public ulong ApplicationId { get; internal set; }
-
- /// <summary>
- /// The message this interaction was created with, if any.
- /// </summary>
- [JsonProperty("message")]
- internal DiscordMessage Message { get; set; }
-
- /// <summary>
- /// Gets the invoking user locale.
- /// </summary>
- [JsonProperty("locale", NullValueHandling = NullValueHandling.Ignore)]
- public string Locale { get; internal set; }
-
- /// <summary>
- /// Gets the guild locale if applicable.
- /// </summary>
- [JsonProperty("guild_locale", NullValueHandling = NullValueHandling.Ignore)]
- public string GuildLocale { get; internal set; }
-
- /// <summary>
- /// Creates a response to this interaction.
+ /// Represents an interaction that was invoked.
/// </summary>
- /// <param name="type">The type of the response.</param>
- /// <param name="builder">The data, if any, to send.</param>
- public Task CreateResponseAsync(InteractionResponseType type, DiscordInteractionResponseBuilder builder = null)
- => this.Discord.ApiClient.CreateInteractionResponseAsync(this.Id, this.Token, type, builder);
-
- /// <summary>
- /// Creates a modal response to this interaction.
- /// </summary>
- /// <param name="builder">The data to send.</param>
- public Task CreateInteractionModalResponseAsync(DiscordInteractionModalBuilder builder)
- => this.Type != InteractionType.Ping && this.Type != InteractionType.ModalSubmit ? this.Discord.ApiClient.CreateInteractionModalResponseAsync(this.Id, this.Token, InteractionResponseType.Modal, builder) : throw new NotSupportedException("You can't respond to an PING with a modal.");
-
- /// <summary>
- /// Gets the original interaction response.
- /// </summary>
- /// <returns>The original message that was sent. This <b>does not work on ephemeral messages.</b></returns>
- public Task<DiscordMessage> GetOriginalResponseAsync()
- => this.Discord.ApiClient.GetOriginalInteractionResponseAsync(this.Discord.CurrentApplication.Id, this.Token);
-
- /// <summary>
- /// Edits the original interaction response.
- /// </summary>
- /// <param name="builder">The webhook builder.</param>
- /// <returns>The edited <see cref="DiscordMessage"/>.</returns>
- public async Task<DiscordMessage> EditOriginalResponseAsync(DiscordWebhookBuilder builder)
+ public sealed class DiscordInteraction : SnowflakeObject
{
- builder.Validate(isInteractionResponse: true);
- if (builder.KeepAttachmentsInternal.HasValue && builder.KeepAttachmentsInternal.Value)
+ /// <summary>
+ /// Gets the type of interaction invoked.
+ /// </summary>
+ [JsonProperty("type")]
+ public InteractionType Type { get; internal set; }
+
+ /// <summary>
+ /// Gets the command data for this interaction.
+ /// </summary>
+ [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordInteractionData Data { get; internal set; }
+
+ /// <summary>
+ /// Gets the Id of the guild that invoked this interaction.
+ /// </summary>
+ [JsonIgnore]
+ public ulong? GuildId { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild that invoked this interaction.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild
+ => (this.Discord as DiscordClient).InternalGetCachedGuild(this.GuildId);
+
+ /// <summary>
+ /// Gets the Id of the channel that invoked this interaction.
+ /// </summary>
+ [JsonIgnore]
+ public ulong ChannelId { get; internal set; }
+
+ /// <summary>
+ /// Gets the channel that invoked this interaction.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel Channel
+ => (this.Discord as DiscordClient).InternalGetCachedChannel(this.ChannelId) ?? (DiscordChannel)(this.Discord as DiscordClient).InternalGetCachedThread(this.ChannelId) ?? (this.Guild == null ? new DiscordDmChannel { Id = this.ChannelId, Type = ChannelType.Private, Discord = this.Discord } : new DiscordChannel() { Id = this.ChannelId, Discord = this.Discord });
+
+ /// <summary>
+ /// Gets the user that invoked this interaction.
+ /// <para>This can be cast to a <see cref="DisCatSharp.Entities.DiscordMember"/> if created in a guild.</para>
+ /// </summary>
+ [JsonIgnore]
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the continuation token for responding to this interaction.
+ /// </summary>
+ [JsonProperty("token")]
+ public string Token { get; internal set; }
+
+ /// <summary>
+ /// Gets the version number for this interaction type.
+ /// </summary>
+ [JsonProperty("version")]
+ public int Version { get; internal set; }
+
+ /// <summary>
+ /// Gets the ID of the application that created this interaction.
+ /// </summary>
+ [JsonProperty("application_id")]
+ public ulong ApplicationId { get; internal set; }
+
+ /// <summary>
+ /// The message this interaction was created with, if any.
+ /// </summary>
+ [JsonProperty("message")]
+ internal DiscordMessage Message { get; set; }
+
+ /// <summary>
+ /// Gets the invoking user locale.
+ /// </summary>
+ [JsonProperty("locale", NullValueHandling = NullValueHandling.Ignore)]
+ public string Locale { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild locale if applicable.
+ /// </summary>
+ [JsonProperty("guild_locale", NullValueHandling = NullValueHandling.Ignore)]
+ public string GuildLocale { get; internal set; }
+
+ /// <summary>
+ /// Creates a response to this interaction.
+ /// </summary>
+ /// <param name="type">The type of the response.</param>
+ /// <param name="builder">The data, if any, to send.</param>
+ public Task CreateResponseAsync(InteractionResponseType type, DiscordInteractionResponseBuilder builder = null)
+ => this.Discord.ApiClient.CreateInteractionResponseAsync(this.Id, this.Token, type, builder);
+
+ /// <summary>
+ /// Creates a modal response to this interaction.
+ /// </summary>
+ /// <param name="builder">The data to send.</param>
+ public Task CreateInteractionModalResponseAsync(DiscordInteractionModalBuilder builder)
+ => this.Type != InteractionType.Ping && this.Type != InteractionType.ModalSubmit ? this.Discord.ApiClient.CreateInteractionModalResponseAsync(this.Id, this.Token, InteractionResponseType.Modal, builder) : throw new NotSupportedException("You can't respond to an PING with a modal.");
+
+ /// <summary>
+ /// Gets the original interaction response.
+ /// </summary>
+ /// <returns>The original message that was sent. This <b>does not work on ephemeral messages.</b></returns>
+ public Task<DiscordMessage> GetOriginalResponseAsync()
+ => this.Discord.ApiClient.GetOriginalInteractionResponseAsync(this.Discord.CurrentApplication.Id, this.Token);
+
+ /// <summary>
+ /// Edits the original interaction response.
+ /// </summary>
+ /// <param name="builder">The webhook builder.</param>
+ /// <returns>The edited <see cref="DiscordMessage"/>.</returns>
+ public async Task<DiscordMessage> EditOriginalResponseAsync(DiscordWebhookBuilder builder)
{
- var attachments = this.Discord.ApiClient.GetOriginalInteractionResponseAsync(this.Discord.CurrentApplication.Id, this.Token).Result.Attachments;
- if (attachments?.Count > 0)
+ builder.Validate(isInteractionResponse: true);
+ if (builder.KeepAttachmentsInternal.HasValue && builder.KeepAttachmentsInternal.Value)
{
- builder.AttachmentsInternal.AddRange(attachments);
+ var attachments = this.Discord.ApiClient.GetOriginalInteractionResponseAsync(this.Discord.CurrentApplication.Id, this.Token).Result.Attachments;
+ if (attachments?.Count > 0)
+ {
+ builder.AttachmentsInternal.AddRange(attachments);
+ }
+ }
+ else if (builder.KeepAttachmentsInternal.HasValue)
+ {
+ builder.AttachmentsInternal.Clear();
}
- }
- else if (builder.KeepAttachmentsInternal.HasValue)
- {
- builder.AttachmentsInternal.Clear();
- }
-
- return await this.Discord.ApiClient.EditOriginalInteractionResponseAsync(this.Discord.CurrentApplication.Id, this.Token, builder).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Deletes the original interaction response.
- /// </summary>>
- public Task DeleteOriginalResponseAsync()
- => this.Discord.ApiClient.DeleteOriginalInteractionResponseAsync(this.Discord.CurrentApplication.Id, this.Token);
-
- /// <summary>
- /// Creates a follow up message to this interaction.
- /// </summary>
- /// <param name="builder">The webhook builder.</param>
- /// <returns>The created <see cref="DiscordMessage"/>.</returns>
- public async Task<DiscordMessage> CreateFollowupMessageAsync(DiscordFollowupMessageBuilder builder)
- {
- builder.Validate();
- return await this.Discord.ApiClient.CreateFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, builder).ConfigureAwait(false);
- }
+ return await this.Discord.ApiClient.EditOriginalInteractionResponseAsync(this.Discord.CurrentApplication.Id, this.Token, builder).ConfigureAwait(false);
+ }
- /// <summary>
- /// Gets a follow up message.
- /// </summary>
- /// <param name="messageId">The id of the follow up message.</param>
- public Task<DiscordMessage> GetFollowupMessageAsync(ulong messageId)
- => this.Discord.ApiClient.GetFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, messageId);
+ /// <summary>
+ /// Deletes the original interaction response.
+ /// </summary>>
+ public Task DeleteOriginalResponseAsync()
+ => this.Discord.ApiClient.DeleteOriginalInteractionResponseAsync(this.Discord.CurrentApplication.Id, this.Token);
+
+ /// <summary>
+ /// Creates a follow up message to this interaction.
+ /// </summary>
+ /// <param name="builder">The webhook builder.</param>
+ /// <returns>The created <see cref="DiscordMessage"/>.</returns>
+ public async Task<DiscordMessage> CreateFollowupMessageAsync(DiscordFollowupMessageBuilder builder)
+ {
+ builder.Validate();
- /// <summary>
- /// Edits a follow up message.
- /// </summary>
- /// <param name="messageId">The id of the follow up message.</param>
- /// <param name="builder">The webhook builder.</param>
- /// <returns>The edited <see cref="DiscordMessage"/>.</returns>
- public async Task<DiscordMessage> EditFollowupMessageAsync(ulong messageId, DiscordWebhookBuilder builder)
- {
- builder.Validate(isFollowup: true);
+ return await this.Discord.ApiClient.CreateFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, builder).ConfigureAwait(false);
+ }
- if (builder.KeepAttachmentsInternal.HasValue && builder.KeepAttachmentsInternal.Value)
+ /// <summary>
+ /// Gets a follow up message.
+ /// </summary>
+ /// <param name="messageId">The id of the follow up message.</param>
+ public Task<DiscordMessage> GetFollowupMessageAsync(ulong messageId)
+ => this.Discord.ApiClient.GetFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, messageId);
+
+ /// <summary>
+ /// Edits a follow up message.
+ /// </summary>
+ /// <param name="messageId">The id of the follow up message.</param>
+ /// <param name="builder">The webhook builder.</param>
+ /// <returns>The edited <see cref="DiscordMessage"/>.</returns>
+ public async Task<DiscordMessage> EditFollowupMessageAsync(ulong messageId, DiscordWebhookBuilder builder)
{
- var attachments = this.Discord.ApiClient.GetFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, messageId).Result.Attachments;
- if (attachments?.Count > 0)
+ builder.Validate(isFollowup: true);
+
+ if (builder.KeepAttachmentsInternal.HasValue && builder.KeepAttachmentsInternal.Value)
{
- builder.AttachmentsInternal.AddRange(attachments);
+ var attachments = this.Discord.ApiClient.GetFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, messageId).Result.Attachments;
+ if (attachments?.Count > 0)
+ {
+ builder.AttachmentsInternal.AddRange(attachments);
+ }
}
- }
- else if (builder.KeepAttachmentsInternal.HasValue)
- {
- builder.AttachmentsInternal.Clear();
+ else if (builder.KeepAttachmentsInternal.HasValue)
+ {
+ builder.AttachmentsInternal.Clear();
+ }
+
+ return await this.Discord.ApiClient.EditFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, messageId, builder).ConfigureAwait(false);
}
- return await this.Discord.ApiClient.EditFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, messageId, builder).ConfigureAwait(false);
+ /// <summary>
+ /// Deletes a follow up message.
+ /// </summary>
+ /// <param name="messageId">The id of the follow up message.</param>
+ public Task DeleteFollowupMessageAsync(ulong messageId)
+ => this.Discord.ApiClient.DeleteFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, messageId);
}
-
- /// <summary>
- /// Deletes a follow up message.
- /// </summary>
- /// <param name="messageId">The id of the follow up message.</param>
- public Task DeleteFollowupMessageAsync(ulong messageId)
- => this.Discord.ApiClient.DeleteFollowupMessageAsync(this.Discord.CurrentApplication.Id, this.Token, messageId);
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteractionApplicationCommandCallbackData.cs b/DisCatSharp/Entities/Interaction/DiscordInteractionApplicationCommandCallbackData.cs
index 3ff059eed..26b7d4cf7 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteractionApplicationCommandCallbackData.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteractionApplicationCommandCallbackData.cs
@@ -1,105 +1,106 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a interactions application command callback data.
-/// </summary>
-internal class DiscordInteractionApplicationCommandCallbackData
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Whether this message is text to speech.
+ /// Represents a interactions application command callback data.
/// </summary>
- [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
- public bool? IsTts { get; internal set; }
+ internal class DiscordInteractionApplicationCommandCallbackData
+ {
+ /// <summary>
+ /// Whether this message is text to speech.
+ /// </summary>
+ [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsTts { get; internal set; }
- /// <summary>
- /// Gets the content.
- /// </summary>
- [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
- public string Content { get; internal set; }
+ /// <summary>
+ /// Gets the content.
+ /// </summary>
+ [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+ public string Content { get; internal set; }
- /// <summary>
- /// Gets the embeds.
- /// </summary>
- [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordEmbed> Embeds { get; internal set; }
+ /// <summary>
+ /// Gets the embeds.
+ /// </summary>
+ [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordEmbed> Embeds { get; internal set; }
- /// <summary>
- /// Gets the mentions.
- /// </summary>
- [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<IMention> Mentions { get; internal set; }
+ /// <summary>
+ /// Gets the mentions.
+ /// </summary>
+ [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<IMention> Mentions { get; internal set; }
- /// <summary>
- /// Gets the flags.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public MessageFlags? Flags { get; internal set; }
+ /// <summary>
+ /// Gets the flags.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public MessageFlags? Flags { get; internal set; }
- /// <summary>
- /// Gets the components.
- /// </summary>
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<DiscordActionRowComponent> Components { get; internal set; }
+ /// <summary>
+ /// Gets the components.
+ /// </summary>
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<DiscordActionRowComponent> Components { get; internal set; }
- /// <summary>
- /// Gets the autocomplete choices.
- /// </summary>
- [JsonProperty("choices", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<DiscordApplicationCommandAutocompleteChoice> Choices { get; internal set; }
+ /// <summary>
+ /// Gets the autocomplete choices.
+ /// </summary>
+ [JsonProperty("choices", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<DiscordApplicationCommandAutocompleteChoice> Choices { get; internal set; }
- /// <summary>
- /// Gets the attachments.
- /// </summary>
- [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
- public List<DiscordAttachment> Attachments { get; set; }
-}
+ /// <summary>
+ /// Gets the attachments.
+ /// </summary>
+ [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
+ public List<DiscordAttachment> Attachments { get; set; }
+ }
-/// <summary>
-/// Represents a interactions application command callback data.
-/// </summary>
-internal class DiscordInteractionApplicationCommandModalCallbackData
-{
/// <summary>
- /// Gets the custom id.
+ /// Represents a interactions application command callback data.
/// </summary>
- [JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
- public string CustomId { get; internal set; }
+ internal class DiscordInteractionApplicationCommandModalCallbackData
+ {
+ /// <summary>
+ /// Gets the custom id.
+ /// </summary>
+ [JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
+ public string CustomId { get; internal set; }
- /// <summary>
- /// Gets the content.
- /// </summary>
- [JsonProperty("title", NullValueHandling = NullValueHandling.Ignore)]
- public string Title { get; internal set; }
+ /// <summary>
+ /// Gets the content.
+ /// </summary>
+ [JsonProperty("title", NullValueHandling = NullValueHandling.Ignore)]
+ public string Title { get; internal set; }
- /// <summary>
- /// Gets the components.
- /// </summary>
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<DiscordComponent> ModalComponents { get; internal set; }
+ /// <summary>
+ /// Gets the components.
+ /// </summary>
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<DiscordComponent> ModalComponents { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteractionData.cs b/DisCatSharp/Entities/Interaction/DiscordInteractionData.cs
index ba5f83799..53046a4e7 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteractionData.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteractionData.cs
@@ -1,90 +1,91 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents the inner data payload of a <see cref="DiscordInteraction"/>.
-/// </summary>
-public sealed class DiscordInteractionData : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of the invoked interaction.
+ /// Represents the inner data payload of a <see cref="DiscordInteraction"/>.
/// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
+ public sealed class DiscordInteractionData : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the name of the invoked interaction.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the parameters and values of the invoked interaction.
- /// </summary>
- [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordInteractionDataOption> Options { get; internal set; }
+ /// <summary>
+ /// Gets the parameters and values of the invoked interaction.
+ /// </summary>
+ [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordInteractionDataOption> Options { get; internal set; }
- /// <summary>
- /// Gets the components (Applicable to modal submits).
- /// </summary>
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordActionRowComponentResult> Components { get; internal set; }
+ /// <summary>
+ /// Gets the components (Applicable to modal submits).
+ /// </summary>
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordActionRowComponentResult> Components { get; internal set; }
- /// <summary>
- /// Gets the Discord snowflake objects resolved from this interaction's arguments.
- /// </summary>
- [JsonProperty("resolved", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordInteractionResolvedCollection Resolved { get; internal set; }
+ /// <summary>
+ /// Gets the Discord snowflake objects resolved from this interaction's arguments.
+ /// </summary>
+ [JsonProperty("resolved", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordInteractionResolvedCollection Resolved { get; internal set; }
- /// <summary>
- /// The Id of the component that invoked this interaction, if applicable.
- /// </summary>
- [JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
- public string CustomId { get; internal set; }
+ /// <summary>
+ /// The Id of the component that invoked this interaction, if applicable.
+ /// </summary>
+ [JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
+ public string CustomId { get; internal set; }
- /// <summary>
- /// The Id of the target. Applicable for context menus.
- /// </summary>
- [JsonProperty("target_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong? Target { get; set; }
+ /// <summary>
+ /// The Id of the target. Applicable for context menus.
+ /// </summary>
+ [JsonProperty("target_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong? Target { get; set; }
- /// <summary>
- /// The type of component that invoked this interaction, if applicable.
- /// </summary>
- [JsonProperty("component_type", NullValueHandling = NullValueHandling.Ignore)]
- public ComponentType ComponentType { get; internal set; }
+ /// <summary>
+ /// The type of component that invoked this interaction, if applicable.
+ /// </summary>
+ [JsonProperty("component_type", NullValueHandling = NullValueHandling.Ignore)]
+ public ComponentType ComponentType { get; internal set; }
- /// <summary>
- /// Gets the values of the interaction.
- /// </summary>
- [JsonProperty("values", NullValueHandling = NullValueHandling.Ignore)]
- public string[] Values { get; internal set; } = Array.Empty<string>();
+ /// <summary>
+ /// Gets the values of the interaction.
+ /// </summary>
+ [JsonProperty("values", NullValueHandling = NullValueHandling.Ignore)]
+ public string[] Values { get; internal set; } = Array.Empty<string>();
- /// <summary>
- /// Gets the type of the interaction.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public ApplicationCommandType Type { get; internal set; }
+ /// <summary>
+ /// Gets the type of the interaction.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public ApplicationCommandType Type { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteractionDataOption.cs b/DisCatSharp/Entities/Interaction/DiscordInteractionDataOption.cs
index 8beb4cd1f..faeca82c7 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteractionDataOption.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteractionDataOption.cs
@@ -1,87 +1,88 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents parameters for interaction commands.
-/// </summary>
-public sealed class DiscordInteractionDataOption
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of this interaction parameter.
+ /// Represents parameters for interaction commands.
/// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
+ public sealed class DiscordInteractionDataOption
+ {
+ /// <summary>
+ /// Gets the name of this interaction parameter.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the type of this interaction parameter.
- /// </summary>
- [JsonProperty("type")]
- public ApplicationCommandOptionType Type { get; internal set; }
+ /// <summary>
+ /// Gets the type of this interaction parameter.
+ /// </summary>
+ [JsonProperty("type")]
+ public ApplicationCommandOptionType Type { get; internal set; }
- /// <summary>
- /// Whether this option is currently focused by the user.
- /// Only applicable for autocomplete option choices.
- /// </summary>
- [JsonProperty("focused")]
- public bool Focused { get; internal set; }
+ /// <summary>
+ /// Whether this option is currently focused by the user.
+ /// Only applicable for autocomplete option choices.
+ /// </summary>
+ [JsonProperty("focused")]
+ public bool Focused { get; internal set; }
- /// <summary>
- /// Gets the value of this interaction parameter.
- /// </summary>
- [JsonProperty("value")]
- internal string RawValue { get; set; }
+ /// <summary>
+ /// Gets the value of this interaction parameter.
+ /// </summary>
+ [JsonProperty("value")]
+ internal string RawValue { get; set; }
- /// <summary>
- /// Gets the value of this interaction parameter.
- /// <para>This can be cast to a <see langword="long"/>, <see langword="bool"></see>, <see langword="string"></see>, <see langword="double"></see> or <see langword="ulong"/> depending on the <see cref="System.Type"/></para>
- /// </summary>
- [JsonIgnore]
- public object Value =>
- this.Type == ApplicationCommandOptionType.Integer && int.TryParse(this.RawValue, out var raw)
- ? raw
- : this.Type == ApplicationCommandOptionType.Integer
- ? long.Parse(this.RawValue)
- : this.Type switch
- {
- ApplicationCommandOptionType.Boolean => bool.Parse(this.RawValue),
- ApplicationCommandOptionType.String => this.RawValue,
- ApplicationCommandOptionType.Channel => ulong.Parse(this.RawValue),
- ApplicationCommandOptionType.User => ulong.Parse(this.RawValue),
- ApplicationCommandOptionType.Role => ulong.Parse(this.RawValue),
- ApplicationCommandOptionType.Mentionable => ulong.Parse(this.RawValue),
- ApplicationCommandOptionType.Number => double.Parse(this.RawValue),
- ApplicationCommandOptionType.Attachment => ulong.Parse(this.RawValue),
- _ => this.RawValue,
- };
+ /// <summary>
+ /// Gets the value of this interaction parameter.
+ /// <para>This can be cast to a <see langword="long"/>, <see langword="bool"></see>, <see langword="string"></see>, <see langword="double"></see> or <see langword="ulong"/> depending on the <see cref="System.Type"/></para>
+ /// </summary>
+ [JsonIgnore]
+ public object Value =>
+ this.Type == ApplicationCommandOptionType.Integer && int.TryParse(this.RawValue, out var raw)
+ ? raw
+ : this.Type == ApplicationCommandOptionType.Integer
+ ? long.Parse(this.RawValue)
+ : this.Type switch
+ {
+ ApplicationCommandOptionType.Boolean => bool.Parse(this.RawValue),
+ ApplicationCommandOptionType.String => this.RawValue,
+ ApplicationCommandOptionType.Channel => ulong.Parse(this.RawValue),
+ ApplicationCommandOptionType.User => ulong.Parse(this.RawValue),
+ ApplicationCommandOptionType.Role => ulong.Parse(this.RawValue),
+ ApplicationCommandOptionType.Mentionable => ulong.Parse(this.RawValue),
+ ApplicationCommandOptionType.Number => double.Parse(this.RawValue),
+ ApplicationCommandOptionType.Attachment => ulong.Parse(this.RawValue),
+ _ => this.RawValue,
+ };
- /// <summary>
- /// Gets the additional parameters if this parameter is a subcommand.
- /// </summary>
- [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordInteractionDataOption> Options { get; internal set; }
+ /// <summary>
+ /// Gets the additional parameters if this parameter is a subcommand.
+ /// </summary>
+ [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordInteractionDataOption> Options { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteractionModalBuilder.cs b/DisCatSharp/Entities/Interaction/DiscordInteractionModalBuilder.cs
index f086c2276..eed462263 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteractionModalBuilder.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteractionModalBuilder.cs
@@ -1,155 +1,156 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Constructs an interaction modal response.
-/// </summary>
-public sealed class DiscordInteractionModalBuilder
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Title of modal.
+ /// Constructs an interaction modal response.
/// </summary>
- public string Title
+ public sealed class DiscordInteractionModalBuilder
{
- get => this._title;
- set
+ /// <summary>
+ /// Title of modal.
+ /// </summary>
+ public string Title
{
- if (value != null && value.Length > 128)
- throw new ArgumentException("Title length cannot exceed 128 characters.", nameof(value));
- this._title = value;
+ get => this._title;
+ set
+ {
+ if (value != null && value.Length > 128)
+ throw new ArgumentException("Title length cannot exceed 128 characters.", nameof(value));
+ this._title = value;
+ }
}
- }
- private string _title;
+ private string _title;
- /// <summary>
- /// Custom id of modal.
- /// </summary>
- public string CustomId { get; set; }
+ /// <summary>
+ /// Custom id of modal.
+ /// </summary>
+ public string CustomId { get; set; }
- /// <summary>
- /// Components to send on this interaction response.
- /// </summary>
- public IReadOnlyList<DiscordActionRowComponent> ModalComponents => this._components;
- private readonly List<DiscordActionRowComponent> _components = new();
+ /// <summary>
+ /// Components to send on this interaction response.
+ /// </summary>
+ public IReadOnlyList<DiscordActionRowComponent> ModalComponents => this._components;
+ private readonly List<DiscordActionRowComponent> _components = new();
- /// <summary>
- /// Constructs a new empty interaction modal builder.
- /// </summary>
- public DiscordInteractionModalBuilder() { }
-
- public DiscordInteractionModalBuilder WithTitle(string title)
- {
- this.Title = title;
- return this;
- }
+ /// <summary>
+ /// Constructs a new empty interaction modal builder.
+ /// </summary>
+ public DiscordInteractionModalBuilder() { }
- public DiscordInteractionModalBuilder WithCustomId(string customId)
- {
- this.CustomId = customId;
- return this;
- }
+ public DiscordInteractionModalBuilder WithTitle(string title)
+ {
+ this.Title = title;
+ return this;
+ }
- /// <summary>
- /// Appends a collection of components to the builder. Each call will append to a new row.
- /// </summary>
- /// <param name="components">The components to append. Up to five.</param>
- /// <returns>The current builder to chain calls with.</returns>
- /// <exception cref="System.ArgumentException">Thrown when passing more than 5 components.</exception>
- public DiscordInteractionModalBuilder AddModalComponents(params DiscordTextComponent[] components)
- => this.AddModalComponents((IEnumerable<DiscordTextComponent>)components);
+ public DiscordInteractionModalBuilder WithCustomId(string customId)
+ {
+ this.CustomId = customId;
+ return this;
+ }
- /// <summary>
- /// Appends a text component to the builder.
- /// </summary>
- /// <param name="component">The component to append.</param>
- /// <returns>The current builder to chain calls with.</returns>
- public DiscordInteractionModalBuilder AddTextComponent(DiscordTextComponent component)
- {
- List<DiscordTextComponent> comp = new(1);
- comp.Add(component);
+ /// <summary>
+ /// Appends a collection of components to the builder. Each call will append to a new row.
+ /// </summary>
+ /// <param name="components">The components to append. Up to five.</param>
+ /// <returns>The current builder to chain calls with.</returns>
+ /// <exception cref="System.ArgumentException">Thrown when passing more than 5 components.</exception>
+ public DiscordInteractionModalBuilder AddModalComponents(params DiscordTextComponent[] components)
+ => this.AddModalComponents((IEnumerable<DiscordTextComponent>)components);
+
+ /// <summary>
+ /// Appends a text component to the builder.
+ /// </summary>
+ /// <param name="component">The component to append.</param>
+ /// <returns>The current builder to chain calls with.</returns>
+ public DiscordInteractionModalBuilder AddTextComponent(DiscordTextComponent component)
+ {
+ List<DiscordTextComponent> comp = new(1);
+ comp.Add(component);
- return this.AddModalComponents(comp);
- }
+ return this.AddModalComponents(comp);
+ }
- /// <summary>
- /// Appends several rows of components to the message
- /// </summary>
- /// <param name="components">The rows of components to add, holding up to five each.</param>
- /// <returns></returns>
- public DiscordInteractionModalBuilder AddModalComponents(IEnumerable<DiscordActionRowComponent> components)
- {
- var ara = components.ToArray();
+ /// <summary>
+ /// Appends several rows of components to the message
+ /// </summary>
+ /// <param name="components">The rows of components to add, holding up to five each.</param>
+ /// <returns></returns>
+ public DiscordInteractionModalBuilder AddModalComponents(IEnumerable<DiscordActionRowComponent> components)
+ {
+ var ara = components.ToArray();
- if (ara.Length + this._components.Count > 5)
- throw new ArgumentException("ActionRow count exceeds maximum of five.");
+ if (ara.Length + this._components.Count > 5)
+ throw new ArgumentException("ActionRow count exceeds maximum of five.");
- foreach (var ar in ara)
- this._components.Add(ar);
+ foreach (var ar in ara)
+ this._components.Add(ar);
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Appends a collection of components to the builder. Each call will append to a new row.
- /// If you add a <see cref="DiscordTextComponent"></see> you can only add one.
- /// </summary>
- /// <param name="components">The components to append. Up to five.</param>
- /// <returns>The current builder to chain calls with.</returns>
- /// <exception cref="System.ArgumentException">Thrown when passing more than 5 components.</exception>
- public DiscordInteractionModalBuilder AddModalComponents(IEnumerable<DiscordTextComponent> components)
- {
- var compArr = components.ToArray();
- var count = compArr.Length;
+ /// <summary>
+ /// Appends a collection of components to the builder. Each call will append to a new row.
+ /// If you add a <see cref="DiscordTextComponent"></see> you can only add one.
+ /// </summary>
+ /// <param name="components">The components to append. Up to five.</param>
+ /// <returns>The current builder to chain calls with.</returns>
+ /// <exception cref="System.ArgumentException">Thrown when passing more than 5 components.</exception>
+ public DiscordInteractionModalBuilder AddModalComponents(IEnumerable<DiscordTextComponent> components)
+ {
+ var compArr = components.ToArray();
+ var count = compArr.Length;
- if (count > 5)
- throw new ArgumentException("Cannot add more than 5 components per action row!");
+ if (count > 5)
+ throw new ArgumentException("Cannot add more than 5 components per action row!");
- if (components.Where(c => c.Type == Enums.ComponentType.InputText).Any() && count < 1)
- throw new ArgumentException("Cannot add more than 1 text components per action row!");
+ if (components.Where(c => c.Type == Enums.ComponentType.InputText).Any() && count < 1)
+ throw new ArgumentException("Cannot add more than 1 text components per action row!");
- var arc = new DiscordActionRowComponent(compArr);
- this._components.Add(arc);
- return this;
- }
+ var arc = new DiscordActionRowComponent(compArr);
+ this._components.Add(arc);
+ return this;
+ }
- /// <summary>
- /// Clears all message components on this builder.
- /// </summary>
- public void ClearComponents()
- => this._components.Clear();
+ /// <summary>
+ /// Clears all message components on this builder.
+ /// </summary>
+ public void ClearComponents()
+ => this._components.Clear();
- /// <summary>
- /// Allows for clearing the Interaction Response Builder so that it can be used again to send a new response.
- /// </summary>
- public void Clear()
- {
- this._components.Clear();
- this.Title = null;
- this.CustomId = null;
+ /// <summary>
+ /// Allows for clearing the Interaction Response Builder so that it can be used again to send a new response.
+ /// </summary>
+ public void Clear()
+ {
+ this._components.Clear();
+ this.Title = null;
+ this.CustomId = null;
+ }
}
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteractionResolvedCollection.cs b/DisCatSharp/Entities/Interaction/DiscordInteractionResolvedCollection.cs
index 7dcd429a6..23972ff52 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteractionResolvedCollection.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteractionResolvedCollection.cs
@@ -1,69 +1,70 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a collection of Discord snowflake objects resolved from interaction arguments.
-/// </summary>
-public sealed class DiscordInteractionResolvedCollection
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the resolved user objects, if any.
+ /// Represents a collection of Discord snowflake objects resolved from interaction arguments.
/// </summary>
- [JsonProperty("users", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyDictionary<ulong, DiscordUser> Users { get; internal set; }
+ public sealed class DiscordInteractionResolvedCollection
+ {
+ /// <summary>
+ /// Gets the resolved user objects, if any.
+ /// </summary>
+ [JsonProperty("users", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyDictionary<ulong, DiscordUser> Users { get; internal set; }
- /// <summary>
- /// Gets the resolved member objects, if any.
- /// </summary>
- [JsonProperty("members", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyDictionary<ulong, DiscordMember> Members { get; internal set; }
+ /// <summary>
+ /// Gets the resolved member objects, if any.
+ /// </summary>
+ [JsonProperty("members", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyDictionary<ulong, DiscordMember> Members { get; internal set; }
- /// <summary>
- /// Gets the resolved channel objects, if any.
- /// </summary>
- [JsonProperty("channels", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyDictionary<ulong, DiscordChannel> Channels { get; internal set; }
+ /// <summary>
+ /// Gets the resolved channel objects, if any.
+ /// </summary>
+ [JsonProperty("channels", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyDictionary<ulong, DiscordChannel> Channels { get; internal set; }
- /// <summary>
- /// Gets the resolved role objects, if any.
- /// </summary>
- [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyDictionary<ulong, DiscordRole> Roles { get; internal set; }
+ /// <summary>
+ /// Gets the resolved role objects, if any.
+ /// </summary>
+ [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyDictionary<ulong, DiscordRole> Roles { get; internal set; }
- /// <summary>
- /// Gets the resolved message objects, if any.
- /// </summary>
- [JsonProperty("messages", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyDictionary<ulong, DiscordMessage> Messages { get; internal set; }
+ /// <summary>
+ /// Gets the resolved message objects, if any.
+ /// </summary>
+ [JsonProperty("messages", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyDictionary<ulong, DiscordMessage> Messages { get; internal set; }
- /// <summary>
- /// Gets the resolved attachments objects, if any.
- /// </summary>
- [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyDictionary<ulong, DiscordAttachment> Attachments { get; internal set; }
+ /// <summary>
+ /// Gets the resolved attachments objects, if any.
+ /// </summary>
+ [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyDictionary<ulong, DiscordAttachment> Attachments { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteractionResponseBuilder.cs b/DisCatSharp/Entities/Interaction/DiscordInteractionResponseBuilder.cs
index 23e377bcd..9613abe83 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteractionResponseBuilder.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteractionResponseBuilder.cs
@@ -1,352 +1,353 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using System.Linq;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Constructs an interaction response.
-/// </summary>
-public sealed class DiscordInteractionResponseBuilder
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Whether this interaction response is text-to-speech.
+ /// Constructs an interaction response.
/// </summary>
- public bool IsTts { get; set; }
-
- /// <summary>
- /// Whether this interaction response should be ephemeral.
- /// </summary>
- public bool IsEphemeral { get; set; }
-
- /// <summary>
- /// Content of the message to send.
- /// </summary>
- public string Content
+ public sealed class DiscordInteractionResponseBuilder
{
- get => this._content;
- set
+ /// <summary>
+ /// Whether this interaction response is text-to-speech.
+ /// </summary>
+ public bool IsTts { get; set; }
+
+ /// <summary>
+ /// Whether this interaction response should be ephemeral.
+ /// </summary>
+ public bool IsEphemeral { get; set; }
+
+ /// <summary>
+ /// Content of the message to send.
+ /// </summary>
+ public string Content
{
- if (value != null && value.Length > 2000)
- throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
- this._content = value;
+ get => this._content;
+ set
+ {
+ if (value != null && value.Length > 2000)
+ throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
+ this._content = value;
+ }
+ }
+ private string _content;
+
+ /// <summary>
+ /// Embeds to send on this interaction response.
+ /// </summary>
+ public IReadOnlyList<DiscordEmbed> Embeds => this._embeds;
+ private readonly List<DiscordEmbed> _embeds = new();
+
+
+ /// <summary>
+ /// Files to send on this interaction response.
+ /// </summary>
+ public IReadOnlyList<DiscordMessageFile> Files => this._files;
+ private readonly List<DiscordMessageFile> _files = new();
+
+ /// <summary>
+ /// Components to send on this interaction response.
+ /// </summary>
+ public IReadOnlyList<DiscordActionRowComponent> Components => this._components;
+ private readonly List<DiscordActionRowComponent> _components = new();
+
+ /// <summary>
+ /// The choices to send on this interaction response.
+ /// Mutually exclusive with content, embed, and components.
+ /// </summary>
+ public IReadOnlyList<DiscordApplicationCommandAutocompleteChoice> Choices => this._choices;
+ private readonly List<DiscordApplicationCommandAutocompleteChoice> _choices = new();
+
+
+ /// <summary>
+ /// Mentions to send on this interaction response.
+ /// </summary>
+ public IEnumerable<IMention> Mentions => this._mentions;
+ private readonly List<IMention> _mentions = new();
+
+ /// <summary>
+ /// Constructs a new empty interaction response builder.
+ /// </summary>
+ public DiscordInteractionResponseBuilder() { }
+
+
+ /// <summary>
+ /// Constructs a new <see cref="DiscordInteractionResponseBuilder"/> based on an existing <see cref="DisCatSharp.Entities.DiscordMessageBuilder"/>.
+ /// </summary>
+ /// <param name="builder">The builder to copy.</param>
+ public DiscordInteractionResponseBuilder(DiscordMessageBuilder builder)
+ {
+ this._content = builder.Content;
+ this._mentions = builder.Mentions;
+ this._embeds.AddRange(builder.Embeds);
+ this._components.AddRange(builder.Components);
}
- }
- private string _content;
-
- /// <summary>
- /// Embeds to send on this interaction response.
- /// </summary>
- public IReadOnlyList<DiscordEmbed> Embeds => this._embeds;
- private readonly List<DiscordEmbed> _embeds = new();
-
-
- /// <summary>
- /// Files to send on this interaction response.
- /// </summary>
- public IReadOnlyList<DiscordMessageFile> Files => this._files;
- private readonly List<DiscordMessageFile> _files = new();
-
- /// <summary>
- /// Components to send on this interaction response.
- /// </summary>
- public IReadOnlyList<DiscordActionRowComponent> Components => this._components;
- private readonly List<DiscordActionRowComponent> _components = new();
-
- /// <summary>
- /// The choices to send on this interaction response.
- /// Mutually exclusive with content, embed, and components.
- /// </summary>
- public IReadOnlyList<DiscordApplicationCommandAutocompleteChoice> Choices => this._choices;
- private readonly List<DiscordApplicationCommandAutocompleteChoice> _choices = new();
-
-
- /// <summary>
- /// Mentions to send on this interaction response.
- /// </summary>
- public IEnumerable<IMention> Mentions => this._mentions;
- private readonly List<IMention> _mentions = new();
-
- /// <summary>
- /// Constructs a new empty interaction response builder.
- /// </summary>
- public DiscordInteractionResponseBuilder() { }
-
-
- /// <summary>
- /// Constructs a new <see cref="DiscordInteractionResponseBuilder"/> based on an existing <see cref="DisCatSharp.Entities.DiscordMessageBuilder"/>.
- /// </summary>
- /// <param name="builder">The builder to copy.</param>
- public DiscordInteractionResponseBuilder(DiscordMessageBuilder builder)
- {
- this._content = builder.Content;
- this._mentions = builder.Mentions;
- this._embeds.AddRange(builder.Embeds);
- this._components.AddRange(builder.Components);
- }
-
-
- /// <summary>
- /// Appends a collection of components to the builder. Each call will append to a new row.
- /// </summary>
- /// <param name="components">The components to append. Up to five.</param>
- /// <returns>The current builder to chain calls with.</returns>
- /// <exception cref="System.ArgumentException">Thrown when passing more than 5 components.</exception>
- public DiscordInteractionResponseBuilder AddComponents(params DiscordComponent[] components)
- => this.AddComponents((IEnumerable<DiscordComponent>)components);
-
- /// <summary>
- /// Appends several rows of components to the message
- /// </summary>
- /// <param name="components">The rows of components to add, holding up to five each.</param>
- /// <returns></returns>
- public DiscordInteractionResponseBuilder AddComponents(IEnumerable<DiscordActionRowComponent> components)
- {
- var ara = components.ToArray();
-
- if (ara.Length + this._components.Count > 5)
- throw new ArgumentException("ActionRow count exceeds maximum of five.");
-
- foreach (var ar in ara)
- this._components.Add(ar);
- return this;
- }
- /// <summary>
- /// Appends a collection of components to the builder. Each call will append to a new row.
- /// </summary>
- /// <param name="components">The components to append. Up to five.</param>
- /// <returns>The current builder to chain calls with.</returns>
- /// <exception cref="System.ArgumentException">Thrown when passing more than 5 components.</exception>
- public DiscordInteractionResponseBuilder AddComponents(IEnumerable<DiscordComponent> components)
- {
- var compArr = components.ToArray();
- var count = compArr.Length;
+ /// <summary>
+ /// Appends a collection of components to the builder. Each call will append to a new row.
+ /// </summary>
+ /// <param name="components">The components to append. Up to five.</param>
+ /// <returns>The current builder to chain calls with.</returns>
+ /// <exception cref="System.ArgumentException">Thrown when passing more than 5 components.</exception>
+ public DiscordInteractionResponseBuilder AddComponents(params DiscordComponent[] components)
+ => this.AddComponents((IEnumerable<DiscordComponent>)components);
+
+ /// <summary>
+ /// Appends several rows of components to the message
+ /// </summary>
+ /// <param name="components">The rows of components to add, holding up to five each.</param>
+ /// <returns></returns>
+ public DiscordInteractionResponseBuilder AddComponents(IEnumerable<DiscordActionRowComponent> components)
+ {
+ var ara = components.ToArray();
- if (count > 5)
- throw new ArgumentException("Cannot add more than 5 components per action row!");
+ if (ara.Length + this._components.Count > 5)
+ throw new ArgumentException("ActionRow count exceeds maximum of five.");
- var arc = new DiscordActionRowComponent(compArr);
- this._components.Add(arc);
- return this;
- }
+ foreach (var ar in ara)
+ this._components.Add(ar);
- /// <summary>
- /// Indicates if the interaction response will be text-to-speech.
- /// </summary>
- /// <param name="tts">Text-to-speech</param>
- public DiscordInteractionResponseBuilder WithTts(bool tts)
- {
- this.IsTts = tts;
- return this;
- }
-
- /// <summary>
- /// Sets the interaction response to be ephemeral.
- /// </summary>
- /// <param name="ephemeral">Whether the response should be ephemeral. Defaults to true.</param>
- public DiscordInteractionResponseBuilder AsEphemeral(bool ephemeral = true)
- {
- this.IsEphemeral = ephemeral;
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Sets the content of the message to send.
- /// </summary>
- /// <param name="content">Content to send.</param>
- public DiscordInteractionResponseBuilder WithContent(string content)
- {
- this.Content = content;
- return this;
- }
+ /// <summary>
+ /// Appends a collection of components to the builder. Each call will append to a new row.
+ /// </summary>
+ /// <param name="components">The components to append. Up to five.</param>
+ /// <returns>The current builder to chain calls with.</returns>
+ /// <exception cref="System.ArgumentException">Thrown when passing more than 5 components.</exception>
+ public DiscordInteractionResponseBuilder AddComponents(IEnumerable<DiscordComponent> components)
+ {
+ var compArr = components.ToArray();
+ var count = compArr.Length;
- /// <summary>
- /// Adds an embed to send with the interaction response.
- /// </summary>
- /// <param name="embed">Embed to add.</param>
- public DiscordInteractionResponseBuilder AddEmbed(DiscordEmbed embed)
- {
- if (embed != null)
- this._embeds.Add(embed); // Interactions will 400 silently //
- return this;
- }
+ if (count > 5)
+ throw new ArgumentException("Cannot add more than 5 components per action row!");
- /// <summary>
- /// Adds the given embeds to send with the interaction response.
- /// </summary>
- /// <param name="embeds">Embeds to add.</param>
- public DiscordInteractionResponseBuilder AddEmbeds(IEnumerable<DiscordEmbed> embeds)
- {
- this._embeds.AddRange(embeds);
- return this;
- }
+ var arc = new DiscordActionRowComponent(compArr);
+ this._components.Add(arc);
+ return this;
+ }
- /// <summary>
- /// Adds a file to the interaction response.
- /// </summary>
- /// <param name="filename">Name of the file.</param>
- /// <param name="data">File data.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <param name="description">Description of the file.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordInteractionResponseBuilder AddFile(string filename, Stream data, bool resetStreamPosition = false, string description = null)
- {
- if (this.Files.Count >= 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ /// <summary>
+ /// Indicates if the interaction response will be text-to-speech.
+ /// </summary>
+ /// <param name="tts">Text-to-speech</param>
+ public DiscordInteractionResponseBuilder WithTts(bool tts)
+ {
+ this.IsTts = tts;
+ return this;
+ }
- if (this._files.Any(x => x.FileName == filename))
- throw new ArgumentException("A File with that filename already exists");
+ /// <summary>
+ /// Sets the interaction response to be ephemeral.
+ /// </summary>
+ /// <param name="ephemeral">Whether the response should be ephemeral. Defaults to true.</param>
+ public DiscordInteractionResponseBuilder AsEphemeral(bool ephemeral = true)
+ {
+ this.IsEphemeral = ephemeral;
+ return this;
+ }
- if (resetStreamPosition)
- this._files.Add(new DiscordMessageFile(filename, data, data.Position, description: description));
- else
- this._files.Add(new DiscordMessageFile(filename, data, null, description: description));
+ /// <summary>
+ /// Sets the content of the message to send.
+ /// </summary>
+ /// <param name="content">Content to send.</param>
+ public DiscordInteractionResponseBuilder WithContent(string content)
+ {
+ this.Content = content;
+ return this;
+ }
- return this;
- }
+ /// <summary>
+ /// Adds an embed to send with the interaction response.
+ /// </summary>
+ /// <param name="embed">Embed to add.</param>
+ public DiscordInteractionResponseBuilder AddEmbed(DiscordEmbed embed)
+ {
+ if (embed != null)
+ this._embeds.Add(embed); // Interactions will 400 silently //
+ return this;
+ }
- /// <summary>
- /// Sets if the message has files to be sent.
- /// </summary>
- /// <param name="stream">The Stream to the file.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <param name="description">Description of the file.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordInteractionResponseBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string description = null)
- {
- if (this.Files.Count >= 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ /// <summary>
+ /// Adds the given embeds to send with the interaction response.
+ /// </summary>
+ /// <param name="embeds">Embeds to add.</param>
+ public DiscordInteractionResponseBuilder AddEmbeds(IEnumerable<DiscordEmbed> embeds)
+ {
+ this._embeds.AddRange(embeds);
+ return this;
+ }
- if (this._files.Any(x => x.FileName == stream.Name))
- throw new ArgumentException("A File with that filename already exists");
+ /// <summary>
+ /// Adds a file to the interaction response.
+ /// </summary>
+ /// <param name="filename">Name of the file.</param>
+ /// <param name="data">File data.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <param name="description">Description of the file.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordInteractionResponseBuilder AddFile(string filename, Stream data, bool resetStreamPosition = false, string description = null)
+ {
+ if (this.Files.Count >= 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (resetStreamPosition)
- this._files.Add(new DiscordMessageFile(stream.Name, stream, stream.Position, description: description));
- else
- this._files.Add(new DiscordMessageFile(stream.Name, stream, null, description: description));
+ if (this._files.Any(x => x.FileName == filename))
+ throw new ArgumentException("A File with that filename already exists");
- return this;
- }
+ if (resetStreamPosition)
+ this._files.Add(new DiscordMessageFile(filename, data, data.Position, description: description));
+ else
+ this._files.Add(new DiscordMessageFile(filename, data, null, description: description));
- /// <summary>
- /// Adds the given files to the interaction response builder.
- /// </summary>
- /// <param name="files">Dictionary of file name and file data.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <returns>The builder to chain calls with.</returns>
- public DiscordInteractionResponseBuilder AddFiles(Dictionary<string, Stream> files, bool resetStreamPosition = false)
- {
- if (this.Files.Count + files.Count > 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ return this;
+ }
- foreach (var file in files)
+ /// <summary>
+ /// Sets if the message has files to be sent.
+ /// </summary>
+ /// <param name="stream">The Stream to the file.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <param name="description">Description of the file.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordInteractionResponseBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string description = null)
{
- if (this._files.Any(x => x.FileName == file.Key))
+ if (this.Files.Count >= 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
+
+ if (this._files.Any(x => x.FileName == stream.Name))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new DiscordMessageFile(file.Key, file.Value, file.Value.Position));
+ this._files.Add(new DiscordMessageFile(stream.Name, stream, stream.Position, description: description));
else
- this._files.Add(new DiscordMessageFile(file.Key, file.Value, null));
+ this._files.Add(new DiscordMessageFile(stream.Name, stream, null, description: description));
+
+ return this;
}
+ /// <summary>
+ /// Adds the given files to the interaction response builder.
+ /// </summary>
+ /// <param name="files">Dictionary of file name and file data.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <returns>The builder to chain calls with.</returns>
+ public DiscordInteractionResponseBuilder AddFiles(Dictionary<string, Stream> files, bool resetStreamPosition = false)
+ {
+ if (this.Files.Count + files.Count > 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
- return this;
- }
+ foreach (var file in files)
+ {
+ if (this._files.Any(x => x.FileName == file.Key))
+ throw new ArgumentException("A File with that filename already exists");
- /// <summary>
- /// Adds the mention to the mentions to parse, etc. with the interaction response.
- /// </summary>
- /// <param name="mention">Mention to add.</param>
- public DiscordInteractionResponseBuilder AddMention(IMention mention)
- {
- this._mentions.Add(mention);
- return this;
- }
+ if (resetStreamPosition)
+ this._files.Add(new DiscordMessageFile(file.Key, file.Value, file.Value.Position));
+ else
+ this._files.Add(new DiscordMessageFile(file.Key, file.Value, null));
+ }
- /// <summary>
- /// Adds the mentions to the mentions to parse, etc. with the interaction response.
- /// </summary>
- /// <param name="mentions">Mentions to add.</param>
- public DiscordInteractionResponseBuilder AddMentions(IEnumerable<IMention> mentions)
- {
- this._mentions.AddRange(mentions);
- return this;
- }
- /// <summary>
- /// Adds a single auto-complete choice to the builder.
- /// </summary>
- /// <param name="choice">The choice to add.</param>
- /// <returns>The current builder to chain calls with.</returns>
- public DiscordInteractionResponseBuilder AddAutoCompleteChoice(DiscordApplicationCommandAutocompleteChoice choice)
- {
- this._choices.Add(choice);
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Adds auto-complete choices to the builder.
- /// </summary>
- /// <param name="choices">The choices to add.</param>
- /// <returns>The current builder to chain calls with.</returns>
- public DiscordInteractionResponseBuilder AddAutoCompleteChoices(IEnumerable<DiscordApplicationCommandAutocompleteChoice> choices)
- {
- this._choices.AddRange(choices);
- return this;
- }
+ /// <summary>
+ /// Adds the mention to the mentions to parse, etc. with the interaction response.
+ /// </summary>
+ /// <param name="mention">Mention to add.</param>
+ public DiscordInteractionResponseBuilder AddMention(IMention mention)
+ {
+ this._mentions.Add(mention);
+ return this;
+ }
- /// <summary>
- /// Adds auto-complete choices to the builder.
- /// </summary>
- /// <param name="choices">The choices to add.</param>
- /// <returns>The current builder to chain calls with.</returns>
- public DiscordInteractionResponseBuilder AddAutoCompleteChoices(params DiscordApplicationCommandAutocompleteChoice[] choices)
- => this.AddAutoCompleteChoices((IEnumerable<DiscordApplicationCommandAutocompleteChoice>)choices);
+ /// <summary>
+ /// Adds the mentions to the mentions to parse, etc. with the interaction response.
+ /// </summary>
+ /// <param name="mentions">Mentions to add.</param>
+ public DiscordInteractionResponseBuilder AddMentions(IEnumerable<IMention> mentions)
+ {
+ this._mentions.AddRange(mentions);
+ return this;
+ }
- /// <summary>
- /// Clears all message components on this builder.
- /// </summary>
- public void ClearComponents()
- => this._components.Clear();
+ /// <summary>
+ /// Adds a single auto-complete choice to the builder.
+ /// </summary>
+ /// <param name="choice">The choice to add.</param>
+ /// <returns>The current builder to chain calls with.</returns>
+ public DiscordInteractionResponseBuilder AddAutoCompleteChoice(DiscordApplicationCommandAutocompleteChoice choice)
+ {
+ this._choices.Add(choice);
+ return this;
+ }
- /// <summary>
- /// Allows for clearing the Interaction Response Builder so that it can be used again to send a new response.
- /// </summary>
- public void Clear()
- {
- this.Content = "";
- this._embeds.Clear();
- this.IsTts = false;
- this.IsEphemeral = false;
- this._mentions.Clear();
- this._components.Clear();
- this._choices.Clear();
- this._files.Clear();
+ /// <summary>
+ /// Adds auto-complete choices to the builder.
+ /// </summary>
+ /// <param name="choices">The choices to add.</param>
+ /// <returns>The current builder to chain calls with.</returns>
+ public DiscordInteractionResponseBuilder AddAutoCompleteChoices(IEnumerable<DiscordApplicationCommandAutocompleteChoice> choices)
+ {
+ this._choices.AddRange(choices);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds auto-complete choices to the builder.
+ /// </summary>
+ /// <param name="choices">The choices to add.</param>
+ /// <returns>The current builder to chain calls with.</returns>
+ public DiscordInteractionResponseBuilder AddAutoCompleteChoices(params DiscordApplicationCommandAutocompleteChoice[] choices)
+ => this.AddAutoCompleteChoices((IEnumerable<DiscordApplicationCommandAutocompleteChoice>)choices);
+
+ /// <summary>
+ /// Clears all message components on this builder.
+ /// </summary>
+ public void ClearComponents()
+ => this._components.Clear();
+
+ /// <summary>
+ /// Allows for clearing the Interaction Response Builder so that it can be used again to send a new response.
+ /// </summary>
+ public void Clear()
+ {
+ this.Content = "";
+ this._embeds.Clear();
+ this.IsTts = false;
+ this.IsEphemeral = false;
+ this._mentions.Clear();
+ this._components.Clear();
+ this._choices.Clear();
+ this._files.Clear();
+ }
}
}
diff --git a/DisCatSharp/Entities/Invite/DiscordInvite.cs b/DisCatSharp/Entities/Invite/DiscordInvite.cs
index 5c10f33f3..18f80b77f 100644
--- a/DisCatSharp/Entities/Invite/DiscordInvite.cs
+++ b/DisCatSharp/Entities/Invite/DiscordInvite.cs
@@ -1,198 +1,199 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord invite.
-/// </summary>
-public class DiscordInvite
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the base client.
- /// </summary>
- internal BaseDiscordClient Discord { get; set; }
-
- /// <summary>
- /// Gets the invite's code.
- /// </summary>
- [JsonProperty("code", NullValueHandling = NullValueHandling.Ignore)]
- public string Code { get; internal set; }
-
- /// <summary>
- /// Gets the invite's url.
- /// </summary>
- [JsonIgnore]
- public string Url => DiscordDomain.GetDomain(CoreDomain.DiscordShortlink).Url + "/" + this.Code;
-
- /// <summary>
- /// Gets the invite's url as Uri.
- /// </summary>
- [JsonIgnore]
- public Uri Uri => new(this.Url);
-
- /// <summary>
- /// Gets the guild this invite is for.
- /// </summary>
- [JsonProperty("guild", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordInviteGuild Guild { get; internal set; }
-
- /// <summary>
- /// Gets the channel this invite is for.
- /// </summary>
- [JsonProperty("channel", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordInviteChannel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the target type for the voice channel this invite is for.
- /// </summary>
- [JsonProperty("target_type", NullValueHandling = NullValueHandling.Ignore)]
- public TargetType? TargetType { get; internal set; }
-
- /// <summary>
- /// Gets the type of this invite.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public InviteType Type { get; internal set; }
-
- /// <summary>
- /// Gets the user that is currently livestreaming.
- /// </summary>
- [JsonProperty("target_user", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUser TargetUser { get; internal set; }
-
- /// <summary>
- /// Gets the embedded partial application to open for this voice channel.
- /// </summary>
- [JsonProperty("target_application", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordApplication TargetApplication { get; internal set; }
-
- /// <summary>
- /// Gets the approximate guild online member count for the invite.
- /// </summary>
- [JsonProperty("approximate_presence_count", NullValueHandling = NullValueHandling.Ignore)]
- public int? ApproximatePresenceCount { get; internal set; }
-
- /// <summary>
- /// Gets the approximate guild total member count for the invite.
- /// </summary>
- [JsonProperty("approximate_member_count", NullValueHandling = NullValueHandling.Ignore)]
- public int? ApproximateMemberCount { get; internal set; }
-
- /// <summary>
- /// Gets the user who created the invite.
- /// </summary>
- [JsonProperty("inviter", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUser Inviter { get; internal set; }
-
- /// <summary>
- /// Gets the number of times this invite has been used.
- /// </summary>
- [JsonProperty("uses", NullValueHandling = NullValueHandling.Ignore)]
- public int Uses { get; internal set; }
-
- /// <summary>
- /// Gets the max number of times this invite can be used.
- /// </summary>
- [JsonProperty("max_uses", NullValueHandling = NullValueHandling.Ignore)]
- public int MaxUses { get; internal set; }
-
- /// <summary>
- /// Gets duration in seconds after which the invite expires.
- /// </summary>
- [JsonProperty("max_age", NullValueHandling = NullValueHandling.Ignore)]
- public int MaxAge { get; internal set; }
-
- /// <summary>
- /// Gets whether this invite only grants temporary membership.
- /// </summary>
- [JsonProperty("temporary", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsTemporary { get; internal set; }
-
- /// <summary>
- /// Gets the date and time this invite was created.
- /// </summary>
- [JsonProperty("created_at", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset CreatedAt { get; internal set; }
-
- /// <summary>
- /// Gets the date and time when this invite expires.
- /// </summary>
- [JsonProperty("expires_at", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset ExpiresAt { get; internal set; }
-
- /// <summary>
- /// Gets the date and time when this invite got expired.
- /// </summary>
- [JsonProperty("expired_at", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset ExpiredAt { get; internal set; }
-
- /// <summary>
- /// Gets whether this invite is revoked.
- /// </summary>
- [JsonProperty("revoked", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsRevoked { get; internal set; }
-
- /// <summary>
- /// Gets the stage instance this invite is for.
- /// </summary>
- [JsonProperty("stage_instance", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordInviteStage Stage { get; internal set; }
-
- /// <summary>
- /// Gets the guild scheduled event data for the invite.
- /// </summary>
- [JsonProperty("guild_scheduled_event", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordScheduledEvent GuildScheduledEvent { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordInvite"/> class.
- /// </summary>
- internal DiscordInvite()
- { }
-
- /// <summary>
- /// Deletes the invite.
- /// </summary>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns></returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission or the <see cref="Permissions.ManageGuild"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordInvite> DeleteAsync(string reason = null)
- => this.Discord.ApiClient.DeleteInviteAsync(this.Code, reason);
-
- /// <summary>
- /// Converts this invite into an invite link.
- /// </summary>
- /// <returns>A discord.gg invite link.</returns>
- public override string ToString()
- => $"{DiscordDomain.GetDomain(CoreDomain.DiscordShortlink).Url}/{this.Code}";
+ /// Represents a Discord invite.
+ /// </summary>
+ public class DiscordInvite
+ {
+ /// <summary>
+ /// Gets the base client.
+ /// </summary>
+ internal BaseDiscordClient Discord { get; set; }
+
+ /// <summary>
+ /// Gets the invite's code.
+ /// </summary>
+ [JsonProperty("code", NullValueHandling = NullValueHandling.Ignore)]
+ public string Code { get; internal set; }
+
+ /// <summary>
+ /// Gets the invite's url.
+ /// </summary>
+ [JsonIgnore]
+ public string Url => DiscordDomain.GetDomain(CoreDomain.DiscordShortlink).Url + "/" + this.Code;
+
+ /// <summary>
+ /// Gets the invite's url as Uri.
+ /// </summary>
+ [JsonIgnore]
+ public Uri Uri => new(this.Url);
+
+ /// <summary>
+ /// Gets the guild this invite is for.
+ /// </summary>
+ [JsonProperty("guild", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordInviteGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Gets the channel this invite is for.
+ /// </summary>
+ [JsonProperty("channel", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordInviteChannel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the target type for the voice channel this invite is for.
+ /// </summary>
+ [JsonProperty("target_type", NullValueHandling = NullValueHandling.Ignore)]
+ public TargetType? TargetType { get; internal set; }
+
+ /// <summary>
+ /// Gets the type of this invite.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public InviteType Type { get; internal set; }
+
+ /// <summary>
+ /// Gets the user that is currently livestreaming.
+ /// </summary>
+ [JsonProperty("target_user", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUser TargetUser { get; internal set; }
+
+ /// <summary>
+ /// Gets the embedded partial application to open for this voice channel.
+ /// </summary>
+ [JsonProperty("target_application", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordApplication TargetApplication { get; internal set; }
+
+ /// <summary>
+ /// Gets the approximate guild online member count for the invite.
+ /// </summary>
+ [JsonProperty("approximate_presence_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int? ApproximatePresenceCount { get; internal set; }
+
+ /// <summary>
+ /// Gets the approximate guild total member count for the invite.
+ /// </summary>
+ [JsonProperty("approximate_member_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int? ApproximateMemberCount { get; internal set; }
+
+ /// <summary>
+ /// Gets the user who created the invite.
+ /// </summary>
+ [JsonProperty("inviter", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUser Inviter { get; internal set; }
+
+ /// <summary>
+ /// Gets the number of times this invite has been used.
+ /// </summary>
+ [JsonProperty("uses", NullValueHandling = NullValueHandling.Ignore)]
+ public int Uses { get; internal set; }
+
+ /// <summary>
+ /// Gets the max number of times this invite can be used.
+ /// </summary>
+ [JsonProperty("max_uses", NullValueHandling = NullValueHandling.Ignore)]
+ public int MaxUses { get; internal set; }
+
+ /// <summary>
+ /// Gets duration in seconds after which the invite expires.
+ /// </summary>
+ [JsonProperty("max_age", NullValueHandling = NullValueHandling.Ignore)]
+ public int MaxAge { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this invite only grants temporary membership.
+ /// </summary>
+ [JsonProperty("temporary", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsTemporary { get; internal set; }
+
+ /// <summary>
+ /// Gets the date and time this invite was created.
+ /// </summary>
+ [JsonProperty("created_at", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset CreatedAt { get; internal set; }
+
+ /// <summary>
+ /// Gets the date and time when this invite expires.
+ /// </summary>
+ [JsonProperty("expires_at", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset ExpiresAt { get; internal set; }
+
+ /// <summary>
+ /// Gets the date and time when this invite got expired.
+ /// </summary>
+ [JsonProperty("expired_at", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset ExpiredAt { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this invite is revoked.
+ /// </summary>
+ [JsonProperty("revoked", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsRevoked { get; internal set; }
+
+ /// <summary>
+ /// Gets the stage instance this invite is for.
+ /// </summary>
+ [JsonProperty("stage_instance", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordInviteStage Stage { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild scheduled event data for the invite.
+ /// </summary>
+ [JsonProperty("guild_scheduled_event", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordScheduledEvent GuildScheduledEvent { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordInvite"/> class.
+ /// </summary>
+ internal DiscordInvite()
+ { }
+
+ /// <summary>
+ /// Deletes the invite.
+ /// </summary>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns></returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageChannels"/> permission or the <see cref="Permissions.ManageGuild"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordInvite> DeleteAsync(string reason = null)
+ => this.Discord.ApiClient.DeleteInviteAsync(this.Code, reason);
+
+ /// <summary>
+ /// Converts this invite into an invite link.
+ /// </summary>
+ /// <returns>A discord.gg invite link.</returns>
+ public override string ToString()
+ => $"{DiscordDomain.GetDomain(CoreDomain.DiscordShortlink).Url}/{this.Code}";
+ }
}
diff --git a/DisCatSharp/Entities/Invite/DiscordInviteChannel.cs b/DisCatSharp/Entities/Invite/DiscordInviteChannel.cs
index 4c1a51fa4..107f70472 100644
--- a/DisCatSharp/Entities/Invite/DiscordInviteChannel.cs
+++ b/DisCatSharp/Entities/Invite/DiscordInviteChannel.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents the channel to which an invite is linked.
-/// </summary>
-public class DiscordInviteChannel : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of the channel.
+ /// Represents the channel to which an invite is linked.
/// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
+ public class DiscordInviteChannel : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the name of the channel.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the type of the channel.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public ChannelType Type { get; internal set; }
+ /// <summary>
+ /// Gets the type of the channel.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public ChannelType Type { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordInviteChannel"/> class.
- /// </summary>
- internal DiscordInviteChannel()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordInviteChannel"/> class.
+ /// </summary>
+ internal DiscordInviteChannel()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Invite/DiscordInviteGuild.cs b/DisCatSharp/Entities/Invite/DiscordInviteGuild.cs
index bf7f7a6d5..837c30d3e 100644
--- a/DisCatSharp/Entities/Invite/DiscordInviteGuild.cs
+++ b/DisCatSharp/Entities/Invite/DiscordInviteGuild.cs
@@ -1,130 +1,131 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Globalization;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a guild to which the user is invited.
-/// </summary>
-public class DiscordInviteGuild : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of the guild.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the guild icon's hash.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
- public string IconHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild icon's url.
- /// </summary>
- [JsonIgnore]
- public string IconUrl
- => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.jpg" : null;
-
- /// <summary>
- /// Gets the hash of guild's invite splash.
- /// </summary>
- [JsonProperty("splash", NullValueHandling = NullValueHandling.Ignore)]
- internal string SplashHash { get; set; }
-
- /// <summary>
- /// Gets the URL of guild's invite splash.
- /// </summary>
- [JsonIgnore]
- public string SplashUrl
- => !string.IsNullOrWhiteSpace(this.SplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.SplashHash}.jpg" : null;
-
- /// <summary>
- /// Gets the guild's banner hash, when applicable.
- /// </summary>
- [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
- public string Banner { get; internal set; }
-
- /// <summary>
- /// Gets the guild's banner in url form.
- /// </summary>
- [JsonIgnore]
- public string BannerUrl
- => !string.IsNullOrWhiteSpace(this.Banner) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.BANNERS}/{this.Id}/{this.Banner}" : null;
-
- /// <summary>
- /// Gets the guild description, when applicable.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
-
- /// <summary>
- /// Gets a collection of this guild's features.
- /// </summary>
- [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<string> Features { get; internal set; }
-
- /// <summary>
- /// Gets the guild's verification level.
- /// </summary>
- [JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
- public VerificationLevel VerificationLevel { get; internal set; }
-
- /// <summary>
- /// Gets vanity URL code for this guild, when applicable.
- /// </summary>
- [JsonProperty("vanity_url_code")]
- public string VanityUrlCode { get; internal set; }
-
- /// <summary>
- /// Gets the guild's welcome screen, when applicable.
- /// </summary>
- [JsonProperty("welcome_screen", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordGuildWelcomeScreen WelcomeScreen { get; internal set; }
-
- /// <summary>
- /// Gets the guild nsfw status.
- /// </summary>
- [JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsNsfw { get; internal set; }
-
- /// <summary>
- /// Gets the guild nsfw level.
- /// </summary>
- [JsonProperty("nsfw_level", NullValueHandling = NullValueHandling.Ignore)]
- public NsfwLevel NsfwLevel { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordInviteGuild"/> class.
+ /// Represents a guild to which the user is invited.
/// </summary>
- internal DiscordInviteGuild()
- { }
+ public class DiscordInviteGuild : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the name of the guild.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild icon's hash.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
+ public string IconHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild icon's url.
+ /// </summary>
+ [JsonIgnore]
+ public string IconUrl
+ => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.jpg" : null;
+
+ /// <summary>
+ /// Gets the hash of guild's invite splash.
+ /// </summary>
+ [JsonProperty("splash", NullValueHandling = NullValueHandling.Ignore)]
+ internal string SplashHash { get; set; }
+
+ /// <summary>
+ /// Gets the URL of guild's invite splash.
+ /// </summary>
+ [JsonIgnore]
+ public string SplashUrl
+ => !string.IsNullOrWhiteSpace(this.SplashHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.SPLASHES}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.SplashHash}.jpg" : null;
+
+ /// <summary>
+ /// Gets the guild's banner hash, when applicable.
+ /// </summary>
+ [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
+ public string Banner { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild's banner in url form.
+ /// </summary>
+ [JsonIgnore]
+ public string BannerUrl
+ => !string.IsNullOrWhiteSpace(this.Banner) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.BANNERS}/{this.Id}/{this.Banner}" : null;
+
+ /// <summary>
+ /// Gets the guild description, when applicable.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
+
+ /// <summary>
+ /// Gets a collection of this guild's features.
+ /// </summary>
+ [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<string> Features { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild's verification level.
+ /// </summary>
+ [JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
+ public VerificationLevel VerificationLevel { get; internal set; }
+
+ /// <summary>
+ /// Gets vanity URL code for this guild, when applicable.
+ /// </summary>
+ [JsonProperty("vanity_url_code")]
+ public string VanityUrlCode { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild's welcome screen, when applicable.
+ /// </summary>
+ [JsonProperty("welcome_screen", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordGuildWelcomeScreen WelcomeScreen { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild nsfw status.
+ /// </summary>
+ [JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsNsfw { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild nsfw level.
+ /// </summary>
+ [JsonProperty("nsfw_level", NullValueHandling = NullValueHandling.Ignore)]
+ public NsfwLevel NsfwLevel { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordInviteGuild"/> class.
+ /// </summary>
+ internal DiscordInviteGuild()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Invite/DiscordInviteStage.cs b/DisCatSharp/Entities/Invite/DiscordInviteStage.cs
index 7b608889a..e656228ef 100644
--- a/DisCatSharp/Entities/Invite/DiscordInviteStage.cs
+++ b/DisCatSharp/Entities/Invite/DiscordInviteStage.cs
@@ -1,72 +1,73 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Concurrent;
using System.Collections.Generic;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a stage instance to which the user is invited.
-/// </summary>
-public class DiscordInviteStage : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the members speaking in the Stage.
+ /// Represents a stage instance to which the user is invited.
/// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordMember> Members { get; internal set; }
+ public class DiscordInviteStage : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the members speaking in the Stage.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordMember> Members { get; internal set; }
- [JsonProperty("members", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordMember> MembersInternal = new();
+ [JsonProperty("members", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordMember> MembersInternal = new();
- /// <summary>
- /// Gets the number of users in the Stage.
- /// </summary>
- [JsonProperty("participant_count", NullValueHandling = NullValueHandling.Ignore)]
- public int ParticipantCount { get; internal set; }
+ /// <summary>
+ /// Gets the number of users in the Stage.
+ /// </summary>
+ [JsonProperty("participant_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int ParticipantCount { get; internal set; }
- /// <summary>
- /// Gets the number of users speaking in the Stage.
- /// </summary>
- [JsonProperty("speaker_count", NullValueHandling = NullValueHandling.Ignore)]
- public int SpeakerCount { get; internal set; }
+ /// <summary>
+ /// Gets the number of users speaking in the Stage.
+ /// </summary>
+ [JsonProperty("speaker_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int SpeakerCount { get; internal set; }
- /// <summary>
- /// Gets the topic of the Stage instance.
- /// </summary>
- [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
- public string Topic { get; internal set; }
+ /// <summary>
+ /// Gets the topic of the Stage instance.
+ /// </summary>
+ [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
+ public string Topic { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordInviteStage"/> class.
- /// </summary>
- internal DiscordInviteStage()
- {
- this.Members = new ReadOnlyConcurrentDictionary<ulong, DiscordMember>(this.MembersInternal);
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordInviteStage"/> class.
+ /// </summary>
+ internal DiscordInviteStage()
+ {
+ this.Members = new ReadOnlyConcurrentDictionary<ulong, DiscordMember>(this.MembersInternal);
+ }
}
}
diff --git a/DisCatSharp/Entities/Message/DiscordAttachment.cs b/DisCatSharp/Entities/Message/DiscordAttachment.cs
index cf6162ce8..c438e10df 100644
--- a/DisCatSharp/Entities/Message/DiscordAttachment.cs
+++ b/DisCatSharp/Entities/Message/DiscordAttachment.cs
@@ -1,93 +1,94 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an attachment for a message.
-/// </summary>
-public class DiscordAttachment : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the name of the file.
+ /// Represents an attachment for a message.
/// </summary>
- [JsonProperty("filename", NullValueHandling = NullValueHandling.Ignore)]
- public string FileName { get; internal set; }
+ public class DiscordAttachment : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the name of the file.
+ /// </summary>
+ [JsonProperty("filename", NullValueHandling = NullValueHandling.Ignore)]
+ public string FileName { get; internal set; }
- /// <summary>
- /// Gets the description of the file.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; set; }
+ /// <summary>
+ /// Gets the description of the file.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; set; }
- /// <summary>
- /// Gets the media, or MIME, type of the file.
- /// </summary>
- [JsonProperty("content_type", NullValueHandling = NullValueHandling.Ignore)]
- public string MediaType { get; internal set; }
+ /// <summary>
+ /// Gets the media, or MIME, type of the file.
+ /// </summary>
+ [JsonProperty("content_type", NullValueHandling = NullValueHandling.Ignore)]
+ public string MediaType { get; internal set; }
- /// <summary>
- /// Gets the file size in bytes.
- /// </summary>
- [JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)]
- public int? FileSize { get; internal set; }
+ /// <summary>
+ /// Gets the file size in bytes.
+ /// </summary>
+ [JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)]
+ public int? FileSize { get; internal set; }
- /// <summary>
- /// Gets the URL of the file.
- /// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public string Url { get; internal set; }
+ /// <summary>
+ /// Gets the URL of the file.
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public string Url { get; internal set; }
- /// <summary>
- /// Gets the proxied URL of the file.
- /// </summary>
- [JsonProperty("proxy_url", NullValueHandling = NullValueHandling.Ignore)]
- public string ProxyUrl { get; internal set; }
+ /// <summary>
+ /// Gets the proxied URL of the file.
+ /// </summary>
+ [JsonProperty("proxy_url", NullValueHandling = NullValueHandling.Ignore)]
+ public string ProxyUrl { get; internal set; }
- /// <summary>
- /// Gets the height. Applicable only if the attachment is an image.
- /// </summary>
- [JsonProperty("height", NullValueHandling = NullValueHandling.Ignore)]
- public int? Height { get; internal set; }
+ /// <summary>
+ /// Gets the height. Applicable only if the attachment is an image.
+ /// </summary>
+ [JsonProperty("height", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Height { get; internal set; }
- /// <summary>
- /// Gets the width. Applicable only if the attachment is an image.
- /// </summary>
- [JsonProperty("width", NullValueHandling = NullValueHandling.Ignore)]
- public int? Width { get; internal set; }
+ /// <summary>
+ /// Gets the width. Applicable only if the attachment is an image.
+ /// </summary>
+ [JsonProperty("width", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Width { get; internal set; }
- /// <summary>
- /// Gets whether this attachment is ephemeral.
- /// Ephemeral attachments will automatically be removed after a set period of time.
- /// Ephemeral attachments on messages are guaranteed to be available as long as the message itself exists.
- /// </summary>
- [JsonProperty("ephemeral", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Ephemeral { get; internal set; }
+ /// <summary>
+ /// Gets whether this attachment is ephemeral.
+ /// Ephemeral attachments will automatically be removed after a set period of time.
+ /// Ephemeral attachments on messages are guaranteed to be available as long as the message itself exists.
+ /// </summary>
+ [JsonProperty("ephemeral", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Ephemeral { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordAttachment"/> class.
- /// </summary>
- internal DiscordAttachment()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordAttachment"/> class.
+ /// </summary>
+ internal DiscordAttachment()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Message/DiscordMentions.cs b/DisCatSharp/Entities/Message/DiscordMentions.cs
index 4a30056db..9da861697 100644
--- a/DisCatSharp/Entities/Message/DiscordMentions.cs
+++ b/DisCatSharp/Entities/Message/DiscordMentions.cs
@@ -1,142 +1,143 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Handles mentionables.
-/// </summary>
-internal class DiscordMentions
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Parse users.
- /// </summary>
- private const string PARSE_USERS = "users";
-
- /// <summary>
- /// Parse roles.
- /// </summary>
- private const string PARSE_ROLES = "roles";
-
- /// <summary>
- /// Parse everyone.
- /// </summary>
- private const string PARSE_EVERYONE = "everyone";
-
- /// <summary>
- /// Collection roles to serialize
- /// </summary>
- [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<ulong> Roles { get; }
-
- /// <summary>
- /// Collection of users to serialize
- /// </summary>
- [JsonProperty("users", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<ulong> Users { get; }
-
- /// <summary>
- /// The values to be parsed
- /// </summary>
- [JsonProperty("parse", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<string> Parse { get; }
-
- /// <summary>
- /// For replies, whether to mention the author of the message being replied to.
- /// </summary>
- [JsonProperty("replied_user", NullValueHandling = NullValueHandling.Ignore)]
- public bool? RepliedUser { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordMentions"/> class.
+ /// Handles mentionables.
/// </summary>
- /// <param name="mentions">The mentions.</param>
- /// <param name="mention">If true, mention.</param>
- /// <param name="repliedUser">If true, replied user.</param>
- internal DiscordMentions(IEnumerable<IMention> mentions, bool mention = false, bool repliedUser = false)
+ internal class DiscordMentions
{
- if (mentions == null)
- return;
-
- if (!mentions.Any())
+ /// <summary>
+ /// Parse users.
+ /// </summary>
+ private const string PARSE_USERS = "users";
+
+ /// <summary>
+ /// Parse roles.
+ /// </summary>
+ private const string PARSE_ROLES = "roles";
+
+ /// <summary>
+ /// Parse everyone.
+ /// </summary>
+ private const string PARSE_EVERYONE = "everyone";
+
+ /// <summary>
+ /// Collection roles to serialize
+ /// </summary>
+ [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<ulong> Roles { get; }
+
+ /// <summary>
+ /// Collection of users to serialize
+ /// </summary>
+ [JsonProperty("users", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<ulong> Users { get; }
+
+ /// <summary>
+ /// The values to be parsed
+ /// </summary>
+ [JsonProperty("parse", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<string> Parse { get; }
+
+ /// <summary>
+ /// For replies, whether to mention the author of the message being replied to.
+ /// </summary>
+ [JsonProperty("replied_user", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? RepliedUser { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMentions"/> class.
+ /// </summary>
+ /// <param name="mentions">The mentions.</param>
+ /// <param name="mention">If true, mention.</param>
+ /// <param name="repliedUser">If true, replied user.</param>
+ internal DiscordMentions(IEnumerable<IMention> mentions, bool mention = false, bool repliedUser = false)
{
- this.Parse = Array.Empty<string>();
- this.RepliedUser = repliedUser;
- return;
- }
+ if (mentions == null)
+ return;
- if (mention)
- {
- this.RepliedUser = repliedUser;
- }
+ if (!mentions.Any())
+ {
+ this.Parse = Array.Empty<string>();
+ this.RepliedUser = repliedUser;
+ return;
+ }
+
+ if (mention)
+ {
+ this.RepliedUser = repliedUser;
+ }
- var roles = new HashSet<ulong>();
- var users = new HashSet<ulong>();
- var parse = new HashSet<string>();
+ var roles = new HashSet<ulong>();
+ var users = new HashSet<ulong>();
+ var parse = new HashSet<string>();
- foreach (var m in mentions)
- {
- switch (m)
+ foreach (var m in mentions)
{
- default:
- throw new NotSupportedException("Type not supported in mentions.");
- case UserMention u:
- if (u.Id.HasValue)
- users.Add(u.Id.Value);
- else
- parse.Add(PARSE_USERS);
-
- break;
-
- case RoleMention r:
- if (r.Id.HasValue)
- roles.Add(r.Id.Value);
- else
- parse.Add(PARSE_ROLES);
- break;
-
- case EveryoneMention e:
- parse.Add(PARSE_EVERYONE);
- break;
-
- case RepliedUserMention _:
- this.RepliedUser = repliedUser;
- break;
+ switch (m)
+ {
+ default:
+ throw new NotSupportedException("Type not supported in mentions.");
+ case UserMention u:
+ if (u.Id.HasValue)
+ users.Add(u.Id.Value);
+ else
+ parse.Add(PARSE_USERS);
+
+ break;
+
+ case RoleMention r:
+ if (r.Id.HasValue)
+ roles.Add(r.Id.Value);
+ else
+ parse.Add(PARSE_ROLES);
+ break;
+
+ case EveryoneMention e:
+ parse.Add(PARSE_EVERYONE);
+ break;
+
+ case RepliedUserMention _:
+ this.RepliedUser = repliedUser;
+ break;
+ }
}
- }
- if (!parse.Contains(PARSE_USERS) && users.Count > 0)
- this.Users = users;
+ if (!parse.Contains(PARSE_USERS) && users.Count > 0)
+ this.Users = users;
- if (!parse.Contains(PARSE_ROLES) && roles.Count > 0)
- this.Roles = roles;
+ if (!parse.Contains(PARSE_ROLES) && roles.Count > 0)
+ this.Roles = roles;
- if (parse.Count > 0)
- this.Parse = parse;
+ if (parse.Count > 0)
+ this.Parse = parse;
+ }
}
}
diff --git a/DisCatSharp/Entities/Message/DiscordMessage.cs b/DisCatSharp/Entities/Message/DiscordMessage.cs
index 02fc1de27..a9324cfa2 100644
--- a/DisCatSharp/Entities/Message/DiscordMessage.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessage.cs
@@ -1,876 +1,877 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord text message.
-/// </summary>
-public class DiscordMessage : SnowflakeObject, IEquatable<DiscordMessage>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordMessage"/> class.
+ /// Represents a Discord text message.
/// </summary>
- internal DiscordMessage()
+ public class DiscordMessage : SnowflakeObject, IEquatable<DiscordMessage>
{
- this._attachmentsLazy = new Lazy<IReadOnlyList<DiscordAttachment>>(() => new ReadOnlyCollection<DiscordAttachment>(this.AttachmentsInternal));
- this._embedsLazy = new Lazy<IReadOnlyList<DiscordEmbed>>(() => new ReadOnlyCollection<DiscordEmbed>(this.EmbedsInternal));
- this._mentionedChannelsLazy = new Lazy<IReadOnlyList<DiscordChannel>>(() => this.MentionedChannelsInternal != null
- ? new ReadOnlyCollection<DiscordChannel>(this.MentionedChannelsInternal)
- : Array.Empty<DiscordChannel>());
- this._mentionedRolesLazy = new Lazy<IReadOnlyList<DiscordRole>>(() => this.MentionedRolesInternal != null ? new ReadOnlyCollection<DiscordRole>(this.MentionedRolesInternal) : Array.Empty<DiscordRole>());
- this.MentionedUsersLazy = new Lazy<IReadOnlyList<DiscordUser>>(() => new ReadOnlyCollection<DiscordUser>(this.MentionedUsersInternal));
- this._reactionsLazy = new Lazy<IReadOnlyList<DiscordReaction>>(() => new ReadOnlyCollection<DiscordReaction>(this.ReactionsInternal));
- this._stickersLazy = new Lazy<IReadOnlyList<DiscordSticker>>(() => new ReadOnlyCollection<DiscordSticker>(this.StickersInternal));
- this._jumpLink = new Lazy<Uri>(() =>
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMessage"/> class.
+ /// </summary>
+ internal DiscordMessage()
{
- string gid = null;
- if (this.Channel != null)
- gid = this.Channel is DiscordDmChannel
- ? "@me"
- : this.Channel is DiscordThreadChannel
- ? this.INTERNAL_THREAD.GuildId.Value.ToString(CultureInfo.InvariantCulture)
- : this.Channel.GuildId.Value.ToString(CultureInfo.InvariantCulture);
-
- var cid = this.ChannelId.ToString(CultureInfo.InvariantCulture);
- var mid = this.Id.ToString(CultureInfo.InvariantCulture);
-
- return new Uri($"https://{(this.Discord.Configuration.UseCanary ? "canary.discord.com" : this.Discord.Configuration.UsePtb ? "ptb.discord.com" : "discord.com")}/channels/{gid}/{cid}/{mid}");
- });
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordMessage"/> class.
- /// </summary>
- /// <param name="other">The other message.</param>
- internal DiscordMessage(DiscordMessage other)
- : this()
- {
- this.Discord = other.Discord;
-
- this.AttachmentsInternal = other.AttachmentsInternal; // the attachments cannot change, thus no need to copy and reallocate.
- this.EmbedsInternal = new List<DiscordEmbed>(other.EmbedsInternal);
-
- if (other.MentionedChannelsInternal != null)
- this.MentionedChannelsInternal = new List<DiscordChannel>(other.MentionedChannelsInternal);
- if (other.MentionedRolesInternal != null)
- this.MentionedRolesInternal = new List<DiscordRole>(other.MentionedRolesInternal);
- if (other.MentionedRoleIds != null)
- this.MentionedRoleIds = new List<ulong>(other.MentionedRoleIds);
- this.MentionedUsersInternal = new List<DiscordUser>(other.MentionedUsersInternal);
- this.ReactionsInternal = new List<DiscordReaction>(other.ReactionsInternal);
- this.StickersInternal = new List<DiscordSticker>(other.StickersInternal);
-
- this.Author = other.Author;
- this.ChannelId = other.ChannelId;
- this.Content = other.Content;
- this.EditedTimestampRaw = other.EditedTimestampRaw;
- this.Id = other.Id;
- this.IsTts = other.IsTts;
- this.MessageType = other.MessageType;
- this.Pinned = other.Pinned;
- this.TimestampRaw = other.TimestampRaw;
- this.WebhookId = other.WebhookId;
- }
-
- /// <summary>
- /// Gets the channel in which the message was sent.
- /// </summary>
- [JsonIgnore]
- public DiscordChannel Channel
- {
- get => (this.Discord as DiscordClient)?.InternalGetCachedChannel(this.ChannelId) ?? this._channel;
- internal set => this._channel = value;
- }
-
- private DiscordChannel _channel;
-
- /// <summary>
- /// Gets the thread in which the message was sent.
- /// </summary>
- [JsonIgnore]
- private DiscordThreadChannel INTERNAL_THREAD
- {
- get => (this.Discord as DiscordClient)?.InternalGetCachedThread(this.ChannelId) ?? this._thread;
- set => this._thread = value;
- }
-
- private DiscordThreadChannel _thread;
-
- /// <summary>
- /// Gets the ID of the channel in which the message was sent.
- /// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; internal set; }
-
- /// <summary>
- /// Gets the components this message was sent with.
- /// </summary>
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<DiscordActionRowComponent> Components { get; internal set; }
-
- /// <summary>
- /// Gets the user or member that sent the message.
- /// </summary>
- [JsonProperty("author", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUser Author { get; internal set; }
-
- /// <summary>
- /// Gets the message's content.
- /// </summary>
- [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
- public string Content { get; internal set; }
-
- /// <summary>
- /// Gets the message's creation timestamp.
- /// </summary>
- [JsonIgnore]
- public DateTimeOffset Timestamp
- => !string.IsNullOrWhiteSpace(this.TimestampRaw) && DateTimeOffset.TryParse(this.TimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
- dto : this.CreationTimestamp;
-
- /// <summary>
- /// Gets the message's creation timestamp as raw string.
- /// </summary>
- [JsonProperty("timestamp", NullValueHandling = NullValueHandling.Ignore)]
- internal string TimestampRaw { get; set; }
-
- /// <summary>
- /// Gets the message's edit timestamp. Will be null if the message was not edited.
- /// </summary>
- [JsonIgnore]
- public DateTimeOffset? EditedTimestamp
- => !string.IsNullOrWhiteSpace(this.EditedTimestampRaw) && DateTimeOffset.TryParse(this.EditedTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
- (DateTimeOffset?)dto : null;
-
- /// <summary>
- /// Gets the message's edit timestamp as raw string. Will be null if the message was not edited.
- /// </summary>
- [JsonProperty("edited_timestamp", NullValueHandling = NullValueHandling.Ignore)]
- internal string EditedTimestampRaw { get; set; }
-
- /// <summary>
- /// Gets whether this message was edited.
- /// </summary>
- [JsonIgnore]
- public bool IsEdited
- => !string.IsNullOrWhiteSpace(this.EditedTimestampRaw);
-
- /// <summary>
- /// Gets whether the message is a text-to-speech message.
- /// </summary>
- [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsTts { get; internal set; }
-
- /// <summary>
- /// Gets whether the message mentions everyone.
- /// </summary>
- [JsonProperty("mention_everyone", NullValueHandling = NullValueHandling.Ignore)]
- public bool MentionEveryone { get; internal set; }
-
- /// <summary>
- /// Gets users or members mentioned by this message.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordUser> MentionedUsers
- => this.MentionedUsersLazy.Value;
-
- [JsonProperty("mentions", NullValueHandling = NullValueHandling.Ignore)]
- internal List<DiscordUser> MentionedUsersInternal;
- [JsonIgnore]
- internal readonly Lazy<IReadOnlyList<DiscordUser>> MentionedUsersLazy;
-
- // TODO: this will probably throw an exception in DMs since it tries to wrap around a null List...
- // this is probably low priority but need to find out a clean way to solve it...
- /// <summary>
- /// Gets roles mentioned by this message.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordRole> MentionedRoles
- => this._mentionedRolesLazy.Value;
-
- [JsonIgnore]
- internal List<DiscordRole> MentionedRolesInternal;
-
- [JsonProperty("mention_roles")]
- internal List<ulong> MentionedRoleIds;
-
- [JsonIgnore]
- private readonly Lazy<IReadOnlyList<DiscordRole>> _mentionedRolesLazy;
-
- /// <summary>
- /// Gets channels mentioned by this message.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordChannel> MentionedChannels
- => this._mentionedChannelsLazy.Value;
-
- [JsonIgnore]
- internal List<DiscordChannel> MentionedChannelsInternal;
- [JsonIgnore]
- private readonly Lazy<IReadOnlyList<DiscordChannel>> _mentionedChannelsLazy;
-
- /// <summary>
- /// Gets files attached to this message.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordAttachment> Attachments
- => this._attachmentsLazy.Value;
+ this._attachmentsLazy = new Lazy<IReadOnlyList<DiscordAttachment>>(() => new ReadOnlyCollection<DiscordAttachment>(this.AttachmentsInternal));
+ this._embedsLazy = new Lazy<IReadOnlyList<DiscordEmbed>>(() => new ReadOnlyCollection<DiscordEmbed>(this.EmbedsInternal));
+ this._mentionedChannelsLazy = new Lazy<IReadOnlyList<DiscordChannel>>(() => this.MentionedChannelsInternal != null
+ ? new ReadOnlyCollection<DiscordChannel>(this.MentionedChannelsInternal)
+ : Array.Empty<DiscordChannel>());
+ this._mentionedRolesLazy = new Lazy<IReadOnlyList<DiscordRole>>(() => this.MentionedRolesInternal != null ? new ReadOnlyCollection<DiscordRole>(this.MentionedRolesInternal) : Array.Empty<DiscordRole>());
+ this.MentionedUsersLazy = new Lazy<IReadOnlyList<DiscordUser>>(() => new ReadOnlyCollection<DiscordUser>(this.MentionedUsersInternal));
+ this._reactionsLazy = new Lazy<IReadOnlyList<DiscordReaction>>(() => new ReadOnlyCollection<DiscordReaction>(this.ReactionsInternal));
+ this._stickersLazy = new Lazy<IReadOnlyList<DiscordSticker>>(() => new ReadOnlyCollection<DiscordSticker>(this.StickersInternal));
+ this._jumpLink = new Lazy<Uri>(() =>
+ {
+ string gid = null;
+ if (this.Channel != null)
+ gid = this.Channel is DiscordDmChannel
+ ? "@me"
+ : this.Channel is DiscordThreadChannel
+ ? this.INTERNAL_THREAD.GuildId.Value.ToString(CultureInfo.InvariantCulture)
+ : this.Channel.GuildId.Value.ToString(CultureInfo.InvariantCulture);
+
+ var cid = this.ChannelId.ToString(CultureInfo.InvariantCulture);
+ var mid = this.Id.ToString(CultureInfo.InvariantCulture);
+
+ return new Uri($"https://{(this.Discord.Configuration.UseCanary ? "canary.discord.com" : this.Discord.Configuration.UsePtb ? "ptb.discord.com" : "discord.com")}/channels/{gid}/{cid}/{mid}");
+ });
+ }
- [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
- internal List<DiscordAttachment> AttachmentsInternal = new();
- [JsonIgnore]
- private readonly Lazy<IReadOnlyList<DiscordAttachment>> _attachmentsLazy;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMessage"/> class.
+ /// </summary>
+ /// <param name="other">The other message.</param>
+ internal DiscordMessage(DiscordMessage other)
+ : this()
+ {
+ this.Discord = other.Discord;
+
+ this.AttachmentsInternal = other.AttachmentsInternal; // the attachments cannot change, thus no need to copy and reallocate.
+ this.EmbedsInternal = new List<DiscordEmbed>(other.EmbedsInternal);
+
+ if (other.MentionedChannelsInternal != null)
+ this.MentionedChannelsInternal = new List<DiscordChannel>(other.MentionedChannelsInternal);
+ if (other.MentionedRolesInternal != null)
+ this.MentionedRolesInternal = new List<DiscordRole>(other.MentionedRolesInternal);
+ if (other.MentionedRoleIds != null)
+ this.MentionedRoleIds = new List<ulong>(other.MentionedRoleIds);
+ this.MentionedUsersInternal = new List<DiscordUser>(other.MentionedUsersInternal);
+ this.ReactionsInternal = new List<DiscordReaction>(other.ReactionsInternal);
+ this.StickersInternal = new List<DiscordSticker>(other.StickersInternal);
+
+ this.Author = other.Author;
+ this.ChannelId = other.ChannelId;
+ this.Content = other.Content;
+ this.EditedTimestampRaw = other.EditedTimestampRaw;
+ this.Id = other.Id;
+ this.IsTts = other.IsTts;
+ this.MessageType = other.MessageType;
+ this.Pinned = other.Pinned;
+ this.TimestampRaw = other.TimestampRaw;
+ this.WebhookId = other.WebhookId;
+ }
- /// <summary>
- /// Gets embeds attached to this message.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordEmbed> Embeds
- => this._embedsLazy.Value;
+ /// <summary>
+ /// Gets the channel in which the message was sent.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel Channel
+ {
+ get => (this.Discord as DiscordClient)?.InternalGetCachedChannel(this.ChannelId) ?? this._channel;
+ internal set => this._channel = value;
+ }
- [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
- internal List<DiscordEmbed> EmbedsInternal = new();
- [JsonIgnore]
- private readonly Lazy<IReadOnlyList<DiscordEmbed>> _embedsLazy;
+ private DiscordChannel _channel;
- /// <summary>
- /// Gets reactions used on this message.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordReaction> Reactions
- => this._reactionsLazy.Value;
+ /// <summary>
+ /// Gets the thread in which the message was sent.
+ /// </summary>
+ [JsonIgnore]
+ private DiscordThreadChannel INTERNAL_THREAD
+ {
+ get => (this.Discord as DiscordClient)?.InternalGetCachedThread(this.ChannelId) ?? this._thread;
+ set => this._thread = value;
+ }
- [JsonProperty("reactions", NullValueHandling = NullValueHandling.Ignore)]
- internal List<DiscordReaction> ReactionsInternal = new();
- [JsonIgnore]
- private readonly Lazy<IReadOnlyList<DiscordReaction>> _reactionsLazy;
+ private DiscordThreadChannel _thread;
+
+ /// <summary>
+ /// Gets the ID of the channel in which the message was sent.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; internal set; }
+
+ /// <summary>
+ /// Gets the components this message was sent with.
+ /// </summary>
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<DiscordActionRowComponent> Components { get; internal set; }
+
+ /// <summary>
+ /// Gets the user or member that sent the message.
+ /// </summary>
+ [JsonProperty("author", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUser Author { get; internal set; }
+
+ /// <summary>
+ /// Gets the message's content.
+ /// </summary>
+ [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+ public string Content { get; internal set; }
+
+ /// <summary>
+ /// Gets the message's creation timestamp.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset Timestamp
+ => !string.IsNullOrWhiteSpace(this.TimestampRaw) && DateTimeOffset.TryParse(this.TimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
+ dto : this.CreationTimestamp;
+
+ /// <summary>
+ /// Gets the message's creation timestamp as raw string.
+ /// </summary>
+ [JsonProperty("timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ internal string TimestampRaw { get; set; }
+
+ /// <summary>
+ /// Gets the message's edit timestamp. Will be null if the message was not edited.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset? EditedTimestamp
+ => !string.IsNullOrWhiteSpace(this.EditedTimestampRaw) && DateTimeOffset.TryParse(this.EditedTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
+ (DateTimeOffset?)dto : null;
+
+ /// <summary>
+ /// Gets the message's edit timestamp as raw string. Will be null if the message was not edited.
+ /// </summary>
+ [JsonProperty("edited_timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ internal string EditedTimestampRaw { get; set; }
+
+ /// <summary>
+ /// Gets whether this message was edited.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsEdited
+ => !string.IsNullOrWhiteSpace(this.EditedTimestampRaw);
+
+ /// <summary>
+ /// Gets whether the message is a text-to-speech message.
+ /// </summary>
+ [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsTts { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the message mentions everyone.
+ /// </summary>
+ [JsonProperty("mention_everyone", NullValueHandling = NullValueHandling.Ignore)]
+ public bool MentionEveryone { get; internal set; }
+
+ /// <summary>
+ /// Gets users or members mentioned by this message.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordUser> MentionedUsers
+ => this.MentionedUsersLazy.Value;
+
+ [JsonProperty("mentions", NullValueHandling = NullValueHandling.Ignore)]
+ internal List<DiscordUser> MentionedUsersInternal;
+ [JsonIgnore]
+ internal readonly Lazy<IReadOnlyList<DiscordUser>> MentionedUsersLazy;
+
+ // TODO: this will probably throw an exception in DMs since it tries to wrap around a null List...
+ // this is probably low priority but need to find out a clean way to solve it...
+ /// <summary>
+ /// Gets roles mentioned by this message.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordRole> MentionedRoles
+ => this._mentionedRolesLazy.Value;
+
+ [JsonIgnore]
+ internal List<DiscordRole> MentionedRolesInternal;
+
+ [JsonProperty("mention_roles")]
+ internal List<ulong> MentionedRoleIds;
+
+ [JsonIgnore]
+ private readonly Lazy<IReadOnlyList<DiscordRole>> _mentionedRolesLazy;
+
+ /// <summary>
+ /// Gets channels mentioned by this message.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordChannel> MentionedChannels
+ => this._mentionedChannelsLazy.Value;
+
+ [JsonIgnore]
+ internal List<DiscordChannel> MentionedChannelsInternal;
+ [JsonIgnore]
+ private readonly Lazy<IReadOnlyList<DiscordChannel>> _mentionedChannelsLazy;
+
+ /// <summary>
+ /// Gets files attached to this message.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordAttachment> Attachments
+ => this._attachmentsLazy.Value;
+
+ [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
+ internal List<DiscordAttachment> AttachmentsInternal = new();
+ [JsonIgnore]
+ private readonly Lazy<IReadOnlyList<DiscordAttachment>> _attachmentsLazy;
+
+ /// <summary>
+ /// Gets embeds attached to this message.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordEmbed> Embeds
+ => this._embedsLazy.Value;
+
+ [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
+ internal List<DiscordEmbed> EmbedsInternal = new();
+ [JsonIgnore]
+ private readonly Lazy<IReadOnlyList<DiscordEmbed>> _embedsLazy;
+
+ /// <summary>
+ /// Gets reactions used on this message.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordReaction> Reactions
+ => this._reactionsLazy.Value;
+
+ [JsonProperty("reactions", NullValueHandling = NullValueHandling.Ignore)]
+ internal List<DiscordReaction> ReactionsInternal = new();
+ [JsonIgnore]
+ private readonly Lazy<IReadOnlyList<DiscordReaction>> _reactionsLazy;
/// <summary>
/// Gets the nonce sent with the message, if the message was sent by the client.
/// </summary>
[JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)]
public string Nonce { get; internal set; }
- /// <summary>
- /// Gets whether the message is pinned.
- /// </summary>
- [JsonProperty("pinned", NullValueHandling = NullValueHandling.Ignore)]
- public bool Pinned { get; internal set; }
-
- /// <summary>
- /// Gets the id of the webhook that generated this message.
- /// </summary>
- [JsonProperty("webhook_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? WebhookId { get; internal set; }
-
- /// <summary>
- /// Gets the type of the message.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public MessageType? MessageType { get; internal set; }
-
- /// <summary>
- /// Gets the message activity in the Rich Presence embed.
- /// </summary>
- [JsonProperty("activity", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMessageActivity Activity { get; internal set; }
-
- /// <summary>
- /// Gets the message application in the Rich Presence embed.
- /// </summary>
- [JsonProperty("application", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMessageApplication Application { get; internal set; }
-
- /// <summary>
- /// Gets the message application id in the Rich Presence embed.
- /// </summary>
- [JsonProperty("application_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ApplicationId { get; internal set; }
-
- /// <summary>
- /// Gets the internal reference.
- /// </summary>
- [JsonProperty("message_reference", NullValueHandling = NullValueHandling.Ignore)]
- internal InternalDiscordMessageReference? InternalReference { get; set; }
-
- /// <summary>
- /// Gets the original message reference from the crossposted message.
- /// </summary>
- [JsonIgnore]
- public DiscordMessageReference Reference
- => this.InternalReference.HasValue ? this?.InternalBuildMessageReference() : null;
-
- /// <summary>
- /// Gets the bitwise flags for this message.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public MessageFlags? Flags { get; internal set; }
-
- /// <summary>
- /// Gets whether the message originated from a webhook.
- /// </summary>
- [JsonIgnore]
- public bool WebhookMessage
- => this.WebhookId != null;
-
- /// <summary>
- /// Gets the jump link to this message.
- /// </summary>
- [JsonIgnore]
- public Uri JumpLink => this._jumpLink.Value;
- private readonly Lazy<Uri> _jumpLink;
-
- /// <summary>
- /// Gets stickers for this message.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordSticker> Stickers
- => this._stickersLazy.Value;
+ /// <summary>
+ /// Gets whether the message is pinned.
+ /// </summary>
+ [JsonProperty("pinned", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Pinned { get; internal set; }
+
+ /// <summary>
+ /// Gets the id of the webhook that generated this message.
+ /// </summary>
+ [JsonProperty("webhook_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? WebhookId { get; internal set; }
+
+ /// <summary>
+ /// Gets the type of the message.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public MessageType? MessageType { get; internal set; }
+
+ /// <summary>
+ /// Gets the message activity in the Rich Presence embed.
+ /// </summary>
+ [JsonProperty("activity", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordMessageActivity Activity { get; internal set; }
+
+ /// <summary>
+ /// Gets the message application in the Rich Presence embed.
+ /// </summary>
+ [JsonProperty("application", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordMessageApplication Application { get; internal set; }
+
+ /// <summary>
+ /// Gets the message application id in the Rich Presence embed.
+ /// </summary>
+ [JsonProperty("application_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ApplicationId { get; internal set; }
+
+ /// <summary>
+ /// Gets the internal reference.
+ /// </summary>
+ [JsonProperty("message_reference", NullValueHandling = NullValueHandling.Ignore)]
+ internal InternalDiscordMessageReference? InternalReference { get; set; }
+
+ /// <summary>
+ /// Gets the original message reference from the crossposted message.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordMessageReference Reference
+ => this.InternalReference.HasValue ? this?.InternalBuildMessageReference() : null;
+
+ /// <summary>
+ /// Gets the bitwise flags for this message.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public MessageFlags? Flags { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the message originated from a webhook.
+ /// </summary>
+ [JsonIgnore]
+ public bool WebhookMessage
+ => this.WebhookId != null;
+
+ /// <summary>
+ /// Gets the jump link to this message.
+ /// </summary>
+ [JsonIgnore]
+ public Uri JumpLink => this._jumpLink.Value;
+ private readonly Lazy<Uri> _jumpLink;
+
+ /// <summary>
+ /// Gets stickers for this message.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordSticker> Stickers
+ => this._stickersLazy.Value;
+
+ [JsonProperty("sticker_items", NullValueHandling = NullValueHandling.Ignore)]
+ internal List<DiscordSticker> StickersInternal = new();
+ [JsonIgnore]
+ private readonly Lazy<IReadOnlyList<DiscordSticker>> _stickersLazy;
+
+ /// <summary>
+ /// Gets the guild id.
+ /// </summary>
+ [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong? GuildId { get; set; }
+
+ /// <summary>
+ /// Gets the message object for the referenced message
+ /// </summary>
+ [JsonProperty("referenced_message", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordMessage ReferencedMessage { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the message is a response to an interaction.
+ /// </summary>
+ [JsonProperty("interaction", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordMessageInteraction Interaction { get; internal set; }
+
+ /// <summary>
+ /// Gets the thread that was started from this message.
+ /// </summary>
+ [JsonProperty("thread", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordThreadChannel Thread { get; internal set; }
+
+ /// <summary>
+ /// Build the message reference.
+ /// </summary>
+ internal DiscordMessageReference InternalBuildMessageReference()
+ {
+ var client = this.Discord as DiscordClient;
+ var guildId = this.InternalReference.Value.GuildId;
+ var channelId = this.InternalReference.Value.ChannelId;
+ var messageId = this.InternalReference.Value.MessageId;
- [JsonProperty("sticker_items", NullValueHandling = NullValueHandling.Ignore)]
- internal List<DiscordSticker> StickersInternal = new();
- [JsonIgnore]
- private readonly Lazy<IReadOnlyList<DiscordSticker>> _stickersLazy;
+ var reference = new DiscordMessageReference();
- /// <summary>
- /// Gets the guild id.
- /// </summary>
- [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong? GuildId { get; set; }
+ if (guildId.HasValue)
+ reference.Guild = client.GuildsInternal.TryGetValue(guildId.Value, out var g)
+ ? g
+ : new DiscordGuild
+ {
+ Id = guildId.Value,
+ Discord = client
+ };
- /// <summary>
- /// Gets the message object for the referenced message
- /// </summary>
- [JsonProperty("referenced_message", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMessage ReferencedMessage { get; internal set; }
+ var channel = client.InternalGetCachedChannel(channelId.Value);
- /// <summary>
- /// Gets whether the message is a response to an interaction.
- /// </summary>
- [JsonProperty("interaction", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMessageInteraction Interaction { get; internal set; }
+ if (channel == null)
+ {
+ reference.Channel = new DiscordChannel
+ {
+ Id = channelId.Value,
+ Discord = client
+ };
- /// <summary>
- /// Gets the thread that was started from this message.
- /// </summary>
- [JsonProperty("thread", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordThreadChannel Thread { get; internal set; }
+ if (guildId.HasValue)
+ reference.Channel.GuildId = guildId.Value;
+ }
- /// <summary>
- /// Build the message reference.
- /// </summary>
- internal DiscordMessageReference InternalBuildMessageReference()
- {
- var client = this.Discord as DiscordClient;
- var guildId = this.InternalReference.Value.GuildId;
- var channelId = this.InternalReference.Value.ChannelId;
- var messageId = this.InternalReference.Value.MessageId;
+ else reference.Channel = channel;
- var reference = new DiscordMessageReference();
+ if (client.MessageCache != null && client.MessageCache.TryGet(m => m.Id == messageId.Value && m.ChannelId == channelId, out var msg))
+ reference.Message = msg;
- if (guildId.HasValue)
- reference.Guild = client.GuildsInternal.TryGetValue(guildId.Value, out var g)
- ? g
- : new DiscordGuild
+ else
+ {
+ reference.Message = new DiscordMessage
{
- Id = guildId.Value,
+ ChannelId = this.ChannelId,
Discord = client
};
- var channel = client.InternalGetCachedChannel(channelId.Value);
-
- if (channel == null)
- {
- reference.Channel = new DiscordChannel
- {
- Id = channelId.Value,
- Discord = client
- };
+ if (messageId.HasValue)
+ reference.Message.Id = messageId.Value;
+ }
- if (guildId.HasValue)
- reference.Channel.GuildId = guildId.Value;
+ return reference;
}
- else reference.Channel = channel;
- if (client.MessageCache != null && client.MessageCache.TryGet(m => m.Id == messageId.Value && m.ChannelId == channelId, out var msg))
- reference.Message = msg;
-
- else
+ /// <summary>
+ /// Gets the mentions.
+ /// </summary>
+ /// <returns>An array of IMentions.</returns>
+ private IMention[] GetMentions()
{
- reference.Message = new DiscordMessage
- {
- ChannelId = this.ChannelId,
- Discord = client
- };
+ var mentions = new List<IMention>();
- if (messageId.HasValue)
- reference.Message.Id = messageId.Value;
- }
+ if (this.ReferencedMessage != null && this.MentionedUsersInternal.Contains(this.ReferencedMessage.Author))
+ mentions.Add(new RepliedUserMention());
- return reference;
- }
+ if (this.MentionedUsersInternal.Any())
+ mentions.AddRange(this.MentionedUsersInternal.Select(m => (IMention)new UserMention(m)));
+ if (this.MentionedRoleIds.Any())
+ mentions.AddRange(this.MentionedRoleIds.Select(r => (IMention)new RoleMention(r)));
- /// <summary>
- /// Gets the mentions.
- /// </summary>
- /// <returns>An array of IMentions.</returns>
- private IMention[] GetMentions()
- {
- var mentions = new List<IMention>();
-
- if (this.ReferencedMessage != null && this.MentionedUsersInternal.Contains(this.ReferencedMessage.Author))
- mentions.Add(new RepliedUserMention());
-
- if (this.MentionedUsersInternal.Any())
- mentions.AddRange(this.MentionedUsersInternal.Select(m => (IMention)new UserMention(m)));
-
- if (this.MentionedRoleIds.Any())
- mentions.AddRange(this.MentionedRoleIds.Select(r => (IMention)new RoleMention(r)));
-
- return mentions.ToArray();
- }
-
- /// <summary>
- /// Populates the mentions.
- /// </summary>
- internal void PopulateMentions()
- {
- var guild = this.Channel?.Guild;
- this.MentionedUsersInternal ??= new List<DiscordUser>();
- this.MentionedRolesInternal ??= new List<DiscordRole>();
- this.MentionedChannelsInternal ??= new List<DiscordChannel>();
+ return mentions.ToArray();
+ }
- var mentionedUsers = new HashSet<DiscordUser>(new DiscordUserComparer());
- if (guild != null)
+ /// <summary>
+ /// Populates the mentions.
+ /// </summary>
+ internal void PopulateMentions()
{
- foreach (var usr in this.MentionedUsersInternal)
+ var guild = this.Channel?.Guild;
+ this.MentionedUsersInternal ??= new List<DiscordUser>();
+ this.MentionedRolesInternal ??= new List<DiscordRole>();
+ this.MentionedChannelsInternal ??= new List<DiscordChannel>();
+
+ var mentionedUsers = new HashSet<DiscordUser>(new DiscordUserComparer());
+ if (guild != null)
{
- usr.Discord = this.Discord;
- this.Discord.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
+ foreach (var usr in this.MentionedUsersInternal)
{
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
-
- mentionedUsers.Add(guild.MembersInternal.TryGetValue(usr.Id, out var member) ? member : usr);
+ usr.Discord = this.Discord;
+ this.Discord.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ return old;
+ });
+
+ mentionedUsers.Add(guild.MembersInternal.TryGetValue(usr.Id, out var member) ? member : usr);
+ }
}
- }
- if (!string.IsNullOrWhiteSpace(this.Content))
- {
- //mentionedUsers.UnionWith(Utilities.GetUserMentions(this).Select(this.Discord.GetCachedOrEmptyUserInternal));
- if (guild != null)
+ if (!string.IsNullOrWhiteSpace(this.Content))
{
- //this._mentionedRoles = this._mentionedRoles.Union(Utilities.GetRoleMentions(this).Select(xid => guild.GetRole(xid))).ToList();
- this.MentionedRolesInternal = this.MentionedRolesInternal.Union(this.MentionedRoleIds.Select(xid => guild.GetRole(xid))).ToList();
- this.MentionedChannelsInternal = this.MentionedChannelsInternal.Union(Utilities.GetChannelMentions(this).Select(xid => guild.GetChannel(xid))).ToList();
+ //mentionedUsers.UnionWith(Utilities.GetUserMentions(this).Select(this.Discord.GetCachedOrEmptyUserInternal));
+ if (guild != null)
+ {
+ //this._mentionedRoles = this._mentionedRoles.Union(Utilities.GetRoleMentions(this).Select(xid => guild.GetRole(xid))).ToList();
+ this.MentionedRolesInternal = this.MentionedRolesInternal.Union(this.MentionedRoleIds.Select(xid => guild.GetRole(xid))).ToList();
+ this.MentionedChannelsInternal = this.MentionedChannelsInternal.Union(Utilities.GetChannelMentions(this).Select(xid => guild.GetChannel(xid))).ToList();
+ }
}
- }
-
- this.MentionedUsersInternal = mentionedUsers.ToList();
- }
-
- /// <summary>
- /// Edits the message.
- /// </summary>
- /// <param name="content">New content.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> ModifyAsync(Optional<string> content)
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, default, this.GetMentions(), default, default, Array.Empty<DiscordMessageFile>(), default);
-
- /// <summary>
- /// Edits the message.
- /// </summary>
- /// <param name="embed">New embed.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> ModifyAsync(Optional<DiscordEmbed> embed = default)
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, embed.Map(v => new[] { v }).ValueOr(Array.Empty<DiscordEmbed>()), this.GetMentions(), default, default, Array.Empty<DiscordMessageFile>(), default);
-
- /// <summary>
- /// Edits the message.
- /// </summary>
- /// <param name="content">New content.</param>
- /// <param name="embed">New embed.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> ModifyAsync(Optional<string> content, Optional<DiscordEmbed> embed = default)
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embed.Map(v => new[] { v }).ValueOr(Array.Empty<DiscordEmbed>()), this.GetMentions(), default, default, Array.Empty<DiscordMessageFile>(), default);
-
- /// <summary>
- /// Edits the message.
- /// </summary>
- /// <param name="content">New content.</param>
- /// <param name="embeds">New embeds.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> ModifyAsync(Optional<string> content, Optional<IEnumerable<DiscordEmbed>> embeds = default)
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embeds, this.GetMentions(), default, default, Array.Empty<DiscordMessageFile>(), default);
-
- /// <summary>
- /// Edits the message.
- /// </summary>
- /// <param name="builder">The builder of the message to edit.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> ModifyAsync(DiscordMessageBuilder builder)
- {
- builder.Validate(true);
- return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, Optional.Some(builder.Embeds.AsEnumerable()), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0 ? Optional.Some(builder.Attachments.AsEnumerable()) : builder.KeepAttachmentsInternal.HasValue ? builder.KeepAttachmentsInternal.Value ? Optional.Some(this.Attachments.AsEnumerable()) : Array.Empty<DiscordAttachment>() : null);
- }
-
- /// <summary>
- /// Edits the message embed suppression.
- /// </summary>
- /// <param name="suppress">Suppress embeds.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> ModifySuppressionAsync(bool suppress = false)
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, default, default, default, suppress, default, default);
-
- /// <summary>
- /// Clears all attachments from the message.
- /// </summary>
- /// <returns></returns>
- public Task<DiscordMessage> ClearAttachmentsAsync()
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, default, this.GetMentions(), default, default, default, Array.Empty<DiscordAttachment>());
-
- /// <summary>
- /// Edits the message.
- /// </summary>
- /// <param name="action">The builder of the message to edit.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> ModifyAsync(Action<DiscordMessageBuilder> action)
- {
- var builder = new DiscordMessageBuilder();
- action(builder);
- builder.Validate(true);
- return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, Optional.Some(builder.Embeds.AsEnumerable()), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0 ? Optional.Some(builder.Attachments.AsEnumerable()) : builder.KeepAttachmentsInternal.HasValue ? builder.KeepAttachmentsInternal.Value ? Optional.Some(this.Attachments.AsEnumerable()) : Array.Empty<DiscordAttachment>() : null);
- }
-
- /// <summary>
- /// Deletes the message.
- /// </summary>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteAsync(string reason = null)
- => this.Discord.ApiClient.DeleteMessageAsync(this.ChannelId, this.Id, reason);
-
- /// <summary>
- /// Creates a thread.
- /// Depending on the <see cref="ChannelType"/> of the parent channel it's either a <see cref="ChannelType.PublicThread"/> or a <see cref="ChannelType.NewsThread"/>.
- /// </summary>
- /// <param name="name">The name of the thread.</param>
- /// <param name="autoArchiveDuration"><see cref="ThreadAutoArchiveDuration"/> till it gets archived. Defaults to <see cref="ThreadAutoArchiveDuration.OneHour"/></param>
- /// <param name="rateLimitPerUser">The per user ratelimit, aka slowdown.</param>
- /// <param name="reason">The reason.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreatePrivateThreads"/> or <see cref="Permissions.SendMessagesInThreads"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.NotSupportedException">Thrown when the <see cref="ThreadAutoArchiveDuration"/> cannot be modified.</exception>
- public async Task<DiscordThreadChannel> CreateThreadAsync(string name, ThreadAutoArchiveDuration autoArchiveDuration = ThreadAutoArchiveDuration.OneHour, int? rateLimitPerUser = null, string reason = null) =>
- Utilities.CheckThreadAutoArchiveDurationFeature(this.Channel.Guild, autoArchiveDuration)
- ? await this.Discord.ApiClient.CreateThreadAsync(this.ChannelId, this.Id, name, autoArchiveDuration, this.Channel.Type == ChannelType.News ? ChannelType.NewsThread : ChannelType.PublicThread, rateLimitPerUser, reason)
- : throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(autoArchiveDuration == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
-
- /// <summary>
- /// Pins the message in its channel.
- /// </summary>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task PinAsync()
- => this.Discord.ApiClient.PinMessageAsync(this.ChannelId, this.Id);
-
- /// <summary>
- /// Unpins the message in its channel.
- /// </summary>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task UnpinAsync()
- => this.Discord.ApiClient.UnpinMessageAsync(this.ChannelId, this.Id);
-
- /// <summary>
- /// Responds to the message. This produces a reply.
- /// </summary>
- /// <param name="content">Message content to respond with.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> RespondAsync(string content)
- => this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, content, null, sticker: null, replyMessageId: this.Id, mentionReply: false, failOnInvalidReply: false);
- /// <summary>
- /// Responds to the message. This produces a reply.
- /// </summary>
- /// <param name="embed">Embed to attach to the message.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> RespondAsync(DiscordEmbed embed)
- => this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, null, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: this.Id, mentionReply: false, failOnInvalidReply: false);
-
- /// <summary>
- /// Responds to the message. This produces a reply.
- /// </summary>
- /// <param name="content">Message content to respond with.</param>
- /// <param name="embed">Embed to attach to the message.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> RespondAsync(string content, DiscordEmbed embed)
- => this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, content, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: this.Id, mentionReply: false, failOnInvalidReply: false);
-
- /// <summary>
- /// Responds to the message. This produces a reply.
- /// </summary>
- /// <param name="builder">The Discord message builder.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> RespondAsync(DiscordMessageBuilder builder)
- => this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, builder.WithReply(this.Id, mention: false, failOnInvalidReply: false));
+ this.MentionedUsersInternal = mentionedUsers.ToList();
+ }
- /// <summary>
- /// Responds to the message. This produces a reply.
- /// </summary>
- /// <param name="action">The Discord message builder.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> RespondAsync(Action<DiscordMessageBuilder> action)
- {
- var builder = new DiscordMessageBuilder();
- action(builder);
- return this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, builder.WithReply(this.Id, mention: false, failOnInvalidReply: false));
- }
+ /// <summary>
+ /// Edits the message.
+ /// </summary>
+ /// <param name="content">New content.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> ModifyAsync(Optional<string> content)
+ => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, default, this.GetMentions(), default, default, Array.Empty<DiscordMessageFile>(), default);
+
+ /// <summary>
+ /// Edits the message.
+ /// </summary>
+ /// <param name="embed">New embed.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> ModifyAsync(Optional<DiscordEmbed> embed = default)
+ => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, embed.Map(v => new[] { v }).ValueOr(Array.Empty<DiscordEmbed>()), this.GetMentions(), default, default, Array.Empty<DiscordMessageFile>(), default);
+
+ /// <summary>
+ /// Edits the message.
+ /// </summary>
+ /// <param name="content">New content.</param>
+ /// <param name="embed">New embed.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> ModifyAsync(Optional<string> content, Optional<DiscordEmbed> embed = default)
+ => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embed.Map(v => new[] { v }).ValueOr(Array.Empty<DiscordEmbed>()), this.GetMentions(), default, default, Array.Empty<DiscordMessageFile>(), default);
+
+ /// <summary>
+ /// Edits the message.
+ /// </summary>
+ /// <param name="content">New content.</param>
+ /// <param name="embeds">New embeds.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> ModifyAsync(Optional<string> content, Optional<IEnumerable<DiscordEmbed>> embeds = default)
+ => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embeds, this.GetMentions(), default, default, Array.Empty<DiscordMessageFile>(), default);
+
+ /// <summary>
+ /// Edits the message.
+ /// </summary>
+ /// <param name="builder">The builder of the message to edit.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> ModifyAsync(DiscordMessageBuilder builder)
+ {
+ builder.Validate(true);
+ return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, Optional.Some(builder.Embeds.AsEnumerable()), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0 ? Optional.Some(builder.Attachments.AsEnumerable()) : builder.KeepAttachmentsInternal.HasValue ? builder.KeepAttachmentsInternal.Value ? Optional.Some(this.Attachments.AsEnumerable()) : Array.Empty<DiscordAttachment>() : null);
+ }
- /// <summary>
- /// Creates a reaction to this message.
- /// </summary>
- /// <param name="emoji">The emoji you want to react with, either an emoji or name:id</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AddReactions"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task CreateReactionAsync(DiscordEmoji emoji)
- => this.Discord.ApiClient.CreateReactionAsync(this.ChannelId, this.Id, emoji.ToReactionString());
+ /// <summary>
+ /// Edits the message embed suppression.
+ /// </summary>
+ /// <param name="suppress">Suppress embeds.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> ModifySuppressionAsync(bool suppress = false)
+ => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, default, default, default, suppress, default, default);
+
+ /// <summary>
+ /// Clears all attachments from the message.
+ /// </summary>
+ /// <returns></returns>
+ public Task<DiscordMessage> ClearAttachmentsAsync()
+ => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, default, this.GetMentions(), default, default, default, Array.Empty<DiscordAttachment>());
+
+ /// <summary>
+ /// Edits the message.
+ /// </summary>
+ /// <param name="action">The builder of the message to edit.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client tried to modify a message not sent by them.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> ModifyAsync(Action<DiscordMessageBuilder> action)
+ {
+ var builder = new DiscordMessageBuilder();
+ action(builder);
+ builder.Validate(true);
+ return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, Optional.Some(builder.Embeds.AsEnumerable()), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0 ? Optional.Some(builder.Attachments.AsEnumerable()) : builder.KeepAttachmentsInternal.HasValue ? builder.KeepAttachmentsInternal.Value ? Optional.Some(this.Attachments.AsEnumerable()) : Array.Empty<DiscordAttachment>() : null);
+ }
- /// <summary>
- /// Deletes your own reaction
- /// </summary>
- /// <param name="emoji">Emoji for the reaction you want to remove, either an emoji or name:id</param>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteOwnReactionAsync(DiscordEmoji emoji)
- => this.Discord.ApiClient.DeleteOwnReactionAsync(this.ChannelId, this.Id, emoji.ToReactionString());
+ /// <summary>
+ /// Deletes the message.
+ /// </summary>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteAsync(string reason = null)
+ => this.Discord.ApiClient.DeleteMessageAsync(this.ChannelId, this.Id, reason);
+
+ /// <summary>
+ /// Creates a thread.
+ /// Depending on the <see cref="ChannelType"/> of the parent channel it's either a <see cref="ChannelType.PublicThread"/> or a <see cref="ChannelType.NewsThread"/>.
+ /// </summary>
+ /// <param name="name">The name of the thread.</param>
+ /// <param name="autoArchiveDuration"><see cref="ThreadAutoArchiveDuration"/> till it gets archived. Defaults to <see cref="ThreadAutoArchiveDuration.OneHour"/></param>
+ /// <param name="rateLimitPerUser">The per user ratelimit, aka slowdown.</param>
+ /// <param name="reason">The reason.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.CreatePrivateThreads"/> or <see cref="Permissions.SendMessagesInThreads"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the channel does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.NotSupportedException">Thrown when the <see cref="ThreadAutoArchiveDuration"/> cannot be modified.</exception>
+ public async Task<DiscordThreadChannel> CreateThreadAsync(string name, ThreadAutoArchiveDuration autoArchiveDuration = ThreadAutoArchiveDuration.OneHour, int? rateLimitPerUser = null, string reason = null) =>
+ Utilities.CheckThreadAutoArchiveDurationFeature(this.Channel.Guild, autoArchiveDuration)
+ ? await this.Discord.ApiClient.CreateThreadAsync(this.ChannelId, this.Id, name, autoArchiveDuration, this.Channel.Type == ChannelType.News ? ChannelType.NewsThread : ChannelType.PublicThread, rateLimitPerUser, reason)
+ : throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(autoArchiveDuration == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
+
+ /// <summary>
+ /// Pins the message in its channel.
+ /// </summary>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task PinAsync()
+ => this.Discord.ApiClient.PinMessageAsync(this.ChannelId, this.Id);
+
+ /// <summary>
+ /// Unpins the message in its channel.
+ /// </summary>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task UnpinAsync()
+ => this.Discord.ApiClient.UnpinMessageAsync(this.ChannelId, this.Id);
+
+ /// <summary>
+ /// Responds to the message. This produces a reply.
+ /// </summary>
+ /// <param name="content">Message content to respond with.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> RespondAsync(string content)
+ => this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, content, null, sticker: null, replyMessageId: this.Id, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Responds to the message. This produces a reply.
+ /// </summary>
+ /// <param name="embed">Embed to attach to the message.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> RespondAsync(DiscordEmbed embed)
+ => this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, null, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: this.Id, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Responds to the message. This produces a reply.
+ /// </summary>
+ /// <param name="content">Message content to respond with.</param>
+ /// <param name="embed">Embed to attach to the message.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> RespondAsync(string content, DiscordEmbed embed)
+ => this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, content, embed != null ? new[] { embed } : null, sticker: null, replyMessageId: this.Id, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Responds to the message. This produces a reply.
+ /// </summary>
+ /// <param name="builder">The Discord message builder.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> RespondAsync(DiscordMessageBuilder builder)
+ => this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, builder.WithReply(this.Id, mention: false, failOnInvalidReply: false));
+
+ /// <summary>
+ /// Responds to the message. This produces a reply.
+ /// </summary>
+ /// <param name="action">The Discord message builder.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the member does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> RespondAsync(Action<DiscordMessageBuilder> action)
+ {
+ var builder = new DiscordMessageBuilder();
+ action(builder);
+ return this.Discord.ApiClient.CreateMessageAsync(this.ChannelId, builder.WithReply(this.Id, mention: false, failOnInvalidReply: false));
+ }
- /// <summary>
- /// Deletes another user's reaction.
- /// </summary>
- /// <param name="emoji">Emoji for the reaction you want to remove, either an emoji or name:id.</param>
- /// <param name="user">Member you want to remove the reaction for</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteReactionAsync(DiscordEmoji emoji, DiscordUser user, string reason = null)
- => this.Discord.ApiClient.DeleteUserReactionAsync(this.ChannelId, this.Id, user.Id, emoji.ToReactionString(), reason);
+ /// <summary>
+ /// Creates a reaction to this message.
+ /// </summary>
+ /// <param name="emoji">The emoji you want to react with, either an emoji or name:id</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AddReactions"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task CreateReactionAsync(DiscordEmoji emoji)
+ => this.Discord.ApiClient.CreateReactionAsync(this.ChannelId, this.Id, emoji.ToReactionString());
+
+ /// <summary>
+ /// Deletes your own reaction
+ /// </summary>
+ /// <param name="emoji">Emoji for the reaction you want to remove, either an emoji or name:id</param>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteOwnReactionAsync(DiscordEmoji emoji)
+ => this.Discord.ApiClient.DeleteOwnReactionAsync(this.ChannelId, this.Id, emoji.ToReactionString());
+
+ /// <summary>
+ /// Deletes another user's reaction.
+ /// </summary>
+ /// <param name="emoji">Emoji for the reaction you want to remove, either an emoji or name:id.</param>
+ /// <param name="user">Member you want to remove the reaction for</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteReactionAsync(DiscordEmoji emoji, DiscordUser user, string reason = null)
+ => this.Discord.ApiClient.DeleteUserReactionAsync(this.ChannelId, this.Id, user.Id, emoji.ToReactionString(), reason);
+
+ /// <summary>
+ /// Gets users that reacted with this emoji.
+ /// </summary>
+ /// <param name="emoji">Emoji to react with.</param>
+ /// <param name="limit">Limit of users to fetch.</param>
+ /// <param name="after">Fetch users after this user's id.</param>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<IReadOnlyList<DiscordUser>> GetReactionsAsync(DiscordEmoji emoji, int limit = 25, ulong? after = null)
+ => this.GetReactionsInternalAsync(emoji, limit, after);
+
+ /// <summary>
+ /// Deletes all reactions for this message.
+ /// </summary>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteAllReactionsAsync(string reason = null)
+ => this.Discord.ApiClient.DeleteAllReactionsAsync(this.ChannelId, this.Id, reason);
+
+ /// <summary>
+ /// Deletes all reactions of a specific reaction for this message.
+ /// </summary>
+ /// <param name="emoji">The emoji to clear, either an emoji or name:id.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteReactionsEmojiAsync(DiscordEmoji emoji)
+ => this.Discord.ApiClient.DeleteReactionsEmojiAsync(this.ChannelId, this.Id, emoji.ToReactionString());
+
+ /// <summary>
+ /// Gets the reactions.
+ /// </summary>
+ /// <param name="emoji">The emoji to search for.</param>
+ /// <param name="limit">The limit of results.</param>
+ /// <param name="after">Get the reasctions after snowflake.</param>
+ private async Task<IReadOnlyList<DiscordUser>> GetReactionsInternalAsync(DiscordEmoji emoji, int limit = 25, ulong? after = null)
+ {
+ if (limit < 0)
+ throw new ArgumentException("Cannot get a negative number of reactions' users.");
- /// <summary>
- /// Gets users that reacted with this emoji.
- /// </summary>
- /// <param name="emoji">Emoji to react with.</param>
- /// <param name="limit">Limit of users to fetch.</param>
- /// <param name="after">Fetch users after this user's id.</param>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<IReadOnlyList<DiscordUser>> GetReactionsAsync(DiscordEmoji emoji, int limit = 25, ulong? after = null)
- => this.GetReactionsInternalAsync(emoji, limit, after);
+ if (limit == 0)
+ return Array.Empty<DiscordUser>();
- /// <summary>
- /// Deletes all reactions for this message.
- /// </summary>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteAllReactionsAsync(string reason = null)
- => this.Discord.ApiClient.DeleteAllReactionsAsync(this.ChannelId, this.Id, reason);
+ var users = new List<DiscordUser>(limit);
+ var remaining = limit;
+ var last = after;
- /// <summary>
- /// Deletes all reactions of a specific reaction for this message.
- /// </summary>
- /// <param name="emoji">The emoji to clear, either an emoji or name:id.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the emoji does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteReactionsEmojiAsync(DiscordEmoji emoji)
- => this.Discord.ApiClient.DeleteReactionsEmojiAsync(this.ChannelId, this.Id, emoji.ToReactionString());
+ int lastCount;
+ do
+ {
+ var fetchSize = remaining > 100 ? 100 : remaining;
+ var fetch = await this.Discord.ApiClient.GetReactionsAsync(this.Channel.Id, this.Id, emoji.ToReactionString(), last, fetchSize).ConfigureAwait(false);
- /// <summary>
- /// Gets the reactions.
- /// </summary>
- /// <param name="emoji">The emoji to search for.</param>
- /// <param name="limit">The limit of results.</param>
- /// <param name="after">Get the reasctions after snowflake.</param>
- private async Task<IReadOnlyList<DiscordUser>> GetReactionsInternalAsync(DiscordEmoji emoji, int limit = 25, ulong? after = null)
- {
- if (limit < 0)
- throw new ArgumentException("Cannot get a negative number of reactions' users.");
+ lastCount = fetch.Count;
+ remaining -= lastCount;
- if (limit == 0)
- return Array.Empty<DiscordUser>();
+ users.AddRange(fetch);
+ last = fetch.LastOrDefault()?.Id;
+ } while (remaining > 0 && lastCount > 0);
- var users = new List<DiscordUser>(limit);
- var remaining = limit;
- var last = after;
+ return new ReadOnlyCollection<DiscordUser>(users);
+ }
- int lastCount;
- do
+ /// <summary>
+ /// Returns a string representation of this message.
+ /// </summary>
+ /// <returns>String representation of this message.</returns>
+ public override string ToString()
+ => $"Message {this.Id}; Attachment count: {this.AttachmentsInternal.Count}; Embed count: {this.EmbedsInternal.Count}; Contents: {this.Content}";
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordMessage"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordMessage"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordMessage);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordMessage"/> is equal to another <see cref="DiscordMessage"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordMessage"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordMessage"/> is equal to this <see cref="DiscordMessage"/>.</returns>
+ public bool Equals(DiscordMessage e)
+ => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.ChannelId == e.ChannelId));
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordMessage"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordMessage"/>.</returns>
+ public override int GetHashCode()
{
- var fetchSize = remaining > 100 ? 100 : remaining;
- var fetch = await this.Discord.ApiClient.GetReactionsAsync(this.Channel.Id, this.Id, emoji.ToReactionString(), last, fetchSize).ConfigureAwait(false);
-
- lastCount = fetch.Count;
- remaining -= lastCount;
-
- users.AddRange(fetch);
- last = fetch.LastOrDefault()?.Id;
- } while (remaining > 0 && lastCount > 0);
-
- return new ReadOnlyCollection<DiscordUser>(users);
- }
-
- /// <summary>
- /// Returns a string representation of this message.
- /// </summary>
- /// <returns>String representation of this message.</returns>
- public override string ToString()
- => $"Message {this.Id}; Attachment count: {this.AttachmentsInternal.Count}; Embed count: {this.EmbedsInternal.Count}; Contents: {this.Content}";
-
- /// <summary>
- /// Checks whether this <see cref="DiscordMessage"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordMessage"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordMessage);
+ var hash = 13;
- /// <summary>
- /// Checks whether this <see cref="DiscordMessage"/> is equal to another <see cref="DiscordMessage"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordMessage"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordMessage"/> is equal to this <see cref="DiscordMessage"/>.</returns>
- public bool Equals(DiscordMessage e)
- => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.ChannelId == e.ChannelId));
+ hash = (hash * 7) + this.Id.GetHashCode();
+ hash = (hash * 7) + this.ChannelId.GetHashCode();
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordMessage"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordMessage"/>.</returns>
- public override int GetHashCode()
- {
- var hash = 13;
-
- hash = (hash * 7) + this.Id.GetHashCode();
- hash = (hash * 7) + this.ChannelId.GetHashCode();
+ return hash;
+ }
- return hash;
- }
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordMessage"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First message to compare.</param>
+ /// <param name="e2">Second message to compare.</param>
+ /// <returns>Whether the two messages are equal.</returns>
+ public static bool operator ==(DiscordMessage e1, DiscordMessage e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- /// <summary>
- /// Gets whether the two <see cref="DiscordMessage"/> objects are equal.
- /// </summary>
- /// <param name="e1">First message to compare.</param>
- /// <param name="e2">Second message to compare.</param>
- /// <returns>Whether the two messages are equal.</returns>
- public static bool operator ==(DiscordMessage e1, DiscordMessage e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ return (o1 != null || o2 == null)
+ && (o1 == null || o2 != null)
+ && ((o1 == null && o2 == null) || (e1.Id == e2.Id && e1.ChannelId == e2.ChannelId));
+ }
- return (o1 != null || o2 == null)
- && (o1 == null || o2 != null)
- && ((o1 == null && o2 == null) || (e1.Id == e2.Id && e1.ChannelId == e2.ChannelId));
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordMessage"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First message to compare.</param>
+ /// <param name="e2">Second message to compare.</param>
+ /// <returns>Whether the two messages are not equal.</returns>
+ public static bool operator !=(DiscordMessage e1, DiscordMessage e2)
+ => !(e1 == e2);
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordMessage"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First message to compare.</param>
- /// <param name="e2">Second message to compare.</param>
- /// <returns>Whether the two messages are not equal.</returns>
- public static bool operator !=(DiscordMessage e1, DiscordMessage e2)
- => !(e1 == e2);
}
diff --git a/DisCatSharp/Entities/Message/DiscordMessageActivity.cs b/DisCatSharp/Entities/Message/DiscordMessageActivity.cs
index bbf70e42e..88e1839db 100644
--- a/DisCatSharp/Entities/Message/DiscordMessageActivity.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessageActivity.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Rich Presence activity.
-/// </summary>
-public class DiscordMessageActivity
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the activity type.
+ /// Represents a Rich Presence activity.
/// </summary>
- [JsonProperty("type")]
- public MessageActivityType Type { get; internal set; }
+ public class DiscordMessageActivity
+ {
+ /// <summary>
+ /// Gets the activity type.
+ /// </summary>
+ [JsonProperty("type")]
+ public MessageActivityType Type { get; internal set; }
- /// <summary>
- /// Gets the party id of the activity.
- /// </summary>
- [JsonProperty("party_id")]
- public string PartyId { get; internal set; }
+ /// <summary>
+ /// Gets the party id of the activity.
+ /// </summary>
+ [JsonProperty("party_id")]
+ public string PartyId { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordMessageActivity"/> class.
- /// </summary>
- internal DiscordMessageActivity()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMessageActivity"/> class.
+ /// </summary>
+ internal DiscordMessageActivity()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Message/DiscordMessageApplication.cs b/DisCatSharp/Entities/Message/DiscordMessageApplication.cs
index 2f6cfa2cc..d2d444e37 100644
--- a/DisCatSharp/Entities/Message/DiscordMessageApplication.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessageApplication.cs
@@ -1,61 +1,62 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Rich Presence application.
-/// </summary>
-public class DiscordMessageApplication : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the ID of this application's cover image.
+ /// Represents a Rich Presence application.
/// </summary>
- [JsonProperty("cover_image")]
- public virtual string CoverImageUrl { get; internal set; }
+ public class DiscordMessageApplication : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the ID of this application's cover image.
+ /// </summary>
+ [JsonProperty("cover_image")]
+ public virtual string CoverImageUrl { get; internal set; }
- /// <summary>
- /// Gets the application's description.
- /// </summary>
- [JsonProperty("description")]
- public string Description { get; internal set; }
+ /// <summary>
+ /// Gets the application's description.
+ /// </summary>
+ [JsonProperty("description")]
+ public string Description { get; internal set; }
- /// <summary>
- /// Gets the ID of the application's icon.
- /// </summary>
- [JsonProperty("icon")]
- public virtual string Icon { get; internal set; }
+ /// <summary>
+ /// Gets the ID of the application's icon.
+ /// </summary>
+ [JsonProperty("icon")]
+ public virtual string Icon { get; internal set; }
- /// <summary>
- /// Gets the application's name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
+ /// <summary>
+ /// Gets the application's name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordMessageApplication"/> class.
- /// </summary>
- internal DiscordMessageApplication()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMessageApplication"/> class.
+ /// </summary>
+ internal DiscordMessageApplication()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Message/DiscordMessageBuilder.cs b/DisCatSharp/Entities/Message/DiscordMessageBuilder.cs
index 8d470b611..823796c0c 100644
--- a/DisCatSharp/Entities/Message/DiscordMessageBuilder.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessageBuilder.cs
@@ -1,459 +1,460 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using System.Linq;
using System.Threading.Tasks;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Constructs a Message to be sent.
-/// </summary>
-public sealed class DiscordMessageBuilder
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets or Sets the Message to be sent.
+ /// Constructs a Message to be sent.
/// </summary>
- public string Content
+ public sealed class DiscordMessageBuilder
{
- get => this._content;
- set
+ /// <summary>
+ /// Gets or Sets the Message to be sent.
+ /// </summary>
+ public string Content
{
- if (value != null && value.Length > 2000)
- throw new ArgumentException("Content cannot exceed 2000 characters.", nameof(value));
- this._content = value;
+ get => this._content;
+ set
+ {
+ if (value != null && value.Length > 2000)
+ throw new ArgumentException("Content cannot exceed 2000 characters.", nameof(value));
+ this._content = value;
+ }
}
- }
- private string _content;
+ private string _content;
- /// <summary>
- /// Gets or sets the embed for the builder. This will always set the builder to have one embed.
- /// </summary>
- public DiscordEmbed Embed
- {
- get => this._embeds.Count > 0 ? this._embeds[0] : null;
- set
+ /// <summary>
+ /// Gets or sets the embed for the builder. This will always set the builder to have one embed.
+ /// </summary>
+ public DiscordEmbed Embed
{
- this._embeds.Clear();
- this._embeds.Add(value);
+ get => this._embeds.Count > 0 ? this._embeds[0] : null;
+ set
+ {
+ this._embeds.Clear();
+ this._embeds.Add(value);
+ }
}
- }
-
- /// <summary>
- /// Gets the Sticker to be send.
- /// </summary>
- public DiscordSticker Sticker { get; set; }
-
- /// <summary>
- /// Gets the Embeds to be sent.
- /// </summary>
- public IReadOnlyList<DiscordEmbed> Embeds => this._embeds;
- private readonly List<DiscordEmbed> _embeds = new();
-
- /// <summary>
- /// Gets or Sets if the message should be TTS.
- /// </summary>
- public bool IsTts { get; set; }
-
- /// <summary>
- /// Whether to keep previous attachments.
- /// </summary>
- internal bool? KeepAttachmentsInternal;
-
- /// <summary>
- /// Gets the Allowed Mentions for the message to be sent.
- /// </summary>
- public List<IMention> Mentions { get; private set; }
-
- /// <summary>
- /// Gets the Files to be sent in the Message.
- /// </summary>
- public IReadOnlyCollection<DiscordMessageFile> Files => this.FilesInternal;
- internal readonly List<DiscordMessageFile> FilesInternal = new();
-
- /// <summary>
- /// Gets the components that will be attached to the message.
- /// </summary>
- public IReadOnlyList<DiscordActionRowComponent> Components => this.ComponentsInternal;
- internal readonly List<DiscordActionRowComponent> ComponentsInternal = new(5);
-
- /// <summary>
- /// Gets the Attachments to be sent in the Message.
- /// </summary>
- public IReadOnlyList<DiscordAttachment> Attachments => this.AttachmentsInternal;
- internal readonly List<DiscordAttachment> AttachmentsInternal = new();
-
- /// <summary>
- /// Gets the Reply Message ID.
- /// </summary>
- public ulong? ReplyId { get; private set; }
-
- /// <summary>
- /// Gets if the Reply should mention the user.
- /// </summary>
- public bool MentionOnReply { get; private set; }
-
- /// <summary>
- /// Gets if the embeds should be suppressed.
- /// </summary>
- public bool Suppressed { get; private set; }
-
- /// <summary>
- /// Gets if the Reply will error if the Reply Message Id does not reference a valid message.
- /// <para>If set to false, invalid replies are send as a regular message.</para>
- /// <para>Defaults to false.</para>
- /// </summary>
- public bool FailOnInvalidReply { get; set; }
-
- /// <summary>
- /// Sets the Content of the Message.
- /// </summary>
- /// <param name="content">The content to be set.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder WithContent(string content)
- {
- this.Content = content;
- return this;
- }
-
- /// <summary>
- /// Adds a sticker to the message. Sticker must be from current guild.
- /// </summary>
- /// <param name="sticker">The sticker to add.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder WithSticker(DiscordSticker sticker)
- {
- this.Sticker = sticker;
- return this;
- }
-
- /// <summary>
- /// Adds a row of components to a message, up to 5 components per row, and up to 5 rows per message.
- /// </summary>
- /// <param name="components">The components to add to the message.</param>
- /// <returns>The current builder to be chained.</returns>
- /// <exception cref="System.ArgumentOutOfRangeException">No components were passed.</exception>
- public DiscordMessageBuilder AddComponents(params DiscordComponent[] components)
- => this.AddComponents((IEnumerable<DiscordComponent>)components);
-
- /// <summary>
- /// Appends several rows of components to the message
- /// </summary>
- /// <param name="components">The rows of components to add, holding up to five each.</param>
- /// <returns></returns>
- public DiscordMessageBuilder AddComponents(IEnumerable<DiscordActionRowComponent> components)
- {
- var ara = components.ToArray();
+ /// <summary>
+ /// Gets the Sticker to be send.
+ /// </summary>
+ public DiscordSticker Sticker { get; set; }
+
+ /// <summary>
+ /// Gets the Embeds to be sent.
+ /// </summary>
+ public IReadOnlyList<DiscordEmbed> Embeds => this._embeds;
+ private readonly List<DiscordEmbed> _embeds = new();
+
+ /// <summary>
+ /// Gets or Sets if the message should be TTS.
+ /// </summary>
+ public bool IsTts { get; set; }
+
+ /// <summary>
+ /// Whether to keep previous attachments.
+ /// </summary>
+ internal bool? KeepAttachmentsInternal;
+
+ /// <summary>
+ /// Gets the Allowed Mentions for the message to be sent.
+ /// </summary>
+ public List<IMention> Mentions { get; private set; }
+
+ /// <summary>
+ /// Gets the Files to be sent in the Message.
+ /// </summary>
+ public IReadOnlyCollection<DiscordMessageFile> Files => this.FilesInternal;
+ internal readonly List<DiscordMessageFile> FilesInternal = new();
+
+ /// <summary>
+ /// Gets the components that will be attached to the message.
+ /// </summary>
+ public IReadOnlyList<DiscordActionRowComponent> Components => this.ComponentsInternal;
+ internal readonly List<DiscordActionRowComponent> ComponentsInternal = new(5);
+
+ /// <summary>
+ /// Gets the Attachments to be sent in the Message.
+ /// </summary>
+ public IReadOnlyList<DiscordAttachment> Attachments => this.AttachmentsInternal;
+ internal readonly List<DiscordAttachment> AttachmentsInternal = new();
+
+ /// <summary>
+ /// Gets the Reply Message ID.
+ /// </summary>
+ public ulong? ReplyId { get; private set; }
+
+ /// <summary>
+ /// Gets if the Reply should mention the user.
+ /// </summary>
+ public bool MentionOnReply { get; private set; }
+
+ /// <summary>
+ /// Gets if the embeds should be suppressed.
+ /// </summary>
+ public bool Suppressed { get; private set; }
+
+ /// <summary>
+ /// Gets if the Reply will error if the Reply Message Id does not reference a valid message.
+ /// <para>If set to false, invalid replies are send as a regular message.</para>
+ /// <para>Defaults to false.</para>
+ /// </summary>
+ public bool FailOnInvalidReply { get; set; }
+
+ /// <summary>
+ /// Sets the Content of the Message.
+ /// </summary>
+ /// <param name="content">The content to be set.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder WithContent(string content)
+ {
+ this.Content = content;
+ return this;
+ }
- if (ara.Length + this.ComponentsInternal.Count > 5)
- throw new ArgumentException("ActionRow count exceeds maximum of five.");
+ /// <summary>
+ /// Adds a sticker to the message. Sticker must be from current guild.
+ /// </summary>
+ /// <param name="sticker">The sticker to add.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder WithSticker(DiscordSticker sticker)
+ {
+ this.Sticker = sticker;
+ return this;
+ }
- foreach (var ar in ara)
- this.ComponentsInternal.Add(ar);
+ /// <summary>
+ /// Adds a row of components to a message, up to 5 components per row, and up to 5 rows per message.
+ /// </summary>
+ /// <param name="components">The components to add to the message.</param>
+ /// <returns>The current builder to be chained.</returns>
+ /// <exception cref="System.ArgumentOutOfRangeException">No components were passed.</exception>
+ public DiscordMessageBuilder AddComponents(params DiscordComponent[] components)
+ => this.AddComponents((IEnumerable<DiscordComponent>)components);
+
+
+ /// <summary>
+ /// Appends several rows of components to the message
+ /// </summary>
+ /// <param name="components">The rows of components to add, holding up to five each.</param>
+ /// <returns></returns>
+ public DiscordMessageBuilder AddComponents(IEnumerable<DiscordActionRowComponent> components)
+ {
+ var ara = components.ToArray();
- return this;
- }
+ if (ara.Length + this.ComponentsInternal.Count > 5)
+ throw new ArgumentException("ActionRow count exceeds maximum of five.");
- /// <summary>
- /// Adds a row of components to a message, up to 5 components per row, and up to 5 rows per message.
- /// </summary>
- /// <param name="components">The components to add to the message.</param>
- /// <returns>The current builder to be chained.</returns>
- /// <exception cref="System.ArgumentOutOfRangeException">No components were passed.</exception>
- public DiscordMessageBuilder AddComponents(IEnumerable<DiscordComponent> components)
- {
- var cmpArr = components.ToArray();
- var count = cmpArr.Length;
+ foreach (var ar in ara)
+ this.ComponentsInternal.Add(ar);
- if (!cmpArr.Any())
- throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
+ return this;
+ }
- if (count > 5)
- throw new ArgumentException("Cannot add more than 5 components per action row!");
+ /// <summary>
+ /// Adds a row of components to a message, up to 5 components per row, and up to 5 rows per message.
+ /// </summary>
+ /// <param name="components">The components to add to the message.</param>
+ /// <returns>The current builder to be chained.</returns>
+ /// <exception cref="System.ArgumentOutOfRangeException">No components were passed.</exception>
+ public DiscordMessageBuilder AddComponents(IEnumerable<DiscordComponent> components)
+ {
+ var cmpArr = components.ToArray();
+ var count = cmpArr.Length;
- var comp = new DiscordActionRowComponent(cmpArr);
- this.ComponentsInternal.Add(comp);
+ if (!cmpArr.Any())
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
- return this;
- }
+ if (count > 5)
+ throw new ArgumentException("Cannot add more than 5 components per action row!");
- /// <summary>
- /// Sets if the message should be TTS.
- /// </summary>
- /// <param name="isTts">If TTS should be set.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder HasTts(bool isTts)
- {
- this.IsTts = isTts;
- return this;
- }
+ var comp = new DiscordActionRowComponent(cmpArr);
+ this.ComponentsInternal.Add(comp);
- /// <summary>
- /// Sets the embed for the current builder.
- /// </summary>
- /// <param name="embed">The embed that should be set.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder WithEmbed(DiscordEmbed embed)
- {
- if (embed == null)
return this;
+ }
- this.Embed = embed;
- return this;
- }
-
- /// <summary>
- /// Appends an embed to the current builder.
- /// </summary>
- /// <param name="embed">The embed that should be appended.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder AddEmbed(DiscordEmbed embed)
- {
- if (embed == null)
- return this; //Providing null embeds will produce a 400 response from Discord.//
- this._embeds.Add(embed);
- return this;
- }
-
- /// <summary>
- /// Appends several embeds to the current builder.
- /// </summary>
- /// <param name="embeds">The embeds that should be appended.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder AddEmbeds(IEnumerable<DiscordEmbed> embeds)
- {
- this._embeds.AddRange(embeds);
- return this;
- }
-
- /// <summary>
- /// Sets if the message has allowed mentions.
- /// </summary>
- /// <param name="allowedMention">The allowed Mention that should be sent.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder WithAllowedMention(IMention allowedMention)
- {
- if (this.Mentions != null)
- this.Mentions.Add(allowedMention);
- else
- this.Mentions = new List<IMention> { allowedMention };
+ /// <summary>
+ /// Sets if the message should be TTS.
+ /// </summary>
+ /// <param name="isTts">If TTS should be set.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder HasTts(bool isTts)
+ {
+ this.IsTts = isTts;
+ return this;
+ }
- return this;
- }
+ /// <summary>
+ /// Sets the embed for the current builder.
+ /// </summary>
+ /// <param name="embed">The embed that should be set.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder WithEmbed(DiscordEmbed embed)
+ {
+ if (embed == null)
+ return this;
- /// <summary>
- /// Sets if the message has allowed mentions.
- /// </summary>
- /// <param name="allowedMentions">The allowed Mentions that should be sent.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder WithAllowedMentions(IEnumerable<IMention> allowedMentions)
- {
- if (this.Mentions != null)
- this.Mentions.AddRange(allowedMentions);
- else
- this.Mentions = allowedMentions.ToList();
+ this.Embed = embed;
+ return this;
+ }
- return this;
- }
+ /// <summary>
+ /// Appends an embed to the current builder.
+ /// </summary>
+ /// <param name="embed">The embed that should be appended.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder AddEmbed(DiscordEmbed embed)
+ {
+ if (embed == null)
+ return this; //Providing null embeds will produce a 400 response from Discord.//
+ this._embeds.Add(embed);
+ return this;
+ }
- /// <summary>
- /// Sets if the message has files to be sent.
- /// </summary>
- /// <param name="fileName">The fileName that the file should be sent as.</param>
- /// <param name="stream">The Stream to the file.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <param name="description">Description of the file.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder WithFile(string fileName, Stream stream, bool resetStreamPosition = false, string description = null)
- {
- if (this.Files.Count > 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ /// <summary>
+ /// Appends several embeds to the current builder.
+ /// </summary>
+ /// <param name="embeds">The embeds that should be appended.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder AddEmbeds(IEnumerable<DiscordEmbed> embeds)
+ {
+ this._embeds.AddRange(embeds);
+ return this;
+ }
- if (this.FilesInternal.Any(x => x.FileName == fileName))
- throw new ArgumentException("A File with that filename already exists");
+ /// <summary>
+ /// Sets if the message has allowed mentions.
+ /// </summary>
+ /// <param name="allowedMention">The allowed Mention that should be sent.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder WithAllowedMention(IMention allowedMention)
+ {
+ if (this.Mentions != null)
+ this.Mentions.Add(allowedMention);
+ else
+ this.Mentions = new List<IMention> { allowedMention };
- if (resetStreamPosition)
- this.FilesInternal.Add(new DiscordMessageFile(fileName, stream, stream.Position, description: description));
- else
- this.FilesInternal.Add(new DiscordMessageFile(fileName, stream, null, description: description));
+ return this;
+ }
- return this;
- }
+ /// <summary>
+ /// Sets if the message has allowed mentions.
+ /// </summary>
+ /// <param name="allowedMentions">The allowed Mentions that should be sent.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder WithAllowedMentions(IEnumerable<IMention> allowedMentions)
+ {
+ if (this.Mentions != null)
+ this.Mentions.AddRange(allowedMentions);
+ else
+ this.Mentions = allowedMentions.ToList();
- /// <summary>
- /// Sets if the message has files to be sent.
- /// </summary>
- /// <param name="stream">The Stream to the file.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <param name="description">Description of the file.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder WithFile(FileStream stream, bool resetStreamPosition = false, string description = null)
- {
- if (this.Files.Count > 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ return this;
+ }
- if (this.FilesInternal.Any(x => x.FileName == stream.Name))
- throw new ArgumentException("A File with that filename already exists");
+ /// <summary>
+ /// Sets if the message has files to be sent.
+ /// </summary>
+ /// <param name="fileName">The fileName that the file should be sent as.</param>
+ /// <param name="stream">The Stream to the file.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <param name="description">Description of the file.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder WithFile(string fileName, Stream stream, bool resetStreamPosition = false, string description = null)
+ {
+ if (this.Files.Count > 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (resetStreamPosition)
- this.FilesInternal.Add(new DiscordMessageFile(stream.Name, stream, stream.Position, description: description));
- else
- this.FilesInternal.Add(new DiscordMessageFile(stream.Name, stream, null, description: description));
+ if (this.FilesInternal.Any(x => x.FileName == fileName))
+ throw new ArgumentException("A File with that filename already exists");
- return this;
- }
+ if (resetStreamPosition)
+ this.FilesInternal.Add(new DiscordMessageFile(fileName, stream, stream.Position, description: description));
+ else
+ this.FilesInternal.Add(new DiscordMessageFile(fileName, stream, null, description: description));
- /// <summary>
- /// Sets if the message has files to be sent.
- /// </summary>
- /// <param name="files">The Files that should be sent.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder WithFiles(Dictionary<string, Stream> files, bool resetStreamPosition = false)
- {
- if (this.Files.Count + files.Count > 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ return this;
+ }
- foreach (var file in files)
+ /// <summary>
+ /// Sets if the message has files to be sent.
+ /// </summary>
+ /// <param name="stream">The Stream to the file.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <param name="description">Description of the file.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder WithFile(FileStream stream, bool resetStreamPosition = false, string description = null)
{
- if (this.FilesInternal.Any(x => x.FileName == file.Key))
+ if (this.Files.Count > 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
+
+ if (this.FilesInternal.Any(x => x.FileName == stream.Name))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this.FilesInternal.Add(new DiscordMessageFile(file.Key, file.Value, file.Value.Position));
+ this.FilesInternal.Add(new DiscordMessageFile(stream.Name, stream, stream.Position, description: description));
else
- this.FilesInternal.Add(new DiscordMessageFile(file.Key, file.Value, null));
+ this.FilesInternal.Add(new DiscordMessageFile(stream.Name, stream, null, description: description));
+
+ return this;
}
- return this;
- }
+ /// <summary>
+ /// Sets if the message has files to be sent.
+ /// </summary>
+ /// <param name="files">The Files that should be sent.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder WithFiles(Dictionary<string, Stream> files, bool resetStreamPosition = false)
+ {
+ if (this.Files.Count + files.Count > 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
- /// <summary>
- /// Modifies the given attachments on edit.
- /// </summary>
- /// <param name="attachments">Attachments to edit.</param>
- /// <returns></returns>
- public DiscordMessageBuilder ModifyAttachments(IEnumerable<DiscordAttachment> attachments)
- {
- this.AttachmentsInternal.AddRange(attachments);
- return this;
- }
+ foreach (var file in files)
+ {
+ if (this.FilesInternal.Any(x => x.FileName == file.Key))
+ throw new ArgumentException("A File with that filename already exists");
- /// <summary>
- /// Whether to keep the message attachments, if new ones are added.
- /// </summary>
- /// <returns></returns>
- public DiscordMessageBuilder KeepAttachments(bool keep)
- {
- this.KeepAttachmentsInternal = keep;
- return this;
- }
+ if (resetStreamPosition)
+ this.FilesInternal.Add(new DiscordMessageFile(file.Key, file.Value, file.Value.Position));
+ else
+ this.FilesInternal.Add(new DiscordMessageFile(file.Key, file.Value, null));
+ }
- /// <summary>
- /// Sets if the message is a reply
- /// </summary>
- /// <param name="messageId">The ID of the message to reply to.</param>
- /// <param name="mention">If we should mention the user in the reply.</param>
- /// <param name="failOnInvalidReply">Whether sending a reply that references an invalid message should be </param>
- /// <returns>The current builder to be chained.</returns>
- public DiscordMessageBuilder WithReply(ulong messageId, bool mention = false, bool failOnInvalidReply = false)
- {
- this.ReplyId = messageId;
- this.MentionOnReply = mention;
- this.FailOnInvalidReply = failOnInvalidReply;
+ return this;
+ }
- if (mention)
+ /// <summary>
+ /// Modifies the given attachments on edit.
+ /// </summary>
+ /// <param name="attachments">Attachments to edit.</param>
+ /// <returns></returns>
+ public DiscordMessageBuilder ModifyAttachments(IEnumerable<DiscordAttachment> attachments)
{
- this.Mentions ??= new List<IMention>();
- this.Mentions.Add(new RepliedUserMention());
+ this.AttachmentsInternal.AddRange(attachments);
+ return this;
}
- return this;
- }
-
+ /// <summary>
+ /// Whether to keep the message attachments, if new ones are added.
+ /// </summary>
+ /// <returns></returns>
+ public DiscordMessageBuilder KeepAttachments(bool keep)
+ {
+ this.KeepAttachmentsInternal = keep;
+ return this;
+ }
- /// <summary>
- /// Sends the Message to a specific channel
- /// </summary>
- /// <param name="channel">The channel the message should be sent to.</param>
- /// <returns>The current builder to be chained.</returns>
- public Task<DiscordMessage> SendAsync(DiscordChannel channel) => channel.SendMessageAsync(this);
+ /// <summary>
+ /// Sets if the message is a reply
+ /// </summary>
+ /// <param name="messageId">The ID of the message to reply to.</param>
+ /// <param name="mention">If we should mention the user in the reply.</param>
+ /// <param name="failOnInvalidReply">Whether sending a reply that references an invalid message should be </param>
+ /// <returns>The current builder to be chained.</returns>
+ public DiscordMessageBuilder WithReply(ulong messageId, bool mention = false, bool failOnInvalidReply = false)
+ {
+ this.ReplyId = messageId;
+ this.MentionOnReply = mention;
+ this.FailOnInvalidReply = failOnInvalidReply;
- /// <summary>
- /// Sends the modified message.
- /// <para>Note: Message replies cannot be modified. To clear the reply, simply pass <see langword="null"/> to <see cref="WithReply"/>.</para>
- /// </summary>
- /// <param name="msg">The original Message to modify.</param>
- /// <returns>The current builder to be chained.</returns>
- public Task<DiscordMessage> ModifyAsync(DiscordMessage msg) => msg.ModifyAsync(this);
+ if (mention)
+ {
+ this.Mentions ??= new List<IMention>();
+ this.Mentions.Add(new RepliedUserMention());
+ }
- /// <summary>
- /// Clears all message components on this builder.
- /// </summary>
- public void ClearComponents()
- => this.ComponentsInternal.Clear();
+ return this;
+ }
- /// <summary>
- /// Allows for clearing the Message Builder so that it can be used again to send a new message.
- /// </summary>
- public void Clear()
- {
- this.Content = "";
- this._embeds.Clear();
- this.IsTts = false;
- this.Mentions = null;
- this.FilesInternal.Clear();
- this.ReplyId = null;
- this.MentionOnReply = false;
- this.ComponentsInternal.Clear();
- this.Suppressed = false;
- this.Sticker = null;
- this.AttachmentsInternal.Clear();
- this.KeepAttachmentsInternal = false;
- }
- /// <summary>
- /// Does the validation before we send a the Create/Modify request.
- /// </summary>
- /// <param name="isModify">Tells the method to perform the Modify Validation or Create Validation.</param>
- internal void Validate(bool isModify = false)
- {
- if (this._embeds.Count > 10)
- throw new ArgumentException("A message can only have up to 10 embeds.");
+ /// <summary>
+ /// Sends the Message to a specific channel
+ /// </summary>
+ /// <param name="channel">The channel the message should be sent to.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public Task<DiscordMessage> SendAsync(DiscordChannel channel) => channel.SendMessageAsync(this);
+
+ /// <summary>
+ /// Sends the modified message.
+ /// <para>Note: Message replies cannot be modified. To clear the reply, simply pass <see langword="null"/> to <see cref="WithReply"/>.</para>
+ /// </summary>
+ /// <param name="msg">The original Message to modify.</param>
+ /// <returns>The current builder to be chained.</returns>
+ public Task<DiscordMessage> ModifyAsync(DiscordMessage msg) => msg.ModifyAsync(this);
+
+ /// <summary>
+ /// Clears all message components on this builder.
+ /// </summary>
+ public void ClearComponents()
+ => this.ComponentsInternal.Clear();
+
+ /// <summary>
+ /// Allows for clearing the Message Builder so that it can be used again to send a new message.
+ /// </summary>
+ public void Clear()
+ {
+ this.Content = "";
+ this._embeds.Clear();
+ this.IsTts = false;
+ this.Mentions = null;
+ this.FilesInternal.Clear();
+ this.ReplyId = null;
+ this.MentionOnReply = false;
+ this.ComponentsInternal.Clear();
+ this.Suppressed = false;
+ this.Sticker = null;
+ this.AttachmentsInternal.Clear();
+ this.KeepAttachmentsInternal = false;
+ }
- if (!isModify)
+ /// <summary>
+ /// Does the validation before we send a the Create/Modify request.
+ /// </summary>
+ /// <param name="isModify">Tells the method to perform the Modify Validation or Create Validation.</param>
+ internal void Validate(bool isModify = false)
{
- if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && (!this.Embeds?.Any() ?? true) && this.Sticker is null)
- throw new ArgumentException("You must specify content, an embed, a sticker or at least one file.");
+ if (this._embeds.Count > 10)
+ throw new ArgumentException("A message can only have up to 10 embeds.");
+
+ if (!isModify)
+ {
+ if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && (!this.Embeds?.Any() ?? true) && this.Sticker is null)
+ throw new ArgumentException("You must specify content, an embed, a sticker or at least one file.");
- if (this.Components.Count > 5)
- throw new InvalidOperationException("You can only have 5 action rows per message.");
+ if (this.Components.Count > 5)
+ throw new InvalidOperationException("You can only have 5 action rows per message.");
- if (this.Components.Any(c => c.Components.Count > 5))
- throw new InvalidOperationException("Action rows can only have 5 components");
+ if (this.Components.Any(c => c.Components.Count > 5))
+ throw new InvalidOperationException("Action rows can only have 5 components");
+ }
}
}
}
diff --git a/DisCatSharp/Entities/Message/DiscordMessageFile.cs b/DisCatSharp/Entities/Message/DiscordMessageFile.cs
index 689ed18a8..cc7c498c4 100644
--- a/DisCatSharp/Entities/Message/DiscordMessageFile.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessageFile.cs
@@ -1,80 +1,81 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents the File that should be sent to Discord from the <see cref="DisCatSharp.Entities.DiscordMessageBuilder"/>.
-/// </summary>
-public class DiscordMessageFile
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordMessageFile"/> class.
+ /// Represents the File that should be sent to Discord from the <see cref="DisCatSharp.Entities.DiscordMessageBuilder"/>.
/// </summary>
- /// <param name="fileName">The file name.</param>
- /// <param name="stream">The stream.</param>
- /// <param name="resetPositionTo">The reset position to.</param>
- /// <param name="fileType">The file type.</param>
- /// <param name="contentType">The content type.</param>
- /// <param name="description">The description.</param>
- internal DiscordMessageFile(string fileName, Stream stream, long? resetPositionTo, string fileType = null, string contentType = null, string description = null)
+ public class DiscordMessageFile
{
- this.FileName = fileName;
- this.FileType = fileType;
- this.ContentType = contentType;
- this.Stream = stream;
- this.ResetPositionTo = resetPositionTo;
- this.Description = description;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMessageFile"/> class.
+ /// </summary>
+ /// <param name="fileName">The file name.</param>
+ /// <param name="stream">The stream.</param>
+ /// <param name="resetPositionTo">The reset position to.</param>
+ /// <param name="fileType">The file type.</param>
+ /// <param name="contentType">The content type.</param>
+ /// <param name="description">The description.</param>
+ internal DiscordMessageFile(string fileName, Stream stream, long? resetPositionTo, string fileType = null, string contentType = null, string description = null)
+ {
+ this.FileName = fileName;
+ this.FileType = fileType;
+ this.ContentType = contentType;
+ this.Stream = stream;
+ this.ResetPositionTo = resetPositionTo;
+ this.Description = description;
+ }
- /// <summary>
- /// Gets the FileName of the File.
- /// </summary>
- public string FileName { get; internal set; }
+ /// <summary>
+ /// Gets the FileName of the File.
+ /// </summary>
+ public string FileName { get; internal set; }
- /// <summary>
- /// Gets the description of the File.
- /// </summary>
- public string Description { get; internal set; }
+ /// <summary>
+ /// Gets the description of the File.
+ /// </summary>
+ public string Description { get; internal set; }
- /// <summary>
- /// Gets the stream of the File.
- /// </summary>
- public Stream Stream { get; internal set; }
+ /// <summary>
+ /// Gets the stream of the File.
+ /// </summary>
+ public Stream Stream { get; internal set; }
- /// <summary>
- /// Gets or sets the file type.
- /// </summary>
- internal string FileType { get; set; }
+ /// <summary>
+ /// Gets or sets the file type.
+ /// </summary>
+ internal string FileType { get; set; }
- /// <summary>
- /// Gets or sets the content type.
- /// </summary>
- internal string ContentType { get; set; }
+ /// <summary>
+ /// Gets or sets the content type.
+ /// </summary>
+ internal string ContentType { get; set; }
- /// <summary>
- /// Gets the position the File should be reset to.
- /// </summary>
- internal long? ResetPositionTo { get; set; }
+ /// <summary>
+ /// Gets the position the File should be reset to.
+ /// </summary>
+ internal long? ResetPositionTo { get; set; }
+ }
}
diff --git a/DisCatSharp/Entities/Message/DiscordMessageInteraction.cs b/DisCatSharp/Entities/Message/DiscordMessageInteraction.cs
index eb56b5a6b..a77db1f8e 100644
--- a/DisCatSharp/Entities/Message/DiscordMessageInteraction.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessageInteraction.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents the message interaction data sent when a message is an interaction response.
-/// </summary>
-public class DiscordMessageInteraction : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the type of the interaction.
+ /// Represents the message interaction data sent when a message is an interaction response.
/// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public InteractionType Type { get; internal set; }
+ public class DiscordMessageInteraction : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the type of the interaction.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public InteractionType Type { get; internal set; }
- /// <summary>
- /// Gets the name of the <see cref="DiscordApplicationCommand"/>.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
+ /// <summary>
+ /// Gets the name of the <see cref="DiscordApplicationCommand"/>.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the user who invoked the interaction.
- /// </summary>
- [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUser User { get; internal set; }
+ /// <summary>
+ /// Gets the user who invoked the interaction.
+ /// </summary>
+ [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUser User { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Message/DiscordMessageReference.cs b/DisCatSharp/Entities/Message/DiscordMessageReference.cs
index c339fb249..d7e49ac98 100644
--- a/DisCatSharp/Entities/Message/DiscordMessageReference.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessageReference.cs
@@ -1,84 +1,85 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents data from the original message.
-/// </summary>
-public class DiscordMessageReference
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the original message.
+ /// Represents data from the original message.
/// </summary>
- public DiscordMessage Message { get; internal set; }
+ public class DiscordMessageReference
+ {
+ /// <summary>
+ /// Gets the original message.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
- /// <summary>
- /// Gets the channel of the original message.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
+ /// <summary>
+ /// Gets the channel of the original message.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the guild of the original message.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild of the original message.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets a readable message reference string.
- /// </summary>
- public override string ToString()
- => $"Guild: {this.Guild.Id}, Channel: {this.Channel.Id}, Message: {this.Message.Id}";
+ /// <summary>
+ /// Gets a readable message reference string.
+ /// </summary>
+ public override string ToString()
+ => $"Guild: {this.Guild.Id}, Channel: {this.Channel.Id}, Message: {this.Message.Id}";
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordMessageReference"/> class.
- /// </summary>
- internal DiscordMessageReference() { }
-}
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordMessageReference"/> class.
+ /// </summary>
+ internal DiscordMessageReference() { }
+ }
-internal struct InternalDiscordMessageReference
-{
- /// <summary>
- /// Gets the message id.
- /// </summary>
- [JsonProperty("message_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong? MessageId { get; set; }
+ internal struct InternalDiscordMessageReference
+ {
+ /// <summary>
+ /// Gets the message id.
+ /// </summary>
+ [JsonProperty("message_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong? MessageId { get; set; }
- /// <summary>
- /// Gets the channel id.
- /// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong? ChannelId { get; set; }
+ /// <summary>
+ /// Gets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong? ChannelId { get; set; }
- /// <summary>
- /// Gets the guild id.
- /// </summary>
- [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong? GuildId { get; set; }
+ /// <summary>
+ /// Gets the guild id.
+ /// </summary>
+ [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong? GuildId { get; set; }
- /// <summary>
- /// Whether it should fail if it does not exists.
- /// </summary>
- [JsonProperty("fail_if_not_exists", NullValueHandling = NullValueHandling.Ignore)]
- public bool FailIfNotExists { get; set; }
+ /// <summary>
+ /// Whether it should fail if it does not exists.
+ /// </summary>
+ [JsonProperty("fail_if_not_exists", NullValueHandling = NullValueHandling.Ignore)]
+ public bool FailIfNotExists { get; set; }
+ }
}
diff --git a/DisCatSharp/Entities/Message/DiscordReaction.cs b/DisCatSharp/Entities/Message/DiscordReaction.cs
index c8ddb8a45..ecbbd8052 100644
--- a/DisCatSharp/Entities/Message/DiscordReaction.cs
+++ b/DisCatSharp/Entities/Message/DiscordReaction.cs
@@ -1,55 +1,56 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a reaction to a message.
-/// </summary>
-public class DiscordReaction
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the total number of users who reacted with this emoji.
+ /// Represents a reaction to a message.
/// </summary>
- [JsonProperty("count", NullValueHandling = NullValueHandling.Ignore)]
- public int Count { get; internal set; }
+ public class DiscordReaction
+ {
+ /// <summary>
+ /// Gets the total number of users who reacted with this emoji.
+ /// </summary>
+ [JsonProperty("count", NullValueHandling = NullValueHandling.Ignore)]
+ public int Count { get; internal set; }
- /// <summary>
- /// Gets whether the current user reacted with this emoji.
- /// </summary>
- [JsonProperty("me", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsMe { get; internal set; }
+ /// <summary>
+ /// Gets whether the current user reacted with this emoji.
+ /// </summary>
+ [JsonProperty("me", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsMe { get; internal set; }
- /// <summary>
- /// Gets the emoji used to react to this message.
- /// </summary>
- [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordEmoji Emoji { get; internal set; }
+ /// <summary>
+ /// Gets the emoji used to react to this message.
+ /// </summary>
+ [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordEmoji Emoji { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordReaction"/> class.
- /// </summary>
- internal DiscordReaction()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordReaction"/> class.
+ /// </summary>
+ internal DiscordReaction()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Message/Mentions.cs b/DisCatSharp/Entities/Message/Mentions.cs
index b1d1c397b..3cd5ba686 100644
--- a/DisCatSharp/Entities/Message/Mentions.cs
+++ b/DisCatSharp/Entities/Message/Mentions.cs
@@ -1,132 +1,133 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Interface for mentionables
-/// </summary>
-public interface IMention { }
-
-
-/// <summary>
-/// Allows a reply to ping the user being replied to.
-/// </summary>
-public readonly struct RepliedUserMention : IMention
-{
- //This is pointless because new RepliedUserMention() will work, but it is here for consistency with the other mentionables.
- /// <summary>
- /// Mention the user being replied to. Alias to <see cref="RepliedUserMention()"/> constructor.
- /// </summary>
- public static readonly RepliedUserMention All = new();
-}
-
-/// <summary>
-/// Allows @everyone and @here pings to mention in the message.
-/// </summary>
-public readonly struct EveryoneMention : IMention
+namespace DisCatSharp.Entities
{
- //This is pointless because new EveryoneMention() will work, but it is here for consistency with the other mentionables.
- /// <summary>
- /// Allow the mentioning of @everyone and @here. Alias to <see cref="EveryoneMention()"/> constructor.
- /// </summary>
- public static readonly EveryoneMention All = new();
-}
-
-/// <summary>
-/// Allows @user pings to mention in the message.
-/// </summary>
-public readonly struct UserMention : IMention
-{
- /// <summary>
- /// Allow mentioning of all users. Alias to <see cref="UserMention()"/> constructor.
- /// </summary>
- public static readonly UserMention All = new();
-
/// <summary>
- /// Optional Id of the user that is allowed to be mentioned. If null, then all user mentions will be allowed.
+ /// Interface for mentionables
/// </summary>
- public ulong? Id { get; }
+ public interface IMention { }
- /// <summary>
- /// Allows the specific user to be mentioned
- /// </summary>
- /// <param name="id"></param>
- public UserMention(ulong id) { this.Id = id; }
/// <summary>
- /// Allows the specific user to be mentioned
+ /// Allows a reply to ping the user being replied to.
/// </summary>
- /// <param name="user"></param>
- public UserMention(DiscordUser user) : this(user.Id) { }
-
- public static implicit operator UserMention(DiscordUser user) => new(user.Id);
-}
+ public readonly struct RepliedUserMention : IMention
+ {
+ //This is pointless because new RepliedUserMention() will work, but it is here for consistency with the other mentionables.
+ /// <summary>
+ /// Mention the user being replied to. Alias to <see cref="RepliedUserMention()"/> constructor.
+ /// </summary>
+ public static readonly RepliedUserMention All = new();
+ }
-/// <summary>
-/// Allows @role pings to mention in the message.
-/// </summary>
-public readonly struct RoleMention : IMention
-{
/// <summary>
- /// Allow the mentioning of all roles. Alias to <see cref="RoleMention()"/> constructor.
+ /// Allows @everyone and @here pings to mention in the message.
/// </summary>
- public static readonly RoleMention All = new();
+ public readonly struct EveryoneMention : IMention
+ {
+ //This is pointless because new EveryoneMention() will work, but it is here for consistency with the other mentionables.
+ /// <summary>
+ /// Allow the mentioning of @everyone and @here. Alias to <see cref="EveryoneMention()"/> constructor.
+ /// </summary>
+ public static readonly EveryoneMention All = new();
+ }
/// <summary>
- /// Optional Id of the role that is allowed to be mentioned. If null, then all role mentions will be allowed.
+ /// Allows @user pings to mention in the message.
/// </summary>
- public ulong? Id { get; }
+ public readonly struct UserMention : IMention
+ {
+ /// <summary>
+ /// Allow mentioning of all users. Alias to <see cref="UserMention()"/> constructor.
+ /// </summary>
+ public static readonly UserMention All = new();
+
+ /// <summary>
+ /// Optional Id of the user that is allowed to be mentioned. If null, then all user mentions will be allowed.
+ /// </summary>
+ public ulong? Id { get; }
+
+ /// <summary>
+ /// Allows the specific user to be mentioned
+ /// </summary>
+ /// <param name="id"></param>
+ public UserMention(ulong id) { this.Id = id; }
+
+ /// <summary>
+ /// Allows the specific user to be mentioned
+ /// </summary>
+ /// <param name="user"></param>
+ public UserMention(DiscordUser user) : this(user.Id) { }
+
+ public static implicit operator UserMention(DiscordUser user) => new(user.Id);
+ }
/// <summary>
- /// Allows the specific id to be mentioned
- /// </summary>
- /// <param name="id"></param>
- public RoleMention(ulong id) { this.Id = id; }
-
- /// <summary>
- /// Allows the specific role to be mentioned
- /// </summary>
- /// <param name="role"></param>
- public RoleMention(DiscordRole role) : this(role.Id) { }
-
- public static implicit operator RoleMention(DiscordRole role) => new(role.Id);
-}
-
-/// <summary>
-/// Contains static instances of common mention patterns.
-/// </summary>
-public static class Mentions
-{
- /// <summary>
- /// All possible mentions - @everyone + @here, users, and roles.
+ /// Allows @role pings to mention in the message.
/// </summary>
- public static IEnumerable<IMention> All { get; } = new IMention[] { EveryoneMention.All, UserMention.All, RoleMention.All };
+ public readonly struct RoleMention : IMention
+ {
+ /// <summary>
+ /// Allow the mentioning of all roles. Alias to <see cref="RoleMention()"/> constructor.
+ /// </summary>
+ public static readonly RoleMention All = new();
+
+ /// <summary>
+ /// Optional Id of the role that is allowed to be mentioned. If null, then all role mentions will be allowed.
+ /// </summary>
+ public ulong? Id { get; }
+
+ /// <summary>
+ /// Allows the specific id to be mentioned
+ /// </summary>
+ /// <param name="id"></param>
+ public RoleMention(ulong id) { this.Id = id; }
+
+ /// <summary>
+ /// Allows the specific role to be mentioned
+ /// </summary>
+ /// <param name="role"></param>
+ public RoleMention(DiscordRole role) : this(role.Id) { }
+
+ public static implicit operator RoleMention(DiscordRole role) => new(role.Id);
+ }
/// <summary>
- /// No mentions allowed.
+ /// Contains static instances of common mention patterns.
/// </summary>
- public static IEnumerable<IMention> None { get; } = Array.Empty<IMention>();
+ public static class Mentions
+ {
+ /// <summary>
+ /// All possible mentions - @everyone + @here, users, and roles.
+ /// </summary>
+ public static IEnumerable<IMention> All { get; } = new IMention[] { EveryoneMention.All, UserMention.All, RoleMention.All };
+
+ /// <summary>
+ /// No mentions allowed.
+ /// </summary>
+ public static IEnumerable<IMention> None { get; } = Array.Empty<IMention>();
+ }
}
diff --git a/DisCatSharp/Entities/Optional.cs b/DisCatSharp/Entities/Optional.cs
index 3ec352227..aac3afc6f 100644
--- a/DisCatSharp/Entities/Optional.cs
+++ b/DisCatSharp/Entities/Optional.cs
@@ -1,400 +1,401 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Helper methods for instantiating an <see cref="Optional{T}"/>.
-/// </summary>
-/// <remarks>
-/// This class only serves to provide <see cref="Some{T}"/> and <see cref="None"/>
-/// as utility that supports type inference.
-/// </remarks>
-public static class Optional
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Provided for easy creation of empty <see cref="Optional{T}"/>s.
- /// </summary>
- public static readonly None None = new();
-
- /// <summary>
- /// Creates a new <see cref="Optional{T}"/> with specified value and valid state.
- /// </summary>
- /// <param name="value">Value to populate the optional with.</param>
- /// <typeparam name="T">Type of the value.</typeparam>
- /// <returns>Created optional.</returns>
- public static Optional<T> Some<T>(T value)
- => value;
-
- /// <summary>
- ///
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="value"></param>
- /// <returns></returns>
- public static Optional<T> FromNullable<T>(T value)
- => value == null
- ? None
- : value;
-
- /// <summary>
- /// Creates a new <see cref="Optional{T}"/> with specified value and valid state.
- /// </summary>
- /// <param name="value">Value to populate the optional with.</param>
- /// <typeparam name="T">Type of the value.</typeparam>
- /// <returns>Created optional.</returns>
- [Obsolete("Renamed to Some.")]
- public static Optional<T> FromValue<T>(T value)
- => value;
-
- /// <summary>
- /// Creates a new empty <see cref="Optional{T}"/> with no value and invalid state.
- /// </summary>
- /// <typeparam name="T">The type that the created instance is wrapping around.</typeparam>
- /// <returns>Created optional.</returns>
- [Obsolete("Use None.")]
- public static Optional<T> FromNoValue<T>()
- => default;
-}
-
-/// <summary>
-/// Unit type for creating an empty <see cref="Optional{T}"/>s.
-/// </summary>
-public struct None
-{ }
-
-/// <summary>
-/// Used internally to make serialization more convenient, do NOT change this, do NOT implement this yourself.
-/// </summary>
-internal interface IOptional
-{
- /// <summary>
- /// Gets a whether it has a value.
- /// </summary>
- bool HasValue { get; }
-
- /// <summary>
- /// Gets the raw value.
+ /// Helper methods for instantiating an <see cref="Optional{T}"/>.
/// </summary>
/// <remarks>
- /// Must NOT throw InvalidOperationException.
+ /// This class only serves to provide <see cref="Some{T}"/> and <see cref="None"/>
+ /// as utility that supports type inference.
/// </remarks>
- object RawValue { get; }
-}
-
-/// <summary>
-/// Represents a wrapper which may or may not have a value.
-/// </summary>
-/// <typeparam name="T">Type of the value.</typeparam>
-[JsonConverter(typeof(OptionalJsonConverter))]
-public readonly struct Optional<T> : IEquatable<Optional<T>>, IEquatable<T>, IOptional
-{
- /// <summary>
- /// Static empty <see cref="Optional"/>.
- /// </summary>
- public static readonly Optional<T> None = default;
-
- /// <summary>
- /// Gets whether this <see cref="Optional{T}"/> has a value.
- /// </summary>
- public bool HasValue { get; }
-
- /// <summary>
- /// Gets the value of this <see cref="Optional{T}"/>.
- /// </summary>
- /// <exception cref="System.InvalidOperationException">If this <see cref="Optional{T}"/> has no value.</exception>
- public T Value => this.HasValue ? this._val : throw new InvalidOperationException("Value is not set.");
-
- /// <summary>
- /// Gets the raw value.
- /// </summary>
- object IOptional.RawValue => this._val;
-
- private readonly T _val;
-
- /// <summary>
- /// Creates a new <see cref="Optional{T}"/> with specified value.
- /// </summary>
- /// <param name="value">Value of this option.</param>
- [Obsolete("Use Optional.Some")]
- public Optional(T value)
+ public static class Optional
{
- this._val = value;
- this.HasValue = true;
+ /// <summary>
+ /// Provided for easy creation of empty <see cref="Optional{T}"/>s.
+ /// </summary>
+ public static readonly None None = new();
+
+ /// <summary>
+ /// Creates a new <see cref="Optional{T}"/> with specified value and valid state.
+ /// </summary>
+ /// <param name="value">Value to populate the optional with.</param>
+ /// <typeparam name="T">Type of the value.</typeparam>
+ /// <returns>Created optional.</returns>
+ public static Optional<T> Some<T>(T value)
+ => value;
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="value"></param>
+ /// <returns></returns>
+ public static Optional<T> FromNullable<T>(T value)
+ => value == null
+ ? None
+ : value;
+
+ /// <summary>
+ /// Creates a new <see cref="Optional{T}"/> with specified value and valid state.
+ /// </summary>
+ /// <param name="value">Value to populate the optional with.</param>
+ /// <typeparam name="T">Type of the value.</typeparam>
+ /// <returns>Created optional.</returns>
+ [Obsolete("Renamed to Some.")]
+ public static Optional<T> FromValue<T>(T value)
+ => value;
+
+ /// <summary>
+ /// Creates a new empty <see cref="Optional{T}"/> with no value and invalid state.
+ /// </summary>
+ /// <typeparam name="T">The type that the created instance is wrapping around.</typeparam>
+ /// <returns>Created optional.</returns>
+ [Obsolete("Use None.")]
+ public static Optional<T> FromNoValue<T>()
+ => default;
}
- [Obsolete("Renamed to Map")]
- public Optional<TOut> IfPresent<TOut>(Func<T, TOut> mapper)
- => this.Map(mapper);
-
- /// <summary>
- /// Performs a mapping operation on the current <see cref="Optional{T}"/>, turning it into an Optional holding a
- /// <typeparamref name="TOut"/> instance if the source optional contains a value; otherwise, returns an
- /// <see cref="Optional{T}"/> of that same type with no value.
- /// </summary>
- /// <param name="mapper">The mapping function to apply on the current value if it exists</param>
- /// <typeparam name="TOut">The type of the target value returned by <paramref name="mapper"/></typeparam>
- /// <returns>
- /// An <see cref="Optional{T}"/> containing a value denoted by calling <paramref name="mapper"/> if the current
- /// <see cref="Optional{T}"/> contains a value; otherwise, an empty <see cref="Optional{T}"/> of the target
- /// type.
- /// </returns>
- public Optional<TOut> Map<TOut>(Func<T, TOut> mapper)
- => this.HasValue
- ? mapper(this._val)
- : Optional.None;
-
- /// <summary>
- /// Maps to <see cref="None"/> for <see cref="None"/>, to <code>default</code> for <code>null</code> and to the mapped value otherwise./>
- /// </summary>
- /// <typeparam name="TOut">The type to map to.</typeparam>
- /// <param name="mapper">The function that does the mapping of the non-null <typeparamref name="T"/>.</param>
- /// <returns>The mapped value.</returns>
- public Optional<TOut> MapOrNull<TOut>(Func<T, TOut> mapper)
- => this.HasValue
- ? this._val == null
- ? default
- : mapper(this._val)
- : Optional.None;
-
/// <summary>
- /// Gets the value of the <see cref="Optional{T}"/> or a specified value, if the <see cref="Optional{T}"/> has no value.
+ /// Unit type for creating an empty <see cref="Optional{T}"/>s.
/// </summary>
- /// <param name="other">The value to return if this has no value.</param>
- /// <returns>Either the value of the <see cref="Optional{T}"/> if present or the provided value.</returns>
- public T ValueOr(T other)
- => this.HasValue
- ? this._val
- : other;
+ public struct None
+ { }
/// <summary>
- /// Gets the value of the <see cref="Optional{T}"/> or the default value for <typeparamref name="T"/>, if the
- /// <see cref="Optional{T}"/> has no value.
+ /// Used internally to make serialization more convenient, do NOT change this, do NOT implement this yourself.
/// </summary>
- /// <returns>Either the value of the <see cref="Optional{T}"/> if present or the type's default value.</returns>
- public T ValueOrDefault()
- => this.ValueOr(default);
-
- /// <summary>
- /// Gets the <see cref="Optional"/>'s value, or throws the provided exception if it's empty.
- /// </summary>
- /// <param name="err">The exception to throw if the optional is empty.</param>
- /// <returns>The value of the <see cref="Optional"/>, if present.</returns>
- public T Expect(Exception err)
- => !this.HasValue ? throw err : this._val;
-
- /// <summary>
- /// Gets the <see cref="Optional"/>'s value, or throws a standard exception with the provided string if it's
- /// empty.
- /// </summary>
- /// <param name="str">The string provided to the exception.</param>
- /// <returns>The value of the <see cref="Optional"/>, if present.</returns>
- public T Expect(string str) => this.Expect(new InvalidOperationException(str));
-
- /// <summary>
- /// Checks if this has a value and tests the predicate if it does.
- /// </summary>
- /// <param name="predicate">The predicate to test if this has a value.</param>
- /// <returns>True if this has a value and the predicate is fulfilled, false otherwise.</returns>
- public bool HasValueAnd(Predicate<T> predicate)
- => this.HasValue && predicate(this._val);
-
- /// <summary>
- /// Returns a string representation of this optional value.
- /// </summary>
- /// <returns>String representation of this optional value.</returns>
- public override string ToString() => $"Optional<{typeof(T)}> ({this.Map(x => x.ToString()).ValueOr("<no value>")})";
+ internal interface IOptional
+ {
+ /// <summary>
+ /// Gets a whether it has a value.
+ /// </summary>
+ bool HasValue { get; }
+
+ /// <summary>
+ /// Gets the raw value.
+ /// </summary>
+ /// <remarks>
+ /// Must NOT throw InvalidOperationException.
+ /// </remarks>
+ object RawValue { get; }
+ }
/// <summary>
- /// Checks whether this <see cref="Optional{T}"/> (or its value) are equal to another object.
+ /// Represents a wrapper which may or may not have a value.
/// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="Optional{T}"/> or its value.</returns>
- public override bool Equals(object obj) =>
- obj switch
+ /// <typeparam name="T">Type of the value.</typeparam>
+ [JsonConverter(typeof(OptionalJsonConverter))]
+ public readonly struct Optional<T> : IEquatable<Optional<T>>, IEquatable<T>, IOptional
+ {
+ /// <summary>
+ /// Static empty <see cref="Optional"/>.
+ /// </summary>
+ public static readonly Optional<T> None = default;
+
+ /// <summary>
+ /// Gets whether this <see cref="Optional{T}"/> has a value.
+ /// </summary>
+ public bool HasValue { get; }
+
+ /// <summary>
+ /// Gets the value of this <see cref="Optional{T}"/>.
+ /// </summary>
+ /// <exception cref="System.InvalidOperationException">If this <see cref="Optional{T}"/> has no value.</exception>
+ public T Value => this.HasValue ? this._val : throw new InvalidOperationException("Value is not set.");
+
+ /// <summary>
+ /// Gets the raw value.
+ /// </summary>
+ object IOptional.RawValue => this._val;
+
+ private readonly T _val;
+
+ /// <summary>
+ /// Creates a new <see cref="Optional{T}"/> with specified value.
+ /// </summary>
+ /// <param name="value">Value of this option.</param>
+ [Obsolete("Use Optional.Some")]
+ public Optional(T value)
{
- T t => this.Equals(t),
- Optional<T> opt => this.Equals(opt),
- _ => false,
- };
-
- /// <summary>
- /// Checks whether this <see cref="Optional{T}"/> is equal to another <see cref="Optional{T}"/>.
- /// </summary>
- /// <param name="e"><see cref="Optional{T}"/> to compare to.</param>
- /// <returns>Whether the <see cref="Optional{T}"/> is equal to this <see cref="Optional{T}"/>.</returns>
- public bool Equals(Optional<T> e) => (!this.HasValue && !e.HasValue) || (this.HasValue == e.HasValue && this.Value.Equals(e.Value));
-
- /// <summary>
- /// Checks whether the value of this <see cref="Optional{T}"/> is equal to specified object.
- /// </summary>
- /// <param name="e">Object to compare to.</param>
- /// <returns>Whether the object is equal to the value of this <see cref="Optional{T}"/>.</returns>
- public bool Equals(T e)
- => this.HasValue && ReferenceEquals(this.Value, e);
-
- /// <summary>
- /// Gets the hash code for this <see cref="Optional{T}"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="Optional{T}"/>.</returns>
- public override int GetHashCode()
- => this.Map(x => x.GetHashCode()).ValueOrDefault();
+ this._val = value;
+ this.HasValue = true;
+ }
- public static implicit operator Optional<T>(T val)
+ [Obsolete("Renamed to Map")]
+ public Optional<TOut> IfPresent<TOut>(Func<T, TOut> mapper)
+ => this.Map(mapper);
+
+ /// <summary>
+ /// Performs a mapping operation on the current <see cref="Optional{T}"/>, turning it into an Optional holding a
+ /// <typeparamref name="TOut"/> instance if the source optional contains a value; otherwise, returns an
+ /// <see cref="Optional{T}"/> of that same type with no value.
+ /// </summary>
+ /// <param name="mapper">The mapping function to apply on the current value if it exists</param>
+ /// <typeparam name="TOut">The type of the target value returned by <paramref name="mapper"/></typeparam>
+ /// <returns>
+ /// An <see cref="Optional{T}"/> containing a value denoted by calling <paramref name="mapper"/> if the current
+ /// <see cref="Optional{T}"/> contains a value; otherwise, an empty <see cref="Optional{T}"/> of the target
+ /// type.
+ /// </returns>
+ public Optional<TOut> Map<TOut>(Func<T, TOut> mapper)
+ => this.HasValue
+ ? mapper(this._val)
+ : Optional.None;
+
+ /// <summary>
+ /// Maps to <see cref="None"/> for <see cref="None"/>, to <code>default</code> for <code>null</code> and to the mapped value otherwise./>
+ /// </summary>
+ /// <typeparam name="TOut">The type to map to.</typeparam>
+ /// <param name="mapper">The function that does the mapping of the non-null <typeparamref name="T"/>.</param>
+ /// <returns>The mapped value.</returns>
+ public Optional<TOut> MapOrNull<TOut>(Func<T, TOut> mapper)
+ => this.HasValue
+ ? this._val == null
+ ? default
+ : mapper(this._val)
+ : Optional.None;
+
+ /// <summary>
+ /// Gets the value of the <see cref="Optional{T}"/> or a specified value, if the <see cref="Optional{T}"/> has no value.
+ /// </summary>
+ /// <param name="other">The value to return if this has no value.</param>
+ /// <returns>Either the value of the <see cref="Optional{T}"/> if present or the provided value.</returns>
+ public T ValueOr(T other)
+ => this.HasValue
+ ? this._val
+ : other;
+
+ /// <summary>
+ /// Gets the value of the <see cref="Optional{T}"/> or the default value for <typeparamref name="T"/>, if the
+ /// <see cref="Optional{T}"/> has no value.
+ /// </summary>
+ /// <returns>Either the value of the <see cref="Optional{T}"/> if present or the type's default value.</returns>
+ public T ValueOrDefault()
+ => this.ValueOr(default);
+
+ /// <summary>
+ /// Gets the <see cref="Optional"/>'s value, or throws the provided exception if it's empty.
+ /// </summary>
+ /// <param name="err">The exception to throw if the optional is empty.</param>
+ /// <returns>The value of the <see cref="Optional"/>, if present.</returns>
+ public T Expect(Exception err)
+ => !this.HasValue ? throw err : this._val;
+
+ /// <summary>
+ /// Gets the <see cref="Optional"/>'s value, or throws a standard exception with the provided string if it's
+ /// empty.
+ /// </summary>
+ /// <param name="str">The string provided to the exception.</param>
+ /// <returns>The value of the <see cref="Optional"/>, if present.</returns>
+ public T Expect(string str) => this.Expect(new InvalidOperationException(str));
+
+ /// <summary>
+ /// Checks if this has a value and tests the predicate if it does.
+ /// </summary>
+ /// <param name="predicate">The predicate to test if this has a value.</param>
+ /// <returns>True if this has a value and the predicate is fulfilled, false otherwise.</returns>
+ public bool HasValueAnd(Predicate<T> predicate)
+ => this.HasValue && predicate(this._val);
+
+ /// <summary>
+ /// Returns a string representation of this optional value.
+ /// </summary>
+ /// <returns>String representation of this optional value.</returns>
+ public override string ToString() => $"Optional<{typeof(T)}> ({this.Map(x => x.ToString()).ValueOr("<no value>")})";
+
+ /// <summary>
+ /// Checks whether this <see cref="Optional{T}"/> (or its value) are equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="Optional{T}"/> or its value.</returns>
+ public override bool Equals(object obj) =>
+ obj switch
+ {
+ T t => this.Equals(t),
+ Optional<T> opt => this.Equals(opt),
+ _ => false,
+ };
+
+ /// <summary>
+ /// Checks whether this <see cref="Optional{T}"/> is equal to another <see cref="Optional{T}"/>.
+ /// </summary>
+ /// <param name="e"><see cref="Optional{T}"/> to compare to.</param>
+ /// <returns>Whether the <see cref="Optional{T}"/> is equal to this <see cref="Optional{T}"/>.</returns>
+ public bool Equals(Optional<T> e) => (!this.HasValue && !e.HasValue) || (this.HasValue == e.HasValue && this.Value.Equals(e.Value));
+
+ /// <summary>
+ /// Checks whether the value of this <see cref="Optional{T}"/> is equal to specified object.
+ /// </summary>
+ /// <param name="e">Object to compare to.</param>
+ /// <returns>Whether the object is equal to the value of this <see cref="Optional{T}"/>.</returns>
+ public bool Equals(T e)
+ => this.HasValue && ReferenceEquals(this.Value, e);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="Optional{T}"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="Optional{T}"/>.</returns>
+ public override int GetHashCode()
+ => this.Map(x => x.GetHashCode()).ValueOrDefault();
+
+ public static implicit operator Optional<T>(T val)
#pragma warning disable 0618
- => new(val);
+ => new(val);
#pragma warning restore 0618
- public static explicit operator T(Optional<T> opt)
- => opt.Value;
+ public static explicit operator T(Optional<T> opt)
+ => opt.Value;
- /// <summary>
- /// Creates an empty optional.
- /// </summary>
- public static implicit operator Optional<T>(None _) => default;
+ /// <summary>
+ /// Creates an empty optional.
+ /// </summary>
+ public static implicit operator Optional<T>(None _) => default;
- public static bool operator ==(Optional<T> opt1, Optional<T> opt2)
- => opt1.Equals(opt2);
+ public static bool operator ==(Optional<T> opt1, Optional<T> opt2)
+ => opt1.Equals(opt2);
- public static bool operator !=(Optional<T> opt1, Optional<T> opt2)
- => !opt1.Equals(opt2);
+ public static bool operator !=(Optional<T> opt1, Optional<T> opt2)
+ => !opt1.Equals(opt2);
- public static bool operator ==(Optional<T> opt, T t)
- => opt.Equals(t);
+ public static bool operator ==(Optional<T> opt, T t)
+ => opt.Equals(t);
- public static bool operator !=(Optional<T> opt, T t)
- => !opt.Equals(t);
-}
+ public static bool operator !=(Optional<T> opt, T t)
+ => !opt.Equals(t);
+ }
-/// <summary>
-/// Represents an optional json contract resolver.
-/// <seealso cref="DiscordJson.s_serializer"/>
-/// </summary>
-internal sealed class OptionalJsonContractResolver : DefaultContractResolver
-{
/// <summary>
- /// Creates the property.
+ /// Represents an optional json contract resolver.
+ /// <seealso cref="DiscordJson.s_serializer"/>
/// </summary>
- /// <param name="member">The member.</param>
- /// <param name="memberSerialization">The member serialization.</param>
- protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
+ internal sealed class OptionalJsonContractResolver : DefaultContractResolver
{
- var property = base.CreateProperty(member, memberSerialization);
-
- var type = property.PropertyType;
-
- if (!type.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IOptional)))
- return property;
+ /// <summary>
+ /// Creates the property.
+ /// </summary>
+ /// <param name="member">The member.</param>
+ /// <param name="memberSerialization">The member serialization.</param>
+ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
+ {
+ var property = base.CreateProperty(member, memberSerialization);
- // we cache the PropertyInfo object here (it's captured in closure). we don't have direct
- // access to the property value so we have to reflect into it from the parent instance
- // we use UnderlyingName instead of PropertyName in case the C# name is different from the Json name.
- var declaringMember = property.DeclaringType.GetTypeInfo().DeclaredMembers
- .FirstOrDefault(e => e.Name == property.UnderlyingName);
+ var type = property.PropertyType;
- switch (declaringMember)
- {
- case PropertyInfo declaringProp:
- property.ShouldSerialize = instance => // instance here is the declaring (parent) type
- {
- var optionalValue = declaringProp.GetValue(instance);
- return (optionalValue as IOptional).HasValue;
- };
- return property;
- case FieldInfo declaringField:
- property.ShouldSerialize = instance => // instance here is the declaring (parent) type
- {
- var optionalValue = declaringField.GetValue(instance);
- return (optionalValue as IOptional).HasValue;
- };
+ if (!type.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IOptional)))
return property;
- default:
- throw new InvalidOperationException(
- "Can only serialize Optional<T> members that are fields or properties");
+
+ // we cache the PropertyInfo object here (it's captured in closure). we don't have direct
+ // access to the property value so we have to reflect into it from the parent instance
+ // we use UnderlyingName instead of PropertyName in case the C# name is different from the Json name.
+ var declaringMember = property.DeclaringType.GetTypeInfo().DeclaredMembers
+ .FirstOrDefault(e => e.Name == property.UnderlyingName);
+
+ switch (declaringMember)
+ {
+ case PropertyInfo declaringProp:
+ property.ShouldSerialize = instance => // instance here is the declaring (parent) type
+ {
+ var optionalValue = declaringProp.GetValue(instance);
+ return (optionalValue as IOptional).HasValue;
+ };
+ return property;
+ case FieldInfo declaringField:
+ property.ShouldSerialize = instance => // instance here is the declaring (parent) type
+ {
+ var optionalValue = declaringField.GetValue(instance);
+ return (optionalValue as IOptional).HasValue;
+ };
+ return property;
+ default:
+ throw new InvalidOperationException(
+ "Can only serialize Optional<T> members that are fields or properties");
+ }
}
}
-}
-/// <summary>
-/// Represents an optional json converter.
-/// </summary>
-internal sealed class OptionalJsonConverter : JsonConverter
-{
/// <summary>
- /// Writes the json.
+ /// Represents an optional json converter.
/// </summary>
- /// <param name="writer">The writer.</param>
- /// <param name="value">The value.</param>
- /// <param name="serializer">The serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ internal sealed class OptionalJsonConverter : JsonConverter
{
- // we don't check for HasValue here since it's checked in OptionalJsonContractResolver
- var val = (value as IOptional).RawValue;
- // JToken.FromObject will throw if `null` so we manually write a null value.
- if (val == null)
+ /// <summary>
+ /// Writes the json.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="value">The value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
- // you can read serializer.NullValueHandling here, but unfortunately you can **not** skip serialization
- // here, or else you will get a nasty JsonWriterException, so we just ignore its value and manually
- // write the null.
- writer.WriteToken(JsonToken.Null);
+ // we don't check for HasValue here since it's checked in OptionalJsonContractResolver
+ var val = (value as IOptional).RawValue;
+ // JToken.FromObject will throw if `null` so we manually write a null value.
+ if (val == null)
+ {
+ // you can read serializer.NullValueHandling here, but unfortunately you can **not** skip serialization
+ // here, or else you will get a nasty JsonWriterException, so we just ignore its value and manually
+ // write the null.
+ writer.WriteToken(JsonToken.Null);
+ }
+ else
+ {
+ // convert the value to a JSON object and write it to the property value.
+ JToken.FromObject(val).WriteTo(writer);
+ }
}
- else
+
+ /// <summary>
+ /// Reads the json.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="objectType">The object type.</param>
+ /// <param name="existingValue">The existing value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
+ JsonSerializer serializer)
{
- // convert the value to a JSON object and write it to the property value.
- JToken.FromObject(val).WriteTo(writer);
- }
- }
+ var genericType = objectType.GenericTypeArguments[0];
- /// <summary>
- /// Reads the json.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="objectType">The object type.</param>
- /// <param name="existingValue">The existing value.</param>
- /// <param name="serializer">The serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
- JsonSerializer serializer)
- {
- var genericType = objectType.GenericTypeArguments[0];
+ var constructor = objectType.GetTypeInfo().DeclaredConstructors
+ .FirstOrDefault(e => e.GetParameters()[0].ParameterType == genericType);
- var constructor = objectType.GetTypeInfo().DeclaredConstructors
- .FirstOrDefault(e => e.GetParameters()[0].ParameterType == genericType);
+ return constructor.Invoke(new[] { serializer.Deserialize(reader, genericType) });
+ }
- return constructor.Invoke(new[] { serializer.Deserialize(reader, genericType) });
+ /// <summary>
+ /// Whether it can convert.
+ /// </summary>
+ /// <param name="objectType">The object type.</param>
+ public override bool CanConvert(Type objectType) => objectType.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IOptional));
}
-
- /// <summary>
- /// Whether it can convert.
- /// </summary>
- /// <param name="objectType">The object type.</param>
- public override bool CanConvert(Type objectType) => objectType.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IOptional));
}
diff --git a/DisCatSharp/Entities/SnowflakeObject.cs b/DisCatSharp/Entities/SnowflakeObject.cs
index 44a1ab6d4..34ddd9f02 100644
--- a/DisCatSharp/Entities/SnowflakeObject.cs
+++ b/DisCatSharp/Entities/SnowflakeObject.cs
@@ -1,57 +1,58 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents an object in Discord API.
-/// </summary>
-public abstract class SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the ID of this object.
+ /// Represents an object in Discord API.
/// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong Id { get; internal set; }
+ public abstract class SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the ID of this object.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong Id { get; internal set; }
- /// <summary>
- /// Gets the date and time this object was created.
- /// </summary>
- [JsonIgnore]
- public DateTimeOffset CreationTimestamp
- => this.Id.GetSnowflakeTime();
+ /// <summary>
+ /// Gets the date and time this object was created.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset CreationTimestamp
+ => this.Id.GetSnowflakeTime();
- /// <summary>
- /// Gets the client instance this object is tied to.
- /// </summary>
- [JsonIgnore]
- internal BaseDiscordClient Discord { get; set; }
+ /// <summary>
+ /// Gets the client instance this object is tied to.
+ /// </summary>
+ [JsonIgnore]
+ internal BaseDiscordClient Discord { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="SnowflakeObject"/> class.
- /// </summary>
- internal SnowflakeObject() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SnowflakeObject"/> class.
+ /// </summary>
+ internal SnowflakeObject() { }
+ }
}
diff --git a/DisCatSharp/Entities/Stage/DiscordStageInstance.cs b/DisCatSharp/Entities/Stage/DiscordStageInstance.cs
index ba379c19b..5429211c4 100644
--- a/DisCatSharp/Entities/Stage/DiscordStageInstance.cs
+++ b/DisCatSharp/Entities/Stage/DiscordStageInstance.cs
@@ -1,115 +1,116 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Stage instance.
-/// </summary>
-public class DiscordStageInstance : SnowflakeObject, IEquatable<DiscordStageInstance>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the guild id of the associated Stage channel.
+ /// Represents a Stage instance.
/// </summary>
- [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong GuildId { get; internal set; }
+ public class DiscordStageInstance : SnowflakeObject, IEquatable<DiscordStageInstance>
+ {
+ /// <summary>
+ /// Gets the guild id of the associated Stage channel.
+ /// </summary>
+ [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong GuildId { get; internal set; }
- /// <summary>
- /// Gets the guild to which this channel belongs.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild
- => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) ? guild : null;
+ /// <summary>
+ /// Gets the guild to which this channel belongs.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild
+ => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) ? guild : null;
- /// <summary>
- /// Gets id of the associated Stage channel.
- /// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; internal set; }
+ /// <summary>
+ /// Gets id of the associated Stage channel.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; internal set; }
- /// <summary>
- /// Gets the topic of the Stage instance.
- /// </summary>
- [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
- public string Topic { get; internal set; }
+ /// <summary>
+ /// Gets the topic of the Stage instance.
+ /// </summary>
+ [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
+ public string Topic { get; internal set; }
- /// <summary>
- /// Gets the topic of the Stage instance.
- /// </summary>
- [JsonProperty("privacy_level", NullValueHandling = NullValueHandling.Ignore), Obsolete("Will be static due to the discovery removal.")]
- public StagePrivacyLevel PrivacyLevel { get; internal set; }
+ /// <summary>
+ /// Gets the topic of the Stage instance.
+ /// </summary>
+ [JsonProperty("privacy_level", NullValueHandling = NullValueHandling.Ignore), Obsolete("Will be static due to the discovery removal.")]
+ public StagePrivacyLevel PrivacyLevel { get; internal set; }
- /// <summary>
- /// Gets whether or not stage discovery is disabled.
- /// </summary>
- [JsonProperty("discoverable_disabled", NullValueHandling = NullValueHandling.Ignore), Obsolete("Will be removed due to the discovery removal.", true)]
- public bool DiscoverableDisabled { get; internal set; }
+ /// <summary>
+ /// Gets whether or not stage discovery is disabled.
+ /// </summary>
+ [JsonProperty("discoverable_disabled", NullValueHandling = NullValueHandling.Ignore), Obsolete("Will be removed due to the discovery removal.", true)]
+ public bool DiscoverableDisabled { get; internal set; }
- /// <summary>
- /// Checks whether this <see cref="DiscordStageInstance"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordStageInstance"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as DiscordStageInstance);
+ /// <summary>
+ /// Checks whether this <see cref="DiscordStageInstance"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordStageInstance"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as DiscordStageInstance);
- /// <summary>
- /// Checks whether this <see cref="DiscordStageInstance"/> is equal to another <see cref="DiscordStageInstance"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordStageInstance"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordStageInstance"/> is equal to this <see cref="DiscordStageInstance"/>.</returns>
- public bool Equals(DiscordStageInstance e)
- => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+ /// <summary>
+ /// Checks whether this <see cref="DiscordStageInstance"/> is equal to another <see cref="DiscordStageInstance"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordStageInstance"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordStageInstance"/> is equal to this <see cref="DiscordStageInstance"/>.</returns>
+ public bool Equals(DiscordStageInstance e)
+ => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordStageInstance"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordStageInstance"/>.</returns>
- public override int GetHashCode() => this.Id.GetHashCode();
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordStageInstance"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordStageInstance"/>.</returns>
+ public override int GetHashCode() => this.Id.GetHashCode();
- /// <summary>
- /// Gets whether the two <see cref="DiscordStageInstance"/> objects are equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are equal.</returns>
- public static bool operator ==(DiscordStageInstance e1, DiscordStageInstance e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordStageInstance"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are equal.</returns>
+ public static bool operator ==(DiscordStageInstance e1, DiscordStageInstance e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
- }
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
- /// <summary>
- /// Gets whether the two <see cref="DiscordStageInstance"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are not equal.</returns>
- public static bool operator !=(DiscordStageInstance e1, DiscordStageInstance e2)
- => !(e1 == e2);
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordStageInstance"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are not equal.</returns>
+ public static bool operator !=(DiscordStageInstance e1, DiscordStageInstance e2)
+ => !(e1 == e2);
+ }
}
diff --git a/DisCatSharp/Entities/Sticker/DiscordSticker.cs b/DisCatSharp/Entities/Sticker/DiscordSticker.cs
index 153c86efa..1f8b70ff3 100644
--- a/DisCatSharp/Entities/Sticker/DiscordSticker.cs
+++ b/DisCatSharp/Entities/Sticker/DiscordSticker.cs
@@ -1,217 +1,218 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord Sticker.
-/// </summary>
-public class DiscordSticker : SnowflakeObject, IEquatable<DiscordSticker>
-{
- /// <summary>
- /// Gets the Pack ID of this sticker.
- /// </summary>
- [JsonProperty("pack_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? PackId { get; internal set; }
-
- /// <summary>
- /// Gets the Name of the sticker.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the Description of the sticker.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; internal set; }
-
- /// <summary>
- /// Gets the type of sticker.
- /// </summary>
- [JsonProperty("type")]
- public StickerType Type { get; internal set; }
-
- /// <summary>
- /// For guild stickers, gets the user that made the sticker.
- /// </summary>
- [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Gets the guild associated with this sticker, if any.
- /// </summary>
- public DiscordGuild Guild => (this.Discord as DiscordClient).InternalGetCachedGuild(this.GuildId);
-
- /// <summary>
- /// Gets the guild id the sticker belongs too.
- /// </summary>
- [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? GuildId { get; internal set; }
-
- /// <summary>
- /// Gets whether this sticker is available. Only applicable to guild stickers.
- /// </summary>
- [JsonProperty("available", NullValueHandling = NullValueHandling.Ignore)]
- public bool Available { get; internal set; }
-
- /// <summary>
- /// Gets the sticker's sort order, if it's in a pack.
- /// </summary>
- [JsonProperty("sort_value", NullValueHandling = NullValueHandling.Ignore)]
- public int? SortValue { get; internal set; }
-
- /// <summary>
- /// Gets the list of tags for the sticker.
- /// </summary>
- [JsonIgnore]
- public IEnumerable<string> Tags
- => this.InternalTags != null ? this.InternalTags.Split(',') : Array.Empty<string>();
-
- /// <summary>
- /// Gets the asset hash of the sticker.
- /// </summary>
- [JsonProperty("asset", NullValueHandling = NullValueHandling.Ignore)]
- public string Asset { get; internal set; }
-
- /// <summary>
- /// Gets the preview asset hash of the sticker.
- /// </summary>
- [JsonProperty("preview_asset", NullValueHandling = NullValueHandling.Ignore)]
- public string PreviewAsset { get; internal set; }
-
- /// <summary>
- /// Gets the Format type of the sticker.
- /// </summary>
- [JsonProperty("format_type")]
- public StickerFormat FormatType { get; internal set; }
-
- /// <summary>
- /// Gets the tags of the sticker.
- /// </summary>
- [JsonProperty("tags", NullValueHandling = NullValueHandling.Ignore)]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
- internal string InternalTags { get; set; }
-
- /// <summary>
- /// Gets the url of the sticker.
- /// </summary>
- [JsonIgnore]
- public string Url => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.STICKERS}/{this.Id}.{(this.FormatType == StickerFormat.Lottie ? "json" : "png")}";
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordSticker"/> class.
- /// </summary>
- internal DiscordSticker()
- { }
-
- /// <summary>
- /// Whether to stickers are equal.
- /// </summary>
- /// <param name="other">DiscordSticker</param>
- public bool Equals(DiscordSticker other) => this.Id == other.Id;
-
- /// <summary>
- /// Gets the sticker in readable format.
- /// </summary>
- public override string ToString() => $"Sticker {this.Id}; {this.Name}; {this.FormatType}";
-
- /// <summary>
- /// Modifies the sticker
- /// </summary>
- /// <param name="name">The name of the sticker</param>
- /// <param name="description">The description of the sticker</param>
- /// <param name="tags">The name of a unicode emoji representing the sticker's expression</param>
- /// <param name="reason">Audit log reason</param>
- /// <returns>A sticker object</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
- public Task<DiscordSticker> ModifyAsync(Optional<string> name, Optional<string> description, Optional<string> tags, string reason = null) =>
- !this.GuildId.HasValue
- ? throw new ArgumentException("This sticker does not belong to a guild.")
- : name.HasValue && (name.Value.Length < 2 || name.Value.Length > 30)
- ? throw new ArgumentException("Sticker name needs to be between 2 and 30 characters long.")
- : description.HasValue && (description.Value.Length < 1 || description.Value.Length > 100)
- ? throw new ArgumentException("Sticker description needs to be between 1 and 100 characters long.")
- : tags.HasValue && !DiscordEmoji.TryFromUnicode(this.Discord, tags.Value, out var emoji)
- ? throw new ArgumentException("Sticker tags needs to be a unicode emoji.")
- : this.Discord.ApiClient.ModifyGuildStickerAsync(this.GuildId.Value, this.Id, name, description, tags, reason);
-
- /// <summary>
- /// Deletes the sticker
- /// </summary>
- /// <param name="reason">Audit log reason</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
- public Task DeleteAsync(string reason = null)
- => this.GuildId.HasValue ? this.Discord.ApiClient.DeleteGuildStickerAsync(this.GuildId.Value, this.Id, reason) : throw new ArgumentException("The requested sticker is no guild sticker.");
-}
-
-/// <summary>
-/// The sticker type
-/// </summary>
-public enum StickerType : long
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Standard nitro sticker
- /// </summary>
- Standard = 1,
-
- /// <summary>
- /// Custom guild sticker
- /// </summary>
- Guild = 2
-}
-
-/// <summary>
-/// The sticker type
-/// </summary>
-public enum StickerFormat : long
-{
- /// <summary>
- /// Sticker is a png
- /// </summary>
- Png = 1,
-
- /// <summary>
- /// Sticker is a animated png
- /// </summary>
- Apng = 2,
-
- /// <summary>
- /// Sticker is lottie
- /// </summary>
- Lottie = 3
+ /// Represents a Discord Sticker.
+ /// </summary>
+ public class DiscordSticker : SnowflakeObject, IEquatable<DiscordSticker>
+ {
+ /// <summary>
+ /// Gets the Pack ID of this sticker.
+ /// </summary>
+ [JsonProperty("pack_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? PackId { get; internal set; }
+
+ /// <summary>
+ /// Gets the Name of the sticker.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the Description of the sticker.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; internal set; }
+
+ /// <summary>
+ /// Gets the type of sticker.
+ /// </summary>
+ [JsonProperty("type")]
+ public StickerType Type { get; internal set; }
+
+ /// <summary>
+ /// For guild stickers, gets the user that made the sticker.
+ /// </summary>
+ [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild associated with this sticker, if any.
+ /// </summary>
+ public DiscordGuild Guild => (this.Discord as DiscordClient).InternalGetCachedGuild(this.GuildId);
+
+ /// <summary>
+ /// Gets the guild id the sticker belongs too.
+ /// </summary>
+ [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? GuildId { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this sticker is available. Only applicable to guild stickers.
+ /// </summary>
+ [JsonProperty("available", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Available { get; internal set; }
+
+ /// <summary>
+ /// Gets the sticker's sort order, if it's in a pack.
+ /// </summary>
+ [JsonProperty("sort_value", NullValueHandling = NullValueHandling.Ignore)]
+ public int? SortValue { get; internal set; }
+
+ /// <summary>
+ /// Gets the list of tags for the sticker.
+ /// </summary>
+ [JsonIgnore]
+ public IEnumerable<string> Tags
+ => this.InternalTags != null ? this.InternalTags.Split(',') : Array.Empty<string>();
+
+ /// <summary>
+ /// Gets the asset hash of the sticker.
+ /// </summary>
+ [JsonProperty("asset", NullValueHandling = NullValueHandling.Ignore)]
+ public string Asset { get; internal set; }
+
+ /// <summary>
+ /// Gets the preview asset hash of the sticker.
+ /// </summary>
+ [JsonProperty("preview_asset", NullValueHandling = NullValueHandling.Ignore)]
+ public string PreviewAsset { get; internal set; }
+
+ /// <summary>
+ /// Gets the Format type of the sticker.
+ /// </summary>
+ [JsonProperty("format_type")]
+ public StickerFormat FormatType { get; internal set; }
+
+ /// <summary>
+ /// Gets the tags of the sticker.
+ /// </summary>
+ [JsonProperty("tags", NullValueHandling = NullValueHandling.Ignore)]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
+ internal string InternalTags { get; set; }
+
+ /// <summary>
+ /// Gets the url of the sticker.
+ /// </summary>
+ [JsonIgnore]
+ public string Url => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.STICKERS}/{this.Id}.{(this.FormatType == StickerFormat.Lottie ? "json" : "png")}";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordSticker"/> class.
+ /// </summary>
+ internal DiscordSticker()
+ { }
+
+ /// <summary>
+ /// Whether to stickers are equal.
+ /// </summary>
+ /// <param name="other">DiscordSticker</param>
+ public bool Equals(DiscordSticker other) => this.Id == other.Id;
+
+ /// <summary>
+ /// Gets the sticker in readable format.
+ /// </summary>
+ public override string ToString() => $"Sticker {this.Id}; {this.Name}; {this.FormatType}";
+
+ /// <summary>
+ /// Modifies the sticker
+ /// </summary>
+ /// <param name="name">The name of the sticker</param>
+ /// <param name="description">The description of the sticker</param>
+ /// <param name="tags">The name of a unicode emoji representing the sticker's expression</param>
+ /// <param name="reason">Audit log reason</param>
+ /// <returns>A sticker object</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
+ public Task<DiscordSticker> ModifyAsync(Optional<string> name, Optional<string> description, Optional<string> tags, string reason = null) =>
+ !this.GuildId.HasValue
+ ? throw new ArgumentException("This sticker does not belong to a guild.")
+ : name.HasValue && (name.Value.Length < 2 || name.Value.Length > 30)
+ ? throw new ArgumentException("Sticker name needs to be between 2 and 30 characters long.")
+ : description.HasValue && (description.Value.Length < 1 || description.Value.Length > 100)
+ ? throw new ArgumentException("Sticker description needs to be between 1 and 100 characters long.")
+ : tags.HasValue && !DiscordEmoji.TryFromUnicode(this.Discord, tags.Value, out var emoji)
+ ? throw new ArgumentException("Sticker tags needs to be a unicode emoji.")
+ : this.Discord.ApiClient.ModifyGuildStickerAsync(this.GuildId.Value, this.Id, name, description, tags, reason);
+
+ /// <summary>
+ /// Deletes the sticker
+ /// </summary>
+ /// <param name="reason">Audit log reason</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the sticker could not be found.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageEmojisAndStickers"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.ArgumentException">Sticker does not belong to a guild.</exception>
+ public Task DeleteAsync(string reason = null)
+ => this.GuildId.HasValue ? this.Discord.ApiClient.DeleteGuildStickerAsync(this.GuildId.Value, this.Id, reason) : throw new ArgumentException("The requested sticker is no guild sticker.");
+ }
+
+ /// <summary>
+ /// The sticker type
+ /// </summary>
+ public enum StickerType : long
+ {
+ /// <summary>
+ /// Standard nitro sticker
+ /// </summary>
+ Standard = 1,
+
+ /// <summary>
+ /// Custom guild sticker
+ /// </summary>
+ Guild = 2
+ }
+
+ /// <summary>
+ /// The sticker type
+ /// </summary>
+ public enum StickerFormat : long
+ {
+ /// <summary>
+ /// Sticker is a png
+ /// </summary>
+ Png = 1,
+
+ /// <summary>
+ /// Sticker is a animated png
+ /// </summary>
+ Apng = 2,
+
+ /// <summary>
+ /// Sticker is lottie
+ /// </summary>
+ Lottie = 3
+ }
}
diff --git a/DisCatSharp/Entities/Sticker/DiscordStickerPack.cs b/DisCatSharp/Entities/Sticker/DiscordStickerPack.cs
index b816a0db6..f5fb2962b 100644
--- a/DisCatSharp/Entities/Sticker/DiscordStickerPack.cs
+++ b/DisCatSharp/Entities/Sticker/DiscordStickerPack.cs
@@ -1,86 +1,87 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord sticker pack.
-/// </summary>
-public sealed class DiscordStickerPack : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the stickers contained in this pack.
+ /// Represents a Discord sticker pack.
/// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordSticker> Stickers => this.StickersInternal;
+ public sealed class DiscordStickerPack : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the stickers contained in this pack.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordSticker> Stickers => this.StickersInternal;
- [JsonProperty("stickers")]
- internal List<DiscordSticker> StickersInternal = new();
+ [JsonProperty("stickers")]
+ internal List<DiscordSticker> StickersInternal = new();
- /// <summary>
- /// Gets the name of this sticker pack.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
+ /// <summary>
+ /// Gets the name of this sticker pack.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the sku id.
- /// </summary>
- [JsonProperty("sku_id")]
- public ulong SkuId { get; internal set; }
+ /// <summary>
+ /// Gets the sku id.
+ /// </summary>
+ [JsonProperty("sku_id")]
+ public ulong SkuId { get; internal set; }
- /// <summary>
- /// Gets the Id of this pack's cover sticker.
- /// </summary>
- [JsonProperty("cover_sticker_id")]
- public ulong CoverStickerId { get; internal set; }
+ /// <summary>
+ /// Gets the Id of this pack's cover sticker.
+ /// </summary>
+ [JsonProperty("cover_sticker_id")]
+ public ulong CoverStickerId { get; internal set; }
- /// <summary>
- /// Gets the pack's cover sticker.
- /// </summary>
- public Task<DiscordSticker> CoverSticker => this.Discord.ApiClient.GetStickerAsync(this.CoverStickerId);
+ /// <summary>
+ /// Gets the pack's cover sticker.
+ /// </summary>
+ public Task<DiscordSticker> CoverSticker => this.Discord.ApiClient.GetStickerAsync(this.CoverStickerId);
- /// <summary>
- /// Gets the Id of this pack's banner.
- /// </summary>
- [JsonProperty("banner_asset_id")]
- public ulong BannerAssetId { get; internal set; }
+ /// <summary>
+ /// Gets the Id of this pack's banner.
+ /// </summary>
+ [JsonProperty("banner_asset_id")]
+ public ulong BannerAssetId { get; internal set; }
- /// <summary>
- /// Gets the pack's banner url.
- /// </summary>
- public string BannerUrl => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.APP_ASSETS}{Endpoints.STICKER_APPLICATION}{Endpoints.STORE}/{this.BannerAssetId}.png?size=4096";
+ /// <summary>
+ /// Gets the pack's banner url.
+ /// </summary>
+ public string BannerUrl => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.APP_ASSETS}{Endpoints.STICKER_APPLICATION}{Endpoints.STORE}/{this.BannerAssetId}.png?size=4096";
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordStickerPack"/> class.
- /// </summary>
- internal DiscordStickerPack()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordStickerPack"/> class.
+ /// </summary>
+ internal DiscordStickerPack()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Thread/DiscordThreadChannel.cs b/DisCatSharp/Entities/Thread/DiscordThreadChannel.cs
index 824b9b811..806f956bc 100644
--- a/DisCatSharp/Entities/Thread/DiscordThreadChannel.cs
+++ b/DisCatSharp/Entities/Thread/DiscordThreadChannel.cs
@@ -1,644 +1,645 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Net.Models;
using DisCatSharp.Net.Serialization;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord thread channel.
-/// </summary>
-public class DiscordThreadChannel : DiscordChannel, IEquatable<DiscordThreadChannel>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets ID of the owner that started this thread.
- /// </summary>
- [JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong OwnerId { get; internal set; }
-
- /// <summary>
- /// Gets the name of this thread.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public new string Name { get; internal set; }
-
- /// <summary>
- /// Gets the type of this thread.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public new ChannelType Type { get; internal set; }
-
- /// <summary>
- /// Gets whether this thread is private.
- /// </summary>
- [JsonIgnore]
- public new bool IsPrivate
- => this.Type == ChannelType.PrivateThread;
-
- /// <summary>
- /// Gets the ID of the last message sent in this thread.
- /// </summary>
- [JsonProperty("last_message_id", NullValueHandling = NullValueHandling.Ignore)]
- public new ulong? LastMessageId { get; internal set; }
-
- /// <summary>
- /// <para>Gets the slowmode delay configured for this thread.</para>
- /// <para>All bots, as well as users with <see cref="Permissions.ManageChannels"/> or <see cref="Permissions.ManageMessages"/> permissions in the channel are exempt from slowmode.</para>
- /// </summary>
- [JsonProperty("rate_limit_per_user", NullValueHandling = NullValueHandling.Ignore)]
- public new int? PerUserRateLimit { get; internal set; }
-
- /// <summary>
- /// Gets an approximate count of messages in a thread, stops counting at 50.
- /// </summary>
- [JsonProperty("message_count", NullValueHandling = NullValueHandling.Ignore)]
- public int? MessageCount { get; internal set; }
-
- /// <summary>
- /// Gets an approximate count of users in a thread, stops counting at 50.
+ /// Represents a discord thread channel.
/// </summary>
- [JsonProperty("member_count", NullValueHandling = NullValueHandling.Ignore)]
- public int? MemberCount { get; internal set; }
-
- /// <summary>
- /// Represents the current member for this thread. This will have a value if the user has joined the thread.
- /// </summary>
- [JsonProperty("member", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordThreadChannelMember CurrentMember { get; internal set; }
-
-
- /// <summary>
- /// Gets when the last pinned message was pinned in this thread.
- /// </summary>
- [JsonIgnore]
- public new DateTimeOffset? LastPinTimestamp
- => !string.IsNullOrWhiteSpace(this.LastPinTimestampRaw) && DateTimeOffset.TryParse(this.LastPinTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
- dto : null;
-
- /// <summary>
- /// Gets when the last pinned message was pinned in this thread as raw string.
- /// </summary>
- [JsonProperty("last_pin_timestamp", NullValueHandling = NullValueHandling.Ignore)]
- internal new string LastPinTimestampRaw { get; set; }
-
- /// <summary>
- /// Gets the threads metadata.
- /// </summary>
- [JsonProperty("thread_metadata", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordThreadChannelMetadata ThreadMetadata { get; internal set; }
-
- /// <summary>
- /// Gets the thread members object.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyDictionary<ulong, DiscordThreadChannelMember> ThreadMembers => new ReadOnlyConcurrentDictionary<ulong, DiscordThreadChannelMember>(this.ThreadMembersInternal);
-
- [JsonProperty("thread_member", NullValueHandling = NullValueHandling.Ignore)]
- [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
- internal ConcurrentDictionary<ulong, DiscordThreadChannelMember> ThreadMembersInternal;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordThreadChannel"/> class.
- /// </summary>
- internal DiscordThreadChannel()
- { }
-
- #region Methods
-
- /// <summary>
- /// Deletes a thread.
- /// </summary>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageThreads"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task DeleteAsync(string reason = null)
- => this.Discord.ApiClient.DeleteThreadAsync(this.Id, reason);
-
- /// <summary>
- /// Modifies the current thread.
- /// </summary>
- /// <param name="action">Action to perform on this thread</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageThreads"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- /// <exception cref="System.NotSupportedException">Thrown when the <see cref="ThreadAutoArchiveDuration"/> cannot be modified. This happens, when the guild hasn't reached a certain boost <see cref="PremiumTier"/>.</exception>
- public Task ModifyAsync(Action<ThreadEditModel> action)
+ public class DiscordThreadChannel : DiscordChannel, IEquatable<DiscordThreadChannel>
{
- var mdl = new ThreadEditModel();
- action(mdl);
-
- var canContinue = !mdl.AutoArchiveDuration.HasValue || !mdl.AutoArchiveDuration.Value.HasValue || Utilities.CheckThreadAutoArchiveDurationFeature(this.Guild, mdl.AutoArchiveDuration.Value.Value);
- if (mdl.Invitable.HasValue)
+ /// <summary>
+ /// Gets ID of the owner that started this thread.
+ /// </summary>
+ [JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong OwnerId { get; internal set; }
+
+ /// <summary>
+ /// Gets the name of this thread.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public new string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the type of this thread.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public new ChannelType Type { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this thread is private.
+ /// </summary>
+ [JsonIgnore]
+ public new bool IsPrivate
+ => this.Type == ChannelType.PrivateThread;
+
+ /// <summary>
+ /// Gets the ID of the last message sent in this thread.
+ /// </summary>
+ [JsonProperty("last_message_id", NullValueHandling = NullValueHandling.Ignore)]
+ public new ulong? LastMessageId { get; internal set; }
+
+ /// <summary>
+ /// <para>Gets the slowmode delay configured for this thread.</para>
+ /// <para>All bots, as well as users with <see cref="Permissions.ManageChannels"/> or <see cref="Permissions.ManageMessages"/> permissions in the channel are exempt from slowmode.</para>
+ /// </summary>
+ [JsonProperty("rate_limit_per_user", NullValueHandling = NullValueHandling.Ignore)]
+ public new int? PerUserRateLimit { get; internal set; }
+
+ /// <summary>
+ /// Gets an approximate count of messages in a thread, stops counting at 50.
+ /// </summary>
+ [JsonProperty("message_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int? MessageCount { get; internal set; }
+
+ /// <summary>
+ /// Gets an approximate count of users in a thread, stops counting at 50.
+ /// </summary>
+ [JsonProperty("member_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int? MemberCount { get; internal set; }
+
+ /// <summary>
+ /// Represents the current member for this thread. This will have a value if the user has joined the thread.
+ /// </summary>
+ [JsonProperty("member", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordThreadChannelMember CurrentMember { get; internal set; }
+
+
+ /// <summary>
+ /// Gets when the last pinned message was pinned in this thread.
+ /// </summary>
+ [JsonIgnore]
+ public new DateTimeOffset? LastPinTimestamp
+ => !string.IsNullOrWhiteSpace(this.LastPinTimestampRaw) && DateTimeOffset.TryParse(this.LastPinTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
+ dto : null;
+
+ /// <summary>
+ /// Gets when the last pinned message was pinned in this thread as raw string.
+ /// </summary>
+ [JsonProperty("last_pin_timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ internal new string LastPinTimestampRaw { get; set; }
+
+ /// <summary>
+ /// Gets the threads metadata.
+ /// </summary>
+ [JsonProperty("thread_metadata", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordThreadChannelMetadata ThreadMetadata { get; internal set; }
+
+ /// <summary>
+ /// Gets the thread members object.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyDictionary<ulong, DiscordThreadChannelMember> ThreadMembers => new ReadOnlyConcurrentDictionary<ulong, DiscordThreadChannelMember>(this.ThreadMembersInternal);
+
+ [JsonProperty("thread_member", NullValueHandling = NullValueHandling.Ignore)]
+ [JsonConverter(typeof(SnowflakeArrayAsDictionaryJsonConverter))]
+ internal ConcurrentDictionary<ulong, DiscordThreadChannelMember> ThreadMembersInternal;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordThreadChannel"/> class.
+ /// </summary>
+ internal DiscordThreadChannel()
+ { }
+
+ #region Methods
+
+ /// <summary>
+ /// Deletes a thread.
+ /// </summary>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageThreads"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task DeleteAsync(string reason = null)
+ => this.Discord.ApiClient.DeleteThreadAsync(this.Id, reason);
+
+ /// <summary>
+ /// Modifies the current thread.
+ /// </summary>
+ /// <param name="action">Action to perform on this thread</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageThreads"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ /// <exception cref="System.NotSupportedException">Thrown when the <see cref="ThreadAutoArchiveDuration"/> cannot be modified. This happens, when the guild hasn't reached a certain boost <see cref="PremiumTier"/>.</exception>
+ public Task ModifyAsync(Action<ThreadEditModel> action)
{
- canContinue = this.Guild.Features.CanCreatePrivateThreads;
- }
- return canContinue ? this.Discord.ApiClient.ModifyThreadAsync(this.Id, mdl.Name, mdl.Locked, mdl.Archived, mdl.PerUserRateLimit, mdl.AutoArchiveDuration, mdl.Invitable, mdl.AuditLogReason) : throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(mdl.AutoArchiveDuration.Value.Value == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
- }
-
- /// <summary>
- /// Archives a thread.
- /// </summary>
- /// <param name="locked">Whether the thread should be locked.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageThreads"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ArchiveAsync(bool locked = true, string reason = null)
- => this.Discord.ApiClient.ModifyThreadAsync(this.Id, null, locked, true, null, null, null, reason: reason);
-
- /// <summary>
- /// Unarchives a thread.
- /// </summary>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task UnarchiveAsync(string reason = null)
- => this.Discord.ApiClient.ModifyThreadAsync(this.Id, null, null, false, null, null, null, reason: reason);
-
- /// <summary>
- /// Gets the members of a thread. Needs the <see cref="DiscordIntents.GuildMembers"/> intent.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<IReadOnlyList<DiscordThreadChannelMember>> GetMembersAsync()
- => await this.Discord.ApiClient.GetThreadMembersAsync(this.Id);
-
- /// <summary>
- /// Adds a member to this thread.
- /// </summary>
- /// <param name="memberId">The member id to be added.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task AddMemberAsync(ulong memberId)
- => this.Discord.ApiClient.AddThreadMemberAsync(this.Id, memberId);
-
- /// <summary>
- /// Adds a member to this thread.
- /// </summary>
- /// <param name="member">The member to be added.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task AddMemberAsync(DiscordMember member)
- => this.AddMemberAsync(member.Id);
-
- /// <summary>
- /// Gets a member in this thread.
- /// </summary>
- /// <param name="memberId">The member to be added.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member is not part of the thread.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordThreadChannelMember> GetMemberAsync(ulong memberId)
- => this.Discord.ApiClient.GetThreadMemberAsync(this.Id, memberId);
+ var mdl = new ThreadEditModel();
+ action(mdl);
- /// <summary>
- /// Gets a member in this thread.
- /// </summary>
- /// <param name="member">The member to be added.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member is not part of the thread.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordThreadChannelMember> GetMemberAsync(DiscordMember member)
- => this.Discord.ApiClient.GetThreadMemberAsync(this.Id, member.Id);
-
- /// <summary>
- /// Removes a member from this thread.
- /// </summary>
- /// <param name="memberId">The member id to be removed.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task RemoveMemberAsync(ulong memberId)
- => this.Discord.ApiClient.RemoveThreadMemberAsync(this.Id, memberId);
-
- /// <summary>
- /// Removes a member from this thread. Only applicable to private threads.
- /// </summary>
- /// <param name="member">The member to be removed.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task RemoveMemberAsync(DiscordMember member)
- => this.RemoveMemberAsync(member.Id);
+ var canContinue = !mdl.AutoArchiveDuration.HasValue || !mdl.AutoArchiveDuration.Value.HasValue || Utilities.CheckThreadAutoArchiveDurationFeature(this.Guild, mdl.AutoArchiveDuration.Value.Value);
+ if (mdl.Invitable.HasValue)
+ {
+ canContinue = this.Guild.Features.CanCreatePrivateThreads;
+ }
+ return canContinue ? this.Discord.ApiClient.ModifyThreadAsync(this.Id, mdl.Name, mdl.Locked, mdl.Archived, mdl.PerUserRateLimit, mdl.AutoArchiveDuration, mdl.Invitable, mdl.AuditLogReason) : throw new NotSupportedException($"Cannot modify ThreadAutoArchiveDuration. Guild needs boost tier {(mdl.AutoArchiveDuration.Value.Value == ThreadAutoArchiveDuration.ThreeDays ? "one" : "two")}.");
+ }
- /// <summary>
- /// Adds a role to this thread. Only applicable to private threads.
- /// </summary>
- /// <param name="roleId">The role id to be added.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task AddRoleAsync(ulong roleId)
- {
- var role = this.Guild.GetRole(roleId);
- var members = await this.Guild.GetAllMembersAsync();
- var roleMembers = members.Where(m => m.Roles.Contains(role));
- foreach (var member in roleMembers)
+ /// <summary>
+ /// Archives a thread.
+ /// </summary>
+ /// <param name="locked">Whether the thread should be locked.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageThreads"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ArchiveAsync(bool locked = true, string reason = null)
+ => this.Discord.ApiClient.ModifyThreadAsync(this.Id, null, locked, true, null, null, null, reason: reason);
+
+ /// <summary>
+ /// Unarchives a thread.
+ /// </summary>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task UnarchiveAsync(string reason = null)
+ => this.Discord.ApiClient.ModifyThreadAsync(this.Id, null, null, false, null, null, null, reason: reason);
+
+ /// <summary>
+ /// Gets the members of a thread. Needs the <see cref="DiscordIntents.GuildMembers"/> intent.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<IReadOnlyList<DiscordThreadChannelMember>> GetMembersAsync()
+ => await this.Discord.ApiClient.GetThreadMembersAsync(this.Id);
+
+ /// <summary>
+ /// Adds a member to this thread.
+ /// </summary>
+ /// <param name="memberId">The member id to be added.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task AddMemberAsync(ulong memberId)
+ => this.Discord.ApiClient.AddThreadMemberAsync(this.Id, memberId);
+
+ /// <summary>
+ /// Adds a member to this thread.
+ /// </summary>
+ /// <param name="member">The member to be added.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task AddMemberAsync(DiscordMember member)
+ => this.AddMemberAsync(member.Id);
+
+ /// <summary>
+ /// Gets a member in this thread.
+ /// </summary>
+ /// <param name="memberId">The member to be added.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member is not part of the thread.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordThreadChannelMember> GetMemberAsync(ulong memberId)
+ => this.Discord.ApiClient.GetThreadMemberAsync(this.Id, memberId);
+
+ /// <summary>
+ /// Gets a member in this thread.
+ /// </summary>
+ /// <param name="member">The member to be added.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the member is not part of the thread.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordThreadChannelMember> GetMemberAsync(DiscordMember member)
+ => this.Discord.ApiClient.GetThreadMemberAsync(this.Id, member.Id);
+
+ /// <summary>
+ /// Removes a member from this thread.
+ /// </summary>
+ /// <param name="memberId">The member id to be removed.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task RemoveMemberAsync(ulong memberId)
+ => this.Discord.ApiClient.RemoveThreadMemberAsync(this.Id, memberId);
+
+ /// <summary>
+ /// Removes a member from this thread. Only applicable to private threads.
+ /// </summary>
+ /// <param name="member">The member to be removed.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task RemoveMemberAsync(DiscordMember member)
+ => this.RemoveMemberAsync(member.Id);
+
+ /// <summary>
+ /// Adds a role to this thread. Only applicable to private threads.
+ /// </summary>
+ /// <param name="roleId">The role id to be added.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task AddRoleAsync(ulong roleId)
{
- await this.Discord.ApiClient.AddThreadMemberAsync(this.Id, member.Id);
+ var role = this.Guild.GetRole(roleId);
+ var members = await this.Guild.GetAllMembersAsync();
+ var roleMembers = members.Where(m => m.Roles.Contains(role));
+ foreach (var member in roleMembers)
+ {
+ await this.Discord.ApiClient.AddThreadMemberAsync(this.Id, member.Id);
+ }
}
- }
- /// <summary>
- /// Adds a role to this thread. Only applicable to private threads.
- /// </summary>
- /// <param name="role">The role to be added.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task AddRoleAsync(DiscordRole role)
- => this.AddRoleAsync(role.Id);
-
- /// <summary>
- /// Removes a role from this thread. Only applicable to private threads.
- /// </summary>
- /// <param name="roleId">The role id to be removed.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task RemoveRoleAsync(ulong roleId)
- {
- var role = this.Guild.GetRole(roleId);
- var members = await this.Guild.GetAllMembersAsync();
- var roleMembers = members.Where(m => m.Roles.Contains(role));
- foreach (var member in roleMembers)
+ /// <summary>
+ /// Adds a role to this thread. Only applicable to private threads.
+ /// </summary>
+ /// <param name="role">The role to be added.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task AddRoleAsync(DiscordRole role)
+ => this.AddRoleAsync(role.Id);
+
+ /// <summary>
+ /// Removes a role from this thread. Only applicable to private threads.
+ /// </summary>
+ /// <param name="roleId">The role id to be removed.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task RemoveRoleAsync(ulong roleId)
{
- await this.Discord.ApiClient.RemoveThreadMemberAsync(this.Id, member.Id);
+ var role = this.Guild.GetRole(roleId);
+ var members = await this.Guild.GetAllMembersAsync();
+ var roleMembers = members.Where(m => m.Roles.Contains(role));
+ foreach (var member in roleMembers)
+ {
+ await this.Discord.ApiClient.RemoveThreadMemberAsync(this.Id, member.Id);
+ }
}
- }
-
- /// <summary>
- /// Removes a role to from thread. Only applicable to private threads.
- /// </summary>
- /// <param name="role">The role to be removed.</param>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task RemoveRoleAsync(DiscordRole role)
- => this.RemoveRoleAsync(role.Id);
-
- /// <summary>
- /// Joins a thread.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client has no access to this thread.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task JoinAsync()
- => this.Discord.ApiClient.JoinThreadAsync(this.Id);
-
- /// <summary>
- /// Leaves a thread.
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client has no access to this thread.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task LeaveAsync()
- => this.Discord.ApiClient.LeaveThreadAsync(this.Id);
-
- /// <summary>
- /// Sends a message to this thread.
- /// </summary>
- /// <param name="content">Content of the message to send.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<DiscordMessage> SendMessageAsync(string content) =>
- !this.IsWritable()
- ? throw new ArgumentException("Cannot send a text message to a non-thread channel.")
- : this.Discord.ApiClient.CreateMessageAsync(this.Id, content, null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
-
- /// <summary>
- /// Sends a message to this thread.
- /// </summary>
- /// <param name="embed">Embed to attach to the message.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<DiscordMessage> SendMessageAsync(DiscordEmbed embed) =>
- !this.IsWritable()
- ? throw new ArgumentException("Cannot send a text message to a non-thread channel.")
- : this.Discord.ApiClient.CreateMessageAsync(this.Id, null, new[] { embed }, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
-
- /// <summary>
- /// Sends a message to this thread.
- /// </summary>
- /// <param name="content">Content of the message to send.</param>
- /// <param name="embed">Embed to attach to the message.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<DiscordMessage> SendMessageAsync(string content, DiscordEmbed embed) =>
- !this.IsWritable()
- ? throw new ArgumentException("Cannot send a text message to a non-thread channel.")
- : this.Discord.ApiClient.CreateMessageAsync(this.Id, content, new[] { embed }, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
-
- /// <summary>
- /// Sends a message to this thread.
- /// </summary>
- /// <param name="builder">The builder with all the items to thread.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<DiscordMessage> SendMessageAsync(DiscordMessageBuilder builder)
- => this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
-
- /// <summary>
- /// Sends a message to this channel.
- /// </summary>
- /// <param name="action">The builder with all the items to send.</param>
- /// <returns>The sent message.</returns>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<DiscordMessage> SendMessageAsync(Action<DiscordMessageBuilder> action)
- {
- var builder = new DiscordMessageBuilder();
- action(builder);
-
- return !this.IsWritable()
- ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
- : this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
- }
-
- /// <summary>
- /// Returns a specific message
- /// </summary>
- /// <param name="id">The id of the message</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new async Task<DiscordMessage> GetMessageAsync(ulong id) =>
- this.Discord.Configuration.MessageCacheSize > 0
- && this.Discord is DiscordClient dc
- && dc.MessageCache != null
- && dc.MessageCache.TryGet(xm => xm.Id == id && xm.ChannelId == this.Id, out var msg)
- ? msg
- : await this.Discord.ApiClient.GetMessageAsync(this.Id, id).ConfigureAwait(false);
- /// <summary>
- /// Returns a list of messages before a certain message.
- /// <param name="limit">The amount of messages to fetch.</param>
- /// <param name="before">Message to fetch before from.</param>
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<IReadOnlyList<DiscordMessage>> GetMessagesBeforeAsync(ulong before, int limit = 100)
- => this.GetMessagesInternalAsync(limit, before, null, null);
-
- /// <summary>
- /// Returns a list of messages after a certain message.
- /// <param name="limit">The amount of messages to fetch.</param>
- /// <param name="after">Message to fetch after from.</param>
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<IReadOnlyList<DiscordMessage>> GetMessagesAfterAsync(ulong after, int limit = 100)
- => this.GetMessagesInternalAsync(limit, null, after, null);
-
- /// <summary>
- /// Returns a list of messages around a certain message.
- /// <param name="limit">The amount of messages to fetch.</param>
- /// <param name="around">Message to fetch around from.</param>
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<IReadOnlyList<DiscordMessage>> GetMessagesAroundAsync(ulong around, int limit = 100)
- => this.GetMessagesInternalAsync(limit, null, null, around);
-
- /// <summary>
- /// Returns a list of messages from the last message in the thread.
- /// <param name="limit">The amount of messages to fetch.</param>
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<IReadOnlyList<DiscordMessage>> GetMessagesAsync(int limit = 100) =>
- this.GetMessagesInternalAsync(limit, null, null, null);
-
- /// <summary>
- /// Returns a list of messages
- /// </summary>
- /// <param name="limit">How many messages should be returned.</param>
- /// <param name="before">Get messages before snowflake.</param>
- /// <param name="after">Get messages after snowflake.</param>
- /// <param name="around">Get messages around snowflake.</param>
- private async Task<IReadOnlyList<DiscordMessage>> GetMessagesInternalAsync(int limit = 100, ulong? before = null, ulong? after = null, ulong? around = null)
- {
- if (this.Type != ChannelType.PublicThread && this.Type != ChannelType.PrivateThread && this.Type != ChannelType.NewsThread)
- throw new ArgumentException("Cannot get the messages of a non-thread channel.");
+ /// <summary>
+ /// Removes a role to from thread. Only applicable to private threads.
+ /// </summary>
+ /// <param name="role">The role to be removed.</param>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task RemoveRoleAsync(DiscordRole role)
+ => this.RemoveRoleAsync(role.Id);
+
+ /// <summary>
+ /// Joins a thread.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client has no access to this thread.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task JoinAsync()
+ => this.Discord.ApiClient.JoinThreadAsync(this.Id);
+
+ /// <summary>
+ /// Leaves a thread.
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client has no access to this thread.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task LeaveAsync()
+ => this.Discord.ApiClient.LeaveThreadAsync(this.Id);
+
+ /// <summary>
+ /// Sends a message to this thread.
+ /// </summary>
+ /// <param name="content">Content of the message to send.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<DiscordMessage> SendMessageAsync(string content) =>
+ !this.IsWritable()
+ ? throw new ArgumentException("Cannot send a text message to a non-thread channel.")
+ : this.Discord.ApiClient.CreateMessageAsync(this.Id, content, null, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Sends a message to this thread.
+ /// </summary>
+ /// <param name="embed">Embed to attach to the message.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<DiscordMessage> SendMessageAsync(DiscordEmbed embed) =>
+ !this.IsWritable()
+ ? throw new ArgumentException("Cannot send a text message to a non-thread channel.")
+ : this.Discord.ApiClient.CreateMessageAsync(this.Id, null, new[] { embed }, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Sends a message to this thread.
+ /// </summary>
+ /// <param name="content">Content of the message to send.</param>
+ /// <param name="embed">Embed to attach to the message.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<DiscordMessage> SendMessageAsync(string content, DiscordEmbed embed) =>
+ !this.IsWritable()
+ ? throw new ArgumentException("Cannot send a text message to a non-thread channel.")
+ : this.Discord.ApiClient.CreateMessageAsync(this.Id, content, new[] { embed }, sticker: null, replyMessageId: null, mentionReply: false, failOnInvalidReply: false);
+
+ /// <summary>
+ /// Sends a message to this thread.
+ /// </summary>
+ /// <param name="builder">The builder with all the items to thread.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<DiscordMessage> SendMessageAsync(DiscordMessageBuilder builder)
+ => this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
+
+ /// <summary>
+ /// Sends a message to this channel.
+ /// </summary>
+ /// <param name="action">The builder with all the items to send.</param>
+ /// <returns>The sent message.</returns>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<DiscordMessage> SendMessageAsync(Action<DiscordMessageBuilder> action)
+ {
+ var builder = new DiscordMessageBuilder();
+ action(builder);
- if (limit < 0)
- throw new ArgumentException("Cannot get a negative number of messages.");
+ return !this.IsWritable()
+ ? throw new ArgumentException("Cannot send a text message to a non-text channel.")
+ : this.Discord.ApiClient.CreateMessageAsync(this.Id, builder);
+ }
- if (limit == 0)
- return Array.Empty<DiscordMessage>();
+ /// <summary>
+ /// Returns a specific message
+ /// </summary>
+ /// <param name="id">The id of the message</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.SendMessagesInThreads"/> permission and <see cref="Permissions.SendTtsMessages"/> if TTS is true or the thread is locked.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new async Task<DiscordMessage> GetMessageAsync(ulong id) =>
+ this.Discord.Configuration.MessageCacheSize > 0
+ && this.Discord is DiscordClient dc
+ && dc.MessageCache != null
+ && dc.MessageCache.TryGet(xm => xm.Id == id && xm.ChannelId == this.Id, out var msg)
+ ? msg
+ : await this.Discord.ApiClient.GetMessageAsync(this.Id, id).ConfigureAwait(false);
+
+ /// <summary>
+ /// Returns a list of messages before a certain message.
+ /// <param name="limit">The amount of messages to fetch.</param>
+ /// <param name="before">Message to fetch before from.</param>
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<IReadOnlyList<DiscordMessage>> GetMessagesBeforeAsync(ulong before, int limit = 100)
+ => this.GetMessagesInternalAsync(limit, before, null, null);
+
+ /// <summary>
+ /// Returns a list of messages after a certain message.
+ /// <param name="limit">The amount of messages to fetch.</param>
+ /// <param name="after">Message to fetch after from.</param>
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<IReadOnlyList<DiscordMessage>> GetMessagesAfterAsync(ulong after, int limit = 100)
+ => this.GetMessagesInternalAsync(limit, null, after, null);
+
+ /// <summary>
+ /// Returns a list of messages around a certain message.
+ /// <param name="limit">The amount of messages to fetch.</param>
+ /// <param name="around">Message to fetch around from.</param>
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<IReadOnlyList<DiscordMessage>> GetMessagesAroundAsync(ulong around, int limit = 100)
+ => this.GetMessagesInternalAsync(limit, null, null, around);
+
+ /// <summary>
+ /// Returns a list of messages from the last message in the thread.
+ /// <param name="limit">The amount of messages to fetch.</param>
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> or the <see cref="Permissions.ReadMessageHistory"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<IReadOnlyList<DiscordMessage>> GetMessagesAsync(int limit = 100) =>
+ this.GetMessagesInternalAsync(limit, null, null, null);
+
+ /// <summary>
+ /// Returns a list of messages
+ /// </summary>
+ /// <param name="limit">How many messages should be returned.</param>
+ /// <param name="before">Get messages before snowflake.</param>
+ /// <param name="after">Get messages after snowflake.</param>
+ /// <param name="around">Get messages around snowflake.</param>
+ private async Task<IReadOnlyList<DiscordMessage>> GetMessagesInternalAsync(int limit = 100, ulong? before = null, ulong? after = null, ulong? around = null)
+ {
+ if (this.Type != ChannelType.PublicThread && this.Type != ChannelType.PrivateThread && this.Type != ChannelType.NewsThread)
+ throw new ArgumentException("Cannot get the messages of a non-thread channel.");
- //return this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, limit, before, after, around);
- if (limit > 100 && around != null)
- throw new InvalidOperationException("Cannot get more than 100 messages around the specified ID.");
+ if (limit < 0)
+ throw new ArgumentException("Cannot get a negative number of messages.");
- var msgs = new List<DiscordMessage>(limit);
- var remaining = limit;
- ulong? last = null;
- var isAfter = after != null;
+ if (limit == 0)
+ return Array.Empty<DiscordMessage>();
- int lastCount;
- do
- {
- var fetchSize = remaining > 100 ? 100 : remaining;
- var fetch = await this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, fetchSize, !isAfter ? last ?? before : null, isAfter ? last ?? after : null, around).ConfigureAwait(false);
+ //return this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, limit, before, after, around);
+ if (limit > 100 && around != null)
+ throw new InvalidOperationException("Cannot get more than 100 messages around the specified ID.");
- lastCount = fetch.Count;
- remaining -= lastCount;
+ var msgs = new List<DiscordMessage>(limit);
+ var remaining = limit;
+ ulong? last = null;
+ var isAfter = after != null;
- if (!isAfter)
+ int lastCount;
+ do
{
- msgs.AddRange(fetch);
- last = fetch.LastOrDefault()?.Id;
+ var fetchSize = remaining > 100 ? 100 : remaining;
+ var fetch = await this.Discord.ApiClient.GetChannelMessagesAsync(this.Id, fetchSize, !isAfter ? last ?? before : null, isAfter ? last ?? after : null, around).ConfigureAwait(false);
+
+ lastCount = fetch.Count;
+ remaining -= lastCount;
+
+ if (!isAfter)
+ {
+ msgs.AddRange(fetch);
+ last = fetch.LastOrDefault()?.Id;
+ }
+ else
+ {
+ msgs.InsertRange(0, fetch);
+ last = fetch.FirstOrDefault()?.Id;
+ }
}
- else
- {
- msgs.InsertRange(0, fetch);
- last = fetch.FirstOrDefault()?.Id;
- }
- }
- while (remaining > 0 && lastCount > 0);
+ while (remaining > 0 && lastCount > 0);
- return new ReadOnlyCollection<DiscordMessage>(msgs);
- }
-
- /// <summary>
- /// Deletes multiple messages if they are less than 14 days old. If they are older, none of the messages will be deleted and you will receive a <see cref="DisCatSharp.Exceptions.BadRequestException"/> error.
- /// </summary>
- /// <param name="messages">A collection of messages to delete.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new async Task DeleteMessagesAsync(IEnumerable<DiscordMessage> messages, string reason = null)
- {
- // don't enumerate more than once
- var msgs = messages.Where(x => x.Channel.Id == this.Id).Select(x => x.Id).ToArray();
- if (messages == null || !msgs.Any())
- throw new ArgumentException("You need to specify at least one message to delete.");
-
- if (msgs.Length < 2)
- {
- await this.Discord.ApiClient.DeleteMessageAsync(this.Id, msgs.Single(), reason).ConfigureAwait(false);
- return;
+ return new ReadOnlyCollection<DiscordMessage>(msgs);
}
- for (var i = 0; i < msgs.Length; i += 100)
- await this.Discord.ApiClient.DeleteMessagesAsync(this.Id, msgs.Skip(i).Take(100), reason).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Deletes a message
- /// </summary>
- /// <param name="message">The message to be deleted.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task DeleteMessageAsync(DiscordMessage message, string reason = null)
- => this.Discord.ApiClient.DeleteMessageAsync(this.Id, message.Id, reason);
-
- /// <summary>
- /// Post a typing indicator
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task TriggerTypingAsync() =>
- this.Type != ChannelType.PublicThread && this.Type != ChannelType.PrivateThread && this.Type != ChannelType.NewsThread
- ? throw new ArgumentException("Cannot start typing in a non-text channel.")
- : this.Discord.ApiClient.TriggerTypingAsync(this.Id);
-
- /// <summary>
- /// Returns all pinned messages
- /// </summary>
- /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission or the client is missing <see cref="Permissions.ReadMessageHistory"/>.</exception>
- /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
- /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public new Task<IReadOnlyList<DiscordMessage>> GetPinnedMessagesAsync() =>
- this.Type != ChannelType.PublicThread && this.Type != ChannelType.PrivateThread && this.Type != ChannelType.News
- ? throw new ArgumentException("A non-thread channel does not have pinned messages.")
- : this.Discord.ApiClient.GetPinnedMessagesAsync(this.Id);
+ /// <summary>
+ /// Deletes multiple messages if they are less than 14 days old. If they are older, none of the messages will be deleted and you will receive a <see cref="DisCatSharp.Exceptions.BadRequestException"/> error.
+ /// </summary>
+ /// <param name="messages">A collection of messages to delete.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new async Task DeleteMessagesAsync(IEnumerable<DiscordMessage> messages, string reason = null)
+ {
+ // don't enumerate more than once
+ var msgs = messages.Where(x => x.Channel.Id == this.Id).Select(x => x.Id).ToArray();
+ if (messages == null || !msgs.Any())
+ throw new ArgumentException("You need to specify at least one message to delete.");
- /// <summary>
- /// Returns a string representation of this thread.
- /// </summary>
- /// <returns>String representation of this thread.</returns>
- public override string ToString()
- => this.Type switch
+ if (msgs.Length < 2)
{
- ChannelType.NewsThread => $"News thread {this.Name} ({this.Id})",
- ChannelType.PublicThread => $"Thread {this.Name} ({this.Id})",
- ChannelType.PrivateThread => $"Private thread {this.Name} ({this.Id})",
- _ => $"Thread {this.Name} ({this.Id})",
- };
- #endregion
-
- /// <summary>
- /// Checks whether this <see cref="DiscordThreadChannel"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordThreadChannel"/>.</returns>
- public override bool Equals(object obj) => this.Equals(obj as DiscordThreadChannel);
+ await this.Discord.ApiClient.DeleteMessageAsync(this.Id, msgs.Single(), reason).ConfigureAwait(false);
+ return;
+ }
- /// <summary>
- /// Checks whether this <see cref="DiscordThreadChannel"/> is equal to another <see cref="DiscordThreadChannel"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordThreadChannel"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordThreadChannel"/> is equal to this <see cref="DiscordThreadChannel"/>.</returns>
- public bool Equals(DiscordThreadChannel e) => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+ for (var i = 0; i < msgs.Length; i += 100)
+ await this.Discord.ApiClient.DeleteMessagesAsync(this.Id, msgs.Skip(i).Take(100), reason).ConfigureAwait(false);
+ }
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordThreadChannel"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordThreadChannel"/>.</returns>
- public override int GetHashCode() => this.Id.GetHashCode();
+ /// <summary>
+ /// Deletes a message
+ /// </summary>
+ /// <param name="message">The message to be deleted.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageMessages"/> permission.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task DeleteMessageAsync(DiscordMessage message, string reason = null)
+ => this.Discord.ApiClient.DeleteMessageAsync(this.Id, message.Id, reason);
+
+ /// <summary>
+ /// Post a typing indicator
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task TriggerTypingAsync() =>
+ this.Type != ChannelType.PublicThread && this.Type != ChannelType.PrivateThread && this.Type != ChannelType.NewsThread
+ ? throw new ArgumentException("Cannot start typing in a non-text channel.")
+ : this.Discord.ApiClient.TriggerTypingAsync(this.Id);
+
+ /// <summary>
+ /// Returns all pinned messages
+ /// </summary>
+ /// <exception cref="DisCatSharp.Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.AccessChannels"/> permission or the client is missing <see cref="Permissions.ReadMessageHistory"/>.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.NotFoundException">Thrown when the thread does not exist.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="DisCatSharp.Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public new Task<IReadOnlyList<DiscordMessage>> GetPinnedMessagesAsync() =>
+ this.Type != ChannelType.PublicThread && this.Type != ChannelType.PrivateThread && this.Type != ChannelType.News
+ ? throw new ArgumentException("A non-thread channel does not have pinned messages.")
+ : this.Discord.ApiClient.GetPinnedMessagesAsync(this.Id);
+
+ /// <summary>
+ /// Returns a string representation of this thread.
+ /// </summary>
+ /// <returns>String representation of this thread.</returns>
+ public override string ToString()
+ => this.Type switch
+ {
+ ChannelType.NewsThread => $"News thread {this.Name} ({this.Id})",
+ ChannelType.PublicThread => $"Thread {this.Name} ({this.Id})",
+ ChannelType.PrivateThread => $"Private thread {this.Name} ({this.Id})",
+ _ => $"Thread {this.Name} ({this.Id})",
+ };
+ #endregion
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordThreadChannel"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordThreadChannel"/>.</returns>
+ public override bool Equals(object obj) => this.Equals(obj as DiscordThreadChannel);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordThreadChannel"/> is equal to another <see cref="DiscordThreadChannel"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordThreadChannel"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordThreadChannel"/> is equal to this <see cref="DiscordThreadChannel"/>.</returns>
+ public bool Equals(DiscordThreadChannel e) => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordThreadChannel"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordThreadChannel"/>.</returns>
+ public override int GetHashCode() => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordThreadChannel"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are equal.</returns>
+ public static bool operator ==(DiscordThreadChannel e1, DiscordThreadChannel e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- /// <summary>
- /// Gets whether the two <see cref="DiscordThreadChannel"/> objects are equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are equal.</returns>
- public static bool operator ==(DiscordThreadChannel e1, DiscordThreadChannel e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordThreadChannel"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are not equal.</returns>
+ public static bool operator !=(DiscordThreadChannel e1, DiscordThreadChannel e2)
+ => !(e1 == e2);
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordThreadChannel"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are not equal.</returns>
- public static bool operator !=(DiscordThreadChannel e1, DiscordThreadChannel e2)
- => !(e1 == e2);
}
diff --git a/DisCatSharp/Entities/Thread/DiscordThreadChannelMember.cs b/DisCatSharp/Entities/Thread/DiscordThreadChannelMember.cs
index bb3473721..0e0e044c5 100644
--- a/DisCatSharp/Entities/Thread/DiscordThreadChannelMember.cs
+++ b/DisCatSharp/Entities/Thread/DiscordThreadChannelMember.cs
@@ -1,138 +1,139 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord thread member object.
-/// </summary>
-public class DiscordThreadChannelMember : SnowflakeObject, IEquatable<DiscordThreadChannelMember>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the id of the user.
- /// </summary>
- [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong UserId { get; internal set; }
-
- /// <summary>
- /// Gets the member object of the user.
- /// </summary>
- [JsonProperty("member", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMember Member { get; internal set; }
-
- /// <summary>
- /// Gets the presence of the user.
- /// </summary>
- [JsonProperty("presence", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordPresence Presence { get; internal set; }
-
- /// <summary>
- /// Gets the timestamp when the user joined the thread.
- /// </summary>
- [JsonIgnore]
- public DateTimeOffset? JoinTimeStamp
- => !string.IsNullOrWhiteSpace(this.JoinTimeStampRaw) && DateTimeOffset.TryParse(this.JoinTimeStampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
- dto : null;
-
- /// <summary>
- /// Gets the timestamp when the user joined the thread as raw string.
- /// </summary>
- [JsonProperty("join_timestamp", NullValueHandling = NullValueHandling.Ignore)]
- internal string JoinTimeStampRaw { get; set; }
-
- /// <summary>
- /// Gets the thread member flags.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public ThreadMemberFlags Flags { get; internal set; }
-
- /// <summary>
- /// Gets the category that contains this channel. For threads, gets the channel this thread was created in.
- /// </summary>
- [JsonIgnore]
- public DiscordChannel Thread
- => this.Guild != null && this.Guild.ThreadsInternal.TryGetValue(this.Id, out var thread) ? thread : null;
-
- /// <summary>
- /// Gets the guild to which this channel belongs.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild
- => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) ? guild : null;
-
- [JsonIgnore]
- internal ulong GuildId;
-
- /// <summary>
- /// Checks whether this <see cref="DiscordThreadChannelMember"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordThreadChannelMember"/>.</returns>
- public override bool Equals(object obj) => this.Equals(obj as DiscordThreadChannelMember);
-
- /// <summary>
- /// Checks whether this <see cref="DiscordThreadChannel"/> is equal to another <see cref="DiscordThreadChannelMember"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordThreadChannel"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordThreadChannel"/> is equal to this <see cref="DiscordThreadChannelMember"/>.</returns>
- public bool Equals(DiscordThreadChannelMember e) => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.UserId == e.UserId));
-
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordThreadChannelMember"/>.
+ /// Represents a discord thread member object.
/// </summary>
- /// <returns>The hash code for this <see cref="DiscordThreadChannelMember"/>.</returns>
- public override int GetHashCode() => HashCode.Combine(this.Id.GetHashCode(), this.UserId.GetHashCode());
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordThreadChannel"/> objects are equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are equal.</returns>
- public static bool operator ==(DiscordThreadChannelMember e1, DiscordThreadChannelMember e2)
+ public class DiscordThreadChannelMember : SnowflakeObject, IEquatable<DiscordThreadChannelMember>
{
- var o1 = e1 as object;
- var o2 = e2 as object;
-
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || (e1.Id == e2.Id && e1.UserId == e2.UserId));
+ /// <summary>
+ /// Gets the id of the user.
+ /// </summary>
+ [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong UserId { get; internal set; }
+
+ /// <summary>
+ /// Gets the member object of the user.
+ /// </summary>
+ [JsonProperty("member", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordMember Member { get; internal set; }
+
+ /// <summary>
+ /// Gets the presence of the user.
+ /// </summary>
+ [JsonProperty("presence", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordPresence Presence { get; internal set; }
+
+ /// <summary>
+ /// Gets the timestamp when the user joined the thread.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset? JoinTimeStamp
+ => !string.IsNullOrWhiteSpace(this.JoinTimeStampRaw) && DateTimeOffset.TryParse(this.JoinTimeStampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
+ dto : null;
+
+ /// <summary>
+ /// Gets the timestamp when the user joined the thread as raw string.
+ /// </summary>
+ [JsonProperty("join_timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ internal string JoinTimeStampRaw { get; set; }
+
+ /// <summary>
+ /// Gets the thread member flags.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public ThreadMemberFlags Flags { get; internal set; }
+
+ /// <summary>
+ /// Gets the category that contains this channel. For threads, gets the channel this thread was created in.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel Thread
+ => this.Guild != null && this.Guild.ThreadsInternal.TryGetValue(this.Id, out var thread) ? thread : null;
+
+ /// <summary>
+ /// Gets the guild to which this channel belongs.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild
+ => this.Discord.Guilds.TryGetValue(this.GuildId, out var guild) ? guild : null;
+
+ [JsonIgnore]
+ internal ulong GuildId;
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordThreadChannelMember"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordThreadChannelMember"/>.</returns>
+ public override bool Equals(object obj) => this.Equals(obj as DiscordThreadChannelMember);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordThreadChannel"/> is equal to another <see cref="DiscordThreadChannelMember"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordThreadChannel"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordThreadChannel"/> is equal to this <see cref="DiscordThreadChannelMember"/>.</returns>
+ public bool Equals(DiscordThreadChannelMember e) => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this.UserId == e.UserId));
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordThreadChannelMember"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordThreadChannelMember"/>.</returns>
+ public override int GetHashCode() => HashCode.Combine(this.Id.GetHashCode(), this.UserId.GetHashCode());
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordThreadChannel"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are equal.</returns>
+ public static bool operator ==(DiscordThreadChannelMember e1, DiscordThreadChannelMember e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
+
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || (e1.Id == e2.Id && e1.UserId == e2.UserId));
+ }
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordThreadChannelMember"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First channel to compare.</param>
+ /// <param name="e2">Second channel to compare.</param>
+ /// <returns>Whether the two channels are not equal.</returns>
+ public static bool operator !=(DiscordThreadChannelMember e1, DiscordThreadChannelMember e2)
+ => !(e1 == e2);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordThreadChannelMember"/> class.
+ /// </summary>
+ internal DiscordThreadChannelMember()
+ { }
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordThreadChannelMember"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First channel to compare.</param>
- /// <param name="e2">Second channel to compare.</param>
- /// <returns>Whether the two channels are not equal.</returns>
- public static bool operator !=(DiscordThreadChannelMember e1, DiscordThreadChannelMember e2)
- => !(e1 == e2);
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordThreadChannelMember"/> class.
- /// </summary>
- internal DiscordThreadChannelMember()
- { }
}
diff --git a/DisCatSharp/Entities/Thread/DiscordThreadChannelMetadata.cs b/DisCatSharp/Entities/Thread/DiscordThreadChannelMetadata.cs
index a01302f23..d894d2807 100644
--- a/DisCatSharp/Entities/Thread/DiscordThreadChannelMetadata.cs
+++ b/DisCatSharp/Entities/Thread/DiscordThreadChannelMetadata.cs
@@ -1,98 +1,99 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord thread metadata object.
-/// </summary>
-public class DiscordThreadChannelMetadata
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets whether the thread is archived or not.
+ /// Represents a discord thread metadata object.
/// </summary>
- [JsonProperty("archived", NullValueHandling = NullValueHandling.Ignore)]
- public bool Archived { get; internal set; }
+ public class DiscordThreadChannelMetadata
+ {
+ /// <summary>
+ /// Gets whether the thread is archived or not.
+ /// </summary>
+ [JsonProperty("archived", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Archived { get; internal set; }
- /// <summary>
- /// Gets ID of the archiver.
- /// </summary>
- [JsonProperty("archiver_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? Archiver { get; internal set; }
+ /// <summary>
+ /// Gets ID of the archiver.
+ /// </summary>
+ [JsonProperty("archiver_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? Archiver { get; internal set; }
- /// <summary>
- /// Gets the time when it will be archived, while there is no action inside the thread (In minutes).
- /// </summary>
- [JsonProperty("auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
- public ThreadAutoArchiveDuration AutoArchiveDuration { get; internal set; }
+ /// <summary>
+ /// Gets the time when it will be archived, while there is no action inside the thread (In minutes).
+ /// </summary>
+ [JsonProperty("auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
+ public ThreadAutoArchiveDuration AutoArchiveDuration { get; internal set; }
- /// <summary>
- /// Gets the timestamp when it was archived.
- /// </summary>
- public DateTimeOffset? ArchiveTimestamp
- => !string.IsNullOrWhiteSpace(this.ArchiveTimestampRaw) && DateTimeOffset.TryParse(this.ArchiveTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
- dto : null;
+ /// <summary>
+ /// Gets the timestamp when it was archived.
+ /// </summary>
+ public DateTimeOffset? ArchiveTimestamp
+ => !string.IsNullOrWhiteSpace(this.ArchiveTimestampRaw) && DateTimeOffset.TryParse(this.ArchiveTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
+ dto : null;
- /// <summary>
- /// Gets the timestamp when it was archived as raw string.
- /// </summary>
- [JsonProperty("archive_timestamp", NullValueHandling = NullValueHandling.Ignore)]
- internal string ArchiveTimestampRaw { get; set; }
+ /// <summary>
+ /// Gets the timestamp when it was archived as raw string.
+ /// </summary>
+ [JsonProperty("archive_timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ internal string ArchiveTimestampRaw { get; set; }
- /// <summary>
- /// Gets whether the thread is locked.
- /// </summary>
- [JsonProperty("locked", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Locked { get; internal set; }
+ /// <summary>
+ /// Gets whether the thread is locked.
+ /// </summary>
+ [JsonProperty("locked", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Locked { get; internal set; }
- /// <summary>
- /// Gets whether non-moderators can add other non-moderators to a thread; only available on private threads.
- /// </summary>
- [JsonProperty("invitable", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Invitable { get; internal set; }
+ /// <summary>
+ /// Gets whether non-moderators can add other non-moderators to a thread; only available on private threads.
+ /// </summary>
+ [JsonProperty("invitable", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Invitable { get; internal set; }
- /// <summary>
- /// Gets the timestamp when the thread was created.
- /// Only populated for threads created after 2022-01-09.
- /// </summary>
- public DateTimeOffset? CreateTimestamp
- => !string.IsNullOrWhiteSpace(this.CreateTimestampRaw) && DateTimeOffset.TryParse(this.CreateTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
- dto : null;
+ /// <summary>
+ /// Gets the timestamp when the thread was created.
+ /// Only populated for threads created after 2022-01-09.
+ /// </summary>
+ public DateTimeOffset? CreateTimestamp
+ => !string.IsNullOrWhiteSpace(this.CreateTimestampRaw) && DateTimeOffset.TryParse(this.CreateTimestampRaw, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dto) ?
+ dto : null;
- /// <summary>
- /// Gets the timestamp when the thread was created as raw string.
- /// Only populated for threads created after 2022-01-09.
- /// </summary>
- [JsonProperty("create_timestamp", NullValueHandling = NullValueHandling.Ignore)]
- internal string CreateTimestampRaw { get; set; }
+ /// <summary>
+ /// Gets the timestamp when the thread was created as raw string.
+ /// Only populated for threads created after 2022-01-09.
+ /// </summary>
+ [JsonProperty("create_timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ internal string CreateTimestampRaw { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordThreadChannelMetadata"/> class.
- /// </summary>
- internal DiscordThreadChannelMetadata()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordThreadChannelMetadata"/> class.
+ /// </summary>
+ internal DiscordThreadChannelMetadata()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/Thread/DiscordThreadResult.cs b/DisCatSharp/Entities/Thread/DiscordThreadResult.cs
index 978c8425a..2e3221698 100644
--- a/DisCatSharp/Entities/Thread/DiscordThreadResult.cs
+++ b/DisCatSharp/Entities/Thread/DiscordThreadResult.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a discord thread result.
-/// </summary>
-public class DiscordThreadResult
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the returned threads.
+ /// Represents a discord thread result.
/// </summary>
- public Dictionary<ulong, DiscordThreadChannel> ReturnedThreads { get; internal set; }
+ public class DiscordThreadResult
+ {
+ /// <summary>
+ /// Gets the returned threads.
+ /// </summary>
+ public Dictionary<ulong, DiscordThreadChannel> ReturnedThreads { get; internal set; }
- /// <summary>
- /// Gets the active members.
- /// </summary>
- public List<DiscordThreadChannelMember> ActiveMembers { get; internal set; }
+ /// <summary>
+ /// Gets the active members.
+ /// </summary>
+ public List<DiscordThreadChannelMember> ActiveMembers { get; internal set; }
- /// <summary>
- /// Whether there are more results.
- /// </summary>
- public bool HasMore { get; internal set; }
+ /// <summary>
+ /// Whether there are more results.
+ /// </summary>
+ public bool HasMore { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordThreadResult"/> class.
- /// </summary>
- internal DiscordThreadResult()
- : base()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordThreadResult"/> class.
+ /// </summary>
+ internal DiscordThreadResult()
+ : base()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/User/DiscordActivity.cs b/DisCatSharp/Entities/User/DiscordActivity.cs
index c7e2d4896..0cf9bf681 100644
--- a/DisCatSharp/Entities/User/DiscordActivity.cs
+++ b/DisCatSharp/Entities/User/DiscordActivity.cs
@@ -1,528 +1,529 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using DisCatSharp.Net.Abstractions;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents user status.
-/// </summary>
-[JsonConverter(typeof(UserStatusConverter))]
-public enum UserStatus
+namespace DisCatSharp.Entities
{
/// <summary>
- /// User is offline.
- /// </summary>
- Offline = 0,
-
- /// <summary>
- /// User is online.
- /// </summary>
- Online = 1,
-
- /// <summary>
- /// User is idle.
- /// </summary>
- Idle = 2,
-
- /// <summary>
- /// User asked not to be disturbed.
- /// </summary>
- DoNotDisturb = 4,
-
- /// <summary>
- /// User is invisible. They will appear as Offline to anyone but themselves.
+ /// Represents user status.
/// </summary>
- Invisible = 5,
-
- /// <summary>
- /// User is streaming.
- /// </summary>
- Streaming = 6
-}
+ [JsonConverter(typeof(UserStatusConverter))]
+ public enum UserStatus
+ {
+ /// <summary>
+ /// User is offline.
+ /// </summary>
+ Offline = 0,
+
+ /// <summary>
+ /// User is online.
+ /// </summary>
+ Online = 1,
+
+ /// <summary>
+ /// User is idle.
+ /// </summary>
+ Idle = 2,
+
+ /// <summary>
+ /// User asked not to be disturbed.
+ /// </summary>
+ DoNotDisturb = 4,
+
+ /// <summary>
+ /// User is invisible. They will appear as Offline to anyone but themselves.
+ /// </summary>
+ Invisible = 5,
+
+ /// <summary>
+ /// User is streaming.
+ /// </summary>
+ Streaming = 6
+ }
-/// <summary>
-/// Represents a user status converter.
-/// </summary>
-internal sealed class UserStatusConverter : JsonConverter
-{
/// <summary>
- /// Writes the json.
+ /// Represents a user status converter.
/// </summary>
- /// <param name="writer">The writer.</param>
- /// <param name="value">The value.</param>
- /// <param name="serializer">The serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ internal sealed class UserStatusConverter : JsonConverter
{
- if (value is UserStatus status)
+ /// <summary>
+ /// Writes the json.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="value">The value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
- switch (status) // reader.Value can be a string, DateTime or DateTimeOffset (yes, it's weird)
+ if (value is UserStatus status)
{
- case UserStatus.Online:
- writer.WriteValue("online");
- return;
-
- case UserStatus.Idle:
- writer.WriteValue("idle");
- return;
-
- case UserStatus.DoNotDisturb:
- writer.WriteValue("dnd");
- return;
-
- case UserStatus.Invisible:
- writer.WriteValue("invisible");
- return;
-
- case UserStatus.Streaming:
- writer.WriteValue("streaming");
- return;
-
- case UserStatus.Offline:
- default:
- writer.WriteValue("offline");
- return;
+ switch (status) // reader.Value can be a string, DateTime or DateTimeOffset (yes, it's weird)
+ {
+ case UserStatus.Online:
+ writer.WriteValue("online");
+ return;
+
+ case UserStatus.Idle:
+ writer.WriteValue("idle");
+ return;
+
+ case UserStatus.DoNotDisturb:
+ writer.WriteValue("dnd");
+ return;
+
+ case UserStatus.Invisible:
+ writer.WriteValue("invisible");
+ return;
+
+ case UserStatus.Streaming:
+ writer.WriteValue("streaming");
+ return;
+
+ case UserStatus.Offline:
+ default:
+ writer.WriteValue("offline");
+ return;
+ }
}
}
- }
-
- /// <summary>
- /// Reads the json.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="objectType">The object type.</param>
- /// <param name="existingValue">The existing value.</param>
- /// <param name="serializer">The serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) =>
- // Active sessions are indicated with an "online", "idle", or "dnd" string per platform. If a user is
- // offline or invisible, the corresponding field is not present.
- reader.Value?.ToString().ToLowerInvariant() switch // reader.Value can be a string, DateTime or DateTimeOffset (yes, it's weird)
- {
- "online" => UserStatus.Online,
- "idle" => UserStatus.Idle,
- "dnd" => UserStatus.DoNotDisturb,
- "invisible" => UserStatus.Invisible,
- "streaming" => UserStatus.Streaming,
- _ => UserStatus.Offline,
- };
-
- /// <summary>
- /// Whether this user5 status can be converted.
- /// </summary>
- /// <param name="objectType">The object type.</param>
- /// <returns>A bool.</returns>
- public override bool CanConvert(Type objectType) => objectType == typeof(UserStatus);
-}
-
-/// <summary>
-/// Represents a game that a user is playing.
-/// </summary>
-public sealed class DiscordActivity
-{
- /// <summary>
- /// Gets or sets the id of user's activity.
- /// </summary>
- public string Id { get; set; }
-
- /// <summary>
- /// Gets or sets the name of user's activity.
- /// </summary>
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the stream URL, if applicable.
- /// </summary>
- public string StreamUrl { get; set; }
-
- /// <summary>
- /// Gets or sets platform in this rich presence.
- /// </summary>
- public string Platform { get; set; }
-
- /// <summary>
- /// Gets or sets sync id in this rich presence.
- /// </summary>
- public string SyncId { get; set; }
-
- /// <summary>
- /// Gets or sets session_id in this rich presence.
- /// </summary>
- public string SessionId { get; set; }
-
- /// <summary>
- /// Gets or sets the activity type.
- /// </summary>
- public ActivityType ActivityType { get; set; }
-
- /// <summary>
- /// Gets the rich presence details, if present.
- /// </summary>
- public DiscordRichPresence RichPresence { get; internal set; }
-
- /// <summary>
- /// Gets the custom status of this activity, if present.
- /// </summary>
- public DiscordCustomStatus CustomStatus { get; internal set; }
-
- /// <summary>
- /// Creates a new, empty instance of a <see cref="DiscordActivity"/>.
- /// </summary>
- public DiscordActivity()
- {
- this.ActivityType = ActivityType.Playing;
- }
-
- /// <summary>
- /// Creates a new instance of a <see cref="DiscordActivity"/> with specified name.
- /// </summary>
- /// <param name="name">Name of the activity.</param>
- public DiscordActivity(string name)
- {
- this.Name = name;
- this.ActivityType = ActivityType.Playing;
- }
-
- /// <summary>
- /// Creates a new instance of a <see cref="DiscordActivity"/> with specified name.
- /// </summary>
- /// <param name="name">Name of the activity.</param>
- /// <param name="type">Type of the activity.</param>
- public DiscordActivity(string name, ActivityType type)
- {
- if (type == ActivityType.Custom)
- throw new InvalidOperationException("Bots cannot use a custom status.");
-
- this.Name = name;
- this.ActivityType = type;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordActivity"/> class.
- /// </summary>
- /// <param name="rawActivity">The raw activity.</param>
- internal DiscordActivity(TransportActivity rawActivity)
- {
- this.UpdateWith(rawActivity);
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordActivity"/> class.
- /// </summary>
- /// <param name="other">The other.</param>
- internal DiscordActivity(DiscordActivity other)
- {
- this.Name = other.Name;
- this.ActivityType = other.ActivityType;
- this.StreamUrl = other.StreamUrl;
- this.SessionId = other.SessionId;
- this.SyncId = other.SyncId;
- this.Platform = other.Platform;
- this.RichPresence = new DiscordRichPresence(other.RichPresence);
- this.CustomStatus = new DiscordCustomStatus(other.CustomStatus);
- }
-
- /// <summary>
- /// Updates a activity with an transport activity.
- /// </summary>
- /// <param name="rawActivity">The raw activity.</param>
- internal void UpdateWith(TransportActivity rawActivity)
- {
- this.Name = rawActivity?.Name;
- this.ActivityType = rawActivity != null ? rawActivity.ActivityType : ActivityType.Playing;
- this.StreamUrl = rawActivity?.StreamUrl;
- this.SessionId = rawActivity?.SessionId;
- this.SyncId = rawActivity?.SyncId;
- this.Platform = rawActivity?.Platform;
-
- if (rawActivity?.IsRichPresence() == true && this.RichPresence != null)
- this.RichPresence.UpdateWith(rawActivity);
- else this.RichPresence = rawActivity?.IsRichPresence() == true ? new DiscordRichPresence(rawActivity) : null;
-
- if (rawActivity?.IsCustomStatus() == true && this.CustomStatus != null)
- this.CustomStatus.UpdateWith(rawActivity.State, rawActivity.Emoji);
- else this.CustomStatus = rawActivity?.IsCustomStatus() == true
- ? new DiscordCustomStatus
+ /// <summary>
+ /// Reads the json.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="objectType">The object type.</param>
+ /// <param name="existingValue">The existing value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) =>
+ // Active sessions are indicated with an "online", "idle", or "dnd" string per platform. If a user is
+ // offline or invisible, the corresponding field is not present.
+ reader.Value?.ToString().ToLowerInvariant() switch // reader.Value can be a string, DateTime or DateTimeOffset (yes, it's weird)
{
- Name = rawActivity.State,
- Emoji = rawActivity.Emoji
- }
- : null;
- }
-}
-
-/// <summary>
-/// Represents details for a custom status activity, attached to a <see cref="DiscordActivity"/>.
-/// </summary>
-public sealed class DiscordCustomStatus
-{
- /// <summary>
- /// Gets the name of this custom status.
- /// </summary>
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the emoji of this custom status, if any.
- /// </summary>
- public DiscordEmoji Emoji { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordCustomStatus"/> class.
- /// </summary>
- internal DiscordCustomStatus()
- { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordCustomStatus"/> class.
- /// </summary>
- /// <param name="other">The other.</param>
- internal DiscordCustomStatus(DiscordCustomStatus other)
- {
- this.Name = other.Name;
- this.Emoji = other.Emoji;
+ "online" => UserStatus.Online,
+ "idle" => UserStatus.Idle,
+ "dnd" => UserStatus.DoNotDisturb,
+ "invisible" => UserStatus.Invisible,
+ "streaming" => UserStatus.Streaming,
+ _ => UserStatus.Offline,
+ };
+
+ /// <summary>
+ /// Whether this user5 status can be converted.
+ /// </summary>
+ /// <param name="objectType">The object type.</param>
+ /// <returns>A bool.</returns>
+ public override bool CanConvert(Type objectType) => objectType == typeof(UserStatus);
}
/// <summary>
- /// Updates a discord status.
+ /// Represents a game that a user is playing.
/// </summary>
- /// <param name="state">The state.</param>
- /// <param name="emoji">The emoji.</param>
- internal void UpdateWith(string state, DiscordEmoji emoji)
+ public sealed class DiscordActivity
{
- this.Name = state;
- this.Emoji = emoji;
- }
-}
-
-/// <summary>
-/// Represents details for Discord rich presence, attached to a <see cref="DiscordActivity"/>.
-/// </summary>
-public sealed class DiscordRichPresence
-{
- /// <summary>
- /// Gets the details of this presence.
- /// </summary>
- public string Details { get; internal set; }
-
- /// <summary>
- /// Gets the game state.
- /// </summary>
- public string State { get; internal set; }
-
- /// <summary>
- /// Gets the application for which the rich presence is for.
- /// </summary>
- public DiscordApplication Application { get; internal set; }
-
- /// <summary>
- /// Gets the instance status.
- /// </summary>
- public bool? Instance { get; internal set; }
-
- /// <summary>
- /// Gets the large image for the rich presence.
- /// </summary>
- public DiscordAsset LargeImage { get; internal set; }
-
- /// <summary>
- /// Gets the hover text for large image.
- /// </summary>
- public string LargeImageText { get; internal set; }
-
- /// <summary>
- /// Gets the small image for the rich presence.
- /// </summary>
- public DiscordAsset SmallImage { get; internal set; }
-
- /// <summary>
- /// Gets the hover text for small image.
- /// </summary>
- public string SmallImageText { get; internal set; }
-
- /// <summary>
- /// Gets the current party size.
- /// </summary>
- public long? CurrentPartySize { get; internal set; }
-
- /// <summary>
- /// Gets the maximum party size.
- /// </summary>
- public long? MaximumPartySize { get; internal set; }
-
- /// <summary>
- /// Gets the party ID.
- /// </summary>
- public ulong? PartyId { get; internal set; }
-
- /// <summary>
- /// Gets the buttons.
- /// </summary>
- public IReadOnlyList<string> Buttons { get; internal set; }
-
- /// <summary>
- /// Gets the game start timestamp.
- /// </summary>
- public DateTimeOffset? StartTimestamp { get; internal set; }
-
- /// <summary>
- /// Gets the game end timestamp.
- /// </summary>
- public DateTimeOffset? EndTimestamp { get; internal set; }
+ /// <summary>
+ /// Gets or sets the id of user's activity.
+ /// </summary>
+ public string Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name of user's activity.
+ /// </summary>
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the stream URL, if applicable.
+ /// </summary>
+ public string StreamUrl { get; set; }
+
+ /// <summary>
+ /// Gets or sets platform in this rich presence.
+ /// </summary>
+ public string Platform { get; set; }
+
+ /// <summary>
+ /// Gets or sets sync id in this rich presence.
+ /// </summary>
+ public string SyncId { get; set; }
+
+ /// <summary>
+ /// Gets or sets session_id in this rich presence.
+ /// </summary>
+ public string SessionId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the activity type.
+ /// </summary>
+ public ActivityType ActivityType { get; set; }
+
+ /// <summary>
+ /// Gets the rich presence details, if present.
+ /// </summary>
+ public DiscordRichPresence RichPresence { get; internal set; }
+
+ /// <summary>
+ /// Gets the custom status of this activity, if present.
+ /// </summary>
+ public DiscordCustomStatus CustomStatus { get; internal set; }
+
+ /// <summary>
+ /// Creates a new, empty instance of a <see cref="DiscordActivity"/>.
+ /// </summary>
+ public DiscordActivity()
+ {
+ this.ActivityType = ActivityType.Playing;
+ }
- /// <summary>
- /// Gets the secret value enabling users to join your game.
- /// </summary>
- public string JoinSecret { get; internal set; }
+ /// <summary>
+ /// Creates a new instance of a <see cref="DiscordActivity"/> with specified name.
+ /// </summary>
+ /// <param name="name">Name of the activity.</param>
+ public DiscordActivity(string name)
+ {
+ this.Name = name;
+ this.ActivityType = ActivityType.Playing;
+ }
- /// <summary>
- /// Gets the secret value enabling users to receive notifications whenever your game state changes.
- /// </summary>
- public string MatchSecret { get; internal set; }
+ /// <summary>
+ /// Creates a new instance of a <see cref="DiscordActivity"/> with specified name.
+ /// </summary>
+ /// <param name="name">Name of the activity.</param>
+ /// <param name="type">Type of the activity.</param>
+ public DiscordActivity(string name, ActivityType type)
+ {
+ if (type == ActivityType.Custom)
+ throw new InvalidOperationException("Bots cannot use a custom status.");
- /// <summary>
- /// Gets the secret value enabling users to spectate your game.
- /// </summary>
- public string SpectateSecret { get; internal set; }
+ this.Name = name;
+ this.ActivityType = type;
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordRichPresence"/> class.
- /// </summary>
- internal DiscordRichPresence() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordActivity"/> class.
+ /// </summary>
+ /// <param name="rawActivity">The raw activity.</param>
+ internal DiscordActivity(TransportActivity rawActivity)
+ {
+ this.UpdateWith(rawActivity);
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordRichPresence"/> class.
- /// </summary>
- /// <param name="rawGame">The raw game.</param>
- internal DiscordRichPresence(TransportActivity rawGame)
- {
- this.UpdateWith(rawGame);
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordActivity"/> class.
+ /// </summary>
+ /// <param name="other">The other.</param>
+ internal DiscordActivity(DiscordActivity other)
+ {
+ this.Name = other.Name;
+ this.ActivityType = other.ActivityType;
+ this.StreamUrl = other.StreamUrl;
+ this.SessionId = other.SessionId;
+ this.SyncId = other.SyncId;
+ this.Platform = other.Platform;
+ this.RichPresence = new DiscordRichPresence(other.RichPresence);
+ this.CustomStatus = new DiscordCustomStatus(other.CustomStatus);
+ }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordRichPresence"/> class.
- /// </summary>
- /// <param name="other">The other.</param>
- internal DiscordRichPresence(DiscordRichPresence other)
- {
- this.Details = other.Details;
- this.State = other.State;
- this.Application = other.Application;
- this.Instance = other.Instance;
- this.LargeImageText = other.LargeImageText;
- this.SmallImageText = other.SmallImageText;
- this.LargeImage = other.LargeImage;
- this.SmallImage = other.SmallImage;
- this.CurrentPartySize = other.CurrentPartySize;
- this.MaximumPartySize = other.MaximumPartySize;
- this.PartyId = other.PartyId;
- this.Buttons = other.Buttons;
- this.StartTimestamp = other.StartTimestamp;
- this.EndTimestamp = other.EndTimestamp;
- this.JoinSecret = other.JoinSecret;
- this.MatchSecret = other.MatchSecret;
- this.SpectateSecret = other.SpectateSecret;
+ /// <summary>
+ /// Updates a activity with an transport activity.
+ /// </summary>
+ /// <param name="rawActivity">The raw activity.</param>
+ internal void UpdateWith(TransportActivity rawActivity)
+ {
+ this.Name = rawActivity?.Name;
+ this.ActivityType = rawActivity != null ? rawActivity.ActivityType : ActivityType.Playing;
+ this.StreamUrl = rawActivity?.StreamUrl;
+ this.SessionId = rawActivity?.SessionId;
+ this.SyncId = rawActivity?.SyncId;
+ this.Platform = rawActivity?.Platform;
+
+ if (rawActivity?.IsRichPresence() == true && this.RichPresence != null)
+ this.RichPresence.UpdateWith(rawActivity);
+ else this.RichPresence = rawActivity?.IsRichPresence() == true ? new DiscordRichPresence(rawActivity) : null;
+
+ if (rawActivity?.IsCustomStatus() == true && this.CustomStatus != null)
+ this.CustomStatus.UpdateWith(rawActivity.State, rawActivity.Emoji);
+ else this.CustomStatus = rawActivity?.IsCustomStatus() == true
+ ? new DiscordCustomStatus
+ {
+ Name = rawActivity.State,
+ Emoji = rawActivity.Emoji
+ }
+ : null;
+ }
}
/// <summary>
- /// Updates a game activity with an transport activity.
+ /// Represents details for a custom status activity, attached to a <see cref="DiscordActivity"/>.
/// </summary>
- /// <param name="rawGame">The raw game.</param>
- internal void UpdateWith(TransportActivity rawGame)
+ public sealed class DiscordCustomStatus
{
- this.Details = rawGame?.Details;
- this.State = rawGame?.State;
- this.Application = rawGame?.ApplicationId != null ? new DiscordApplication { Id = rawGame.ApplicationId.Value } : null;
- this.Instance = rawGame?.Instance;
- this.LargeImageText = rawGame?.Assets?.LargeImageText;
- this.SmallImageText = rawGame?.Assets?.SmallImageText;
- this.CurrentPartySize = rawGame?.Party?.Size?.Current;
- this.MaximumPartySize = rawGame?.Party?.Size?.Maximum;
- if (rawGame?.Party != null && ulong.TryParse(rawGame.Party.Id, NumberStyles.Number, CultureInfo.InvariantCulture, out var partyId))
- this.PartyId = partyId;
- this.Buttons = rawGame?.Buttons;
- this.StartTimestamp = rawGame?.Timestamps?.Start;
- this.EndTimestamp = rawGame?.Timestamps?.End;
- this.JoinSecret = rawGame?.Secrets?.Join;
- this.MatchSecret = rawGame?.Secrets?.Match;
- this.SpectateSecret = rawGame?.Secrets?.Spectate;
-
- var lid = rawGame?.Assets?.LargeImage;
- if (lid != null)
+ /// <summary>
+ /// Gets the name of this custom status.
+ /// </summary>
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the emoji of this custom status, if any.
+ /// </summary>
+ public DiscordEmoji Emoji { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordCustomStatus"/> class.
+ /// </summary>
+ internal DiscordCustomStatus()
+ { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordCustomStatus"/> class.
+ /// </summary>
+ /// <param name="other">The other.</param>
+ internal DiscordCustomStatus(DiscordCustomStatus other)
{
- if (lid.StartsWith("spotify:"))
- this.LargeImage = new DiscordSpotifyAsset { Id = lid };
- else if (ulong.TryParse(lid, NumberStyles.Number, CultureInfo.InvariantCulture, out var ulid))
- this.LargeImage = new DiscordApplicationAsset { Id = lid, Application = this.Application, Type = ApplicationAssetType.LargeImage };
+ this.Name = other.Name;
+ this.Emoji = other.Emoji;
}
- var sid = rawGame?.Assets?.SmallImage;
- if (sid != null)
+ /// <summary>
+ /// Updates a discord status.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ /// <param name="emoji">The emoji.</param>
+ internal void UpdateWith(string state, DiscordEmoji emoji)
{
- if (sid.StartsWith("spotify:"))
- this.SmallImage = new DiscordSpotifyAsset { Id = sid };
- else if (ulong.TryParse(sid, NumberStyles.Number, CultureInfo.InvariantCulture, out var usid))
- this.SmallImage = new DiscordApplicationAsset { Id = sid, Application = this.Application, Type = ApplicationAssetType.LargeImage };
+ this.Name = state;
+ this.Emoji = emoji;
}
}
-}
-/// <summary>
-/// Determines the type of a user activity.
-/// </summary>
-public enum ActivityType
-{
/// <summary>
- /// Indicates the user is playing a game.
+ /// Represents details for Discord rich presence, attached to a <see cref="DiscordActivity"/>.
/// </summary>
- Playing = 0,
-
- /// <summary>
- /// Indicates the user is streaming a game.
- /// </summary>
- Streaming = 1,
+ public sealed class DiscordRichPresence
+ {
+ /// <summary>
+ /// Gets the details of this presence.
+ /// </summary>
+ public string Details { get; internal set; }
+
+ /// <summary>
+ /// Gets the game state.
+ /// </summary>
+ public string State { get; internal set; }
+
+ /// <summary>
+ /// Gets the application for which the rich presence is for.
+ /// </summary>
+ public DiscordApplication Application { get; internal set; }
+
+ /// <summary>
+ /// Gets the instance status.
+ /// </summary>
+ public bool? Instance { get; internal set; }
+
+ /// <summary>
+ /// Gets the large image for the rich presence.
+ /// </summary>
+ public DiscordAsset LargeImage { get; internal set; }
+
+ /// <summary>
+ /// Gets the hover text for large image.
+ /// </summary>
+ public string LargeImageText { get; internal set; }
+
+ /// <summary>
+ /// Gets the small image for the rich presence.
+ /// </summary>
+ public DiscordAsset SmallImage { get; internal set; }
+
+ /// <summary>
+ /// Gets the hover text for small image.
+ /// </summary>
+ public string SmallImageText { get; internal set; }
+
+ /// <summary>
+ /// Gets the current party size.
+ /// </summary>
+ public long? CurrentPartySize { get; internal set; }
+
+ /// <summary>
+ /// Gets the maximum party size.
+ /// </summary>
+ public long? MaximumPartySize { get; internal set; }
+
+ /// <summary>
+ /// Gets the party ID.
+ /// </summary>
+ public ulong? PartyId { get; internal set; }
+
+ /// <summary>
+ /// Gets the buttons.
+ /// </summary>
+ public IReadOnlyList<string> Buttons { get; internal set; }
+
+ /// <summary>
+ /// Gets the game start timestamp.
+ /// </summary>
+ public DateTimeOffset? StartTimestamp { get; internal set; }
+
+ /// <summary>
+ /// Gets the game end timestamp.
+ /// </summary>
+ public DateTimeOffset? EndTimestamp { get; internal set; }
+
+ /// <summary>
+ /// Gets the secret value enabling users to join your game.
+ /// </summary>
+ public string JoinSecret { get; internal set; }
+
+ /// <summary>
+ /// Gets the secret value enabling users to receive notifications whenever your game state changes.
+ /// </summary>
+ public string MatchSecret { get; internal set; }
+
+ /// <summary>
+ /// Gets the secret value enabling users to spectate your game.
+ /// </summary>
+ public string SpectateSecret { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordRichPresence"/> class.
+ /// </summary>
+ internal DiscordRichPresence() { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordRichPresence"/> class.
+ /// </summary>
+ /// <param name="rawGame">The raw game.</param>
+ internal DiscordRichPresence(TransportActivity rawGame)
+ {
+ this.UpdateWith(rawGame);
+ }
- /// <summary>
- /// Indicates the user is listening to something.
- /// </summary>
- ListeningTo = 2,
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordRichPresence"/> class.
+ /// </summary>
+ /// <param name="other">The other.</param>
+ internal DiscordRichPresence(DiscordRichPresence other)
+ {
+ this.Details = other.Details;
+ this.State = other.State;
+ this.Application = other.Application;
+ this.Instance = other.Instance;
+ this.LargeImageText = other.LargeImageText;
+ this.SmallImageText = other.SmallImageText;
+ this.LargeImage = other.LargeImage;
+ this.SmallImage = other.SmallImage;
+ this.CurrentPartySize = other.CurrentPartySize;
+ this.MaximumPartySize = other.MaximumPartySize;
+ this.PartyId = other.PartyId;
+ this.Buttons = other.Buttons;
+ this.StartTimestamp = other.StartTimestamp;
+ this.EndTimestamp = other.EndTimestamp;
+ this.JoinSecret = other.JoinSecret;
+ this.MatchSecret = other.MatchSecret;
+ this.SpectateSecret = other.SpectateSecret;
+ }
- /// <summary>
- /// Indicates the user is watching something.
- /// </summary>
- Watching = 3,
+ /// <summary>
+ /// Updates a game activity with an transport activity.
+ /// </summary>
+ /// <param name="rawGame">The raw game.</param>
+ internal void UpdateWith(TransportActivity rawGame)
+ {
+ this.Details = rawGame?.Details;
+ this.State = rawGame?.State;
+ this.Application = rawGame?.ApplicationId != null ? new DiscordApplication { Id = rawGame.ApplicationId.Value } : null;
+ this.Instance = rawGame?.Instance;
+ this.LargeImageText = rawGame?.Assets?.LargeImageText;
+ this.SmallImageText = rawGame?.Assets?.SmallImageText;
+ this.CurrentPartySize = rawGame?.Party?.Size?.Current;
+ this.MaximumPartySize = rawGame?.Party?.Size?.Maximum;
+ if (rawGame?.Party != null && ulong.TryParse(rawGame.Party.Id, NumberStyles.Number, CultureInfo.InvariantCulture, out var partyId))
+ this.PartyId = partyId;
+ this.Buttons = rawGame?.Buttons;
+ this.StartTimestamp = rawGame?.Timestamps?.Start;
+ this.EndTimestamp = rawGame?.Timestamps?.End;
+ this.JoinSecret = rawGame?.Secrets?.Join;
+ this.MatchSecret = rawGame?.Secrets?.Match;
+ this.SpectateSecret = rawGame?.Secrets?.Spectate;
+
+ var lid = rawGame?.Assets?.LargeImage;
+ if (lid != null)
+ {
+ if (lid.StartsWith("spotify:"))
+ this.LargeImage = new DiscordSpotifyAsset { Id = lid };
+ else if (ulong.TryParse(lid, NumberStyles.Number, CultureInfo.InvariantCulture, out var ulid))
+ this.LargeImage = new DiscordApplicationAsset { Id = lid, Application = this.Application, Type = ApplicationAssetType.LargeImage };
+ }
- /// <summary>
- /// Indicates the current activity is a custom status.
- /// </summary>
- Custom = 4,
+ var sid = rawGame?.Assets?.SmallImage;
+ if (sid != null)
+ {
+ if (sid.StartsWith("spotify:"))
+ this.SmallImage = new DiscordSpotifyAsset { Id = sid };
+ else if (ulong.TryParse(sid, NumberStyles.Number, CultureInfo.InvariantCulture, out var usid))
+ this.SmallImage = new DiscordApplicationAsset { Id = sid, Application = this.Application, Type = ApplicationAssetType.LargeImage };
+ }
+ }
+ }
/// <summary>
- /// Indicates the user is competing in something.
+ /// Determines the type of a user activity.
/// </summary>
- Competing = 5
+ public enum ActivityType
+ {
+ /// <summary>
+ /// Indicates the user is playing a game.
+ /// </summary>
+ Playing = 0,
+
+ /// <summary>
+ /// Indicates the user is streaming a game.
+ /// </summary>
+ Streaming = 1,
+
+ /// <summary>
+ /// Indicates the user is listening to something.
+ /// </summary>
+ ListeningTo = 2,
+
+ /// <summary>
+ /// Indicates the user is watching something.
+ /// </summary>
+ Watching = 3,
+
+ /// <summary>
+ /// Indicates the current activity is a custom status.
+ /// </summary>
+ Custom = 4,
+
+ /// <summary>
+ /// Indicates the user is competing in something.
+ /// </summary>
+ Competing = 5
+ }
}
diff --git a/DisCatSharp/Entities/User/DiscordConnection.cs b/DisCatSharp/Entities/User/DiscordConnection.cs
index bec1a992f..722e014bc 100644
--- a/DisCatSharp/Entities/User/DiscordConnection.cs
+++ b/DisCatSharp/Entities/User/DiscordConnection.cs
@@ -1,99 +1,100 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Gets a Discord connection to a 3rd party service.
-/// </summary>
-public class DiscordConnection
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the id of the connection account
+ /// Gets a Discord connection to a 3rd party service.
/// </summary>
- [JsonProperty("id")]
- public string Id { get; internal set; }
+ public class DiscordConnection
+ {
+ /// <summary>
+ /// Gets the id of the connection account
+ /// </summary>
+ [JsonProperty("id")]
+ public string Id { get; internal set; }
- /// <summary>
- /// Gets the username of the connection account.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; internal set; }
+ /// <summary>
+ /// Gets the username of the connection account.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the service of the connection (twitch, youtube, steam, twitter, facebook, spotify, leagueoflegends, reddit)
- /// </summary>
- [JsonProperty("type")]
- public string Type { get; internal set; }
+ /// <summary>
+ /// Gets the service of the connection (twitch, youtube, steam, twitter, facebook, spotify, leagueoflegends, reddit)
+ /// </summary>
+ [JsonProperty("type")]
+ public string Type { get; internal set; }
- /// <summary>
- /// Gets whether the connection is revoked.
- /// </summary>
- [JsonProperty("revoked")]
- public bool IsRevoked { get; internal set; }
+ /// <summary>
+ /// Gets whether the connection is revoked.
+ /// </summary>
+ [JsonProperty("revoked")]
+ public bool IsRevoked { get; internal set; }
- /// <summary>
- /// Gets a collection of partial server integrations.
- /// </summary>
- [JsonProperty("integrations", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<DiscordIntegration> Integrations { get; internal set; }
+ /// <summary>
+ /// Gets a collection of partial server integrations.
+ /// </summary>
+ [JsonProperty("integrations", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<DiscordIntegration> Integrations { get; internal set; }
- /// <summary>
- /// Gets whether the connection is verified.
- /// </summary>
- [JsonProperty("verified", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Verified { get; internal set; }
+ /// <summary>
+ /// Gets whether the connection is verified.
+ /// </summary>
+ [JsonProperty("verified", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Verified { get; internal set; }
- /// <summary>
- /// Gets whether the connection will show a activity.
- /// </summary>
- [JsonProperty("show_activity", NullValueHandling = NullValueHandling.Ignore)]
- public bool? ShowActivity { get; internal set; }
+ /// <summary>
+ /// Gets whether the connection will show a activity.
+ /// </summary>
+ [JsonProperty("show_activity", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? ShowActivity { get; internal set; }
- /// <summary>
- /// Whether the connection will sync friends.
- /// </summary>
- [JsonProperty("friend_sync", NullValueHandling = NullValueHandling.Ignore)]
- public bool? FriendSync { get; internal set; }
+ /// <summary>
+ /// Whether the connection will sync friends.
+ /// </summary>
+ [JsonProperty("friend_sync", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? FriendSync { get; internal set; }
- /// <summary>
- /// Gets the visibility of the connection.
- /// </summary>
- [JsonProperty("visibility", NullValueHandling = NullValueHandling.Ignore)]
- public long? Visibility { get; internal set; }
+ /// <summary>
+ /// Gets the visibility of the connection.
+ /// </summary>
+ [JsonProperty("visibility", NullValueHandling = NullValueHandling.Ignore)]
+ public long? Visibility { get; internal set; }
- /// <summary>
- /// Gets the client instance this object is tied to.
- /// </summary>
- [JsonIgnore]
- internal BaseDiscordClient Discord { get; set; }
+ /// <summary>
+ /// Gets the client instance this object is tied to.
+ /// </summary>
+ [JsonIgnore]
+ internal BaseDiscordClient Discord { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordConnection"/> class.
- /// </summary>
- internal DiscordConnection()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordConnection"/> class.
+ /// </summary>
+ internal DiscordConnection()
+ { }
+ }
}
diff --git a/DisCatSharp/Entities/User/DiscordPresence.cs b/DisCatSharp/Entities/User/DiscordPresence.cs
index e31a8210d..fff12a2bc 100644
--- a/DisCatSharp/Entities/User/DiscordPresence.cs
+++ b/DisCatSharp/Entities/User/DiscordPresence.cs
@@ -1,150 +1,151 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Net.Abstractions;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a user presence.
-/// </summary>
-public sealed class DiscordPresence
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the discord client.
- /// </summary>
- [JsonIgnore]
- internal DiscordClient Discord { get; set; }
-
- /// <summary>
- /// Gets the internal user.
- /// </summary>
- [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
- internal TransportUser InternalUser { get; set; }
-
- /// <summary>
- /// Gets the user that owns this presence.
- /// </summary>
- [JsonIgnore]
- public DiscordUser User
- => this.Discord.GetCachedOrEmptyUserInternal(this.InternalUser.Id);
-
- /// <summary>
- /// Gets the user's current activity.
- /// </summary>
- [JsonIgnore]
- public DiscordActivity Activity { get; internal set; }
-
- /// <summary>
- /// Gets the raw activity.
- /// </summary>
- internal TransportActivity RawActivity { get; set; }
-
- /// <summary>
- /// Gets the user's current activities.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordActivity> Activities => this.InternalActivities;
-
- [JsonIgnore]
- internal DiscordActivity[] InternalActivities;
-
- /// <summary>
- /// Gets the raw activities.
- /// </summary>
- [JsonProperty("activities", NullValueHandling = NullValueHandling.Ignore)]
- internal TransportActivity[] RawActivities { get; set; }
-
- /// <summary>
- /// Gets this user's status.
- /// </summary>
- [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
- public UserStatus Status { get; internal set; }
-
- /// <summary>
- /// Gets the guild id for which this presence was set.
- /// </summary>
- [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong GuildId { get; set; }
-
- /// <summary>
- /// Gets the guild for which this presence was set.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild
- => this.GuildId != 0 ? this.Discord.GuildsInternal[this.GuildId] : null;
-
- /// <summary>
- /// Gets this user's platform-dependent status.
- /// </summary>
- [JsonProperty("client_status", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordClientStatus ClientStatus { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordPresence"/> class.
- /// </summary>
- internal DiscordPresence()
- { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordPresence"/> class.
+ /// Represents a user presence.
/// </summary>
- /// <param name="other">The other.</param>
- internal DiscordPresence(DiscordPresence other)
+ public sealed class DiscordPresence
{
- this.Discord = other.Discord;
- this.Activity = other.Activity;
- this.RawActivity = other.RawActivity;
- this.InternalActivities = (DiscordActivity[])other.InternalActivities?.Clone();
- this.RawActivities = (TransportActivity[])other.RawActivities?.Clone();
- this.Status = other.Status;
- this.InternalUser = new TransportUser(other.InternalUser);
+ /// <summary>
+ /// Gets the discord client.
+ /// </summary>
+ [JsonIgnore]
+ internal DiscordClient Discord { get; set; }
+
+ /// <summary>
+ /// Gets the internal user.
+ /// </summary>
+ [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
+ internal TransportUser InternalUser { get; set; }
+
+ /// <summary>
+ /// Gets the user that owns this presence.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordUser User
+ => this.Discord.GetCachedOrEmptyUserInternal(this.InternalUser.Id);
+
+ /// <summary>
+ /// Gets the user's current activity.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordActivity Activity { get; internal set; }
+
+ /// <summary>
+ /// Gets the raw activity.
+ /// </summary>
+ internal TransportActivity RawActivity { get; set; }
+
+ /// <summary>
+ /// Gets the user's current activities.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordActivity> Activities => this.InternalActivities;
+
+ [JsonIgnore]
+ internal DiscordActivity[] InternalActivities;
+
+ /// <summary>
+ /// Gets the raw activities.
+ /// </summary>
+ [JsonProperty("activities", NullValueHandling = NullValueHandling.Ignore)]
+ internal TransportActivity[] RawActivities { get; set; }
+
+ /// <summary>
+ /// Gets this user's status.
+ /// </summary>
+ [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
+ public UserStatus Status { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild id for which this presence was set.
+ /// </summary>
+ [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong GuildId { get; set; }
+
+ /// <summary>
+ /// Gets the guild for which this presence was set.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild
+ => this.GuildId != 0 ? this.Discord.GuildsInternal[this.GuildId] : null;
+
+ /// <summary>
+ /// Gets this user's platform-dependent status.
+ /// </summary>
+ [JsonProperty("client_status", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordClientStatus ClientStatus { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordPresence"/> class.
+ /// </summary>
+ internal DiscordPresence()
+ { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordPresence"/> class.
+ /// </summary>
+ /// <param name="other">The other.</param>
+ internal DiscordPresence(DiscordPresence other)
+ {
+ this.Discord = other.Discord;
+ this.Activity = other.Activity;
+ this.RawActivity = other.RawActivity;
+ this.InternalActivities = (DiscordActivity[])other.InternalActivities?.Clone();
+ this.RawActivities = (TransportActivity[])other.RawActivities?.Clone();
+ this.Status = other.Status;
+ this.InternalUser = new TransportUser(other.InternalUser);
+ }
}
-}
-
-/// <summary>
-/// Represents a client status.
-/// </summary>
-public sealed class DiscordClientStatus
-{
- /// <summary>
- /// Gets the user's status set for an active desktop (Windows, Linux, Mac) application session.
- /// </summary>
- [JsonProperty("desktop", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Populate)]
- public Optional<UserStatus> Desktop { get; internal set; } = UserStatus.Offline;
-
- /// <summary>
- /// Gets the user's status set for an active mobile (iOS, Android) application session.
- /// </summary>
- [JsonProperty("mobile", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Populate)]
- public Optional<UserStatus> Mobile { get; internal set; } = UserStatus.Offline;
/// <summary>
- /// Gets the user's status set for an active web (browser, bot account) application session.
+ /// Represents a client status.
/// </summary>
- [JsonProperty("web", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Populate)]
- public Optional<UserStatus> Web { get; internal set; } = UserStatus.Offline;
+ public sealed class DiscordClientStatus
+ {
+ /// <summary>
+ /// Gets the user's status set for an active desktop (Windows, Linux, Mac) application session.
+ /// </summary>
+ [JsonProperty("desktop", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Populate)]
+ public Optional<UserStatus> Desktop { get; internal set; } = UserStatus.Offline;
+
+ /// <summary>
+ /// Gets the user's status set for an active mobile (iOS, Android) application session.
+ /// </summary>
+ [JsonProperty("mobile", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Populate)]
+ public Optional<UserStatus> Mobile { get; internal set; } = UserStatus.Offline;
+
+ /// <summary>
+ /// Gets the user's status set for an active web (browser, bot account) application session.
+ /// </summary>
+ [JsonProperty("web", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Populate)]
+ public Optional<UserStatus> Web { get; internal set; } = UserStatus.Offline;
+ }
}
diff --git a/DisCatSharp/Entities/User/DiscordTeam.cs b/DisCatSharp/Entities/User/DiscordTeam.cs
index 86f294b94..45ea9248e 100644
--- a/DisCatSharp/Entities/User/DiscordTeam.cs
+++ b/DisCatSharp/Entities/User/DiscordTeam.cs
@@ -1,202 +1,203 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a team consisting of users. A team can own an application.
-/// </summary>
-public sealed class DiscordTeam : SnowflakeObject, IEquatable<DiscordTeam>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the team's name.
+ /// Represents a team consisting of users. A team can own an application.
/// </summary>
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets the team's icon hash.
- /// </summary>
- public string IconHash { get; internal set; }
-
- /// <summary>
- /// Gets the team's icon.
- /// </summary>
- public string Icon
- => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.TEAM_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets the owner of the team.
- /// </summary>
- public DiscordUser Owner { get; internal set; }
-
- /// <summary>
- /// Gets the members of this team.
- /// </summary>
- public IReadOnlyList<DiscordTeamMember> Members { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordTeam"/> class.
- /// </summary>
- /// <param name="tt">The tt.</param>
- internal DiscordTeam(TransportTeam tt)
+ public sealed class DiscordTeam : SnowflakeObject, IEquatable<DiscordTeam>
{
- this.Id = tt.Id;
- this.Name = tt.Name;
- this.IconHash = tt.IconHash;
+ /// <summary>
+ /// Gets the team's name.
+ /// </summary>
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's icon hash.
+ /// </summary>
+ public string IconHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the team's icon.
+ /// </summary>
+ public string Icon
+ => !string.IsNullOrWhiteSpace(this.IconHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.TEAM_ICONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.IconHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets the owner of the team.
+ /// </summary>
+ public DiscordUser Owner { get; internal set; }
+
+ /// <summary>
+ /// Gets the members of this team.
+ /// </summary>
+ public IReadOnlyList<DiscordTeamMember> Members { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordTeam"/> class.
+ /// </summary>
+ /// <param name="tt">The tt.</param>
+ internal DiscordTeam(TransportTeam tt)
+ {
+ this.Id = tt.Id;
+ this.Name = tt.Name;
+ this.IconHash = tt.IconHash;
+ }
+
+ /// <summary>
+ /// Compares this team to another object and returns whether they are equal.
+ /// </summary>
+ /// <param name="obj">Object to compare this team to.</param>
+ /// <returns>Whether this team is equal to the given object.</returns>
+ public override bool Equals(object obj)
+ => obj is DiscordTeam other && this == other;
+
+ /// <summary>
+ /// Compares this team to another team and returns whether they are equal.
+ /// </summary>
+ /// <param name="other">Team to compare to.</param>
+ /// <returns>Whether the teams are equal.</returns>
+ public bool Equals(DiscordTeam other)
+ => this == other;
+
+ /// <summary>
+ /// Gets the hash code of this team.
+ /// </summary>
+ /// <returns>Hash code of this team.</returns>
+ public override int GetHashCode()
+ => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Converts this team to its string representation.
+ /// </summary>
+ /// <returns>The string representation of this team.</returns>
+ public override string ToString()
+ => $"Team: {this.Name} ({this.Id})";
+
+ public static bool operator ==(DiscordTeam left, DiscordTeam right)
+ => left?.Id == right?.Id;
+
+ public static bool operator !=(DiscordTeam left, DiscordTeam right)
+ => left?.Id != right?.Id;
}
/// <summary>
- /// Compares this team to another object and returns whether they are equal.
- /// </summary>
- /// <param name="obj">Object to compare this team to.</param>
- /// <returns>Whether this team is equal to the given object.</returns>
- public override bool Equals(object obj)
- => obj is DiscordTeam other && this == other;
-
- /// <summary>
- /// Compares this team to another team and returns whether they are equal.
- /// </summary>
- /// <param name="other">Team to compare to.</param>
- /// <returns>Whether the teams are equal.</returns>
- public bool Equals(DiscordTeam other)
- => this == other;
-
- /// <summary>
- /// Gets the hash code of this team.
- /// </summary>
- /// <returns>Hash code of this team.</returns>
- public override int GetHashCode()
- => this.Id.GetHashCode();
-
- /// <summary>
- /// Converts this team to its string representation.
- /// </summary>
- /// <returns>The string representation of this team.</returns>
- public override string ToString()
- => $"Team: {this.Name} ({this.Id})";
-
- public static bool operator ==(DiscordTeam left, DiscordTeam right)
- => left?.Id == right?.Id;
-
- public static bool operator !=(DiscordTeam left, DiscordTeam right)
- => left?.Id != right?.Id;
-}
-
-/// <summary>
-/// Represents a member of <see cref="DiscordTeam"/>.
-/// </summary>
-public sealed class DiscordTeamMember : IEquatable<DiscordTeamMember>
-{
- /// <summary>
- /// Gets the member's membership status.
- /// </summary>
- public DiscordTeamMembershipStatus MembershipStatus { get; internal set; }
-
- /// <summary>
- /// Gets the member's permissions within the team.
+ /// Represents a member of <see cref="DiscordTeam"/>.
/// </summary>
- public IReadOnlyCollection<string> Permissions { get; internal set; }
-
- /// <summary>
- /// Gets the id of the team this member belongs to.
- /// </summary>
- public ulong? TeamId { get; internal set; }
-
- /// <summary>
- /// Gets the name of the team this member belongs to.
- /// </summary>
- public string TeamName { get; internal set; }
-
- /// <summary>
- /// Gets the user who is the team member.
- /// </summary>
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordTeamMember"/> class.
- /// </summary>
- /// <param name="ttm">The ttm.</param>
- internal DiscordTeamMember(TransportTeamMember ttm)
+ public sealed class DiscordTeamMember : IEquatable<DiscordTeamMember>
{
- this.MembershipStatus = (DiscordTeamMembershipStatus)ttm.MembershipState;
- this.Permissions = new ReadOnlySet<string>(new HashSet<string>(ttm.Permissions));
+ /// <summary>
+ /// Gets the member's membership status.
+ /// </summary>
+ public DiscordTeamMembershipStatus MembershipStatus { get; internal set; }
+
+ /// <summary>
+ /// Gets the member's permissions within the team.
+ /// </summary>
+ public IReadOnlyCollection<string> Permissions { get; internal set; }
+
+ /// <summary>
+ /// Gets the id of the team this member belongs to.
+ /// </summary>
+ public ulong? TeamId { get; internal set; }
+
+ /// <summary>
+ /// Gets the name of the team this member belongs to.
+ /// </summary>
+ public string TeamName { get; internal set; }
+
+ /// <summary>
+ /// Gets the user who is the team member.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordTeamMember"/> class.
+ /// </summary>
+ /// <param name="ttm">The ttm.</param>
+ internal DiscordTeamMember(TransportTeamMember ttm)
+ {
+ this.MembershipStatus = (DiscordTeamMembershipStatus)ttm.MembershipState;
+ this.Permissions = new ReadOnlySet<string>(new HashSet<string>(ttm.Permissions));
+ }
+
+ /// <summary>
+ /// Compares this team member to another object and returns whether they are equal.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether this team is equal to given object.</returns>
+ public override bool Equals(object obj)
+ => obj is DiscordTeamMember other && this == other;
+
+ /// <summary>
+ /// Compares this team member to another team member and returns whether they are equal.
+ /// </summary>
+ /// <param name="other">Team member to compare to.</param>
+ /// <returns>Whether this team member is equal to the given one.</returns>
+ public bool Equals(DiscordTeamMember other)
+ => this == other;
+
+ /// <summary>
+ /// Gets a hash code of this team member.
+ /// </summary>
+ /// <returns>Hash code of this team member.</returns>
+ public override int GetHashCode() => HashCode.Combine(this.User, this.TeamId);
+
+ /// <summary>
+ /// Converts this team member to their string representation.
+ /// </summary>
+ /// <returns>String representation of this team member.</returns>
+ public override string ToString()
+ => $"Team member: {this.User.Username}#{this.User.Discriminator} ({this.User.Id}), part of team {this.TeamName} ({this.TeamId})";
+
+ public static bool operator ==(DiscordTeamMember left, DiscordTeamMember right)
+ => left?.TeamId == right?.TeamId && left?.User?.Id == right?.User?.Id;
+
+ public static bool operator !=(DiscordTeamMember left, DiscordTeamMember right)
+ => left?.TeamId != right?.TeamId || left?.User?.Id != right?.User?.Id;
}
/// <summary>
- /// Compares this team member to another object and returns whether they are equal.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether this team is equal to given object.</returns>
- public override bool Equals(object obj)
- => obj is DiscordTeamMember other && this == other;
-
- /// <summary>
- /// Compares this team member to another team member and returns whether they are equal.
- /// </summary>
- /// <param name="other">Team member to compare to.</param>
- /// <returns>Whether this team member is equal to the given one.</returns>
- public bool Equals(DiscordTeamMember other)
- => this == other;
-
- /// <summary>
- /// Gets a hash code of this team member.
- /// </summary>
- /// <returns>Hash code of this team member.</returns>
- public override int GetHashCode() => HashCode.Combine(this.User, this.TeamId);
-
- /// <summary>
- /// Converts this team member to their string representation.
+ /// Signifies the status of user's team membership.
/// </summary>
- /// <returns>String representation of this team member.</returns>
- public override string ToString()
- => $"Team member: {this.User.Username}#{this.User.Discriminator} ({this.User.Id}), part of team {this.TeamName} ({this.TeamId})";
-
- public static bool operator ==(DiscordTeamMember left, DiscordTeamMember right)
- => left?.TeamId == right?.TeamId && left?.User?.Id == right?.User?.Id;
-
- public static bool operator !=(DiscordTeamMember left, DiscordTeamMember right)
- => left?.TeamId != right?.TeamId || left?.User?.Id != right?.User?.Id;
-}
-
-/// <summary>
-/// Signifies the status of user's team membership.
-/// </summary>
-public enum DiscordTeamMembershipStatus : int
-{
- /// <summary>
- /// Indicates that this user is invited to the team, and is pending membership.
- /// </summary>
- Invited = 1,
-
- /// <summary>
- /// Indicates that this user is a member of the team.
- /// </summary>
- Accepted = 2
+ public enum DiscordTeamMembershipStatus : int
+ {
+ /// <summary>
+ /// Indicates that this user is invited to the team, and is pending membership.
+ /// </summary>
+ Invited = 1,
+
+ /// <summary>
+ /// Indicates that this user is a member of the team.
+ /// </summary>
+ Accepted = 2
+ }
}
diff --git a/DisCatSharp/Entities/User/DiscordUser.cs b/DisCatSharp/Entities/User/DiscordUser.cs
index ed88b96f6..033f31a36 100644
--- a/DisCatSharp/Entities/User/DiscordUser.cs
+++ b/DisCatSharp/Entities/User/DiscordUser.cs
@@ -1,458 +1,459 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Exceptions;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord user.
-/// </summary>
-public class DiscordUser : SnowflakeObject, IEquatable<DiscordUser>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordUser"/> class.
- /// </summary>
- internal DiscordUser()
- { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordUser"/> class.
- /// </summary>
- /// <param name="transport">The transport user.</param>
- internal DiscordUser(TransportUser transport)
- {
- this.Id = transport.Id;
- this.Username = transport.Username;
- this.Discriminator = transport.Discriminator;
- this.AvatarHash = transport.AvatarHash;
- this.BannerHash = transport.BannerHash;
- this.BannerColorInternal = transport.BannerColor;
- this.IsBot = transport.IsBot;
- this.MfaEnabled = transport.MfaEnabled;
- this.Verified = transport.Verified;
- this.Email = transport.Email;
- this.PremiumType = transport.PremiumType;
- this.Locale = transport.Locale;
- this.Flags = transport.Flags;
- this.OAuthFlags = transport.OAuthFlags;
- this.Bio = transport.Bio;
- }
-
- /// <summary>
- /// Gets this user's username.
- /// </summary>
- [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
- public virtual string Username { get; internal set; }
-
- /// <summary>
- /// Gets this user's username with the discriminator.
- /// Example: Discord#0000
- /// </summary>
- [JsonIgnore]
- public virtual string UsernameWithDiscriminator
- => $"{this.Username}#{this.Discriminator}";
-
- /// <summary>
- /// Gets the user's 4-digit discriminator.
- /// </summary>
- [JsonProperty("discriminator", NullValueHandling = NullValueHandling.Ignore)]
- public virtual string Discriminator { get; internal set; }
-
- /// <summary>
- /// Gets the discriminator integer.
- /// </summary>
- [JsonIgnore]
- internal int DiscriminatorInt
- => int.Parse(this.Discriminator, NumberStyles.Integer, CultureInfo.InvariantCulture);
-
- /// <summary>
- /// Gets the user's banner color, if set. Mutually exclusive with <see cref="BannerHash"/>.
- /// </summary>
- public virtual DiscordColor? BannerColor
- => !this.BannerColorInternal.HasValue ? null : new DiscordColor(this.BannerColorInternal.Value);
-
- /// <summary>
- /// Gets the user's banner color integer.
- /// </summary>
- [JsonProperty("accent_color")]
- internal int? BannerColorInternal;
-
- /// <summary>
- /// Gets the user's banner url
- /// </summary>
- [JsonIgnore]
- public string BannerUrl
- => string.IsNullOrWhiteSpace(this.BannerHash) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.BANNERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.BannerHash}.{(this.BannerHash.StartsWith("a_") ? "gif" : "png")}?size=4096";
-
- /// <summary>
- /// Gets the user's profile banner hash. Mutually exclusive with <see cref="BannerColor"/>.
- /// </summary>
- [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
- public virtual string BannerHash { get; internal set; }
-
- /// <summary>
- /// Gets the users bio.
- /// This is not available to bots tho.
- /// </summary>
- [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
- public virtual string Bio { get; internal set; }
-
- /// <summary>
- /// Gets the user's avatar hash.
- /// </summary>
- [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
- public virtual string AvatarHash { get; internal set; }
-
- /// <summary>
- /// Returns a uri to this users profile.
- /// </summary>
- [JsonIgnore]
- public Uri ProfileUri => new($"{DiscordDomain.GetDomain(CoreDomain.Discord).Url}{Endpoints.USERS}/{this.Id}");
-
- /// <summary>
- /// Returns a string representing the direct URL to this users profile.
- /// </summary>
- /// <returns>The URL of this users profile.</returns>
- [JsonIgnore]
- public string ProfileUrl => this.ProfileUri.AbsoluteUri;
-
- /// <summary>
- /// Gets the user's avatar URL.s
- /// </summary>
- [JsonIgnore]
- public string AvatarUrl
- => string.IsNullOrWhiteSpace(this.AvatarHash) ? this.DefaultAvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.AvatarHash}.{(this.AvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
-
- /// <summary>
- /// Gets the URL of default avatar for this user.
- /// </summary>
- [JsonIgnore]
- public string DefaultAvatarUrl
- => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMBED}{Endpoints.AVATARS}/{(this.DiscriminatorInt % 5).ToString(CultureInfo.InvariantCulture)}.png?size=1024";
-
- /// <summary>
- /// Gets whether the user is a bot.
- /// </summary>
- [JsonProperty("bot", NullValueHandling = NullValueHandling.Ignore)]
- public virtual bool IsBot { get; internal set; }
-
- /// <summary>
- /// Gets whether the user has multi-factor authentication enabled.
- /// </summary>
- [JsonProperty("mfa_enabled", NullValueHandling = NullValueHandling.Ignore)]
- public virtual bool? MfaEnabled { get; internal set; }
-
- /// <summary>
- /// Gets whether the user is an official Discord system user.
- /// </summary>
- [JsonProperty("system", NullValueHandling = NullValueHandling.Ignore)]
- public bool? IsSystem { get; internal set; }
-
- /// <summary>
- /// Gets whether the user is verified.
- /// <para>This is only present in OAuth.</para>
- /// </summary>
- [JsonProperty("verified", NullValueHandling = NullValueHandling.Ignore)]
- public virtual bool? Verified { get; internal set; }
-
- /// <summary>
- /// Gets the user's email address.
- /// <para>This is only present in OAuth.</para>
- /// </summary>
- [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)]
- public virtual string Email { get; internal set; }
-
- /// <summary>
- /// Gets the user's premium type.
- /// </summary>
- [JsonProperty("premium_type", NullValueHandling = NullValueHandling.Ignore)]
- public virtual PremiumType? PremiumType { get; internal set; }
-
- /// <summary>
- /// Gets the user's chosen language
- /// </summary>
- [JsonProperty("locale", NullValueHandling = NullValueHandling.Ignore)]
- public virtual string Locale { get; internal set; }
-
- /// <summary>
- /// Gets the user's flags for OAuth.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public virtual UserFlags? OAuthFlags { get; internal set; }
-
- /// <summary>
- /// Gets the user's flags.
- /// </summary>
- [JsonProperty("public_flags", NullValueHandling = NullValueHandling.Ignore)]
- public virtual UserFlags? Flags { get; internal set; }
-
- /// <summary>
- /// Gets the user's mention string.
- /// </summary>
- [JsonIgnore]
- public string Mention
- => Formatter.Mention(this, this is DiscordMember);
-
- /// <summary>
- /// Gets whether this user is the Client which created this object.
- /// </summary>
- [JsonIgnore]
- public bool IsCurrent
- => this.Id == this.Discord.CurrentUser.Id;
-
- #region Extension of DiscordUser
-
- /// <summary>
- /// Whether this member is a <see cref="UserFlags.CertifiedModerator"/>
- /// </summary>
- /// <returns><see cref="bool"/></returns>
- [JsonIgnore]
- public bool IsMod
- => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.CertifiedModerator);
-
- /// <summary>
- /// Whether this member is a <see cref="UserFlags.Partner"/>
- /// </summary>
- /// <returns><see cref="bool"/></returns>
- [JsonIgnore]
- public bool IsPartner
- => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.Partner);
-
- /// <summary>
- /// Whether this member is a <see cref="UserFlags.VerifiedBot"/>
- /// </summary>
- /// <returns><see cref="bool"/></returns>
- [JsonIgnore]
- public bool IsVerifiedBot
- => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.VerifiedBot);
-
- /// <summary>
- /// Whether this member is a <see cref="UserFlags.VerifiedDeveloper"/>
- /// </summary>
- /// <returns><see cref="bool"/></returns>
- [JsonIgnore]
- public bool IsBotDev
- => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.VerifiedDeveloper);
-
- /// <summary>
- /// Whether this member is a <see cref="UserFlags.Staff"/>
- /// </summary>
- /// <returns><see cref="bool"/></returns>
- [JsonIgnore]
- public bool IsStaff
- => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.Staff);
-
- #endregion
-
- /// <summary>
- /// Fetches the user from the API.
- /// </summary>
- /// <returns>The user with fresh data from the API.</returns>
- public async Task<DiscordUser> GetFromApiAsync()
- => await this.Discord.ApiClient.GetUserAsync(this.Id);
-
- /// <summary>
- /// Whether this user is in a <see cref="DiscordGuild"/>
+ /// Represents a Discord user.
/// </summary>
- /// <example>
- /// <code>
- /// DiscordGuild guild = await Client.GetGuildAsync(806675511555915806);
- /// DiscordUser user = await Client.GetUserAsync(469957180968271873);
- /// Console.WriteLine($"{user.Username} {(user.IsInGuild(guild) ? "is a" : "is not a")} member of {guild.Name}");
- /// </code>
- /// results to <c>J_M_Lutra is a member of Project Nyaw~</c>.
- /// </example>
- /// <param name="guild"><see cref="DiscordGuild"/></param>
- /// <returns><see cref="bool"/></returns>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
- public async Task<bool> IsInGuild(DiscordGuild guild)
+ public class DiscordUser : SnowflakeObject, IEquatable<DiscordUser>
{
- try
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordUser"/> class.
+ /// </summary>
+ internal DiscordUser()
+ { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordUser"/> class.
+ /// </summary>
+ /// <param name="transport">The transport user.</param>
+ internal DiscordUser(TransportUser transport)
{
- var member = await guild.GetMemberAsync(this.Id);
- return member is not null;
-
+ this.Id = transport.Id;
+ this.Username = transport.Username;
+ this.Discriminator = transport.Discriminator;
+ this.AvatarHash = transport.AvatarHash;
+ this.BannerHash = transport.BannerHash;
+ this.BannerColorInternal = transport.BannerColor;
+ this.IsBot = transport.IsBot;
+ this.MfaEnabled = transport.MfaEnabled;
+ this.Verified = transport.Verified;
+ this.Email = transport.Email;
+ this.PremiumType = transport.PremiumType;
+ this.Locale = transport.Locale;
+ this.Flags = transport.Flags;
+ this.OAuthFlags = transport.OAuthFlags;
+ this.Bio = transport.Bio;
}
- catch (NotFoundException)
- {
- return false;
- }
- }
-
- /// <summary>
- /// Whether this user is not in a <see cref="DiscordGuild"/>
- /// </summary>
- /// <param name="guild"><see cref="DiscordGuild"/></param>
- /// <returns><see cref="bool"/></returns>
- public async Task<bool> IsNotInGuild(DiscordGuild guild)
- => !await this.IsInGuild(guild);
-
- /// <summary>
- /// Returns the DiscordMember in the specified <see cref="DiscordGuild"/>
- /// </summary>
- /// <param name="guild">The <see cref="DiscordGuild"/> to get this user on.</param>
- /// <returns>The <see cref="DiscordMember"/>.</returns>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the user is not part of the guild.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMember> ConvertToMember(DiscordGuild guild)
- => await guild.GetMemberAsync(this.Id);
-
- /// <summary>
- /// Unbans this user from a guild.
- /// </summary>
- /// <param name="guild">Guild to unban this user from.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the user does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task UnbanAsync(DiscordGuild guild, string reason = null)
- => guild.UnbanMemberAsync(this, reason);
-
- /// <summary>
- /// Gets this user's presence.
- /// </summary>
- [JsonIgnore]
- public DiscordPresence Presence
- => this.Discord is DiscordClient dc && dc.Presences.TryGetValue(this.Id, out var presence) ? presence : null;
-
- /// <summary>
- /// Gets the user's avatar URL, in requested format and size.
- /// </summary>
- /// <param name="fmt">Format of the avatar to get.</param>
- /// <param name="size">Maximum size of the avatar. Must be a power of two, minimum 16, maximum 2048.</param>
- /// <returns>URL of the user's avatar.</returns>
- public string GetAvatarUrl(ImageFormat fmt, ushort size = 1024)
- {
- if (fmt == ImageFormat.Unknown)
- throw new ArgumentException("You must specify valid image format.", nameof(fmt));
-
- if (size < 16 || size > 2048)
- throw new ArgumentOutOfRangeException(nameof(size));
- var log = Math.Log(size, 2);
- if (log < 4 || log > 11 || log % 1 != 0)
- throw new ArgumentOutOfRangeException(nameof(size));
-
- var sfmt = "";
- sfmt = fmt switch
- {
- ImageFormat.Gif => "gif",
- ImageFormat.Jpeg => "jpg",
- ImageFormat.Png => "png",
- ImageFormat.WebP => "webp",
- ImageFormat.Auto => !string.IsNullOrWhiteSpace(this.AvatarHash) ? this.AvatarHash.StartsWith("a_") ? "gif" : "png" : "png",
- _ => throw new ArgumentOutOfRangeException(nameof(fmt)),
- };
- var ssize = size.ToString(CultureInfo.InvariantCulture);
- if (!string.IsNullOrWhiteSpace(this.AvatarHash))
+ /// <summary>
+ /// Gets this user's username.
+ /// </summary>
+ [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string Username { get; internal set; }
+
+ /// <summary>
+ /// Gets this user's username with the discriminator.
+ /// Example: Discord#0000
+ /// </summary>
+ [JsonIgnore]
+ public virtual string UsernameWithDiscriminator
+ => $"{this.Username}#{this.Discriminator}";
+
+ /// <summary>
+ /// Gets the user's 4-digit discriminator.
+ /// </summary>
+ [JsonProperty("discriminator", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string Discriminator { get; internal set; }
+
+ /// <summary>
+ /// Gets the discriminator integer.
+ /// </summary>
+ [JsonIgnore]
+ internal int DiscriminatorInt
+ => int.Parse(this.Discriminator, NumberStyles.Integer, CultureInfo.InvariantCulture);
+
+ /// <summary>
+ /// Gets the user's banner color, if set. Mutually exclusive with <see cref="BannerHash"/>.
+ /// </summary>
+ public virtual DiscordColor? BannerColor
+ => !this.BannerColorInternal.HasValue ? null : new DiscordColor(this.BannerColorInternal.Value);
+
+ /// <summary>
+ /// Gets the user's banner color integer.
+ /// </summary>
+ [JsonProperty("accent_color")]
+ internal int? BannerColorInternal;
+
+ /// <summary>
+ /// Gets the user's banner url
+ /// </summary>
+ [JsonIgnore]
+ public string BannerUrl
+ => string.IsNullOrWhiteSpace(this.BannerHash) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.BANNERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.BannerHash}.{(this.BannerHash.StartsWith("a_") ? "gif" : "png")}?size=4096";
+
+ /// <summary>
+ /// Gets the user's profile banner hash. Mutually exclusive with <see cref="BannerColor"/>.
+ /// </summary>
+ [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string BannerHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the users bio.
+ /// This is not available to bots tho.
+ /// </summary>
+ [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string Bio { get; internal set; }
+
+ /// <summary>
+ /// Gets the user's avatar hash.
+ /// </summary>
+ [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string AvatarHash { get; internal set; }
+
+ /// <summary>
+ /// Returns a uri to this users profile.
+ /// </summary>
+ [JsonIgnore]
+ public Uri ProfileUri => new($"{DiscordDomain.GetDomain(CoreDomain.Discord).Url}{Endpoints.USERS}/{this.Id}");
+
+ /// <summary>
+ /// Returns a string representing the direct URL to this users profile.
+ /// </summary>
+ /// <returns>The URL of this users profile.</returns>
+ [JsonIgnore]
+ public string ProfileUrl => this.ProfileUri.AbsoluteUri;
+
+ /// <summary>
+ /// Gets the user's avatar URL.s
+ /// </summary>
+ [JsonIgnore]
+ public string AvatarUrl
+ => string.IsNullOrWhiteSpace(this.AvatarHash) ? this.DefaultAvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.AvatarHash}.{(this.AvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
+
+ /// <summary>
+ /// Gets the URL of default avatar for this user.
+ /// </summary>
+ [JsonIgnore]
+ public string DefaultAvatarUrl
+ => $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMBED}{Endpoints.AVATARS}/{(this.DiscriminatorInt % 5).ToString(CultureInfo.InvariantCulture)}.png?size=1024";
+
+ /// <summary>
+ /// Gets whether the user is a bot.
+ /// </summary>
+ [JsonProperty("bot", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual bool IsBot { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the user has multi-factor authentication enabled.
+ /// </summary>
+ [JsonProperty("mfa_enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual bool? MfaEnabled { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the user is an official Discord system user.
+ /// </summary>
+ [JsonProperty("system", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsSystem { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the user is verified.
+ /// <para>This is only present in OAuth.</para>
+ /// </summary>
+ [JsonProperty("verified", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual bool? Verified { get; internal set; }
+
+ /// <summary>
+ /// Gets the user's email address.
+ /// <para>This is only present in OAuth.</para>
+ /// </summary>
+ [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string Email { get; internal set; }
+
+ /// <summary>
+ /// Gets the user's premium type.
+ /// </summary>
+ [JsonProperty("premium_type", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual PremiumType? PremiumType { get; internal set; }
+
+ /// <summary>
+ /// Gets the user's chosen language
+ /// </summary>
+ [JsonProperty("locale", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string Locale { get; internal set; }
+
+ /// <summary>
+ /// Gets the user's flags for OAuth.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual UserFlags? OAuthFlags { get; internal set; }
+
+ /// <summary>
+ /// Gets the user's flags.
+ /// </summary>
+ [JsonProperty("public_flags", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual UserFlags? Flags { get; internal set; }
+
+ /// <summary>
+ /// Gets the user's mention string.
+ /// </summary>
+ [JsonIgnore]
+ public string Mention
+ => Formatter.Mention(this, this is DiscordMember);
+
+ /// <summary>
+ /// Gets whether this user is the Client which created this object.
+ /// </summary>
+ [JsonIgnore]
+ public bool IsCurrent
+ => this.Id == this.Discord.CurrentUser.Id;
+
+ #region Extension of DiscordUser
+
+ /// <summary>
+ /// Whether this member is a <see cref="UserFlags.CertifiedModerator"/>
+ /// </summary>
+ /// <returns><see cref="bool"/></returns>
+ [JsonIgnore]
+ public bool IsMod
+ => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.CertifiedModerator);
+
+ /// <summary>
+ /// Whether this member is a <see cref="UserFlags.Partner"/>
+ /// </summary>
+ /// <returns><see cref="bool"/></returns>
+ [JsonIgnore]
+ public bool IsPartner
+ => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.Partner);
+
+ /// <summary>
+ /// Whether this member is a <see cref="UserFlags.VerifiedBot"/>
+ /// </summary>
+ /// <returns><see cref="bool"/></returns>
+ [JsonIgnore]
+ public bool IsVerifiedBot
+ => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.VerifiedBot);
+
+ /// <summary>
+ /// Whether this member is a <see cref="UserFlags.VerifiedDeveloper"/>
+ /// </summary>
+ /// <returns><see cref="bool"/></returns>
+ [JsonIgnore]
+ public bool IsBotDev
+ => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.VerifiedDeveloper);
+
+ /// <summary>
+ /// Whether this member is a <see cref="UserFlags.Staff"/>
+ /// </summary>
+ /// <returns><see cref="bool"/></returns>
+ [JsonIgnore]
+ public bool IsStaff
+ => this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.Staff);
+
+ #endregion
+
+ /// <summary>
+ /// Fetches the user from the API.
+ /// </summary>
+ /// <returns>The user with fresh data from the API.</returns>
+ public async Task<DiscordUser> GetFromApiAsync()
+ => await this.Discord.ApiClient.GetUserAsync(this.Id);
+
+ /// <summary>
+ /// Whether this user is in a <see cref="DiscordGuild"/>
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// DiscordGuild guild = await Client.GetGuildAsync(806675511555915806);
+ /// DiscordUser user = await Client.GetUserAsync(469957180968271873);
+ /// Console.WriteLine($"{user.Username} {(user.IsInGuild(guild) ? "is a" : "is not a")} member of {guild.Name}");
+ /// </code>
+ /// results to <c>J_M_Lutra is a member of Project Nyaw~</c>.
+ /// </example>
+ /// <param name="guild"><see cref="DiscordGuild"/></param>
+ /// <returns><see cref="bool"/></returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
+ public async Task<bool> IsInGuild(DiscordGuild guild)
{
- var id = this.Id.ToString(CultureInfo.InvariantCulture);
- return $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{id}/{this.AvatarHash}.{sfmt}?size={ssize}";
+ try
+ {
+ var member = await guild.GetMemberAsync(this.Id);
+ return member is not null;
+
+ }
+ catch (NotFoundException)
+ {
+ return false;
+ }
}
- else
+
+ /// <summary>
+ /// Whether this user is not in a <see cref="DiscordGuild"/>
+ /// </summary>
+ /// <param name="guild"><see cref="DiscordGuild"/></param>
+ /// <returns><see cref="bool"/></returns>
+ public async Task<bool> IsNotInGuild(DiscordGuild guild)
+ => !await this.IsInGuild(guild);
+
+ /// <summary>
+ /// Returns the DiscordMember in the specified <see cref="DiscordGuild"/>
+ /// </summary>
+ /// <param name="guild">The <see cref="DiscordGuild"/> to get this user on.</param>
+ /// <returns>The <see cref="DiscordMember"/>.</returns>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the user is not part of the guild.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMember> ConvertToMember(DiscordGuild guild)
+ => await guild.GetMemberAsync(this.Id);
+
+ /// <summary>
+ /// Unbans this user from a guild.
+ /// </summary>
+ /// <param name="guild">Guild to unban this user from.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.BanMembers"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the user does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task UnbanAsync(DiscordGuild guild, string reason = null)
+ => guild.UnbanMemberAsync(this, reason);
+
+ /// <summary>
+ /// Gets this user's presence.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordPresence Presence
+ => this.Discord is DiscordClient dc && dc.Presences.TryGetValue(this.Id, out var presence) ? presence : null;
+
+ /// <summary>
+ /// Gets the user's avatar URL, in requested format and size.
+ /// </summary>
+ /// <param name="fmt">Format of the avatar to get.</param>
+ /// <param name="size">Maximum size of the avatar. Must be a power of two, minimum 16, maximum 2048.</param>
+ /// <returns>URL of the user's avatar.</returns>
+ public string GetAvatarUrl(ImageFormat fmt, ushort size = 1024)
{
- var type = (this.DiscriminatorInt % 5).ToString(CultureInfo.InvariantCulture);
- return $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMBED}{Endpoints.AVATARS}/{type}.{sfmt}?size={ssize}";
+ if (fmt == ImageFormat.Unknown)
+ throw new ArgumentException("You must specify valid image format.", nameof(fmt));
+
+ if (size < 16 || size > 2048)
+ throw new ArgumentOutOfRangeException(nameof(size));
+
+ var log = Math.Log(size, 2);
+ if (log < 4 || log > 11 || log % 1 != 0)
+ throw new ArgumentOutOfRangeException(nameof(size));
+
+ var sfmt = "";
+ sfmt = fmt switch
+ {
+ ImageFormat.Gif => "gif",
+ ImageFormat.Jpeg => "jpg",
+ ImageFormat.Png => "png",
+ ImageFormat.WebP => "webp",
+ ImageFormat.Auto => !string.IsNullOrWhiteSpace(this.AvatarHash) ? this.AvatarHash.StartsWith("a_") ? "gif" : "png" : "png",
+ _ => throw new ArgumentOutOfRangeException(nameof(fmt)),
+ };
+ var ssize = size.ToString(CultureInfo.InvariantCulture);
+ if (!string.IsNullOrWhiteSpace(this.AvatarHash))
+ {
+ var id = this.Id.ToString(CultureInfo.InvariantCulture);
+ return $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{id}/{this.AvatarHash}.{sfmt}?size={ssize}";
+ }
+ else
+ {
+ var type = (this.DiscriminatorInt % 5).ToString(CultureInfo.InvariantCulture);
+ return $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMBED}{Endpoints.AVATARS}/{type}.{sfmt}?size={ssize}";
+ }
}
- }
- /// <summary>
- /// Returns a string representation of this user.
- /// </summary>
- /// <returns>String representation of this user.</returns>
- public override string ToString() => $"User {this.Id}; {this.Username}#{this.Discriminator}";
-
- /// <summary>
- /// Checks whether this <see cref="DiscordUser"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordUser"/>.</returns>
- public override bool Equals(object obj) => this.Equals(obj as DiscordUser);
+ /// <summary>
+ /// Returns a string representation of this user.
+ /// </summary>
+ /// <returns>String representation of this user.</returns>
+ public override string ToString() => $"User {this.Id}; {this.Username}#{this.Discriminator}";
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordUser"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordUser"/>.</returns>
+ public override bool Equals(object obj) => this.Equals(obj as DiscordUser);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordUser"/> is equal to another <see cref="DiscordUser"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordUser"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordUser"/> is equal to this <see cref="DiscordUser"/>.</returns>
+ public bool Equals(DiscordUser e) => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordUser"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordUser"/>.</returns>
+ public override int GetHashCode() => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordUser"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First user to compare.</param>
+ /// <param name="e2">Second user to compare.</param>
+ /// <returns>Whether the two users are equal.</returns>
+ public static bool operator ==(DiscordUser e1, DiscordUser e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- /// <summary>
- /// Checks whether this <see cref="DiscordUser"/> is equal to another <see cref="DiscordUser"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordUser"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordUser"/> is equal to this <see cref="DiscordUser"/>.</returns>
- public bool Equals(DiscordUser e) => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordUser"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordUser"/>.</returns>
- public override int GetHashCode() => this.Id.GetHashCode();
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordUser"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First user to compare.</param>
+ /// <param name="e2">Second user to compare.</param>
+ /// <returns>Whether the two users are not equal.</returns>
+ public static bool operator !=(DiscordUser e1, DiscordUser e2)
+ => !(e1 == e2);
+ }
/// <summary>
- /// Gets whether the two <see cref="DiscordUser"/> objects are equal.
+ /// Represents a user comparer.
/// </summary>
- /// <param name="e1">First user to compare.</param>
- /// <param name="e2">Second user to compare.</param>
- /// <returns>Whether the two users are equal.</returns>
- public static bool operator ==(DiscordUser e1, DiscordUser e2)
+ internal class DiscordUserComparer : IEqualityComparer<DiscordUser>
{
- var o1 = e1 as object;
- var o2 = e2 as object;
-
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ /// <summary>
+ /// Whether the users are equal.
+ /// </summary>
+ /// <param name="x">The first user</param>
+ /// <param name="y">The second user.</param>
+ public bool Equals(DiscordUser x, DiscordUser y) => x.Equals(y);
+
+ /// <summary>
+ /// Gets the hash code.
+ /// </summary>
+ /// <param name="obj">The user.</param>
+ public int GetHashCode(DiscordUser obj) => obj.Id.GetHashCode();
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordUser"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First user to compare.</param>
- /// <param name="e2">Second user to compare.</param>
- /// <returns>Whether the two users are not equal.</returns>
- public static bool operator !=(DiscordUser e1, DiscordUser e2)
- => !(e1 == e2);
-}
-
-/// <summary>
-/// Represents a user comparer.
-/// </summary>
-internal class DiscordUserComparer : IEqualityComparer<DiscordUser>
-{
- /// <summary>
- /// Whether the users are equal.
- /// </summary>
- /// <param name="x">The first user</param>
- /// <param name="y">The second user.</param>
- public bool Equals(DiscordUser x, DiscordUser y) => x.Equals(y);
-
- /// <summary>
- /// Gets the hash code.
- /// </summary>
- /// <param name="obj">The user.</param>
- public int GetHashCode(DiscordUser obj) => obj.Id.GetHashCode();
}
diff --git a/DisCatSharp/Entities/Voice/DiscordVoiceRegion.cs b/DisCatSharp/Entities/Voice/DiscordVoiceRegion.cs
index 0b7531de7..49bd61796 100644
--- a/DisCatSharp/Entities/Voice/DiscordVoiceRegion.cs
+++ b/DisCatSharp/Entities/Voice/DiscordVoiceRegion.cs
@@ -1,120 +1,121 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents information about a Discord voice server region.
-/// </summary>
-public class DiscordVoiceRegion
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the unique ID for the region.
- /// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public string Id { get; internal set; }
-
- /// <summary>
- /// Gets the name of the region.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets an example server hostname for this region.
- /// </summary>
- [JsonProperty("sample_hostname", NullValueHandling = NullValueHandling.Ignore)]
- public string SampleHostname { get; internal set; }
-
- /// <summary>
- /// Gets an example server port for this region.
- /// </summary>
- [JsonProperty("sample_port", NullValueHandling = NullValueHandling.Ignore)]
- public int SamplePort { get; internal set; }
-
- /// <summary>
- /// Gets whether this region is the most optimal for the current user.
- /// </summary>
- [JsonProperty("optimal", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsOptimal { get; internal set; }
-
- /// <summary>
- /// Gets whether this voice region is deprecated.
- /// </summary>
- [JsonProperty("deprecated", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsDeprecated { get; internal set; }
-
- /// <summary>
- /// Gets whether this is a custom voice region.
+ /// Represents information about a Discord voice server region.
/// </summary>
- [JsonProperty("custom", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsCustom { get; internal set; }
-
- /// <summary>
- /// Gets whether two <see cref="DiscordVoiceRegion"/>s are equal.
- /// </summary>
- /// <param name="region">The region to compare with.</param>
- public bool Equals(DiscordVoiceRegion region)
- => this == region;
-
- /// <summary>
- /// Whether two regions are equal.
- /// </summary>
- /// <param name="obj">A voice region.</param>
- public override bool Equals(object obj) => this.Equals(obj as DiscordVoiceRegion);
-
- /// <summary>
- /// Gets the hash code.
- /// </summary>
- public override int GetHashCode() => this.Id.GetHashCode();
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordVoiceRegion"/> objects are equal.
- /// </summary>
- /// <param name="left">First voice region to compare.</param>
- /// <param name="right">Second voice region to compare.</param>
- /// <returns>Whether the two voice regions are equal.</returns>
- public static bool operator ==(DiscordVoiceRegion left, DiscordVoiceRegion right)
+ public class DiscordVoiceRegion
{
- var o1 = left as object;
- var o2 = right as object;
-
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || left.Id == right.Id);
+ /// <summary>
+ /// Gets the unique ID for the region.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public string Id { get; internal set; }
+
+ /// <summary>
+ /// Gets the name of the region.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets an example server hostname for this region.
+ /// </summary>
+ [JsonProperty("sample_hostname", NullValueHandling = NullValueHandling.Ignore)]
+ public string SampleHostname { get; internal set; }
+
+ /// <summary>
+ /// Gets an example server port for this region.
+ /// </summary>
+ [JsonProperty("sample_port", NullValueHandling = NullValueHandling.Ignore)]
+ public int SamplePort { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this region is the most optimal for the current user.
+ /// </summary>
+ [JsonProperty("optimal", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsOptimal { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this voice region is deprecated.
+ /// </summary>
+ [JsonProperty("deprecated", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsDeprecated { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this is a custom voice region.
+ /// </summary>
+ [JsonProperty("custom", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsCustom { get; internal set; }
+
+ /// <summary>
+ /// Gets whether two <see cref="DiscordVoiceRegion"/>s are equal.
+ /// </summary>
+ /// <param name="region">The region to compare with.</param>
+ public bool Equals(DiscordVoiceRegion region)
+ => this == region;
+
+ /// <summary>
+ /// Whether two regions are equal.
+ /// </summary>
+ /// <param name="obj">A voice region.</param>
+ public override bool Equals(object obj) => this.Equals(obj as DiscordVoiceRegion);
+
+ /// <summary>
+ /// Gets the hash code.
+ /// </summary>
+ public override int GetHashCode() => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordVoiceRegion"/> objects are equal.
+ /// </summary>
+ /// <param name="left">First voice region to compare.</param>
+ /// <param name="right">Second voice region to compare.</param>
+ /// <returns>Whether the two voice regions are equal.</returns>
+ public static bool operator ==(DiscordVoiceRegion left, DiscordVoiceRegion right)
+ {
+ var o1 = left as object;
+ var o2 = right as object;
+
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || left.Id == right.Id);
+ }
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordVoiceRegion"/> objects are not equal.
+ /// </summary>
+ /// <param name="left">First voice region to compare.</param>
+ /// <param name="right">Second voice region to compare.</param>
+ /// <returns>Whether the two voice regions are not equal.</returns>
+ public static bool operator !=(DiscordVoiceRegion left, DiscordVoiceRegion right)
+ => !(left == right);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordVoiceRegion"/> class.
+ /// </summary>
+ internal DiscordVoiceRegion()
+ { }
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordVoiceRegion"/> objects are not equal.
- /// </summary>
- /// <param name="left">First voice region to compare.</param>
- /// <param name="right">Second voice region to compare.</param>
- /// <returns>Whether the two voice regions are not equal.</returns>
- public static bool operator !=(DiscordVoiceRegion left, DiscordVoiceRegion right)
- => !(left == right);
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordVoiceRegion"/> class.
- /// </summary>
- internal DiscordVoiceRegion()
- { }
}
diff --git a/DisCatSharp/Entities/Voice/DiscordVoiceState.cs b/DisCatSharp/Entities/Voice/DiscordVoiceState.cs
index 68d56f6de..d24ad8ca2 100644
--- a/DisCatSharp/Entities/Voice/DiscordVoiceState.cs
+++ b/DisCatSharp/Entities/Voice/DiscordVoiceState.cs
@@ -1,215 +1,216 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using DisCatSharp.Net.Abstractions;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord voice state.
-/// </summary>
-public class DiscordVoiceState
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the discord client.
- /// </summary>
- internal DiscordClient Discord { get; set; }
-
- /// <summary>
- /// Gets ID of the guild this voice state is associated with.
- /// </summary>
- [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong? GuildId { get; set; }
-
- /// <summary>
- /// Gets the guild associated with this voice state.
- /// </summary>
- [JsonIgnore]
- public DiscordGuild Guild
- => this.GuildId != null ? this.Discord.Guilds[this.GuildId.Value] : this.Channel?.Guild;
-
- /// <summary>
- /// Gets ID of the channel this user is connected to.
+ /// Represents a Discord voice state.
/// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Include)]
- internal ulong? ChannelId { get; set; }
-
- /// <summary>
- /// Gets the channel this user is connected to.
- /// </summary>
- [JsonIgnore]
- public DiscordChannel Channel
- => this.ChannelId != null && this.ChannelId.Value != 0 ? this.Discord.InternalGetCachedChannel(this.ChannelId.Value) : null;
-
- /// <summary>
- /// Gets ID of the user to which this voice state belongs.
- /// </summary>
- [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
- internal ulong UserId { get; set; }
-
- /// <summary>
- /// Gets the user associated with this voice state.
- /// <para>This can be cast to a <see cref="DisCatSharp.Entities.DiscordMember"/> if this voice state was in a guild.</para>
- /// </summary>
- [JsonIgnore]
- public DiscordUser User
+ public class DiscordVoiceState
{
- get
+ /// <summary>
+ /// Gets the discord client.
+ /// </summary>
+ internal DiscordClient Discord { get; set; }
+
+ /// <summary>
+ /// Gets ID of the guild this voice state is associated with.
+ /// </summary>
+ [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong? GuildId { get; set; }
+
+ /// <summary>
+ /// Gets the guild associated with this voice state.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild
+ => this.GuildId != null ? this.Discord.Guilds[this.GuildId.Value] : this.Channel?.Guild;
+
+ /// <summary>
+ /// Gets ID of the channel this user is connected to.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Include)]
+ internal ulong? ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets the channel this user is connected to.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordChannel Channel
+ => this.ChannelId != null && this.ChannelId.Value != 0 ? this.Discord.InternalGetCachedChannel(this.ChannelId.Value) : null;
+
+ /// <summary>
+ /// Gets ID of the user to which this voice state belongs.
+ /// </summary>
+ [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal ulong UserId { get; set; }
+
+ /// <summary>
+ /// Gets the user associated with this voice state.
+ /// <para>This can be cast to a <see cref="DisCatSharp.Entities.DiscordMember"/> if this voice state was in a guild.</para>
+ /// </summary>
+ [JsonIgnore]
+ public DiscordUser User
{
- var usr = null as DiscordUser;
+ get
+ {
+ var usr = null as DiscordUser;
- if (this.Guild != null)
- usr = this.Guild.MembersInternal.TryGetValue(this.UserId, out var member) ? member : null;
+ if (this.Guild != null)
+ usr = this.Guild.MembersInternal.TryGetValue(this.UserId, out var member) ? member : null;
- if (usr == null)
- usr = this.Discord.GetCachedOrEmptyUserInternal(this.UserId);
+ if (usr == null)
+ usr = this.Discord.GetCachedOrEmptyUserInternal(this.UserId);
- return usr;
+ return usr;
+ }
}
- }
-
- /// <summary>
- /// Gets ID of the session of this voice state.
- /// </summary>
- [JsonProperty("session_id", NullValueHandling = NullValueHandling.Ignore)]
- internal string SessionId { get; set; }
-
- /// <summary>
- /// Gets whether this user is deafened.
- /// </summary>
- [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsServerDeafened { get; internal set; }
-
- /// <summary>
- /// Gets whether this user is muted.
- /// </summary>
- [JsonProperty("mute", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsServerMuted { get; internal set; }
-
- /// <summary>
- /// Gets whether this user is locally deafened.
- /// </summary>
- [JsonProperty("self_deaf", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsSelfDeafened { get; internal set; }
-
- /// <summary>
- /// Gets whether this user is locally muted.
- /// </summary>
- [JsonProperty("self_mute", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsSelfMuted { get; internal set; }
- /// <summary>
- /// Gets whether this user's camera is enabled.
- /// </summary>
- [JsonProperty("self_video", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsSelfVideo { get; internal set; }
-
- /// <summary>
- /// Gets whether this user is using the Go Live feature.
- /// </summary>
- [JsonProperty("self_stream", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsSelfStream { get; internal set; }
-
- /// <summary>
- /// Gets whether the current user has suppressed this user.
- /// </summary>
- [JsonProperty("suppress", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsSuppressed { get; internal set; }
-
- /// <summary>
- /// Gets the time at which this user requested to speak.
- /// </summary>
- [JsonProperty("request_to_speak_timestamp", NullValueHandling = NullValueHandling.Ignore)]
- internal DateTimeOffset? RequestToSpeakTimestamp { get; set; }
-
- /// <summary>
- /// Gets the member this voice state belongs to.
- /// </summary>
- [JsonIgnore]
- public DiscordMember Member
- => this.Guild.Members.TryGetValue(this.TransportMember.User.Id, out var member) ? member : new DiscordMember(this.TransportMember) { Discord = this.Discord };
-
- /// <summary>
- /// Gets the transport member.
- /// </summary>
- [JsonProperty("member", NullValueHandling = NullValueHandling.Ignore)]
- internal TransportMember TransportMember { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordVoiceState"/> class.
- /// </summary>
- internal DiscordVoiceState()
- { }
+ /// <summary>
+ /// Gets ID of the session of this voice state.
+ /// </summary>
+ [JsonProperty("session_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal string SessionId { get; set; }
+
+ /// <summary>
+ /// Gets whether this user is deafened.
+ /// </summary>
+ [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsServerDeafened { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this user is muted.
+ /// </summary>
+ [JsonProperty("mute", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsServerMuted { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this user is locally deafened.
+ /// </summary>
+ [JsonProperty("self_deaf", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsSelfDeafened { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this user is locally muted.
+ /// </summary>
+ [JsonProperty("self_mute", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsSelfMuted { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this user's camera is enabled.
+ /// </summary>
+ [JsonProperty("self_video", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsSelfVideo { get; internal set; }
+
+ /// <summary>
+ /// Gets whether this user is using the Go Live feature.
+ /// </summary>
+ [JsonProperty("self_stream", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsSelfStream { get; internal set; }
+
+ /// <summary>
+ /// Gets whether the current user has suppressed this user.
+ /// </summary>
+ [JsonProperty("suppress", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsSuppressed { get; internal set; }
+
+ /// <summary>
+ /// Gets the time at which this user requested to speak.
+ /// </summary>
+ [JsonProperty("request_to_speak_timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ internal DateTimeOffset? RequestToSpeakTimestamp { get; set; }
+
+ /// <summary>
+ /// Gets the member this voice state belongs to.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordMember Member
+ => this.Guild.Members.TryGetValue(this.TransportMember.User.Id, out var member) ? member : new DiscordMember(this.TransportMember) { Discord = this.Discord };
+
+ /// <summary>
+ /// Gets the transport member.
+ /// </summary>
+ [JsonProperty("member", NullValueHandling = NullValueHandling.Ignore)]
+ internal TransportMember TransportMember { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordVoiceState"/> class.
+ /// </summary>
+ internal DiscordVoiceState()
+ { }
+
+ // copy constructor for reduced boilerplate
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordVoiceState"/> class.
+ /// </summary>
+ /// <param name="other">The other.</param>
+ internal DiscordVoiceState(DiscordVoiceState other)
+ {
+ this.Discord = other.Discord;
+
+ this.UserId = other.UserId;
+ this.ChannelId = other.ChannelId;
+ this.GuildId = other.GuildId;
+
+ this.IsServerDeafened = other.IsServerDeafened;
+ this.IsServerMuted = other.IsServerMuted;
+ this.IsSuppressed = other.IsSuppressed;
+ this.IsSelfDeafened = other.IsSelfDeafened;
+ this.IsSelfMuted = other.IsSelfMuted;
+ this.IsSelfStream = other.IsSelfStream;
+ this.IsSelfVideo = other.IsSelfVideo;
+
+ this.SessionId = other.SessionId;
+ this.RequestToSpeakTimestamp = other.RequestToSpeakTimestamp;
+ }
- // copy constructor for reduced boilerplate
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordVoiceState"/> class.
- /// </summary>
- /// <param name="other">The other.</param>
- internal DiscordVoiceState(DiscordVoiceState other)
- {
- this.Discord = other.Discord;
-
- this.UserId = other.UserId;
- this.ChannelId = other.ChannelId;
- this.GuildId = other.GuildId;
-
- this.IsServerDeafened = other.IsServerDeafened;
- this.IsServerMuted = other.IsServerMuted;
- this.IsSuppressed = other.IsSuppressed;
- this.IsSelfDeafened = other.IsSelfDeafened;
- this.IsSelfMuted = other.IsSelfMuted;
- this.IsSelfStream = other.IsSelfStream;
- this.IsSelfVideo = other.IsSelfVideo;
-
- this.SessionId = other.SessionId;
- this.RequestToSpeakTimestamp = other.RequestToSpeakTimestamp;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordVoiceState"/> class.
+ /// </summary>
+ /// <param name="m">The m.</param>
+ internal DiscordVoiceState(DiscordMember m)
+ {
+ this.Discord = m.Discord as DiscordClient;
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordVoiceState"/> class.
- /// </summary>
- /// <param name="m">The m.</param>
- internal DiscordVoiceState(DiscordMember m)
- {
- this.Discord = m.Discord as DiscordClient;
+ this.UserId = m.Id;
+ this.ChannelId = 0;
+ this.GuildId = m.GuildId;
- this.UserId = m.Id;
- this.ChannelId = 0;
- this.GuildId = m.GuildId;
+ this.IsServerDeafened = m.IsDeafened;
+ this.IsServerMuted = m.IsMuted;
- this.IsServerDeafened = m.IsDeafened;
- this.IsServerMuted = m.IsMuted;
+ // Values not filled out are values that are not known from a DiscordMember
+ }
- // Values not filled out are values that are not known from a DiscordMember
+ /// <summary>
+ /// Gets a readable voice state string.
+ /// </summary>
+ public override string ToString() => $"{this.UserId.ToString(CultureInfo.InvariantCulture)} in {(this.GuildId ?? this.Channel.GuildId.Value).ToString(CultureInfo.InvariantCulture)}";
}
-
- /// <summary>
- /// Gets a readable voice state string.
- /// </summary>
- public override string ToString() => $"{this.UserId.ToString(CultureInfo.InvariantCulture)} in {(this.GuildId ?? this.Channel.GuildId.Value).ToString(CultureInfo.InvariantCulture)}";
}
diff --git a/DisCatSharp/Entities/Webhook/DiscordWebhook.cs b/DisCatSharp/Entities/Webhook/DiscordWebhook.cs
index 90094ebbb..92db0e938 100644
--- a/DisCatSharp/Entities/Webhook/DiscordWebhook.cs
+++ b/DisCatSharp/Entities/Webhook/DiscordWebhook.cs
@@ -1,279 +1,280 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents information about a Discord webhook.
-/// </summary>
-public class DiscordWebhook : SnowflakeObject, IEquatable<DiscordWebhook>
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the api client.
- /// </summary>
- internal DiscordApiClient ApiClient { get; set; }
-
- /// <summary>
- /// Gets the id of the guild this webhook belongs to.
- /// </summary>
- [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong GuildId { get; internal set; }
-
- /// <summary>
- /// Gets the ID of the channel this webhook belongs to.
- /// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; internal set; }
-
- /// <summary>
- /// Gets the user this webhook was created by.
- /// </summary>
- [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Gets the default name of this webhook.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets hash of the default avatar for this webhook.
- /// </summary>
- [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
- internal string AvatarHash { get; set; }
-
- /// <summary>
- /// Gets the partial source guild for this webhook (For Channel Follower Webhooks).
- /// </summary>
- [JsonProperty("source_guild", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordGuild SourceGuild { get; set; }
-
- /// <summary>
- /// Gets the partial source channel for this webhook (For Channel Follower Webhooks).
+ /// Represents information about a Discord webhook.
/// </summary>
- [JsonProperty("source_channel", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordChannel SourceChannel { get; set; }
-
- /// <summary>
- /// Gets the url used for executing the webhook.
- /// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public string Url { get; set; }
-
- /// <summary>
- /// Gets the default avatar url for this webhook.
- /// </summary>
- public string AvatarUrl
- => !string.IsNullOrWhiteSpace(this.AvatarHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{this.Id}/{this.AvatarHash}.png?size=1024" : null;
-
- /// <summary>
- /// Gets the secure token of this webhook.
- /// </summary>
- [JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)]
- public string Token { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordWebhook"/> class.
- /// </summary>
- internal DiscordWebhook()
- { }
-
- /// <summary>
- /// Modifies this webhook.
- /// </summary>
- /// <param name="name">New default name for this webhook.</param>
- /// <param name="avatar">New avatar for this webhook.</param>
- /// <param name="channelId">The new channel id to move the webhook to.</param>
- /// <param name="reason">Reason for audit logs.</param>
- /// <returns>The modified webhook.</returns>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordWebhook> ModifyAsync(string name = null, Optional<Stream> avatar = default, ulong? channelId = null, string reason = null)
+ public class DiscordWebhook : SnowflakeObject, IEquatable<DiscordWebhook>
{
- var avatarb64 = ImageTool.Base64FromStream(avatar);
-
- var newChannelId = channelId ?? this.ChannelId;
-
- return this.Discord.ApiClient.ModifyWebhookAsync(this.Id, newChannelId, name, avatarb64, reason);
- }
-
- /// <summary>
- /// Gets a previously-sent webhook message.
- /// </summary>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> GetMessageAsync(ulong messageId)
- => await (this.Discord?.ApiClient ?? this.ApiClient).GetWebhookMessageAsync(this.Id, this.Token, messageId).ConfigureAwait(false);
-
- /// <summary>
- /// Gets a previously-sent webhook message.
- /// </summary>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> GetMessageAsync(ulong messageId, ulong threadId)
- => await (this.Discord?.ApiClient ?? this.ApiClient).GetWebhookMessageAsync(this.Id, this.Token, messageId, threadId).ConfigureAwait(false);
-
- /// <summary>
- /// Permanently deletes this webhook.
- /// </summary>
- /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteAsync()
- => this.Discord.ApiClient.DeleteWebhookAsync(this.Id, this.Token);
-
- /// <summary>
- /// Executes this webhook with the given <see cref="DiscordWebhookBuilder"/>.
- /// </summary>
- /// <param name="builder">Webhook builder filled with data to send.</param>
- /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task<DiscordMessage> ExecuteAsync(DiscordWebhookBuilder builder, string threadId = null)
- => (this.Discord?.ApiClient ?? this.ApiClient).ExecuteWebhookAsync(this.Id, this.Token, builder, threadId);
-
- /// <summary>
- /// Executes this webhook in Slack compatibility mode.
- /// </summary>
- /// <param name="json">JSON containing Slack-compatible payload for this webhook.</param>
- /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ExecuteSlackAsync(string json, string threadId = null)
- => (this.Discord?.ApiClient ?? this.ApiClient).ExecuteWebhookSlackAsync(this.Id, this.Token, json, threadId);
+ /// <summary>
+ /// Gets the api client.
+ /// </summary>
+ internal DiscordApiClient ApiClient { get; set; }
+
+ /// <summary>
+ /// Gets the id of the guild this webhook belongs to.
+ /// </summary>
+ [JsonProperty("guild_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong GuildId { get; internal set; }
+
+ /// <summary>
+ /// Gets the ID of the channel this webhook belongs to.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; internal set; }
+
+ /// <summary>
+ /// Gets the user this webhook was created by.
+ /// </summary>
+ [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the default name of this webhook.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
+
+ /// <summary>
+ /// Gets hash of the default avatar for this webhook.
+ /// </summary>
+ [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
+ internal string AvatarHash { get; set; }
+
+ /// <summary>
+ /// Gets the partial source guild for this webhook (For Channel Follower Webhooks).
+ /// </summary>
+ [JsonProperty("source_guild", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordGuild SourceGuild { get; set; }
+
+ /// <summary>
+ /// Gets the partial source channel for this webhook (For Channel Follower Webhooks).
+ /// </summary>
+ [JsonProperty("source_channel", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordChannel SourceChannel { get; set; }
+
+ /// <summary>
+ /// Gets the url used for executing the webhook.
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public string Url { get; set; }
+
+ /// <summary>
+ /// Gets the default avatar url for this webhook.
+ /// </summary>
+ public string AvatarUrl
+ => !string.IsNullOrWhiteSpace(this.AvatarHash) ? $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{this.Id}/{this.AvatarHash}.png?size=1024" : null;
+
+ /// <summary>
+ /// Gets the secure token of this webhook.
+ /// </summary>
+ [JsonProperty("token", NullValueHandling = NullValueHandling.Ignore)]
+ public string Token { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordWebhook"/> class.
+ /// </summary>
+ internal DiscordWebhook()
+ { }
+
+ /// <summary>
+ /// Modifies this webhook.
+ /// </summary>
+ /// <param name="name">New default name for this webhook.</param>
+ /// <param name="avatar">New avatar for this webhook.</param>
+ /// <param name="channelId">The new channel id to move the webhook to.</param>
+ /// <param name="reason">Reason for audit logs.</param>
+ /// <returns>The modified webhook.</returns>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordWebhook> ModifyAsync(string name = null, Optional<Stream> avatar = default, ulong? channelId = null, string reason = null)
+ {
+ var avatarb64 = ImageTool.Base64FromStream(avatar);
- /// <summary>
- /// Executes this webhook in GitHub compatibility mode.
- /// </summary>
- /// <param name="json">JSON containing GitHub-compatible payload for this webhook.</param>
- /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task ExecuteGithubAsync(string json, string threadId = null)
- => (this.Discord?.ApiClient ?? this.ApiClient).ExecuteWebhookGithubAsync(this.Id, this.Token, json, threadId);
+ var newChannelId = channelId ?? this.ChannelId;
- /// <summary>
- /// Edits a previously-sent webhook message.
- /// </summary>
- /// <param name="messageId">The id of the message to edit.</param>
- /// <param name="builder">The builder of the message to edit.</param>
- /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
- /// <returns>The modified <see cref="DiscordMessage"/></returns>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public async Task<DiscordMessage> EditMessageAsync(ulong messageId, DiscordWebhookBuilder builder, string threadId = null)
- {
- builder.Validate(true);
- if (builder.KeepAttachmentsInternal.HasValue && builder.KeepAttachmentsInternal.Value)
- {
- builder.AttachmentsInternal.AddRange(this.ApiClient.GetWebhookMessageAsync(this.Id, this.Token, messageId.ToString(), threadId).Result.Attachments);
+ return this.Discord.ApiClient.ModifyWebhookAsync(this.Id, newChannelId, name, avatarb64, reason);
}
- else if (builder.KeepAttachmentsInternal.HasValue)
+
+ /// <summary>
+ /// Gets a previously-sent webhook message.
+ /// </summary>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> GetMessageAsync(ulong messageId)
+ => await (this.Discord?.ApiClient ?? this.ApiClient).GetWebhookMessageAsync(this.Id, this.Token, messageId).ConfigureAwait(false);
+
+ /// <summary>
+ /// Gets a previously-sent webhook message.
+ /// </summary>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> GetMessageAsync(ulong messageId, ulong threadId)
+ => await (this.Discord?.ApiClient ?? this.ApiClient).GetWebhookMessageAsync(this.Id, this.Token, messageId, threadId).ConfigureAwait(false);
+
+ /// <summary>
+ /// Permanently deletes this webhook.
+ /// </summary>
+ /// <exception cref="Exceptions.UnauthorizedException">Thrown when the client does not have the <see cref="Permissions.ManageWebhooks"/> permission.</exception>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteAsync()
+ => this.Discord.ApiClient.DeleteWebhookAsync(this.Id, this.Token);
+
+ /// <summary>
+ /// Executes this webhook with the given <see cref="DiscordWebhookBuilder"/>.
+ /// </summary>
+ /// <param name="builder">Webhook builder filled with data to send.</param>
+ /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task<DiscordMessage> ExecuteAsync(DiscordWebhookBuilder builder, string threadId = null)
+ => (this.Discord?.ApiClient ?? this.ApiClient).ExecuteWebhookAsync(this.Id, this.Token, builder, threadId);
+
+ /// <summary>
+ /// Executes this webhook in Slack compatibility mode.
+ /// </summary>
+ /// <param name="json">JSON containing Slack-compatible payload for this webhook.</param>
+ /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ExecuteSlackAsync(string json, string threadId = null)
+ => (this.Discord?.ApiClient ?? this.ApiClient).ExecuteWebhookSlackAsync(this.Id, this.Token, json, threadId);
+
+ /// <summary>
+ /// Executes this webhook in GitHub compatibility mode.
+ /// </summary>
+ /// <param name="json">JSON containing GitHub-compatible payload for this webhook.</param>
+ /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task ExecuteGithubAsync(string json, string threadId = null)
+ => (this.Discord?.ApiClient ?? this.ApiClient).ExecuteWebhookGithubAsync(this.Id, this.Token, json, threadId);
+
+ /// <summary>
+ /// Edits a previously-sent webhook message.
+ /// </summary>
+ /// <param name="messageId">The id of the message to edit.</param>
+ /// <param name="builder">The builder of the message to edit.</param>
+ /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
+ /// <returns>The modified <see cref="DiscordMessage"/></returns>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public async Task<DiscordMessage> EditMessageAsync(ulong messageId, DiscordWebhookBuilder builder, string threadId = null)
{
- builder.AttachmentsInternal.Clear();
+ builder.Validate(true);
+ if (builder.KeepAttachmentsInternal.HasValue && builder.KeepAttachmentsInternal.Value)
+ {
+ builder.AttachmentsInternal.AddRange(this.ApiClient.GetWebhookMessageAsync(this.Id, this.Token, messageId.ToString(), threadId).Result.Attachments);
+ }
+ else if (builder.KeepAttachmentsInternal.HasValue)
+ {
+ builder.AttachmentsInternal.Clear();
+ }
+ return await (this.Discord?.ApiClient ?? this.ApiClient).EditWebhookMessageAsync(this.Id, this.Token, messageId.ToString(), builder, threadId).ConfigureAwait(false);
}
- return await (this.Discord?.ApiClient ?? this.ApiClient).EditWebhookMessageAsync(this.Id, this.Token, messageId.ToString(), builder, threadId).ConfigureAwait(false);
- }
- /// <summary>
- /// Deletes a message that was created by the webhook.
- /// </summary>
- /// <param name="messageId">The id of the message to delete</param>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteMessageAsync(ulong messageId)
- => (this.Discord?.ApiClient ?? this.ApiClient).DeleteWebhookMessageAsync(this.Id, this.Token, messageId);
-
- /// <summary>
- /// Deletes a message that was created by the webhook.
- /// </summary>
- /// <param name="messageId">The id of the message to delete</param>
- /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
- /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
- /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
- /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
- public Task DeleteMessageAsync(ulong messageId, ulong threadId)
- => (this.Discord?.ApiClient ?? this.ApiClient).DeleteWebhookMessageAsync(this.Id, this.Token, messageId, threadId);
-
- /// <summary>
- /// Checks whether this <see cref="DiscordWebhook"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="DiscordWebhook"/>.</returns>
- public override bool Equals(object obj) => this.Equals(obj as DiscordWebhook);
-
- /// <summary>
- /// Checks whether this <see cref="DiscordWebhook"/> is equal to another <see cref="DiscordWebhook"/>.
- /// </summary>
- /// <param name="e"><see cref="DiscordWebhook"/> to compare to.</param>
- /// <returns>Whether the <see cref="DiscordWebhook"/> is equal to this <see cref="DiscordWebhook"/>.</returns>
- public bool Equals(DiscordWebhook e) => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
-
- /// <summary>
- /// Gets the hash code for this <see cref="DiscordWebhook"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="DiscordWebhook"/>.</returns>
- public override int GetHashCode() => this.Id.GetHashCode();
+ /// <summary>
+ /// Deletes a message that was created by the webhook.
+ /// </summary>
+ /// <param name="messageId">The id of the message to delete</param>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteMessageAsync(ulong messageId)
+ => (this.Discord?.ApiClient ?? this.ApiClient).DeleteWebhookMessageAsync(this.Id, this.Token, messageId);
+
+ /// <summary>
+ /// Deletes a message that was created by the webhook.
+ /// </summary>
+ /// <param name="messageId">The id of the message to delete</param>
+ /// <param name="threadId">Target thread id (Optional). Defaults to null.</param>
+ /// <exception cref="Exceptions.NotFoundException">Thrown when the webhook does not exist.</exception>
+ /// <exception cref="Exceptions.BadRequestException">Thrown when an invalid parameter was provided.</exception>
+ /// <exception cref="Exceptions.ServerErrorException">Thrown when Discord is unable to process the request.</exception>
+ public Task DeleteMessageAsync(ulong messageId, ulong threadId)
+ => (this.Discord?.ApiClient ?? this.ApiClient).DeleteWebhookMessageAsync(this.Id, this.Token, messageId, threadId);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordWebhook"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="DiscordWebhook"/>.</returns>
+ public override bool Equals(object obj) => this.Equals(obj as DiscordWebhook);
+
+ /// <summary>
+ /// Checks whether this <see cref="DiscordWebhook"/> is equal to another <see cref="DiscordWebhook"/>.
+ /// </summary>
+ /// <param name="e"><see cref="DiscordWebhook"/> to compare to.</param>
+ /// <returns>Whether the <see cref="DiscordWebhook"/> is equal to this <see cref="DiscordWebhook"/>.</returns>
+ public bool Equals(DiscordWebhook e) => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="DiscordWebhook"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="DiscordWebhook"/>.</returns>
+ public override int GetHashCode() => this.Id.GetHashCode();
+
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordWebhook"/> objects are equal.
+ /// </summary>
+ /// <param name="e1">First webhook to compare.</param>
+ /// <param name="e2">Second webhook to compare.</param>
+ /// <returns>Whether the two webhooks are equal.</returns>
+ public static bool operator ==(DiscordWebhook e1, DiscordWebhook e2)
+ {
+ var o1 = e1 as object;
+ var o2 = e2 as object;
- /// <summary>
- /// Gets whether the two <see cref="DiscordWebhook"/> objects are equal.
- /// </summary>
- /// <param name="e1">First webhook to compare.</param>
- /// <param name="e2">Second webhook to compare.</param>
- /// <returns>Whether the two webhooks are equal.</returns>
- public static bool operator ==(DiscordWebhook e1, DiscordWebhook e2)
- {
- var o1 = e1 as object;
- var o2 = e2 as object;
+ return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ }
- return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
+ /// <summary>
+ /// Gets whether the two <see cref="DiscordWebhook"/> objects are not equal.
+ /// </summary>
+ /// <param name="e1">First webhook to compare.</param>
+ /// <param name="e2">Second webhook to compare.</param>
+ /// <returns>Whether the two webhooks are not equal.</returns>
+ public static bool operator !=(DiscordWebhook e1, DiscordWebhook e2)
+ => !(e1 == e2);
}
-
- /// <summary>
- /// Gets whether the two <see cref="DiscordWebhook"/> objects are not equal.
- /// </summary>
- /// <param name="e1">First webhook to compare.</param>
- /// <param name="e2">Second webhook to compare.</param>
- /// <returns>Whether the two webhooks are not equal.</returns>
- public static bool operator !=(DiscordWebhook e1, DiscordWebhook e2)
- => !(e1 == e2);
}
diff --git a/DisCatSharp/Entities/Webhook/DiscordWebhookBuilder.cs b/DisCatSharp/Entities/Webhook/DiscordWebhookBuilder.cs
index 45cbf86e8..5ff9c6c18 100644
--- a/DisCatSharp/Entities/Webhook/DiscordWebhookBuilder.cs
+++ b/DisCatSharp/Entities/Webhook/DiscordWebhookBuilder.cs
@@ -1,441 +1,442 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using System.Linq;
using System.Threading.Tasks;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Constructs ready-to-send webhook requests.
-/// </summary>
-public sealed class DiscordWebhookBuilder
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Username to use for this webhook request.
- /// </summary>
- public Optional<string> Username { get; set; }
-
- /// <summary>
- /// Avatar url to use for this webhook request.
+ /// Constructs ready-to-send webhook requests.
/// </summary>
- public Optional<string> AvatarUrl { get; set; }
-
- /// <summary>
- /// Whether this webhook request is text-to-speech.
- /// </summary>
- public bool IsTts { get; set; }
-
- /// <summary>
- /// Message to send on this webhook request.
- /// </summary>
- public string Content
+ public sealed class DiscordWebhookBuilder
{
- get => this._content;
- set
+ /// <summary>
+ /// Username to use for this webhook request.
+ /// </summary>
+ public Optional<string> Username { get; set; }
+
+ /// <summary>
+ /// Avatar url to use for this webhook request.
+ /// </summary>
+ public Optional<string> AvatarUrl { get; set; }
+
+ /// <summary>
+ /// Whether this webhook request is text-to-speech.
+ /// </summary>
+ public bool IsTts { get; set; }
+
+ /// <summary>
+ /// Message to send on this webhook request.
+ /// </summary>
+ public string Content
{
- if (value != null && value.Length > 2000)
- throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
- this._content = value;
+ get => this._content;
+ set
+ {
+ if (value != null && value.Length > 2000)
+ throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
+ this._content = value;
+ }
}
- }
- private string _content;
-
- /// <summary>
- /// Whether to keep previous attachments.
- /// </summary>
- internal bool? KeepAttachmentsInternal;
-
- /// <summary>
- /// Embeds to send on this webhook request.
- /// </summary>
- public IReadOnlyList<DiscordEmbed> Embeds => this._embeds;
- private readonly List<DiscordEmbed> _embeds = new();
-
- /// <summary>
- /// Files to send on this webhook request.
- /// </summary>
- public IReadOnlyList<DiscordMessageFile> Files => this._files;
- private readonly List<DiscordMessageFile> _files = new();
-
- /// <summary>
- /// Mentions to send on this webhook request.
- /// </summary>
- public IReadOnlyList<IMention> Mentions => this._mentions;
- private readonly List<IMention> _mentions = new();
-
- /// <summary>
- /// Gets the components.
- /// </summary>
- public IReadOnlyList<DiscordActionRowComponent> Components => this._components;
- private readonly List<DiscordActionRowComponent> _components = new();
-
-
- /// <summary>
- /// Attachments to keep on this webhook request.
- /// </summary>
- public IEnumerable<DiscordAttachment> Attachments => this.AttachmentsInternal;
- internal readonly List<DiscordAttachment> AttachmentsInternal = new();
-
- /// <summary>
- /// Constructs a new empty webhook request builder.
- /// </summary>
- public DiscordWebhookBuilder() { } // I still see no point in initializing collections with empty collections. //
-
-
- /// <summary>
- /// Adds a row of components to the builder, up to 5 components per row, and up to 5 rows per message.
- /// </summary>
- /// <param name="components">The components to add to the builder.</param>
- /// <returns>The current builder to be chained.</returns>
- /// <exception cref="System.ArgumentOutOfRangeException">No components were passed.</exception>
- public DiscordWebhookBuilder AddComponents(params DiscordComponent[] components)
- => this.AddComponents((IEnumerable<DiscordComponent>)components);
-
-
- /// <summary>
- /// Appends several rows of components to the builder
- /// </summary>
- /// <param name="components">The rows of components to add, holding up to five each.</param>
- /// <returns></returns>
- public DiscordWebhookBuilder AddComponents(IEnumerable<DiscordActionRowComponent> components)
- {
- var ara = components.ToArray();
-
- if (ara.Length + this._components.Count > 5)
- throw new ArgumentException("ActionRow count exceeds maximum of five.");
-
- foreach (var ar in ara)
- this._components.Add(ar);
-
- return this;
- }
-
- /// <summary>
- /// Adds a row of components to the builder, up to 5 components per row, and up to 5 rows per message.
- /// </summary>
- /// <param name="components">The components to add to the builder.</param>
- /// <returns>The current builder to be chained.</returns>
- /// <exception cref="System.ArgumentOutOfRangeException">No components were passed.</exception>
- public DiscordWebhookBuilder AddComponents(IEnumerable<DiscordComponent> components)
- {
- var cmpArr = components.ToArray();
- var count = cmpArr.Length;
-
- if (!cmpArr.Any())
- throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
-
- if (count > 5)
- throw new ArgumentException("Cannot add more than 5 components per action row!");
-
- var comp = new DiscordActionRowComponent(cmpArr);
- this._components.Add(comp);
-
- return this;
- }
-
- /// <summary>
- /// Sets the username for this webhook builder.
- /// </summary>
- /// <param name="username">Username of the webhook</param>
- public DiscordWebhookBuilder WithUsername(string username)
- {
- this.Username = username;
- return this;
- }
+ private string _content;
+
+ /// <summary>
+ /// Whether to keep previous attachments.
+ /// </summary>
+ internal bool? KeepAttachmentsInternal;
+
+ /// <summary>
+ /// Embeds to send on this webhook request.
+ /// </summary>
+ public IReadOnlyList<DiscordEmbed> Embeds => this._embeds;
+ private readonly List<DiscordEmbed> _embeds = new();
+
+ /// <summary>
+ /// Files to send on this webhook request.
+ /// </summary>
+ public IReadOnlyList<DiscordMessageFile> Files => this._files;
+ private readonly List<DiscordMessageFile> _files = new();
+
+ /// <summary>
+ /// Mentions to send on this webhook request.
+ /// </summary>
+ public IReadOnlyList<IMention> Mentions => this._mentions;
+ private readonly List<IMention> _mentions = new();
+
+ /// <summary>
+ /// Gets the components.
+ /// </summary>
+ public IReadOnlyList<DiscordActionRowComponent> Components => this._components;
+ private readonly List<DiscordActionRowComponent> _components = new();
+
+
+ /// <summary>
+ /// Attachments to keep on this webhook request.
+ /// </summary>
+ public IEnumerable<DiscordAttachment> Attachments => this.AttachmentsInternal;
+ internal readonly List<DiscordAttachment> AttachmentsInternal = new();
+
+ /// <summary>
+ /// Constructs a new empty webhook request builder.
+ /// </summary>
+ public DiscordWebhookBuilder() { } // I still see no point in initializing collections with empty collections. //
+
+
+ /// <summary>
+ /// Adds a row of components to the builder, up to 5 components per row, and up to 5 rows per message.
+ /// </summary>
+ /// <param name="components">The components to add to the builder.</param>
+ /// <returns>The current builder to be chained.</returns>
+ /// <exception cref="System.ArgumentOutOfRangeException">No components were passed.</exception>
+ public DiscordWebhookBuilder AddComponents(params DiscordComponent[] components)
+ => this.AddComponents((IEnumerable<DiscordComponent>)components);
+
+
+ /// <summary>
+ /// Appends several rows of components to the builder
+ /// </summary>
+ /// <param name="components">The rows of components to add, holding up to five each.</param>
+ /// <returns></returns>
+ public DiscordWebhookBuilder AddComponents(IEnumerable<DiscordActionRowComponent> components)
+ {
+ var ara = components.ToArray();
- /// <summary>
- /// Sets the avatar of this webhook builder from its url.
- /// </summary>
- /// <param name="avatarUrl">Avatar url of the webhook</param>
- public DiscordWebhookBuilder WithAvatarUrl(string avatarUrl)
- {
- this.AvatarUrl = avatarUrl;
- return this;
- }
+ if (ara.Length + this._components.Count > 5)
+ throw new ArgumentException("ActionRow count exceeds maximum of five.");
- /// <summary>
- /// Indicates if the webhook must use text-to-speech.
- /// </summary>
- /// <param name="tts">Text-to-speech</param>
- public DiscordWebhookBuilder WithTts(bool tts)
- {
- this.IsTts = tts;
- return this;
- }
+ foreach (var ar in ara)
+ this._components.Add(ar);
- /// <summary>
- /// Sets the message to send at the execution of the webhook.
- /// </summary>
- /// <param name="content">Message to send.</param>
- public DiscordWebhookBuilder WithContent(string content)
- {
- this.Content = content;
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Adds an embed to send at the execution of the webhook.
- /// </summary>
- /// <param name="embed">Embed to add.</param>
- public DiscordWebhookBuilder AddEmbed(DiscordEmbed embed)
- {
- if (embed != null)
- this._embeds.Add(embed);
+ /// <summary>
+ /// Adds a row of components to the builder, up to 5 components per row, and up to 5 rows per message.
+ /// </summary>
+ /// <param name="components">The components to add to the builder.</param>
+ /// <returns>The current builder to be chained.</returns>
+ /// <exception cref="System.ArgumentOutOfRangeException">No components were passed.</exception>
+ public DiscordWebhookBuilder AddComponents(IEnumerable<DiscordComponent> components)
+ {
+ var cmpArr = components.ToArray();
+ var count = cmpArr.Length;
- return this;
- }
+ if (!cmpArr.Any())
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
- /// <summary>
- /// Adds the given embeds to send at the execution of the webhook.
- /// </summary>
- /// <param name="embeds">Embeds to add.</param>
- public DiscordWebhookBuilder AddEmbeds(IEnumerable<DiscordEmbed> embeds)
- {
- this._embeds.AddRange(embeds);
- return this;
- }
+ if (count > 5)
+ throw new ArgumentException("Cannot add more than 5 components per action row!");
- /// <summary>
- /// Adds a file to send at the execution of the webhook.
- /// </summary>
- /// <param name="filename">Name of the file.</param>
- /// <param name="data">File data.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <param name="description">Description of the file.</param>
- public DiscordWebhookBuilder AddFile(string filename, Stream data, bool resetStreamPosition = false, string description = null)
- {
- if (this.Files.Count > 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ var comp = new DiscordActionRowComponent(cmpArr);
+ this._components.Add(comp);
- if (this._files.Any(x => x.FileName == filename))
- throw new ArgumentException("A File with that filename already exists");
+ return this;
+ }
- if (resetStreamPosition)
- this._files.Add(new DiscordMessageFile(filename, data, data.Position, description: description));
- else
- this._files.Add(new DiscordMessageFile(filename, data, null, description: description));
+ /// <summary>
+ /// Sets the username for this webhook builder.
+ /// </summary>
+ /// <param name="username">Username of the webhook</param>
+ public DiscordWebhookBuilder WithUsername(string username)
+ {
+ this.Username = username;
+ return this;
+ }
- return this;
- }
+ /// <summary>
+ /// Sets the avatar of this webhook builder from its url.
+ /// </summary>
+ /// <param name="avatarUrl">Avatar url of the webhook</param>
+ public DiscordWebhookBuilder WithAvatarUrl(string avatarUrl)
+ {
+ this.AvatarUrl = avatarUrl;
+ return this;
+ }
- /// <summary>
- /// Sets if the message has files to be sent.
- /// </summary>
- /// <param name="stream">The Stream to the file.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- /// <param name="description">Description of the file.</param>
- /// <returns></returns>
- public DiscordWebhookBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string description = null)
- {
- if (this.Files.Count > 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ /// <summary>
+ /// Indicates if the webhook must use text-to-speech.
+ /// </summary>
+ /// <param name="tts">Text-to-speech</param>
+ public DiscordWebhookBuilder WithTts(bool tts)
+ {
+ this.IsTts = tts;
+ return this;
+ }
- if (this._files.Any(x => x.FileName == stream.Name))
- throw new ArgumentException("A File with that filename already exists");
+ /// <summary>
+ /// Sets the message to send at the execution of the webhook.
+ /// </summary>
+ /// <param name="content">Message to send.</param>
+ public DiscordWebhookBuilder WithContent(string content)
+ {
+ this.Content = content;
+ return this;
+ }
- if (resetStreamPosition)
- this._files.Add(new DiscordMessageFile(stream.Name, stream, stream.Position, description: description));
- else
- this._files.Add(new DiscordMessageFile(stream.Name, stream, null, description: description));
+ /// <summary>
+ /// Adds an embed to send at the execution of the webhook.
+ /// </summary>
+ /// <param name="embed">Embed to add.</param>
+ public DiscordWebhookBuilder AddEmbed(DiscordEmbed embed)
+ {
+ if (embed != null)
+ this._embeds.Add(embed);
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Adds the given files to send at the execution of the webhook.
- /// </summary>
- /// <param name="files">Dictionary of file name and file data.</param>
- /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
- public DiscordWebhookBuilder AddFiles(Dictionary<string, Stream> files, bool resetStreamPosition = false)
- {
- if (this.Files.Count + files.Count > 10)
- throw new ArgumentException("Cannot send more than 10 files with a single message.");
+ /// <summary>
+ /// Adds the given embeds to send at the execution of the webhook.
+ /// </summary>
+ /// <param name="embeds">Embeds to add.</param>
+ public DiscordWebhookBuilder AddEmbeds(IEnumerable<DiscordEmbed> embeds)
+ {
+ this._embeds.AddRange(embeds);
+ return this;
+ }
- foreach (var file in files)
+ /// <summary>
+ /// Adds a file to send at the execution of the webhook.
+ /// </summary>
+ /// <param name="filename">Name of the file.</param>
+ /// <param name="data">File data.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <param name="description">Description of the file.</param>
+ public DiscordWebhookBuilder AddFile(string filename, Stream data, bool resetStreamPosition = false, string description = null)
{
- if (this._files.Any(x => x.FileName == file.Key))
+ if (this.Files.Count > 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
+
+ if (this._files.Any(x => x.FileName == filename))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new DiscordMessageFile(file.Key, file.Value, file.Value.Position));
+ this._files.Add(new DiscordMessageFile(filename, data, data.Position, description: description));
else
- this._files.Add(new DiscordMessageFile(file.Key, file.Value, null));
- }
-
+ this._files.Add(new DiscordMessageFile(filename, data, null, description: description));
- return this;
- }
-
- /// <summary>
- /// Modifies the given attachments on edit.
- /// </summary>
- /// <param name="attachments">Attachments to edit.</param>
- /// <returns></returns>
- public DiscordWebhookBuilder ModifyAttachments(IEnumerable<DiscordAttachment> attachments)
- {
- this.AttachmentsInternal.AddRange(attachments);
- return this;
- }
-
- /// <summary>
- /// Whether to keep the message attachments, if new ones are added.
- /// </summary>
- /// <returns></returns>
- public DiscordWebhookBuilder KeepAttachments(bool keep)
- {
- this.KeepAttachmentsInternal = keep;
- return this;
- }
-
- /// <summary>
- /// Adds the mention to the mentions to parse, etc. at the execution of the webhook.
- /// </summary>
- /// <param name="mention">Mention to add.</param>
- public DiscordWebhookBuilder AddMention(IMention mention)
- {
- this._mentions.Add(mention);
- return this;
- }
+ return this;
+ }
- /// <summary>
- /// Adds the mentions to the mentions to parse, etc. at the execution of the webhook.
- /// </summary>
- /// <param name="mentions">Mentions to add.</param>
- public DiscordWebhookBuilder AddMentions(IEnumerable<IMention> mentions)
- {
- this._mentions.AddRange(mentions);
- return this;
- }
+ /// <summary>
+ /// Sets if the message has files to be sent.
+ /// </summary>
+ /// <param name="stream">The Stream to the file.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ /// <param name="description">Description of the file.</param>
+ /// <returns></returns>
+ public DiscordWebhookBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string description = null)
+ {
+ if (this.Files.Count > 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
- /// <summary>
- /// Executes a webhook.
- /// </summary>
- /// <param name="webhook">The webhook that should be executed.</param>
- /// <returns>The message sent</returns>
- public async Task<DiscordMessage> SendAsync(DiscordWebhook webhook) => await webhook.ExecuteAsync(this).ConfigureAwait(false);
+ if (this._files.Any(x => x.FileName == stream.Name))
+ throw new ArgumentException("A File with that filename already exists");
- /// <summary>
- /// Executes a webhook.
- /// </summary>
- /// <param name="webhook">The webhook that should be executed.</param>
- /// <param name="threadId">Target thread id.</param>
- /// <returns>The message sent</returns>
- public async Task<DiscordMessage> SendAsync(DiscordWebhook webhook, ulong threadId) => await webhook.ExecuteAsync(this, threadId.ToString()).ConfigureAwait(false);
+ if (resetStreamPosition)
+ this._files.Add(new DiscordMessageFile(stream.Name, stream, stream.Position, description: description));
+ else
+ this._files.Add(new DiscordMessageFile(stream.Name, stream, null, description: description));
- /// <summary>
- /// Sends the modified webhook message.
- /// </summary>
- /// <param name="webhook">The webhook that should be executed.</param>
- /// <param name="message">The message to modify.</param>
- /// <returns>The modified message</returns>
- public async Task<DiscordMessage> ModifyAsync(DiscordWebhook webhook, DiscordMessage message) => await this.ModifyAsync(webhook, message.Id).ConfigureAwait(false);
+ return this;
+ }
- /// <summary>
- /// Sends the modified webhook message.
- /// </summary>
- /// <param name="webhook">The webhook that should be executed.</param>
- /// <param name="messageId">The id of the message to modify.</param>
- /// <returns>The modified message</returns>
- public async Task<DiscordMessage> ModifyAsync(DiscordWebhook webhook, ulong messageId) => await webhook.EditMessageAsync(messageId, this).ConfigureAwait(false);
+ /// <summary>
+ /// Adds the given files to send at the execution of the webhook.
+ /// </summary>
+ /// <param name="files">Dictionary of file name and file data.</param>
+ /// <param name="resetStreamPosition">Tells the API Client to reset the stream position to what it was after the file is sent.</param>
+ public DiscordWebhookBuilder AddFiles(Dictionary<string, Stream> files, bool resetStreamPosition = false)
+ {
+ if (this.Files.Count + files.Count > 10)
+ throw new ArgumentException("Cannot send more than 10 files with a single message.");
- /// <summary>
- /// Sends the modified webhook message.
- /// </summary>
- /// <param name="webhook">The webhook that should be executed.</param>
- /// <param name="message">The message to modify.</param>
- /// <param name="thread">Target thread.</param>
- /// <returns>The modified message</returns>
- public async Task<DiscordMessage> ModifyAsync(DiscordWebhook webhook, DiscordMessage message, DiscordThreadChannel thread) => await this.ModifyAsync(webhook, message.Id, thread.Id).ConfigureAwait(false);
+ foreach (var file in files)
+ {
+ if (this._files.Any(x => x.FileName == file.Key))
+ throw new ArgumentException("A File with that filename already exists");
- /// <summary>
- /// Sends the modified webhook message.
- /// </summary>
- /// <param name="webhook">The webhook that should be executed.</param>
- /// <param name="messageId">The id of the message to modify.</param>
- /// <param name="threadId">Target thread id.</param>
- /// <returns>The modified message</returns>
- public async Task<DiscordMessage> ModifyAsync(DiscordWebhook webhook, ulong messageId, ulong threadId) => await webhook.EditMessageAsync(messageId, this, threadId.ToString()).ConfigureAwait(false);
+ if (resetStreamPosition)
+ this._files.Add(new DiscordMessageFile(file.Key, file.Value, file.Value.Position));
+ else
+ this._files.Add(new DiscordMessageFile(file.Key, file.Value, null));
+ }
- /// <summary>
- /// Clears all message components on this builder.
- /// </summary>
- public void ClearComponents()
- => this._components.Clear();
- /// <summary>
- /// Allows for clearing the Webhook Builder so that it can be used again to send a new message.
- /// </summary>
- public void Clear()
- {
- this.Content = "";
- this._embeds.Clear();
- this.IsTts = false;
- this._mentions.Clear();
- this._files.Clear();
- this.AttachmentsInternal.Clear();
- this._components.Clear();
- this.KeepAttachmentsInternal = false;
- }
+ return this;
+ }
- /// <summary>
- /// Does the validation before we send a the Create/Modify request.
- /// </summary>
- /// <param name="isModify">Tells the method to perform the Modify Validation or Create Validation.</param>
- /// <param name="isFollowup">Tells the method to perform the follow up message validation.</param>
- /// <param name="isInteractionResponse">Tells the method to perform the interaction response validation.</param>
- internal void Validate(bool isModify = false, bool isFollowup = false, bool isInteractionResponse = false)
- {
- if (isModify)
+ /// <summary>
+ /// Modifies the given attachments on edit.
+ /// </summary>
+ /// <param name="attachments">Attachments to edit.</param>
+ /// <returns></returns>
+ public DiscordWebhookBuilder ModifyAttachments(IEnumerable<DiscordAttachment> attachments)
{
- if (this.Username.HasValue)
- throw new ArgumentException("You cannot change the username of a message.");
-
- if (this.AvatarUrl.HasValue)
- throw new ArgumentException("You cannot change the avatar of a message.");
+ this.AttachmentsInternal.AddRange(attachments);
+ return this;
}
- else if (isFollowup)
+
+ /// <summary>
+ /// Whether to keep the message attachments, if new ones are added.
+ /// </summary>
+ /// <returns></returns>
+ public DiscordWebhookBuilder KeepAttachments(bool keep)
{
- if (this.Username.HasValue)
- throw new ArgumentException("You cannot change the username of a follow up message.");
+ this.KeepAttachmentsInternal = keep;
+ return this;
+ }
- if (this.AvatarUrl.HasValue)
- throw new ArgumentException("You cannot change the avatar of a follow up message.");
+ /// <summary>
+ /// Adds the mention to the mentions to parse, etc. at the execution of the webhook.
+ /// </summary>
+ /// <param name="mention">Mention to add.</param>
+ public DiscordWebhookBuilder AddMention(IMention mention)
+ {
+ this._mentions.Add(mention);
+ return this;
}
- else if (isInteractionResponse)
+
+ /// <summary>
+ /// Adds the mentions to the mentions to parse, etc. at the execution of the webhook.
+ /// </summary>
+ /// <param name="mentions">Mentions to add.</param>
+ public DiscordWebhookBuilder AddMentions(IEnumerable<IMention> mentions)
{
- if (this.Username.HasValue)
- throw new ArgumentException("You cannot change the username of an interaction response.");
+ this._mentions.AddRange(mentions);
+ return this;
+ }
- if (this.AvatarUrl.HasValue)
- throw new ArgumentException("You cannot change the avatar of an interaction response.");
+ /// <summary>
+ /// Executes a webhook.
+ /// </summary>
+ /// <param name="webhook">The webhook that should be executed.</param>
+ /// <returns>The message sent</returns>
+ public async Task<DiscordMessage> SendAsync(DiscordWebhook webhook) => await webhook.ExecuteAsync(this).ConfigureAwait(false);
+
+ /// <summary>
+ /// Executes a webhook.
+ /// </summary>
+ /// <param name="webhook">The webhook that should be executed.</param>
+ /// <param name="threadId">Target thread id.</param>
+ /// <returns>The message sent</returns>
+ public async Task<DiscordMessage> SendAsync(DiscordWebhook webhook, ulong threadId) => await webhook.ExecuteAsync(this, threadId.ToString()).ConfigureAwait(false);
+
+ /// <summary>
+ /// Sends the modified webhook message.
+ /// </summary>
+ /// <param name="webhook">The webhook that should be executed.</param>
+ /// <param name="message">The message to modify.</param>
+ /// <returns>The modified message</returns>
+ public async Task<DiscordMessage> ModifyAsync(DiscordWebhook webhook, DiscordMessage message) => await this.ModifyAsync(webhook, message.Id).ConfigureAwait(false);
+
+ /// <summary>
+ /// Sends the modified webhook message.
+ /// </summary>
+ /// <param name="webhook">The webhook that should be executed.</param>
+ /// <param name="messageId">The id of the message to modify.</param>
+ /// <returns>The modified message</returns>
+ public async Task<DiscordMessage> ModifyAsync(DiscordWebhook webhook, ulong messageId) => await webhook.EditMessageAsync(messageId, this).ConfigureAwait(false);
+
+ /// <summary>
+ /// Sends the modified webhook message.
+ /// </summary>
+ /// <param name="webhook">The webhook that should be executed.</param>
+ /// <param name="message">The message to modify.</param>
+ /// <param name="thread">Target thread.</param>
+ /// <returns>The modified message</returns>
+ public async Task<DiscordMessage> ModifyAsync(DiscordWebhook webhook, DiscordMessage message, DiscordThreadChannel thread) => await this.ModifyAsync(webhook, message.Id, thread.Id).ConfigureAwait(false);
+
+ /// <summary>
+ /// Sends the modified webhook message.
+ /// </summary>
+ /// <param name="webhook">The webhook that should be executed.</param>
+ /// <param name="messageId">The id of the message to modify.</param>
+ /// <param name="threadId">Target thread id.</param>
+ /// <returns>The modified message</returns>
+ public async Task<DiscordMessage> ModifyAsync(DiscordWebhook webhook, ulong messageId, ulong threadId) => await webhook.EditMessageAsync(messageId, this, threadId.ToString()).ConfigureAwait(false);
+
+ /// <summary>
+ /// Clears all message components on this builder.
+ /// </summary>
+ public void ClearComponents()
+ => this._components.Clear();
+
+ /// <summary>
+ /// Allows for clearing the Webhook Builder so that it can be used again to send a new message.
+ /// </summary>
+ public void Clear()
+ {
+ this.Content = "";
+ this._embeds.Clear();
+ this.IsTts = false;
+ this._mentions.Clear();
+ this._files.Clear();
+ this.AttachmentsInternal.Clear();
+ this._components.Clear();
+ this.KeepAttachmentsInternal = false;
}
- else
+
+ /// <summary>
+ /// Does the validation before we send a the Create/Modify request.
+ /// </summary>
+ /// <param name="isModify">Tells the method to perform the Modify Validation or Create Validation.</param>
+ /// <param name="isFollowup">Tells the method to perform the follow up message validation.</param>
+ /// <param name="isInteractionResponse">Tells the method to perform the interaction response validation.</param>
+ internal void Validate(bool isModify = false, bool isFollowup = false, bool isInteractionResponse = false)
{
- if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && !this.Embeds.Any())
- throw new ArgumentException("You must specify content, an embed, or at least one file.");
+ if (isModify)
+ {
+ if (this.Username.HasValue)
+ throw new ArgumentException("You cannot change the username of a message.");
+
+ if (this.AvatarUrl.HasValue)
+ throw new ArgumentException("You cannot change the avatar of a message.");
+ }
+ else if (isFollowup)
+ {
+ if (this.Username.HasValue)
+ throw new ArgumentException("You cannot change the username of a follow up message.");
+
+ if (this.AvatarUrl.HasValue)
+ throw new ArgumentException("You cannot change the avatar of a follow up message.");
+ }
+ else if (isInteractionResponse)
+ {
+ if (this.Username.HasValue)
+ throw new ArgumentException("You cannot change the username of an interaction response.");
+
+ if (this.AvatarUrl.HasValue)
+ throw new ArgumentException("You cannot change the avatar of an interaction response.");
+ }
+ else
+ {
+ if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && !this.Embeds.Any())
+ throw new ArgumentException("You must specify content, an embed, or at least one file.");
+ }
}
}
}
diff --git a/DisCatSharp/Entities/Widget/DiscordWidget.cs b/DisCatSharp/Entities/Widget/DiscordWidget.cs
index 90a9e0fc1..981b8c9bd 100644
--- a/DisCatSharp/Entities/Widget/DiscordWidget.cs
+++ b/DisCatSharp/Entities/Widget/DiscordWidget.cs
@@ -1,69 +1,70 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord guild's widget.
-/// </summary>
-public class DiscordWidget : SnowflakeObject
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the guild.
+ /// Represents a Discord guild's widget.
/// </summary>
- [JsonIgnore]
- public DiscordGuild Guild { get; internal set; }
+ public class DiscordWidget : SnowflakeObject
+ {
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ [JsonIgnore]
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the guild's name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; internal set; }
+ /// <summary>
+ /// Gets the guild's name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets the guild's invite URL.
- /// </summary>
- [JsonProperty("instant_invite", NullValueHandling = NullValueHandling.Ignore)]
- public string InstantInviteUrl { get; internal set; }
+ /// <summary>
+ /// Gets the guild's invite URL.
+ /// </summary>
+ [JsonProperty("instant_invite", NullValueHandling = NullValueHandling.Ignore)]
+ public string InstantInviteUrl { get; internal set; }
- /// <summary>
- /// Gets the number of online members.
- /// </summary>
- [JsonProperty("presence_count", NullValueHandling = NullValueHandling.Ignore)]
- public int PresenceCount { get; internal set; }
+ /// <summary>
+ /// Gets the number of online members.
+ /// </summary>
+ [JsonProperty("presence_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int PresenceCount { get; internal set; }
- /// <summary>
- /// Gets a list of online members.
- /// </summary>
- [JsonProperty("members", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<DiscordWidgetMember> Members { get; internal set; }
+ /// <summary>
+ /// Gets a list of online members.
+ /// </summary>
+ [JsonProperty("members", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<DiscordWidgetMember> Members { get; internal set; }
- /// <summary>
- /// Gets a list of widget channels.
- /// </summary>
- [JsonIgnore]
- public IReadOnlyList<DiscordChannel> Channels { get; internal set; }
+ /// <summary>
+ /// Gets a list of widget channels.
+ /// </summary>
+ [JsonIgnore]
+ public IReadOnlyList<DiscordChannel> Channels { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Widget/DiscordWidgetMember.cs b/DisCatSharp/Entities/Widget/DiscordWidgetMember.cs
index dec35d4bf..4859c2f08 100644
--- a/DisCatSharp/Entities/Widget/DiscordWidgetMember.cs
+++ b/DisCatSharp/Entities/Widget/DiscordWidgetMember.cs
@@ -1,67 +1,68 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a member within a Discord guild's widget.
-/// </summary>
-public class DiscordWidgetMember
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the member's identifier within the widget.
+ /// Represents a member within a Discord guild's widget.
/// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong Id { get; internal set; }
+ public class DiscordWidgetMember
+ {
+ /// <summary>
+ /// Gets the member's identifier within the widget.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong Id { get; internal set; }
- /// <summary>
- /// Gets the member's username.
- /// </summary>
- [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
- public string Username { get; internal set; }
+ /// <summary>
+ /// Gets the member's username.
+ /// </summary>
+ [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
+ public string Username { get; internal set; }
- /// <summary>
- /// Gets the member's discriminator.
- /// </summary>
- [JsonProperty("discriminator", NullValueHandling = NullValueHandling.Ignore)]
- public string Discriminator { get; internal set; }
+ /// <summary>
+ /// Gets the member's discriminator.
+ /// </summary>
+ [JsonProperty("discriminator", NullValueHandling = NullValueHandling.Ignore)]
+ public string Discriminator { get; internal set; }
- /// <summary>
- /// Gets the member's avatar.
- /// </summary>
- [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
- public string Avatar { get; internal set; }
+ /// <summary>
+ /// Gets the member's avatar.
+ /// </summary>
+ [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
+ public string Avatar { get; internal set; }
- /// <summary>
- /// Gets the member's online status.
- /// </summary>
- [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
- public string Status { get; internal set; }
+ /// <summary>
+ /// Gets the member's online status.
+ /// </summary>
+ [JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
+ public string Status { get; internal set; }
- /// <summary>
- /// Gets the member's avatar url.
- /// </summary>
- [JsonProperty("avatar_url", NullValueHandling = NullValueHandling.Ignore)]
- public string AvatarUrl { get; internal set; }
+ /// <summary>
+ /// Gets the member's avatar url.
+ /// </summary>
+ [JsonProperty("avatar_url", NullValueHandling = NullValueHandling.Ignore)]
+ public string AvatarUrl { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Entities/Widget/DiscordWidgetSettings.cs b/DisCatSharp/Entities/Widget/DiscordWidgetSettings.cs
index e10acbc23..f9d339edb 100644
--- a/DisCatSharp/Entities/Widget/DiscordWidgetSettings.cs
+++ b/DisCatSharp/Entities/Widget/DiscordWidgetSettings.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Entities;
-
-/// <summary>
-/// Represents a Discord guild's widget settings.
-/// </summary>
-public class DiscordWidgetSettings
+namespace DisCatSharp.Entities
{
/// <summary>
- /// Gets the guild.
+ /// Represents a Discord guild's widget settings.
/// </summary>
- internal DiscordGuild Guild { get; set; }
+ public class DiscordWidgetSettings
+ {
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ internal DiscordGuild Guild { get; set; }
- /// <summary>
- /// Gets the guild's widget channel id.
- /// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; internal set; }
+ /// <summary>
+ /// Gets the guild's widget channel id.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; internal set; }
- /// <summary>
- /// Gets the guild's widget channel.
- /// </summary>
- public DiscordChannel Channel
- => this.Guild?.GetChannel(this.ChannelId);
+ /// <summary>
+ /// Gets the guild's widget channel.
+ /// </summary>
+ public DiscordChannel Channel
+ => this.Guild?.GetChannel(this.ChannelId);
- /// <summary>
- /// Whether if the guild's widget is enabled.
- /// </summary>
- [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsEnabled { get; internal set; }
+ /// <summary>
+ /// Whether if the guild's widget is enabled.
+ /// </summary>
+ [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsEnabled { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Enums/Application/ApplicationCommandOptionType.cs b/DisCatSharp/Enums/Application/ApplicationCommandOptionType.cs
index acbfda267..5450b71b1 100644
--- a/DisCatSharp/Enums/Application/ApplicationCommandOptionType.cs
+++ b/DisCatSharp/Enums/Application/ApplicationCommandOptionType.cs
@@ -1,84 +1,85 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the type of parameter when invoking an interaction.
-/// </summary>
-public enum ApplicationCommandOptionType
+namespace DisCatSharp
{
/// <summary>
- /// Whether this parameter is another subcommand.
+ /// Represents the type of parameter when invoking an interaction.
/// </summary>
- SubCommand = 1,
+ public enum ApplicationCommandOptionType
+ {
+ /// <summary>
+ /// Whether this parameter is another subcommand.
+ /// </summary>
+ SubCommand = 1,
- /// <summary>
- /// Whether this parameter is apart of a subcommand group.
- /// </summary>
- SubCommandGroup = 2,
+ /// <summary>
+ /// Whether this parameter is apart of a subcommand group.
+ /// </summary>
+ SubCommandGroup = 2,
- /// <summary>
- /// Whether this parameter is a string.
- /// </summary>
- String = 3,
+ /// <summary>
+ /// Whether this parameter is a string.
+ /// </summary>
+ String = 3,
- /// <summary>
- /// Whether this parameter is an integer.
- /// </summary>
- Integer = 4,
+ /// <summary>
+ /// Whether this parameter is an integer.
+ /// </summary>
+ Integer = 4,
- /// <summary>
- /// Whether this parameter is a boolean.
- /// </summary>
- Boolean = 5,
+ /// <summary>
+ /// Whether this parameter is a boolean.
+ /// </summary>
+ Boolean = 5,
- /// <summary>
- /// Whether this parameter is a Discord user.
- /// </summary>
- User = 6,
+ /// <summary>
+ /// Whether this parameter is a Discord user.
+ /// </summary>
+ User = 6,
- /// <summary>
- /// Whether this parameter is a Discord channel.
- /// </summary>
- Channel = 7,
+ /// <summary>
+ /// Whether this parameter is a Discord channel.
+ /// </summary>
+ Channel = 7,
- /// <summary>
- /// Whether this parameter is a Discord role.
- /// </summary>
- Role = 8,
+ /// <summary>
+ /// Whether this parameter is a Discord role.
+ /// </summary>
+ Role = 8,
- /// <summary>
- /// Whether this parameter is a mentionable.
- /// </summary>
- Mentionable = 9,
+ /// <summary>
+ /// Whether this parameter is a mentionable.
+ /// </summary>
+ Mentionable = 9,
- /// <summary>
- /// Whether this parameter is a number.
- /// </summary>
- Number = 10,
+ /// <summary>
+ /// Whether this parameter is a number.
+ /// </summary>
+ Number = 10,
- /// <summary>
- /// Whether this parameter is a attachment.
- /// </summary>
- Attachment = 11
+ /// <summary>
+ /// Whether this parameter is a attachment.
+ /// </summary>
+ Attachment = 11
+ }
}
diff --git a/DisCatSharp/Enums/Application/ApplicationCommandPermissionType.cs b/DisCatSharp/Enums/Application/ApplicationCommandPermissionType.cs
index c1652cf2e..a0ecf0761 100644
--- a/DisCatSharp/Enums/Application/ApplicationCommandPermissionType.cs
+++ b/DisCatSharp/Enums/Application/ApplicationCommandPermissionType.cs
@@ -1,47 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
+namespace DisCatSharp
+{ /// <summary>
+ /// Represents the type of the application command permission.
+ /// </summary>
+ [Flags]
+ public enum ApplicationCommandPermissionType
+ {
+ /// <summary>
+ /// The permission is bound to a role.
+ /// </summary>
+ Role = 1,
-/// <summary>
- /// Represents the type of the application command permission.
- /// </summary>
-[Flags]
-public enum ApplicationCommandPermissionType
-{
- /// <summary>
- /// The permission is bound to a role.
- /// </summary>
- Role = 1,
+ /// <summary>
+ /// The permission is bound to a user.
+ /// </summary>
+ User = 2,
- /// <summary>
- /// The permission is bound to a user.
- /// </summary>
- User = 2,
-
- /// <summary>
- /// The permission is bound to a channel.
- /// </summary>
- Channel = 3
+ /// <summary>
+ /// The permission is bound to a channel.
+ /// </summary>
+ Channel = 3
+ }
}
diff --git a/DisCatSharp/Enums/Application/ApplicationCommandType.cs b/DisCatSharp/Enums/Application/ApplicationCommandType.cs
index b082f3a57..af5eb5197 100644
--- a/DisCatSharp/Enums/Application/ApplicationCommandType.cs
+++ b/DisCatSharp/Enums/Application/ApplicationCommandType.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Enums;
-
-/// <summary>
-/// Represents the type of an <see cref="DisCatSharp.Entities.DiscordApplicationCommand"/>.
-/// </summary>
-public enum ApplicationCommandType
+namespace DisCatSharp.Enums
{
/// <summary>
- /// This command is registered as a slash-command, aka "Chat Input".
+ /// Represents the type of an <see cref="DisCatSharp.Entities.DiscordApplicationCommand"/>.
/// </summary>
- ChatInput = 1,
+ public enum ApplicationCommandType
+ {
+ /// <summary>
+ /// This command is registered as a slash-command, aka "Chat Input".
+ /// </summary>
+ ChatInput = 1,
- /// <summary>
- /// This command is registered as a user context menu, and is applicable when interacting a user.
- /// </summary>
- User = 2,
+ /// <summary>
+ /// This command is registered as a user context menu, and is applicable when interacting a user.
+ /// </summary>
+ User = 2,
- /// <summary>
- /// This command is registered as a message context menu, and is applicable when interacting with a message.
- /// </summary>
- Message = 3,
+ /// <summary>
+ /// This command is registered as a message context menu, and is applicable when interacting with a message.
+ /// </summary>
+ Message = 3,
- /// <summary>
- /// Inbound only: An auto-complete option is being interacted with.
- /// </summary>
- AutoCompleteRequest = 4,
+ /// <summary>
+ /// Inbound only: An auto-complete option is being interacted with.
+ /// </summary>
+ AutoCompleteRequest = 4,
- /// <summary>
- /// Inbound only: A modal was submitted.
- /// </summary>
- ModalSubmit = 5
+ /// <summary>
+ /// Inbound only: A modal was submitted.
+ /// </summary>
+ ModalSubmit = 5
+ }
}
diff --git a/DisCatSharp/Enums/Application/ApplicationFlags.cs b/DisCatSharp/Enums/Application/ApplicationFlags.cs
index 9e1ba73df..9a2730cf1 100644
--- a/DisCatSharp/Enums/Application/ApplicationFlags.cs
+++ b/DisCatSharp/Enums/Application/ApplicationFlags.cs
@@ -1,98 +1,99 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents additional details of an application.
-/// </summary>
-[Flags]
-public enum ApplicationFlags
+namespace DisCatSharp
{
/// <summary>
- /// The application is embedded and can be used by users.
- /// This was introdruced to avoid users using in-dev apps.
+ /// Represents additional details of an application.
/// </summary>
- EmbeddedReleased = 1 << 1,
+ [Flags]
+ public enum ApplicationFlags
+ {
+ /// <summary>
+ /// The application is embedded and can be used by users.
+ /// This was introdruced to avoid users using in-dev apps.
+ /// </summary>
+ EmbeddedReleased = 1 << 1,
- /// <summary>
- /// The application is a managed emoji.
- /// </summary>
- ManagedEmoji = 1 << 2,
+ /// <summary>
+ /// The application is a managed emoji.
+ /// </summary>
+ ManagedEmoji = 1 << 2,
- /// <summary>
- /// The application can create group dms.
- /// </summary>
- GroupDmCreate = 1 << 5,
+ /// <summary>
+ /// The application can create group dms.
+ /// </summary>
+ GroupDmCreate = 1 << 5,
- /// <summary>
- /// The application has connected to RPC.
- /// </summary>
- RpcHasConnected = 1 << 11,
+ /// <summary>
+ /// The application has connected to RPC.
+ /// </summary>
+ RpcHasConnected = 1 << 11,
- /// <summary>
- /// The application can track presence data.
- /// </summary>
- GatewayPresence = 1 << 12,
+ /// <summary>
+ /// The application can track presence data.
+ /// </summary>
+ GatewayPresence = 1 << 12,
- /// <summary>
- /// The application can track presence data (limited).
- /// </summary>
- GatewayPresenceLimited = 1 << 13,
+ /// <summary>
+ /// The application can track presence data (limited).
+ /// </summary>
+ GatewayPresenceLimited = 1 << 13,
- /// <summary>
- /// The application can track guild members.
- /// </summary>
- GatewayGuildMembers = 1 << 14,
+ /// <summary>
+ /// The application can track guild members.
+ /// </summary>
+ GatewayGuildMembers = 1 << 14,
- /// <summary>
- /// The application can track guild members (limited).
- /// </summary>
- GatewayGuildMembersLimited = 1 << 15,
+ /// <summary>
+ /// The application can track guild members (limited).
+ /// </summary>
+ GatewayGuildMembersLimited = 1 << 15,
- /// <summary>
- /// The application can track pending guild member verifications (limited).
- /// </summary>
- VerificationPendingGuildLimit = 1 << 16,
+ /// <summary>
+ /// The application can track pending guild member verifications (limited).
+ /// </summary>
+ VerificationPendingGuildLimit = 1 << 16,
- /// <summary>
- /// The application is embedded.
- /// </summary>
- Embedded = 1 << 17,
+ /// <summary>
+ /// The application is embedded.
+ /// </summary>
+ Embedded = 1 << 17,
- /// <summary>
- /// The application can track message content.
- /// </summary>
- GatewayMessageContent = 1 << 18,
+ /// <summary>
+ /// The application can track message content.
+ /// </summary>
+ GatewayMessageContent = 1 << 18,
- /// <summary>
- /// The application can track message content (limited).
- /// </summary>
- GatewayMessageContentLimited = 1 << 19,
+ /// <summary>
+ /// The application can track message content (limited).
+ /// </summary>
+ GatewayMessageContentLimited = 1 << 19,
- /// <summary>
- /// Related to embedded applications.
- /// </summary>
- EmbeddedFirstParty = 1 << 20
+ /// <summary>
+ /// Related to embedded applications.
+ /// </summary>
+ EmbeddedFirstParty = 1 << 20
+ }
}
diff --git a/DisCatSharp/Enums/Channel/ChannelFlags.cs b/DisCatSharp/Enums/Channel/ChannelFlags.cs
index 34e10bf2b..ea0d80a78 100644
--- a/DisCatSharp/Enums/Channel/ChannelFlags.cs
+++ b/DisCatSharp/Enums/Channel/ChannelFlags.cs
@@ -1,40 +1,41 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a channel's flags.
-/// </summary>
-public enum ChannelFlags : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that this channel is removed from the guilds home feed.
+ /// Represents a channel's flags.
/// </summary>
- RemovedFromHome = 1 << 0,
+ public enum ChannelFlags : int
+ {
+ /// <summary>
+ /// Indicates that this channel is removed from the guilds home feed.
+ /// </summary>
+ RemovedFromHome = 1 << 0,
- /// <summary>
- /// Indicates that this thread is pinned to the top of its parent forum channel.
- /// Forum channel thread only.
- /// </summary>
- Pinned = 1 << 1
+ /// <summary>
+ /// Indicates that this thread is pinned to the top of its parent forum channel.
+ /// Forum channel thread only.
+ /// </summary>
+ Pinned = 1 << 1
+ }
}
diff --git a/DisCatSharp/Enums/Channel/ChannelType.cs b/DisCatSharp/Enums/Channel/ChannelType.cs
index 8fbd96e43..26c2c01f4 100644
--- a/DisCatSharp/Enums/Channel/ChannelType.cs
+++ b/DisCatSharp/Enums/Channel/ChannelType.cs
@@ -1,101 +1,102 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a channel's type.
-/// </summary>
-public enum ChannelType : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that this is a text channel.
+ /// Represents a channel's type.
/// </summary>
- Text = 0,
+ public enum ChannelType : int
+ {
+ /// <summary>
+ /// Indicates that this is a text channel.
+ /// </summary>
+ Text = 0,
- /// <summary>
- /// Indicates that this is a private channel.
- /// </summary>
- Private = 1,
+ /// <summary>
+ /// Indicates that this is a private channel.
+ /// </summary>
+ Private = 1,
- /// <summary>
- /// Indicates that this is a voice channel.
- /// </summary>
- Voice = 2,
+ /// <summary>
+ /// Indicates that this is a voice channel.
+ /// </summary>
+ Voice = 2,
- /// <summary>
- /// Indicates that this is a group direct message channel.
- /// </summary>
- Group = 3,
+ /// <summary>
+ /// Indicates that this is a group direct message channel.
+ /// </summary>
+ Group = 3,
- /// <summary>
- /// Indicates that this is a channel category.
- /// </summary>
- Category = 4,
+ /// <summary>
+ /// Indicates that this is a channel category.
+ /// </summary>
+ Category = 4,
- /// <summary>
- /// Indicates that this is a news channel.
- /// </summary>
- News = 5,
+ /// <summary>
+ /// Indicates that this is a news channel.
+ /// </summary>
+ News = 5,
- /// <summary>
- /// Indicates that this is a store channel.
- /// </summary>
- Store = 6,
+ /// <summary>
+ /// Indicates that this is a store channel.
+ /// </summary>
+ Store = 6,
- /// <summary>
- /// Indicates that this is a temporary sub-channel within a news channel.
- /// </summary>
- NewsThread = 10,
+ /// <summary>
+ /// Indicates that this is a temporary sub-channel within a news channel.
+ /// </summary>
+ NewsThread = 10,
- /// <summary>
- /// Indicates that this is a temporary sub-channel within a text channel.
- /// </summary>
- PublicThread = 11,
+ /// <summary>
+ /// Indicates that this is a temporary sub-channel within a text channel.
+ /// </summary>
+ PublicThread = 11,
- /// <summary>
- /// Indicates that this is a temporary sub-channel within a text channel that is only viewable
- /// by those invited and those with the MANAGE_THREADS permission.
- /// </summary>
- PrivateThread = 12,
+ /// <summary>
+ /// Indicates that this is a temporary sub-channel within a text channel that is only viewable
+ /// by those invited and those with the MANAGE_THREADS permission.
+ /// </summary>
+ PrivateThread = 12,
- /// <summary>
- /// Indicates that this is a stage channel.
- /// </summary>
- Stage = 13,
+ /// <summary>
+ /// Indicates that this is a stage channel.
+ /// </summary>
+ Stage = 13,
- /// <summary>
- /// Indicates that this is a guild directory channel.
- /// This is used for hub guilds (feature for schools).
- /// </summary>
- GuildDirectory = 14,
+ /// <summary>
+ /// Indicates that this is a guild directory channel.
+ /// This is used for hub guilds (feature for schools).
+ /// </summary>
+ GuildDirectory = 14,
- /// <summary>
- /// Indicates that this is a guild forum channel (Threads only channel).
- /// </summary>
- Forum = 15,
+ /// <summary>
+ /// Indicates that this is a guild forum channel (Threads only channel).
+ /// </summary>
+ Forum = 15,
- /// <summary>
- /// Indicates unknown channel type.
- /// </summary>
- Unknown = int.MaxValue
+ /// <summary>
+ /// Indicates unknown channel type.
+ /// </summary>
+ Unknown = int.MaxValue
+ }
}
diff --git a/DisCatSharp/Enums/Channel/DirectoryCategory.cs b/DisCatSharp/Enums/Channel/DirectoryCategory.cs
index 416ecb0b9..0202ac0d8 100644
--- a/DisCatSharp/Enums/Channel/DirectoryCategory.cs
+++ b/DisCatSharp/Enums/Channel/DirectoryCategory.cs
@@ -1,59 +1,60 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a directory entries primary category type.
-/// </summary>
-public enum DirectoryCategory : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that this entry falls under the category Clubs.
+ /// Represents a directory entries primary category type.
/// </summary>
- Clubs = 1,
+ public enum DirectoryCategory : int
+ {
+ /// <summary>
+ /// Indicates that this entry falls under the category Clubs.
+ /// </summary>
+ Clubs = 1,
- /// <summary>
- /// Indicates that this entry falls under the category Classes.
- /// </summary>
- Classes = 2,
+ /// <summary>
+ /// Indicates that this entry falls under the category Classes.
+ /// </summary>
+ Classes = 2,
- /// <summary>
- /// Indicates that this entry falls under the category Social and Study.
- /// </summary>
- SocialAndStudy = 3,
+ /// <summary>
+ /// Indicates that this entry falls under the category Social and Study.
+ /// </summary>
+ SocialAndStudy = 3,
- /// <summary>
- /// Indicates that this entry falls under the category Majors and Subjects.
- /// </summary>
- MajorsAndSubjects = 4,
+ /// <summary>
+ /// Indicates that this entry falls under the category Majors and Subjects.
+ /// </summary>
+ MajorsAndSubjects = 4,
- /// <summary>
- /// Indicates that this entry falls under the category Miscellaneous.
- /// </summary>
- Miscellaneous = 5,
+ /// <summary>
+ /// Indicates that this entry falls under the category Miscellaneous.
+ /// </summary>
+ Miscellaneous = 5,
- /// <summary>
- /// Indicates unknown category type.
- /// </summary>
- Unknown = int.MaxValue
+ /// <summary>
+ /// Indicates unknown category type.
+ /// </summary>
+ Unknown = int.MaxValue
+ }
}
diff --git a/DisCatSharp/Enums/Channel/OverwriteType.cs b/DisCatSharp/Enums/Channel/OverwriteType.cs
index bb336b64b..470b5d65b 100644
--- a/DisCatSharp/Enums/Channel/OverwriteType.cs
+++ b/DisCatSharp/Enums/Channel/OverwriteType.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a channel permission overwrite's type.
-/// </summary>
-public enum OverwriteType : int
+namespace DisCatSharp
{
/// <summary>
- /// Specifies that this overwrite applies to a role.
+ /// Represents a channel permission overwrite's type.
/// </summary>
- Role = 0,
+ public enum OverwriteType : int
+ {
+ /// <summary>
+ /// Specifies that this overwrite applies to a role.
+ /// </summary>
+ Role = 0,
- /// <summary>
- /// Specifies that this overwrite applies to a member.
- /// </summary>
- Member = 1,
+ /// <summary>
+ /// Specifies that this overwrite applies to a member.
+ /// </summary>
+ Member = 1,
+ }
}
diff --git a/DisCatSharp/Enums/Channel/VideoQualityMode.cs b/DisCatSharp/Enums/Channel/VideoQualityMode.cs
index 0ac8f3e9c..c0f0c80fa 100644
--- a/DisCatSharp/Enums/Channel/VideoQualityMode.cs
+++ b/DisCatSharp/Enums/Channel/VideoQualityMode.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the video quality mode of a voice channel. This is applicable to voice channels only.
-/// </summary>
-public enum VideoQualityMode : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that the video quality is automatically chosen, or there is no value set.
+ /// Represents the video quality mode of a voice channel. This is applicable to voice channels only.
/// </summary>
- Auto = 1,
+ public enum VideoQualityMode : int
+ {
+ /// <summary>
+ /// Indicates that the video quality is automatically chosen, or there is no value set.
+ /// </summary>
+ Auto = 1,
- /// <summary>
- /// Indicates that the video quality is 720p.
- /// </summary>
- Full = 2,
+ /// <summary>
+ /// Indicates that the video quality is 720p.
+ /// </summary>
+ Full = 2,
+ }
}
diff --git a/DisCatSharp/Enums/Discord/DiscordDomain.cs b/DisCatSharp/Enums/Discord/DiscordDomain.cs
index 09c24911b..d28a40277 100644
--- a/DisCatSharp/Enums/Discord/DiscordDomain.cs
+++ b/DisCatSharp/Enums/Discord/DiscordDomain.cs
@@ -1,268 +1,269 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.Enums;
-
-/// <summary>
-/// Core Domains
-/// </summary>
-public enum CoreDomain
+namespace DisCatSharp.Enums
{
/// <summary>
- /// dis.gd
- /// </summary>
- [DomainHelp("Marketing URL shortener", "dis.gd")]
- DiscordMarketing = 1,
-
- /// <summary>
- /// discord.co
- /// </summary>
- [DomainHelp("Admin panel, internal tools", "discord.co")]
- DiscordAdmin = 2,
-
- /// <summary>
- /// discord.com
- /// </summary>
- [DomainHelp("New app, marketing website, API host", "discord.com")]
- Discord = 3,
-
- /// <summary>
- /// discord.design
- /// </summary>
- [DomainHelp("Dribbble profile shortlink", "discord.design")]
- DiscordDesign = 4,
-
- /// <summary>
- /// discord.dev
- /// </summary>
- [DomainHelp("Developer site shortlinks", "discord.dev")]
- DiscordDev = 5,
-
- /// <summary>
- /// discord.gg
- /// </summary>
- [DomainHelp("Invite shortlinks", "discord.gg")]
- DiscordShortlink = 6,
-
- /// <summary>
- /// discord.gift
- /// </summary>
- [DomainHelp("Gift shortlinks", "discord.gift")]
- DiscordGift = 7,
-
- /// <summary>
- /// discord.media
- /// </summary>
- [DomainHelp("Voice servers", "discord.media")]
- DiscordMedia = 8,
-
- /// <summary>
- /// discord.new
+ /// Core Domains
/// </summary>
- [DomainHelp("Template shortlinks", "discord.new")]
- DiscordTemplate = 9,
-
- /// <summary>
- /// discord.store
- /// </summary>
- [DomainHelp("Merch store", "discord.store")]
- DiscordMerch = 10,
-
- /// <summary>
- /// discord.tools
- /// </summary>
- [DomainHelp("Internal tools", "discord.tools")]
- DiscordTools = 11,
-
- /// <summary>
- /// discordapp.com
- /// </summary>
- [DomainHelp("Old app, marketing website, and API; CDN", "discordapp.com")]
- DiscordAppOld = 12,
-
- /// <summary>
- /// discordapp.net
- /// </summary>
- [DomainHelp("Media Proxy", "discordapp.net")]
- DiscordAppMediaProxy = 13,
-
- /// <summary>
- /// discordmerch.com
- /// </summary>
- [DomainHelp("Merch store", "discordmerch.com")]
- DiscordMerchOld = 14,
-
- /// <summary>
- /// discordpartygames.com
- /// </summary>
- [DomainHelp("Voice channel activity API host", "discordpartygames.com")]
- DiscordActivityAlt = 15,
-
- /// <summary>
- /// discord-activities.com
- /// </summary>
- [DomainHelp("Voice channel activity API host", "discord-activities.com")]
- DiscordActivityAlt2 = 16,
-
- /// <summary>
- /// discordsays.com
- /// </summary>
- [DomainHelp("Voice channel activity host", "discordsays.com")]
- DiscordActivity = 17,
-
- /// <summary>
- /// discordstatus.com
- /// </summary>
- [DomainHelp("Status page", "discordstatus.com")]
- DiscordStatus = 18,
-
- /// <summary>
- /// cdn.discordapp.com
- /// </summary>
- [DomainHelp("CDN", "cdn.discordapp.com")]
- DiscordCdn = 19,
-
-}
-
-/// <summary>
-/// Other Domains
-/// </summary>
-public enum OtherDomain
-{
- /// <summary>
- /// airhorn.solutions
- /// </summary>
- [DomainHelp("API implementation example", "airhorn.solutions")]
- Airhorn = 1,
-
- /// <summary>
- /// airhornbot.com
- /// </summary>
- [DomainHelp("API implementation example", "airhornbot.com")]
- AirhornAlt = 2,
-
- /// <summary>
- /// bigbeans.solutions
- /// </summary>
- [DomainHelp("April Fools 2017", "bigbeans.solutions")]
- AprilFools = 3,
-
- /// <summary>
- /// watchanimeattheoffice.com
- /// </summary>
- [DomainHelp("HypeSquad form placeholder/meme", "watchanimeattheoffice.com")]
- HypeSquadMeme = 4
-}
+ public enum CoreDomain
+ {
+ /// <summary>
+ /// dis.gd
+ /// </summary>
+ [DomainHelp("Marketing URL shortener", "dis.gd")]
+ DiscordMarketing = 1,
+
+ /// <summary>
+ /// discord.co
+ /// </summary>
+ [DomainHelp("Admin panel, internal tools", "discord.co")]
+ DiscordAdmin = 2,
+
+ /// <summary>
+ /// discord.com
+ /// </summary>
+ [DomainHelp("New app, marketing website, API host", "discord.com")]
+ Discord = 3,
+
+ /// <summary>
+ /// discord.design
+ /// </summary>
+ [DomainHelp("Dribbble profile shortlink", "discord.design")]
+ DiscordDesign = 4,
+
+ /// <summary>
+ /// discord.dev
+ /// </summary>
+ [DomainHelp("Developer site shortlinks", "discord.dev")]
+ DiscordDev = 5,
+
+ /// <summary>
+ /// discord.gg
+ /// </summary>
+ [DomainHelp("Invite shortlinks", "discord.gg")]
+ DiscordShortlink = 6,
+
+ /// <summary>
+ /// discord.gift
+ /// </summary>
+ [DomainHelp("Gift shortlinks", "discord.gift")]
+ DiscordGift = 7,
+
+ /// <summary>
+ /// discord.media
+ /// </summary>
+ [DomainHelp("Voice servers", "discord.media")]
+ DiscordMedia = 8,
+
+ /// <summary>
+ /// discord.new
+ /// </summary>
+ [DomainHelp("Template shortlinks", "discord.new")]
+ DiscordTemplate = 9,
+
+ /// <summary>
+ /// discord.store
+ /// </summary>
+ [DomainHelp("Merch store", "discord.store")]
+ DiscordMerch = 10,
+
+ /// <summary>
+ /// discord.tools
+ /// </summary>
+ [DomainHelp("Internal tools", "discord.tools")]
+ DiscordTools = 11,
+
+ /// <summary>
+ /// discordapp.com
+ /// </summary>
+ [DomainHelp("Old app, marketing website, and API; CDN", "discordapp.com")]
+ DiscordAppOld = 12,
+
+ /// <summary>
+ /// discordapp.net
+ /// </summary>
+ [DomainHelp("Media Proxy", "discordapp.net")]
+ DiscordAppMediaProxy = 13,
+
+ /// <summary>
+ /// discordmerch.com
+ /// </summary>
+ [DomainHelp("Merch store", "discordmerch.com")]
+ DiscordMerchOld = 14,
+
+ /// <summary>
+ /// discordpartygames.com
+ /// </summary>
+ [DomainHelp("Voice channel activity API host", "discordpartygames.com")]
+ DiscordActivityAlt = 15,
+
+ /// <summary>
+ /// discord-activities.com
+ /// </summary>
+ [DomainHelp("Voice channel activity API host", "discord-activities.com")]
+ DiscordActivityAlt2 = 16,
+
+ /// <summary>
+ /// discordsays.com
+ /// </summary>
+ [DomainHelp("Voice channel activity host", "discordsays.com")]
+ DiscordActivity = 17,
+
+ /// <summary>
+ /// discordstatus.com
+ /// </summary>
+ [DomainHelp("Status page", "discordstatus.com")]
+ DiscordStatus = 18,
+
+ /// <summary>
+ /// cdn.discordapp.com
+ /// </summary>
+ [DomainHelp("CDN", "cdn.discordapp.com")]
+ DiscordCdn = 19,
-/// <summary>
-/// Unused Domains
-/// </summary>
-public enum UnusedDomain
-{
- /// <summary>
- /// discordapp.io
- /// </summary>
- [Obsolete("Not in use.", false)]
- [DomainHelp("IO domain for discord", "discordapp.io")]
- DiscordAppIo = 1,
+ }
/// <summary>
- /// discordcdn.com
+ /// Other Domains
/// </summary>
- [Obsolete("Not in use.", false)]
- [DomainHelp("Alternative CDN domain", "discordcdn.com")]
- DiscordCdnCom = 2
-}
+ public enum OtherDomain
+ {
+ /// <summary>
+ /// airhorn.solutions
+ /// </summary>
+ [DomainHelp("API implementation example", "airhorn.solutions")]
+ Airhorn = 1,
+
+ /// <summary>
+ /// airhornbot.com
+ /// </summary>
+ [DomainHelp("API implementation example", "airhornbot.com")]
+ AirhornAlt = 2,
+
+ /// <summary>
+ /// bigbeans.solutions
+ /// </summary>
+ [DomainHelp("April Fools 2017", "bigbeans.solutions")]
+ AprilFools = 3,
+
+ /// <summary>
+ /// watchanimeattheoffice.com
+ /// </summary>
+ [DomainHelp("HypeSquad form placeholder/meme", "watchanimeattheoffice.com")]
+ HypeSquadMeme = 4
+ }
-/// <summary>
-/// Represents a discord domain.
-/// </summary>
-public static class DiscordDomain
-{
/// <summary>
- /// Gets a domain.
- /// Valid types: <see cref="CoreDomain"/>, <see cref="OtherDomain"/> and <see cref="UnusedDomain"/>.
+ /// Unused Domains
/// </summary>
- /// <param name="domainEnum">The domain type.</param>
- /// <returns>A DomainHelpAttribute.</returns>
- public static DomainHelpAttribute GetDomain(Enum domainEnum)
+ public enum UnusedDomain
{
- if (domainEnum is not CoreDomain && domainEnum is not OtherDomain && domainEnum is not UnusedDomain)
- throw new NotSupportedException($"Invalid type. Found: {domainEnum.GetType()} Expected: CoreDomain or OtherDomain or UnusedDomain");
-
- if (domainEnum is CoreDomain domain && (domain == CoreDomain.DiscordAdmin || domain == CoreDomain.DiscordTools))
- throw new UnauthorizedAccessException("You don't have access to this domains");
-
- var memberInfo = domainEnum.GetType().GetMember(domainEnum.ToString()).FirstOrDefault();
- if (memberInfo != null)
- {
- var attribute = (DomainHelpAttribute)memberInfo.GetCustomAttributes(typeof(DomainHelpAttribute), false).FirstOrDefault();
- return attribute;
- }
-
- return null;
+ /// <summary>
+ /// discordapp.io
+ /// </summary>
+ [Obsolete("Not in use.", false)]
+ [DomainHelp("IO domain for discord", "discordapp.io")]
+ DiscordAppIo = 1,
+
+ /// <summary>
+ /// discordcdn.com
+ /// </summary>
+ [Obsolete("Not in use.", false)]
+ [DomainHelp("Alternative CDN domain", "discordcdn.com")]
+ DiscordCdnCom = 2
}
-}
-/// <summary>
-/// Defines a description and url for this domain.
-/// </summary>
-[AttributeUsage(AttributeTargets.All)]
-public class DomainHelpAttribute : Attribute
-{
/// <summary>
- /// Gets the Description for this domain.
+ /// Represents a discord domain.
/// </summary>
- public string Description { get; }
+ public static class DiscordDomain
+ {
+ /// <summary>
+ /// Gets a domain.
+ /// Valid types: <see cref="CoreDomain"/>, <see cref="OtherDomain"/> and <see cref="UnusedDomain"/>.
+ /// </summary>
+ /// <param name="domainEnum">The domain type.</param>
+ /// <returns>A DomainHelpAttribute.</returns>
+ public static DomainHelpAttribute GetDomain(Enum domainEnum)
+ {
+ if (domainEnum is not CoreDomain && domainEnum is not OtherDomain && domainEnum is not UnusedDomain)
+ throw new NotSupportedException($"Invalid type. Found: {domainEnum.GetType()} Expected: CoreDomain or OtherDomain or UnusedDomain");
- /// <summary>
- /// Gets the Uri for this domain.
- /// </summary>
- public Uri Uri { get; }
+ if (domainEnum is CoreDomain domain && (domain == CoreDomain.DiscordAdmin || domain == CoreDomain.DiscordTools))
+ throw new UnauthorizedAccessException("You don't have access to this domains");
- /// <summary>
- /// Gets the Domain for this domain.
- /// </summary>
- public string Domain { get; }
+ var memberInfo = domainEnum.GetType().GetMember(domainEnum.ToString()).FirstOrDefault();
+ if (memberInfo != null)
+ {
+ var attribute = (DomainHelpAttribute)memberInfo.GetCustomAttributes(typeof(DomainHelpAttribute), false).FirstOrDefault();
+ return attribute;
+ }
- /// <summary>
- /// Gets the Url for this domain.
- /// </summary>
- public string Url { get; }
+ return null;
+ }
+ }
/// <summary>
- /// Defines a description and URIs for this domain.
+ /// Defines a description and url for this domain.
/// </summary>
- /// <param name="desc">Description for this domain.</param>
- /// <param name="domain">Url for this domain.</param>
- public DomainHelpAttribute(string desc, string domain)
+ [AttributeUsage(AttributeTargets.All)]
+ public class DomainHelpAttribute : Attribute
{
- this.Description = desc;
- this.Domain = domain;
- var url = $"https://{domain}";
- this.Url = url;
- this.Uri = new Uri(url);
+ /// <summary>
+ /// Gets the Description for this domain.
+ /// </summary>
+ public string Description { get; }
+
+ /// <summary>
+ /// Gets the Uri for this domain.
+ /// </summary>
+ public Uri Uri { get; }
+
+ /// <summary>
+ /// Gets the Domain for this domain.
+ /// </summary>
+ public string Domain { get; }
+
+ /// <summary>
+ /// Gets the Url for this domain.
+ /// </summary>
+ public string Url { get; }
+
+ /// <summary>
+ /// Defines a description and URIs for this domain.
+ /// </summary>
+ /// <param name="desc">Description for this domain.</param>
+ /// <param name="domain">Url for this domain.</param>
+ public DomainHelpAttribute(string desc, string domain)
+ {
+ this.Description = desc;
+ this.Domain = domain;
+ var url = $"https://{domain}";
+ this.Url = url;
+ this.Uri = new Uri(url);
+ }
}
}
diff --git a/DisCatSharp/Enums/Discord/DiscordShortlink.cs b/DisCatSharp/Enums/Discord/DiscordShortlink.cs
index c68dcdaa5..1e62163b8 100644
--- a/DisCatSharp/Enums/Discord/DiscordShortlink.cs
+++ b/DisCatSharp/Enums/Discord/DiscordShortlink.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Enums;
-
-/// <summary>
-/// Discord short links.
-/// </summary>
-public static class DiscordShortlink
+namespace DisCatSharp.Enums
{
- public static readonly string Support = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/support";
- public static readonly string TrustAndSafety = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/request";
- public static readonly string Contact = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/contact";
- public static readonly string BugReport = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/bugreport";
- public static readonly string TranslationError = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/lang-feedback";
- public static readonly string Status = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/status";
- public static readonly string Terms = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/terms";
- public static readonly string Guidelines = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/guidelines";
- public static readonly string Moderation = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/moderation";
+ /// <summary>
+ /// Discord short links.
+ /// </summary>
+ public static class DiscordShortlink
+ {
+ public static readonly string Support = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/support";
+ public static readonly string TrustAndSafety = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/request";
+ public static readonly string Contact = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/contact";
+ public static readonly string BugReport = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/bugreport";
+ public static readonly string TranslationError = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/lang-feedback";
+ public static readonly string Status = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/status";
+ public static readonly string Terms = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/terms";
+ public static readonly string Guidelines = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/guidelines";
+ public static readonly string Moderation = $"{DiscordDomain.GetDomain(CoreDomain.DiscordMarketing).Url}/moderation";
+ }
}
diff --git a/DisCatSharp/Enums/GatewayCompressionLevel.cs b/DisCatSharp/Enums/GatewayCompressionLevel.cs
index b99191898..89d67ce12 100644
--- a/DisCatSharp/Enums/GatewayCompressionLevel.cs
+++ b/DisCatSharp/Enums/GatewayCompressionLevel.cs
@@ -1,44 +1,45 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Determines at which level should the WebSocket traffic be compressed.
-/// </summary>
-public enum GatewayCompressionLevel : byte
+namespace DisCatSharp
{
/// <summary>
- /// Defines that traffic should not be compressed at all.
+ /// Determines at which level should the WebSocket traffic be compressed.
/// </summary>
- None = 0,
+ public enum GatewayCompressionLevel : byte
+ {
+ /// <summary>
+ /// Defines that traffic should not be compressed at all.
+ /// </summary>
+ None = 0,
- /// <summary>
- /// Defines that traffic should be compressed at payload level.
- /// </summary>
- Payload = 1,
+ /// <summary>
+ /// Defines that traffic should be compressed at payload level.
+ /// </summary>
+ Payload = 1,
- /// <summary>
- /// Defines that entire traffic stream should be compressed.
- /// </summary>
- Stream = 2
+ /// <summary>
+ /// Defines that entire traffic stream should be compressed.
+ /// </summary>
+ Stream = 2
+ }
}
diff --git a/DisCatSharp/Enums/Guild/HubType.cs b/DisCatSharp/Enums/Guild/HubType.cs
index f110978cb..60d8515db 100644
--- a/DisCatSharp/Enums/Guild/HubType.cs
+++ b/DisCatSharp/Enums/Guild/HubType.cs
@@ -1,44 +1,45 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a guilds hub type.
-/// </summary>
-public enum HubType : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that the hub is a default one.
+ /// Represents a guilds hub type.
/// </summary>
- Default = 0,
+ public enum HubType : int
+ {
+ /// <summary>
+ /// Indicates that the hub is a default one.
+ /// </summary>
+ Default = 0,
- /// <summary>
- /// Indicates that the hub is a high school.
- /// </summary>
- HighSchool = 1,
+ /// <summary>
+ /// Indicates that the hub is a high school.
+ /// </summary>
+ HighSchool = 1,
- /// <summary>
- /// Indicates that the hub is a college.
- /// </summary>
- College = 2
+ /// <summary>
+ /// Indicates that the hub is a college.
+ /// </summary>
+ College = 2
+ }
}
diff --git a/DisCatSharp/Enums/Guild/MemberFlags.cs b/DisCatSharp/Enums/Guild/MemberFlags.cs
index 174a60958..a6421eab7 100644
--- a/DisCatSharp/Enums/Guild/MemberFlags.cs
+++ b/DisCatSharp/Enums/Guild/MemberFlags.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Enums;
-
-/// <summary>
-/// Represents additional details of a member account.
-/// </summary>
-[Flags]
-public enum MemberFlags
+namespace DisCatSharp.Enums
{
/// <summary>
- /// This member has no flags.
+ /// Represents additional details of a member account.
/// </summary>
- None = 0,
+ [Flags]
+ public enum MemberFlags
+ {
+ /// <summary>
+ /// This member has no flags.
+ /// </summary>
+ None = 0,
- /// <summary>
- /// This member has joined and left the guild before.
- /// </summary>
- DidRejoin = 1 << 0,
+ /// <summary>
+ /// This member has joined and left the guild before.
+ /// </summary>
+ DidRejoin = 1 << 0,
+ }
}
diff --git a/DisCatSharp/Enums/Guild/MembershipScreeningFieldType.cs b/DisCatSharp/Enums/Guild/MembershipScreeningFieldType.cs
index a2ffac86b..34598b46e 100644
--- a/DisCatSharp/Enums/Guild/MembershipScreeningFieldType.cs
+++ b/DisCatSharp/Enums/Guild/MembershipScreeningFieldType.cs
@@ -1,41 +1,42 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a membership screening field type
-/// </summary>
-[JsonConverter(typeof(StringEnumConverter))]
-public enum MembershipScreeningFieldType
+namespace DisCatSharp
{
/// <summary>
- /// Specifies the server rules
+ /// Represents a membership screening field type
/// </summary>
- [EnumMember(Value = "TERMS")]
- Terms
+ [JsonConverter(typeof(StringEnumConverter))]
+ public enum MembershipScreeningFieldType
+ {
+ /// <summary>
+ /// Specifies the server rules
+ /// </summary>
+ [EnumMember(Value = "TERMS")]
+ Terms
+ }
}
diff --git a/DisCatSharp/Enums/Guild/NsfwLevel.cs b/DisCatSharp/Enums/Guild/NsfwLevel.cs
index b6a96166c..916db495f 100644
--- a/DisCatSharp/Enums/Guild/NsfwLevel.cs
+++ b/DisCatSharp/Enums/Guild/NsfwLevel.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
// ReSharper disable InconsistentNaming
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a guild's content level.
-/// </summary>
-public enum NsfwLevel
+namespace DisCatSharp
{
/// <summary>
- /// Indicates the guild has no special NSFW level.
+ /// Represents a guild's content level.
/// </summary>
- Default = 0,
+ public enum NsfwLevel
+ {
+ /// <summary>
+ /// Indicates the guild has no special NSFW level.
+ /// </summary>
+ Default = 0,
- /// <summary>
- /// Indicates the guild has extremely suggestive or mature content that would only be suitable for users over 18
- /// </summary>
- Explicit = 1,
+ /// <summary>
+ /// Indicates the guild has extremely suggestive or mature content that would only be suitable for users over 18
+ /// </summary>
+ Explicit = 1,
- /// <summary>
- /// Indicates the guild has no content that could be deemed NSFW. It is SFW.
- /// </summary>
- Safe = 2,
+ /// <summary>
+ /// Indicates the guild has no content that could be deemed NSFW. It is SFW.
+ /// </summary>
+ Safe = 2,
- /// <summary>
- /// Indicates the guild has mildly NSFW content that may not be suitable for users under 18.
- /// </summary>
- Age_Restricted = 3
+ /// <summary>
+ /// Indicates the guild has mildly NSFW content that may not be suitable for users under 18.
+ /// </summary>
+ Age_Restricted = 3
+ }
}
diff --git a/DisCatSharp/Enums/Guild/PremiumTier.cs b/DisCatSharp/Enums/Guild/PremiumTier.cs
index 27717f131..09703973b 100644
--- a/DisCatSharp/Enums/Guild/PremiumTier.cs
+++ b/DisCatSharp/Enums/Guild/PremiumTier.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a server's premium tier.
-/// </summary>
-public enum PremiumTier : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that this server was not boosted.
+ /// Represents a server's premium tier.
/// </summary>
- None = 0,
+ public enum PremiumTier : int
+ {
+ /// <summary>
+ /// Indicates that this server was not boosted.
+ /// </summary>
+ None = 0,
- /// <summary>
- /// Indicates that this server was boosted two times.
- /// </summary>
- TierOne = 1,
+ /// <summary>
+ /// Indicates that this server was boosted two times.
+ /// </summary>
+ TierOne = 1,
- /// <summary>
- /// Indicates that this server was boosted seven times.
- /// </summary>
- TierTwo = 2,
+ /// <summary>
+ /// Indicates that this server was boosted seven times.
+ /// </summary>
+ TierTwo = 2,
- /// <summary>
- /// Indicates that this server was boosted fourteen times.
- /// </summary>
- TierThree = 3,
+ /// <summary>
+ /// Indicates that this server was boosted fourteen times.
+ /// </summary>
+ TierThree = 3,
- /// <summary>
- /// Indicates an unknown premium tier.
- /// </summary>
- Unknown = int.MaxValue
+ /// <summary>
+ /// Indicates an unknown premium tier.
+ /// </summary>
+ Unknown = int.MaxValue
+ }
}
diff --git a/DisCatSharp/Enums/Guild/PriceTierType.cs b/DisCatSharp/Enums/Guild/PriceTierType.cs
index 78c328850..209d1a04c 100644
--- a/DisCatSharp/Enums/Guild/PriceTierType.cs
+++ b/DisCatSharp/Enums/Guild/PriceTierType.cs
@@ -1,34 +1,35 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a price tier type.
-/// </summary>
-public enum PriceTierType : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that this is a role subscription.
+ /// Represents a price tier type.
/// </summary>
- GuildRoleSubscriptions = 1
+ public enum PriceTierType : int
+ {
+ /// <summary>
+ /// Indicates that this is a role subscription.
+ /// </summary>
+ GuildRoleSubscriptions = 1
+ }
}
diff --git a/DisCatSharp/Enums/Guild/RoleFlags.cs b/DisCatSharp/Enums/Guild/RoleFlags.cs
index 1d2e0bc15..2eb3cc04b 100644
--- a/DisCatSharp/Enums/Guild/RoleFlags.cs
+++ b/DisCatSharp/Enums/Guild/RoleFlags.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Enums;
-
-/// <summary>
-/// Represents additional details of a role.
-/// </summary>
-[Flags]
-public enum RoleFlags
+namespace DisCatSharp.Enums
{
/// <summary>
- /// This role has no flags.
+ /// Represents additional details of a role.
/// </summary>
- None = 0,
+ [Flags]
+ public enum RoleFlags
+ {
+ /// <summary>
+ /// This role has no flags.
+ /// </summary>
+ None = 0,
- /// <summary>
- /// This role is in a prompt.
- /// </summary>
- InPrompt = 1 << 0,
+ /// <summary>
+ /// This role is in a prompt.
+ /// </summary>
+ InPrompt = 1 << 0,
+ }
}
diff --git a/DisCatSharp/Enums/Guild/SystemChannelFlags.cs b/DisCatSharp/Enums/Guild/SystemChannelFlags.cs
index 8ff081258..3a7fac72c 100644
--- a/DisCatSharp/Enums/Guild/SystemChannelFlags.cs
+++ b/DisCatSharp/Enums/Guild/SystemChannelFlags.cs
@@ -1,76 +1,77 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents a system channel flags extension.
-/// </summary>
-public static class SystemChannelFlagsExtension
+namespace DisCatSharp
{
/// <summary>
- /// Calculates whether these system channel flags contain a specific flag.
+ /// Represents a system channel flags extension.
/// </summary>
- /// <param name="baseFlags">The existing flags.</param>
- /// <param name="flag">The flag to search for.</param>
- /// <returns></returns>
- public static bool HasSystemChannelFlag(this SystemChannelFlags baseFlags, SystemChannelFlags flag) => (baseFlags & flag) == flag;
-}
+ public static class SystemChannelFlagsExtension
+ {
+ /// <summary>
+ /// Calculates whether these system channel flags contain a specific flag.
+ /// </summary>
+ /// <param name="baseFlags">The existing flags.</param>
+ /// <param name="flag">The flag to search for.</param>
+ /// <returns></returns>
+ public static bool HasSystemChannelFlag(this SystemChannelFlags baseFlags, SystemChannelFlags flag) => (baseFlags & flag) == flag;
+ }
-/// <summary>
-/// Represents settings for a guild's system channel.
-/// </summary>
-[Flags]
-public enum SystemChannelFlags
-{
/// <summary>
- /// Member join messages are disabled.
+ /// Represents settings for a guild's system channel.
/// </summary>
- SuppressJoinNotifications = 1 << 0,
+ [Flags]
+ public enum SystemChannelFlags
+ {
+ /// <summary>
+ /// Member join messages are disabled.
+ /// </summary>
+ SuppressJoinNotifications = 1 << 0,
- /// <summary>
- /// Server boost messages are disabled.
- /// </summary>
- SuppressPremiumSubscriptions = 1 << 1,
+ /// <summary>
+ /// Server boost messages are disabled.
+ /// </summary>
+ SuppressPremiumSubscriptions = 1 << 1,
- /// <summary>
- /// Server setup tips are disabled.
- /// </summary>
- SuppressGuildReminderNotifications = 1 << 2,
+ /// <summary>
+ /// Server setup tips are disabled.
+ /// </summary>
+ SuppressGuildReminderNotifications = 1 << 2,
- /// <summary>
- /// Suppress member join sticker replies.
- /// </summary>
- SuppressJoinNotificationReplies = 1 << 3,
+ /// <summary>
+ /// Suppress member join sticker replies.
+ /// </summary>
+ SuppressJoinNotificationReplies = 1 << 3,
- /// <summary>
- /// Role subscription purchase messages are disabled.
- /// </summary>
- SuppressRoleSubbscriptionPurchaseNotification = 1<<4,
+ /// <summary>
+ /// Role subscription purchase messages are disabled.
+ /// </summary>
+ SuppressRoleSubbscriptionPurchaseNotification = 1<<4,
- /// <summary>
- /// Suppress role subscription purchase sticker replies.
- /// </summary>
- SuppressRoleSubbscriptionPurchaseNotificationReplies = 1<<5,
+ /// <summary>
+ /// Suppress role subscription purchase sticker replies.
+ /// </summary>
+ SuppressRoleSubbscriptionPurchaseNotificationReplies = 1<<5,
+ }
}
diff --git a/DisCatSharp/Enums/Interaction/ButtonStyle.cs b/DisCatSharp/Enums/Interaction/ButtonStyle.cs
index 05f1a024d..e7e972c66 100644
--- a/DisCatSharp/Enums/Interaction/ButtonStyle.cs
+++ b/DisCatSharp/Enums/Interaction/ButtonStyle.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Enums;
-
-/// <summary>
-/// Represents a button's style/color.
-/// </summary>
-public enum ButtonStyle : int
+namespace DisCatSharp.Enums
{
/// <summary>
- /// Blurple button.
+ /// Represents a button's style/color.
/// </summary>
- Primary = 1,
+ public enum ButtonStyle : int
+ {
+ /// <summary>
+ /// Blurple button.
+ /// </summary>
+ Primary = 1,
- /// <summary>
- /// Grey button.
- /// </summary>
- Secondary = 2,
+ /// <summary>
+ /// Grey button.
+ /// </summary>
+ Secondary = 2,
- /// <summary>
- /// Green button.
- /// </summary>
- Success = 3,
+ /// <summary>
+ /// Green button.
+ /// </summary>
+ Success = 3,
/// <summary>
/// Red button.
/// </summary>
Danger = 4,
/// <summary>
/// Link Button.
/// </summary>
Link = 5
}
+}
diff --git a/DisCatSharp/Enums/Interaction/ComponentType.cs b/DisCatSharp/Enums/Interaction/ComponentType.cs
index 2a81ddb2c..25c951aa3 100644
--- a/DisCatSharp/Enums/Interaction/ComponentType.cs
+++ b/DisCatSharp/Enums/Interaction/ComponentType.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Enums;
-
-/// <summary>
-/// Represents a type of component.
-/// </summary>
-public enum ComponentType
+namespace DisCatSharp.Enums
{
/// <summary>
- /// A row of components.
+ /// Represents a type of component.
/// </summary>
- ActionRow = 1,
+ public enum ComponentType
+ {
+ /// <summary>
+ /// A row of components.
+ /// </summary>
+ ActionRow = 1,
- /// <summary>
- /// A button.
- /// </summary>
- Button = 2,
+ /// <summary>
+ /// A button.
+ /// </summary>
+ Button = 2,
- /// <summary>
- /// A select menu.
- /// </summary>
- Select = 3,
+ /// <summary>
+ /// A select menu.
+ /// </summary>
+ Select = 3,
- /// <summary>
- /// A input text.
- /// </summary>
- InputText = 4
+ /// <summary>
+ /// A input text.
+ /// </summary>
+ InputText = 4
+ }
}
diff --git a/DisCatSharp/Enums/Interaction/InteractionResponseType.cs b/DisCatSharp/Enums/Interaction/InteractionResponseType.cs
index c1f327212..4af483009 100644
--- a/DisCatSharp/Enums/Interaction/InteractionResponseType.cs
+++ b/DisCatSharp/Enums/Interaction/InteractionResponseType.cs
@@ -1,64 +1,65 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the type of interaction response
-/// </summary>
-public enum InteractionResponseType
+namespace DisCatSharp
{
/// <summary>
- /// Acknowledges a Ping.
+ /// Represents the type of interaction response
/// </summary>
- Pong = 1,
+ public enum InteractionResponseType
+ {
+ /// <summary>
+ /// Acknowledges a Ping.
+ /// </summary>
+ Pong = 1,
- /// <summary>
- /// Responds to the interaction with a message.
- /// </summary>
- ChannelMessageWithSource = 4,
+ /// <summary>
+ /// Responds to the interaction with a message.
+ /// </summary>
+ ChannelMessageWithSource = 4,
- /// <summary>
- /// Acknowledges an interaction to edit to a response later. The user sees a "thinking" state.
- /// </summary>
- DeferredChannelMessageWithSource = 5,
+ /// <summary>
+ /// Acknowledges an interaction to edit to a response later. The user sees a "thinking" state.
+ /// </summary>
+ DeferredChannelMessageWithSource = 5,
- /// <summary>
- /// Acknowledges a component interaction to allow a response later.
- /// </summary>
- DeferredMessageUpdate = 6,
+ /// <summary>
+ /// Acknowledges a component interaction to allow a response later.
+ /// </summary>
+ DeferredMessageUpdate = 6,
- /// <summary>
- /// Responds to a component interaction by editing the message it's attached to.
- /// </summary>
- UpdateMessage = 7,
+ /// <summary>
+ /// Responds to a component interaction by editing the message it's attached to.
+ /// </summary>
+ UpdateMessage = 7,
- /// <summary>
- /// Responds to an auto-complete request.
- /// </summary>
- AutoCompleteResult = 8,
+ /// <summary>
+ /// Responds to an auto-complete request.
+ /// </summary>
+ AutoCompleteResult = 8,
- /// <summary>
- /// Responds to the interaction with a modal.
- /// </summary>
- Modal = 9
+ /// <summary>
+ /// Responds to the interaction with a modal.
+ /// </summary>
+ Modal = 9
+ }
}
diff --git a/DisCatSharp/Enums/Interaction/InteractionType.cs b/DisCatSharp/Enums/Interaction/InteractionType.cs
index eab20853d..bef710a38 100644
--- a/DisCatSharp/Enums/Interaction/InteractionType.cs
+++ b/DisCatSharp/Enums/Interaction/InteractionType.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the type of interaction used.
-/// </summary>
-public enum InteractionType
+namespace DisCatSharp
{
/// <summary>
- /// Sent when registering an HTTP interaction endpoint with Discord. Must be replied to with a Pong.
+ /// Represents the type of interaction used.
/// </summary>
- Ping = 1,
+ public enum InteractionType
+ {
+ /// <summary>
+ /// Sent when registering an HTTP interaction endpoint with Discord. Must be replied to with a Pong.
+ /// </summary>
+ Ping = 1,
- /// <summary>
- /// An application command.
- /// </summary>
- ApplicationCommand = 2,
+ /// <summary>
+ /// An application command.
+ /// </summary>
+ ApplicationCommand = 2,
- /// <summary>
- /// A component.
- /// </summary>
- Component = 3,
+ /// <summary>
+ /// A component.
+ /// </summary>
+ Component = 3,
- /// <summary>
- /// An autocomplete field.
- /// </summary>
- AutoComplete = 4,
+ /// <summary>
+ /// An autocomplete field.
+ /// </summary>
+ AutoComplete = 4,
- /// <summary>
- /// A modal component.
- /// </summary>
- ModalSubmit = 5
+ /// <summary>
+ /// A modal component.
+ /// </summary>
+ ModalSubmit = 5
+ }
}
diff --git a/DisCatSharp/Enums/Interaction/TextComponentStyle.cs b/DisCatSharp/Enums/Interaction/TextComponentStyle.cs
index b86e2f3cb..7114d7d83 100644
--- a/DisCatSharp/Enums/Interaction/TextComponentStyle.cs
+++ b/DisCatSharp/Enums/Interaction/TextComponentStyle.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Enums;
-
-/// <summary>
-/// Represents a button's style/color.
-/// </summary>
-public enum TextComponentStyle : int
+namespace DisCatSharp.Enums
{
/// <summary>
- /// A small text input.
+ /// Represents a button's style/color.
/// </summary>
- Small = 1,
+ public enum TextComponentStyle : int
+ {
+ /// <summary>
+ /// A small text input.
+ /// </summary>
+ Small = 1,
- /// <summary>
- /// A paragraph text input.
- /// </summary>
- Paragraph = 2
+ /// <summary>
+ /// A paragraph text input.
+ /// </summary>
+ Paragraph = 2
+ }
}
diff --git a/DisCatSharp/Enums/Invite/InviteType.cs b/DisCatSharp/Enums/Invite/InviteType.cs
index a2f400255..8e0f23b46 100644
--- a/DisCatSharp/Enums/Invite/InviteType.cs
+++ b/DisCatSharp/Enums/Invite/InviteType.cs
@@ -1,44 +1,45 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the invite type .
-/// </summary>
-public enum InviteType
+namespace DisCatSharp
{
/// <summary>
- /// Represents a guild invite.
+ /// Represents the invite type .
/// </summary>
- Guild = 0,
+ public enum InviteType
+ {
+ /// <summary>
+ /// Represents a guild invite.
+ /// </summary>
+ Guild = 0,
- /// <summary>
- /// Represents a group dm invite.
- /// </summary>
- GroupDm = 1,
+ /// <summary>
+ /// Represents a group dm invite.
+ /// </summary>
+ GroupDm = 1,
- /// <summary>
- /// Represents a friend invite.
- /// </summary>
- User = 2
+ /// <summary>
+ /// Represents a friend invite.
+ /// </summary>
+ User = 2
+ }
}
diff --git a/DisCatSharp/Enums/Invite/TargetActivity.cs b/DisCatSharp/Enums/Invite/TargetActivity.cs
index 5baada3b5..6863c73dd 100644
--- a/DisCatSharp/Enums/Invite/TargetActivity.cs
+++ b/DisCatSharp/Enums/Invite/TargetActivity.cs
@@ -1,215 +1,216 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
// ReSharper disable InconsistentNaming
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the activity this invite is for.
-/// </summary>
-public enum TargetActivity : ulong
+namespace DisCatSharp
{
/// <summary>
- /// Represents no embedded application.
- /// </summary>
- None = 0,
-
- /// <summary>
- /// Represents the embedded application Betrayal.io.
- /// </summary>
- Betrayal = 773336526917861400,
-
- /// <summary>
- /// Represents the embedded application Chess in the park.
- /// Dev?
- /// </summary>
- ChessInThePark = 832012774040141894,
-
- /// <summary>
- /// Represents the embedded application Doodle Crew.
- /// </summary>
- DoodleCrew = 878067389634314250,
-
- /// <summary>
- /// Represents the embedded application Fishington.io.
- /// </summary>
- Fishington = 814288819477020702,
-
- /// <summary>
- /// Represents the embedded application Letter Tile.
- /// </summary>
- LetterTile = 879863686565621790,
-
- /// <summary>
- /// Represents the embedded application Poker Night.
- /// </summary>
- PokerNight = 755827207812677713,
-
- /// <summary>
- /// Represents the embedded application Spell Cast.
- /// </summary>
- SpellCast = 852509694341283871,
-
- /// <summary>
- /// Represents the embedded application Watch Together.
- /// </summary>
- WatchTogether = 880218394199220334,
-
- /// <summary>
- /// Represents the embedded application Watch Together.
- /// This is the dev version.
- /// </summary>
- WatchTogetherDev = 880218832743055411,
-
- /// <summary>
- /// Represents the embedded application Word Snacks.
- /// </summary>
- WordSnacks = 879863976006127627,
-
- /// <summary>
- /// Represents the embedded application YouTube Together.
- /// </summary>
- YouTubeTogether = 755600276941176913,
-
- #region New Prod
- /// <summary>
- /// Represents the embedded application Awkword.
- /// </summary>
- Awkword = 879863881349087252,
-
- /// <summary>
- /// Represents the embedded application Putts.
- /// </summary>
- Putts = 832012854282158180,
-
- /// <summary>
- /// Represents the embedded application CG3 Prod.
- /// </summary>
- CG3Prod = 832013003968348200,
-
- /// <summary>
- /// Represents the embedded application CG4 Prod.
- /// </summary>
- CG4Prod = 832025144389533716,
-
- /// <summary>
- /// Represents the embedded application Sketchy Artist.
- /// </summary>
- SketchyArtist = 879864070101172255,
- #endregion
-
- #region New Dev
- /// <summary>
- /// Represents the embedded application Sketchy Artist.
- /// This is the dev version.
- /// </summary>
- SketchyArtistDev = 879864104980979792,
-
- /// <summary>
- /// Represents the embedded application Word Snacks.
- /// This is the dev version.
- /// </summary>
- WordSnacksDev = 879864010126786570,
-
- /// <summary>
- /// Represents the embedded application Doodle Crew.
- /// This is the dev version.
- /// </summary>
- DoodleCrewDev = 878067427668275241,
-
- /// <summary>
- /// Represents the embedded application Chess in the park.
- /// This is the dev version.
- /// </summary>
- ChessInTheParkDev = 832012586023256104,
-
- /// <summary>
- /// Represents the embedded application CG3 Dev.
- /// This is the dev version.
- /// </summary>
- CG3Dev = 832012682520428625,
-
- /// <summary>
- /// Represents the embedded application CG4 Dev.
- /// This is the dev version.
- /// </summary>
- CG4kDev = 832013108234289153,
-
- /// <summary>
- /// Represents the embedded application Decoders.
- /// This is the dev version.
- /// </summary>
- DecodersDev = 891001866073296967,
- #endregion
-
- #region New Staging
- /// <summary>
- /// Represents the embedded application PN.
- /// This is the staging version.
- /// </summary>
- PNStaging = 763116274876022855,
-
- /// <summary>
- /// Represents the embedded application CG2.
- /// This is the staging version.
- /// </summary>
- CG2Staging = 832012730599735326,
-
- /// <summary>
- /// Represents the embedded application CG3.
- /// This is the staging version.
- /// </summary>
- CG3Staging = 832012938398400562,
-
- /// <summary>
- /// Represents the embedded application CG4.
- /// This is the staging version.
- /// </summary>
- CG4Staging = 832025061657280566,
- #endregion
-
- #region New QA
- /// <summary>
- /// Represents the embedded application Poker.
- /// This is the QA version.
- /// </summary>
- PokerQA = 801133024841957428,
-
- /// <summary>
- /// Represents the embedded application CG2.
- /// This is the QA version.
- /// </summary>
- CG2QA = 832012815819604009,
-
- /// <summary>
- /// Represents the embedded application CG 3.
- /// This is the QA version.
- /// </summary>
- CG3QA = 832012894068801636,
-
- /// <summary>
- /// Represents the embedded application CG 4.
- /// This is the QA version.
- /// </summary>
- CG4QA = 832025114077298718,
- #endregion
+ /// Represents the activity this invite is for.
+ /// </summary>
+ public enum TargetActivity : ulong
+ {
+ /// <summary>
+ /// Represents no embedded application.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// Represents the embedded application Betrayal.io.
+ /// </summary>
+ Betrayal = 773336526917861400,
+
+ /// <summary>
+ /// Represents the embedded application Chess in the park.
+ /// Dev?
+ /// </summary>
+ ChessInThePark = 832012774040141894,
+
+ /// <summary>
+ /// Represents the embedded application Doodle Crew.
+ /// </summary>
+ DoodleCrew = 878067389634314250,
+
+ /// <summary>
+ /// Represents the embedded application Fishington.io.
+ /// </summary>
+ Fishington = 814288819477020702,
+
+ /// <summary>
+ /// Represents the embedded application Letter Tile.
+ /// </summary>
+ LetterTile = 879863686565621790,
+
+ /// <summary>
+ /// Represents the embedded application Poker Night.
+ /// </summary>
+ PokerNight = 755827207812677713,
+
+ /// <summary>
+ /// Represents the embedded application Spell Cast.
+ /// </summary>
+ SpellCast = 852509694341283871,
+
+ /// <summary>
+ /// Represents the embedded application Watch Together.
+ /// </summary>
+ WatchTogether = 880218394199220334,
+
+ /// <summary>
+ /// Represents the embedded application Watch Together.
+ /// This is the dev version.
+ /// </summary>
+ WatchTogetherDev = 880218832743055411,
+
+ /// <summary>
+ /// Represents the embedded application Word Snacks.
+ /// </summary>
+ WordSnacks = 879863976006127627,
+
+ /// <summary>
+ /// Represents the embedded application YouTube Together.
+ /// </summary>
+ YouTubeTogether = 755600276941176913,
+
+ #region New Prod
+ /// <summary>
+ /// Represents the embedded application Awkword.
+ /// </summary>
+ Awkword = 879863881349087252,
+
+ /// <summary>
+ /// Represents the embedded application Putts.
+ /// </summary>
+ Putts = 832012854282158180,
+
+ /// <summary>
+ /// Represents the embedded application CG3 Prod.
+ /// </summary>
+ CG3Prod = 832013003968348200,
+
+ /// <summary>
+ /// Represents the embedded application CG4 Prod.
+ /// </summary>
+ CG4Prod = 832025144389533716,
+
+ /// <summary>
+ /// Represents the embedded application Sketchy Artist.
+ /// </summary>
+ SketchyArtist = 879864070101172255,
+ #endregion
+
+ #region New Dev
+ /// <summary>
+ /// Represents the embedded application Sketchy Artist.
+ /// This is the dev version.
+ /// </summary>
+ SketchyArtistDev = 879864104980979792,
+
+ /// <summary>
+ /// Represents the embedded application Word Snacks.
+ /// This is the dev version.
+ /// </summary>
+ WordSnacksDev = 879864010126786570,
+
+ /// <summary>
+ /// Represents the embedded application Doodle Crew.
+ /// This is the dev version.
+ /// </summary>
+ DoodleCrewDev = 878067427668275241,
+
+ /// <summary>
+ /// Represents the embedded application Chess in the park.
+ /// This is the dev version.
+ /// </summary>
+ ChessInTheParkDev = 832012586023256104,
+
+ /// <summary>
+ /// Represents the embedded application CG3 Dev.
+ /// This is the dev version.
+ /// </summary>
+ CG3Dev = 832012682520428625,
+
+ /// <summary>
+ /// Represents the embedded application CG4 Dev.
+ /// This is the dev version.
+ /// </summary>
+ CG4kDev = 832013108234289153,
+
+ /// <summary>
+ /// Represents the embedded application Decoders.
+ /// This is the dev version.
+ /// </summary>
+ DecodersDev = 891001866073296967,
+ #endregion
+
+ #region New Staging
+ /// <summary>
+ /// Represents the embedded application PN.
+ /// This is the staging version.
+ /// </summary>
+ PNStaging = 763116274876022855,
+
+ /// <summary>
+ /// Represents the embedded application CG2.
+ /// This is the staging version.
+ /// </summary>
+ CG2Staging = 832012730599735326,
+
+ /// <summary>
+ /// Represents the embedded application CG3.
+ /// This is the staging version.
+ /// </summary>
+ CG3Staging = 832012938398400562,
+
+ /// <summary>
+ /// Represents the embedded application CG4.
+ /// This is the staging version.
+ /// </summary>
+ CG4Staging = 832025061657280566,
+ #endregion
+
+ #region New QA
+ /// <summary>
+ /// Represents the embedded application Poker.
+ /// This is the QA version.
+ /// </summary>
+ PokerQA = 801133024841957428,
+
+ /// <summary>
+ /// Represents the embedded application CG2.
+ /// This is the QA version.
+ /// </summary>
+ CG2QA = 832012815819604009,
+
+ /// <summary>
+ /// Represents the embedded application CG 3.
+ /// This is the QA version.
+ /// </summary>
+ CG3QA = 832012894068801636,
+
+ /// <summary>
+ /// Represents the embedded application CG 4.
+ /// This is the QA version.
+ /// </summary>
+ CG4QA = 832025114077298718,
+ #endregion
+ }
}
diff --git a/DisCatSharp/Enums/Invite/TargetType.cs b/DisCatSharp/Enums/Invite/TargetType.cs
index 9c9b749b0..0c602c660 100644
--- a/DisCatSharp/Enums/Invite/TargetType.cs
+++ b/DisCatSharp/Enums/Invite/TargetType.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the invite type .
-/// </summary>
-public enum TargetType
+namespace DisCatSharp
{
/// <summary>
- /// Represents a streaming invite.
+ /// Represents the invite type .
/// </summary>
- Streaming = 1,
+ public enum TargetType
+ {
+ /// <summary>
+ /// Represents a streaming invite.
+ /// </summary>
+ Streaming = 1,
- /// <summary>
- /// Represents a activity invite.
- /// </summary>
- EmbeddedApplication = 2
+ /// <summary>
+ /// Represents a activity invite.
+ /// </summary>
+ EmbeddedApplication = 2
+ }
}
diff --git a/DisCatSharp/Enums/Message/MentionType.cs b/DisCatSharp/Enums/Message/MentionType.cs
index 036c2f5f0..8bdab1eb0 100644
--- a/DisCatSharp/Enums/Message/MentionType.cs
+++ b/DisCatSharp/Enums/Message/MentionType.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Type of mention being made
-/// </summary>
-public enum MentionType
+namespace DisCatSharp
{
/// <summary>
- /// No mention (wtf?)
+ /// Type of mention being made
/// </summary>
- None = 0,
+ public enum MentionType
+ {
+ /// <summary>
+ /// No mention (wtf?)
+ /// </summary>
+ None = 0,
- /// <summary>
- /// Mentioned Username
- /// </summary>
- Username = 1,
+ /// <summary>
+ /// Mentioned Username
+ /// </summary>
+ Username = 1,
- /// <summary>
- /// Mentioned Nickname
- /// </summary>
- Nickname = 2,
+ /// <summary>
+ /// Mentioned Nickname
+ /// </summary>
+ Nickname = 2,
- /// <summary>
- /// Mentioned Channel
- /// </summary>
- Channel = 4,
+ /// <summary>
+ /// Mentioned Channel
+ /// </summary>
+ Channel = 4,
- /// <summary>
- /// Mentioned Role
- /// </summary>
- Role = 8
+ /// <summary>
+ /// Mentioned Role
+ /// </summary>
+ Role = 8
+ }
}
diff --git a/DisCatSharp/Enums/Message/MessageActivityType.cs b/DisCatSharp/Enums/Message/MessageActivityType.cs
index 65ceecfb3..e9ca3b5df 100644
--- a/DisCatSharp/Enums/Message/MessageActivityType.cs
+++ b/DisCatSharp/Enums/Message/MessageActivityType.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Indicates the type of MessageActivity for the Rich Presence.
-/// </summary>
-public enum MessageActivityType
+namespace DisCatSharp
{
/// <summary>
- /// Invites the user to join.
+ /// Indicates the type of MessageActivity for the Rich Presence.
/// </summary>
- Join = 1,
+ public enum MessageActivityType
+ {
+ /// <summary>
+ /// Invites the user to join.
+ /// </summary>
+ Join = 1,
- /// <summary>
- /// Invites the user to spectate.
- /// </summary>
- Spectate = 2,
+ /// <summary>
+ /// Invites the user to spectate.
+ /// </summary>
+ Spectate = 2,
- /// <summary>
- /// Invites the user to listen.
- /// </summary>
- Listen = 3,
+ /// <summary>
+ /// Invites the user to listen.
+ /// </summary>
+ Listen = 3,
- /// <summary>
- /// Allows the user to request to join.
- /// </summary>
- JoinRequest = 4
+ /// <summary>
+ /// Allows the user to request to join.
+ /// </summary>
+ JoinRequest = 4
+ }
}
diff --git a/DisCatSharp/Enums/Message/MessageFlags.cs b/DisCatSharp/Enums/Message/MessageFlags.cs
index 1b5466303..edbb194e4 100644
--- a/DisCatSharp/Enums/Message/MessageFlags.cs
+++ b/DisCatSharp/Enums/Message/MessageFlags.cs
@@ -1,91 +1,92 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents a message flag extensions.
-/// </summary>
-public static class MessageFlagExtensions
+namespace DisCatSharp
{
/// <summary>
- /// Calculates whether these message flags contain a specific flag.
+ /// Represents a message flag extensions.
/// </summary>
- /// <param name="baseFlags">The existing flags.</param>
- /// <param name="flag">The flags to search for.</param>
- /// <returns></returns>
- public static bool HasMessageFlag(this MessageFlags baseFlags, MessageFlags flag) => (baseFlags & flag) == flag;
-}
+ public static class MessageFlagExtensions
+ {
+ /// <summary>
+ /// Calculates whether these message flags contain a specific flag.
+ /// </summary>
+ /// <param name="baseFlags">The existing flags.</param>
+ /// <param name="flag">The flags to search for.</param>
+ /// <returns></returns>
+ public static bool HasMessageFlag(this MessageFlags baseFlags, MessageFlags flag) => (baseFlags & flag) == flag;
+ }
-/// <summary>
-/// Represents additional features of a message.
-/// </summary>
-[Flags]
-public enum MessageFlags
-{
/// <summary>
- /// Whether this message is the original message that was published from a news channel to subscriber channels.
+ /// Represents additional features of a message.
/// </summary>
- Crossposted = 1 << 0,
+ [Flags]
+ public enum MessageFlags
+ {
+ /// <summary>
+ /// Whether this message is the original message that was published from a news channel to subscriber channels.
+ /// </summary>
+ Crossposted = 1 << 0,
- /// <summary>
- /// Whether this message is crossposted (automatically posted in a subscriber channel).
- /// </summary>
- IsCrosspost = 1 << 1,
+ /// <summary>
+ /// Whether this message is crossposted (automatically posted in a subscriber channel).
+ /// </summary>
+ IsCrosspost = 1 << 1,
- /// <summary>
- /// Whether any embeds in the message are hidden.
- /// </summary>
- SuppressedEmbeds = 1 << 2,
+ /// <summary>
+ /// Whether any embeds in the message are hidden.
+ /// </summary>
+ SuppressedEmbeds = 1 << 2,
- /// <summary>
- /// The source message for this crosspost has been deleted.
- /// </summary>
- SourceMessageDelete = 1 << 3,
+ /// <summary>
+ /// The source message for this crosspost has been deleted.
+ /// </summary>
+ SourceMessageDelete = 1 << 3,
- /// <summary>
- /// The message came from the urgent message system.
- /// </summary>
- Urgent = 1 << 4,
+ /// <summary>
+ /// The message came from the urgent message system.
+ /// </summary>
+ Urgent = 1 << 4,
- /// <summary>
- /// The message has an associated thread, with the same id as the message.
- /// </summary>
- HasThread = 1 << 5,
+ /// <summary>
+ /// The message has an associated thread, with the same id as the message.
+ /// </summary>
+ HasThread = 1 << 5,
- /// <summary>
- /// The message is only visible to the user who invoked the interaction.
- /// </summary>
- Ephemeral = 1 << 6,
+ /// <summary>
+ /// The message is only visible to the user who invoked the interaction.
+ /// </summary>
+ Ephemeral = 1 << 6,
- /// <summary>
- /// The message is an interaction response and the bot is "thinking".
- /// </summary>
- Loading = 1 << 7,
+ /// <summary>
+ /// The message is an interaction response and the bot is "thinking".
+ /// </summary>
+ Loading = 1 << 7,
- /// <summary>
- /// The message is warning that some roles failed to mention in thread.
- /// </summary>
- FailedToMentionSomeRolesInThread = 1 << 8
+ /// <summary>
+ /// The message is warning that some roles failed to mention in thread.
+ /// </summary>
+ FailedToMentionSomeRolesInThread = 1 << 8
+ }
}
diff --git a/DisCatSharp/Enums/Message/MessageType.cs b/DisCatSharp/Enums/Message/MessageType.cs
index 7e7e0c21d..b1bca2b34 100644
--- a/DisCatSharp/Enums/Message/MessageType.cs
+++ b/DisCatSharp/Enums/Message/MessageType.cs
@@ -1,159 +1,160 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the type of a message.
-/// </summary>
-public enum MessageType : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates a regular message.
- /// </summary>
- Default = 0,
-
- /// <summary>
- /// Message indicating a recipient was added to a group direct message or a thread channel.
- /// </summary>
- RecipientAdd = 1,
-
- /// <summary>
- /// Message indicating a recipient was removed from a group direct message or a thread channel.
- /// </summary>
- RecipientRemove = 2,
-
- /// <summary>
- /// Message indicating a call.
- /// </summary>
- Call = 3,
-
- /// <summary>
- /// Message indicating a group direct message or thread channel rename.
- /// </summary>
- ChannelNameChange = 4,
-
- /// <summary>
- /// Message indicating a group direct message channel icon change.
- /// </summary>
- ChannelIconChange = 5,
-
- /// <summary>
- /// Message indicating a user pinned a message to a channel.
- /// </summary>
- ChannelPinnedMessage = 6,
-
- /// <summary>
- /// Message indicating a guild member joined. Most frequently seen in newer, smaller guilds.
- /// </summary>
- GuildMemberJoin = 7,
-
- /// <summary>
- /// Message indicating a member nitro boosted a guild.
- /// </summary>
- UserPremiumGuildSubscription = 8,
-
- /// <summary>
- /// Message indicating a guild reached tier one of nitro boosts.
- /// </summary>
- TierOneUserPremiumGuildSubscription = 9,
-
- /// <summary>
- /// Message indicating a guild reached tier two of nitro boosts.
- /// </summary>
- TierTwoUserPremiumGuildSubscription = 10,
-
- /// <summary>
- /// Message indicating a guild reached tier three of nitro boosts.
- /// </summary>
- TierThreeUserPremiumGuildSubscription = 11,
-
- /// <summary>
- /// Message indicating a user followed a news channel.
- /// </summary>
- ChannelFollowAdd = 12,
-
- /// <summary>
- /// Message indicating a user is streaming in a guild.
- /// </summary>
- GuildStream = 13,
-
- /// <summary>
- /// Message indicating a guild was removed from guild discovery.
- /// </summary>
- GuildDiscoveryDisqualified = 14,
-
- /// <summary>
- /// Message indicating a guild was re-added to guild discovery.
- /// </summary>
- GuildDiscoveryRequalified = 15,
-
- /// <summary>
- /// Message indicating that a guild has failed to meet guild discovery requirements for a week.
- /// </summary>
- GuildDiscoveryGracePeriodInitialWarning = 16,
-
- /// <summary>
- /// Message indicating that a guild has failed to meet guild discovery requirements for 3 weeks.
- /// </summary>
- GuildDiscoveryGracePeriodFinalWarning = 17,
-
- /// <summary>
- /// Message indicating a thread was created.
- /// </summary>
- ThreadCreated = 18,
-
- /// <summary>
- /// Message indicating a user replied to another user.
- /// </summary>
- Reply = 19,
-
- /// <summary>
- /// Message indicating an slash command was invoked.
- /// </summary>
- ChatInputCommand = 20,
-
- /// <summary>
- /// Message indicating a new was message sent as the first message in threads that are started from an existing message in the parent channel.
- /// </summary>
- ThreadStarterMessage = 21,
-
- /// <summary>
- /// Message reminding you to invite people to help you build the server.
- /// </summary>
- GuildInviteReminder = 22,
-
- /// <summary>
- /// Message indicating an context menu command was invoked.
- /// </summary>
- ContextMenuCommand = 23,
-
- /// <summary>
- /// Message indicating the guilds automod acted.
- /// </summary>
- AutoModerationAction = 24,
-
- /// <summary>
- /// Message indicating that a member purchased a role subscription.
- /// </summary>
- RoleSubscriptionPurchase = 25
+ /// Represents the type of a message.
+ /// </summary>
+ public enum MessageType : int
+ {
+ /// <summary>
+ /// Indicates a regular message.
+ /// </summary>
+ Default = 0,
+
+ /// <summary>
+ /// Message indicating a recipient was added to a group direct message or a thread channel.
+ /// </summary>
+ RecipientAdd = 1,
+
+ /// <summary>
+ /// Message indicating a recipient was removed from a group direct message or a thread channel.
+ /// </summary>
+ RecipientRemove = 2,
+
+ /// <summary>
+ /// Message indicating a call.
+ /// </summary>
+ Call = 3,
+
+ /// <summary>
+ /// Message indicating a group direct message or thread channel rename.
+ /// </summary>
+ ChannelNameChange = 4,
+
+ /// <summary>
+ /// Message indicating a group direct message channel icon change.
+ /// </summary>
+ ChannelIconChange = 5,
+
+ /// <summary>
+ /// Message indicating a user pinned a message to a channel.
+ /// </summary>
+ ChannelPinnedMessage = 6,
+
+ /// <summary>
+ /// Message indicating a guild member joined. Most frequently seen in newer, smaller guilds.
+ /// </summary>
+ GuildMemberJoin = 7,
+
+ /// <summary>
+ /// Message indicating a member nitro boosted a guild.
+ /// </summary>
+ UserPremiumGuildSubscription = 8,
+
+ /// <summary>
+ /// Message indicating a guild reached tier one of nitro boosts.
+ /// </summary>
+ TierOneUserPremiumGuildSubscription = 9,
+
+ /// <summary>
+ /// Message indicating a guild reached tier two of nitro boosts.
+ /// </summary>
+ TierTwoUserPremiumGuildSubscription = 10,
+
+ /// <summary>
+ /// Message indicating a guild reached tier three of nitro boosts.
+ /// </summary>
+ TierThreeUserPremiumGuildSubscription = 11,
+
+ /// <summary>
+ /// Message indicating a user followed a news channel.
+ /// </summary>
+ ChannelFollowAdd = 12,
+
+ /// <summary>
+ /// Message indicating a user is streaming in a guild.
+ /// </summary>
+ GuildStream = 13,
+
+ /// <summary>
+ /// Message indicating a guild was removed from guild discovery.
+ /// </summary>
+ GuildDiscoveryDisqualified = 14,
+
+ /// <summary>
+ /// Message indicating a guild was re-added to guild discovery.
+ /// </summary>
+ GuildDiscoveryRequalified = 15,
+
+ /// <summary>
+ /// Message indicating that a guild has failed to meet guild discovery requirements for a week.
+ /// </summary>
+ GuildDiscoveryGracePeriodInitialWarning = 16,
+
+ /// <summary>
+ /// Message indicating that a guild has failed to meet guild discovery requirements for 3 weeks.
+ /// </summary>
+ GuildDiscoveryGracePeriodFinalWarning = 17,
+
+ /// <summary>
+ /// Message indicating a thread was created.
+ /// </summary>
+ ThreadCreated = 18,
+
+ /// <summary>
+ /// Message indicating a user replied to another user.
+ /// </summary>
+ Reply = 19,
+
+ /// <summary>
+ /// Message indicating an slash command was invoked.
+ /// </summary>
+ ChatInputCommand = 20,
+
+ /// <summary>
+ /// Message indicating a new was message sent as the first message in threads that are started from an existing message in the parent channel.
+ /// </summary>
+ ThreadStarterMessage = 21,
+
+ /// <summary>
+ /// Message reminding you to invite people to help you build the server.
+ /// </summary>
+ GuildInviteReminder = 22,
+
+ /// <summary>
+ /// Message indicating an context menu command was invoked.
+ /// </summary>
+ ContextMenuCommand = 23,
+
+ /// <summary>
+ /// Message indicating the guilds automod acted.
+ /// </summary>
+ AutoModerationAction = 24,
+
+ /// <summary>
+ /// Message indicating that a member purchased a role subscription.
+ /// </summary>
+ RoleSubscriptionPurchase = 25
+ }
}
diff --git a/DisCatSharp/Enums/Message/TimestampFormat.cs b/DisCatSharp/Enums/Message/TimestampFormat.cs
index ae205ed19..fb408ac6b 100644
--- a/DisCatSharp/Enums/Message/TimestampFormat.cs
+++ b/DisCatSharp/Enums/Message/TimestampFormat.cs
@@ -1,64 +1,65 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Denotes the type of formatting to use for timestamps.
-/// </summary>
-public enum TimestampFormat : byte
+namespace DisCatSharp
{
/// <summary>
- /// A short date. e.g. 18/06/2021.
+ /// Denotes the type of formatting to use for timestamps.
/// </summary>
- ShortDate = (byte)'d',
+ public enum TimestampFormat : byte
+ {
+ /// <summary>
+ /// A short date. e.g. 18/06/2021.
+ /// </summary>
+ ShortDate = (byte)'d',
- /// <summary>
- /// A long date. e.g. 18 June 2021.
- /// </summary>
- LongDate = (byte)'D',
+ /// <summary>
+ /// A long date. e.g. 18 June 2021.
+ /// </summary>
+ LongDate = (byte)'D',
- /// <summary>
- /// A short date and time. e.g. 18 June 2021 03:50.
- /// </summary>
- ShortDateTime = (byte)'f',
+ /// <summary>
+ /// A short date and time. e.g. 18 June 2021 03:50.
+ /// </summary>
+ ShortDateTime = (byte)'f',
- /// <summary>
- /// A long date and time. e.g. Friday 18 June 2021 03:50.
- /// </summary>
- LongDateTime = (byte)'F',
+ /// <summary>
+ /// A long date and time. e.g. Friday 18 June 2021 03:50.
+ /// </summary>
+ LongDateTime = (byte)'F',
- /// <summary>
- /// A short time. e.g. 03:50.
- /// </summary>
- ShortTime = (byte)'t',
+ /// <summary>
+ /// A short time. e.g. 03:50.
+ /// </summary>
+ ShortTime = (byte)'t',
- /// <summary>
- /// A long time. e.g. 03:50:15.
- /// </summary>
- LongTime = (byte)'T',
+ /// <summary>
+ /// A long time. e.g. 03:50:15.
+ /// </summary>
+ LongTime = (byte)'T',
- /// <summary>
- /// The time relative to the client. e.g. An hour ago.
- /// </summary>
- RelativeTime = (byte)'R'
+ /// <summary>
+ /// The time relative to the client. e.g. An hour ago.
+ /// </summary>
+ RelativeTime = (byte)'R'
+ }
}
diff --git a/DisCatSharp/Enums/OAuth.cs b/DisCatSharp/Enums/OAuth.cs
index 485867d83..0a48eb241 100644
--- a/DisCatSharp/Enums/OAuth.cs
+++ b/DisCatSharp/Enums/OAuth.cs
@@ -1,117 +1,118 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
// ReSharper disable InconsistentNaming
-namespace DisCatSharp.Enums;
-
-/// <summary>
-/// The oauth scopes.
-/// </summary>
-public static class OAuth
+namespace DisCatSharp.Enums
{
/// <summary>
- /// The default scopes for bots.
+ /// The oauth scopes.
/// </summary>
- private const string BOT_DEFAULT = "bot applications.commands"; // applications.commands.permissions.update
+ public static class OAuth
+ {
+ /// <summary>
+ /// The default scopes for bots.
+ /// </summary>
+ private const string BOT_DEFAULT = "bot applications.commands"; // applications.commands.permissions.update
- /// <summary>
- /// The bot minimal scopes.
- /// </summary>
- private const string BOT_MINIMAL = "bot applications.commands";
+ /// <summary>
+ /// The bot minimal scopes.
+ /// </summary>
+ private const string BOT_MINIMAL = "bot applications.commands";
- /// <summary>
- /// The bot only scope.
- /// </summary>
- private const string BOT_ONLY = "bot";
+ /// <summary>
+ /// The bot only scope.
+ /// </summary>
+ private const string BOT_ONLY = "bot";
- /// <summary>
- /// The basic identify scopes.
- /// </summary>
- private const string IDENTIFY_BASIC = "identify email";
+ /// <summary>
+ /// The basic identify scopes.
+ /// </summary>
+ private const string IDENTIFY_BASIC = "identify email";
- /// <summary>
- /// The extended identify scopes.
- /// </summary>
- private const string IDENTIFY_EXTENDED = "identify email guilds connections";
+ /// <summary>
+ /// The extended identify scopes.
+ /// </summary>
+ private const string IDENTIFY_EXTENDED = "identify email guilds connections";
- /// <summary>
- /// All scopes for bots and identify.
- /// </summary>
- private const string ALL = BOT_DEFAULT + " " + IDENTIFY_EXTENDED;
+ /// <summary>
+ /// All scopes for bots and identify.
+ /// </summary>
+ private const string ALL = BOT_DEFAULT + " " + IDENTIFY_EXTENDED;
- /// <summary>
- /// The oauth scope.
- /// </summary>
+ /// <summary>
+ /// The oauth scope.
+ /// </summary>
+ /// <summary>
+ /// Resolves the scopes.
+ /// </summary>
+ /// <param name="scope">The scope.</param>
+ /// <returns>A string representing the scopes.</returns>
+ public static string ResolveScopes(OAuthScopes scope) =>
+ scope switch
+ {
+ OAuthScopes.BOT_DEFAULT => BOT_DEFAULT,
+ OAuthScopes.BOT_MINIMAL => BOT_MINIMAL,
+ OAuthScopes.BOT_ONLY => BOT_ONLY,
+ OAuthScopes.IDENTIFY_BASIC => IDENTIFY_BASIC,
+ OAuthScopes.IDENTIFY_EXTENDED => IDENTIFY_EXTENDED,
+ OAuthScopes.ALL => ALL,
+ _ => BOT_DEFAULT,
+ };
+ }
/// <summary>
- /// Resolves the scopes.
+ /// The oauth scopes.
/// </summary>
- /// <param name="scope">The scope.</param>
- /// <returns>A string representing the scopes.</returns>
- public static string ResolveScopes(OAuthScopes scope) =>
- scope switch
- {
- OAuthScopes.BOT_DEFAULT => BOT_DEFAULT,
- OAuthScopes.BOT_MINIMAL => BOT_MINIMAL,
- OAuthScopes.BOT_ONLY => BOT_ONLY,
- OAuthScopes.IDENTIFY_BASIC => IDENTIFY_BASIC,
- OAuthScopes.IDENTIFY_EXTENDED => IDENTIFY_EXTENDED,
- OAuthScopes.ALL => ALL,
- _ => BOT_DEFAULT,
- };
-}
-/// <summary>
-/// The oauth scopes.
-/// </summary>
-public enum OAuthScopes
-{
- /// <summary>
- /// Scopes: bot applications.commands (Excluding applications.commands.permissions.update for now)
- /// </summary>
- BOT_DEFAULT = 0,
+ public enum OAuthScopes
+ {
+ /// <summary>
+ /// Scopes: bot applications.commands (Excluding applications.commands.permissions.update for now)
+ /// </summary>
+ BOT_DEFAULT = 0,
- /// <summary>
- /// Scopes: bot applications.commands
- /// </summary>
- BOT_MINIMAL = 1,
+ /// <summary>
+ /// Scopes: bot applications.commands
+ /// </summary>
+ BOT_MINIMAL = 1,
- /// <summary>
- /// Scopes: bot
- /// </summary>
- BOT_ONLY = 2,
+ /// <summary>
+ /// Scopes: bot
+ /// </summary>
+ BOT_ONLY = 2,
- /// <summary>
- /// Scopes: identify email
- /// </summary>
- IDENTIFY_BASIC = 3,
+ /// <summary>
+ /// Scopes: identify email
+ /// </summary>
+ IDENTIFY_BASIC = 3,
- /// <summary>
- /// Scopes: identify email guilds connections
- /// </summary>
- IDENTIFY_EXTENDED = 4,
+ /// <summary>
+ /// Scopes: identify email guilds connections
+ /// </summary>
+ IDENTIFY_EXTENDED = 4,
- /// <summary>
- /// Scopes: bot applications.commands applications.commands.permissions.update identify email guilds connections
- /// </summary>
- ALL = 5
+ /// <summary>
+ /// Scopes: bot applications.commands applications.commands.permissions.update identify email guilds connections
+ /// </summary>
+ ALL = 5
+ }
}
diff --git a/DisCatSharp/Enums/Permission.cs b/DisCatSharp/Enums/Permission.cs
index 6e38b1aa8..07a738c82 100644
--- a/DisCatSharp/Enums/Permission.cs
+++ b/DisCatSharp/Enums/Permission.cs
@@ -1,366 +1,367 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents permission methods.
-/// </summary>
-public static class PermissionMethods
-{
- /// <summary>
- /// Gets the full permissions enum (long).
- /// </summary>
- internal static Permissions FullPerms { get; } = (Permissions)2199023255551L;
-
- /// <summary>
- /// Calculates whether this permission set contains the given permission.
- /// </summary>
- /// <param name="p">The permissions to calculate from</param>
- /// <param name="permission">permission you want to check</param>
- /// <returns></returns>
- public static bool HasPermission(this Permissions p, Permissions permission)
- => p.HasFlag(Permissions.Administrator) || (p & permission) == permission;
-
- /// <summary>
- /// Grants permissions.
- /// </summary>
- /// <param name="p">The permissions to add to.</param>
- /// <param name="grant">Permission to add.</param>
- /// <returns></returns>
- public static Permissions Grant(this Permissions p, Permissions grant) => p | grant;
-
- /// <summary>
- /// Revokes permissions.
- /// </summary>
- /// <param name="p">The permissions to take from.</param>
- /// <param name="revoke">Permission to take.</param>
- /// <returns></returns>
- public static Permissions Revoke(this Permissions p, Permissions revoke) => p & ~revoke;
-}
-
-/// <summary>
-/// Whether a permission is allowed, denied or unset
-/// </summary>
-public enum PermissionLevel
+namespace DisCatSharp
{
/// <summary>
- /// Said permission is Allowed
- /// </summary>
- Allowed,
-
- /// <summary>
- /// Said permission is Denied
- /// </summary>
- Denied,
-
- /// <summary>
- /// Said permission is Unset
- /// </summary>
- Unset
-}
-
-/// <summary>
-/// Bitwise permission flags.
-/// </summary>
-[Flags]
-public enum Permissions : long
-{
- /// <summary>
- /// Indicates no permissions given.
- /// This disallows users to run application command per default.
- /// </summary>
- [PermissionString("No permissions")]
- None = 0,
-
- /// <summary>
- /// Indicates all permissions are granted
- /// </summary>
- [PermissionString("All permissions")]
- All = 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111,
-
- /// <summary>
- /// Allows creation of instant channel invites.
- /// </summary>
- [PermissionString("Create instant invites")]
- CreateInstantInvite = 1L << 0,
-
- /// <summary>
- /// Allows kicking members.
- /// </summary>
- [PermissionString("Kick members")]
- KickMembers = 1L << 1,
-
- /// <summary>
- /// Allows banning and unbanning members.
- /// </summary>
- [PermissionString("Ban members")]
- BanMembers = 1L << 2,
-
- /// <summary>
- /// Enables full access on a given guild. This also overrides other permissions.
- /// </summary>
- [PermissionString("Administrator")]
- Administrator = 1L << 3,
-
- /// <summary>
- /// Allows managing channels.
- /// </summary>
- [PermissionString("Manage channels")]
- ManageChannels = 1L << 4,
-
- /// <summary>
- /// Allows managing the guild.
- /// </summary>
- [PermissionString("Manage guild")]
- ManageGuild = 1L << 5,
-
- /// <summary>
- /// Allows adding reactions to messages.
- /// </summary>
- [PermissionString("Add reactions")]
- AddReactions = 1L << 6,
-
- /// <summary>
- /// Allows viewing audit log entries.
- /// </summary>
- [PermissionString("View audit log")]
- ViewAuditLog = 1L << 7,
-
- /// <summary>
- /// Allows the use of priority speaker.
- /// </summary>
- [PermissionString("Use priority speaker")]
- PrioritySpeaker = 1L << 8,
-
- /// <summary>
- /// Allows the user to go live.
+ /// Represents permission methods.
/// </summary>
- [PermissionString("Allow stream")]
- Stream = 1L << 9,
-
- /// <summary>
- /// Allows accessing text and voice channels. Disabling this permission hides channels.
- /// </summary>
- [PermissionString("Read messages")]
- AccessChannels = 1L << 10,
-
- /// <summary>
- /// Allows sending messages (does not allow sending messages in threads).
- /// </summary>
- [PermissionString("Send messages")]
- SendMessages = 1L << 11,
-
- /// <summary>
- /// Allows sending text-to-speech messages.
- /// </summary>
- [PermissionString("Send TTS messages")]
- SendTtsMessages = 1L << 12,
-
- /// <summary>
- /// Allows managing messages of other users.
- /// </summary>
- [PermissionString("Manage messages")]
- ManageMessages = 1L << 13,
-
- /// <summary>
- /// Allows embedding content in messages.
- /// </summary>
- [PermissionString("Use embeds")]
- EmbedLinks = 1L << 14,
-
- /// <summary>
- /// Allows uploading files.
- /// </summary>
- [PermissionString("Attach files")]
- AttachFiles = 1L << 15,
-
- /// <summary>
- /// Allows reading message history.
- /// </summary>
- [PermissionString("Read message history")]
- ReadMessageHistory = 1L << 16,
-
- /// <summary>
- /// Allows using @everyone and @here mentions.
- /// </summary>
- [PermissionString("Mention everyone")]
- MentionEveryone = 1L << 17,
-
- /// <summary>
- /// Allows using emojis from external servers, such as twitch or nitro emojis.
- /// </summary>
- [PermissionString("Use external emojis")]
- UseExternalEmojis = 1L << 18,
-
- [PermissionString("View guild insights")]
- ViewGuildInsights = 1L << 19,
-
- /// <summary>
- /// Allows connecting to voice chat.
- /// </summary>
- [PermissionString("Use voice chat")]
- UseVoice = 1L << 20,
-
- /// <summary>
- /// Allows speaking in voice chat.
- /// </summary>
- [PermissionString("Speak")]
- Speak = 1L << 21,
-
- /// <summary>
- /// Allows muting other members in voice chat.
- /// </summary>
- [PermissionString("Mute voice chat members")]
- MuteMembers = 1L << 22,
-
- /// <summary>
- /// Allows deafening other members in voice chat.
- /// </summary>
- [PermissionString("Deafen voice chat members")]
- DeafenMembers = 1L << 23,
-
- /// <summary>
- /// Allows moving voice chat members.
- /// </summary>
- [PermissionString("Move voice chat members")]
- MoveMembers = 1L << 24,
-
- /// <summary>
- /// Allows using voice activation in voice chat. Revoking this will usage of push-to-talk.
- /// </summary>
- [PermissionString("Use voice activity detection")]
- UseVoiceDetection = 1L << 25,
-
- /// <summary>
- /// Allows changing of own nickname.
- /// </summary>
- [PermissionString("Change own nickname")]
- ChangeNickname = 1L << 26,
-
- /// <summary>
- /// Allows managing nicknames of other members.
- /// </summary>
- [PermissionString("Manage nicknames")]
- ManageNicknames = 1L << 27,
-
- /// <summary>
- /// Allows managing roles in a guild.
- /// </summary>
- [PermissionString("Manage roles")]
- ManageRoles = 1L << 28,
-
- /// <summary>
- /// Allows managing webhooks in a guild.
- /// </summary>
- [PermissionString("Manage webhooks")]
- ManageWebhooks = 1L << 29,
-
- /// <summary>
- /// Allows managing guild emojis and stickers.
- /// </summary>
- [PermissionString("Manage emojis & stickers")]
- ManageEmojisAndStickers = 1L << 30,
-
- /// <summary>
- /// Allows the user to use slash commands.
- /// </summary>
- [PermissionString("Use application commands")]
- UseApplicationCommands = 1L << 31,
-
- /// <summary>
- /// Allows for requesting to speak in stage channels.
- /// </summary>
- [PermissionString("Request to speak")]
- RequestToSpeak = 1L << 32,
-
- /// <summary>
- /// Allows managing guild events.
- /// </summary>
- [PermissionString("Manage Events")]
- ManageEvents = 1L << 33,
-
- /// <summary>
- /// Allows for deleting and archiving threads, and viewing all private threads.
- /// </summary>
- [PermissionString("Manage Threads")]
- ManageThreads = 1L << 34,
-
- /// <summary>
- /// Allows for creating threads.
- /// </summary>
- [PermissionString("Create Public Threads")]
- CreatePublicThreads = 1L << 35,
-
- /// <summary>
- /// Allows for creating private threads.
- /// </summary>
- [PermissionString("Create Private Threads")]
- CreatePrivateThreads = 1L << 36,
-
- /// <summary>
- /// Allows the usage of custom stickers from other servers.
- /// </summary>
- [PermissionString("Use external Stickers")]
- UseExternalStickers = 1L << 37,
-
- /// <summary>
- /// Allows for sending messages in threads.
- /// </summary>
- [PermissionString("Send messages in Threads")]
- SendMessagesInThreads = 1L << 38,
-
- /// <summary>
- /// Allows for launching activities (applications with the `EMBEDDED` flag) in a voice channel.
- /// </summary>
- [PermissionString("Start Embedded Activities")]
- StartEmbeddedActivities = 1L << 39,
+ public static class PermissionMethods
+ {
+ /// <summary>
+ /// Gets the full permissions enum (long).
+ /// </summary>
+ internal static Permissions FullPerms { get; } = (Permissions)2199023255551L;
+
+ /// <summary>
+ /// Calculates whether this permission set contains the given permission.
+ /// </summary>
+ /// <param name="p">The permissions to calculate from</param>
+ /// <param name="permission">permission you want to check</param>
+ /// <returns></returns>
+ public static bool HasPermission(this Permissions p, Permissions permission)
+ => p.HasFlag(Permissions.Administrator) || (p & permission) == permission;
+
+ /// <summary>
+ /// Grants permissions.
+ /// </summary>
+ /// <param name="p">The permissions to add to.</param>
+ /// <param name="grant">Permission to add.</param>
+ /// <returns></returns>
+ public static Permissions Grant(this Permissions p, Permissions grant) => p | grant;
+
+ /// <summary>
+ /// Revokes permissions.
+ /// </summary>
+ /// <param name="p">The permissions to take from.</param>
+ /// <param name="revoke">Permission to take.</param>
+ /// <returns></returns>
+ public static Permissions Revoke(this Permissions p, Permissions revoke) => p & ~revoke;
+ }
/// <summary>
- /// Allows to perform limited moderation actions (timeout).
+ /// Whether a permission is allowed, denied or unset
/// </summary>
- [PermissionString("Moderate Members")]
- ModerateMembers = 1L << 40
-}
+ public enum PermissionLevel
+ {
+ /// <summary>
+ /// Said permission is Allowed
+ /// </summary>
+ Allowed,
+
+ /// <summary>
+ /// Said permission is Denied
+ /// </summary>
+ Denied,
+
+ /// <summary>
+ /// Said permission is Unset
+ /// </summary>
+ Unset
+ }
-/// <summary>
-/// Defines a readable name for this permission.
-/// </summary>
-[AttributeUsage(AttributeTargets.Field)]
-public sealed class PermissionStringAttribute : Attribute
-{
/// <summary>
- /// Gets the readable name for this permission.
+ /// Bitwise permission flags.
/// </summary>
- public string String { get; }
+ [Flags]
+ public enum Permissions : long
+ {
+ /// <summary>
+ /// Indicates no permissions given.
+ /// This disallows users to run application command per default.
+ /// </summary>
+ [PermissionString("No permissions")]
+ None = 0,
+
+ /// <summary>
+ /// Indicates all permissions are granted
+ /// </summary>
+ [PermissionString("All permissions")]
+ All = 0b0001_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111,
+
+ /// <summary>
+ /// Allows creation of instant channel invites.
+ /// </summary>
+ [PermissionString("Create instant invites")]
+ CreateInstantInvite = 1L << 0,
+
+ /// <summary>
+ /// Allows kicking members.
+ /// </summary>
+ [PermissionString("Kick members")]
+ KickMembers = 1L << 1,
+
+ /// <summary>
+ /// Allows banning and unbanning members.
+ /// </summary>
+ [PermissionString("Ban members")]
+ BanMembers = 1L << 2,
+
+ /// <summary>
+ /// Enables full access on a given guild. This also overrides other permissions.
+ /// </summary>
+ [PermissionString("Administrator")]
+ Administrator = 1L << 3,
+
+ /// <summary>
+ /// Allows managing channels.
+ /// </summary>
+ [PermissionString("Manage channels")]
+ ManageChannels = 1L << 4,
+
+ /// <summary>
+ /// Allows managing the guild.
+ /// </summary>
+ [PermissionString("Manage guild")]
+ ManageGuild = 1L << 5,
+
+ /// <summary>
+ /// Allows adding reactions to messages.
+ /// </summary>
+ [PermissionString("Add reactions")]
+ AddReactions = 1L << 6,
+
+ /// <summary>
+ /// Allows viewing audit log entries.
+ /// </summary>
+ [PermissionString("View audit log")]
+ ViewAuditLog = 1L << 7,
+
+ /// <summary>
+ /// Allows the use of priority speaker.
+ /// </summary>
+ [PermissionString("Use priority speaker")]
+ PrioritySpeaker = 1L << 8,
+
+ /// <summary>
+ /// Allows the user to go live.
+ /// </summary>
+ [PermissionString("Allow stream")]
+ Stream = 1L << 9,
+
+ /// <summary>
+ /// Allows accessing text and voice channels. Disabling this permission hides channels.
+ /// </summary>
+ [PermissionString("Read messages")]
+ AccessChannels = 1L << 10,
+
+ /// <summary>
+ /// Allows sending messages (does not allow sending messages in threads).
+ /// </summary>
+ [PermissionString("Send messages")]
+ SendMessages = 1L << 11,
+
+ /// <summary>
+ /// Allows sending text-to-speech messages.
+ /// </summary>
+ [PermissionString("Send TTS messages")]
+ SendTtsMessages = 1L << 12,
+
+ /// <summary>
+ /// Allows managing messages of other users.
+ /// </summary>
+ [PermissionString("Manage messages")]
+ ManageMessages = 1L << 13,
+
+ /// <summary>
+ /// Allows embedding content in messages.
+ /// </summary>
+ [PermissionString("Use embeds")]
+ EmbedLinks = 1L << 14,
+
+ /// <summary>
+ /// Allows uploading files.
+ /// </summary>
+ [PermissionString("Attach files")]
+ AttachFiles = 1L << 15,
+
+ /// <summary>
+ /// Allows reading message history.
+ /// </summary>
+ [PermissionString("Read message history")]
+ ReadMessageHistory = 1L << 16,
+
+ /// <summary>
+ /// Allows using @everyone and @here mentions.
+ /// </summary>
+ [PermissionString("Mention everyone")]
+ MentionEveryone = 1L << 17,
+
+ /// <summary>
+ /// Allows using emojis from external servers, such as twitch or nitro emojis.
+ /// </summary>
+ [PermissionString("Use external emojis")]
+ UseExternalEmojis = 1L << 18,
+
+ [PermissionString("View guild insights")]
+ ViewGuildInsights = 1L << 19,
+
+ /// <summary>
+ /// Allows connecting to voice chat.
+ /// </summary>
+ [PermissionString("Use voice chat")]
+ UseVoice = 1L << 20,
+
+ /// <summary>
+ /// Allows speaking in voice chat.
+ /// </summary>
+ [PermissionString("Speak")]
+ Speak = 1L << 21,
+
+ /// <summary>
+ /// Allows muting other members in voice chat.
+ /// </summary>
+ [PermissionString("Mute voice chat members")]
+ MuteMembers = 1L << 22,
+
+ /// <summary>
+ /// Allows deafening other members in voice chat.
+ /// </summary>
+ [PermissionString("Deafen voice chat members")]
+ DeafenMembers = 1L << 23,
+
+ /// <summary>
+ /// Allows moving voice chat members.
+ /// </summary>
+ [PermissionString("Move voice chat members")]
+ MoveMembers = 1L << 24,
+
+ /// <summary>
+ /// Allows using voice activation in voice chat. Revoking this will usage of push-to-talk.
+ /// </summary>
+ [PermissionString("Use voice activity detection")]
+ UseVoiceDetection = 1L << 25,
+
+ /// <summary>
+ /// Allows changing of own nickname.
+ /// </summary>
+ [PermissionString("Change own nickname")]
+ ChangeNickname = 1L << 26,
+
+ /// <summary>
+ /// Allows managing nicknames of other members.
+ /// </summary>
+ [PermissionString("Manage nicknames")]
+ ManageNicknames = 1L << 27,
+
+ /// <summary>
+ /// Allows managing roles in a guild.
+ /// </summary>
+ [PermissionString("Manage roles")]
+ ManageRoles = 1L << 28,
+
+ /// <summary>
+ /// Allows managing webhooks in a guild.
+ /// </summary>
+ [PermissionString("Manage webhooks")]
+ ManageWebhooks = 1L << 29,
+
+ /// <summary>
+ /// Allows managing guild emojis and stickers.
+ /// </summary>
+ [PermissionString("Manage emojis & stickers")]
+ ManageEmojisAndStickers = 1L << 30,
+
+ /// <summary>
+ /// Allows the user to use slash commands.
+ /// </summary>
+ [PermissionString("Use application commands")]
+ UseApplicationCommands = 1L << 31,
+
+ /// <summary>
+ /// Allows for requesting to speak in stage channels.
+ /// </summary>
+ [PermissionString("Request to speak")]
+ RequestToSpeak = 1L << 32,
+
+ /// <summary>
+ /// Allows managing guild events.
+ /// </summary>
+ [PermissionString("Manage Events")]
+ ManageEvents = 1L << 33,
+
+ /// <summary>
+ /// Allows for deleting and archiving threads, and viewing all private threads.
+ /// </summary>
+ [PermissionString("Manage Threads")]
+ ManageThreads = 1L << 34,
+
+ /// <summary>
+ /// Allows for creating threads.
+ /// </summary>
+ [PermissionString("Create Public Threads")]
+ CreatePublicThreads = 1L << 35,
+
+ /// <summary>
+ /// Allows for creating private threads.
+ /// </summary>
+ [PermissionString("Create Private Threads")]
+ CreatePrivateThreads = 1L << 36,
+
+ /// <summary>
+ /// Allows the usage of custom stickers from other servers.
+ /// </summary>
+ [PermissionString("Use external Stickers")]
+ UseExternalStickers = 1L << 37,
+
+ /// <summary>
+ /// Allows for sending messages in threads.
+ /// </summary>
+ [PermissionString("Send messages in Threads")]
+ SendMessagesInThreads = 1L << 38,
+
+ /// <summary>
+ /// Allows for launching activities (applications with the `EMBEDDED` flag) in a voice channel.
+ /// </summary>
+ [PermissionString("Start Embedded Activities")]
+ StartEmbeddedActivities = 1L << 39,
+
+ /// <summary>
+ /// Allows to perform limited moderation actions (timeout).
+ /// </summary>
+ [PermissionString("Moderate Members")]
+ ModerateMembers = 1L << 40
+ }
/// <summary>
/// Defines a readable name for this permission.
/// </summary>
- /// <param name="str">Readable name for this permission.</param>
- public PermissionStringAttribute(string str)
+ [AttributeUsage(AttributeTargets.Field)]
+ public sealed class PermissionStringAttribute : Attribute
{
- this.String = str;
+ /// <summary>
+ /// Gets the readable name for this permission.
+ /// </summary>
+ public string String { get; }
+
+ /// <summary>
+ /// Defines a readable name for this permission.
+ /// </summary>
+ /// <param name="str">Readable name for this permission.</param>
+ public PermissionStringAttribute(string str)
+ {
+ this.String = str;
+ }
}
}
diff --git a/DisCatSharp/Enums/ScheduledEvent/ScheduledEventEntityType.cs b/DisCatSharp/Enums/ScheduledEvent/ScheduledEventEntityType.cs
index 3d932e952..5780b1c38 100644
--- a/DisCatSharp/Enums/ScheduledEvent/ScheduledEventEntityType.cs
+++ b/DisCatSharp/Enums/ScheduledEvent/ScheduledEventEntityType.cs
@@ -1,44 +1,45 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the entity type for a scheduled event.
-/// </summary>
-public enum ScheduledEventEntityType : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that the events is hold in a stage instance.
+ /// Represents the entity type for a scheduled event.
/// </summary>
- StageInstance = 1,
+ public enum ScheduledEventEntityType : int
+ {
+ /// <summary>
+ /// Indicates that the events is hold in a stage instance.
+ /// </summary>
+ StageInstance = 1,
- /// <summary>
- /// Indicates that the events is hold in a voice channel.
- /// </summary>
- Voice = 2,
+ /// <summary>
+ /// Indicates that the events is hold in a voice channel.
+ /// </summary>
+ Voice = 2,
- /// <summary>
- /// Indicates that the events is hold external.
- /// </summary>
- External = 3
+ /// <summary>
+ /// Indicates that the events is hold external.
+ /// </summary>
+ External = 3
+ }
}
diff --git a/DisCatSharp/Enums/ScheduledEvent/ScheduledEventPrivacyLevel.cs b/DisCatSharp/Enums/ScheduledEvent/ScheduledEventPrivacyLevel.cs
index b87d42f4d..6183072c1 100644
--- a/DisCatSharp/Enums/ScheduledEvent/ScheduledEventPrivacyLevel.cs
+++ b/DisCatSharp/Enums/ScheduledEvent/ScheduledEventPrivacyLevel.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the privacy level for a guild scheduled event.
-/// </summary>
-public enum ScheduledEventPrivacyLevel : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that the guild scheduled event is public.
+ /// Represents the privacy level for a guild scheduled event.
/// </summary>
- Public = 1,
+ public enum ScheduledEventPrivacyLevel : int
+ {
+ /// <summary>
+ /// Indicates that the guild scheduled event is public.
+ /// </summary>
+ Public = 1,
- /// <summary>
- /// Indicates that the the guild scheduled event is only accessible to guild members.
- /// </summary>
- GuildOnly = 2
+ /// <summary>
+ /// Indicates that the the guild scheduled event is only accessible to guild members.
+ /// </summary>
+ GuildOnly = 2
+ }
}
diff --git a/DisCatSharp/Enums/ScheduledEvent/ScheduledEventStatus.cs b/DisCatSharp/Enums/ScheduledEvent/ScheduledEventStatus.cs
index 1b532bbd8..1888ab6f4 100644
--- a/DisCatSharp/Enums/ScheduledEvent/ScheduledEventStatus.cs
+++ b/DisCatSharp/Enums/ScheduledEvent/ScheduledEventStatus.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the status for a scheduled event.
-/// </summary>
-public enum ScheduledEventStatus : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that the event is scheduled.
+ /// Represents the status for a scheduled event.
/// </summary>
- Scheduled = 1,
+ public enum ScheduledEventStatus : int
+ {
+ /// <summary>
+ /// Indicates that the event is scheduled.
+ /// </summary>
+ Scheduled = 1,
- /// <summary>
- /// Indicates that the event is active.
- /// </summary>
- Active = 2,
+ /// <summary>
+ /// Indicates that the event is active.
+ /// </summary>
+ Active = 2,
- /// <summary>
- /// Indicates that the event is completed.
- /// </summary>
- Completed = 3,
+ /// <summary>
+ /// Indicates that the event is completed.
+ /// </summary>
+ Completed = 3,
- /// <summary>
- /// Indicates that the event is canceled.
- /// </summary>
- Canceled = 4
+ /// <summary>
+ /// Indicates that the event is canceled.
+ /// </summary>
+ Canceled = 4
+ }
}
diff --git a/DisCatSharp/Enums/Stage/StagePrivacyLevel.cs b/DisCatSharp/Enums/Stage/StagePrivacyLevel.cs
index f317e253e..693484da7 100644
--- a/DisCatSharp/Enums/Stage/StagePrivacyLevel.cs
+++ b/DisCatSharp/Enums/Stage/StagePrivacyLevel.cs
@@ -1,39 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the privacy level for a stage.
-/// </summary>
-public enum StagePrivacyLevel : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that the stage is public visible.
+ /// Represents the privacy level for a stage.
/// </summary>
- Public = 1,
+ public enum StagePrivacyLevel : int
+ {
+ /// <summary>
+ /// Indicates that the stage is public visible.
+ /// </summary>
+ Public = 1,
- /// <summary>
- /// Indicates that the stage is only visible to guild members.
- /// </summary>
- GuildOnly = 2
+ /// <summary>
+ /// Indicates that the stage is only visible to guild members.
+ /// </summary>
+ GuildOnly = 2
+ }
}
diff --git a/DisCatSharp/Enums/Thread/ThreadAutoArchiveDuration.cs b/DisCatSharp/Enums/Thread/ThreadAutoArchiveDuration.cs
index 220a4696b..127ff6919 100644
--- a/DisCatSharp/Enums/Thread/ThreadAutoArchiveDuration.cs
+++ b/DisCatSharp/Enums/Thread/ThreadAutoArchiveDuration.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents the auto-archive duration for a thread.
-/// </summary>
-public enum ThreadAutoArchiveDuration : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that the thread will be auto archived after one hour.
+ /// Represents the auto-archive duration for a thread.
/// </summary>
- OneHour = 60,
+ public enum ThreadAutoArchiveDuration : int
+ {
+ /// <summary>
+ /// Indicates that the thread will be auto archived after one hour.
+ /// </summary>
+ OneHour = 60,
- /// <summary>
- /// Indicates that the thread will be auto archived after one day / 24 hours.
- /// </summary>
- OneDay = 1440,
+ /// <summary>
+ /// Indicates that the thread will be auto archived after one day / 24 hours.
+ /// </summary>
+ OneDay = 1440,
- /// <summary>
- /// Indicates that the thread will be auto archived after three days.
- /// </summary>
- ThreeDays = 4320,
+ /// <summary>
+ /// Indicates that the thread will be auto archived after three days.
+ /// </summary>
+ ThreeDays = 4320,
- /// <summary>
- /// Indicates that the thread will be auto archived after a week.
- /// </summary>
- OneWeek = 10080
+ /// <summary>
+ /// Indicates that the thread will be auto archived after a week.
+ /// </summary>
+ OneWeek = 10080
+ }
}
diff --git a/DisCatSharp/Enums/Thread/ThreadMemberFlags.cs b/DisCatSharp/Enums/Thread/ThreadMemberFlags.cs
index d0ff4d2ca..2ccef3962 100644
--- a/DisCatSharp/Enums/Thread/ThreadMemberFlags.cs
+++ b/DisCatSharp/Enums/Thread/ThreadMemberFlags.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents notification settings for a thread.
-/// </summary>
-public enum ThreadMemberFlags : int
+namespace DisCatSharp
{
/// <summary>
- /// Indicates that the notification setting is set to has interacted.
+ /// Represents notification settings for a thread.
/// </summary>
- HasInteracted = 1,
+ public enum ThreadMemberFlags : int
+ {
+ /// <summary>
+ /// Indicates that the notification setting is set to has interacted.
+ /// </summary>
+ HasInteracted = 1,
- /// <summary>
- /// Indicates that the notification setting is set to all messages.
- /// </summary>
- AllMessages = 2,
+ /// <summary>
+ /// Indicates that the notification setting is set to all messages.
+ /// </summary>
+ AllMessages = 2,
- /// <summary>
- /// Indicates that the notification setting is set to only mentions.
- /// </summary>
- OnlyMentions = 4,
+ /// <summary>
+ /// Indicates that the notification setting is set to only mentions.
+ /// </summary>
+ OnlyMentions = 4,
- /// <summary>
- /// Indicates that the notification setting is set to none.
- /// </summary>
- None = 8
+ /// <summary>
+ /// Indicates that the notification setting is set to none.
+ /// </summary>
+ None = 8
+ }
}
diff --git a/DisCatSharp/Enums/TokenType.cs b/DisCatSharp/Enums/TokenType.cs
index cb24c09a6..0c57a7f1a 100644
--- a/DisCatSharp/Enums/TokenType.cs
+++ b/DisCatSharp/Enums/TokenType.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents the token type
-/// </summary>
-public enum TokenType
+namespace DisCatSharp
{
/// <summary>
- /// User token type
+ /// Represents the token type
/// </summary>
- [Obsolete("Logging in with a user token may result in your account being terminated, and is therefore highly unrecommended." +
- "\nIf anything goes wrong with this, we will not provide any support!", true)]
- User = 0,
+ public enum TokenType
+ {
+ /// <summary>
+ /// User token type
+ /// </summary>
+ [Obsolete("Logging in with a user token may result in your account being terminated, and is therefore highly unrecommended." +
+ "\nIf anything goes wrong with this, we will not provide any support!", true)]
+ User = 0,
- /// <summary>
- /// Bot token type
- /// </summary>
- Bot = 1,
+ /// <summary>
+ /// Bot token type
+ /// </summary>
+ Bot = 1,
- /// <summary>
- /// Bearer token type (used for oAuth)
- /// </summary>
- Bearer = 2
+ /// <summary>
+ /// Bearer token type (used for oAuth)
+ /// </summary>
+ Bearer = 2
+ }
}
diff --git a/DisCatSharp/Enums/User/PremiumType.cs b/DisCatSharp/Enums/User/PremiumType.cs
index 1957de3b5..e6d3f8a87 100644
--- a/DisCatSharp/Enums/User/PremiumType.cs
+++ b/DisCatSharp/Enums/User/PremiumType.cs
@@ -1,44 +1,45 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp;
-
-/// <summary>
-/// The type of Nitro subscription on a user's account.
-/// </summary>
-public enum PremiumType
+namespace DisCatSharp
{
/// <summary>
- /// User does not have any perks.
+ /// The type of Nitro subscription on a user's account.
/// </summary>
- None = 0,
+ public enum PremiumType
+ {
+ /// <summary>
+ /// User does not have any perks.
+ /// </summary>
+ None = 0,
- /// <summary>
- /// Includes basic app perks like animated emojis and avatars.
- /// </summary>
- NitroClassic = 1,
+ /// <summary>
+ /// Includes basic app perks like animated emojis and avatars.
+ /// </summary>
+ NitroClassic = 1,
- /// <summary>
- /// Includes all app perks.
- /// </summary>
- Nitro = 2
+ /// <summary>
+ /// Includes all app perks.
+ /// </summary>
+ Nitro = 2
+ }
}
diff --git a/DisCatSharp/Enums/User/UserFlags.cs b/DisCatSharp/Enums/User/UserFlags.cs
index 74bee3fc7..75201ff64 100644
--- a/DisCatSharp/Enums/User/UserFlags.cs
+++ b/DisCatSharp/Enums/User/UserFlags.cs
@@ -1,178 +1,179 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents additional details of a users account.
-/// </summary>
-[Flags]
-public enum UserFlags
+namespace DisCatSharp
{
/// <summary>
- /// The user has no flags.
- /// </summary>
- None = 0,
-
- /// <summary>
- /// The user is a Discord employee.
- /// </summary>
- Staff = 1 << 0,
-
- /// <summary>
- /// The user is a Discord partner.
- /// </summary>
- Partner = 1 << 1,
-
- /// <summary>
- /// The user has the HypeSquad badge.
- /// </summary>
- HypeSquad = 1 << 2,
-
- /// <summary>
- /// The user reached the first bug hunter tier.
- /// </summary>
- BugHunterLevelOne = 1 << 3,
-
- /// <summary>
- /// The user has SMS recovery for 2FA enabled.
- /// </summary>
- MfaSms = 1 << 4,
-
- /// <summary>
- /// The user is marked as dismissed Nitro promotion
- /// </summary>
- PremiumPromoDismissed = 1 << 5,
-
- /// <summary>
- /// The user is a member of house bravery.
- /// </summary>
- HouseBravery = 1 << 6,
-
- /// <summary>
- /// The user is a member of house brilliance.
- /// </summary>
- HouseBrilliance = 1 << 7,
-
- /// <summary>
- /// The user is a member of house balance.
- /// </summary>
- HouseBalance = 1 << 8,
-
- /// <summary>
- /// The user has the early supporter badge.
- /// </summary>
- PremiumEarlySupporter = 1 << 9,
-
- /// <summary>
- /// User is a <see cref="Entities.DiscordTeam"/>.
- /// </summary>
- TeamPseudoUser = 1 << 10,
-
- /// <summary>
- /// Relates to partner/verification applications.
- /// </summary>
- PartnerOrVerificationApplication = 1 << 11,
-
- /// <summary>
- /// Whether the user is an official system user.
- /// </summary>
- System = 1 << 12,
-
- /// <summary>
- /// Whether the user has unread system messages.
- /// </summary>
- HasUnreadUrgentMessages = 1 << 13,
-
- /// <summary>
- /// The user reached the second bug hunter tier.
- /// </summary>
- BugHunterLevelTwo = 1 << 14,
-
- /// <summary>
- /// The user has a pending deletion for being underage in DOB prompt.
- /// </summary>
- UnderageDeleted = 1 << 15,
-
- /// <summary>
- /// The user is a verified bot.
- /// </summary>
- VerifiedBot = 1 << 16,
-
- /// <summary>
- /// The user is a verified bot developer.
- /// </summary>
- VerifiedDeveloper = 1 << 17,
-
- /// <summary>
- /// The user is a discord certified moderator.
- /// </summary>
- CertifiedModerator = 1 << 18,
-
- /// <summary>
- /// The user is a bot and has set an interactions endpoint url.
- /// </summary>
- BotHttpInteractions = 1 << 19,
-
- /// <summary>
- /// The user is disabled for being a spammer.
- /// </summary>
- Spammer = 1 << 20,
-
- /// <summary>
- /// Nitro is disabled for user.
- /// Used by discord staff instead of forcedNonPremium.
- /// </summary>
- DisablePremium = 1 << 21,
-
- /// <summary>
- /// The user has a premium discriminator.
- /// </summary>
- PremiumDiscriminator = 1 << 37,
-
- /// <summary>
- /// The user has used the desktop client
- /// </summary>
- UsedDesktopClient = 1 << 38,
-
- /// <summary>
- /// The user has used the web client
- /// </summary>
- UsedWebClient = 1 << 39,
-
- /// <summary>
- /// The user has used the mobile client
- /// </summary>
- UsedMobileClient = 1 << 40,
-
- /// <summary>
- /// The user is currently temporarily or permanently disabled.
- /// </summary>
- Disabled = 1 << 42,
-
- /// <summary>
- /// The user has a verified email.
- /// </summary>
- VerifiedEmail = 1 << 43
+ /// Represents additional details of a users account.
+ /// </summary>
+ [Flags]
+ public enum UserFlags
+ {
+ /// <summary>
+ /// The user has no flags.
+ /// </summary>
+ None = 0,
+
+ /// <summary>
+ /// The user is a Discord employee.
+ /// </summary>
+ Staff = 1 << 0,
+
+ /// <summary>
+ /// The user is a Discord partner.
+ /// </summary>
+ Partner = 1 << 1,
+
+ /// <summary>
+ /// The user has the HypeSquad badge.
+ /// </summary>
+ HypeSquad = 1 << 2,
+
+ /// <summary>
+ /// The user reached the first bug hunter tier.
+ /// </summary>
+ BugHunterLevelOne = 1 << 3,
+
+ /// <summary>
+ /// The user has SMS recovery for 2FA enabled.
+ /// </summary>
+ MfaSms = 1 << 4,
+
+ /// <summary>
+ /// The user is marked as dismissed Nitro promotion
+ /// </summary>
+ PremiumPromoDismissed = 1 << 5,
+
+ /// <summary>
+ /// The user is a member of house bravery.
+ /// </summary>
+ HouseBravery = 1 << 6,
+
+ /// <summary>
+ /// The user is a member of house brilliance.
+ /// </summary>
+ HouseBrilliance = 1 << 7,
+
+ /// <summary>
+ /// The user is a member of house balance.
+ /// </summary>
+ HouseBalance = 1 << 8,
+
+ /// <summary>
+ /// The user has the early supporter badge.
+ /// </summary>
+ PremiumEarlySupporter = 1 << 9,
+
+ /// <summary>
+ /// User is a <see cref="Entities.DiscordTeam"/>.
+ /// </summary>
+ TeamPseudoUser = 1 << 10,
+
+ /// <summary>
+ /// Relates to partner/verification applications.
+ /// </summary>
+ PartnerOrVerificationApplication = 1 << 11,
+
+ /// <summary>
+ /// Whether the user is an official system user.
+ /// </summary>
+ System = 1 << 12,
+
+ /// <summary>
+ /// Whether the user has unread system messages.
+ /// </summary>
+ HasUnreadUrgentMessages = 1 << 13,
+
+ /// <summary>
+ /// The user reached the second bug hunter tier.
+ /// </summary>
+ BugHunterLevelTwo = 1 << 14,
+
+ /// <summary>
+ /// The user has a pending deletion for being underage in DOB prompt.
+ /// </summary>
+ UnderageDeleted = 1 << 15,
+
+ /// <summary>
+ /// The user is a verified bot.
+ /// </summary>
+ VerifiedBot = 1 << 16,
+
+ /// <summary>
+ /// The user is a verified bot developer.
+ /// </summary>
+ VerifiedDeveloper = 1 << 17,
+
+ /// <summary>
+ /// The user is a discord certified moderator.
+ /// </summary>
+ CertifiedModerator = 1 << 18,
+
+ /// <summary>
+ /// The user is a bot and has set an interactions endpoint url.
+ /// </summary>
+ BotHttpInteractions = 1 << 19,
+
+ /// <summary>
+ /// The user is disabled for being a spammer.
+ /// </summary>
+ Spammer = 1 << 20,
+
+ /// <summary>
+ /// Nitro is disabled for user.
+ /// Used by discord staff instead of forcedNonPremium.
+ /// </summary>
+ DisablePremium = 1 << 21,
+
+ /// <summary>
+ /// The user has a premium discriminator.
+ /// </summary>
+ PremiumDiscriminator = 1 << 37,
+
+ /// <summary>
+ /// The user has used the desktop client
+ /// </summary>
+ UsedDesktopClient = 1 << 38,
+
+ /// <summary>
+ /// The user has used the web client
+ /// </summary>
+ UsedWebClient = 1 << 39,
+
+ /// <summary>
+ /// The user has used the mobile client
+ /// </summary>
+ UsedMobileClient = 1 << 40,
+
+ /// <summary>
+ /// The user is currently temporarily or permanently disabled.
+ /// </summary>
+ Disabled = 1 << 42,
+
+ /// <summary>
+ /// The user has a verified email.
+ /// </summary>
+ VerifiedEmail = 1 << 43
+ }
}
diff --git a/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs b/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs
index 18b302be8..e3144f386 100644
--- a/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs
+++ b/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for application command events.
-/// </summary>
-public sealed class ApplicationCommandEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the command that was modified.
+ /// Represents arguments for application command events.
/// </summary>
- public DiscordApplicationCommand Command { get; internal set; }
+ public sealed class ApplicationCommandEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the command that was modified.
+ /// </summary>
+ public DiscordApplicationCommand Command { get; internal set; }
- /// <summary>
- /// Gets the optional guild of the command.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the optional guild of the command.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ApplicationCommandEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- public ApplicationCommandEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ApplicationCommandEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public ApplicationCommandEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs b/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs
index 14cd55163..9d87bc1b0 100644
--- a/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs
@@ -1,61 +1,62 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for application command permissions update events.
-/// </summary>
-public sealed class ApplicationCommandPermissionsUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the application command permissions.
- /// </summary>
- public List<DiscordApplicationCommandPermission> Permissions { get; internal set; }
-
- /// <summary>
- /// Gets the application command.
- /// </summary>
- public DiscordApplicationCommand Command { get; internal set; }
-
- /// <summary>
- /// Gets the application id.
- /// </summary>
- public ulong ApplicationId { get; internal set; }
-
- /// <summary>
- /// Gets the guild.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ApplicationCommandPermissionsUpdateEventArgs"/> class.
+ /// Represents arguments for application command permissions update events.
/// </summary>
- /// <param name="provider">The provider.</param>
- public ApplicationCommandPermissionsUpdateEventArgs(IServiceProvider provider) : base(provider)
- { }
+ public sealed class ApplicationCommandPermissionsUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the application command permissions.
+ /// </summary>
+ public List<DiscordApplicationCommandPermission> Permissions { get; internal set; }
+
+ /// <summary>
+ /// Gets the application command.
+ /// </summary>
+ public DiscordApplicationCommand Command { get; internal set; }
+
+ /// <summary>
+ /// Gets the application id.
+ /// </summary>
+ public ulong ApplicationId { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ApplicationCommandPermissionsUpdateEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public ApplicationCommandPermissionsUpdateEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs b/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs
index 3244d3c2d..a4c83d22b 100644
--- a/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs
+++ b/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs
@@ -1,60 +1,61 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for application command events.
-/// </summary>
-public sealed class GuildApplicationCommandCountEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the count of slash commands.
- /// </summary>
- public int SlashCommands { get; internal set; }
-
- /// <summary>
- /// Gets the count of user context menu commands.
- /// </summary>
- public int UserContextMenuCommands { get; internal set; }
-
- /// <summary>
- /// Gets the count of message context menu commands.
- /// </summary>
- public int MessageContextMenuCommands { get; internal set; }
-
- /// <summary>
- /// Gets the guild.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildApplicationCommandCountEventArgs"/> class.
+ /// Represents arguments for application command events.
/// </summary>
- /// <param name="provider">The provider.</param>
- public GuildApplicationCommandCountEventArgs(IServiceProvider provider) : base(provider)
- { }
+ public sealed class GuildApplicationCommandCountEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the count of slash commands.
+ /// </summary>
+ public int SlashCommands { get; internal set; }
+
+ /// <summary>
+ /// Gets the count of user context menu commands.
+ /// </summary>
+ public int UserContextMenuCommands { get; internal set; }
+
+ /// <summary>
+ /// Gets the count of message context menu commands.
+ /// </summary>
+ public int MessageContextMenuCommands { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildApplicationCommandCountEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public GuildApplicationCommandCountEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Channel/ChannelCreateEventArgs.cs b/DisCatSharp/EventArgs/Channel/ChannelCreateEventArgs.cs
index 7ff599851..43616bffb 100644
--- a/DisCatSharp/EventArgs/Channel/ChannelCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Channel/ChannelCreateEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ChannelCreated"/> event.
-/// </summary>
-public class ChannelCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the channel that was created.
+ /// Represents arguments for <see cref="DiscordClient.ChannelCreated"/> event.
/// </summary>
- public DiscordChannel Channel { get; internal set; }
+ public class ChannelCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the channel that was created.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the guild in which the channel was created.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the channel was created.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ChannelCreateEventArgs"/> class.
- /// </summary>
- internal ChannelCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ChannelCreateEventArgs"/> class.
+ /// </summary>
+ internal ChannelCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Channel/ChannelDeleteEventArgs.cs b/DisCatSharp/EventArgs/Channel/ChannelDeleteEventArgs.cs
index 55760513e..39d7035f3 100644
--- a/DisCatSharp/EventArgs/Channel/ChannelDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Channel/ChannelDeleteEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ChannelDeleted"/> event.
-/// </summary>
-public class ChannelDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the channel that was deleted.
+ /// Represents arguments for <see cref="DiscordClient.ChannelDeleted"/> event.
/// </summary>
- public DiscordChannel Channel { get; internal set; }
+ public class ChannelDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the channel that was deleted.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the guild this channel belonged to.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild this channel belonged to.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ChannelDeleteEventArgs"/> class.
- /// </summary>
- internal ChannelDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ChannelDeleteEventArgs"/> class.
+ /// </summary>
+ internal ChannelDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Channel/ChannelPinsUpdateEventArgs.cs b/DisCatSharp/EventArgs/Channel/ChannelPinsUpdateEventArgs.cs
index feb635e27..29ea5e55c 100644
--- a/DisCatSharp/EventArgs/Channel/ChannelPinsUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Channel/ChannelPinsUpdateEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ChannelPinsUpdated"/> event.
-/// </summary>
-public class ChannelPinsUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild in which the update occurred.
+ /// Represents arguments for <see cref="DiscordClient.ChannelPinsUpdated"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class ChannelPinsUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild in which the update occurred.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the channel in which the update occurred.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
+ /// <summary>
+ /// Gets the channel in which the update occurred.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the timestamp of the latest pin.
- /// </summary>
- public DateTimeOffset? LastPinTimestamp { get; internal set; }
+ /// <summary>
+ /// Gets the timestamp of the latest pin.
+ /// </summary>
+ public DateTimeOffset? LastPinTimestamp { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ChannelPinsUpdateEventArgs"/> class.
- /// </summary>
- internal ChannelPinsUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ChannelPinsUpdateEventArgs"/> class.
+ /// </summary>
+ internal ChannelPinsUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Channel/ChannelUpdateEventArgs.cs b/DisCatSharp/EventArgs/Channel/ChannelUpdateEventArgs.cs
index 978ec8b78..df7462a8a 100644
--- a/DisCatSharp/EventArgs/Channel/ChannelUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Channel/ChannelUpdateEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ChannelUpdated"/> event.
-/// </summary>
-public class ChannelUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the post-update channel.
+ /// Represents arguments for <see cref="DiscordClient.ChannelUpdated"/> event.
/// </summary>
- public DiscordChannel ChannelAfter { get; internal set; }
+ public class ChannelUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the post-update channel.
+ /// </summary>
+ public DiscordChannel ChannelAfter { get; internal set; }
- /// <summary>
- /// Gets the pre-update channel.
- /// </summary>
- public DiscordChannel ChannelBefore { get; internal set; }
+ /// <summary>
+ /// Gets the pre-update channel.
+ /// </summary>
+ public DiscordChannel ChannelBefore { get; internal set; }
- /// <summary>
- /// Gets the guild in which the update occurred.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the update occurred.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ChannelUpdateEventArgs"/> class.
- /// </summary>
- internal ChannelUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ChannelUpdateEventArgs"/> class.
+ /// </summary>
+ internal ChannelUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Channel/DMChannelDeleteEventArgs.cs b/DisCatSharp/EventArgs/Channel/DMChannelDeleteEventArgs.cs
index 193d3ae42..e6fd47309 100644
--- a/DisCatSharp/EventArgs/Channel/DMChannelDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Channel/DMChannelDeleteEventArgs.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.DmChannelDeleted"/> event.
-/// </summary>
-public class DmChannelDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the direct message channel that was deleted.
+ /// Represents arguments for <see cref="DiscordClient.DmChannelDeleted"/> event.
/// </summary>
- public DiscordDmChannel Channel { get; internal set; }
+ public class DmChannelDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the direct message channel that was deleted.
+ /// </summary>
+ public DiscordDmChannel Channel { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DmChannelDeleteEventArgs"/> class.
- /// </summary>
- internal DmChannelDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DmChannelDeleteEventArgs"/> class.
+ /// </summary>
+ internal DmChannelDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/ClientErrorEventArgs.cs b/DisCatSharp/EventArgs/ClientErrorEventArgs.cs
index 6feba8d81..46c36eb67 100644
--- a/DisCatSharp/EventArgs/ClientErrorEventArgs.cs
+++ b/DisCatSharp/EventArgs/ClientErrorEventArgs.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ClientErrored"/> event.
-/// </summary>
-public class ClientErrorEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the exception thrown by the client.
+ /// Represents arguments for <see cref="DiscordClient.ClientErrored"/> event.
/// </summary>
- public Exception Exception { get; internal set; }
+ public class ClientErrorEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the exception thrown by the client.
+ /// </summary>
+ public Exception Exception { get; internal set; }
- /// <summary>
- /// Gets the name of the event that threw the exception.
- /// </summary>
- public string EventName { get; internal set; }
+ /// <summary>
+ /// Gets the name of the event that threw the exception.
+ /// </summary>
+ public string EventName { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ClientErrorEventArgs"/> class.
- /// </summary>
- internal ClientErrorEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientErrorEventArgs"/> class.
+ /// </summary>
+ internal ClientErrorEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/DiscordEventArgs.cs b/DisCatSharp/EventArgs/DiscordEventArgs.cs
index a1b4cb987..5de761fa8 100644
--- a/DisCatSharp/EventArgs/DiscordEventArgs.cs
+++ b/DisCatSharp/EventArgs/DiscordEventArgs.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.DependencyInjection;
-namespace DisCatSharp.EventArgs;
-
-// Note: this might seem useless, but should we ever need to add a common property or method to all event arg
-// classes, it would be useful to already have a base for all of it.
-
-/// <summary>
-/// Common base for all other <see cref="DiscordClient"/>-related event argument classes.
-/// </summary>
-public abstract class DiscordEventArgs : AsyncEventArgs
+namespace DisCatSharp.EventArgs
{
- /// <summary>
- /// <para>Gets the service provider.</para>
- /// <para>This allows passing data around without resorting to static members.</para>
- /// <para>Defaults to an empty service provider.</para>
- /// </summary>
- public IServiceProvider ServiceProvider { get; internal set; } = new ServiceCollection().BuildServiceProvider(true);
+ // Note: this might seem useless, but should we ever need to add a common property or method to all event arg
+ // classes, it would be useful to already have a base for all of it.
/// <summary>
- /// Initializes a new instance of the <see cref="DiscordEventArgs"/> class.
+ /// Common base for all other <see cref="DiscordClient"/>-related event argument classes.
/// </summary>
- protected DiscordEventArgs(IServiceProvider provider)
+ public abstract class DiscordEventArgs : AsyncEventArgs
{
- if (provider != null)
- this.ServiceProvider = provider.CreateScope().ServiceProvider;
+ /// <summary>
+ /// <para>Gets the service provider.</para>
+ /// <para>This allows passing data around without resorting to static members.</para>
+ /// <para>Defaults to an empty service provider.</para>
+ /// </summary>
+ public IServiceProvider ServiceProvider { get; internal set; } = new ServiceCollection().BuildServiceProvider(true);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordEventArgs"/> class.
+ /// </summary>
+ protected DiscordEventArgs(IServiceProvider provider)
+ {
+ if (provider != null)
+ this.ServiceProvider = provider.CreateScope().ServiceProvider;
+ }
}
}
diff --git a/DisCatSharp/EventArgs/EmbeddedActivityUpdateEventArgs.cs b/DisCatSharp/EventArgs/EmbeddedActivityUpdateEventArgs.cs
index 6422ff2e6..c7a7833a5 100644
--- a/DisCatSharp/EventArgs/EmbeddedActivityUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/EmbeddedActivityUpdateEventArgs.cs
@@ -1,64 +1,65 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.EmbeddedActivityUpdated"/> event.
-/// </summary>
-public class EmbeddedActivityUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild.
+ /// Represents arguments for <see cref="DiscordClient.EmbeddedActivityUpdated"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class EmbeddedActivityUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the channel.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
+ /// <summary>
+ /// Gets the channel.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the embedded activity.
- /// </summary>
- public DiscordActivity EmbeddedActivityBefore { get; internal set; }
+ /// <summary>
+ /// Gets the embedded activity.
+ /// </summary>
+ public DiscordActivity EmbeddedActivityBefore { get; internal set; }
- /// <summary>
- /// Gets the embedded activity.
- /// </summary>
- public DiscordActivity EmbeddedActivityAfter { get; internal set; }
+ /// <summary>
+ /// Gets the embedded activity.
+ /// </summary>
+ public DiscordActivity EmbeddedActivityAfter { get; internal set; }
- /// <summary>
- /// Gets the users in the activity.
- /// </summary>
- public IReadOnlyList<DiscordMember> Users { get; internal set; }
+ /// <summary>
+ /// Gets the users in the activity.
+ /// </summary>
+ public IReadOnlyList<DiscordMember> Users { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="EmbeddedActivityUpdateEventArgs"/> class.
- /// </summary>
- internal EmbeddedActivityUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EmbeddedActivityUpdateEventArgs"/> class.
+ /// </summary>
+ internal EmbeddedActivityUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Ban/GuildBanAddEventArgs.cs b/DisCatSharp/EventArgs/Guild/Ban/GuildBanAddEventArgs.cs
index 803f719b5..7efd8cea3 100644
--- a/DisCatSharp/EventArgs/Guild/Ban/GuildBanAddEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Ban/GuildBanAddEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildBanAdded"/> event.
-/// </summary>
-public class GuildBanAddEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the member that was banned.
+ /// Represents arguments for <see cref="DiscordClient.GuildBanAdded"/> event.
/// </summary>
- public DiscordMember Member { get; internal set; }
+ public class GuildBanAddEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the member that was banned.
+ /// </summary>
+ public DiscordMember Member { get; internal set; }
- /// <summary>
- /// Gets the guild this member was banned in.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild this member was banned in.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildBanAddEventArgs"/> class.
- /// </summary>
- internal GuildBanAddEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildBanAddEventArgs"/> class.
+ /// </summary>
+ internal GuildBanAddEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Ban/GuildBanRemoveEventArgs.cs b/DisCatSharp/EventArgs/Guild/Ban/GuildBanRemoveEventArgs.cs
index 5a9953bc6..7b8ddc59c 100644
--- a/DisCatSharp/EventArgs/Guild/Ban/GuildBanRemoveEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Ban/GuildBanRemoveEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildBanRemoved"/> event.
-/// </summary>
-public class GuildBanRemoveEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the member that just got unbanned.
+ /// Represents arguments for <see cref="DiscordClient.GuildBanRemoved"/> event.
/// </summary>
- public DiscordMember Member { get; internal set; }
+ public class GuildBanRemoveEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the member that just got unbanned.
+ /// </summary>
+ public DiscordMember Member { get; internal set; }
- /// <summary>
- /// Gets the guild this member was unbanned in.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild this member was unbanned in.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildBanRemoveEventArgs"/> class.
- /// </summary>
- internal GuildBanRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildBanRemoveEventArgs"/> class.
+ /// </summary>
+ internal GuildBanRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/GuildCreateEventArgs.cs b/DisCatSharp/EventArgs/Guild/GuildCreateEventArgs.cs
index fdc0ae163..431e34842 100644
--- a/DisCatSharp/EventArgs/Guild/GuildCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/GuildCreateEventArgs.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildCreated"/> event.
-/// </summary>
-public class GuildCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild that was created.
+ /// Represents arguments for <see cref="DiscordClient.GuildCreated"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class GuildCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild that was created.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildCreateEventArgs"/> class.
- /// </summary>
- internal GuildCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildCreateEventArgs"/> class.
+ /// </summary>
+ internal GuildCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/GuildDeleteEventArgs.cs b/DisCatSharp/EventArgs/Guild/GuildDeleteEventArgs.cs
index 27828abf5..346d71d6c 100644
--- a/DisCatSharp/EventArgs/Guild/GuildDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/GuildDeleteEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildDeleted"/> event.
-/// </summary>
-public class GuildDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild that was deleted.
+ /// Represents arguments for <see cref="DiscordClient.GuildDeleted"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class GuildDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild that was deleted.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets whether the guild is unavailable or not.
- /// </summary>
- public bool Unavailable { get; internal set; }
+ /// <summary>
+ /// Gets whether the guild is unavailable or not.
+ /// </summary>
+ public bool Unavailable { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildDeleteEventArgs"/> class.
- /// </summary>
- internal GuildDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildDeleteEventArgs"/> class.
+ /// </summary>
+ internal GuildDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/GuildDownloadCompletedEventArgs.cs b/DisCatSharp/EventArgs/Guild/GuildDownloadCompletedEventArgs.cs
index 240a8d07a..a0f816b2a 100644
--- a/DisCatSharp/EventArgs/Guild/GuildDownloadCompletedEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/GuildDownloadCompletedEventArgs.cs
@@ -1,50 +1,51 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildDownloadCompleted"/> event.
-/// </summary>
-public class GuildDownloadCompletedEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the dictionary of guilds that just finished downloading.
+ /// Represents arguments for <see cref="DiscordClient.GuildDownloadCompleted"/> event.
/// </summary>
- public IReadOnlyDictionary<ulong, DiscordGuild> Guilds { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildDownloadCompletedEventArgs"/> class.
- /// </summary>
- /// <param name="guilds">The guilds.</param>
- /// <param name="provider">Service provider.</param>
- internal GuildDownloadCompletedEventArgs(IReadOnlyDictionary<ulong, DiscordGuild> guilds, IServiceProvider provider)
- : base(provider)
+ public class GuildDownloadCompletedEventArgs : DiscordEventArgs
{
- this.Guilds = guilds;
+ /// <summary>
+ /// Gets the dictionary of guilds that just finished downloading.
+ /// </summary>
+ public IReadOnlyDictionary<ulong, DiscordGuild> Guilds { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildDownloadCompletedEventArgs"/> class.
+ /// </summary>
+ /// <param name="guilds">The guilds.</param>
+ /// <param name="provider">Service provider.</param>
+ internal GuildDownloadCompletedEventArgs(IReadOnlyDictionary<ulong, DiscordGuild> guilds, IServiceProvider provider)
+ : base(provider)
+ {
+ this.Guilds = guilds;
+ }
}
}
diff --git a/DisCatSharp/EventArgs/Guild/GuildEmojisUpdateEventArgs.cs b/DisCatSharp/EventArgs/Guild/GuildEmojisUpdateEventArgs.cs
index a6d82c910..11db6eb0a 100644
--- a/DisCatSharp/EventArgs/Guild/GuildEmojisUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/GuildEmojisUpdateEventArgs.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildEmojisUpdated"/> event.
-/// </summary>
-public class GuildEmojisUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the list of emojis after the change.
+ /// Represents arguments for <see cref="DiscordClient.GuildEmojisUpdated"/> event.
/// </summary>
- public IReadOnlyDictionary<ulong, DiscordEmoji> EmojisAfter { get; internal set; }
+ public class GuildEmojisUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the list of emojis after the change.
+ /// </summary>
+ public IReadOnlyDictionary<ulong, DiscordEmoji> EmojisAfter { get; internal set; }
- /// <summary>
- /// Gets the list of emojis before the change.
- /// </summary>
- public IReadOnlyDictionary<ulong, DiscordEmoji> EmojisBefore { get; internal set; }
+ /// <summary>
+ /// Gets the list of emojis before the change.
+ /// </summary>
+ public IReadOnlyDictionary<ulong, DiscordEmoji> EmojisBefore { get; internal set; }
- /// <summary>
- /// Gets the guild in which the update occurred.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the update occurred.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildEmojisUpdateEventArgs"/> class.
- /// </summary>
- internal GuildEmojisUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildEmojisUpdateEventArgs"/> class.
+ /// </summary>
+ internal GuildEmojisUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/GuildIntegrationsUpdateEventArgs.cs b/DisCatSharp/EventArgs/Guild/GuildIntegrationsUpdateEventArgs.cs
index 0b0e29183..a2a9cab1e 100644
--- a/DisCatSharp/EventArgs/Guild/GuildIntegrationsUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/GuildIntegrationsUpdateEventArgs.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildIntegrationsUpdated"/> event.
-/// </summary>
-public class GuildIntegrationsUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild that had its integrations updated.
+ /// Represents arguments for <see cref="DiscordClient.GuildIntegrationsUpdated"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class GuildIntegrationsUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild that had its integrations updated.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildIntegrationsUpdateEventArgs"/> class.
- /// </summary>
- internal GuildIntegrationsUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildIntegrationsUpdateEventArgs"/> class.
+ /// </summary>
+ internal GuildIntegrationsUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/GuildStickersUpdateEventArgs.cs b/DisCatSharp/EventArgs/Guild/GuildStickersUpdateEventArgs.cs
index 8ff51ce7b..26b5c7e87 100644
--- a/DisCatSharp/EventArgs/Guild/GuildStickersUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/GuildStickersUpdateEventArgs.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents event args for the <see cref="DiscordClient.GuildStickersUpdated"/> event.
-/// </summary>
-public class GuildStickersUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the list of stickers after the change.
+ /// Represents event args for the <see cref="DiscordClient.GuildStickersUpdated"/> event.
/// </summary>
- public IReadOnlyDictionary<ulong, DiscordSticker> StickersAfter { get; internal set; }
+ public class GuildStickersUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the list of stickers after the change.
+ /// </summary>
+ public IReadOnlyDictionary<ulong, DiscordSticker> StickersAfter { get; internal set; }
- /// <summary>
- /// Gets the list of stickers before the change.
- /// </summary>
- public IReadOnlyDictionary<ulong, DiscordSticker> StickersBefore { get; internal set; }
+ /// <summary>
+ /// Gets the list of stickers before the change.
+ /// </summary>
+ public IReadOnlyDictionary<ulong, DiscordSticker> StickersBefore { get; internal set; }
- /// <summary>
- /// Gets the guild in which the update occurred.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the update occurred.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildStickersUpdateEventArgs"/> class.
- /// </summary>
- internal GuildStickersUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildStickersUpdateEventArgs"/> class.
+ /// </summary>
+ internal GuildStickersUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/GuildUpdateEventArgs.cs b/DisCatSharp/EventArgs/Guild/GuildUpdateEventArgs.cs
index b900030b6..f760470bc 100644
--- a/DisCatSharp/EventArgs/Guild/GuildUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/GuildUpdateEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildUpdated"/> event.
-/// </summary>
-public class GuildUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild before it was updated.
+ /// Represents arguments for <see cref="DiscordClient.GuildUpdated"/> event.
/// </summary>
- public DiscordGuild GuildBefore { get; internal set; }
+ public class GuildUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild before it was updated.
+ /// </summary>
+ public DiscordGuild GuildBefore { get; internal set; }
- /// <summary>
- /// Gets the guild after it was updated.
- /// </summary>
- public DiscordGuild GuildAfter { get; internal set; }
+ /// <summary>
+ /// Gets the guild after it was updated.
+ /// </summary>
+ public DiscordGuild GuildAfter { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildUpdateEventArgs"/> class.
- /// </summary>
- internal GuildUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildUpdateEventArgs"/> class.
+ /// </summary>
+ internal GuildUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationCreateEventArgs.cs b/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationCreateEventArgs.cs
index f34a11b9a..fdf8d6903 100644
--- a/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationCreateEventArgs.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildIntegrationCreated"/> event.
-/// </summary>
-public class GuildIntegrationCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the integration that was created.
+ /// Represents arguments for <see cref="DiscordClient.GuildIntegrationCreated"/> event.
/// </summary>
- ///
- public DiscordIntegration Integration { get; internal set; }
+ public class GuildIntegrationCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the integration that was created.
+ /// </summary>
+ ///
+ public DiscordIntegration Integration { get; internal set; }
- /// <summary>
- /// Gets the guild where the integration was created.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild where the integration was created.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildIntegrationCreateEventArgs"/> class.
- /// </summary>
- internal GuildIntegrationCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildIntegrationCreateEventArgs"/> class.
+ /// </summary>
+ internal GuildIntegrationCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationDeleteEventArgs.cs b/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationDeleteEventArgs.cs
index 7bb9b22c8..c2f9dee83 100644
--- a/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationDeleteEventArgs.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildIntegrationDeleted"/> event.
-/// </summary>
-public class GuildIntegrationDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the integration id which where deleted.
+ /// Represents arguments for <see cref="DiscordClient.GuildIntegrationDeleted"/> event.
/// </summary>
- ///
- public ulong IntegrationId { get; internal set; }
+ public class GuildIntegrationDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the integration id which where deleted.
+ /// </summary>
+ ///
+ public ulong IntegrationId { get; internal set; }
- /// <summary>
- /// Gets the guild where the integration which where deleted.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild where the integration which where deleted.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the application id of the integration which where deleted.
- /// </summary>
- public ulong? ApplicationId { get; internal set; }
+ /// <summary>
+ /// Gets the application id of the integration which where deleted.
+ /// </summary>
+ public ulong? ApplicationId { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildIntegrationDeleteEventArgs"/> class.
- /// </summary>
- internal GuildIntegrationDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildIntegrationDeleteEventArgs"/> class.
+ /// </summary>
+ internal GuildIntegrationDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationUpdateEventArgs.cs b/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationUpdateEventArgs.cs
index 6eb91cffc..fad1fd562 100644
--- a/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Integration/GuildIntegrationUpdateEventArgs.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildIntegrationUpdated"/> event.
-/// </summary>
-public class GuildIntegrationUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the integration that was updated.
+ /// Represents arguments for <see cref="DiscordClient.GuildIntegrationUpdated"/> event.
/// </summary>
- ///
- public DiscordIntegration Integration { get; internal set; }
+ public class GuildIntegrationUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the integration that was updated.
+ /// </summary>
+ ///
+ public DiscordIntegration Integration { get; internal set; }
- /// <summary>
- /// Gets the guild where the integration was updated.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild where the integration was updated.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildIntegrationUpdateEventArgs"/> class.
- /// </summary>
- internal GuildIntegrationUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildIntegrationUpdateEventArgs"/> class.
+ /// </summary>
+ internal GuildIntegrationUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Member/GuildMemberAddEventArgs.cs b/DisCatSharp/EventArgs/Guild/Member/GuildMemberAddEventArgs.cs
index 715e94084..ec26e7cd9 100644
--- a/DisCatSharp/EventArgs/Guild/Member/GuildMemberAddEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Member/GuildMemberAddEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildMemberAdded"/> event.
-/// </summary>
-public class GuildMemberAddEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the member that was added.
+ /// Represents arguments for <see cref="DiscordClient.GuildMemberAdded"/> event.
/// </summary>
- public DiscordMember Member { get; internal set; }
+ public class GuildMemberAddEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the member that was added.
+ /// </summary>
+ public DiscordMember Member { get; internal set; }
- /// <summary>
- /// Gets the guild the member was added to.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild the member was added to.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildMemberAddEventArgs"/> class.
- /// </summary>
- internal GuildMemberAddEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildMemberAddEventArgs"/> class.
+ /// </summary>
+ internal GuildMemberAddEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Member/GuildMemberRemoveEventArgs.cs b/DisCatSharp/EventArgs/Guild/Member/GuildMemberRemoveEventArgs.cs
index af469b35b..f08613cc7 100644
--- a/DisCatSharp/EventArgs/Guild/Member/GuildMemberRemoveEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Member/GuildMemberRemoveEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildMemberRemoved"/> event.
-/// </summary>
-public class GuildMemberRemoveEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild the member was removed from.
+ /// Represents arguments for <see cref="DiscordClient.GuildMemberRemoved"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class GuildMemberRemoveEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild the member was removed from.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the member that was removed.
- /// </summary>
- public DiscordMember Member { get; internal set; }
+ /// <summary>
+ /// Gets the member that was removed.
+ /// </summary>
+ public DiscordMember Member { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildMemberRemoveEventArgs"/> class.
- /// </summary>
- internal GuildMemberRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildMemberRemoveEventArgs"/> class.
+ /// </summary>
+ internal GuildMemberRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Member/GuildMemberUpdateEventArgs.cs b/DisCatSharp/EventArgs/Guild/Member/GuildMemberUpdateEventArgs.cs
index 8626b4018..644cd73e7 100644
--- a/DisCatSharp/EventArgs/Guild/Member/GuildMemberUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Member/GuildMemberUpdateEventArgs.cs
@@ -1,113 +1,114 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.Net;
-namespace DisCatSharp.EventArgs;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildMemberUpdated"/> event.
-/// </summary>
-public class GuildMemberUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild in which the update occurred.
+ /// Represents arguments for <see cref="DiscordClient.GuildMemberUpdated"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class GuildMemberUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild in which the update occurred.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets a collection containing post-update roles.
- /// </summary>
- public IReadOnlyList<DiscordRole> RolesAfter { get; internal set; }
+ /// <summary>
+ /// Gets a collection containing post-update roles.
+ /// </summary>
+ public IReadOnlyList<DiscordRole> RolesAfter { get; internal set; }
- /// <summary>
- /// Gets a collection containing pre-update roles.
- /// </summary>
- public IReadOnlyList<DiscordRole> RolesBefore { get; internal set; }
+ /// <summary>
+ /// Gets a collection containing pre-update roles.
+ /// </summary>
+ public IReadOnlyList<DiscordRole> RolesBefore { get; internal set; }
- /// <summary>
- /// Gets the member's new nickname.
- /// </summary>
- public string NicknameAfter { get; internal set; }
+ /// <summary>
+ /// Gets the member's new nickname.
+ /// </summary>
+ public string NicknameAfter { get; internal set; }
- /// <summary>
- /// Gets the member's old nickname.
- /// </summary>
- public string NicknameBefore { get; internal set; }
+ /// <summary>
+ /// Gets the member's old nickname.
+ /// </summary>
+ public string NicknameBefore { get; internal set; }
- /// <summary>
- /// Gets whether the member had passed membership screening before the update.
- /// </summary>
- public bool? PendingBefore { get; internal set; }
+ /// <summary>
+ /// Gets whether the member had passed membership screening before the update.
+ /// </summary>
+ public bool? PendingBefore { get; internal set; }
- /// <summary>
- /// Gets whether the member had passed membership screening after the update.
- /// </summary>
- public bool? PendingAfter { get; internal set; }
+ /// <summary>
+ /// Gets whether the member had passed membership screening after the update.
+ /// </summary>
+ public bool? PendingAfter { get; internal set; }
- /// <summary>
- /// Gets whether the member is timed out before the update.
- /// </summary>
- public DateTimeOffset? TimeoutBefore { get; internal set; }
+ /// <summary>
+ /// Gets whether the member is timed out before the update.
+ /// </summary>
+ public DateTimeOffset? TimeoutBefore { get; internal set; }
- /// <summary>
- /// Gets whether the member is timed out after the update.
- /// </summary>
- public DateTimeOffset? TimeoutAfter { get; internal set; }
+ /// <summary>
+ /// Gets whether the member is timed out after the update.
+ /// </summary>
+ public DateTimeOffset? TimeoutAfter { get; internal set; }
- /// <summary>
- /// Gets the member that was updated.
- /// </summary>
- public DiscordMember Member { get; internal set; }
+ /// <summary>
+ /// Gets the member that was updated.
+ /// </summary>
+ public DiscordMember Member { get; internal set; }
- public virtual string GuildAvatarHashBefore { get; internal set; }
+ public virtual string GuildAvatarHashBefore { get; internal set; }
- public string GuildAvatarUrlBefore
- => string.IsNullOrWhiteSpace(this.GuildAvatarHashBefore) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.Guild.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Member.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.GuildAvatarHashBefore}.{(this.GuildAvatarHashBefore.StartsWith("a_") ? "gif" : "png")}?size=1024";
+ public string GuildAvatarUrlBefore
+ => string.IsNullOrWhiteSpace(this.GuildAvatarHashBefore) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.Guild.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Member.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.GuildAvatarHashBefore}.{(this.GuildAvatarHashBefore.StartsWith("a_") ? "gif" : "png")}?size=1024";
- public virtual string GuildAvatarHashAfter { get; internal set; }
+ public virtual string GuildAvatarHashAfter { get; internal set; }
- public string GuildAvatarUrlAfter
- => string.IsNullOrWhiteSpace(this.GuildAvatarHashAfter) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.Guild.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Member.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.GuildAvatarHashAfter}.{(this.GuildAvatarHashAfter.StartsWith("a_") ? "gif" : "png")}?size=1024";
+ public string GuildAvatarUrlAfter
+ => string.IsNullOrWhiteSpace(this.GuildAvatarHashAfter) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.Guild.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Member.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.GuildAvatarHashAfter}.{(this.GuildAvatarHashAfter.StartsWith("a_") ? "gif" : "png")}?size=1024";
- public virtual string AvatarHashBefore { get; internal set; }
+ public virtual string AvatarHashBefore { get; internal set; }
- public string AvatarUrlBefore
- => string.IsNullOrWhiteSpace(this.AvatarHashBefore) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.Guild.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Member.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.AvatarHashBefore}.{(this.AvatarHashBefore.StartsWith("a_") ? "gif" : "png")}?size=1024";
+ public string AvatarUrlBefore
+ => string.IsNullOrWhiteSpace(this.AvatarHashBefore) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.Guild.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Member.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.AvatarHashBefore}.{(this.AvatarHashBefore.StartsWith("a_") ? "gif" : "png")}?size=1024";
- public virtual string AvatarHashAfter { get; internal set; }
+ public virtual string AvatarHashAfter { get; internal set; }
- public string AvatarUrlAfter
- => string.IsNullOrWhiteSpace(this.AvatarHashAfter) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.Guild.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Member.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.AvatarHashAfter}.{(this.AvatarHashAfter.StartsWith("a_") ? "gif" : "png")}?size=1024";
+ public string AvatarUrlAfter
+ => string.IsNullOrWhiteSpace(this.AvatarHashAfter) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this.Guild.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Member.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.AvatarHashAfter}.{(this.AvatarHashAfter.StartsWith("a_") ? "gif" : "png")}?size=1024";
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildMemberUpdateEventArgs"/> class.
- /// </summary>
- internal GuildMemberUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildMemberUpdateEventArgs"/> class.
+ /// </summary>
+ internal GuildMemberUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Member/GuildMembersChunkEventArgs.cs b/DisCatSharp/EventArgs/Guild/Member/GuildMembersChunkEventArgs.cs
index 3c9e7b37b..dc46d2d12 100644
--- a/DisCatSharp/EventArgs/Guild/Member/GuildMembersChunkEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Member/GuildMembersChunkEventArgs.cs
@@ -1,74 +1,75 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildMembersChunked"/> event.
-/// </summary>
-public class GuildMembersChunkEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild that requested this chunk.
+ /// Represents arguments for <see cref="DiscordClient.GuildMembersChunked"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class GuildMembersChunkEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild that requested this chunk.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the collection of members returned from this chunk.
- /// </summary>
- public IReadOnlyCollection<DiscordMember> Members { get; internal set; }
+ /// <summary>
+ /// Gets the collection of members returned from this chunk.
+ /// </summary>
+ public IReadOnlyCollection<DiscordMember> Members { get; internal set; }
- /// <summary>
- /// Gets the current chunk index from the response.
- /// </summary>
- public int ChunkIndex { get; internal set; }
+ /// <summary>
+ /// Gets the current chunk index from the response.
+ /// </summary>
+ public int ChunkIndex { get; internal set; }
- /// <summary>
- /// Gets the total amount of chunks for the request.
- /// </summary>
- public int ChunkCount { get; internal set; }
+ /// <summary>
+ /// Gets the total amount of chunks for the request.
+ /// </summary>
+ public int ChunkCount { get; internal set; }
- /// <summary>
- /// Gets the collection of presences returned from this chunk, if specified.
- /// </summary>
- public IReadOnlyCollection<DiscordPresence> Presences { get; internal set; }
+ /// <summary>
+ /// Gets the collection of presences returned from this chunk, if specified.
+ /// </summary>
+ public IReadOnlyCollection<DiscordPresence> Presences { get; internal set; }
- /// <summary>
- /// Gets the returned Ids that were not found in the chunk, if specified.
- /// </summary>
- public IReadOnlyCollection<ulong> NotFound { get; internal set; }
+ /// <summary>
+ /// Gets the returned Ids that were not found in the chunk, if specified.
+ /// </summary>
+ public IReadOnlyCollection<ulong> NotFound { get; internal set; }
- /// <summary>
- /// Gets the unique string used to identify the request, if specified.
- /// </summary>
- public string Nonce { get; set; }
+ /// <summary>
+ /// Gets the unique string used to identify the request, if specified.
+ /// </summary>
+ public string Nonce { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildMembersChunkEventArgs"/> class.
- /// </summary>
- internal GuildMembersChunkEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildMembersChunkEventArgs"/> class.
+ /// </summary>
+ internal GuildMembersChunkEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Role/GuildRoleCreateEventArgs.cs b/DisCatSharp/EventArgs/Guild/Role/GuildRoleCreateEventArgs.cs
index 67e951f7f..c015e2e8f 100644
--- a/DisCatSharp/EventArgs/Guild/Role/GuildRoleCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Role/GuildRoleCreateEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildRoleCreated"/> event.
-/// </summary>
-public class GuildRoleCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild in which the role was created.
+ /// Represents arguments for <see cref="DiscordClient.GuildRoleCreated"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class GuildRoleCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild in which the role was created.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the role that was created.
- /// </summary>
- public DiscordRole Role { get; internal set; }
+ /// <summary>
+ /// Gets the role that was created.
+ /// </summary>
+ public DiscordRole Role { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildRoleCreateEventArgs"/> class.
- /// </summary>
- internal GuildRoleCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildRoleCreateEventArgs"/> class.
+ /// </summary>
+ internal GuildRoleCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Role/GuildRoleDeleteEventArgs.cs b/DisCatSharp/EventArgs/Guild/Role/GuildRoleDeleteEventArgs.cs
index a510075fc..446500e01 100644
--- a/DisCatSharp/EventArgs/Guild/Role/GuildRoleDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Role/GuildRoleDeleteEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildRoleDeleted"/> event.
-/// </summary>
-public class GuildRoleDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild in which the role was deleted.
+ /// Represents arguments for <see cref="DiscordClient.GuildRoleDeleted"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class GuildRoleDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild in which the role was deleted.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the role that was deleted.
- /// </summary>
- public DiscordRole Role { get; internal set; }
+ /// <summary>
+ /// Gets the role that was deleted.
+ /// </summary>
+ public DiscordRole Role { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildRoleDeleteEventArgs"/> class.
- /// </summary>
- internal GuildRoleDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildRoleDeleteEventArgs"/> class.
+ /// </summary>
+ internal GuildRoleDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Role/GuildRoleUpdateEventArgs.cs b/DisCatSharp/EventArgs/Guild/Role/GuildRoleUpdateEventArgs.cs
index b7ea253f3..3ac65f281 100644
--- a/DisCatSharp/EventArgs/Guild/Role/GuildRoleUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Role/GuildRoleUpdateEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildRoleUpdated"/> event.
-/// </summary>
-public class GuildRoleUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild in which the update occurred.
+ /// Represents arguments for <see cref="DiscordClient.GuildRoleUpdated"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class GuildRoleUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild in which the update occurred.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the post-update role.
- /// </summary>
- public DiscordRole RoleAfter { get; internal set; }
+ /// <summary>
+ /// Gets the post-update role.
+ /// </summary>
+ public DiscordRole RoleAfter { get; internal set; }
- /// <summary>
- /// Gets the pre-update role.
- /// </summary>
- public DiscordRole RoleBefore { get; internal set; }
+ /// <summary>
+ /// Gets the pre-update role.
+ /// </summary>
+ public DiscordRole RoleBefore { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildRoleUpdateEventArgs"/> class.
- /// </summary>
- internal GuildRoleUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildRoleUpdateEventArgs"/> class.
+ /// </summary>
+ internal GuildRoleUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventCreateEventArgs.cs b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventCreateEventArgs.cs
index c0bdb1020..8a9eef617 100644
--- a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventCreateEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildScheduledEventCreated"/> event.
-/// </summary>
-public class GuildScheduledEventCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the scheduled event that was created.
+ /// Represents arguments for <see cref="DiscordClient.GuildScheduledEventCreated"/> event.
/// </summary>
- public DiscordScheduledEvent ScheduledEvent { get; internal set; }
+ public class GuildScheduledEventCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the scheduled event that was created.
+ /// </summary>
+ public DiscordScheduledEvent ScheduledEvent { get; internal set; }
- /// <summary>
- /// Gets the guild in which the scheduled event was created.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the scheduled event was created.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildScheduledEventCreateEventArgs"/> class.
- /// </summary>
- internal GuildScheduledEventCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildScheduledEventCreateEventArgs"/> class.
+ /// </summary>
+ internal GuildScheduledEventCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventDeleteEventArgs.cs b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventDeleteEventArgs.cs
index 7a97ce1e2..6a1f6cf51 100644
--- a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventDeleteEventArgs.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildScheduledEventDeleted"/> event.
-/// </summary>
-public class GuildScheduledEventDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the scheduled event that was deleted.
+ /// Represents arguments for <see cref="DiscordClient.GuildScheduledEventDeleted"/> event.
/// </summary>
- public DiscordScheduledEvent ScheduledEvent { get; internal set; }
+ public class GuildScheduledEventDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the scheduled event that was deleted.
+ /// </summary>
+ public DiscordScheduledEvent ScheduledEvent { get; internal set; }
- /// <summary>
- /// Gets the reason of deletion for the scheduled event.
- /// Important to determine why and how it was deleted.
- /// </summary>
- public ScheduledEventStatus Reason { get; internal set; }
+ /// <summary>
+ /// Gets the reason of deletion for the scheduled event.
+ /// Important to determine why and how it was deleted.
+ /// </summary>
+ public ScheduledEventStatus Reason { get; internal set; }
- /// <summary>
- /// Gets the guild in which the scheduled event was deleted.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the scheduled event was deleted.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildScheduledEventDeleteEventArgs"/> class.
- /// </summary>
- internal GuildScheduledEventDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildScheduledEventDeleteEventArgs"/> class.
+ /// </summary>
+ internal GuildScheduledEventDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUpdateEventArgs.cs b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUpdateEventArgs.cs
index 028da90bc..836ba1139 100644
--- a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUpdateEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildScheduledEventUpdated"/> event.
-/// </summary>
-public class GuildScheduledEventUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the scheduled event that was updated.
+ /// Represents arguments for <see cref="DiscordClient.GuildScheduledEventUpdated"/> event.
/// </summary>
- public DiscordScheduledEvent ScheduledEventAfter { get; internal set; }
+ public class GuildScheduledEventUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the scheduled event that was updated.
+ /// </summary>
+ public DiscordScheduledEvent ScheduledEventAfter { get; internal set; }
- /// <summary>
- /// Gets the old scheduled event that was updated.
- /// </summary>
- public DiscordScheduledEvent ScheduledEventBefore { get; internal set; }
+ /// <summary>
+ /// Gets the old scheduled event that was updated.
+ /// </summary>
+ public DiscordScheduledEvent ScheduledEventBefore { get; internal set; }
- /// <summary>
- /// Gets the guild in which the scheduled event was updated.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the scheduled event was updated.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildScheduledEventUpdateEventArgs"/> class.
- /// </summary>
- internal GuildScheduledEventUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildScheduledEventUpdateEventArgs"/> class.
+ /// </summary>
+ internal GuildScheduledEventUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUserAddEventArgs.cs b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUserAddEventArgs.cs
index 44cf4bd00..8be54ddc1 100644
--- a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUserAddEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUserAddEventArgs.cs
@@ -1,58 +1,59 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildScheduledEventUserAdded"/> event.
-/// </summary>
-public class GuildScheduledEventUserAddEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the scheduled event.
- /// </summary>
- public DiscordScheduledEvent ScheduledEvent { get; internal set; }
-
- /// <summary>
- /// Gets the guild.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Gets the user which has subscribed to this scheduled event.
- /// </summary>
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Gets the member which has subscribed to this scheduled event.
- /// </summary>
- public DiscordMember Member { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildScheduledEventUserAddEventArgs"/> class.
+ /// Represents arguments for <see cref="DiscordClient.GuildScheduledEventUserAdded"/> event.
/// </summary>
- internal GuildScheduledEventUserAddEventArgs(IServiceProvider provider) : base(provider) { }
+ public class GuildScheduledEventUserAddEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the scheduled event.
+ /// </summary>
+ public DiscordScheduledEvent ScheduledEvent { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Gets the user which has subscribed to this scheduled event.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the member which has subscribed to this scheduled event.
+ /// </summary>
+ public DiscordMember Member { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildScheduledEventUserAddEventArgs"/> class.
+ /// </summary>
+ internal GuildScheduledEventUserAddEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUserRemoveEventArgs.cs b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUserRemoveEventArgs.cs
index 362a8fba8..518ecf662 100644
--- a/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUserRemoveEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/ScheduledEvent/GuildScheduledEventUserRemoveEventArgs.cs
@@ -1,58 +1,59 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildScheduledEventUserRemoved"/> event.
-/// </summary>
-public class GuildScheduledEventUserRemoveEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the scheduled event.
- /// </summary>
- public DiscordScheduledEvent ScheduledEvent { get; internal set; }
-
- /// <summary>
- /// Gets the guild.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Gets the user which has unsubscribed from this scheduled event.
- /// </summary>
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Gets the member which has unsubscribed from this scheduled event.
- /// </summary>
- public DiscordMember Member { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildScheduledEventUserRemoveEventArgs"/> class.
+ /// Represents arguments for <see cref="DiscordClient.GuildScheduledEventUserRemoved"/> event.
/// </summary>
- internal GuildScheduledEventUserRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ public class GuildScheduledEventUserRemoveEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the scheduled event.
+ /// </summary>
+ public DiscordScheduledEvent ScheduledEvent { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Gets the user which has unsubscribed from this scheduled event.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the member which has unsubscribed from this scheduled event.
+ /// </summary>
+ public DiscordMember Member { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildScheduledEventUserRemoveEventArgs"/> class.
+ /// </summary>
+ internal GuildScheduledEventUserRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutAddEventArgs.cs b/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutAddEventArgs.cs
index ecb359050..c67e43f57 100644
--- a/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutAddEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutAddEventArgs.cs
@@ -1,68 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildMemberTimeoutAdded"/> event.
-/// </summary>
-public class GuildMemberTimeoutAddEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the member that was timed out.
+ /// Represents arguments for <see cref="DiscordClient.GuildMemberTimeoutAdded"/> event.
/// </summary>
- public DiscordMember Target { get; internal set; }
+ public class GuildMemberTimeoutAddEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the member that was timed out.
+ /// </summary>
+ public DiscordMember Target { get; internal set; }
- /// <summary>
- /// Gets the member that timed out the member.
- /// </summary>
- public DiscordMember Actor { get; internal set; }
+ /// <summary>
+ /// Gets the member that timed out the member.
+ /// </summary>
+ public DiscordMember Actor { get; internal set; }
- /// <summary>
- /// Gets the guild this member was timed out.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild this member was timed out.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the audit log id.
- /// </summary>
- public ulong? AuditLogId { get; internal set; }
+ /// <summary>
+ /// Gets the audit log id.
+ /// </summary>
+ public ulong? AuditLogId { get; internal set; }
- /// <summary>
- /// Gets the audit log reason.
- /// </summary>
- public string AuditLogReason { get; internal set; }
+ /// <summary>
+ /// Gets the audit log reason.
+ /// </summary>
+ public string AuditLogReason { get; internal set; }
- /// <summary>
- /// Gets the timeout time.
- /// </summary>
- public DateTimeOffset Timeout { get; internal set; }
+ /// <summary>
+ /// Gets the timeout time.
+ /// </summary>
+ public DateTimeOffset Timeout { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildMemberTimeoutAddEventArgs"/> class.
- /// </summary>
- internal GuildMemberTimeoutAddEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildMemberTimeoutAddEventArgs"/> class.
+ /// </summary>
+ internal GuildMemberTimeoutAddEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutRemoveEventArgs.cs b/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutRemoveEventArgs.cs
index 508552be0..05252ebef 100644
--- a/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutRemoveEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutRemoveEventArgs.cs
@@ -1,68 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildMemberTimeoutRemoved"/> event.
-/// </summary>
-public class GuildMemberTimeoutRemoveEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the member that was affected by the timeout.
+ /// Represents arguments for <see cref="DiscordClient.GuildMemberTimeoutRemoved"/> event.
/// </summary>
- public DiscordMember Target { get; internal set; }
+ public class GuildMemberTimeoutRemoveEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the member that was affected by the timeout.
+ /// </summary>
+ public DiscordMember Target { get; internal set; }
- /// <summary>
- /// Gets the member that removed the timeout for the member.
- /// </summary>
- public DiscordMember Actor { get; internal set; }
+ /// <summary>
+ /// Gets the member that removed the timeout for the member.
+ /// </summary>
+ public DiscordMember Actor { get; internal set; }
- /// <summary>
- /// Gets the guild this member was timed out.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild this member was timed out.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the audit log id.
- /// </summary>
- public ulong? AuditLogId { get; internal set; }
+ /// <summary>
+ /// Gets the audit log id.
+ /// </summary>
+ public ulong? AuditLogId { get; internal set; }
- /// <summary>
- /// Gets the audit log reason.
- /// </summary>
- public string AuditLogReason { get; internal set; }
+ /// <summary>
+ /// Gets the audit log reason.
+ /// </summary>
+ public string AuditLogReason { get; internal set; }
- /// <summary>
- /// Gets the timeout time before the remove.
- /// </summary>
- public DateTimeOffset TimeoutBefore { get; internal set; }
+ /// <summary>
+ /// Gets the timeout time before the remove.
+ /// </summary>
+ public DateTimeOffset TimeoutBefore { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildMemberTimeoutRemoveEventArgs"/> class.
- /// </summary>
- internal GuildMemberTimeoutRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildMemberTimeoutRemoveEventArgs"/> class.
+ /// </summary>
+ internal GuildMemberTimeoutRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutUpdateEventArgs.cs b/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutUpdateEventArgs.cs
index ea0c5f18c..ac404bd48 100644
--- a/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Timeout/GuildMemberTimeoutUpdateEventArgs.cs
@@ -1,73 +1,74 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.GuildMemberTimeoutChanged"/> event.
-/// </summary>
-public class GuildMemberTimeoutUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the member that was timed out.
+ /// Represents arguments for <see cref="DiscordClient.GuildMemberTimeoutChanged"/> event.
/// </summary>
- public DiscordMember Target { get; internal set; }
+ public class GuildMemberTimeoutUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the member that was timed out.
+ /// </summary>
+ public DiscordMember Target { get; internal set; }
- /// <summary>
- /// Gets the member that timed out the member.
- /// </summary>
- public DiscordMember Actor { get; internal set; }
+ /// <summary>
+ /// Gets the member that timed out the member.
+ /// </summary>
+ public DiscordMember Actor { get; internal set; }
- /// <summary>
- /// Gets the guild this member was timed out.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild this member was timed out.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the audit log id.
- /// </summary>
- public ulong? AuditLogId { get; internal set; }
+ /// <summary>
+ /// Gets the audit log id.
+ /// </summary>
+ public ulong? AuditLogId { get; internal set; }
- /// <summary>
- /// Gets the audit log reason.
- /// </summary>
- public string AuditLogReason { get; internal set; }
+ /// <summary>
+ /// Gets the audit log reason.
+ /// </summary>
+ public string AuditLogReason { get; internal set; }
- /// <summary>
- /// Gets the timeout time before the update.
- /// </summary>
- public DateTimeOffset TimeoutBefore { get; internal set; }
+ /// <summary>
+ /// Gets the timeout time before the update.
+ /// </summary>
+ public DateTimeOffset TimeoutBefore { get; internal set; }
- /// <summary>
- /// Gets the timeout time after the update.
- /// </summary>
- public DateTimeOffset TimeoutAfter { get; internal set; }
+ /// <summary>
+ /// Gets the timeout time after the update.
+ /// </summary>
+ public DateTimeOffset TimeoutAfter { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildMemberTimeoutAddEventArgs"/> class.
- /// </summary>
- internal GuildMemberTimeoutUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildMemberTimeoutAddEventArgs"/> class.
+ /// </summary>
+ internal GuildMemberTimeoutUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/HeartBeatEventArgs.cs b/DisCatSharp/EventArgs/HeartBeatEventArgs.cs
index 9a9e80b98..be4185a40 100644
--- a/DisCatSharp/EventArgs/HeartBeatEventArgs.cs
+++ b/DisCatSharp/EventArgs/HeartBeatEventArgs.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.Heartbeated"/> event.
-/// </summary>
-public class HeartbeatEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the round-trip time of the heartbeat.
+ /// Represents arguments for <see cref="DiscordClient.Heartbeated"/> event.
/// </summary>
- public int Ping { get; internal set; }
+ public class HeartbeatEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the round-trip time of the heartbeat.
+ /// </summary>
+ public int Ping { get; internal set; }
- /// <summary>
- /// Gets the timestamp of the heartbeat.
- /// </summary>
- public DateTimeOffset Timestamp { get; internal set; }
+ /// <summary>
+ /// Gets the timestamp of the heartbeat.
+ /// </summary>
+ public DateTimeOffset Timestamp { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="HeartbeatEventArgs"/> class.
- /// </summary>
- internal HeartbeatEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HeartbeatEventArgs"/> class.
+ /// </summary>
+ internal HeartbeatEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs b/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs
index 712b98732..749b96dee 100644
--- a/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs
@@ -1,61 +1,62 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// The context menu interaction create event args.
-/// </summary>
-public sealed class ContextMenuInteractionCreateEventArgs : InteractionCreateEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// The type of context menu that was used. This is never <see cref="DisCatSharp.Enums.ApplicationCommandType.ChatInput"/>.
- /// </summary>
- public ApplicationCommandType Type { get; internal set; }
-
- /// <summary>
- /// The user that invoked this interaction. Can be cast to a member if this was on a guild.
- /// </summary>
- public DiscordUser User => this.Interaction.User;
-
- /// <summary>
- /// The user this interaction targets, if applicable.
- /// </summary>
- public DiscordUser TargetUser { get; internal set; }
-
- /// <summary>
- /// The message this interaction targets, if applicable.
- /// </summary>
- public DiscordMessage TargetMessage { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ContextMenuInteractionCreateEventArgs"/> class.
+ /// The context menu interaction create event args.
/// </summary>
- /// <param name="provider">The provider.</param>
- public ContextMenuInteractionCreateEventArgs(IServiceProvider provider) : base(provider)
- { }
+ public sealed class ContextMenuInteractionCreateEventArgs : InteractionCreateEventArgs
+ {
+ /// <summary>
+ /// The type of context menu that was used. This is never <see cref="DisCatSharp.Enums.ApplicationCommandType.ChatInput"/>.
+ /// </summary>
+ public ApplicationCommandType Type { get; internal set; }
+
+ /// <summary>
+ /// The user that invoked this interaction. Can be cast to a member if this was on a guild.
+ /// </summary>
+ public DiscordUser User => this.Interaction.User;
+
+ /// <summary>
+ /// The user this interaction targets, if applicable.
+ /// </summary>
+ public DiscordUser TargetUser { get; internal set; }
+
+ /// <summary>
+ /// The message this interaction targets, if applicable.
+ /// </summary>
+ public DiscordMessage TargetMessage { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ContextMenuInteractionCreateEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public ContextMenuInteractionCreateEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs b/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs
index 2d1284d00..43f68f1e9 100644
--- a/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.InteractionCreated"/>
-/// </summary>
-public class InteractionCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the interaction data that was invoked.
+ /// Represents arguments for <see cref="DiscordClient.InteractionCreated"/>
/// </summary>
- public DiscordInteraction Interaction { get; internal set; }
+ public class InteractionCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the interaction data that was invoked.
+ /// </summary>
+ public DiscordInteraction Interaction { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="InteractionCreateEventArgs"/> class.
- /// </summary>
- /// <param name="provider">The provider.</param>
- public InteractionCreateEventArgs(IServiceProvider provider) : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InteractionCreateEventArgs"/> class.
+ /// </summary>
+ /// <param name="provider">The provider.</param>
+ public InteractionCreateEventArgs(IServiceProvider provider) : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Invite/InviteCreateEventArgs.cs b/DisCatSharp/EventArgs/Invite/InviteCreateEventArgs.cs
index ee5430d14..040e626ac 100644
--- a/DisCatSharp/EventArgs/Invite/InviteCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Invite/InviteCreateEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.InviteCreated"/>
-/// </summary>
-public sealed class InviteCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild that created the invite.
+ /// Represents arguments for <see cref="DiscordClient.InviteCreated"/>
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public sealed class InviteCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild that created the invite.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the channel that the invite is for.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
+ /// <summary>
+ /// Gets the channel that the invite is for.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the created invite.
- /// </summary>
- public DiscordInvite Invite { get; internal set; }
+ /// <summary>
+ /// Gets the created invite.
+ /// </summary>
+ public DiscordInvite Invite { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="InviteCreateEventArgs"/> class.
- /// </summary>
- internal InviteCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InviteCreateEventArgs"/> class.
+ /// </summary>
+ internal InviteCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Invite/InviteDeleteEventArgs.cs b/DisCatSharp/EventArgs/Invite/InviteDeleteEventArgs.cs
index 51a6b84b1..0fe4e2653 100644
--- a/DisCatSharp/EventArgs/Invite/InviteDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Invite/InviteDeleteEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.InviteDeleted"/>
-/// </summary>
-public sealed class InviteDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild that deleted the invite.
+ /// Represents arguments for <see cref="DiscordClient.InviteDeleted"/>
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public sealed class InviteDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild that deleted the invite.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the channel that the invite was for.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
+ /// <summary>
+ /// Gets the channel that the invite was for.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the deleted invite.
- /// </summary>
- public DiscordInvite Invite { get; internal set; }
+ /// <summary>
+ /// Gets the deleted invite.
+ /// </summary>
+ public DiscordInvite Invite { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="InviteDeleteEventArgs"/> class.
- /// </summary>
- internal InviteDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="InviteDeleteEventArgs"/> class.
+ /// </summary>
+ internal InviteDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/ComponentInteractionCreateEventArgs.cs b/DisCatSharp/EventArgs/Message/ComponentInteractionCreateEventArgs.cs
index 32909fb77..a42422f7d 100644
--- a/DisCatSharp/EventArgs/Message/ComponentInteractionCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/ComponentInteractionCreateEventArgs.cs
@@ -1,68 +1,70 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ComponentInteractionCreated"/>.
-/// </summary>
-public class ComponentInteractionCreateEventArgs : InteractionCreateEventArgs
+namespace DisCatSharp.EventArgs
{
- /// <summary>
- /// The Id of the component that was interacted with.
- /// </summary>
- public string Id => this.Interaction.Data.CustomId;
/// <summary>
- /// The user that invoked this interaction.
+ /// Represents arguments for <see cref="DiscordClient.ComponentInteractionCreated"/>.
/// </summary>
- public DiscordUser User => this.Interaction.User;
+ public class ComponentInteractionCreateEventArgs : InteractionCreateEventArgs
+ {
+ /// <summary>
+ /// The Id of the component that was interacted with.
+ /// </summary>
+ public string Id => this.Interaction.Data.CustomId;
- /// <summary>
- /// The guild this interaction was invoked on, if any.
- /// </summary>
- public DiscordGuild Guild => this.Channel.Guild;
+ /// <summary>
+ /// The user that invoked this interaction.
+ /// </summary>
+ public DiscordUser User => this.Interaction.User;
- /// <summary>
- /// The channel this interaction was invoked in.
- /// </summary>
- public DiscordChannel Channel => this.Interaction.Channel;
+ /// <summary>
+ /// The guild this interaction was invoked on, if any.
+ /// </summary>
+ public DiscordGuild Guild => this.Channel.Guild;
- /// <summary>
- /// The value(s) selected. Only applicable to SelectMenu components.
- /// </summary>
- public string[] Values => this.Interaction.Data.Values;
+ /// <summary>
+ /// The channel this interaction was invoked in.
+ /// </summary>
+ public DiscordChannel Channel => this.Interaction.Channel;
- /// <summary>
- /// The message this interaction is attached to.
- /// </summary>
- public DiscordMessage Message { get; internal set; }
+ /// <summary>
+ /// The value(s) selected. Only applicable to SelectMenu components.
+ /// </summary>
+ public string[] Values => this.Interaction.Data.Values;
- /// <summary>
- /// Initializes a new instance of the <see cref="ComponentInteractionCreateEventArgs"/> class.
- /// </summary>
- internal ComponentInteractionCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// The message this interaction is attached to.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ComponentInteractionCreateEventArgs"/> class.
+ /// </summary>
+ internal ComponentInteractionCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/MessageAcknowledgeEventArgs.cs b/DisCatSharp/EventArgs/Message/MessageAcknowledgeEventArgs.cs
index fdbd03eaf..dfc8e44fc 100644
--- a/DisCatSharp/EventArgs/Message/MessageAcknowledgeEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/MessageAcknowledgeEventArgs.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.MessageAcknowledged"/> event.
-/// </summary>
-public sealed class MessageAcknowledgeEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the message that was acknowledged.
+ /// Represents arguments for <see cref="DiscordClient.MessageAcknowledged"/> event.
/// </summary>
- public DiscordMessage Message { get; internal set; }
+ public sealed class MessageAcknowledgeEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the message that was acknowledged.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
- /// <summary>
- /// Gets the channel for which the message was acknowledged.
- /// </summary>
- public DiscordChannel Channel
- => this.Message.Channel;
+ /// <summary>
+ /// Gets the channel for which the message was acknowledged.
+ /// </summary>
+ public DiscordChannel Channel
+ => this.Message.Channel;
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageAcknowledgeEventArgs"/> class.
- /// </summary>
- internal MessageAcknowledgeEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageAcknowledgeEventArgs"/> class.
+ /// </summary>
+ internal MessageAcknowledgeEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/MessageBulkDeleteEventArgs.cs b/DisCatSharp/EventArgs/Message/MessageBulkDeleteEventArgs.cs
index c287f89aa..60f66864b 100644
--- a/DisCatSharp/EventArgs/Message/MessageBulkDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/MessageBulkDeleteEventArgs.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.MessagesBulkDeleted"/> event.
-/// </summary>
-public class MessageBulkDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets a collection of the deleted messages.
+ /// Represents arguments for <see cref="DiscordClient.MessagesBulkDeleted"/> event.
/// </summary>
- public IReadOnlyList<DiscordMessage> Messages { get; internal set; }
+ public class MessageBulkDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets a collection of the deleted messages.
+ /// </summary>
+ public IReadOnlyList<DiscordMessage> Messages { get; internal set; }
- /// <summary>
- /// Gets the channel in which the deletion occurred.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
+ /// <summary>
+ /// Gets the channel in which the deletion occurred.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the guild in which the deletion occurred.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the deletion occurred.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageBulkDeleteEventArgs"/> class.
- /// </summary>
- internal MessageBulkDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageBulkDeleteEventArgs"/> class.
+ /// </summary>
+ internal MessageBulkDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/MessageCreateEventArgs.cs b/DisCatSharp/EventArgs/Message/MessageCreateEventArgs.cs
index e4180bb73..073c65bae 100644
--- a/DisCatSharp/EventArgs/Message/MessageCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/MessageCreateEventArgs.cs
@@ -1,77 +1,78 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.MessageCreated"/> event.
-/// </summary>
-public class MessageCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the message that was created.
+ /// Represents arguments for <see cref="DiscordClient.MessageCreated"/> event.
/// </summary>
- public DiscordMessage Message { get; internal set; }
+ public class MessageCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the message that was created.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
- /// <summary>
- /// Gets the channel this message belongs to.
- /// </summary>
- public DiscordChannel Channel
- => this.Message.Channel;
+ /// <summary>
+ /// Gets the channel this message belongs to.
+ /// </summary>
+ public DiscordChannel Channel
+ => this.Message.Channel;
- /// <summary>
- /// Gets the guild this message belongs to.
- /// </summary>
- public DiscordGuild Guild
- => this.Channel.Guild;
+ /// <summary>
+ /// Gets the guild this message belongs to.
+ /// </summary>
+ public DiscordGuild Guild
+ => this.Channel.Guild;
- /// <summary>
- /// Gets the author of the message.
- /// </summary>
- public DiscordUser Author
- => this.Message.Author;
+ /// <summary>
+ /// Gets the author of the message.
+ /// </summary>
+ public DiscordUser Author
+ => this.Message.Author;
- /// <summary>
- /// Gets the collection of mentioned users.
- /// </summary>
- public IReadOnlyList<DiscordUser> MentionedUsers { get; internal set; }
+ /// <summary>
+ /// Gets the collection of mentioned users.
+ /// </summary>
+ public IReadOnlyList<DiscordUser> MentionedUsers { get; internal set; }
- /// <summary>
- /// Gets the collection of mentioned roles.
- /// </summary>
- public IReadOnlyList<DiscordRole> MentionedRoles { get; internal set; }
+ /// <summary>
+ /// Gets the collection of mentioned roles.
+ /// </summary>
+ public IReadOnlyList<DiscordRole> MentionedRoles { get; internal set; }
- /// <summary>
- /// Gets the collection of mentioned channels.
- /// </summary>
- public IReadOnlyList<DiscordChannel> MentionedChannels { get; internal set; }
+ /// <summary>
+ /// Gets the collection of mentioned channels.
+ /// </summary>
+ public IReadOnlyList<DiscordChannel> MentionedChannels { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageCreateEventArgs"/> class.
- /// </summary>
- internal MessageCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageCreateEventArgs"/> class.
+ /// </summary>
+ internal MessageCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/MessageDeleteEventArgs.cs b/DisCatSharp/EventArgs/Message/MessageDeleteEventArgs.cs
index b127eb601..4cd884a54 100644
--- a/DisCatSharp/EventArgs/Message/MessageDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/MessageDeleteEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.MessageDeleted"/> event.
-/// </summary>
-public class MessageDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the message that was deleted.
+ /// Represents arguments for <see cref="DiscordClient.MessageDeleted"/> event.
/// </summary>
- public DiscordMessage Message { get; internal set; }
+ public class MessageDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the message that was deleted.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
- /// <summary>
- /// Gets the channel this message belonged to.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
+ /// <summary>
+ /// Gets the channel this message belonged to.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the guild this message belonged to.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild this message belonged to.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageDeleteEventArgs"/> class.
- /// </summary>
- internal MessageDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageDeleteEventArgs"/> class.
+ /// </summary>
+ internal MessageDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/MessageUpdateEventArgs.cs b/DisCatSharp/EventArgs/Message/MessageUpdateEventArgs.cs
index fa1653fa4..84cd43f8f 100644
--- a/DisCatSharp/EventArgs/Message/MessageUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/MessageUpdateEventArgs.cs
@@ -1,82 +1,83 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.MessageUpdated"/> event.
-/// </summary>
-public class MessageUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the message that was updated.
+ /// Represents arguments for <see cref="DiscordClient.MessageUpdated"/> event.
/// </summary>
- public DiscordMessage Message { get; internal set; }
+ public class MessageUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the message that was updated.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
- /// <summary>
- /// Gets the message before it got updated. This property will be null if the message was not cached.
- /// </summary>
- public DiscordMessage MessageBefore { get; internal set; }
+ /// <summary>
+ /// Gets the message before it got updated. This property will be null if the message was not cached.
+ /// </summary>
+ public DiscordMessage MessageBefore { get; internal set; }
- /// <summary>
- /// Gets the channel this message belongs to.
- /// </summary>
- public DiscordChannel Channel
- => this.Message.Channel;
+ /// <summary>
+ /// Gets the channel this message belongs to.
+ /// </summary>
+ public DiscordChannel Channel
+ => this.Message.Channel;
- /// <summary>
- /// Gets the guild this message belongs to.
- /// </summary>
- public DiscordGuild Guild
- => this.Channel.Guild;
+ /// <summary>
+ /// Gets the guild this message belongs to.
+ /// </summary>
+ public DiscordGuild Guild
+ => this.Channel.Guild;
- /// <summary>
- /// Gets the author of the message.
- /// </summary>
- public DiscordUser Author
- => this.Message.Author;
+ /// <summary>
+ /// Gets the author of the message.
+ /// </summary>
+ public DiscordUser Author
+ => this.Message.Author;
- /// <summary>
- /// Gets the collection of mentioned users.
- /// </summary>
- public IReadOnlyList<DiscordUser> MentionedUsers { get; internal set; }
+ /// <summary>
+ /// Gets the collection of mentioned users.
+ /// </summary>
+ public IReadOnlyList<DiscordUser> MentionedUsers { get; internal set; }
- /// <summary>
- /// Gets the collection of mentioned roles.
- /// </summary>
- public IReadOnlyList<DiscordRole> MentionedRoles { get; internal set; }
+ /// <summary>
+ /// Gets the collection of mentioned roles.
+ /// </summary>
+ public IReadOnlyList<DiscordRole> MentionedRoles { get; internal set; }
- /// <summary>
- /// Gets the collection of mentioned channels.
- /// </summary>
- public IReadOnlyList<DiscordChannel> MentionedChannels { get; internal set; }
+ /// <summary>
+ /// Gets the collection of mentioned channels.
+ /// </summary>
+ public IReadOnlyList<DiscordChannel> MentionedChannels { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageUpdateEventArgs"/> class.
- /// </summary>
- internal MessageUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageUpdateEventArgs"/> class.
+ /// </summary>
+ internal MessageUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/Reaction/MessageReactionAddEventArgs.cs b/DisCatSharp/EventArgs/Message/Reaction/MessageReactionAddEventArgs.cs
index fb729aac4..6ae1496be 100644
--- a/DisCatSharp/EventArgs/Message/Reaction/MessageReactionAddEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/Reaction/MessageReactionAddEventArgs.cs
@@ -1,69 +1,70 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.MessageReactionAdded"/> event.
-/// </summary>
-public class MessageReactionAddEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the message for which the update occurred.
+ /// Represents arguments for <see cref="DiscordClient.MessageReactionAdded"/> event.
/// </summary>
- public DiscordMessage Message { get; internal set; }
+ public class MessageReactionAddEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the message for which the update occurred.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
- /// <summary>
- /// Gets the channel to which this message belongs.
- /// </summary>
- /// <remarks>
- /// This will be <c>null</c> for an uncached channel, which will usually happen for when this event triggers on
- /// DM channels in which no prior messages were received or sent.
- /// </remarks>
- public DiscordChannel Channel
- => this.Message.Channel;
+ /// <summary>
+ /// Gets the channel to which this message belongs.
+ /// </summary>
+ /// <remarks>
+ /// This will be <c>null</c> for an uncached channel, which will usually happen for when this event triggers on
+ /// DM channels in which no prior messages were received or sent.
+ /// </remarks>
+ public DiscordChannel Channel
+ => this.Message.Channel;
- /// <summary>
- /// Gets the user who created the reaction.
- /// <para>This can be cast to a <see cref="DisCatSharp.Entities.DiscordMember"/> if the reaction was in a guild.</para>
- /// </summary>
- public DiscordUser User { get; internal set; }
+ /// <summary>
+ /// Gets the user who created the reaction.
+ /// <para>This can be cast to a <see cref="DisCatSharp.Entities.DiscordMember"/> if the reaction was in a guild.</para>
+ /// </summary>
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the guild in which the reaction was added.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the reaction was added.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the emoji used for this reaction.
- /// </summary>
- public DiscordEmoji Emoji { get; internal set; }
+ /// <summary>
+ /// Gets the emoji used for this reaction.
+ /// </summary>
+ public DiscordEmoji Emoji { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageReactionAddEventArgs"/> class.
- /// </summary>
- internal MessageReactionAddEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageReactionAddEventArgs"/> class.
+ /// </summary>
+ internal MessageReactionAddEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/Reaction/MessageReactionRemoveEmojiEventArgs.cs b/DisCatSharp/EventArgs/Message/Reaction/MessageReactionRemoveEmojiEventArgs.cs
index 1cb2d61ff..fc75b7923 100644
--- a/DisCatSharp/EventArgs/Message/Reaction/MessageReactionRemoveEmojiEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/Reaction/MessageReactionRemoveEmojiEventArgs.cs
@@ -1,58 +1,59 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.MessageReactionRemovedEmoji"/>
-/// </summary>
-public sealed class MessageReactionRemoveEmojiEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the channel the removed reactions were in.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the guild the removed reactions were in.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Gets the message that had the removed reactions.
- /// </summary>
- public DiscordMessage Message { get; internal set; }
-
- /// <summary>
- /// Gets the emoji of the reaction that was removed.
- /// </summary>
- public DiscordEmoji Emoji { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageReactionRemoveEmojiEventArgs"/> class.
+ /// Represents arguments for <see cref="DiscordClient.MessageReactionRemovedEmoji"/>
/// </summary>
- internal MessageReactionRemoveEmojiEventArgs(IServiceProvider provider) : base(provider) { }
+ public sealed class MessageReactionRemoveEmojiEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the channel the removed reactions were in.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild the removed reactions were in.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Gets the message that had the removed reactions.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
+
+ /// <summary>
+ /// Gets the emoji of the reaction that was removed.
+ /// </summary>
+ public DiscordEmoji Emoji { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageReactionRemoveEmojiEventArgs"/> class.
+ /// </summary>
+ internal MessageReactionRemoveEmojiEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/Reaction/MessageReactionRemoveEventArgs.cs b/DisCatSharp/EventArgs/Message/Reaction/MessageReactionRemoveEventArgs.cs
index 697352558..f56effba3 100644
--- a/DisCatSharp/EventArgs/Message/Reaction/MessageReactionRemoveEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/Reaction/MessageReactionRemoveEventArgs.cs
@@ -1,68 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.MessageReactionRemoved"/> event.
-/// </summary>
-public class MessageReactionRemoveEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the message for which the update occurred.
+ /// Represents arguments for <see cref="DiscordClient.MessageReactionRemoved"/> event.
/// </summary>
- public DiscordMessage Message { get; internal set; }
+ public class MessageReactionRemoveEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the message for which the update occurred.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
- /// <summary>
- /// Gets the channel to which this message belongs.
- /// </summary>
- /// <remarks>
- /// This will be <c>null</c> for an uncached channel, which will usually happen for when this event triggers on
- /// DM channels in which no prior messages were received or sent.
- /// </remarks>
- public DiscordChannel Channel
- => this.Message.Channel;
+ /// <summary>
+ /// Gets the channel to which this message belongs.
+ /// </summary>
+ /// <remarks>
+ /// This will be <c>null</c> for an uncached channel, which will usually happen for when this event triggers on
+ /// DM channels in which no prior messages were received or sent.
+ /// </remarks>
+ public DiscordChannel Channel
+ => this.Message.Channel;
- /// <summary>
- /// Gets the users whose reaction was removed.
- /// </summary>
- public DiscordUser User { get; internal set; }
+ /// <summary>
+ /// Gets the users whose reaction was removed.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the guild in which the reaction was deleted.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the reaction was deleted.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the emoji used for this reaction.
- /// </summary>
- public DiscordEmoji Emoji { get; internal set; }
+ /// <summary>
+ /// Gets the emoji used for this reaction.
+ /// </summary>
+ public DiscordEmoji Emoji { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageReactionRemoveEventArgs"/> class.
- /// </summary>
- internal MessageReactionRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageReactionRemoveEventArgs"/> class.
+ /// </summary>
+ internal MessageReactionRemoveEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Message/Reaction/MessageReactionsClearEventArgs.cs b/DisCatSharp/EventArgs/Message/Reaction/MessageReactionsClearEventArgs.cs
index e7cf0bf35..75e76af3d 100644
--- a/DisCatSharp/EventArgs/Message/Reaction/MessageReactionsClearEventArgs.cs
+++ b/DisCatSharp/EventArgs/Message/Reaction/MessageReactionsClearEventArgs.cs
@@ -1,59 +1,60 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.MessageReactionsCleared"/> event.
-/// </summary>
-public class MessageReactionsClearEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the message for which the update occurred.
+ /// Represents arguments for <see cref="DiscordClient.MessageReactionsCleared"/> event.
/// </summary>
- public DiscordMessage Message { get; internal set; }
+ public class MessageReactionsClearEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the message for which the update occurred.
+ /// </summary>
+ public DiscordMessage Message { get; internal set; }
- /// <summary>
- /// Gets the channel to which this message belongs.
- /// </summary>
- /// <remarks>
- /// This will be <c>null</c> for an uncached channel, which will usually happen for when this event triggers on
- /// DM channels in which no prior messages were received or sent.
- /// </remarks>
- public DiscordChannel Channel
- => this.Message.Channel;
+ /// <summary>
+ /// Gets the channel to which this message belongs.
+ /// </summary>
+ /// <remarks>
+ /// This will be <c>null</c> for an uncached channel, which will usually happen for when this event triggers on
+ /// DM channels in which no prior messages were received or sent.
+ /// </remarks>
+ public DiscordChannel Channel
+ => this.Message.Channel;
- /// <summary>
- /// Gets the guild in which the reactions were cleared.
- /// </summary>
- public DiscordGuild Guild
- => this.Channel.Guild;
+ /// <summary>
+ /// Gets the guild in which the reactions were cleared.
+ /// </summary>
+ public DiscordGuild Guild
+ => this.Channel.Guild;
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageReactionsClearEventArgs"/> class.
- /// </summary>
- internal MessageReactionsClearEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageReactionsClearEventArgs"/> class.
+ /// </summary>
+ internal MessageReactionsClearEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/PayloadReceivedEventArgs.cs b/DisCatSharp/EventArgs/PayloadReceivedEventArgs.cs
index c60a90ac4..a521be52c 100644
--- a/DisCatSharp/EventArgs/PayloadReceivedEventArgs.cs
+++ b/DisCatSharp/EventArgs/PayloadReceivedEventArgs.cs
@@ -1,62 +1,63 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json.Linq;
-namespace DisCatSharp.EventArgs;
-
-/// <summary>
-/// Represents a gateway payload.
-/// </summary>
-public class PayloadReceivedEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// The JSON from this payload event.
+ /// Represents a gateway payload.
/// </summary>
- public string Json
+ public class PayloadReceivedEventArgs : DiscordEventArgs
{
- get
+ /// <summary>
+ /// The JSON from this payload event.
+ /// </summary>
+ public string Json
{
- if (string.IsNullOrWhiteSpace(this._json))
- this._json = this.PayloadObject.ToString();
- return this._json;
+ get
+ {
+ if (string.IsNullOrWhiteSpace(this._json))
+ this._json = this.PayloadObject.ToString();
+ return this._json;
+ }
}
- }
- private string _json;
- /// <summary>
- /// Gets or sets the payload object.
- /// </summary>
- internal JObject PayloadObject { get; set; }
+ private string _json;
+ /// <summary>
+ /// Gets or sets the payload object.
+ /// </summary>
+ internal JObject PayloadObject { get; set; }
- /// <summary>
- /// The name of this event.
- /// </summary>
- public string EventName { get; internal set; }
+ /// <summary>
+ /// The name of this event.
+ /// </summary>
+ public string EventName { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="PayloadReceivedEventArgs"/> class.
- /// </summary>
- internal PayloadReceivedEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PayloadReceivedEventArgs"/> class.
+ /// </summary>
+ internal PayloadReceivedEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/RateLimitExceptionEventArgs.cs b/DisCatSharp/EventArgs/RateLimitExceptionEventArgs.cs
index 3c0f32720..6d63efaa3 100644
--- a/DisCatSharp/EventArgs/RateLimitExceptionEventArgs.cs
+++ b/DisCatSharp/EventArgs/RateLimitExceptionEventArgs.cs
@@ -1,42 +1,43 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Exceptions;
-namespace DisCatSharp.EventArgs;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.RateLimitHit"/> event.
-/// </summary>
-public class RateLimitExceptionEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
- public RateLimitException Exception { get; internal set; }
-
- public string ApiEndpoint { get; internal set; }
-
/// <summary>
- /// Initializes a new instance of the <see cref="HeartbeatEventArgs"/> class.
+ /// Represents arguments for <see cref="DiscordClient.RateLimitHit"/> event.
/// </summary>
- internal RateLimitExceptionEventArgs(IServiceProvider provider) : base(provider) { }
+ public class RateLimitExceptionEventArgs : DiscordEventArgs
+ {
+ public RateLimitException Exception { get; internal set; }
+
+ public string ApiEndpoint { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HeartbeatEventArgs"/> class.
+ /// </summary>
+ internal RateLimitExceptionEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/ReadyEventArgs.cs b/DisCatSharp/EventArgs/ReadyEventArgs.cs
index 3f6127fda..36915abf1 100644
--- a/DisCatSharp/EventArgs/ReadyEventArgs.cs
+++ b/DisCatSharp/EventArgs/ReadyEventArgs.cs
@@ -1,36 +1,37 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.Ready"/> event.
-/// </summary>
-public sealed class ReadyEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Initializes a new instance of the <see cref="ReadyEventArgs"/> class.
+ /// Represents arguments for <see cref="DiscordClient.Ready"/> event.
/// </summary>
- internal ReadyEventArgs(IServiceProvider provider) : base(provider) { }
+ public sealed class ReadyEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ReadyEventArgs"/> class.
+ /// </summary>
+ internal ReadyEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Socket/SocketDisconnectEventArgs.cs b/DisCatSharp/EventArgs/Socket/SocketDisconnectEventArgs.cs
index 4ff6353b5..5a67788fe 100644
--- a/DisCatSharp/EventArgs/Socket/SocketDisconnectEventArgs.cs
+++ b/DisCatSharp/EventArgs/Socket/SocketDisconnectEventArgs.cs
@@ -1,62 +1,63 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.SocketClosed"/> event.
-/// </summary>
-public class SocketCloseEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the close code sent by remote host.
+ /// Represents arguments for <see cref="DiscordClient.SocketClosed"/> event.
/// </summary>
- public int CloseCode { get; internal set; }
+ public class SocketCloseEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the close code sent by remote host.
+ /// </summary>
+ public int CloseCode { get; internal set; }
- /// <summary>
- /// Gets the close message sent by remote host.
- /// </summary>
- public string CloseMessage { get; internal set; }
+ /// <summary>
+ /// Gets the close message sent by remote host.
+ /// </summary>
+ public string CloseMessage { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="SocketCloseEventArgs"/> class.
- /// </summary>
- public SocketCloseEventArgs(IServiceProvider provider) : base(provider) { }
-}
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SocketCloseEventArgs"/> class.
+ /// </summary>
+ public SocketCloseEventArgs(IServiceProvider provider) : base(provider) { }
+ }
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.SocketErrored"/> event.
-/// </summary>
-public class SocketErrorEventArgs : DiscordEventArgs
-{
/// <summary>
- /// Gets the exception thrown by websocket client.
+ /// Represents arguments for <see cref="DiscordClient.SocketErrored"/> event.
/// </summary>
- public Exception Exception { get; internal set; }
+ public class SocketErrorEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the exception thrown by websocket client.
+ /// </summary>
+ public Exception Exception { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="SocketErrorEventArgs"/> class.
- /// </summary>
- public SocketErrorEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SocketErrorEventArgs"/> class.
+ /// </summary>
+ public SocketErrorEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Socket/SocketEventArgs.cs b/DisCatSharp/EventArgs/Socket/SocketEventArgs.cs
index e618f4e4d..649dda384 100644
--- a/DisCatSharp/EventArgs/Socket/SocketEventArgs.cs
+++ b/DisCatSharp/EventArgs/Socket/SocketEventArgs.cs
@@ -1,36 +1,37 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-
-/// <summary>
-/// Represents basic socket event arguments.
-/// </summary>
-public class SocketEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Creates a new event argument container.
+ /// Represents basic socket event arguments.
/// </summary>
- public SocketEventArgs(IServiceProvider provider) : base(provider) { }
+ public class SocketEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Creates a new event argument container.
+ /// </summary>
+ public SocketEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Socket/WebSocketMessageEventArgs.cs b/DisCatSharp/EventArgs/Socket/WebSocketMessageEventArgs.cs
index db5eaa561..db78afe1b 100644
--- a/DisCatSharp/EventArgs/Socket/WebSocketMessageEventArgs.cs
+++ b/DisCatSharp/EventArgs/Socket/WebSocketMessageEventArgs.cs
@@ -1,71 +1,72 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Common.Utilities;
-namespace DisCatSharp.EventArgs;
-
-/// <summary>
-/// Represents base class for raw socket message event arguments.
-/// </summary>
-public abstract class SocketMessageEventArgs : AsyncEventArgs
-{ }
-
-/// <summary>
-/// Represents arguments for text message websocket event.
-/// </summary>
-public sealed class SocketTextMessageEventArgs : SocketMessageEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the received message string.
+ /// Represents base class for raw socket message event arguments.
/// </summary>
- public string Message { get; }
+ public abstract class SocketMessageEventArgs : AsyncEventArgs
+ { }
/// <summary>
- /// Creates a new instance of text message event arguments.
+ /// Represents arguments for text message websocket event.
/// </summary>
- /// <param name="message">Received message string.</param>
- public SocketTextMessageEventArgs(string message)
+ public sealed class SocketTextMessageEventArgs : SocketMessageEventArgs
{
- this.Message = message;
- }
-}
+ /// <summary>
+ /// Gets the received message string.
+ /// </summary>
+ public string Message { get; }
-/// <summary>
-/// Represents arguments for binary message websocket event.
-/// </summary>
-public sealed class SocketBinaryMessageEventArgs : SocketMessageEventArgs
-{
- /// <summary>
- /// Gets the received message bytes.
- /// </summary>
- public byte[] Message { get; }
+ /// <summary>
+ /// Creates a new instance of text message event arguments.
+ /// </summary>
+ /// <param name="message">Received message string.</param>
+ public SocketTextMessageEventArgs(string message)
+ {
+ this.Message = message;
+ }
+ }
/// <summary>
- /// Creates a new instance of binary message event arguments.
+ /// Represents arguments for binary message websocket event.
/// </summary>
- /// <param name="message">Received message bytes.</param>
- public SocketBinaryMessageEventArgs(byte[] message)
+ public sealed class SocketBinaryMessageEventArgs : SocketMessageEventArgs
{
- this.Message = message;
+ /// <summary>
+ /// Gets the received message bytes.
+ /// </summary>
+ public byte[] Message { get; }
+
+ /// <summary>
+ /// Creates a new instance of binary message event arguments.
+ /// </summary>
+ /// <param name="message">Received message bytes.</param>
+ public SocketBinaryMessageEventArgs(byte[] message)
+ {
+ this.Message = message;
+ }
}
}
diff --git a/DisCatSharp/EventArgs/Stage/StageInstanceCreateEventArgs.cs b/DisCatSharp/EventArgs/Stage/StageInstanceCreateEventArgs.cs
index 250657f36..c42d50343 100644
--- a/DisCatSharp/EventArgs/Stage/StageInstanceCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Stage/StageInstanceCreateEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.StageInstanceCreated"/> event.
-/// </summary>
-public class StageInstanceCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the stage instance that was created.
+ /// Represents arguments for <see cref="DiscordClient.StageInstanceCreated"/> event.
/// </summary>
- public DiscordStageInstance StageInstance { get; internal set; }
+ public class StageInstanceCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the stage instance that was created.
+ /// </summary>
+ public DiscordStageInstance StageInstance { get; internal set; }
- /// <summary>
- /// Gets the guild in which the stage instance was created.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the stage instance was created.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="StageInstanceCreateEventArgs"/> class.
- /// </summary>
- internal StageInstanceCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StageInstanceCreateEventArgs"/> class.
+ /// </summary>
+ internal StageInstanceCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Stage/StageInstanceDeleteEventArgs.cs b/DisCatSharp/EventArgs/Stage/StageInstanceDeleteEventArgs.cs
index 0a43d4d5e..05971a9ea 100644
--- a/DisCatSharp/EventArgs/Stage/StageInstanceDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Stage/StageInstanceDeleteEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.StageInstanceDeleted"/> event.
-/// </summary>
-public class StageInstanceDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the stage instance that was deleted.
+ /// Represents arguments for <see cref="DiscordClient.StageInstanceDeleted"/> event.
/// </summary>
- public DiscordStageInstance StageInstance { get; internal set; }
+ public class StageInstanceDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the stage instance that was deleted.
+ /// </summary>
+ public DiscordStageInstance StageInstance { get; internal set; }
- /// <summary>
- /// Gets the guild in which the stage instance was deleted.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the stage instance was deleted.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="StageInstanceDeleteEventArgs"/> class.
- /// </summary>
- internal StageInstanceDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StageInstanceDeleteEventArgs"/> class.
+ /// </summary>
+ internal StageInstanceDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Stage/StageInstanceUpdateEventArgs.cs b/DisCatSharp/EventArgs/Stage/StageInstanceUpdateEventArgs.cs
index 4cad1b26d..6fc31b812 100644
--- a/DisCatSharp/EventArgs/Stage/StageInstanceUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Stage/StageInstanceUpdateEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.StageInstanceUpdated"/> event.
-/// </summary>
-public class StageInstanceUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the stage instance that was updated.
+ /// Represents arguments for <see cref="DiscordClient.StageInstanceUpdated"/> event.
/// </summary>
- public DiscordStageInstance StageInstance { get; internal set; }
+ public class StageInstanceUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the stage instance that was updated.
+ /// </summary>
+ public DiscordStageInstance StageInstance { get; internal set; }
- /// <summary>
- /// Gets the guild in which the stage instance was updated.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the stage instance was updated.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="StageInstanceUpdateEventArgs"/> class.
- /// </summary>
- internal StageInstanceUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StageInstanceUpdateEventArgs"/> class.
+ /// </summary>
+ internal StageInstanceUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Thread/ThreadCreateEventArgs.cs b/DisCatSharp/EventArgs/Thread/ThreadCreateEventArgs.cs
index 0d5e36117..750828f64 100644
--- a/DisCatSharp/EventArgs/Thread/ThreadCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Thread/ThreadCreateEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ThreadCreated"/> event.
-/// </summary>
-public class ThreadCreateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the thread that was created.
+ /// Represents arguments for <see cref="DiscordClient.ThreadCreated"/> event.
/// </summary>
- public DiscordThreadChannel Thread { get; internal set; }
+ public class ThreadCreateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the thread that was created.
+ /// </summary>
+ public DiscordThreadChannel Thread { get; internal set; }
- /// <summary>
- /// Gets the threads parent channel.
- /// </summary>
- public DiscordChannel Parent { get; internal set; }
+ /// <summary>
+ /// Gets the threads parent channel.
+ /// </summary>
+ public DiscordChannel Parent { get; internal set; }
- /// <summary>
- /// Gets the guild in which the thread was created.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the thread was created.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ThreadCreateEventArgs"/> class.
- /// </summary>
- internal ThreadCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ThreadCreateEventArgs"/> class.
+ /// </summary>
+ internal ThreadCreateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Thread/ThreadDeleteEventArgs.cs b/DisCatSharp/EventArgs/Thread/ThreadDeleteEventArgs.cs
index 8d2a39c6b..2b590b7de 100644
--- a/DisCatSharp/EventArgs/Thread/ThreadDeleteEventArgs.cs
+++ b/DisCatSharp/EventArgs/Thread/ThreadDeleteEventArgs.cs
@@ -1,58 +1,59 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ThreadDeleted"/> event.
-/// </summary>
-public class ThreadDeleteEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the thread that was deleted.
- /// </summary>
- public DiscordThreadChannel Thread { get; internal set; }
-
- /// <summary>
- /// Gets the threads parent channel.
- /// </summary>
- public DiscordChannel Parent { get; internal set; }
-
- /// <summary>
- /// Gets the guild this thread belonged to.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Gets the threads type.
- /// </summary>
- public ChannelType Type { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ThreadDeleteEventArgs"/> class.
+ /// Represents arguments for <see cref="DiscordClient.ThreadDeleted"/> event.
/// </summary>
- internal ThreadDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ public class ThreadDeleteEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the thread that was deleted.
+ /// </summary>
+ public DiscordThreadChannel Thread { get; internal set; }
+
+ /// <summary>
+ /// Gets the threads parent channel.
+ /// </summary>
+ public DiscordChannel Parent { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild this thread belonged to.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Gets the threads type.
+ /// </summary>
+ public ChannelType Type { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ThreadDeleteEventArgs"/> class.
+ /// </summary>
+ internal ThreadDeleteEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Thread/ThreadListSyncEventArgs.cs b/DisCatSharp/EventArgs/Thread/ThreadListSyncEventArgs.cs
index 24865cd0a..c0d3e1f5a 100644
--- a/DisCatSharp/EventArgs/Thread/ThreadListSyncEventArgs.cs
+++ b/DisCatSharp/EventArgs/Thread/ThreadListSyncEventArgs.cs
@@ -1,59 +1,60 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ThreadListSynced"/> event.
-/// </summary>
-public class ThreadListSyncEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets all thread member objects from the synced threads for the current user, indicating which threads the current user has been added to
- /// </summary>
- public IReadOnlyList<DiscordThreadChannelMember> Members { get; internal set; }
-
- /// <summary>
- /// Gets all active threads in the given channels that the current user can access.
- /// </summary>
- public IReadOnlyList<DiscordThreadChannel> Threads { get; internal set; }
-
- /// <summary>
- /// Gets the parent channels whose threads are being synced. If empty, then threads are synced for the guild. May contain channels that have no active threads as well.
- /// </summary>
- public IReadOnlyList<DiscordChannel> Channels { get; internal set; }
-
- /// <summary>
- /// Gets the guild being synced.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ThreadListSyncEventArgs"/> class.
+ /// Represents arguments for <see cref="DiscordClient.ThreadListSynced"/> event.
/// </summary>
- internal ThreadListSyncEventArgs(IServiceProvider provider) : base(provider) { }
+ public class ThreadListSyncEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets all thread member objects from the synced threads for the current user, indicating which threads the current user has been added to
+ /// </summary>
+ public IReadOnlyList<DiscordThreadChannelMember> Members { get; internal set; }
+
+ /// <summary>
+ /// Gets all active threads in the given channels that the current user can access.
+ /// </summary>
+ public IReadOnlyList<DiscordThreadChannel> Threads { get; internal set; }
+
+ /// <summary>
+ /// Gets the parent channels whose threads are being synced. If empty, then threads are synced for the guild. May contain channels that have no active threads as well.
+ /// </summary>
+ public IReadOnlyList<DiscordChannel> Channels { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild being synced.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ThreadListSyncEventArgs"/> class.
+ /// </summary>
+ internal ThreadListSyncEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Thread/ThreadMemberUpdateEventArgs.cs b/DisCatSharp/EventArgs/Thread/ThreadMemberUpdateEventArgs.cs
index 996f90444..4b3209387 100644
--- a/DisCatSharp/EventArgs/Thread/ThreadMemberUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Thread/ThreadMemberUpdateEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ThreadMemberUpdated"/> event.
-/// </summary>
-public class ThreadMemberUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the thread member that was updated.
+ /// Represents arguments for <see cref="DiscordClient.ThreadMemberUpdated"/> event.
/// </summary>
- public DiscordThreadChannelMember ThreadMember { get; internal set; }
+ public class ThreadMemberUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the thread member that was updated.
+ /// </summary>
+ public DiscordThreadChannelMember ThreadMember { get; internal set; }
- /// <summary>
- /// Gets the thread.
- /// </summary>
- public DiscordThreadChannel Thread { get; internal set; }
+ /// <summary>
+ /// Gets the thread.
+ /// </summary>
+ public DiscordThreadChannel Thread { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ThreadMemberUpdateEventArgs"/> class.
- /// </summary>
- internal ThreadMemberUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ThreadMemberUpdateEventArgs"/> class.
+ /// </summary>
+ internal ThreadMemberUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Thread/ThreadMembersUpdateEventArgs.cs b/DisCatSharp/EventArgs/Thread/ThreadMembersUpdateEventArgs.cs
index 759f70bdf..181b484e0 100644
--- a/DisCatSharp/EventArgs/Thread/ThreadMembersUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Thread/ThreadMembersUpdateEventArgs.cs
@@ -1,70 +1,71 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ThreadMembersUpdated"/> event.
-/// </summary>
-public class ThreadMembersUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the approximate number of members in the thread, capped at 50.
+ /// Represents arguments for <see cref="DiscordClient.ThreadMembersUpdated"/> event.
/// </summary>
- public int MemberCount { get; internal set; }
+ public class ThreadMembersUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the approximate number of members in the thread, capped at 50.
+ /// </summary>
+ public int MemberCount { get; internal set; }
- /// <summary>
- /// Gets the users who were removed from the thread.
- /// </summary>
- public IReadOnlyList<DiscordMember> RemovedMembers { get; internal set; }
+ /// <summary>
+ /// Gets the users who were removed from the thread.
+ /// </summary>
+ public IReadOnlyList<DiscordMember> RemovedMembers { get; internal set; }
- /// <summary>
- /// Gets the users who were added to the thread.
- /// </summary>
- public IReadOnlyList<DiscordThreadChannelMember> AddedMembers { get; internal set; }
+ /// <summary>
+ /// Gets the users who were added to the thread.
+ /// </summary>
+ public IReadOnlyList<DiscordThreadChannelMember> AddedMembers { get; internal set; }
- /// <summary>
- /// Gets the id of the thread.
- /// </summary>
- public ulong ThreadId { get; internal set; }
+ /// <summary>
+ /// Gets the id of the thread.
+ /// </summary>
+ public ulong ThreadId { get; internal set; }
- /// <summary>
- /// Gets the id of the thread.
- /// </summary>
- public DiscordThreadChannel Thread { get; internal set; }
+ /// <summary>
+ /// Gets the id of the thread.
+ /// </summary>
+ public DiscordThreadChannel Thread { get; internal set; }
- /// <summary>
- /// Gets the guild.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ThreadMembersUpdateEventArgs"/> class.
- /// </summary>
- internal ThreadMembersUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ThreadMembersUpdateEventArgs"/> class.
+ /// </summary>
+ internal ThreadMembersUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Thread/ThreadUpdateEventArgs.cs b/DisCatSharp/EventArgs/Thread/ThreadUpdateEventArgs.cs
index 1e1c5f7b3..37b4da5b2 100644
--- a/DisCatSharp/EventArgs/Thread/ThreadUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Thread/ThreadUpdateEventArgs.cs
@@ -1,58 +1,59 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.ThreadUpdated"/> event.
-/// </summary>
-public class ThreadUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the post-update thread.
- /// </summary>
- public DiscordThreadChannel ThreadAfter { get; internal set; }
-
- /// <summary>
- /// Gets the pre-update thread.
- /// </summary>
- public DiscordThreadChannel ThreadBefore { get; internal set; }
-
- /// <summary>
- /// Gets the threads parent channel.
- /// </summary>
- public DiscordChannel Parent { get; internal set; }
-
- /// <summary>
- /// Gets the guild in which the thread was updated.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ThreadUpdateEventArgs"/> class.
+ /// Represents arguments for <see cref="DiscordClient.ThreadUpdated"/> event.
/// </summary>
- internal ThreadUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ public class ThreadUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the post-update thread.
+ /// </summary>
+ public DiscordThreadChannel ThreadAfter { get; internal set; }
+
+ /// <summary>
+ /// Gets the pre-update thread.
+ /// </summary>
+ public DiscordThreadChannel ThreadBefore { get; internal set; }
+
+ /// <summary>
+ /// Gets the threads parent channel.
+ /// </summary>
+ public DiscordChannel Parent { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild in which the thread was updated.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ThreadUpdateEventArgs"/> class.
+ /// </summary>
+ internal ThreadUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/TypingStartEventArgs.cs b/DisCatSharp/EventArgs/TypingStartEventArgs.cs
index bfae02e72..2de47c71a 100644
--- a/DisCatSharp/EventArgs/TypingStartEventArgs.cs
+++ b/DisCatSharp/EventArgs/TypingStartEventArgs.cs
@@ -1,59 +1,60 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.TypingStarted"/> event.
-/// </summary>
-public class TypingStartEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the channel in which the indicator was triggered.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the user that started typing.
- /// <para>This can be cast to a <see cref="DisCatSharp.Entities.DiscordMember"/> if the typing occurred in a guild.</para>
- /// </summary>
- public DiscordUser User { get; internal set; }
-
- /// <summary>
- /// Gets the guild in which the indicator was triggered.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
-
- /// <summary>
- /// Gets the date and time at which the user started typing.
- /// </summary>
- public DateTimeOffset StartedAt { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TypingStartEventArgs"/> class.
+ /// Represents arguments for <see cref="DiscordClient.TypingStarted"/> event.
/// </summary>
- internal TypingStartEventArgs(IServiceProvider provider) : base(provider) { }
+ public class TypingStartEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the channel in which the indicator was triggered.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the user that started typing.
+ /// <para>This can be cast to a <see cref="DisCatSharp.Entities.DiscordMember"/> if the typing occurred in a guild.</para>
+ /// </summary>
+ public DiscordUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild in which the indicator was triggered.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
+
+ /// <summary>
+ /// Gets the date and time at which the user started typing.
+ /// </summary>
+ public DateTimeOffset StartedAt { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TypingStartEventArgs"/> class.
+ /// </summary>
+ internal TypingStartEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/UnknownEventArgs.cs b/DisCatSharp/EventArgs/UnknownEventArgs.cs
index 6d34a5207..98e6ca189 100644
--- a/DisCatSharp/EventArgs/UnknownEventArgs.cs
+++ b/DisCatSharp/EventArgs/UnknownEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.UnknownEvent"/> event.
-/// </summary>
-public class UnknownEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the event's name.
+ /// Represents arguments for <see cref="DiscordClient.UnknownEvent"/> event.
/// </summary>
- public string EventName { get; internal set; }
+ public class UnknownEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the event's name.
+ /// </summary>
+ public string EventName { get; internal set; }
- /// <summary>
- /// Gets the event's data.
- /// </summary>
- public string Json { get; internal set; }
+ /// <summary>
+ /// Gets the event's data.
+ /// </summary>
+ public string Json { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="UnknownEventArgs"/> class.
- /// </summary>
- internal UnknownEventArgs(IServiceProvider provider)
- : base(provider)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnknownEventArgs"/> class.
+ /// </summary>
+ internal UnknownEventArgs(IServiceProvider provider)
+ : base(provider)
+ { }
+ }
}
diff --git a/DisCatSharp/EventArgs/User/PresenceUpdateEventArgs.cs b/DisCatSharp/EventArgs/User/PresenceUpdateEventArgs.cs
index 475763a90..f8a744d07 100644
--- a/DisCatSharp/EventArgs/User/PresenceUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/User/PresenceUpdateEventArgs.cs
@@ -1,73 +1,74 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.PresenceUpdated"/> event.
-/// </summary>
-public class PresenceUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the user whose presence was updated.
+ /// Represents arguments for <see cref="DiscordClient.PresenceUpdated"/> event.
/// </summary>
- public DiscordUser User { get; internal set; }
+ public class PresenceUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the user whose presence was updated.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the user's new game.
- /// </summary>
- public DiscordActivity Activity { get; internal set; }
+ /// <summary>
+ /// Gets the user's new game.
+ /// </summary>
+ public DiscordActivity Activity { get; internal set; }
- /// <summary>
- /// Gets the user's status.
- /// </summary>
- public UserStatus Status { get; internal set; }
+ /// <summary>
+ /// Gets the user's status.
+ /// </summary>
+ public UserStatus Status { get; internal set; }
- /// <summary>
- /// Gets the user's old presence.
- /// </summary>
- public DiscordPresence PresenceBefore { get; internal set; }
+ /// <summary>
+ /// Gets the user's old presence.
+ /// </summary>
+ public DiscordPresence PresenceBefore { get; internal set; }
- /// <summary>
- /// Gets the user's new presence.
- /// </summary>
- public DiscordPresence PresenceAfter { get; internal set; }
+ /// <summary>
+ /// Gets the user's new presence.
+ /// </summary>
+ public DiscordPresence PresenceAfter { get; internal set; }
- /// <summary>
- /// Gets the user prior to presence update.
- /// </summary>
- public DiscordUser UserBefore { get; internal set; }
+ /// <summary>
+ /// Gets the user prior to presence update.
+ /// </summary>
+ public DiscordUser UserBefore { get; internal set; }
- /// <summary>
- /// Gets the user after the presence update.
- /// </summary>
- public DiscordUser UserAfter { get; internal set; }
+ /// <summary>
+ /// Gets the user after the presence update.
+ /// </summary>
+ public DiscordUser UserAfter { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="PresenceUpdateEventArgs"/> class.
- /// </summary>
- internal PresenceUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PresenceUpdateEventArgs"/> class.
+ /// </summary>
+ internal PresenceUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/User/UserSettingsUpdateEventArgs.cs b/DisCatSharp/EventArgs/User/UserSettingsUpdateEventArgs.cs
index a0c9cce57..f4f4f7608 100644
--- a/DisCatSharp/EventArgs/User/UserSettingsUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/User/UserSettingsUpdateEventArgs.cs
@@ -1,43 +1,44 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.UserSettingsUpdated"/> event.
-/// </summary>
-public class UserSettingsUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the user whose settings were updated.
+ /// Represents arguments for <see cref="DiscordClient.UserSettingsUpdated"/> event.
/// </summary>
- public DiscordUser User { get; internal set; }
+ public class UserSettingsUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the user whose settings were updated.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="UserSettingsUpdateEventArgs"/> class.
- /// </summary>
- internal UserSettingsUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserSettingsUpdateEventArgs"/> class.
+ /// </summary>
+ internal UserSettingsUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/User/UserSpeakingEventArgs.cs b/DisCatSharp/EventArgs/User/UserSpeakingEventArgs.cs
index 268b3b087..59e1a9f91 100644
--- a/DisCatSharp/EventArgs/User/UserSpeakingEventArgs.cs
+++ b/DisCatSharp/EventArgs/User/UserSpeakingEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for UserSpeaking event.
-/// </summary>
-public class UserSpeakingEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the users whose speaking state changed.
+ /// Represents arguments for UserSpeaking event.
/// </summary>
- public DiscordUser User { get; internal set; }
+ public class UserSpeakingEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the users whose speaking state changed.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the SSRC of the audio source.
- /// </summary>
- public uint Ssrc { get; internal set; }
+ /// <summary>
+ /// Gets the SSRC of the audio source.
+ /// </summary>
+ public uint Ssrc { get; internal set; }
- /// <summary>
- /// Gets whether this user is speaking.
- /// </summary>
- public bool Speaking { get; internal set; }
+ /// <summary>
+ /// Gets whether this user is speaking.
+ /// </summary>
+ public bool Speaking { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="UserSpeakingEventArgs"/> class.
- /// </summary>
- internal UserSpeakingEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserSpeakingEventArgs"/> class.
+ /// </summary>
+ internal UserSpeakingEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/User/UserUpdateEventArgs.cs b/DisCatSharp/EventArgs/User/UserUpdateEventArgs.cs
index edacca312..8c73afca4 100644
--- a/DisCatSharp/EventArgs/User/UserUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/User/UserUpdateEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.UserUpdated"/> event.
-/// </summary>
-public class UserUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the post-update user.
+ /// Represents arguments for <see cref="DiscordClient.UserUpdated"/> event.
/// </summary>
- public DiscordUser UserAfter { get; internal set; }
+ public class UserUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the post-update user.
+ /// </summary>
+ public DiscordUser UserAfter { get; internal set; }
- /// <summary>
- /// Gets the pre-update user.
- /// </summary>
- public DiscordUser UserBefore { get; internal set; }
+ /// <summary>
+ /// Gets the pre-update user.
+ /// </summary>
+ public DiscordUser UserBefore { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="UserUpdateEventArgs"/> class.
- /// </summary>
- internal UserUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserUpdateEventArgs"/> class.
+ /// </summary>
+ internal UserUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Voice/VoiceServerUpdateEventArgs.cs b/DisCatSharp/EventArgs/Voice/VoiceServerUpdateEventArgs.cs
index 20811c42a..4be986017 100644
--- a/DisCatSharp/EventArgs/Voice/VoiceServerUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Voice/VoiceServerUpdateEventArgs.cs
@@ -1,53 +1,54 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.VoiceServerUpdated"/> event.
-/// </summary>
-public class VoiceServerUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild for which the update occurred.
+ /// Represents arguments for <see cref="DiscordClient.VoiceServerUpdated"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class VoiceServerUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild for which the update occurred.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the new voice endpoint.
- /// </summary>
- public string Endpoint { get; internal set; }
+ /// <summary>
+ /// Gets the new voice endpoint.
+ /// </summary>
+ public string Endpoint { get; internal set; }
- /// <summary>
- /// Gets the voice connection token.
- /// </summary>
- internal string VoiceToken { get; set; }
+ /// <summary>
+ /// Gets the voice connection token.
+ /// </summary>
+ internal string VoiceToken { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="VoiceServerUpdateEventArgs"/> class.
- /// </summary>
- internal VoiceServerUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VoiceServerUpdateEventArgs"/> class.
+ /// </summary>
+ internal VoiceServerUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/Voice/VoiceStateUpdateEventArgs.cs b/DisCatSharp/EventArgs/Voice/VoiceStateUpdateEventArgs.cs
index 9b924355e..717be7451 100644
--- a/DisCatSharp/EventArgs/Voice/VoiceStateUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Voice/VoiceStateUpdateEventArgs.cs
@@ -1,68 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.VoiceStateUpdated"/> event.
-/// </summary>
-public class VoiceStateUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the user whose voice state was updated.
+ /// Represents arguments for <see cref="DiscordClient.VoiceStateUpdated"/> event.
/// </summary>
- public DiscordUser User { get; internal set; }
+ public class VoiceStateUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the user whose voice state was updated.
+ /// </summary>
+ public DiscordUser User { get; internal set; }
- /// <summary>
- /// Gets the guild in which the update occurred.
- /// </summary>
- public DiscordGuild Guild { get; internal set; }
+ /// <summary>
+ /// Gets the guild in which the update occurred.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the related voice channel.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
+ /// <summary>
+ /// Gets the related voice channel.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Gets the voice state pre-update.
- /// </summary>
- public DiscordVoiceState Before { get; internal set; }
+ /// <summary>
+ /// Gets the voice state pre-update.
+ /// </summary>
+ public DiscordVoiceState Before { get; internal set; }
- /// <summary>
- /// Gets the voice state post-update.
- /// </summary>
- public DiscordVoiceState After { get; internal set; }
+ /// <summary>
+ /// Gets the voice state post-update.
+ /// </summary>
+ public DiscordVoiceState After { get; internal set; }
- /// <summary>
- /// Gets the ID of voice session.
- /// </summary>
- internal string SessionId { get; set; }
+ /// <summary>
+ /// Gets the ID of voice session.
+ /// </summary>
+ internal string SessionId { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="VoiceStateUpdateEventArgs"/> class.
- /// </summary>
- internal VoiceStateUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="VoiceStateUpdateEventArgs"/> class.
+ /// </summary>
+ internal VoiceStateUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/WebhooksUpdateEventArgs.cs b/DisCatSharp/EventArgs/WebhooksUpdateEventArgs.cs
index e679c78c4..e5e9c1815 100644
--- a/DisCatSharp/EventArgs/WebhooksUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/WebhooksUpdateEventArgs.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-
-/// <summary>
-/// Represents arguments to <see cref="DiscordClient.WebhooksUpdated"/> event.
-/// </summary>
-public class WebhooksUpdateEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets the guild that had its webhooks updated.
+ /// Represents arguments to <see cref="DiscordClient.WebhooksUpdated"/> event.
/// </summary>
- public DiscordGuild Guild { get; internal set; }
+ public class WebhooksUpdateEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets the guild that had its webhooks updated.
+ /// </summary>
+ public DiscordGuild Guild { get; internal set; }
- /// <summary>
- /// Gets the channel to which the webhook belongs to.
- /// </summary>
- public DiscordChannel Channel { get; internal set; }
+ /// <summary>
+ /// Gets the channel to which the webhook belongs to.
+ /// </summary>
+ public DiscordChannel Channel { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="WebhooksUpdateEventArgs"/> class.
- /// </summary>
- internal WebhooksUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebhooksUpdateEventArgs"/> class.
+ /// </summary>
+ internal WebhooksUpdateEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/EventArgs/ZombiedEventArgs.cs b/DisCatSharp/EventArgs/ZombiedEventArgs.cs
index 9433652df..98210708a 100644
--- a/DisCatSharp/EventArgs/ZombiedEventArgs.cs
+++ b/DisCatSharp/EventArgs/ZombiedEventArgs.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.EventArgs;
-
-/// <summary>
-/// Represents arguments for <see cref="DiscordClient.Zombied"/> event.
-/// </summary>
-public class ZombiedEventArgs : DiscordEventArgs
+namespace DisCatSharp.EventArgs
{
/// <summary>
- /// Gets how many heartbeat failures have occurred.
+ /// Represents arguments for <see cref="DiscordClient.Zombied"/> event.
/// </summary>
- public int Failures { get; internal set; }
+ public class ZombiedEventArgs : DiscordEventArgs
+ {
+ /// <summary>
+ /// Gets how many heartbeat failures have occurred.
+ /// </summary>
+ public int Failures { get; internal set; }
- /// <summary>
- /// Gets whether the zombie event occurred whilst guilds are downloading.
- /// </summary>
- public bool GuildDownloadCompleted { get; internal set; }
+ /// <summary>
+ /// Gets whether the zombie event occurred whilst guilds are downloading.
+ /// </summary>
+ public bool GuildDownloadCompleted { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ZombiedEventArgs"/> class.
- /// </summary>
- internal ZombiedEventArgs(IServiceProvider provider) : base(provider) { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ZombiedEventArgs"/> class.
+ /// </summary>
+ internal ZombiedEventArgs(IServiceProvider provider) : base(provider) { }
+ }
}
diff --git a/DisCatSharp/Exceptions/BadRequestException.cs b/DisCatSharp/Exceptions/BadRequestException.cs
index 490842894..7b5c00b17 100644
--- a/DisCatSharp/Exceptions/BadRequestException.cs
+++ b/DisCatSharp/Exceptions/BadRequestException.cs
@@ -1,86 +1,87 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Exceptions;
-
-/// <summary>
-/// Represents an exception thrown when a malformed request is sent.
-/// </summary>
-public class BadRequestException : Exception
+namespace DisCatSharp.Exceptions
{
/// <summary>
- /// Gets the request that caused the exception.
+ /// Represents an exception thrown when a malformed request is sent.
/// </summary>
- public BaseRestRequest WebRequest { get; internal set; }
+ public class BadRequestException : Exception
+ {
+ /// <summary>
+ /// Gets the request that caused the exception.
+ /// </summary>
+ public BaseRestRequest WebRequest { get; internal set; }
- /// <summary>
- /// Gets the response to the request.
- /// </summary>
- public RestResponse WebResponse { get; internal set; }
+ /// <summary>
+ /// Gets the response to the request.
+ /// </summary>
+ public RestResponse WebResponse { get; internal set; }
- /// <summary>
- /// Gets the error code for this exception.
- /// </summary>
- public int Code { get; internal set; }
+ /// <summary>
+ /// Gets the error code for this exception.
+ /// </summary>
+ public int Code { get; internal set; }
- /// <summary>
- /// Gets the JSON message received.
- /// </summary>
- public string JsonMessage { get; internal set; }
+ /// <summary>
+ /// Gets the JSON message received.
+ /// </summary>
+ public string JsonMessage { get; internal set; }
- /// <summary>
- /// Gets the form error responses in JSON format.
- /// </summary>
- public string Errors { get; internal set; }
+ /// <summary>
+ /// Gets the form error responses in JSON format.
+ /// </summary>
+ public string Errors { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DisCatSharp.Exceptions.BadRequestException"/> class.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="response">The response.</param>
- internal BadRequestException(BaseRestRequest request, RestResponse response) : base("Bad request: " + response.ResponseCode)
- {
- this.WebRequest = request;
- this.WebResponse = response;
-
- try
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DisCatSharp.Exceptions.BadRequestException"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="response">The response.</param>
+ internal BadRequestException(BaseRestRequest request, RestResponse response) : base("Bad request: " + response.ResponseCode)
{
- var j = JObject.Parse(response.Response);
+ this.WebRequest = request;
+ this.WebResponse = response;
+
+ try
+ {
+ var j = JObject.Parse(response.Response);
- if (j["code"] != null)
- this.Code = (int)j["code"];
+ if (j["code"] != null)
+ this.Code = (int)j["code"];
- if (j["message"] != null)
- this.JsonMessage = j["message"].ToString();
+ if (j["message"] != null)
+ this.JsonMessage = j["message"].ToString();
- if (j["errors"] != null)
- this.Errors = j["errors"].ToString();
+ if (j["errors"] != null)
+ this.Errors = j["errors"].ToString();
+ }
+ catch { }
}
- catch { }
}
}
diff --git a/DisCatSharp/Exceptions/NotFoundException.cs b/DisCatSharp/Exceptions/NotFoundException.cs
index 76bb50d24..473983698 100644
--- a/DisCatSharp/Exceptions/NotFoundException.cs
+++ b/DisCatSharp/Exceptions/NotFoundException.cs
@@ -1,70 +1,71 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Exceptions;
-
-/// <summary>
-/// Represents an exception thrown when a requested resource is not found.
-/// </summary>
-public class NotFoundException : Exception
+namespace DisCatSharp.Exceptions
{
/// <summary>
- /// Gets the request that caused the exception.
+ /// Represents an exception thrown when a requested resource is not found.
/// </summary>
- public BaseRestRequest WebRequest { get; internal set; }
+ public class NotFoundException : Exception
+ {
+ /// <summary>
+ /// Gets the request that caused the exception.
+ /// </summary>
+ public BaseRestRequest WebRequest { get; internal set; }
- /// <summary>
- /// Gets the response to the request.
- /// </summary>
- public RestResponse WebResponse { get; internal set; }
+ /// <summary>
+ /// Gets the response to the request.
+ /// </summary>
+ public RestResponse WebResponse { get; internal set; }
- /// <summary>
- /// Gets the JSON received.
- /// </summary>
- public string JsonMessage { get; internal set; }
+ /// <summary>
+ /// Gets the JSON received.
+ /// </summary>
+ public string JsonMessage { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DisCatSharp.Exceptions.NotFoundException"/> class.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="response">The response.</param>
- internal NotFoundException(BaseRestRequest request, RestResponse response) : base("Not found: " + response.ResponseCode)
- {
- this.WebRequest = request;
- this.WebResponse = response;
-
- try
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DisCatSharp.Exceptions.NotFoundException"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="response">The response.</param>
+ internal NotFoundException(BaseRestRequest request, RestResponse response) : base("Not found: " + response.ResponseCode)
{
- var j = JObject.Parse(response.Response);
+ this.WebRequest = request;
+ this.WebResponse = response;
+
+ try
+ {
+ var j = JObject.Parse(response.Response);
- if (j["message"] != null)
- this.JsonMessage = j["message"].ToString();
+ if (j["message"] != null)
+ this.JsonMessage = j["message"].ToString();
+ }
+ catch (Exception) { }
}
- catch (Exception) { }
}
}
diff --git a/DisCatSharp/Exceptions/RateLimitException.cs b/DisCatSharp/Exceptions/RateLimitException.cs
index 568d8c907..825da63fb 100644
--- a/DisCatSharp/Exceptions/RateLimitException.cs
+++ b/DisCatSharp/Exceptions/RateLimitException.cs
@@ -1,70 +1,71 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Exceptions;
-
-/// <summary>
-/// Represents an exception thrown when too many requests are sent.
-/// </summary>
-public class RateLimitException : Exception
+namespace DisCatSharp.Exceptions
{
/// <summary>
- /// Gets the request that caused the exception.
+ /// Represents an exception thrown when too many requests are sent.
/// </summary>
- public BaseRestRequest WebRequest { get; internal set; }
+ public class RateLimitException : Exception
+ {
+ /// <summary>
+ /// Gets the request that caused the exception.
+ /// </summary>
+ public BaseRestRequest WebRequest { get; internal set; }
- /// <summary>
- /// Gets the response to the request.
- /// </summary>
- public RestResponse WebResponse { get; internal set; }
+ /// <summary>
+ /// Gets the response to the request.
+ /// </summary>
+ public RestResponse WebResponse { get; internal set; }
- /// <summary>
- /// Gets the JSON received.
- /// </summary>
- public string JsonMessage { get; internal set; }
+ /// <summary>
+ /// Gets the JSON received.
+ /// </summary>
+ public string JsonMessage { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="RateLimitException"/> class.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="response">The response.</param>
- internal RateLimitException(BaseRestRequest request, RestResponse response) : base("Rate limited: " + response.ResponseCode)
- {
- this.WebRequest = request;
- this.WebResponse = response;
-
- try
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RateLimitException"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="response">The response.</param>
+ internal RateLimitException(BaseRestRequest request, RestResponse response) : base("Rate limited: " + response.ResponseCode)
{
- var j = JObject.Parse(response.Response);
+ this.WebRequest = request;
+ this.WebResponse = response;
+
+ try
+ {
+ var j = JObject.Parse(response.Response);
- if (j["message"] != null)
- this.JsonMessage = j["message"].ToString();
+ if (j["message"] != null)
+ this.JsonMessage = j["message"].ToString();
+ }
+ catch (Exception) { }
}
- catch (Exception) { }
}
}
diff --git a/DisCatSharp/Exceptions/RequestSizeException.cs b/DisCatSharp/Exceptions/RequestSizeException.cs
index bdc3446bb..d5ef0d22d 100644
--- a/DisCatSharp/Exceptions/RequestSizeException.cs
+++ b/DisCatSharp/Exceptions/RequestSizeException.cs
@@ -1,70 +1,71 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Exceptions;
-
-/// <summary>
-/// Represents an exception thrown when the request sent to Discord is too large.
-/// </summary>
-public class RequestSizeException : Exception
+namespace DisCatSharp.Exceptions
{
/// <summary>
- /// Gets the request that caused the exception.
+ /// Represents an exception thrown when the request sent to Discord is too large.
/// </summary>
- public BaseRestRequest WebRequest { get; internal set; }
+ public class RequestSizeException : Exception
+ {
+ /// <summary>
+ /// Gets the request that caused the exception.
+ /// </summary>
+ public BaseRestRequest WebRequest { get; internal set; }
- /// <summary>
- /// Gets the response to the request.
- /// </summary>
- public RestResponse WebResponse { get; internal set; }
+ /// <summary>
+ /// Gets the response to the request.
+ /// </summary>
+ public RestResponse WebResponse { get; internal set; }
- /// <summary>
- /// Gets the JSON received.
- /// </summary>
- public string JsonMessage { get; internal set; }
+ /// <summary>
+ /// Gets the JSON received.
+ /// </summary>
+ public string JsonMessage { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="RequestSizeException"/> class.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="response">The response.</param>
- internal RequestSizeException(BaseRestRequest request, RestResponse response) : base($"Request entity too large: {response.ResponseCode}. Make sure the data sent is within Discord's upload limit.")
- {
- this.WebRequest = request;
- this.WebResponse = response;
-
- try
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RequestSizeException"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="response">The response.</param>
+ internal RequestSizeException(BaseRestRequest request, RestResponse response) : base($"Request entity too large: {response.ResponseCode}. Make sure the data sent is within Discord's upload limit.")
{
- var j = JObject.Parse(response.Response);
+ this.WebRequest = request;
+ this.WebResponse = response;
+
+ try
+ {
+ var j = JObject.Parse(response.Response);
- if (j["message"] != null)
- this.JsonMessage = j["message"].ToString();
+ if (j["message"] != null)
+ this.JsonMessage = j["message"].ToString();
+ }
+ catch (Exception) { }
}
- catch (Exception) { }
}
}
diff --git a/DisCatSharp/Exceptions/ServerErrorException.cs b/DisCatSharp/Exceptions/ServerErrorException.cs
index 837c1c1e5..c56dad0a3 100644
--- a/DisCatSharp/Exceptions/ServerErrorException.cs
+++ b/DisCatSharp/Exceptions/ServerErrorException.cs
@@ -1,70 +1,71 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Exceptions;
-
-/// <summary>
-/// Represents an exception thrown when Discord returns an Internal Server Error.
-/// </summary>
-public class ServerErrorException : Exception
+namespace DisCatSharp.Exceptions
{
/// <summary>
- /// Gets the request that caused the exception.
+ /// Represents an exception thrown when Discord returns an Internal Server Error.
/// </summary>
- public BaseRestRequest WebRequest { get; internal set; }
+ public class ServerErrorException : Exception
+ {
+ /// <summary>
+ /// Gets the request that caused the exception.
+ /// </summary>
+ public BaseRestRequest WebRequest { get; internal set; }
- /// <summary>
- /// Gets the response to the request.
- /// </summary>
- public RestResponse WebResponse { get; internal set; }
+ /// <summary>
+ /// Gets the response to the request.
+ /// </summary>
+ public RestResponse WebResponse { get; internal set; }
- /// <summary>
- /// Gets the JSON received.
- /// </summary>
- public string JsonMessage { get; internal set; }
+ /// <summary>
+ /// Gets the JSON received.
+ /// </summary>
+ public string JsonMessage { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DisCatSharp.Exceptions.ServerErrorException"/> class.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="response">The response.</param>
- internal ServerErrorException(BaseRestRequest request, RestResponse response) : base("Internal Server Error: " + response.ResponseCode)
- {
- this.WebRequest = request;
- this.WebResponse = response;
-
- try
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DisCatSharp.Exceptions.ServerErrorException"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="response">The response.</param>
+ internal ServerErrorException(BaseRestRequest request, RestResponse response) : base("Internal Server Error: " + response.ResponseCode)
{
- var j = JObject.Parse(response.Response);
+ this.WebRequest = request;
+ this.WebResponse = response;
+
+ try
+ {
+ var j = JObject.Parse(response.Response);
- if (j["message"] != null)
- this.JsonMessage = j["message"].ToString();
+ if (j["message"] != null)
+ this.JsonMessage = j["message"].ToString();
+ }
+ catch (Exception) { }
}
- catch (Exception) { }
}
}
diff --git a/DisCatSharp/Exceptions/UnauthorizedException.cs b/DisCatSharp/Exceptions/UnauthorizedException.cs
index aaad0898c..9403bf8f4 100644
--- a/DisCatSharp/Exceptions/UnauthorizedException.cs
+++ b/DisCatSharp/Exceptions/UnauthorizedException.cs
@@ -1,70 +1,71 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Exceptions;
-
-/// <summary>
-/// Represents an exception thrown when requester doesn't have necessary permissions to complete the request.
-/// </summary>
-public class UnauthorizedException : Exception
+namespace DisCatSharp.Exceptions
{
/// <summary>
- /// Gets the request that caused the exception.
+ /// Represents an exception thrown when requester doesn't have necessary permissions to complete the request.
/// </summary>
- public BaseRestRequest WebRequest { get; internal set; }
+ public class UnauthorizedException : Exception
+ {
+ /// <summary>
+ /// Gets the request that caused the exception.
+ /// </summary>
+ public BaseRestRequest WebRequest { get; internal set; }
- /// <summary>
- /// Gets the response to the request.
- /// </summary>
- public RestResponse WebResponse { get; internal set; }
+ /// <summary>
+ /// Gets the response to the request.
+ /// </summary>
+ public RestResponse WebResponse { get; internal set; }
- /// <summary>
- /// Gets the JSON received.
- /// </summary>
- public string JsonMessage { get; internal set; }
+ /// <summary>
+ /// Gets the JSON received.
+ /// </summary>
+ public string JsonMessage { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="DisCatSharp.Exceptions.UnauthorizedException"/> class.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="response">The response.</param>
- internal UnauthorizedException(BaseRestRequest request, RestResponse response) : base("Unauthorized: " + response.ResponseCode)
- {
- this.WebRequest = request;
- this.WebResponse = response;
-
- try
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DisCatSharp.Exceptions.UnauthorizedException"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="response">The response.</param>
+ internal UnauthorizedException(BaseRestRequest request, RestResponse response) : base("Unauthorized: " + response.ResponseCode)
{
- var j = JObject.Parse(response.Response);
+ this.WebRequest = request;
+ this.WebResponse = response;
+
+ try
+ {
+ var j = JObject.Parse(response.Response);
- if (j["message"] != null)
- this.JsonMessage = j["message"].ToString();
+ if (j["message"] != null)
+ this.JsonMessage = j["message"].ToString();
+ }
+ catch (Exception) { }
}
- catch (Exception) { }
}
}
diff --git a/DisCatSharp/Formatter.cs b/DisCatSharp/Formatter.cs
index 682792469..37cdc40bb 100644
--- a/DisCatSharp/Formatter.cs
+++ b/DisCatSharp/Formatter.cs
@@ -1,205 +1,206 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Text.RegularExpressions;
using DisCatSharp.Entities;
-namespace DisCatSharp;
-
-/// <summary>
-/// Contains markdown formatting helpers.
-/// </summary>
-public static class Formatter
+namespace DisCatSharp
{
/// <summary>
- /// Gets the md sanitize regex.
- /// </summary>
- private static Regex s_mdSanitizeRegex { get; } = new(@"([`\*_~<>\[\]\(\)""@\!\&#:\|])", RegexOptions.ECMAScript);
- /// <summary>
- /// Gets the md strip regex.
- /// </summary>
- private static Regex s_mdStripRegex { get; } = new(@"([`\*_~\[\]\(\)""\|]|<@\!?\d+>|<#\d+>|<@\&\d+>|<:[a-zA-Z0-9_\-]:\d+>)", RegexOptions.ECMAScript);
-
- /// <summary>
- /// Creates a block of code.
- /// </summary>
- /// <param name="content">Contents of the block.</param>
- /// <param name="language">Language to use for highlighting.</param>
- /// <returns>Formatted block of code.</returns>
- public static string BlockCode(string content, string language = "")
- => $"```{language}\n{content}\n```";
-
- /// <summary>
- /// Creates inline code snippet.
- /// </summary>
- /// <param name="content">Contents of the snippet.</param>
- /// <returns>Formatted inline code snippet.</returns>
- public static string InlineCode(string content)
- => $"`{content}`";
-
- /// <summary>
- /// Creates a rendered timestamp.
- /// </summary>
- /// <param name="time">The time from now.</param>
- /// <param name="format">The format to render the timestamp in. Defaults to relative.</param>
- /// <returns>A formatted timestamp.</returns>
- public static string Timestamp(TimeSpan time, TimestampFormat format = TimestampFormat.RelativeTime)
- => Timestamp(DateTimeOffset.UtcNow + time, format);
-
- /// <summary>
- /// Creates a rendered timestamp.
- /// </summary>
- /// <param name="time">Timestamp to format.</param>
- /// <param name="format">The format to render the timestamp in. Defaults to relative.</param>
- /// <returns>A formatted timestamp.</returns>
- public static string Timestamp(DateTimeOffset time, TimestampFormat format = TimestampFormat.RelativeTime)
- => $"<t:{time.ToUnixTimeSeconds()}:{(char)format}>";
-
- /// <summary>
- /// Creates a rendered timestamp.
- /// </summary>
- /// <param name="time">The time from now.</param>
- /// <param name="format">The format to render the timestamp in. Defaults to relative.</param>
- /// <returns>A formatted timestamp relative to now.</returns>
- public static string Timestamp(DateTime time, TimestampFormat format = TimestampFormat.RelativeTime)
- => Timestamp(time.ToUniversalTime() - DateTime.UtcNow, format);
-
- /// <summary>
- /// Creates bold text.
- /// </summary>
- /// <param name="content">Text to embolden.</param>
- /// <returns>Formatted text.</returns>
- public static string Bold(string content)
- => $"**{content}**";
-
- /// <summary>
- /// Creates italicized text.
- /// </summary>
- /// <param name="content">Text to italicize.</param>
- /// <returns>Formatted text.</returns>
- public static string Italic(string content)
- => $"*{content}*";
-
- /// <summary>
- /// Creates spoiler from text.
- /// </summary>
- /// <param name="content">Text to spoiler.</param>
- /// <returns>Formatted text.</returns>
- public static string Spoiler(string content)
- => $"||{content}||";
-
- /// <summary>
- /// Creates underlined text.
- /// </summary>
- /// <param name="content">Text to underline.</param>
- /// <returns>Formatted text.</returns>
- public static string Underline(string content)
- => $"__{content}__";
-
- /// <summary>
- /// Creates strikethrough text.
- /// </summary>
- /// <param name="content">Text to strikethrough.</param>
- /// <returns>Formatted text.</returns>
- public static string Strike(string content)
- => $"~~{content}~~";
-
- /// <summary>
- /// Creates a URL that won't create a link preview.
- /// </summary>
- /// <param name="url">Url to prevent from being previewed.</param>
- /// <returns>Formatted url.</returns>
- public static string EmbedlessUrl(Uri url)
- => $"<{url}>";
-
- /// <summary>
- /// Creates a masked link. This link will display as specified text, and alternatively provided alt text. This can only be used in embeds.
- /// </summary>
- /// <param name="text">Text to display the link as.</param>
- /// <param name="url">Url that the link will lead to.</param>
- /// <param name="altText">Alt text to display on hover.</param>
- /// <returns>Formatted url.</returns>
- public static string MaskedUrl(string text, Uri url, string altText = "")
- => $"[{text}]({url}{(!string.IsNullOrWhiteSpace(altText) ? $" \"{altText}\"" : "")})";
-
- /// <summary>
- /// Escapes all markdown formatting from specified text.
- /// </summary>
- /// <param name="text">Text to sanitize.</param>
- /// <returns>Sanitized text.</returns>
- public static string Sanitize(string text)
- => s_mdSanitizeRegex.Replace(text, m => $"\\{m.Groups[1].Value}");
-
- /// <summary>
- /// Removes all markdown formatting from specified text.
- /// </summary>
- /// <param name="text">Text to strip of formatting.</param>
- /// <returns>Formatting-stripped text.</returns>
- public static string Strip(string text)
- => s_mdStripRegex.Replace(text, m => string.Empty);
-
- /// <summary>
- /// Creates a mention for specified user or member. Can optionally specify to resolve nicknames.
- /// </summary>
- /// <param name="user">User to create mention for.</param>
- /// <param name="nickname">Whether the mention should resolve nicknames or not.</param>
- /// <returns>Formatted mention.</returns>
- public static string Mention(DiscordUser user, bool nickname = false)
- => nickname
- ? $"<@!{user.Id.ToString(CultureInfo.InvariantCulture)}>"
- : $"<@{user.Id.ToString(CultureInfo.InvariantCulture)}>";
-
- /// <summary>
- /// Creates a mention for specified channel.
- /// </summary>
- /// <param name="channel">Channel to mention.</param>
- /// <returns>Formatted mention.</returns>
- public static string Mention(DiscordChannel channel)
- => $"<#{channel.Id.ToString(CultureInfo.InvariantCulture)}>";
-
- /// <summary>
- /// Creates a mention for specified role.
- /// </summary>
- /// <param name="role">Role to mention.</param>
- /// <returns>Formatted mention.</returns>
- public static string Mention(DiscordRole role)
- => $"<@&{role.Id.ToString(CultureInfo.InvariantCulture)}>";
-
- /// <summary>
- /// Creates a custom emoji string.
- /// </summary>
- /// <param name="emoji">Emoji to display.</param>
- /// <returns>Formatted emoji.</returns>
- public static string Emoji(DiscordEmoji emoji)
- => $"<:{emoji.Name}:{emoji.Id.ToString(CultureInfo.InvariantCulture)}>";
-
- /// <summary>
- /// Creates a url for using attachments in embeds. This can only be used as an Image URL, Thumbnail URL, Author icon URL or Footer icon URL.
- /// </summary>
- /// <param name="filename">Name of attached image to display</param>
- /// <returns></returns>
- public static string AttachedImageUrl(string filename)
- => $"attachment://{filename}";
+ /// Contains markdown formatting helpers.
+ /// </summary>
+ public static class Formatter
+ {
+ /// <summary>
+ /// Gets the md sanitize regex.
+ /// </summary>
+ private static Regex s_mdSanitizeRegex { get; } = new(@"([`\*_~<>\[\]\(\)""@\!\&#:\|])", RegexOptions.ECMAScript);
+ /// <summary>
+ /// Gets the md strip regex.
+ /// </summary>
+ private static Regex s_mdStripRegex { get; } = new(@"([`\*_~\[\]\(\)""\|]|<@\!?\d+>|<#\d+>|<@\&\d+>|<:[a-zA-Z0-9_\-]:\d+>)", RegexOptions.ECMAScript);
+
+ /// <summary>
+ /// Creates a block of code.
+ /// </summary>
+ /// <param name="content">Contents of the block.</param>
+ /// <param name="language">Language to use for highlighting.</param>
+ /// <returns>Formatted block of code.</returns>
+ public static string BlockCode(string content, string language = "")
+ => $"```{language}\n{content}\n```";
+
+ /// <summary>
+ /// Creates inline code snippet.
+ /// </summary>
+ /// <param name="content">Contents of the snippet.</param>
+ /// <returns>Formatted inline code snippet.</returns>
+ public static string InlineCode(string content)
+ => $"`{content}`";
+
+ /// <summary>
+ /// Creates a rendered timestamp.
+ /// </summary>
+ /// <param name="time">The time from now.</param>
+ /// <param name="format">The format to render the timestamp in. Defaults to relative.</param>
+ /// <returns>A formatted timestamp.</returns>
+ public static string Timestamp(TimeSpan time, TimestampFormat format = TimestampFormat.RelativeTime)
+ => Timestamp(DateTimeOffset.UtcNow + time, format);
+
+ /// <summary>
+ /// Creates a rendered timestamp.
+ /// </summary>
+ /// <param name="time">Timestamp to format.</param>
+ /// <param name="format">The format to render the timestamp in. Defaults to relative.</param>
+ /// <returns>A formatted timestamp.</returns>
+ public static string Timestamp(DateTimeOffset time, TimestampFormat format = TimestampFormat.RelativeTime)
+ => $"<t:{time.ToUnixTimeSeconds()}:{(char)format}>";
+
+ /// <summary>
+ /// Creates a rendered timestamp.
+ /// </summary>
+ /// <param name="time">The time from now.</param>
+ /// <param name="format">The format to render the timestamp in. Defaults to relative.</param>
+ /// <returns>A formatted timestamp relative to now.</returns>
+ public static string Timestamp(DateTime time, TimestampFormat format = TimestampFormat.RelativeTime)
+ => Timestamp(time.ToUniversalTime() - DateTime.UtcNow, format);
+
+ /// <summary>
+ /// Creates bold text.
+ /// </summary>
+ /// <param name="content">Text to embolden.</param>
+ /// <returns>Formatted text.</returns>
+ public static string Bold(string content)
+ => $"**{content}**";
+
+ /// <summary>
+ /// Creates italicized text.
+ /// </summary>
+ /// <param name="content">Text to italicize.</param>
+ /// <returns>Formatted text.</returns>
+ public static string Italic(string content)
+ => $"*{content}*";
+
+ /// <summary>
+ /// Creates spoiler from text.
+ /// </summary>
+ /// <param name="content">Text to spoiler.</param>
+ /// <returns>Formatted text.</returns>
+ public static string Spoiler(string content)
+ => $"||{content}||";
+
+ /// <summary>
+ /// Creates underlined text.
+ /// </summary>
+ /// <param name="content">Text to underline.</param>
+ /// <returns>Formatted text.</returns>
+ public static string Underline(string content)
+ => $"__{content}__";
+
+ /// <summary>
+ /// Creates strikethrough text.
+ /// </summary>
+ /// <param name="content">Text to strikethrough.</param>
+ /// <returns>Formatted text.</returns>
+ public static string Strike(string content)
+ => $"~~{content}~~";
+
+ /// <summary>
+ /// Creates a URL that won't create a link preview.
+ /// </summary>
+ /// <param name="url">Url to prevent from being previewed.</param>
+ /// <returns>Formatted url.</returns>
+ public static string EmbedlessUrl(Uri url)
+ => $"<{url}>";
+
+ /// <summary>
+ /// Creates a masked link. This link will display as specified text, and alternatively provided alt text. This can only be used in embeds.
+ /// </summary>
+ /// <param name="text">Text to display the link as.</param>
+ /// <param name="url">Url that the link will lead to.</param>
+ /// <param name="altText">Alt text to display on hover.</param>
+ /// <returns>Formatted url.</returns>
+ public static string MaskedUrl(string text, Uri url, string altText = "")
+ => $"[{text}]({url}{(!string.IsNullOrWhiteSpace(altText) ? $" \"{altText}\"" : "")})";
+
+ /// <summary>
+ /// Escapes all markdown formatting from specified text.
+ /// </summary>
+ /// <param name="text">Text to sanitize.</param>
+ /// <returns>Sanitized text.</returns>
+ public static string Sanitize(string text)
+ => s_mdSanitizeRegex.Replace(text, m => $"\\{m.Groups[1].Value}");
+
+ /// <summary>
+ /// Removes all markdown formatting from specified text.
+ /// </summary>
+ /// <param name="text">Text to strip of formatting.</param>
+ /// <returns>Formatting-stripped text.</returns>
+ public static string Strip(string text)
+ => s_mdStripRegex.Replace(text, m => string.Empty);
+
+ /// <summary>
+ /// Creates a mention for specified user or member. Can optionally specify to resolve nicknames.
+ /// </summary>
+ /// <param name="user">User to create mention for.</param>
+ /// <param name="nickname">Whether the mention should resolve nicknames or not.</param>
+ /// <returns>Formatted mention.</returns>
+ public static string Mention(DiscordUser user, bool nickname = false)
+ => nickname
+ ? $"<@!{user.Id.ToString(CultureInfo.InvariantCulture)}>"
+ : $"<@{user.Id.ToString(CultureInfo.InvariantCulture)}>";
+
+ /// <summary>
+ /// Creates a mention for specified channel.
+ /// </summary>
+ /// <param name="channel">Channel to mention.</param>
+ /// <returns>Formatted mention.</returns>
+ public static string Mention(DiscordChannel channel)
+ => $"<#{channel.Id.ToString(CultureInfo.InvariantCulture)}>";
+
+ /// <summary>
+ /// Creates a mention for specified role.
+ /// </summary>
+ /// <param name="role">Role to mention.</param>
+ /// <returns>Formatted mention.</returns>
+ public static string Mention(DiscordRole role)
+ => $"<@&{role.Id.ToString(CultureInfo.InvariantCulture)}>";
+
+ /// <summary>
+ /// Creates a custom emoji string.
+ /// </summary>
+ /// <param name="emoji">Emoji to display.</param>
+ /// <returns>Formatted emoji.</returns>
+ public static string Emoji(DiscordEmoji emoji)
+ => $"<:{emoji.Name}:{emoji.Id.ToString(CultureInfo.InvariantCulture)}>";
+
+ /// <summary>
+ /// Creates a url for using attachments in embeds. This can only be used as an Image URL, Thumbnail URL, Author icon URL or Footer icon URL.
+ /// </summary>
+ /// <param name="filename">Name of attached image to display</param>
+ /// <returns></returns>
+ public static string AttachedImageUrl(string filename)
+ => $"attachment://{filename}";
+ }
}
diff --git a/DisCatSharp/ImageTool.cs b/DisCatSharp/ImageTool.cs
index 2700bcab2..b8751d4dd 100644
--- a/DisCatSharp/ImageTool.cs
+++ b/DisCatSharp/ImageTool.cs
@@ -1,238 +1,239 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using System.Text;
using DisCatSharp.Entities;
-namespace DisCatSharp;
-
-/// <summary>
-/// Tool to detect image formats and convert from binary data to base64 strings.
-/// </summary>
-public sealed class ImageTool : IDisposable
+namespace DisCatSharp
{
/// <summary>
- /// The png magic .
- /// </summary>
- private const ulong PNG_MAGIC = 0x0A1A_0A0D_474E_5089;
- /// <summary>
- /// The jpeg magic 1.
- /// </summary>
- private const ushort JPEG_MAGIC_1 = 0xD8FF;
- /// <summary>
- /// The jpeg magic 2.
- /// </summary>
- private const ushort JPEG_MAGIC_2 = 0xD9FF;
- /// <summary>
- /// The gif magic 1
- /// </summary>
- private const ulong GIF_MAGIC_1 = 0x0000_6139_3846_4947;
- /// <summary>
- /// The gif magic 2.
- /// </summary>
- private const ulong GIF_MAGIC_2 = 0x0000_6137_3846_4947;
- /// <summary>
- /// The webp magic 1.
- /// </summary>
- private const uint WEBP_MAGIC_1 = 0x4646_4952;
- /// <summary>
- /// The webp magic 2.
- /// </summary>
- private const uint WEBP_MAGIC_2 = 0x5042_4557;
-
- /// <summary>
- /// The gif mask.
- /// </summary>
- private const ulong GIF_MASK = 0x0000_FFFF_FFFF_FFFF;
- /// <summary>
- /// The mask 32.
- /// </summary>
- private const ulong MASK32 = 0x0000_0000_FFFF_FFFF;
- /// <summary>
- /// The mask 16.
- /// </summary>
- private const uint MASK16 = 0x0000_FFFF;
-
- /// <summary>
- /// Gets the stream this tool is operating on.
- /// </summary>
- public Stream SourceStream { get; }
-
- private ImageFormat _ifcache;
- private string _b64Cache;
-
- /// <summary>
- /// Creates a new image tool from given stream.
+ /// Tool to detect image formats and convert from binary data to base64 strings.
/// </summary>
- /// <param name="stream">Stream to work with.</param>
- public ImageTool(Stream stream)
+ public sealed class ImageTool : IDisposable
{
- if (stream == null)
- throw new ArgumentNullException(nameof(stream));
-
- if (!stream.CanRead || !stream.CanSeek)
- throw new ArgumentException("The stream needs to be both readable and seekable.", nameof(stream));
+ /// <summary>
+ /// The png magic .
+ /// </summary>
+ private const ulong PNG_MAGIC = 0x0A1A_0A0D_474E_5089;
+ /// <summary>
+ /// The jpeg magic 1.
+ /// </summary>
+ private const ushort JPEG_MAGIC_1 = 0xD8FF;
+ /// <summary>
+ /// The jpeg magic 2.
+ /// </summary>
+ private const ushort JPEG_MAGIC_2 = 0xD9FF;
+ /// <summary>
+ /// The gif magic 1
+ /// </summary>
+ private const ulong GIF_MAGIC_1 = 0x0000_6139_3846_4947;
+ /// <summary>
+ /// The gif magic 2.
+ /// </summary>
+ private const ulong GIF_MAGIC_2 = 0x0000_6137_3846_4947;
+ /// <summary>
+ /// The webp magic 1.
+ /// </summary>
+ private const uint WEBP_MAGIC_1 = 0x4646_4952;
+ /// <summary>
+ /// The webp magic 2.
+ /// </summary>
+ private const uint WEBP_MAGIC_2 = 0x5042_4557;
+
+ /// <summary>
+ /// The gif mask.
+ /// </summary>
+ private const ulong GIF_MASK = 0x0000_FFFF_FFFF_FFFF;
+ /// <summary>
+ /// The mask 32.
+ /// </summary>
+ private const ulong MASK32 = 0x0000_0000_FFFF_FFFF;
+ /// <summary>
+ /// The mask 16.
+ /// </summary>
+ private const uint MASK16 = 0x0000_FFFF;
+
+ /// <summary>
+ /// Gets the stream this tool is operating on.
+ /// </summary>
+ public Stream SourceStream { get; }
+
+ private ImageFormat _ifcache;
+ private string _b64Cache;
+
+ /// <summary>
+ /// Creates a new image tool from given stream.
+ /// </summary>
+ /// <param name="stream">Stream to work with.</param>
+ public ImageTool(Stream stream)
+ {
+ if (stream == null)
+ throw new ArgumentNullException(nameof(stream));
- this.SourceStream = stream;
- this.SourceStream.Seek(0, SeekOrigin.Begin);
+ if (!stream.CanRead || !stream.CanSeek)
+ throw new ArgumentException("The stream needs to be both readable and seekable.", nameof(stream));
- this._ifcache = 0;
- this._b64Cache = null;
- }
+ this.SourceStream = stream;
+ this.SourceStream.Seek(0, SeekOrigin.Begin);
- /// <summary>
- /// Detects the format of this image.
- /// </summary>
- /// <returns>Detected format.</returns>
- public ImageFormat GetFormat()
- {
- if (this._ifcache != ImageFormat.Unknown)
- return this._ifcache;
+ this._ifcache = 0;
+ this._b64Cache = null;
+ }
- using (var br = new BinaryReader(this.SourceStream, Utilities.UTF8, true))
+ /// <summary>
+ /// Detects the format of this image.
+ /// </summary>
+ /// <returns>Detected format.</returns>
+ public ImageFormat GetFormat()
{
- var bgn64 = br.ReadUInt64();
- if (bgn64 == PNG_MAGIC)
- return this._ifcache = ImageFormat.Png;
-
- bgn64 &= GIF_MASK;
- if (bgn64 == GIF_MAGIC_1 || bgn64 == GIF_MAGIC_2)
- return this._ifcache = ImageFormat.Gif;
+ if (this._ifcache != ImageFormat.Unknown)
+ return this._ifcache;
- var bgn32 = (uint)(bgn64 & MASK32);
- if (bgn32 == WEBP_MAGIC_1 && br.ReadUInt32() == WEBP_MAGIC_2)
- return this._ifcache = ImageFormat.WebP;
-
- var bgn16 = (ushort)(bgn32 & MASK16);
- if (bgn16 == JPEG_MAGIC_1)
+ using (var br = new BinaryReader(this.SourceStream, Utilities.UTF8, true))
{
- this.SourceStream.Seek(-2, SeekOrigin.End);
- if (br.ReadUInt16() == JPEG_MAGIC_2)
- return this._ifcache = ImageFormat.Jpeg;
+ var bgn64 = br.ReadUInt64();
+ if (bgn64 == PNG_MAGIC)
+ return this._ifcache = ImageFormat.Png;
+
+ bgn64 &= GIF_MASK;
+ if (bgn64 == GIF_MAGIC_1 || bgn64 == GIF_MAGIC_2)
+ return this._ifcache = ImageFormat.Gif;
+
+ var bgn32 = (uint)(bgn64 & MASK32);
+ if (bgn32 == WEBP_MAGIC_1 && br.ReadUInt32() == WEBP_MAGIC_2)
+ return this._ifcache = ImageFormat.WebP;
+
+ var bgn16 = (ushort)(bgn32 & MASK16);
+ if (bgn16 == JPEG_MAGIC_1)
+ {
+ this.SourceStream.Seek(-2, SeekOrigin.End);
+ if (br.ReadUInt16() == JPEG_MAGIC_2)
+ return this._ifcache = ImageFormat.Jpeg;
+ }
}
- }
- throw new InvalidDataException("The data within the stream was not valid image data.");
- }
-
- /// <summary>
- /// Converts this image into base64 data format string.
- /// </summary>
- /// <returns>Data-scheme base64 string.</returns>
- public string GetBase64()
- {
- if (this._b64Cache != null)
- return this._b64Cache;
+ throw new InvalidDataException("The data within the stream was not valid image data.");
+ }
- var fmt = this.GetFormat();
- var sb = new StringBuilder();
+ /// <summary>
+ /// Converts this image into base64 data format string.
+ /// </summary>
+ /// <returns>Data-scheme base64 string.</returns>
+ public string GetBase64()
+ {
+ if (this._b64Cache != null)
+ return this._b64Cache;
- sb.Append("data:image/")
- .Append(fmt.ToString().ToLowerInvariant())
- .Append(";base64,");
+ var fmt = this.GetFormat();
+ var sb = new StringBuilder();
- this.SourceStream.Seek(0, SeekOrigin.Begin);
- var buff = new byte[this.SourceStream.Length];
- var br = 0;
- while (br < buff.Length)
- br += this.SourceStream.Read(buff, br, (int)this.SourceStream.Length - br);
+ sb.Append("data:image/")
+ .Append(fmt.ToString().ToLowerInvariant())
+ .Append(";base64,");
- sb.Append(Convert.ToBase64String(buff));
+ this.SourceStream.Seek(0, SeekOrigin.Begin);
+ var buff = new byte[this.SourceStream.Length];
+ var br = 0;
+ while (br < buff.Length)
+ br += this.SourceStream.Read(buff, br, (int)this.SourceStream.Length - br);
- return this._b64Cache = sb.ToString();
- }
+ sb.Append(Convert.ToBase64String(buff));
- /// <summary>
- /// Disposes this image tool.
- /// </summary>
- public void Dispose()
- {
- if (this.SourceStream != null)
- this.SourceStream.Dispose();
- }
-
- /// <summary>
- /// Utility function to convert an image stream into a base 64 string.
- /// </summary>
- /// <param name="stream">The stream.</param>
- /// <returns>The base 64 string.</returns>
- public static string Base64FromStream(Stream stream)
- {
- using var imgtool = new ImageTool(stream);
- return imgtool.GetBase64();
- }
+ return this._b64Cache = sb.ToString();
+ }
- /// <summary>
- /// Utility function to convert an optional image stream into an optional base 64 string.
- /// </summary>
- /// <param name="stream">The optional stream.</param>
- /// <returns>The optional base 64 string.</returns>
- public static Optional<string> Base64FromStream(Optional<Stream> stream)
- {
- if (stream.HasValue)
+ /// <summary>
+ /// Disposes this image tool.
+ /// </summary>
+ public void Dispose()
{
- var val = stream.Value;
- return val != null ? Base64FromStream(val) : null;
+ if (this.SourceStream != null)
+ this.SourceStream.Dispose();
}
- return Optional.None;
- }
-}
-
-/// <summary>
-/// Represents format of an image.
-/// </summary>
-public enum ImageFormat : int
-{
- /// <summary>
- /// The format is unknown
- /// </summary>
- Unknown = 0,
-
- /// <summary>
- /// The format is a jpeg
- /// </summary>
- Jpeg = 1,
-
- /// <summary>
- /// The format is a png
- /// </summary>
- Png = 2,
+ /// <summary>
+ /// Utility function to convert an image stream into a base 64 string.
+ /// </summary>
+ /// <param name="stream">The stream.</param>
+ /// <returns>The base 64 string.</returns>
+ public static string Base64FromStream(Stream stream)
+ {
+ using var imgtool = new ImageTool(stream);
+ return imgtool.GetBase64();
+ }
- /// <summary>
- /// The format is a gif
- /// </summary>
- Gif = 3,
+ /// <summary>
+ /// Utility function to convert an optional image stream into an optional base 64 string.
+ /// </summary>
+ /// <param name="stream">The optional stream.</param>
+ /// <returns>The optional base 64 string.</returns>
+ public static Optional<string> Base64FromStream(Optional<Stream> stream)
+ {
+ if (stream.HasValue)
+ {
+ var val = stream.Value;
+ return val != null ? Base64FromStream(val) : null;
+ }
- /// <summary>
- /// The format is a webp
- /// </summary>
- WebP = 4,
+ return Optional.None;
+ }
+ }
/// <summary>
- /// The format will be automatically detected
+ /// Represents format of an image.
/// </summary>
- Auto = 5
+ public enum ImageFormat : int
+ {
+ /// <summary>
+ /// The format is unknown
+ /// </summary>
+ Unknown = 0,
+
+ /// <summary>
+ /// The format is a jpeg
+ /// </summary>
+ Jpeg = 1,
+
+ /// <summary>
+ /// The format is a png
+ /// </summary>
+ Png = 2,
+
+ /// <summary>
+ /// The format is a gif
+ /// </summary>
+ Gif = 3,
+
+ /// <summary>
+ /// The format is a webp
+ /// </summary>
+ WebP = 4,
+
+ /// <summary>
+ /// The format will be automatically detected
+ /// </summary>
+ Auto = 5
+ }
}
diff --git a/DisCatSharp/Internals.cs b/DisCatSharp/Internals.cs
index 68939f900..96a539fc5 100644
--- a/DisCatSharp/Internals.cs
+++ b/DisCatSharp/Internals.cs
@@ -1,93 +1,94 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using System.Text;
using DisCatSharp.Entities;
-namespace DisCatSharp;
-
-/// <summary>
-/// Internal tools.
-/// </summary>
-public static class Internals
+namespace DisCatSharp
{
/// <summary>
- /// Gets the version of the library
+ /// Internal tools.
/// </summary>
- private static string s_versionHeader
- => Utilities.VersionHeader;
+ public static class Internals
+ {
+ /// <summary>
+ /// Gets the version of the library
+ /// </summary>
+ private static string s_versionHeader
+ => Utilities.VersionHeader;
- /// <summary>
- /// Gets the permission strings.
- /// </summary>
- private static Dictionary<Permissions, string> s_permissionStrings
- => Utilities.PermissionStrings;
+ /// <summary>
+ /// Gets the permission strings.
+ /// </summary>
+ private static Dictionary<Permissions, string> s_permissionStrings
+ => Utilities.PermissionStrings;
- /// <summary>
- /// Gets the utf8 encoding
- /// </summary>
- internal static UTF8Encoding Utf8
- => Utilities.UTF8;
+ /// <summary>
+ /// Gets the utf8 encoding
+ /// </summary>
+ internal static UTF8Encoding Utf8
+ => Utilities.UTF8;
- /// <summary>
- /// Initializes a new instance of the <see cref="Internals"/> class.
- /// </summary>
- static Internals() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Internals"/> class.
+ /// </summary>
+ static Internals() { }
- /// <summary>
- /// Whether the <see cref="DiscordChannel"/> is joinable via voice.
- /// </summary>
- /// <param name="channel">The channel.</param>
- internal static bool IsVoiceJoinable(this DiscordChannel channel) => channel.Type == ChannelType.Voice || channel.Type == ChannelType.Stage;
+ /// <summary>
+ /// Whether the <see cref="DiscordChannel"/> is joinable via voice.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ internal static bool IsVoiceJoinable(this DiscordChannel channel) => channel.Type == ChannelType.Voice || channel.Type == ChannelType.Stage;
- /// <summary>
- /// Whether the <see cref="DiscordChannel"/> can have threads.
- /// </summary>
- /// <param name="channel">The channel.</param>
- internal static bool IsThreadHolder(this DiscordChannel channel) => channel.Type == ChannelType.Text || channel.Type == ChannelType.News || channel.Type == ChannelType.Forum;
+ /// <summary>
+ /// Whether the <see cref="DiscordChannel"/> can have threads.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ internal static bool IsThreadHolder(this DiscordChannel channel) => channel.Type == ChannelType.Text || channel.Type == ChannelType.News || channel.Type == ChannelType.Forum;
- /// <summary>
- /// Whether the <see cref="DiscordChannel"/> is related to threads.
- /// </summary>
- /// <param name="channel">The channel.</param>
- internal static bool IsThread(this DiscordChannel channel) => channel.Type == ChannelType.PublicThread || channel.Type == ChannelType.PrivateThread || channel.Type == ChannelType.NewsThread;
+ /// <summary>
+ /// Whether the <see cref="DiscordChannel"/> is related to threads.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ internal static bool IsThread(this DiscordChannel channel) => channel.Type == ChannelType.PublicThread || channel.Type == ChannelType.PrivateThread || channel.Type == ChannelType.NewsThread;
- /// <summary>
- /// Whether users can write the <see cref="DiscordChannel"/>.
- /// </summary>
- /// <param name="channel">The channel.</param>
- internal static bool IsWritable(this DiscordChannel channel) => channel.Type == ChannelType.PublicThread || channel.Type == ChannelType.PrivateThread || channel.Type == ChannelType.NewsThread || channel.Type == ChannelType.Text || channel.Type == ChannelType.News || channel.Type == ChannelType.Group || channel.Type == ChannelType.Private || channel.Type == ChannelType.Voice;
+ /// <summary>
+ /// Whether users can write the <see cref="DiscordChannel"/>.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ internal static bool IsWritable(this DiscordChannel channel) => channel.Type == ChannelType.PublicThread || channel.Type == ChannelType.PrivateThread || channel.Type == ChannelType.NewsThread || channel.Type == ChannelType.Text || channel.Type == ChannelType.News || channel.Type == ChannelType.Group || channel.Type == ChannelType.Private || channel.Type == ChannelType.Voice;
- /// <summary>
- /// Whether the <see cref="DiscordChannel"/> is moveable in a parent.
- /// </summary>
- /// <param name="channel">The channel.</param>
- internal static bool IsMovableInParent(this DiscordChannel channel) => channel.Type == ChannelType.Voice || channel.Type == ChannelType.Stage || channel.Type == ChannelType.Text || channel.Type == ChannelType.Forum || channel.Type == ChannelType.News;
+ /// <summary>
+ /// Whether the <see cref="DiscordChannel"/> is moveable in a parent.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ internal static bool IsMovableInParent(this DiscordChannel channel) => channel.Type == ChannelType.Voice || channel.Type == ChannelType.Stage || channel.Type == ChannelType.Text || channel.Type == ChannelType.Forum || channel.Type == ChannelType.News;
- /// <summary>
- /// Whether the <see cref="DiscordChannel"/> is moveable.
- /// </summary>
- /// <param name="channel">The channel.</param>
- internal static bool IsMovable(this DiscordChannel channel) => channel.Type == ChannelType.Voice || channel.Type == ChannelType.Stage || channel.Type == ChannelType.Text || channel.Type == ChannelType.Category || channel.Type == ChannelType.Forum || channel.Type == ChannelType.News;
+ /// <summary>
+ /// Whether the <see cref="DiscordChannel"/> is moveable.
+ /// </summary>
+ /// <param name="channel">The channel.</param>
+ internal static bool IsMovable(this DiscordChannel channel) => channel.Type == ChannelType.Voice || channel.Type == ChannelType.Stage || channel.Type == ChannelType.Text || channel.Type == ChannelType.Category || channel.Type == ChannelType.Forum || channel.Type == ChannelType.News;
+ }
}
diff --git a/DisCatSharp/Logging/CompositeDefaultLogger.cs b/DisCatSharp/Logging/CompositeDefaultLogger.cs
index d857252a6..c855a1956 100644
--- a/DisCatSharp/Logging/CompositeDefaultLogger.cs
+++ b/DisCatSharp/Logging/CompositeDefaultLogger.cs
@@ -1,78 +1,79 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a composite default logger.
-/// </summary>
-internal class CompositeDefaultLogger : ILogger<BaseDiscordClient>
+namespace DisCatSharp
{
/// <summary>
- /// Gets the loggers.
- /// </summary>
- private readonly IEnumerable<ILogger<BaseDiscordClient>> _loggers;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="CompositeDefaultLogger"/> class.
+ /// Represents a composite default logger.
/// </summary>
- /// <param name="providers">The providers.</param>
- public CompositeDefaultLogger(IEnumerable<ILoggerProvider> providers)
+ internal class CompositeDefaultLogger : ILogger<BaseDiscordClient>
{
- this._loggers = providers.Select(x => x.CreateLogger(typeof(BaseDiscordClient).FullName))
- .OfType<ILogger<BaseDiscordClient>>()
- .ToList();
- }
+ /// <summary>
+ /// Gets the loggers.
+ /// </summary>
+ private readonly IEnumerable<ILogger<BaseDiscordClient>> _loggers;
- /// <summary>
- /// Whether the logger is enabled.
- /// </summary>
- /// <param name="logLevel">The log level.</param>
- public bool IsEnabled(LogLevel logLevel)
- => true;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CompositeDefaultLogger"/> class.
+ /// </summary>
+ /// <param name="providers">The providers.</param>
+ public CompositeDefaultLogger(IEnumerable<ILoggerProvider> providers)
+ {
+ this._loggers = providers.Select(x => x.CreateLogger(typeof(BaseDiscordClient).FullName))
+ .OfType<ILogger<BaseDiscordClient>>()
+ .ToList();
+ }
- /// <summary>
- /// Logs an event.
- /// </summary>
- /// <param name="logLevel">The log level.</param>
- /// <param name="eventId">The event id.</param>
- /// <param name="state">The state.</param>
- /// <param name="exception">The exception.</param>
- /// <param name="formatter">The formatter.</param>
- public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
- {
- foreach (var logger in this._loggers)
- logger.Log(logLevel, eventId, state, exception, formatter);
- }
+ /// <summary>
+ /// Whether the logger is enabled.
+ /// </summary>
+ /// <param name="logLevel">The log level.</param>
+ public bool IsEnabled(LogLevel logLevel)
+ => true;
- /// <summary>
- /// Begins the scope.
- /// </summary>
- /// <param name="state">The state.</param>
- public IDisposable BeginScope<TState>(TState state) => throw new NotImplementedException();
+ /// <summary>
+ /// Logs an event.
+ /// </summary>
+ /// <param name="logLevel">The log level.</param>
+ /// <param name="eventId">The event id.</param>
+ /// <param name="state">The state.</param>
+ /// <param name="exception">The exception.</param>
+ /// <param name="formatter">The formatter.</param>
+ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
+ {
+ foreach (var logger in this._loggers)
+ logger.Log(logLevel, eventId, state, exception, formatter);
+ }
+
+ /// <summary>
+ /// Begins the scope.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ public IDisposable BeginScope<TState>(TState state) => throw new NotImplementedException();
+ }
}
diff --git a/DisCatSharp/Logging/DefaultLogger.cs b/DisCatSharp/Logging/DefaultLogger.cs
index dc8462c20..a6d6e6265 100644
--- a/DisCatSharp/Logging/DefaultLogger.cs
+++ b/DisCatSharp/Logging/DefaultLogger.cs
@@ -1,148 +1,149 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a default logger.
-/// </summary>
-public class DefaultLogger : ILogger<BaseDiscordClient>
+namespace DisCatSharp
{
- private static readonly object s_lock = new();
-
- /// <summary>
- /// Gets the minimum log level.
- /// </summary>
- private readonly LogLevel _minimumLevel;
-
- /// <summary>
- /// Gets the timestamp format.
- /// </summary>
- private readonly string _timestampFormat;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DefaultLogger"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- internal DefaultLogger(BaseDiscordClient client)
- : this(client.Configuration.MinimumLogLevel, client.Configuration.LogTimestampFormat)
- { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DefaultLogger"/> class.
- /// </summary>
- /// <param name="minLevel">The min level.</param>
- /// <param name="timestampFormat">The timestamp format.</param>
- internal DefaultLogger(LogLevel minLevel = LogLevel.Information, string timestampFormat = "yyyy-MM-dd HH:mm:ss zzz")
- {
- this._minimumLevel = minLevel;
- this._timestampFormat = timestampFormat;
- }
-
/// <summary>
- /// Logs an event.
+ /// Represents a default logger.
/// </summary>
- /// <param name="logLevel">The log level.</param>
- /// <param name="eventId">The event id.</param>
- /// <param name="state">The state.</param>
- /// <param name="exception">The exception.</param>
- /// <param name="formatter">The formatter.</param>
- public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
+ public class DefaultLogger : ILogger<BaseDiscordClient>
{
- if (!this.IsEnabled(logLevel))
- return;
+ private static readonly object s_lock = new();
+
+ /// <summary>
+ /// Gets the minimum log level.
+ /// </summary>
+ private readonly LogLevel _minimumLevel;
+
+ /// <summary>
+ /// Gets the timestamp format.
+ /// </summary>
+ private readonly string _timestampFormat;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultLogger"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ internal DefaultLogger(BaseDiscordClient client)
+ : this(client.Configuration.MinimumLogLevel, client.Configuration.LogTimestampFormat)
+ { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultLogger"/> class.
+ /// </summary>
+ /// <param name="minLevel">The min level.</param>
+ /// <param name="timestampFormat">The timestamp format.</param>
+ internal DefaultLogger(LogLevel minLevel = LogLevel.Information, string timestampFormat = "yyyy-MM-dd HH:mm:ss zzz")
+ {
+ this._minimumLevel = minLevel;
+ this._timestampFormat = timestampFormat;
+ }
- lock (s_lock)
+ /// <summary>
+ /// Logs an event.
+ /// </summary>
+ /// <param name="logLevel">The log level.</param>
+ /// <param name="eventId">The event id.</param>
+ /// <param name="state">The state.</param>
+ /// <param name="exception">The exception.</param>
+ /// <param name="formatter">The formatter.</param>
+ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
- var ename = eventId.Name;
- ename = ename?.Length > 12 ? ename?[..12] : ename;
- Console.Write($"[{DateTimeOffset.Now.ToString(this._timestampFormat)}] [{eventId.Id,-4}/{ename,-12}] ");
+ if (!this.IsEnabled(logLevel))
+ return;
- switch (logLevel)
+ lock (s_lock)
{
- case LogLevel.Trace:
- Console.ForegroundColor = ConsoleColor.Gray;
- break;
-
- case LogLevel.Debug:
- Console.ForegroundColor = ConsoleColor.DarkMagenta;
- break;
-
- case LogLevel.Information:
- Console.ForegroundColor = ConsoleColor.DarkCyan;
- break;
-
- case LogLevel.Warning:
- Console.ForegroundColor = ConsoleColor.Yellow;
- break;
-
- case LogLevel.Error:
- Console.ForegroundColor = ConsoleColor.Red;
- break;
-
- case LogLevel.Critical:
- Console.BackgroundColor = ConsoleColor.Red;
- Console.ForegroundColor = ConsoleColor.Black;
- break;
+ var ename = eventId.Name;
+ ename = ename?.Length > 12 ? ename?[..12] : ename;
+ Console.Write($"[{DateTimeOffset.Now.ToString(this._timestampFormat)}] [{eventId.Id,-4}/{ename,-12}] ");
+
+ switch (logLevel)
+ {
+ case LogLevel.Trace:
+ Console.ForegroundColor = ConsoleColor.Gray;
+ break;
+
+ case LogLevel.Debug:
+ Console.ForegroundColor = ConsoleColor.DarkMagenta;
+ break;
+
+ case LogLevel.Information:
+ Console.ForegroundColor = ConsoleColor.DarkCyan;
+ break;
+
+ case LogLevel.Warning:
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ break;
+
+ case LogLevel.Error:
+ Console.ForegroundColor = ConsoleColor.Red;
+ break;
+
+ case LogLevel.Critical:
+ Console.BackgroundColor = ConsoleColor.Red;
+ Console.ForegroundColor = ConsoleColor.Black;
+ break;
+ }
+ Console.Write(logLevel switch
+ {
+ LogLevel.Trace => "[Trace] ",
+ LogLevel.Debug => "[Debug] ",
+ LogLevel.Information => "[Info ] ",
+ LogLevel.Warning => "[Warn ] ",
+ LogLevel.Error => "[Error] ",
+ LogLevel.Critical => "[Critical ]",
+ LogLevel.None => "[None ] ",
+ _ => "[?????] "
+ });
+ Console.ResetColor();
+
+ //The foreground color is off.
+ if (logLevel == LogLevel.Critical)
+ Console.Write(" ");
+
+ var message = formatter(state, exception);
+ Console.WriteLine(message);
+ if (exception != null)
+ Console.WriteLine(exception);
}
- Console.Write(logLevel switch
- {
- LogLevel.Trace => "[Trace] ",
- LogLevel.Debug => "[Debug] ",
- LogLevel.Information => "[Info ] ",
- LogLevel.Warning => "[Warn ] ",
- LogLevel.Error => "[Error] ",
- LogLevel.Critical => "[Critical ]",
- LogLevel.None => "[None ] ",
- _ => "[?????] "
- });
- Console.ResetColor();
-
- //The foreground color is off.
- if (logLevel == LogLevel.Critical)
- Console.Write(" ");
-
- var message = formatter(state, exception);
- Console.WriteLine(message);
- if (exception != null)
- Console.WriteLine(exception);
}
- }
- /// <summary>
- /// Whether the logger is enabled.
- /// </summary>
- /// <param name="logLevel">The log level.</param>
- public bool IsEnabled(LogLevel logLevel)
- => logLevel >= this._minimumLevel;
-
- /// <summary>
- /// Begins the scope.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>An IDisposable.</returns>
- public IDisposable BeginScope<TState>(TState state) => throw new NotImplementedException();
+ /// <summary>
+ /// Whether the logger is enabled.
+ /// </summary>
+ /// <param name="logLevel">The log level.</param>
+ public bool IsEnabled(LogLevel logLevel)
+ => logLevel >= this._minimumLevel;
+
+ /// <summary>
+ /// Begins the scope.
+ /// </summary>
+ /// <param name="state">The state.</param>
+ /// <returns>An IDisposable.</returns>
+ public IDisposable BeginScope<TState>(TState state) => throw new NotImplementedException();
+ }
}
diff --git a/DisCatSharp/Logging/DefaultLoggerFactory.cs b/DisCatSharp/Logging/DefaultLoggerFactory.cs
index e25097d00..8c182abee 100644
--- a/DisCatSharp/Logging/DefaultLoggerFactory.cs
+++ b/DisCatSharp/Logging/DefaultLoggerFactory.cs
@@ -1,72 +1,73 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a default logger factory.
-/// </summary>
-internal class DefaultLoggerFactory : ILoggerFactory
+namespace DisCatSharp
{
/// <summary>
- /// Gets the providers.
+ /// Represents a default logger factory.
/// </summary>
- private readonly List<ILoggerProvider> _providers = new();
- private bool _isDisposed;
+ internal class DefaultLoggerFactory : ILoggerFactory
+ {
+ /// <summary>
+ /// Gets the providers.
+ /// </summary>
+ private readonly List<ILoggerProvider> _providers = new();
+ private bool _isDisposed;
- /// <summary>
- /// Adds a provider.
- /// </summary>
- /// <param name="provider">The provider to be added.</param>
- public void AddProvider(ILoggerProvider provider) => this._providers.Add(provider);
+ /// <summary>
+ /// Adds a provider.
+ /// </summary>
+ /// <param name="provider">The provider to be added.</param>
+ public void AddProvider(ILoggerProvider provider) => this._providers.Add(provider);
- /// <summary>
- /// Creates the logger.
- /// </summary>
- /// <param name="categoryName">The category name.</param>
- public ILogger CreateLogger(string categoryName) =>
- this._isDisposed
- ? throw new InvalidOperationException("This logger factory is already disposed.")
- : categoryName != typeof(BaseDiscordClient).FullName && categoryName != typeof(DiscordWebhookClient).FullName
- ? throw new ArgumentException($"This factory can only provide instances of loggers for {typeof(BaseDiscordClient).FullName} or {typeof(DiscordWebhookClient).FullName}.", nameof(categoryName))
- : new CompositeDefaultLogger(this._providers);
+ /// <summary>
+ /// Creates the logger.
+ /// </summary>
+ /// <param name="categoryName">The category name.</param>
+ public ILogger CreateLogger(string categoryName) =>
+ this._isDisposed
+ ? throw new InvalidOperationException("This logger factory is already disposed.")
+ : categoryName != typeof(BaseDiscordClient).FullName && categoryName != typeof(DiscordWebhookClient).FullName
+ ? throw new ArgumentException($"This factory can only provide instances of loggers for {typeof(BaseDiscordClient).FullName} or {typeof(DiscordWebhookClient).FullName}.", nameof(categoryName))
+ : new CompositeDefaultLogger(this._providers);
- /// <summary>
- /// Disposes the logger.
- /// </summary>
- public void Dispose()
- {
- if (this._isDisposed)
- return;
- this._isDisposed = true;
+ /// <summary>
+ /// Disposes the logger.
+ /// </summary>
+ public void Dispose()
+ {
+ if (this._isDisposed)
+ return;
+ this._isDisposed = true;
- foreach (var provider in this._providers)
- provider.Dispose();
+ foreach (var provider in this._providers)
+ provider.Dispose();
- this._providers.Clear();
+ this._providers.Clear();
+ }
}
}
diff --git a/DisCatSharp/Logging/DefaultLoggerProvider.cs b/DisCatSharp/Logging/DefaultLoggerProvider.cs
index 5f816a6e7..4ca44445b 100644
--- a/DisCatSharp/Logging/DefaultLoggerProvider.cs
+++ b/DisCatSharp/Logging/DefaultLoggerProvider.cs
@@ -1,88 +1,89 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a default logger provider.
-/// </summary>
-internal class DefaultLoggerProvider : ILoggerProvider
+namespace DisCatSharp
{
/// <summary>
- /// Gets the minimum log level.
+ /// Represents a default logger provider.
/// </summary>
- private readonly LogLevel _minimumLevel;
+ internal class DefaultLoggerProvider : ILoggerProvider
+ {
+ /// <summary>
+ /// Gets the minimum log level.
+ /// </summary>
+ private readonly LogLevel _minimumLevel;
- /// <summary>
- /// Gets the timestamp format.
- /// </summary>
- private readonly string _timestampFormat;
+ /// <summary>
+ /// Gets the timestamp format.
+ /// </summary>
+ private readonly string _timestampFormat;
- private bool _isDisposed;
+ private bool _isDisposed;
- /// <summary>
- /// Initializes a new instance of the <see cref="DefaultLoggerProvider"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- internal DefaultLoggerProvider(BaseDiscordClient client)
- : this(client.Configuration.MinimumLogLevel, client.Configuration.LogTimestampFormat)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultLoggerProvider"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ internal DefaultLoggerProvider(BaseDiscordClient client)
+ : this(client.Configuration.MinimumLogLevel, client.Configuration.LogTimestampFormat)
+ { }
- /// <summary>
- /// Initializes a new instance of the <see cref="DefaultLoggerProvider"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- internal DefaultLoggerProvider(DiscordWebhookClient client)
- : this(client.MinimumLogLevel, client.LogTimestampFormat)
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultLoggerProvider"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ internal DefaultLoggerProvider(DiscordWebhookClient client)
+ : this(client.MinimumLogLevel, client.LogTimestampFormat)
+ { }
- /// <summary>
- /// Initializes a new instance of the <see cref="DefaultLoggerProvider"/> class.
- /// </summary>
- /// <param name="minLevel">The min level.</param>
- /// <param name="timestampFormat">The timestamp format.</param>
- internal DefaultLoggerProvider(LogLevel minLevel = LogLevel.Information, string timestampFormat = "yyyy-MM-dd HH:mm:ss zzz")
- {
- this._minimumLevel = minLevel;
- this._timestampFormat = timestampFormat;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultLoggerProvider"/> class.
+ /// </summary>
+ /// <param name="minLevel">The min level.</param>
+ /// <param name="timestampFormat">The timestamp format.</param>
+ internal DefaultLoggerProvider(LogLevel minLevel = LogLevel.Information, string timestampFormat = "yyyy-MM-dd HH:mm:ss zzz")
+ {
+ this._minimumLevel = minLevel;
+ this._timestampFormat = timestampFormat;
+ }
- /// <summary>
- /// Creates the logger.
- /// </summary>
- /// <param name="categoryName">The category name.</param>
- public ILogger CreateLogger(string categoryName) =>
- this._isDisposed
- ? throw new InvalidOperationException("This logger provider is already disposed.")
- : categoryName != typeof(BaseDiscordClient).FullName && categoryName != typeof(DiscordWebhookClient).FullName
- ? throw new ArgumentException($"This provider can only provide instances of loggers for {typeof(BaseDiscordClient).FullName} or {typeof(DiscordWebhookClient).FullName}.", nameof(categoryName))
- : new DefaultLogger(this._minimumLevel, this._timestampFormat);
+ /// <summary>
+ /// Creates the logger.
+ /// </summary>
+ /// <param name="categoryName">The category name.</param>
+ public ILogger CreateLogger(string categoryName) =>
+ this._isDisposed
+ ? throw new InvalidOperationException("This logger provider is already disposed.")
+ : categoryName != typeof(BaseDiscordClient).FullName && categoryName != typeof(DiscordWebhookClient).FullName
+ ? throw new ArgumentException($"This provider can only provide instances of loggers for {typeof(BaseDiscordClient).FullName} or {typeof(DiscordWebhookClient).FullName}.", nameof(categoryName))
+ : new DefaultLogger(this._minimumLevel, this._timestampFormat);
- /// <summary>
- /// Disposes the logger.
- /// </summary>
- public void Dispose() => this._isDisposed = true;
+ /// <summary>
+ /// Disposes the logger.
+ /// </summary>
+ public void Dispose() => this._isDisposed = true;
+ }
}
diff --git a/DisCatSharp/Logging/LoggerEvents.cs b/DisCatSharp/Logging/LoggerEvents.cs
index 204120a3f..88980815c 100644
--- a/DisCatSharp/Logging/LoggerEvents.cs
+++ b/DisCatSharp/Logging/LoggerEvents.cs
@@ -1,171 +1,172 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Contains well-defined event IDs used by core of DisCatSharp.
-/// </summary>
-public static class LoggerEvents
+namespace DisCatSharp
{
/// <summary>
- /// Miscellaneous events, that do not fit in any other category.
- /// </summary>
- public static EventId Misc { get; } = new(100, "DisCatSharp");
-
- /// <summary>
- /// Events pertaining to startup tasks.
- /// </summary>
- public static EventId Startup { get; } = new(101, nameof(Startup));
-
- /// <summary>
- /// Events typically emitted whenever WebSocket connections fail or are terminated.
- /// </summary>
- public static EventId ConnectionFailure { get; } = new(102, nameof(ConnectionFailure));
-
- /// <summary>
- /// Events pertaining to Discord-issued session state updates.
- /// </summary>
- public static EventId SessionUpdate { get; } = new(103, nameof(SessionUpdate));
-
- /// <summary>
- /// Events emitted when exceptions are thrown in handlers attached to async events.
- /// </summary>
- public static EventId EventHandlerException { get; } = new(104, nameof(EventHandlerException));
-
- /// <summary>
- /// Events emitted for various high-level WebSocket receive events.
- /// </summary>
- public static EventId WebSocketReceive { get; } = new(105, nameof(WebSocketReceive));
-
- /// <summary>
- /// Events emitted for various low-level WebSocket receive events.
- /// </summary>
- public static EventId WebSocketReceiveRaw { get; } = new(106, nameof(WebSocketReceiveRaw));
-
- /// <summary>
- /// Events emitted for various low-level WebSocket send events.
- /// </summary>
- public static EventId WebSocketSendRaw { get; } = new(107, nameof(WebSocketSendRaw));
-
- /// <summary>
- /// Events emitted for various WebSocket payload processing failures, typically when deserialization or decoding fails.
- /// </summary>
- public static EventId WebSocketReceiveFailure { get; } = new(108, nameof(WebSocketReceiveFailure));
-
- /// <summary>
- /// Events pertaining to connection lifecycle, specifically, heartbeats.
- /// </summary>
- public static EventId Heartbeat { get; } = new(109, nameof(Heartbeat));
-
- /// <summary>
- /// Events pertaining to various heartbeat failures, typically fatal.
- /// </summary>
- public static EventId HeartbeatFailure { get; } = new(110, nameof(HeartbeatFailure));
-
- /// <summary>
- /// Events pertaining to clean connection closes.
- /// </summary>
- public static EventId ConnectionClose { get; } = new(111, nameof(ConnectionClose));
-
- /// <summary>
- /// Events emitted when REST processing fails for any reason.
- /// </summary>
- public static EventId RestError { get; } = new(112, nameof(RestError));
-
- /// <summary>
- /// Events pertaining to the <see cref="DiscordShardedClient"/> shard startup.
- /// </summary>
- public static EventId ShardStartup { get; } = new(113, nameof(ShardStartup));
-
- /// <summary>
- /// Events pertaining to ratelimit exhaustion.
- /// </summary>
- public static EventId RatelimitHit { get; } = new(114, nameof(RatelimitHit));
-
- /// <summary>
- /// Events pertaining to ratelimit diagnostics. Typically contain raw bucket info.
- /// </summary>
- public static EventId RatelimitDiag { get; } = new(115, nameof(RatelimitDiag));
-
- /// <summary>
- /// Events emitted when a ratelimit is exhausted and a request is preemptively blocked.
- /// </summary>
- public static EventId RatelimitPreemptive { get; } = new(116, nameof(RatelimitPreemptive));
-
- /// <summary>
- /// Events pertaining to audit log processing.
- /// </summary>
- public static EventId AuditLog { get; } = new(117, nameof(AuditLog));
-
- /// <summary>
- /// Events containing raw (but decompressed) payloads, received from Discord Gateway.
- /// </summary>
- public static EventId GatewayWsRx { get; } = new(118, "Gateway ↓");
-
- /// <summary>
- /// Events containing raw payloads, as they're being sent to Discord Gateway.
- /// </summary>
- public static EventId GatewayWsTx { get; } = new(119, "Gateway ↑");
-
- /// <summary>
- /// Events pertaining to Gateway Intents. Typically diagnostic information.
- /// </summary>
- public static EventId Intents { get; } = new(120, nameof(Intents));
-
- /// <summary>
- /// Events pertaining to autosharded client shard shutdown, clean or otherwise.
- /// </summary>
- public static EventId ShardShutdown { get; } = new(121, nameof(ShardShutdown));
-
- /// <summary>
- /// Events pertaining to the <see cref="DiscordShardedClient"/>'s shards not initializing correctly.
- /// </summary>
- public static EventId ShardClientError { get; } = new(122, nameof(ShardClientError));
-
- /// <summary>
- /// Events containing raw payloads, as they're received from Discord's REST API.
- /// </summary>
- public static EventId RestRx { get; } = new(123, "REST ↓");
-
- /// <summary>
- /// Events containing raw payloads, as they're sent to Discord's REST API.
- /// </summary>
- public static EventId RestTx { get; } = new(124, "REST ↑");
-
- /// <summary>
- /// Event is rest cleaner.
- /// </summary>
- public static EventId RestCleaner { get; } = new(125, nameof(RestCleaner));
-
- /// <summary>
- /// Event is rest hash mover.
- /// </summary>
- public static EventId RestHashMover { get; } = new(126, nameof(RestHashMover));
-
- /// <summary>
- /// Events pertaining to Discord API requests from the <see cref="DiscordShardedClient"/>.
- /// </summary>
- public static EventId ShardRest { get; } = new(127, nameof(ShardRest));
+ /// Contains well-defined event IDs used by core of DisCatSharp.
+ /// </summary>
+ public static class LoggerEvents
+ {
+ /// <summary>
+ /// Miscellaneous events, that do not fit in any other category.
+ /// </summary>
+ public static EventId Misc { get; } = new(100, "DisCatSharp");
+
+ /// <summary>
+ /// Events pertaining to startup tasks.
+ /// </summary>
+ public static EventId Startup { get; } = new(101, nameof(Startup));
+
+ /// <summary>
+ /// Events typically emitted whenever WebSocket connections fail or are terminated.
+ /// </summary>
+ public static EventId ConnectionFailure { get; } = new(102, nameof(ConnectionFailure));
+
+ /// <summary>
+ /// Events pertaining to Discord-issued session state updates.
+ /// </summary>
+ public static EventId SessionUpdate { get; } = new(103, nameof(SessionUpdate));
+
+ /// <summary>
+ /// Events emitted when exceptions are thrown in handlers attached to async events.
+ /// </summary>
+ public static EventId EventHandlerException { get; } = new(104, nameof(EventHandlerException));
+
+ /// <summary>
+ /// Events emitted for various high-level WebSocket receive events.
+ /// </summary>
+ public static EventId WebSocketReceive { get; } = new(105, nameof(WebSocketReceive));
+
+ /// <summary>
+ /// Events emitted for various low-level WebSocket receive events.
+ /// </summary>
+ public static EventId WebSocketReceiveRaw { get; } = new(106, nameof(WebSocketReceiveRaw));
+
+ /// <summary>
+ /// Events emitted for various low-level WebSocket send events.
+ /// </summary>
+ public static EventId WebSocketSendRaw { get; } = new(107, nameof(WebSocketSendRaw));
+
+ /// <summary>
+ /// Events emitted for various WebSocket payload processing failures, typically when deserialization or decoding fails.
+ /// </summary>
+ public static EventId WebSocketReceiveFailure { get; } = new(108, nameof(WebSocketReceiveFailure));
+
+ /// <summary>
+ /// Events pertaining to connection lifecycle, specifically, heartbeats.
+ /// </summary>
+ public static EventId Heartbeat { get; } = new(109, nameof(Heartbeat));
+
+ /// <summary>
+ /// Events pertaining to various heartbeat failures, typically fatal.
+ /// </summary>
+ public static EventId HeartbeatFailure { get; } = new(110, nameof(HeartbeatFailure));
+
+ /// <summary>
+ /// Events pertaining to clean connection closes.
+ /// </summary>
+ public static EventId ConnectionClose { get; } = new(111, nameof(ConnectionClose));
+
+ /// <summary>
+ /// Events emitted when REST processing fails for any reason.
+ /// </summary>
+ public static EventId RestError { get; } = new(112, nameof(RestError));
+
+ /// <summary>
+ /// Events pertaining to the <see cref="DiscordShardedClient"/> shard startup.
+ /// </summary>
+ public static EventId ShardStartup { get; } = new(113, nameof(ShardStartup));
+
+ /// <summary>
+ /// Events pertaining to ratelimit exhaustion.
+ /// </summary>
+ public static EventId RatelimitHit { get; } = new(114, nameof(RatelimitHit));
+
+ /// <summary>
+ /// Events pertaining to ratelimit diagnostics. Typically contain raw bucket info.
+ /// </summary>
+ public static EventId RatelimitDiag { get; } = new(115, nameof(RatelimitDiag));
+
+ /// <summary>
+ /// Events emitted when a ratelimit is exhausted and a request is preemptively blocked.
+ /// </summary>
+ public static EventId RatelimitPreemptive { get; } = new(116, nameof(RatelimitPreemptive));
+
+ /// <summary>
+ /// Events pertaining to audit log processing.
+ /// </summary>
+ public static EventId AuditLog { get; } = new(117, nameof(AuditLog));
+
+ /// <summary>
+ /// Events containing raw (but decompressed) payloads, received from Discord Gateway.
+ /// </summary>
+ public static EventId GatewayWsRx { get; } = new(118, "Gateway ↓");
+
+ /// <summary>
+ /// Events containing raw payloads, as they're being sent to Discord Gateway.
+ /// </summary>
+ public static EventId GatewayWsTx { get; } = new(119, "Gateway ↑");
+
+ /// <summary>
+ /// Events pertaining to Gateway Intents. Typically diagnostic information.
+ /// </summary>
+ public static EventId Intents { get; } = new(120, nameof(Intents));
+
+ /// <summary>
+ /// Events pertaining to autosharded client shard shutdown, clean or otherwise.
+ /// </summary>
+ public static EventId ShardShutdown { get; } = new(121, nameof(ShardShutdown));
+
+ /// <summary>
+ /// Events pertaining to the <see cref="DiscordShardedClient"/>'s shards not initializing correctly.
+ /// </summary>
+ public static EventId ShardClientError { get; } = new(122, nameof(ShardClientError));
+
+ /// <summary>
+ /// Events containing raw payloads, as they're received from Discord's REST API.
+ /// </summary>
+ public static EventId RestRx { get; } = new(123, "REST ↓");
+
+ /// <summary>
+ /// Events containing raw payloads, as they're sent to Discord's REST API.
+ /// </summary>
+ public static EventId RestTx { get; } = new(124, "REST ↑");
+
+ /// <summary>
+ /// Event is rest cleaner.
+ /// </summary>
+ public static EventId RestCleaner { get; } = new(125, nameof(RestCleaner));
+
+ /// <summary>
+ /// Event is rest hash mover.
+ /// </summary>
+ public static EventId RestHashMover { get; } = new(126, nameof(RestHashMover));
+
+ /// <summary>
+ /// Events pertaining to Discord API requests from the <see cref="DiscordShardedClient"/>.
+ /// </summary>
+ public static EventId ShardRest { get; } = new(127, nameof(ShardRest));
+ }
}
diff --git a/DisCatSharp/Logging/ShardedLoggerFactory.cs b/DisCatSharp/Logging/ShardedLoggerFactory.cs
index 8bf85bdca..44c411a95 100644
--- a/DisCatSharp/Logging/ShardedLoggerFactory.cs
+++ b/DisCatSharp/Logging/ShardedLoggerFactory.cs
@@ -1,68 +1,69 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a sharded logger factory.
-/// </summary>
-internal class ShardedLoggerFactory : ILoggerFactory
+namespace DisCatSharp
{
/// <summary>
- /// Gets the logger.
- /// </summary>
- private readonly ILogger<BaseDiscordClient> _logger;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ShardedLoggerFactory"/> class.
+ /// Represents a sharded logger factory.
/// </summary>
- /// <param name="instance">The instance.</param>
- public ShardedLoggerFactory(ILogger<BaseDiscordClient> instance)
+ internal class ShardedLoggerFactory : ILoggerFactory
{
- this._logger = instance;
- }
+ /// <summary>
+ /// Gets the logger.
+ /// </summary>
+ private readonly ILogger<BaseDiscordClient> _logger;
- /// <summary>
- /// Adds a provider.
- /// </summary>
- /// <param name="provider">The provider to be added.</param>
- public void AddProvider(ILoggerProvider provider) => throw new InvalidOperationException("This is a passthrough logger container, it cannot register new providers.");
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ShardedLoggerFactory"/> class.
+ /// </summary>
+ /// <param name="instance">The instance.</param>
+ public ShardedLoggerFactory(ILogger<BaseDiscordClient> instance)
+ {
+ this._logger = instance;
+ }
- /// <summary>
- /// Creates a logger.
- /// </summary>
- /// <param name="categoryName">The category name.</param>
- public ILogger CreateLogger(string categoryName) =>
- categoryName != typeof(BaseDiscordClient).FullName
- ? throw new ArgumentException($"This factory can only provide instances of loggers for {typeof(BaseDiscordClient).FullName}.", nameof(categoryName))
- : this._logger;
+ /// <summary>
+ /// Adds a provider.
+ /// </summary>
+ /// <param name="provider">The provider to be added.</param>
+ public void AddProvider(ILoggerProvider provider) => throw new InvalidOperationException("This is a passthrough logger container, it cannot register new providers.");
- /// <summary>
- /// Disposes the logger.
- /// </summary>
- public void Dispose()
- { }
+ /// <summary>
+ /// Creates a logger.
+ /// </summary>
+ /// <param name="categoryName">The category name.</param>
+ public ILogger CreateLogger(string categoryName) =>
+ categoryName != typeof(BaseDiscordClient).FullName
+ ? throw new ArgumentException($"This factory can only provide instances of loggers for {typeof(BaseDiscordClient).FullName}.", nameof(categoryName))
+ : this._logger;
+
+ /// <summary>
+ /// Disposes the logger.
+ /// </summary>
+ public void Dispose()
+ { }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/AuditLogAbstractions.cs b/DisCatSharp/Net/Abstractions/AuditLogAbstractions.cs
index 0102f25ea..af338b04f 100644
--- a/DisCatSharp/Net/Abstractions/AuditLogAbstractions.cs
+++ b/DisCatSharp/Net/Abstractions/AuditLogAbstractions.cs
@@ -1,576 +1,577 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a audit log user.
-/// </summary>
-internal sealed class AuditLogUser
-{
- /// <summary>
- /// Gets or sets the username.
- /// </summary>
- [JsonProperty("username")]
- public string Username { get; set; }
-
- /// <summary>
- /// Gets or sets the discriminator.
- /// </summary>
- [JsonProperty("discriminator")]
- public string Discriminator { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- [JsonProperty("id")]
- public ulong Id { get; set; }
-
- /// <summary>
- /// Gets or sets the avatar hash.
- /// </summary>
- [JsonProperty("avatar")]
- public string AvatarHash { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log webhook.
-/// </summary>
-internal sealed class AuditLogWebhook
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id")]
- public ulong ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the token.
- /// </summary>
- [JsonProperty("token")]
- public string Token { get; set; }
-
- /// <summary>
- /// Gets or sets the avatar hash.
- /// </summary>
- [JsonProperty("avatar")]
- public string AvatarHash { get; set; }
-
- /// <summary>
- /// Gets or sets the guild id.
- /// </summary>
- [JsonProperty("guild_id")]
- public ulong GuildId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- [JsonProperty("id")]
- public ulong Id { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log thread metadata.
-/// </summary>
-internal sealed class AuditLogThreadMetadata
-{
- /// <summary>
- /// Gets whether the thread is archived.
- /// </summary>
- [JsonProperty("archived")]
- public bool Archived { get; set; }
-
- /// <summary>
- /// Gets the threads archive timestamp.
- /// </summary>
- [JsonProperty("archive_timestamp", NullValueHandling = NullValueHandling.Ignore)]
- public string ArchiveTimestamp { get; set; }
-
- /// <summary>
- /// Gets the threads auto archive duration.
- /// </summary>
- [JsonProperty("auto_archive_duration")]
- public int AutoArchiveDuration { get; set; }
-
- /// <summary>
- /// Gets whether the thread is locked.
- /// </summary>
- [JsonProperty("locked")]
- public bool Locked { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log thread.
-/// </summary>
-internal sealed class AuditLogThread
-{
- /// <summary>
- /// Gets the thread id.
- /// </summary>
- [JsonProperty("id")]
- public ulong Id { get; set; }
-
- /// <summary>
- /// Gets the thread guild id.
- /// </summary>
- [JsonProperty("guild_id")]
- public ulong GuildId { get; set; }
-
- /// <summary>
- /// Gets the thread parent channel id.
- /// </summary>
- [JsonProperty("parent_id")]
- public ulong ParentId { get; set; }
-
- /// <summary>
- /// Gets the thread owner id.
- /// </summary>
- [JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? OwnerId { get; set; }
-
- /// <summary>
- /// Gets the thread type.
- /// </summary>
- [JsonProperty("type")]
- public ChannelType Type { get; set; }
-
- /// <summary>
- /// Gets the thread name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets the thread last message id.
- /// </summary>
- [JsonProperty("last_message_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? LastMessageId { get; set; }
-
- /// <summary>
- /// Gets the thread metadata.
- /// </summary>
- [JsonProperty("thread_metadata")]
- public AuditLogThreadMetadata Metadata { get; set; }
-
- /// <summary>
- /// Gets the thread approximate message count.
- /// </summary>
- [JsonProperty("message_count", NullValueHandling = NullValueHandling.Ignore)]
- public int? MessageCount { get; set; }
-
- /// <summary>
- /// Gets the thread member count.
- /// </summary>
- [JsonProperty("member_count", NullValueHandling = NullValueHandling.Ignore)]
- public int? MemberCount { get; set; }
-
- /// <summary>
- /// Gets the thread rate limit per user.
- /// </summary>
- [JsonProperty("rate_limit_per_user", NullValueHandling = NullValueHandling.Ignore)]
- public int? RateLimitPerUser { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log scheduled event.
-/// </summary>
-internal sealed class AuditLogGuildScheduledEvent
-{
- /// <summary>
- /// Gets the scheduled event id.
- /// </summary>
- [JsonProperty("id")]
- public ulong Id { get; set; }
-
- /// <summary>
- /// Gets the scheduled event guild id.
- /// </summary>
- [JsonProperty("guild_id")]
- public ulong GuildId { get; set; }
-
- /// <summary>
- /// Gets the scheduled event channel id.
- /// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; set; }
-
- /// <summary>
- /// Gets the scheduled event creator id.
- /// </summary>
- [JsonProperty("creator_id")]
- public ulong CreatorId { get; set; }
-
- /// <summary>
- /// Gets the scheduled event name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets the scheduled event description.
- /// </summary>
- [JsonProperty("description")]
- public string Description { get; set; }
-
- /// <summary>
- /// Gets the scheduled event image.
- /// </summary>
- [JsonProperty("image", NullValueHandling = NullValueHandling.Ignore)]
- public string Image { get; set; }
-
- /// <summary>
- /// Gets the scheduled event scheduled start time.
- /// </summary>
- [JsonProperty("scheduled_start_time")]
- public string ScheduledStartTime;
-
- /// <summary>
- /// Gets the scheduled event scheduled end time.
- /// </summary>
- [JsonProperty("scheduled_end_time")]
- public string ScheduledEndTime { get; set; }
-
- /// <summary>
- /// Gets the scheduled event privacy level.
- /// </summary>
- [JsonProperty("privacy_level")]
- public ScheduledEventPrivacyLevel PrivacyLevel { get; set; }
-
- /// <summary>
- /// Gets the scheduled event status.
- /// </summary>
- [JsonProperty("status")]
- public ScheduledEventStatus Status { get; set; }
-
- /// <summary>
- /// Gets the scheduled event entity type.
- /// </summary>
- [JsonProperty("entity_type")]
- public ScheduledEventEntityType EntityType { get; set; }
-
- /// <summary>
- /// Gets the scheduled event entity id.
- /// </summary>
- [JsonProperty("entity_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong EntityId { get; set; }
-
- /// <summary>
- /// Gets the scheduled event entity metadata.
- /// </summary>
- [JsonProperty("entity_metadata")]
- public AuditLogGuildScheduledEventEntityMetadata EntityMetadata { get; set; }
-
- /// <summary>
- /// Gets the scheduled event sku ids.
- /// </summary>
- [JsonProperty("sku_ids")]
- public List<ulong> SkuIds { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log scheduled event entity metadata.
-/// </summary>
-internal sealed class AuditLogGuildScheduledEventEntityMetadata
-{
- /// <summary>
- /// Gets the scheduled events external location.
- /// </summary>
- [JsonProperty("location")]
- public string Location { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log integration account.
-/// </summary>
-internal sealed class AuditLogIntegrationAccount
-{
- /// <summary>
- /// Gets the account id.
- /// </summary>
- [JsonProperty("id")]
- public string Id { get; set; }
-
- /// <summary>
- /// Gets the account name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log integration.
-/// </summary>
-internal sealed class AuditLogIntegration
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets the integration id.
- /// </summary>
- [JsonProperty("id")]
- public ulong Id { get; set; }
-
- /// <summary>
- /// Gets the integration type.
- /// </summary>
- [JsonProperty("type")]
- public string Type { get; set; }
-
- /// <summary>
- /// Gets the integration name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets the integration account.
- /// </summary>
- [JsonProperty("account")]
- public AuditLogIntegrationAccount Account { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log action change.
-/// </summary>
-internal sealed class AuditLogActionChange
-{
- // this can be a string or an array
- /// <summary>
- /// Gets or sets the old value.
- /// </summary>
- [JsonProperty("old_value")]
- public object OldValue { get; set; }
-
- /// <summary>
- /// Gets the old values.
- /// </summary>
- [JsonIgnore]
- public IEnumerable<JObject> OldValues
- => (this.OldValue as JArray)?.ToObject<IEnumerable<JObject>>();
-
- /// <summary>
- /// Gets the old value ulong.
- /// </summary>
- [JsonIgnore]
- public ulong OldValueUlong
- => (ulong)this.OldValue;
-
- /// <summary>
- /// Gets the old value string.
- /// </summary>
- [JsonIgnore]
- public string OldValueString
- => (string)this.OldValue;
-
- // this can be a string or an array
- /// <summary>
- /// Gets or sets the new value.
- /// </summary>
- [JsonProperty("new_value")]
- public object NewValue { get; set; }
-
- /// <summary>
- /// Gets the new values.
- /// </summary>
- [JsonIgnore]
- public IEnumerable<JObject> NewValues
- => (this.NewValue as JArray)?.ToObject<IEnumerable<JObject>>();
-
- /// <summary>
- /// Gets the new value ulong.
- /// </summary>
- [JsonIgnore]
- public ulong NewValueUlong
- => (ulong)this.NewValue;
-
- /// <summary>
- /// Gets the new value string.
- /// </summary>
- [JsonIgnore]
- public string NewValueString
- => (string)this.NewValue;
-
- /// <summary>
- /// Gets or sets the key.
- /// </summary>
- [JsonProperty("key")]
- public string Key { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log action options.
-/// </summary>
-internal sealed class AuditLogActionOptions
-{
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- [JsonProperty("type")]
- public object Type { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- [JsonProperty("id")]
- public ulong Id { get; set; }
-
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id")]
- public ulong ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the message id.
- /// </summary>
- [JsonProperty("message_id")]
- public ulong MessageId { get; set; }
-
- /// <summary>
- /// Gets or sets the count.
- /// </summary>
- [JsonProperty("count")]
- public int Count { get; set; }
-
- /// <summary>
- /// Gets or sets the delete member days.
- /// </summary>
- [JsonProperty("delete_member_days")]
- public int DeleteMemberDays { get; set; }
-
- /// <summary>
- /// Gets or sets the members removed.
- /// </summary>
- [JsonProperty("members_removed")]
- public int MembersRemoved { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log action.
-/// </summary>
-internal sealed class AuditLogAction
-{
- /// <summary>
- /// Gets or sets the target id.
- /// </summary>
- [JsonProperty("target_id")]
- public ulong? TargetId { get; set; }
-
- /// <summary>
- /// Gets or sets the user id.
- /// </summary>
- [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong UserId { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- [JsonProperty("id")]
- public ulong Id { get; set; }
-
- /// <summary>
- /// Gets or sets the action type.
- /// </summary>
- [JsonProperty("action_type", NullValueHandling = NullValueHandling.Ignore)]
- public AuditLogActionType ActionType { get; set; }
-
- /// <summary>
- /// Gets or sets the changes.
- /// </summary>
- [JsonProperty("changes")]
- public IEnumerable<AuditLogActionChange> Changes { get; set; }
-
- /// <summary>
- /// Gets or sets the options.
- /// </summary>
- [JsonProperty("options")]
- public AuditLogActionOptions Options { get; set; }
-
- /// <summary>
- /// Gets or sets the reason.
- /// </summary>
- [JsonProperty("reason")]
- public string Reason { get; set; }
-}
-
-/// <summary>
-/// Represents a audit log.
-/// </summary>
-internal sealed class AuditLog
-{
- /// <summary>
- /// Gets or sets the webhooks.
- /// </summary>
- [JsonProperty("webhooks")]
- public IEnumerable<AuditLogWebhook> Webhooks { get; set; }
-
- /// <summary>
- /// Gets or sets the users.
- /// </summary>
- [JsonProperty("users")]
- public IEnumerable<AuditLogUser> Users { get; set; }
-
- /// <summary>
- /// Gets or sets the entries.
- /// </summary>
- [JsonProperty("audit_log_entries")]
- public IEnumerable<AuditLogAction> Entries { get; set; }
-
- /// <summary>
- /// Gets or sets the scheduled events.
- /// </summary>
- [JsonProperty("guild_scheduled_events")]
- public IEnumerable<AuditLogGuildScheduledEvent> ScheduledEvents { get; set; }
-
- /// <summary>
- /// Gets or sets the threads.
- /// </summary>
- [JsonProperty("threads")]
- public IEnumerable<AuditLogThread> Threads { get; set; }
-
- /// <summary>
- /// Gets or sets the integrations.
- /// Twitch related.
- /// </summary>
- [JsonProperty("integrations")]
- public IEnumerable<AuditLogIntegration> Integrations { get; set; }
-
- /*
+ /// Represents a audit log user.
+ /// </summary>
+ internal sealed class AuditLogUser
+ {
+ /// <summary>
+ /// Gets or sets the username.
+ /// </summary>
+ [JsonProperty("username")]
+ public string Username { get; set; }
+
+ /// <summary>
+ /// Gets or sets the discriminator.
+ /// </summary>
+ [JsonProperty("discriminator")]
+ public string Discriminator { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the avatar hash.
+ /// </summary>
+ [JsonProperty("avatar")]
+ public string AvatarHash { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log webhook.
+ /// </summary>
+ internal sealed class AuditLogWebhook
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public ulong ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ [JsonProperty("token")]
+ public string Token { get; set; }
+
+ /// <summary>
+ /// Gets or sets the avatar hash.
+ /// </summary>
+ [JsonProperty("avatar")]
+ public string AvatarHash { get; set; }
+
+ /// <summary>
+ /// Gets or sets the guild id.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log thread metadata.
+ /// </summary>
+ internal sealed class AuditLogThreadMetadata
+ {
+ /// <summary>
+ /// Gets whether the thread is archived.
+ /// </summary>
+ [JsonProperty("archived")]
+ public bool Archived { get; set; }
+
+ /// <summary>
+ /// Gets the threads archive timestamp.
+ /// </summary>
+ [JsonProperty("archive_timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ public string ArchiveTimestamp { get; set; }
+
+ /// <summary>
+ /// Gets the threads auto archive duration.
+ /// </summary>
+ [JsonProperty("auto_archive_duration")]
+ public int AutoArchiveDuration { get; set; }
+
+ /// <summary>
+ /// Gets whether the thread is locked.
+ /// </summary>
+ [JsonProperty("locked")]
+ public bool Locked { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log thread.
+ /// </summary>
+ internal sealed class AuditLogThread
+ {
+ /// <summary>
+ /// Gets the thread id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+
+ /// <summary>
+ /// Gets the thread guild id.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
+
+ /// <summary>
+ /// Gets the thread parent channel id.
+ /// </summary>
+ [JsonProperty("parent_id")]
+ public ulong ParentId { get; set; }
+
+ /// <summary>
+ /// Gets the thread owner id.
+ /// </summary>
+ [JsonProperty("owner_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? OwnerId { get; set; }
+
+ /// <summary>
+ /// Gets the thread type.
+ /// </summary>
+ [JsonProperty("type")]
+ public ChannelType Type { get; set; }
+
+ /// <summary>
+ /// Gets the thread name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets the thread last message id.
+ /// </summary>
+ [JsonProperty("last_message_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? LastMessageId { get; set; }
+
+ /// <summary>
+ /// Gets the thread metadata.
+ /// </summary>
+ [JsonProperty("thread_metadata")]
+ public AuditLogThreadMetadata Metadata { get; set; }
+
+ /// <summary>
+ /// Gets the thread approximate message count.
+ /// </summary>
+ [JsonProperty("message_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int? MessageCount { get; set; }
+
+ /// <summary>
+ /// Gets the thread member count.
+ /// </summary>
+ [JsonProperty("member_count", NullValueHandling = NullValueHandling.Ignore)]
+ public int? MemberCount { get; set; }
+
+ /// <summary>
+ /// Gets the thread rate limit per user.
+ /// </summary>
+ [JsonProperty("rate_limit_per_user", NullValueHandling = NullValueHandling.Ignore)]
+ public int? RateLimitPerUser { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log scheduled event.
+ /// </summary>
+ internal sealed class AuditLogGuildScheduledEvent
+ {
+ /// <summary>
+ /// Gets the scheduled event id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event guild id.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event channel id.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event creator id.
+ /// </summary>
+ [JsonProperty("creator_id")]
+ public ulong CreatorId { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event description.
+ /// </summary>
+ [JsonProperty("description")]
+ public string Description { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event image.
+ /// </summary>
+ [JsonProperty("image", NullValueHandling = NullValueHandling.Ignore)]
+ public string Image { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event scheduled start time.
+ /// </summary>
+ [JsonProperty("scheduled_start_time")]
+ public string ScheduledStartTime;
+
+ /// <summary>
+ /// Gets the scheduled event scheduled end time.
+ /// </summary>
+ [JsonProperty("scheduled_end_time")]
+ public string ScheduledEndTime { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event privacy level.
+ /// </summary>
+ [JsonProperty("privacy_level")]
+ public ScheduledEventPrivacyLevel PrivacyLevel { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event status.
+ /// </summary>
+ [JsonProperty("status")]
+ public ScheduledEventStatus Status { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event entity type.
+ /// </summary>
+ [JsonProperty("entity_type")]
+ public ScheduledEventEntityType EntityType { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event entity id.
+ /// </summary>
+ [JsonProperty("entity_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong EntityId { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event entity metadata.
+ /// </summary>
+ [JsonProperty("entity_metadata")]
+ public AuditLogGuildScheduledEventEntityMetadata EntityMetadata { get; set; }
+
+ /// <summary>
+ /// Gets the scheduled event sku ids.
+ /// </summary>
+ [JsonProperty("sku_ids")]
+ public List<ulong> SkuIds { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log scheduled event entity metadata.
+ /// </summary>
+ internal sealed class AuditLogGuildScheduledEventEntityMetadata
+ {
+ /// <summary>
+ /// Gets the scheduled events external location.
+ /// </summary>
+ [JsonProperty("location")]
+ public string Location { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log integration account.
+ /// </summary>
+ internal sealed class AuditLogIntegrationAccount
+ {
+ /// <summary>
+ /// Gets the account id.
+ /// </summary>
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ /// <summary>
+ /// Gets the account name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log integration.
+ /// </summary>
+ internal sealed class AuditLogIntegration
+ {
+ /// <summary>
+ /// Gets the integration id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+
+ /// <summary>
+ /// Gets the integration type.
+ /// </summary>
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ /// <summary>
+ /// Gets the integration name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets the integration account.
+ /// </summary>
+ [JsonProperty("account")]
+ public AuditLogIntegrationAccount Account { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log action change.
+ /// </summary>
+ internal sealed class AuditLogActionChange
+ {
+ // this can be a string or an array
+ /// <summary>
+ /// Gets or sets the old value.
+ /// </summary>
+ [JsonProperty("old_value")]
+ public object OldValue { get; set; }
+
+ /// <summary>
+ /// Gets the old values.
+ /// </summary>
+ [JsonIgnore]
+ public IEnumerable<JObject> OldValues
+ => (this.OldValue as JArray)?.ToObject<IEnumerable<JObject>>();
+
+ /// <summary>
+ /// Gets the old value ulong.
+ /// </summary>
+ [JsonIgnore]
+ public ulong OldValueUlong
+ => (ulong)this.OldValue;
+
+ /// <summary>
+ /// Gets the old value string.
+ /// </summary>
+ [JsonIgnore]
+ public string OldValueString
+ => (string)this.OldValue;
+
+ // this can be a string or an array
+ /// <summary>
+ /// Gets or sets the new value.
+ /// </summary>
+ [JsonProperty("new_value")]
+ public object NewValue { get; set; }
+
+ /// <summary>
+ /// Gets the new values.
+ /// </summary>
+ [JsonIgnore]
+ public IEnumerable<JObject> NewValues
+ => (this.NewValue as JArray)?.ToObject<IEnumerable<JObject>>();
+
+ /// <summary>
+ /// Gets the new value ulong.
+ /// </summary>
+ [JsonIgnore]
+ public ulong NewValueUlong
+ => (ulong)this.NewValue;
+
+ /// <summary>
+ /// Gets the new value string.
+ /// </summary>
+ [JsonIgnore]
+ public string NewValueString
+ => (string)this.NewValue;
+
+ /// <summary>
+ /// Gets or sets the key.
+ /// </summary>
+ [JsonProperty("key")]
+ public string Key { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log action options.
+ /// </summary>
+ internal sealed class AuditLogActionOptions
+ {
+ /// <summary>
+ /// Gets or sets the type.
+ /// </summary>
+ [JsonProperty("type")]
+ public object Type { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public ulong ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the message id.
+ /// </summary>
+ [JsonProperty("message_id")]
+ public ulong MessageId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the count.
+ /// </summary>
+ [JsonProperty("count")]
+ public int Count { get; set; }
+
+ /// <summary>
+ /// Gets or sets the delete member days.
+ /// </summary>
+ [JsonProperty("delete_member_days")]
+ public int DeleteMemberDays { get; set; }
+
+ /// <summary>
+ /// Gets or sets the members removed.
+ /// </summary>
+ [JsonProperty("members_removed")]
+ public int MembersRemoved { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log action.
+ /// </summary>
+ internal sealed class AuditLogAction
+ {
+ /// <summary>
+ /// Gets or sets the target id.
+ /// </summary>
+ [JsonProperty("target_id")]
+ public ulong? TargetId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ [JsonProperty("user_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong UserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the action type.
+ /// </summary>
+ [JsonProperty("action_type", NullValueHandling = NullValueHandling.Ignore)]
+ public AuditLogActionType ActionType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the changes.
+ /// </summary>
+ [JsonProperty("changes")]
+ public IEnumerable<AuditLogActionChange> Changes { get; set; }
+
+ /// <summary>
+ /// Gets or sets the options.
+ /// </summary>
+ [JsonProperty("options")]
+ public AuditLogActionOptions Options { get; set; }
+
+ /// <summary>
+ /// Gets or sets the reason.
+ /// </summary>
+ [JsonProperty("reason")]
+ public string Reason { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a audit log.
+ /// </summary>
+ internal sealed class AuditLog
+ {
+ /// <summary>
+ /// Gets or sets the webhooks.
+ /// </summary>
+ [JsonProperty("webhooks")]
+ public IEnumerable<AuditLogWebhook> Webhooks { get; set; }
+
+ /// <summary>
+ /// Gets or sets the users.
+ /// </summary>
+ [JsonProperty("users")]
+ public IEnumerable<AuditLogUser> Users { get; set; }
+
+ /// <summary>
+ /// Gets or sets the entries.
+ /// </summary>
+ [JsonProperty("audit_log_entries")]
+ public IEnumerable<AuditLogAction> Entries { get; set; }
+
+ /// <summary>
+ /// Gets or sets the scheduled events.
+ /// </summary>
+ [JsonProperty("guild_scheduled_events")]
+ public IEnumerable<AuditLogGuildScheduledEvent> ScheduledEvents { get; set; }
+
+ /// <summary>
+ /// Gets or sets the threads.
+ /// </summary>
+ [JsonProperty("threads")]
+ public IEnumerable<AuditLogThread> Threads { get; set; }
+
+ /// <summary>
+ /// Gets or sets the integrations.
+ /// Twitch related.
+ /// </summary>
+ [JsonProperty("integrations")]
+ public IEnumerable<AuditLogIntegration> Integrations { get; set; }
+
+ /*
/// <summary>
/// Gets or sets the application commands.
/// Related to Permissions V2.
/// </summary>
[JsonProperty("application_commands")]
public IEnumerable<AuditLogApplicationCommand> ApplicationCommands { get; set; }
*/
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/ClientProperties.cs b/DisCatSharp/Net/Abstractions/ClientProperties.cs
index f330c668d..5d4d97566 100644
--- a/DisCatSharp/Net/Abstractions/ClientProperties.cs
+++ b/DisCatSharp/Net/Abstractions/ClientProperties.cs
@@ -1,113 +1,114 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Reflection;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents data for identify payload's client properties.
-/// </summary>
-internal sealed class ClientProperties
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the discord client.
- /// </summary>
- [JsonIgnore]
- public BaseDiscordClient Discord { get; set; }
-
- /// <summary>
- /// Gets the client's operating system.
+ /// Represents data for identify payload's client properties.
/// </summary>
- [JsonProperty("$os")]
- public string OperatingSystem
+ internal sealed class ClientProperties
{
- get
+ /// <summary>
+ /// Gets or sets the discord client.
+ /// </summary>
+ [JsonIgnore]
+ public BaseDiscordClient Discord { get; set; }
+
+ /// <summary>
+ /// Gets the client's operating system.
+ /// </summary>
+ [JsonProperty("$os")]
+ public string OperatingSystem
{
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
- return "windows";
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
- return "linux";
- else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
- return "osx";
+ get
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ return "windows";
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ return "linux";
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ return "osx";
- var plat = RuntimeInformation.OSDescription.ToLowerInvariant();
- if (plat.Contains("freebsd"))
- return "freebsd";
- else if (plat.Contains("openbsd"))
- return "openbsd";
- else if (plat.Contains("netbsd"))
- return "netbsd";
- else if (plat.Contains("dragonfly"))
- return "dragonflybsd";
- else if (plat.Contains("miros bsd") || plat.Contains("mirbsd"))
- return "miros bsd";
- else if (plat.Contains("desktopbsd"))
- return "desktopbsd";
- else return plat.Contains("darwin") ? "osx" : plat.Contains("unix") ? "unix" : "toaster (unknown)";
+ var plat = RuntimeInformation.OSDescription.ToLowerInvariant();
+ if (plat.Contains("freebsd"))
+ return "freebsd";
+ else if (plat.Contains("openbsd"))
+ return "openbsd";
+ else if (plat.Contains("netbsd"))
+ return "netbsd";
+ else if (plat.Contains("dragonfly"))
+ return "dragonflybsd";
+ else if (plat.Contains("miros bsd") || plat.Contains("mirbsd"))
+ return "miros bsd";
+ else if (plat.Contains("desktopbsd"))
+ return "desktopbsd";
+ else return plat.Contains("darwin") ? "osx" : plat.Contains("unix") ? "unix" : "toaster (unknown)";
+ }
}
- }
- /// <summary>
- /// Gets the client's browser.
- /// </summary>
- [JsonProperty("$browser")]
- public string Browser
- {
- get
+ /// <summary>
+ /// Gets the client's browser.
+ /// </summary>
+ [JsonProperty("$browser")]
+ public string Browser
{
- if (this.Discord.Configuration.MobileStatus)
- return "Discord Android";
-
- else
+ get
{
- var a = typeof(DiscordClient).GetTypeInfo().Assembly;
- var an = a.GetName();
- return $"DisCatSharp {an.Version.ToString(4)}";
+ if (this.Discord.Configuration.MobileStatus)
+ return "Discord Android";
+
+ else
+ {
+ var a = typeof(DiscordClient).GetTypeInfo().Assembly;
+ var an = a.GetName();
+ return $"DisCatSharp {an.Version.ToString(4)}";
+ }
}
}
- }
- /// <summary>
- /// Gets the client's device.
- /// </summary>
- [JsonProperty("$device")]
- public string Device
- => this.Browser;
+ /// <summary>
+ /// Gets the client's device.
+ /// </summary>
+ [JsonProperty("$device")]
+ public string Device
+ => this.Browser;
- /// <summary>
- /// Gets the client's referrer.
- /// </summary>
- [JsonProperty("$referrer")]
- public string Referrer
- => "";
+ /// <summary>
+ /// Gets the client's referrer.
+ /// </summary>
+ [JsonProperty("$referrer")]
+ public string Referrer
+ => "";
- /// <summary>
- /// Gets the client's referring domain.
- /// </summary>
- [JsonProperty("$referring_domain")]
- public string ReferringDomain
- => "";
+ /// <summary>
+ /// Gets the client's referring domain.
+ /// </summary>
+ [JsonProperty("$referring_domain")]
+ public string ReferringDomain
+ => "";
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/FollowedChannelAddPayload.cs b/DisCatSharp/Net/Abstractions/FollowedChannelAddPayload.cs
index 82644bafc..95a6f2511 100644
--- a/DisCatSharp/Net/Abstractions/FollowedChannelAddPayload.cs
+++ b/DisCatSharp/Net/Abstractions/FollowedChannelAddPayload.cs
@@ -1,37 +1,38 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a followed channel add payload.
-/// </summary>
-internal sealed class FollowedChannelAddPayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the webhook channel id.
+ /// Represents a followed channel add payload.
/// </summary>
- [JsonProperty("webhook_channel_id")]
- public ulong WebhookChannelId { get; set; }
+ internal sealed class FollowedChannelAddPayload
+ {
+ /// <summary>
+ /// Gets or sets the webhook channel id.
+ /// </summary>
+ [JsonProperty("webhook_channel_id")]
+ public ulong WebhookChannelId { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Gateway/GatewayHello.cs b/DisCatSharp/Net/Abstractions/Gateway/GatewayHello.cs
index 084507917..e0cd0432b 100644
--- a/DisCatSharp/Net/Abstractions/Gateway/GatewayHello.cs
+++ b/DisCatSharp/Net/Abstractions/Gateway/GatewayHello.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents data for a websocket hello payload.
-/// </summary>
-internal sealed class GatewayHello
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets the target heartbeat interval (in milliseconds) requested by Discord.
+ /// Represents data for a websocket hello payload.
/// </summary>
- [JsonProperty("heartbeat_interval")]
- public int HeartbeatInterval { get; private set; }
+ internal sealed class GatewayHello
+ {
+ /// <summary>
+ /// Gets the target heartbeat interval (in milliseconds) requested by Discord.
+ /// </summary>
+ [JsonProperty("heartbeat_interval")]
+ public int HeartbeatInterval { get; private set; }
- /// <summary>
- /// Gets debug data sent by Discord. This contains a list of servers to which the client is connected.
- /// </summary>
- [JsonProperty("_trace")]
- public IReadOnlyList<string> Trace { get; private set; }
+ /// <summary>
+ /// Gets debug data sent by Discord. This contains a list of servers to which the client is connected.
+ /// </summary>
+ [JsonProperty("_trace")]
+ public IReadOnlyList<string> Trace { get; private set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Gateway/GatewayIdentifyResume.cs b/DisCatSharp/Net/Abstractions/Gateway/GatewayIdentifyResume.cs
index 0b910d7a0..d10901c23 100644
--- a/DisCatSharp/Net/Abstractions/Gateway/GatewayIdentifyResume.cs
+++ b/DisCatSharp/Net/Abstractions/Gateway/GatewayIdentifyResume.cs
@@ -1,104 +1,105 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents data for websocket identify payload.
-/// </summary>
-internal sealed class GatewayIdentify
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the discord client.
+ /// Represents data for websocket identify payload.
/// </summary>
- [JsonIgnore]
- public BaseDiscordClient Discord { get; set; }
+ internal sealed class GatewayIdentify
+ {
+ /// <summary>
+ /// Gets or sets the discord client.
+ /// </summary>
+ [JsonIgnore]
+ public BaseDiscordClient Discord { get; set; }
- /// <summary>
- /// Gets or sets the token used to identify the client to Discord.
- /// </summary>
- [JsonProperty("token")]
- public string Token { get; set; }
+ /// <summary>
+ /// Gets or sets the token used to identify the client to Discord.
+ /// </summary>
+ [JsonProperty("token")]
+ public string Token { get; set; }
- /// <summary>
- /// Gets or sets the client's properties.
- /// </summary>
- [JsonProperty("properties")]
- public ClientProperties ClientProperties =>
- new() { Discord = this.Discord };
+ /// <summary>
+ /// Gets or sets the client's properties.
+ /// </summary>
+ [JsonProperty("properties")]
+ public ClientProperties ClientProperties =>
+ new() { Discord = this.Discord };
- /// <summary>
- /// Gets or sets whether to encrypt websocket traffic.
- /// </summary>
- [JsonProperty("compress")]
- public bool Compress { get; set; }
+ /// <summary>
+ /// Gets or sets whether to encrypt websocket traffic.
+ /// </summary>
+ [JsonProperty("compress")]
+ public bool Compress { get; set; }
- /// <summary>
- /// Gets or sets the member count at which the guild is to be considered large.
- /// </summary>
- [JsonProperty("large_threshold")]
- public int LargeThreshold { get; set; }
+ /// <summary>
+ /// Gets or sets the member count at which the guild is to be considered large.
+ /// </summary>
+ [JsonProperty("large_threshold")]
+ public int LargeThreshold { get; set; }
- /// <summary>
- /// Gets or sets the shard info for this connection.
- /// </summary>
- [JsonProperty("shard")]
- public ShardInfo ShardInfo { get; set; }
+ /// <summary>
+ /// Gets or sets the shard info for this connection.
+ /// </summary>
+ [JsonProperty("shard")]
+ public ShardInfo ShardInfo { get; set; }
- /// <summary>
- /// Gets or sets the presence for this connection.
- /// </summary>
- [JsonProperty("presence", NullValueHandling = NullValueHandling.Ignore)]
- public StatusUpdate Presence { get; set; }
+ /// <summary>
+ /// Gets or sets the presence for this connection.
+ /// </summary>
+ [JsonProperty("presence", NullValueHandling = NullValueHandling.Ignore)]
+ public StatusUpdate Presence { get; set; }
- /// <summary>
- /// Gets or sets the intent flags for this connection.
- /// </summary>
- [JsonProperty("intents")]
- public DiscordIntents Intents { get; set; }
-}
+ /// <summary>
+ /// Gets or sets the intent flags for this connection.
+ /// </summary>
+ [JsonProperty("intents")]
+ public DiscordIntents Intents { get; set; }
+ }
-/// <summary>
-/// Represents data for websocket identify payload.
-/// </summary>
-internal sealed class GatewayResume
-{
/// <summary>
- /// Gets or sets the token used to identify the client to Discord.
+ /// Represents data for websocket identify payload.
/// </summary>
- [JsonProperty("token")]
- public string Token { get; set; }
+ internal sealed class GatewayResume
+ {
+ /// <summary>
+ /// Gets or sets the token used to identify the client to Discord.
+ /// </summary>
+ [JsonProperty("token")]
+ public string Token { get; set; }
- /// <summary>
- /// Gets or sets the session id used to resume last session.
- /// </summary>
- [JsonProperty("session_id")]
- public string SessionId { get; set; }
+ /// <summary>
+ /// Gets or sets the session id used to resume last session.
+ /// </summary>
+ [JsonProperty("session_id")]
+ public string SessionId { get; set; }
- /// <summary>
- /// Gets or sets the last received sequence number.
- /// </summary>
- [JsonProperty("seq")]
- public long SequenceNumber { get; set; }
+ /// <summary>
+ /// Gets or sets the last received sequence number.
+ /// </summary>
+ [JsonProperty("seq")]
+ public long SequenceNumber { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Gateway/GatewayInfo.cs b/DisCatSharp/Net/Abstractions/Gateway/GatewayInfo.cs
index dd061a7d8..0e89be600 100644
--- a/DisCatSharp/Net/Abstractions/Gateway/GatewayInfo.cs
+++ b/DisCatSharp/Net/Abstractions/Gateway/GatewayInfo.cs
@@ -1,49 +1,50 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents information used to identify with Discord.
-/// </summary>
-public sealed class GatewayInfo
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets the gateway URL for the WebSocket connection.
+ /// Represents information used to identify with Discord.
/// </summary>
- [JsonProperty("url")]
- public string Url { get; set; }
+ public sealed class GatewayInfo
+ {
+ /// <summary>
+ /// Gets the gateway URL for the WebSocket connection.
+ /// </summary>
+ [JsonProperty("url")]
+ public string Url { get; set; }
- /// <summary>
- /// Gets the recommended amount of shards.
- /// </summary>
- [JsonProperty("shards")]
- public int ShardCount { get; internal set; }
+ /// <summary>
+ /// Gets the recommended amount of shards.
+ /// </summary>
+ [JsonProperty("shards")]
+ public int ShardCount { get; internal set; }
- /// <summary>
- /// Gets the session start limit data.
- /// </summary>
- [JsonProperty("session_start_limit")]
- public SessionBucket SessionBucket { get; internal set; }
+ /// <summary>
+ /// Gets the session start limit data.
+ /// </summary>
+ [JsonProperty("session_start_limit")]
+ public SessionBucket SessionBucket { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Gateway/GatewayOpCode.cs b/DisCatSharp/Net/Abstractions/Gateway/GatewayOpCode.cs
index a151afd47..2f8867f14 100644
--- a/DisCatSharp/Net/Abstractions/Gateway/GatewayOpCode.cs
+++ b/DisCatSharp/Net/Abstractions/Gateway/GatewayOpCode.cs
@@ -1,94 +1,95 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Specifies an OP code in a gateway payload.
-/// </summary>
-internal enum GatewayOpCode : int
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Used for dispatching events.
+ /// Specifies an OP code in a gateway payload.
/// </summary>
- Dispatch = 0,
+ internal enum GatewayOpCode : int
+ {
+ /// <summary>
+ /// Used for dispatching events.
+ /// </summary>
+ Dispatch = 0,
- /// <summary>
- /// Used for pinging the gateway or client, to ensure the connection is still alive.
- /// </summary>
- Heartbeat = 1,
+ /// <summary>
+ /// Used for pinging the gateway or client, to ensure the connection is still alive.
+ /// </summary>
+ Heartbeat = 1,
- /// <summary>
- /// Used for initial handshake with the gateway.
- /// </summary>
- Identify = 2,
+ /// <summary>
+ /// Used for initial handshake with the gateway.
+ /// </summary>
+ Identify = 2,
- /// <summary>
- /// Used to update client status.
- /// </summary>
- StatusUpdate = 3,
+ /// <summary>
+ /// Used to update client status.
+ /// </summary>
+ StatusUpdate = 3,
- /// <summary>
- /// Used to update voice state, when joining, leaving, or moving between voice channels.
- /// </summary>
- VoiceStateUpdate = 4,
+ /// <summary>
+ /// Used to update voice state, when joining, leaving, or moving between voice channels.
+ /// </summary>
+ VoiceStateUpdate = 4,
- /// <summary>
- /// Used for pinging the voice gateway or client, to ensure the connection is still alive.
- /// </summary>
- VoiceServerPing = 5,
+ /// <summary>
+ /// Used for pinging the voice gateway or client, to ensure the connection is still alive.
+ /// </summary>
+ VoiceServerPing = 5,
- /// <summary>
- /// Used to resume a closed connection.
- /// </summary>
- Resume = 6,
+ /// <summary>
+ /// Used to resume a closed connection.
+ /// </summary>
+ Resume = 6,
- /// <summary>
- /// Used to notify the client that it has to reconnect.
- /// </summary>
- Reconnect = 7,
+ /// <summary>
+ /// Used to notify the client that it has to reconnect.
+ /// </summary>
+ Reconnect = 7,
- /// <summary>
- /// Used to request guild members.
- /// </summary>
- RequestGuildMembers = 8,
+ /// <summary>
+ /// Used to request guild members.
+ /// </summary>
+ RequestGuildMembers = 8,
- /// <summary>
- /// Used to notify the client about an invalidated session.
- /// </summary>
- InvalidSession = 9,
+ /// <summary>
+ /// Used to notify the client about an invalidated session.
+ /// </summary>
+ InvalidSession = 9,
- /// <summary>
- /// Used by the gateway upon connecting.
- /// </summary>
- Hello = 10,
+ /// <summary>
+ /// Used by the gateway upon connecting.
+ /// </summary>
+ Hello = 10,
- /// <summary>
- /// Used to acknowledge a heartbeat.
- /// </summary>
- HeartbeatAck = 11,
+ /// <summary>
+ /// Used to acknowledge a heartbeat.
+ /// </summary>
+ HeartbeatAck = 11,
- /// <summary>
- /// Used to request guild synchronization.
- /// </summary>
- GuildSync = 12
+ /// <summary>
+ /// Used to request guild synchronization.
+ /// </summary>
+ GuildSync = 12
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Gateway/GatewayPayload.cs b/DisCatSharp/Net/Abstractions/Gateway/GatewayPayload.cs
index 636b3cc6d..8c1dd0903 100644
--- a/DisCatSharp/Net/Abstractions/Gateway/GatewayPayload.cs
+++ b/DisCatSharp/Net/Abstractions/Gateway/GatewayPayload.cs
@@ -1,55 +1,56 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a websocket payload exchanged between Discord and the client.
-/// </summary>
-internal sealed class GatewayPayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the OP code of the payload.
+ /// Represents a websocket payload exchanged between Discord and the client.
/// </summary>
- [JsonProperty("op")]
- public GatewayOpCode OpCode { get; set; }
+ internal sealed class GatewayPayload
+ {
+ /// <summary>
+ /// Gets or sets the OP code of the payload.
+ /// </summary>
+ [JsonProperty("op")]
+ public GatewayOpCode OpCode { get; set; }
- /// <summary>
- /// Gets or sets the data of the payload.
- /// </summary>
- [JsonProperty("d")]
- public object Data { get; set; }
+ /// <summary>
+ /// Gets or sets the data of the payload.
+ /// </summary>
+ [JsonProperty("d")]
+ public object Data { get; set; }
- /// <summary>
- /// Gets or sets the sequence number of the payload. Only present for OP 0.
- /// </summary>
- [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)]
- public int? Sequence { get; set; }
+ /// <summary>
+ /// Gets or sets the sequence number of the payload. Only present for OP 0.
+ /// </summary>
+ [JsonProperty("s", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Sequence { get; set; }
- /// <summary>
- /// Gets or sets the event name of the payload. Only present for OP 0.
- /// </summary>
- [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)]
- public string EventName { get; set; }
+ /// <summary>
+ /// Gets or sets the event name of the payload. Only present for OP 0.
+ /// </summary>
+ [JsonProperty("t", NullValueHandling = NullValueHandling.Ignore)]
+ public string EventName { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Gateway/GatewayRequestGuildMembers.cs b/DisCatSharp/Net/Abstractions/Gateway/GatewayRequestGuildMembers.cs
index cddc7e1cf..d5a4fb89f 100644
--- a/DisCatSharp/Net/Abstractions/Gateway/GatewayRequestGuildMembers.cs
+++ b/DisCatSharp/Net/Abstractions/Gateway/GatewayRequestGuildMembers.cs
@@ -1,80 +1,81 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Request guild members.
-/// </summary>
-internal sealed class GatewayRequestGuildMembers
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets the guild id.
+ /// Request guild members.
/// </summary>
- [JsonProperty("guild_id")]
- public ulong GuildId { get; }
+ internal sealed class GatewayRequestGuildMembers
+ {
+ /// <summary>
+ /// Gets the guild id.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; }
- /// <summary>
- /// Gets the query.
- /// </summary>
- [JsonProperty("query", NullValueHandling = NullValueHandling.Ignore)]
- public string Query { get; set; }
+ /// <summary>
+ /// Gets the query.
+ /// </summary>
+ [JsonProperty("query", NullValueHandling = NullValueHandling.Ignore)]
+ public string Query { get; set; }
- /// <summary>
- /// Gets the limit.
- /// </summary>
- [JsonProperty("limit")]
- public int Limit { get; set; }
+ /// <summary>
+ /// Gets the limit.
+ /// </summary>
+ [JsonProperty("limit")]
+ public int Limit { get; set; }
- /// <summary>
- /// Gets whether presences should be returned.
- /// </summary>
- [JsonProperty("presences", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Presences { get; set; }
+ /// <summary>
+ /// Gets whether presences should be returned.
+ /// </summary>
+ [JsonProperty("presences", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Presences { get; set; }
- /// <summary>
- /// Gets the user ids.
- /// </summary>
- [JsonProperty("user_ids", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<ulong> UserIds { get; set; }
+ /// <summary>
+ /// Gets the user ids.
+ /// </summary>
+ [JsonProperty("user_ids", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<ulong> UserIds { get; set; }
- /// <summary>
- /// Gets the nonce.
- /// </summary>
- [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)]
- public string Nonce { get; internal set; }
+ /// <summary>
+ /// Gets the nonce.
+ /// </summary>
+ [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)]
+ public string Nonce { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="GatewayRequestGuildMembers"/> class.
- /// </summary>
- /// <param name="guild">The guild.</param>
- public GatewayRequestGuildMembers(DiscordGuild guild)
- {
- this.GuildId = guild.Id;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GatewayRequestGuildMembers"/> class.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ public GatewayRequestGuildMembers(DiscordGuild guild)
+ {
+ this.GuildId = guild.Id;
+ }
}
}
diff --git a/DisCatSharp/Net/Abstractions/IOAuth2Payload.cs b/DisCatSharp/Net/Abstractions/IOAuth2Payload.cs
index 37fd25469..499147def 100644
--- a/DisCatSharp/Net/Abstractions/IOAuth2Payload.cs
+++ b/DisCatSharp/Net/Abstractions/IOAuth2Payload.cs
@@ -1,34 +1,35 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a OAuth2 payload.
-/// </summary>
-internal interface IOAuth2Payload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the access token.
+ /// Represents a OAuth2 payload.
/// </summary>
- string AccessToken { get; set; }
+ internal interface IOAuth2Payload
+ {
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ string AccessToken { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/ReadyPayload.cs b/DisCatSharp/Net/Abstractions/ReadyPayload.cs
index 94dd2a935..24d9301fe 100644
--- a/DisCatSharp/Net/Abstractions/ReadyPayload.cs
+++ b/DisCatSharp/Net/Abstractions/ReadyPayload.cs
@@ -1,65 +1,66 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents data for websocket ready event payload.
-/// </summary>
-internal class ReadyPayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets the gateway version the client is connected to.
+ /// Represents data for websocket ready event payload.
/// </summary>
- [JsonProperty("v")]
- public int GatewayVersion { get; private set; }
+ internal class ReadyPayload
+ {
+ /// <summary>
+ /// Gets the gateway version the client is connected to.
+ /// </summary>
+ [JsonProperty("v")]
+ public int GatewayVersion { get; private set; }
- /// <summary>
- /// Gets the current user.
- /// </summary>
- [JsonProperty("user")]
- public TransportUser CurrentUser { get; private set; }
+ /// <summary>
+ /// Gets the current user.
+ /// </summary>
+ [JsonProperty("user")]
+ public TransportUser CurrentUser { get; private set; }
- /// <summary>
- /// Gets the guilds available for this shard.
- /// </summary>
- [JsonProperty("guilds")]
- public IReadOnlyList<DiscordGuild> Guilds { get; private set; }
+ /// <summary>
+ /// Gets the guilds available for this shard.
+ /// </summary>
+ [JsonProperty("guilds")]
+ public IReadOnlyList<DiscordGuild> Guilds { get; private set; }
- /// <summary>
- /// Gets the current session's ID.
- /// </summary>
- [JsonProperty("session_id")]
- public string SessionId { get; private set; }
+ /// <summary>
+ /// Gets the current session's ID.
+ /// </summary>
+ [JsonProperty("session_id")]
+ public string SessionId { get; private set; }
- /// <summary>
- /// Gets debug data sent by Discord. This contains a list of servers to which the client is connected.
- /// </summary>
- [JsonProperty("_trace")]
- public IReadOnlyList<string> Trace { get; private set; }
+ /// <summary>
+ /// Gets debug data sent by Discord. This contains a list of servers to which the client is connected.
+ /// </summary>
+ [JsonProperty("_trace")]
+ public IReadOnlyList<string> Trace { get; private set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs
index 8bae38307..f073c2e14 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs
@@ -1,234 +1,235 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a application command create payload.
-/// </summary>
-internal class RestApplicationCommandCreatePayload
-{
- /// <summary>
- /// Gets the type.
- /// </summary>
- [JsonProperty("type")]
- public ApplicationCommandType Type { get; set; }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets the name localizations.
- /// </summary>
- [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<Dictionary<string, string>> NameLocalizations { get; set; }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; set; }
-
- /// <summary>
- /// Gets the description localizations.
- /// </summary>
- [JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<Dictionary<string, string>> DescriptionLocalizations { get; set; }
-
- /// <summary>
- /// Gets the options.
- /// </summary>
- [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordApplicationCommandOption> Options { get; set; }
-
- /// <summary>
- /// Whether the command is allowed for everyone.
- /// </summary>
- [JsonProperty("default_permission")]
- public bool DefaultPermission { get; set; } = true;
-
- /// <summary>
- /// The command needed permissions.
- /// </summary>
- [JsonProperty("default_member_permissions", NullValueHandling = NullValueHandling.Include)]
- public Permissions? DefaultMemberPermission { get; set; }
-
- /// <summary>
- /// Whether the command is allowed for dms.
- /// </summary>
- [JsonProperty("dm_permission", NullValueHandling = NullValueHandling.Include)]
- public bool? DmPermission { get; set; }
-}
-
-/// <summary>
-/// Represents a application command edit payload.
-/// </summary>
-internal class RestApplicationCommandEditPayload
-{
- /// <summary>
- /// Gets the name.
- /// </summary>
- [JsonProperty("name")]
- public Optional<string> Name { get; set; }
-
- /// <summary>
- /// Gets the name localizations.
- /// </summary>
- [JsonProperty("name_localizations")]
- public Optional<Dictionary<string, string>> NameLocalizations { get; set; }
-
- /// <summary>
- /// Gets the description.
- /// </summary>
- [JsonProperty("description")]
- public Optional<string> Description { get; set; }
-
- /// <summary>
- /// Gets the description localizations.
- /// </summary>
- [JsonProperty("description_localizations")]
- public Optional<Dictionary<string, string>> DescriptionLocalizations { get; set; }
-
- /// <summary>
- /// Gets the options.
- /// </summary>
- [JsonProperty("options")]
- public Optional<IReadOnlyCollection<DiscordApplicationCommandOption>> Options { get; set; }
-
- /// <summary>
- /// Gets the default permission.
- /// </summary>
- [JsonProperty("default_permission")]
- public Optional<bool> DefaultPermission { get; set; } = true;
-
- /// <summary>
- /// The command needed permissions.
- /// </summary>
- [JsonProperty("default_member_permissions", NullValueHandling = NullValueHandling.Include)]
- public Optional<Permissions> DefaultMemberPermission { get; set; }
-
- /// <summary>
- /// Whether the command is allowed for dms.
- /// </summary>
- [JsonProperty("dm_permission", NullValueHandling = NullValueHandling.Include)]
- public Optional<bool> DmPermission { get; set; }
-}
-
-/// <summary>
-/// Represents a interaction response payload.
-/// </summary>
-internal class RestInteractionResponsePayload
-{
- /// <summary>
- /// Gets the type.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public InteractionResponseType Type { get; set; }
-
- /// <summary>
- /// Gets the data.
- /// </summary>
- [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordInteractionApplicationCommandCallbackData Data { get; set; }
-
- /// <summary>
- /// Gets the attachments.
- /// </summary>
- [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
- public List<DiscordAttachment> Attachments { get; set; }
-}
-
-/// <summary>
-/// Represents a interaction response payload.
-/// </summary>
-internal class RestInteractionModalResponsePayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets the type.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public InteractionResponseType Type { get; set; }
-
- /// <summary>
- /// Gets the data.
- /// </summary>
- [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordInteractionApplicationCommandModalCallbackData Data { get; set; }
-}
-
-/// <summary>
-/// Represents a followup message create payload.
-/// </summary>
-internal class RestFollowupMessageCreatePayload
-{
- /// <summary>
- /// Gets the content.
- /// </summary>
- [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
- public string Content { get; set; }
-
- /// <summary>
- /// Get whether the message is tts.
- /// </summary>
- [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
- public bool? IsTts { get; set; }
-
- /// <summary>
- /// Gets the embeds.
- /// </summary>
- [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordEmbed> Embeds { get; set; }
-
- /// <summary>
- /// Gets the mentions.
- /// </summary>
- [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMentions Mentions { get; set; }
-
- /// <summary>
- /// Gets the flags.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public int? Flags { get; set; }
-
- /// <summary>
- /// Gets the components.
- /// </summary>
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<DiscordActionRowComponent> Components { get; set; }
-
- /// <summary>
- /// Gets attachments.
- /// </summary>
- [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
- public List<DiscordAttachment> Attachments { get; set; }
+ /// Represents a application command create payload.
+ /// </summary>
+ internal class RestApplicationCommandCreatePayload
+ {
+ /// <summary>
+ /// Gets the type.
+ /// </summary>
+ [JsonProperty("type")]
+ public ApplicationCommandType Type { get; set; }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets the name localizations.
+ /// </summary>
+ [JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<Dictionary<string, string>> NameLocalizations { get; set; }
+
+ /// <summary>
+ /// Gets the description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; set; }
+
+ /// <summary>
+ /// Gets the description localizations.
+ /// </summary>
+ [JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<Dictionary<string, string>> DescriptionLocalizations { get; set; }
+
+ /// <summary>
+ /// Gets the options.
+ /// </summary>
+ [JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordApplicationCommandOption> Options { get; set; }
+
+ /// <summary>
+ /// Whether the command is allowed for everyone.
+ /// </summary>
+ [JsonProperty("default_permission")]
+ public bool DefaultPermission { get; set; } = true;
+
+ /// <summary>
+ /// The command needed permissions.
+ /// </summary>
+ [JsonProperty("default_member_permissions", NullValueHandling = NullValueHandling.Include)]
+ public Permissions? DefaultMemberPermission { get; set; }
+
+ /// <summary>
+ /// Whether the command is allowed for dms.
+ /// </summary>
+ [JsonProperty("dm_permission", NullValueHandling = NullValueHandling.Include)]
+ public bool? DmPermission { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a application command edit payload.
+ /// </summary>
+ internal class RestApplicationCommandEditPayload
+ {
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public Optional<string> Name { get; set; }
+
+ /// <summary>
+ /// Gets the name localizations.
+ /// </summary>
+ [JsonProperty("name_localizations")]
+ public Optional<Dictionary<string, string>> NameLocalizations { get; set; }
+
+ /// <summary>
+ /// Gets the description.
+ /// </summary>
+ [JsonProperty("description")]
+ public Optional<string> Description { get; set; }
+
+ /// <summary>
+ /// Gets the description localizations.
+ /// </summary>
+ [JsonProperty("description_localizations")]
+ public Optional<Dictionary<string, string>> DescriptionLocalizations { get; set; }
+
+ /// <summary>
+ /// Gets the options.
+ /// </summary>
+ [JsonProperty("options")]
+ public Optional<IReadOnlyCollection<DiscordApplicationCommandOption>> Options { get; set; }
+
+ /// <summary>
+ /// Gets the default permission.
+ /// </summary>
+ [JsonProperty("default_permission")]
+ public Optional<bool> DefaultPermission { get; set; } = true;
+
+ /// <summary>
+ /// The command needed permissions.
+ /// </summary>
+ [JsonProperty("default_member_permissions", NullValueHandling = NullValueHandling.Include)]
+ public Optional<Permissions> DefaultMemberPermission { get; set; }
+
+ /// <summary>
+ /// Whether the command is allowed for dms.
+ /// </summary>
+ [JsonProperty("dm_permission", NullValueHandling = NullValueHandling.Include)]
+ public Optional<bool> DmPermission { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a interaction response payload.
+ /// </summary>
+ internal class RestInteractionResponsePayload
+ {
+ /// <summary>
+ /// Gets the type.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public InteractionResponseType Type { get; set; }
+
+ /// <summary>
+ /// Gets the data.
+ /// </summary>
+ [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordInteractionApplicationCommandCallbackData Data { get; set; }
+
+ /// <summary>
+ /// Gets the attachments.
+ /// </summary>
+ [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
+ public List<DiscordAttachment> Attachments { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a interaction response payload.
+ /// </summary>
+ internal class RestInteractionModalResponsePayload
+ {
+ /// <summary>
+ /// Gets the type.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public InteractionResponseType Type { get; set; }
+
+ /// <summary>
+ /// Gets the data.
+ /// </summary>
+ [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordInteractionApplicationCommandModalCallbackData Data { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a followup message create payload.
+ /// </summary>
+ internal class RestFollowupMessageCreatePayload
+ {
+ /// <summary>
+ /// Gets the content.
+ /// </summary>
+ [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+ public string Content { get; set; }
+
+ /// <summary>
+ /// Get whether the message is tts.
+ /// </summary>
+ [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsTts { get; set; }
+
+ /// <summary>
+ /// Gets the embeds.
+ /// </summary>
+ [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordEmbed> Embeds { get; set; }
+
+ /// <summary>
+ /// Gets the mentions.
+ /// </summary>
+ [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordMentions Mentions { get; set; }
+
+ /// <summary>
+ /// Gets the flags.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Flags { get; set; }
+
+ /// <summary>
+ /// Gets the components.
+ /// </summary>
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<DiscordActionRowComponent> Components { get; set; }
+
+ /// <summary>
+ /// Gets attachments.
+ /// </summary>
+ [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
+ public List<DiscordAttachment> Attachments { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs
index 4f81783b1..a23ad3828 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs
@@ -1,504 +1,505 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a channel create payload.
-/// </summary>
-internal sealed class RestChannelCreatePayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- [JsonProperty("type")]
- public ChannelType Type { get; set; }
-
- /// <summary>
- /// Gets or sets the parent.
- /// </summary>
- [JsonProperty("parent_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? Parent { get; set; }
-
- /// <summary>
- /// Gets or sets the topic.
- /// </summary>
- [JsonProperty("topic")]
- public Optional<string> Topic { get; set; }
-
- /// <summary>
- /// Gets or sets the bitrate.
- /// </summary>
- [JsonProperty("bitrate", NullValueHandling = NullValueHandling.Ignore)]
- public int? Bitrate { get; set; }
-
- /// <summary>
- /// Gets or sets the user limit.
- /// </summary>
- [JsonProperty("user_limit", NullValueHandling = NullValueHandling.Ignore)]
- public int? UserLimit { get; set; }
-
- /// <summary>
- /// Gets or sets the permission overwrites.
- /// </summary>
- [JsonProperty("permission_overwrites", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordRestOverwrite> PermissionOverwrites { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether nsfw.
- /// </summary>
- [JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Nsfw { get; set; }
-
- /// <summary>
- /// Gets or sets the per user rate limit.
- /// </summary>
- [JsonProperty("rate_limit_per_user")]
- public Optional<int?> PerUserRateLimit { get; set; }
-
- /// <summary>
- /// Gets or sets the quality mode.
- /// </summary>
- [JsonProperty("video_quality_mode", NullValueHandling = NullValueHandling.Ignore)]
- public VideoQualityMode? QualityMode { get; set; }
-
- /// <summary>
- /// Gets or sets the default auto archive duration.
- /// </summary>
- [JsonProperty("default_auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
- public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { get; set; }
-}
-
-/// <summary>
-/// Represents a channel modify payload.
-/// </summary>
-internal sealed class RestChannelModifyPayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- [JsonProperty("type")]
- public Optional<ChannelType> Type { get; set; }
-
- /// <summary>
- /// Gets or sets the position.
- /// </summary>
- [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
- public int? Position { get; set; }
-
- /// <summary>
- /// Gets or sets the topic.
- /// </summary>
- [JsonProperty("topic")]
- public Optional<string> Topic { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether nsfw.
- /// </summary>
- [JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Nsfw { get; set; }
-
- /// <summary>
- /// Gets or sets the parent.
- /// </summary>
- [JsonProperty("parent_id")]
- public Optional<ulong?> Parent { get; set; }
-
- /// <summary>
- /// Gets or sets the bitrate.
- /// </summary>
- [JsonProperty("bitrate", NullValueHandling = NullValueHandling.Ignore)]
- public int? Bitrate { get; set; }
-
- /// <summary>
- /// Gets or sets the user limit.
- /// </summary>
- [JsonProperty("user_limit", NullValueHandling = NullValueHandling.Ignore)]
- public int? UserLimit { get; set; }
-
- /// <summary>
- /// Gets or sets the per user rate limit.
- /// </summary>
- [JsonProperty("rate_limit_per_user")]
- public Optional<int?> PerUserRateLimit { get; set; }
-
- /// <summary>
- /// Gets or sets the rtc region.
- /// </summary>
- [JsonProperty("rtc_region")]
- public Optional<string> RtcRegion { get; set; }
-
- /// <summary>
- /// Gets or sets the quality mode.
- /// </summary>
- [JsonProperty("video_quality_mode", NullValueHandling = NullValueHandling.Ignore)]
- public VideoQualityMode? QualityMode { get; set; }
-
- /// <summary>
- /// Gets or sets the default auto archive duration.
- /// </summary>
- [JsonProperty("default_auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
- public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { get; set; }
-
- /// <summary>
- /// Gets or sets the permission overwrites.
- /// </summary>
- [JsonProperty("permission_overwrites", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordRestOverwrite> PermissionOverwrites { get; set; }
-
- /// <summary>
- /// Gets or sets the banner base64.
- /// </summary>
- [JsonProperty("banner")]
- public Optional<string> BannerBase64 { get; set; }
-}
-
-/// <summary>
-/// Represents a channel message edit payload.
-/// </summary>
-internal class RestChannelMessageEditPayload
-{
- /// <summary>
- /// Gets or sets the content.
- /// </summary>
- [JsonProperty("content", NullValueHandling = NullValueHandling.Include)]
- public string Content { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether has content.
- /// </summary>
- [JsonIgnore]
- public bool HasContent { get; set; }
-
- /// <summary>
- /// Gets or sets the embeds.
- /// </summary>
- [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordEmbed> Embeds { get; set; }
-
- /// <summary>
- /// Gets or sets the mentions.
- /// </summary>
- [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMentions Mentions { get; set; }
-
- /// <summary>
- /// Gets or sets the attachments.
- /// </summary>
- [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordAttachment> Attachments { get; set; }
-
- /// <summary>
- /// Gets or sets the flags.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public MessageFlags? Flags { get; set; }
-
- /// <summary>
- /// Gets or sets the components.
- /// </summary>
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection<DiscordActionRowComponent> Components { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether has embed.
- /// </summary>
- [JsonIgnore]
- public bool HasEmbed { get; set; }
-
- /// <summary>
- /// Should serialize the content.
- /// </summary>
- public bool ShouldSerializeContent()
- => this.HasContent;
-
- /// <summary>
- /// Should serialize the embed.
- /// </summary>
- public bool ShouldSerializeEmbed()
- => this.HasEmbed;
-}
-
-/// <summary>
-/// Represents a channel message create payload.
-/// </summary>
-internal sealed class RestChannelMessageCreatePayload : RestChannelMessageEditPayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets a value indicating whether t t is s.
- /// </summary>
- [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
- public bool? IsTts { get; set; }
-
- /// <summary>
- /// Gets or sets the stickers ids.
- /// </summary>
- [JsonProperty("sticker_ids", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<ulong> StickersIds { get; set; }
-
- /// <summary>
- /// Gets or sets the message reference.
- /// </summary>
- [JsonProperty("message_reference", NullValueHandling = NullValueHandling.Ignore)]
- public InternalDiscordMessageReference? MessageReference { get; set; }
-
-}
-
-/// <summary>
-/// Represents a channel message create multipart payload.
-/// </summary>
-internal sealed class RestChannelMessageCreateMultipartPayload
-{
- /// <summary>
- /// Gets or sets the content.
- /// </summary>
- [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
- public string Content { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether t t is s.
- /// </summary>
- [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
- public bool? IsTts { get; set; }
-
- /// <summary>
- /// Gets or sets the embeds.
- /// </summary>
- [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordEmbed> Embeds { get; set; }
-
- /// <summary>
- /// Gets or sets the mentions.
- /// </summary>
- [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMentions Mentions { get; set; }
-
- /// <summary>
- /// Gets or sets the message reference.
- /// </summary>
- [JsonProperty("message_reference", NullValueHandling = NullValueHandling.Ignore)]
- public InternalDiscordMessageReference? MessageReference { get; set; }
-}
-
-/// <summary>
-/// Represents a channel message bulk delete payload.
-/// </summary>
-internal sealed class RestChannelMessageBulkDeletePayload
-{
- /// <summary>
- /// Gets or sets the messages.
- /// </summary>
- [JsonProperty("messages", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<ulong> Messages { get; set; }
-}
-
-/// <summary>
-/// Represents a channel invite create payload.
-/// </summary>
-internal sealed class RestChannelInviteCreatePayload
-{
- /// <summary>
- /// Gets or sets the max age.
- /// </summary>
- [JsonProperty("max_age", NullValueHandling = NullValueHandling.Ignore)]
- public int MaxAge { get; set; }
-
- /// <summary>
- /// Gets or sets the max uses.
- /// </summary>
- [JsonProperty("max_uses", NullValueHandling = NullValueHandling.Ignore)]
- public int MaxUses { get; set; }
-
- /// <summary>
- /// Gets or sets the target type.
- /// </summary>
- [JsonProperty("target_type", NullValueHandling = NullValueHandling.Ignore)]
- public TargetType? TargetType { get; set; }
-
- /// <summary>
- /// Gets or sets the target application.
- /// </summary>
- [JsonProperty("target_application_id", NullValueHandling = NullValueHandling.Ignore)]
- public TargetActivity? TargetApplication { get; set; }
-
- /// <summary>
- /// Gets or sets the target user id.
- /// </summary>
- [JsonProperty("target_user_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? TargetUserId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether temporary.
- /// </summary>
- [JsonProperty("temporary", NullValueHandling = NullValueHandling.Ignore)]
- public bool Temporary { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether unique.
- /// </summary>
- [JsonProperty("unique", NullValueHandling = NullValueHandling.Ignore)]
- public bool Unique { get; set; }
-}
-
-/// <summary>
-/// Represents a channel permission edit payload.
-/// </summary>
-internal sealed class RestChannelPermissionEditPayload
-{
- /// <summary>
- /// Gets or sets the allow.
- /// </summary>
- [JsonProperty("allow", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions Allow { get; set; }
-
- /// <summary>
- /// Gets or sets the deny.
- /// </summary>
- [JsonProperty("deny", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions Deny { get; set; }
-
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public string Type { get; set; }
-}
-
-/// <summary>
-/// Represents a channel group dm recipient add payload.
-/// </summary>
-internal sealed class RestChannelGroupDmRecipientAddPayload : IOAuth2Payload
-{
- /// <summary>
- /// Gets or sets the access token.
- /// </summary>
- [JsonProperty("access_token")]
- public string AccessToken { get; set; }
-
- /// <summary>
- /// Gets or sets the nickname.
- /// </summary>
- [JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
- public string Nickname { get; set; }
-}
-
-/// <summary>
-/// The acknowledge payload.
-/// </summary>
-internal sealed class AcknowledgePayload
-{
- /// <summary>
- /// Gets or sets the token.
- /// </summary>
- [JsonProperty("token", NullValueHandling = NullValueHandling.Include)]
- public string Token { get; set; }
-}
-
-/// <summary>
-/// Represents a thread channel create payload.
-/// </summary>
-internal sealed class RestThreadChannelCreatePayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the auto archive duration.
- /// </summary>
- [JsonProperty("auto_archive_duration")]
- public ThreadAutoArchiveDuration AutoArchiveDuration { get; set; }
-
- /// <summary>
- /// Gets or sets the rate limit per user.
- /// </summary>
- [JsonProperty("rate_limit_per_user")]
- public int? PerUserRateLimit { get; set; }
-
- /// <summary>
- /// Gets or sets the thread type.
- /// </summary>
- [JsonProperty("type")]
- public ChannelType Type { get; set; }
-}
-
-/// <summary>
-/// Represents a thread channel modify payload.
-/// </summary>
-internal sealed class RestThreadChannelModifyPayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the archived.
- /// </summary>
- [JsonProperty("archived", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<bool?> Archived { get; set; }
-
- /// <summary>
- /// Gets or sets the auto archive duration.
- /// </summary>
- [JsonProperty("auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<ThreadAutoArchiveDuration?> AutoArchiveDuration { get; set; }
-
- /// <summary>
- /// Gets or sets the locked.
- /// </summary>
- [JsonProperty("locked", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<bool?> Locked { get; set; }
-
- /// <summary>
- /// Gets or sets the per user rate limit.
- /// </summary>
- [JsonProperty("rate_limit_per_user", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<int?> PerUserRateLimit { get; set; }
-
- /// <summary>
- /// Gets or sets the thread's invitable state.
- /// </summary>
- [JsonProperty("invitable", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<bool?> Invitable { internal get; set; }
+ /// Represents a channel create payload.
+ /// </summary>
+ internal sealed class RestChannelCreatePayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type.
+ /// </summary>
+ [JsonProperty("type")]
+ public ChannelType Type { get; set; }
+
+ /// <summary>
+ /// Gets or sets the parent.
+ /// </summary>
+ [JsonProperty("parent_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? Parent { get; set; }
+
+ /// <summary>
+ /// Gets or sets the topic.
+ /// </summary>
+ [JsonProperty("topic")]
+ public Optional<string> Topic { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bitrate.
+ /// </summary>
+ [JsonProperty("bitrate", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Bitrate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user limit.
+ /// </summary>
+ [JsonProperty("user_limit", NullValueHandling = NullValueHandling.Ignore)]
+ public int? UserLimit { get; set; }
+
+ /// <summary>
+ /// Gets or sets the permission overwrites.
+ /// </summary>
+ [JsonProperty("permission_overwrites", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordRestOverwrite> PermissionOverwrites { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether nsfw.
+ /// </summary>
+ [JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Nsfw { get; set; }
+
+ /// <summary>
+ /// Gets or sets the per user rate limit.
+ /// </summary>
+ [JsonProperty("rate_limit_per_user")]
+ public Optional<int?> PerUserRateLimit { get; set; }
+
+ /// <summary>
+ /// Gets or sets the quality mode.
+ /// </summary>
+ [JsonProperty("video_quality_mode", NullValueHandling = NullValueHandling.Ignore)]
+ public VideoQualityMode? QualityMode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default auto archive duration.
+ /// </summary>
+ [JsonProperty("default_auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
+ public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a channel modify payload.
+ /// </summary>
+ internal sealed class RestChannelModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type.
+ /// </summary>
+ [JsonProperty("type")]
+ public Optional<ChannelType> Type { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position.
+ /// </summary>
+ [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Position { get; set; }
+
+ /// <summary>
+ /// Gets or sets the topic.
+ /// </summary>
+ [JsonProperty("topic")]
+ public Optional<string> Topic { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether nsfw.
+ /// </summary>
+ [JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Nsfw { get; set; }
+
+ /// <summary>
+ /// Gets or sets the parent.
+ /// </summary>
+ [JsonProperty("parent_id")]
+ public Optional<ulong?> Parent { get; set; }
+
+ /// <summary>
+ /// Gets or sets the bitrate.
+ /// </summary>
+ [JsonProperty("bitrate", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Bitrate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user limit.
+ /// </summary>
+ [JsonProperty("user_limit", NullValueHandling = NullValueHandling.Ignore)]
+ public int? UserLimit { get; set; }
+
+ /// <summary>
+ /// Gets or sets the per user rate limit.
+ /// </summary>
+ [JsonProperty("rate_limit_per_user")]
+ public Optional<int?> PerUserRateLimit { get; set; }
+
+ /// <summary>
+ /// Gets or sets the rtc region.
+ /// </summary>
+ [JsonProperty("rtc_region")]
+ public Optional<string> RtcRegion { get; set; }
+
+ /// <summary>
+ /// Gets or sets the quality mode.
+ /// </summary>
+ [JsonProperty("video_quality_mode", NullValueHandling = NullValueHandling.Ignore)]
+ public VideoQualityMode? QualityMode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default auto archive duration.
+ /// </summary>
+ [JsonProperty("default_auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
+ public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the permission overwrites.
+ /// </summary>
+ [JsonProperty("permission_overwrites", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordRestOverwrite> PermissionOverwrites { get; set; }
+
+ /// <summary>
+ /// Gets or sets the banner base64.
+ /// </summary>
+ [JsonProperty("banner")]
+ public Optional<string> BannerBase64 { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a channel message edit payload.
+ /// </summary>
+ internal class RestChannelMessageEditPayload
+ {
+ /// <summary>
+ /// Gets or sets the content.
+ /// </summary>
+ [JsonProperty("content", NullValueHandling = NullValueHandling.Include)]
+ public string Content { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether has content.
+ /// </summary>
+ [JsonIgnore]
+ public bool HasContent { get; set; }
+
+ /// <summary>
+ /// Gets or sets the embeds.
+ /// </summary>
+ [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordEmbed> Embeds { get; set; }
+
+ /// <summary>
+ /// Gets or sets the mentions.
+ /// </summary>
+ [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordMentions Mentions { get; set; }
+
+ /// <summary>
+ /// Gets or sets the attachments.
+ /// </summary>
+ [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordAttachment> Attachments { get; set; }
+
+ /// <summary>
+ /// Gets or sets the flags.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public MessageFlags? Flags { get; set; }
+
+ /// <summary>
+ /// Gets or sets the components.
+ /// </summary>
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyCollection<DiscordActionRowComponent> Components { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether has embed.
+ /// </summary>
+ [JsonIgnore]
+ public bool HasEmbed { get; set; }
+
+ /// <summary>
+ /// Should serialize the content.
+ /// </summary>
+ public bool ShouldSerializeContent()
+ => this.HasContent;
+
+ /// <summary>
+ /// Should serialize the embed.
+ /// </summary>
+ public bool ShouldSerializeEmbed()
+ => this.HasEmbed;
+ }
+
+ /// <summary>
+ /// Represents a channel message create payload.
+ /// </summary>
+ internal sealed class RestChannelMessageCreatePayload : RestChannelMessageEditPayload
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether t t is s.
+ /// </summary>
+ [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsTts { get; set; }
+
+ /// <summary>
+ /// Gets or sets the stickers ids.
+ /// </summary>
+ [JsonProperty("sticker_ids", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<ulong> StickersIds { get; set; }
+
+ /// <summary>
+ /// Gets or sets the message reference.
+ /// </summary>
+ [JsonProperty("message_reference", NullValueHandling = NullValueHandling.Ignore)]
+ public InternalDiscordMessageReference? MessageReference { get; set; }
+
+ }
+
+ /// <summary>
+ /// Represents a channel message create multipart payload.
+ /// </summary>
+ internal sealed class RestChannelMessageCreateMultipartPayload
+ {
+ /// <summary>
+ /// Gets or sets the content.
+ /// </summary>
+ [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+ public string Content { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether t t is s.
+ /// </summary>
+ [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsTts { get; set; }
+
+ /// <summary>
+ /// Gets or sets the embeds.
+ /// </summary>
+ [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordEmbed> Embeds { get; set; }
+
+ /// <summary>
+ /// Gets or sets the mentions.
+ /// </summary>
+ [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordMentions Mentions { get; set; }
+
+ /// <summary>
+ /// Gets or sets the message reference.
+ /// </summary>
+ [JsonProperty("message_reference", NullValueHandling = NullValueHandling.Ignore)]
+ public InternalDiscordMessageReference? MessageReference { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a channel message bulk delete payload.
+ /// </summary>
+ internal sealed class RestChannelMessageBulkDeletePayload
+ {
+ /// <summary>
+ /// Gets or sets the messages.
+ /// </summary>
+ [JsonProperty("messages", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<ulong> Messages { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a channel invite create payload.
+ /// </summary>
+ internal sealed class RestChannelInviteCreatePayload
+ {
+ /// <summary>
+ /// Gets or sets the max age.
+ /// </summary>
+ [JsonProperty("max_age", NullValueHandling = NullValueHandling.Ignore)]
+ public int MaxAge { get; set; }
+
+ /// <summary>
+ /// Gets or sets the max uses.
+ /// </summary>
+ [JsonProperty("max_uses", NullValueHandling = NullValueHandling.Ignore)]
+ public int MaxUses { get; set; }
+
+ /// <summary>
+ /// Gets or sets the target type.
+ /// </summary>
+ [JsonProperty("target_type", NullValueHandling = NullValueHandling.Ignore)]
+ public TargetType? TargetType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the target application.
+ /// </summary>
+ [JsonProperty("target_application_id", NullValueHandling = NullValueHandling.Ignore)]
+ public TargetActivity? TargetApplication { get; set; }
+
+ /// <summary>
+ /// Gets or sets the target user id.
+ /// </summary>
+ [JsonProperty("target_user_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? TargetUserId { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether temporary.
+ /// </summary>
+ [JsonProperty("temporary", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Temporary { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether unique.
+ /// </summary>
+ [JsonProperty("unique", NullValueHandling = NullValueHandling.Ignore)]
+ public bool Unique { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a channel permission edit payload.
+ /// </summary>
+ internal sealed class RestChannelPermissionEditPayload
+ {
+ /// <summary>
+ /// Gets or sets the allow.
+ /// </summary>
+ [JsonProperty("allow", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions Allow { get; set; }
+
+ /// <summary>
+ /// Gets or sets the deny.
+ /// </summary>
+ [JsonProperty("deny", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions Deny { get; set; }
+
+ /// <summary>
+ /// Gets or sets the type.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public string Type { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a channel group dm recipient add payload.
+ /// </summary>
+ internal sealed class RestChannelGroupDmRecipientAddPayload : IOAuth2Payload
+ {
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ [JsonProperty("access_token")]
+ public string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the nickname.
+ /// </summary>
+ [JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
+ public string Nickname { get; set; }
+ }
+
+ /// <summary>
+ /// The acknowledge payload.
+ /// </summary>
+ internal sealed class AcknowledgePayload
+ {
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ [JsonProperty("token", NullValueHandling = NullValueHandling.Include)]
+ public string Token { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a thread channel create payload.
+ /// </summary>
+ internal sealed class RestThreadChannelCreatePayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the auto archive duration.
+ /// </summary>
+ [JsonProperty("auto_archive_duration")]
+ public ThreadAutoArchiveDuration AutoArchiveDuration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the rate limit per user.
+ /// </summary>
+ [JsonProperty("rate_limit_per_user")]
+ public int? PerUserRateLimit { get; set; }
+
+ /// <summary>
+ /// Gets or sets the thread type.
+ /// </summary>
+ [JsonProperty("type")]
+ public ChannelType Type { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a thread channel modify payload.
+ /// </summary>
+ internal sealed class RestThreadChannelModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the archived.
+ /// </summary>
+ [JsonProperty("archived", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<bool?> Archived { get; set; }
+
+ /// <summary>
+ /// Gets or sets the auto archive duration.
+ /// </summary>
+ [JsonProperty("auto_archive_duration", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<ThreadAutoArchiveDuration?> AutoArchiveDuration { get; set; }
+
+ /// <summary>
+ /// Gets or sets the locked.
+ /// </summary>
+ [JsonProperty("locked", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<bool?> Locked { get; set; }
+
+ /// <summary>
+ /// Gets or sets the per user rate limit.
+ /// </summary>
+ [JsonProperty("rate_limit_per_user", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<int?> PerUserRateLimit { get; set; }
+
+ /// <summary>
+ /// Gets or sets the thread's invitable state.
+ /// </summary>
+ [JsonProperty("invitable", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<bool?> Invitable { internal get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestGuildPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestGuildPayloads.cs
index 27141c1e2..e3003eb9a 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestGuildPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestGuildPayloads.cs
@@ -1,723 +1,724 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// The reason action.
-/// </summary>
-internal interface IReasonAction
-{
- /// <summary>
- /// Gets or sets the reason.
- /// </summary>
- string Reason { get; set; }
-
- //[JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)]
- //public string Reason { get; set; }
-}
-
-/// <summary>
-/// Represents a guild create payload.
-/// </summary>
-internal class RestGuildCreatePayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the region id.
- /// </summary>
- [JsonProperty("region", NullValueHandling = NullValueHandling.Ignore)]
- public string RegionId { get; set; }
-
- /// <summary>
- /// Gets or sets the icon base64.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
- public Optional<string> IconBase64 { get; set; }
-
- /// <summary>
- /// Gets or sets the verification level.
- /// </summary>
- [JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
- public VerificationLevel? VerificationLevel { get; set; }
-
- /// <summary>
- /// Gets or sets the default message notifications.
- /// </summary>
- [JsonProperty("default_message_notifications", NullValueHandling = NullValueHandling.Ignore)]
- public DefaultMessageNotifications? DefaultMessageNotifications { get; set; }
-
- /// <summary>
- /// Gets or sets the system channel flags.
- /// </summary>
- [JsonProperty("system_channel_flags", NullValueHandling = NullValueHandling.Ignore)]
- public SystemChannelFlags? SystemChannelFlags { get; set; }
-
- /// <summary>
- /// Gets or sets the roles.
- /// </summary>
- [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordRole> Roles { get; set; }
-
- /// <summary>
- /// Gets or sets the channels.
- /// </summary>
- [JsonProperty("channels", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<RestChannelCreatePayload> Channels { get; set; }
-}
-
-/// <summary>
-/// Represents a guild create from template payload.
-/// </summary>
-internal sealed class RestGuildCreateFromTemplatePayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the icon base64.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
- public Optional<string> IconBase64 { get; set; }
-}
-
-/// <summary>
-/// Represents a guild modify payload.
-/// </summary>
-internal sealed class RestGuildModifyPayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name")]
- public Optional<string> Name { get; set; }
-
- /// <summary>
- /// Gets or sets the icon base64.
- /// </summary>
- [JsonProperty("icon")]
- public Optional<string> IconBase64 { get; set; }
-
- /// <summary>
- /// Gets or sets the verification level.
- /// </summary>
- [JsonProperty("verification_level")]
- public Optional<VerificationLevel> VerificationLevel { get; set; }
-
- /// <summary>
- /// Gets or sets the default message notifications.
- /// </summary>
- [JsonProperty("default_message_notifications")]
- public Optional<DefaultMessageNotifications> DefaultMessageNotifications { get; set; }
-
- /// <summary>
- /// Gets or sets the owner id.
- /// </summary>
- [JsonProperty("owner_id")]
- public Optional<ulong> OwnerId { get; set; }
-
- /// <summary>
- /// Gets or sets the splash base64.
- /// </summary>
- [JsonProperty("splash")]
- public Optional<string> SplashBase64 { get; set; }
-
- /// <summary>
- /// Gets or sets the banner base64.
- /// </summary>
- [JsonProperty("banner")]
- public Optional<string> BannerBase64 { get; set; }
-
-
- /// <summary>
- /// Gets or sets the discovery splash base64.
- /// </summary>
- [JsonProperty("discovery_splash")]
- public Optional<string> DiscoverySplashBase64 { get; set; }
-
- /// <summary>
- /// Gets or sets the afk channel id.
- /// </summary>
- [JsonProperty("afk_channel_id")]
- public Optional<ulong?> AfkChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the afk timeout.
- /// </summary>
- [JsonProperty("afk_timeout")]
- public Optional<int> AfkTimeout { get; set; }
-
- /// <summary>
- /// Gets or sets the mfa level.
- /// </summary>
- [JsonProperty("mfa_level")]
- public Optional<MfaLevel> MfaLevel { get; set; }
-
- /// <summary>
- /// Gets or sets the explicit content filter.
- /// </summary>
- [JsonProperty("explicit_content_filter")]
- public Optional<ExplicitContentFilter> ExplicitContentFilter { get; set; }
-
- /// <summary>
- /// Gets or sets the system channel id.
- /// </summary>
- [JsonProperty("system_channel_id", NullValueHandling = NullValueHandling.Include)]
- public Optional<ulong?> SystemChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the system channel flags.
- /// </summary>
- [JsonProperty("system_channel_flags", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<SystemChannelFlags> SystemChannelFlags { get; set; }
-
- /// <summary>
- /// Gets or sets the rules channel id.
- /// </summary>
- [JsonProperty("rules_channel_id")]
- public Optional<ulong?> RulesChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the public updates channel id.
- /// </summary>
- [JsonProperty("public_updates_channel_id")]
- public Optional<ulong?> PublicUpdatesChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the preferred locale.
- /// </summary>
- [JsonProperty("preferred_locale")]
- public Optional<string> PreferredLocale { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
- public Optional<string> Description { get; set; }
-
- /// <summary>
- /// Gets or sets whether the premium progress bar should be enabled.
- /// </summary>
- [JsonProperty("premium_progress_bar_enabled", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<bool> PremiumProgressBarEnabled { get; set; }
-}
-
-/// <summary>
-/// Represents a guild community modify payload.
-/// </summary>
-internal sealed class RestGuildCommunityModifyPayload
-{
- /// <summary>
- /// Gets or sets the verification level.
- /// </summary>
- [JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<VerificationLevel> VerificationLevel { get; set; }
-
- /// <summary>
- /// Gets or sets the default message notifications.
- /// </summary>
- [JsonProperty("default_message_notifications", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<DefaultMessageNotifications> DefaultMessageNotifications { get; set; }
-
- /// <summary>
- /// Gets or sets the explicit content filter.
- /// </summary>
- [JsonProperty("explicit_content_filter", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<ExplicitContentFilter> ExplicitContentFilter { get; set; }
-
- /// <summary>
- /// Gets or sets the rules channel id.
- /// </summary>
- [JsonProperty("rules_channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<ulong?> RulesChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the public updates channel id.
- /// </summary>
- [JsonProperty("public_updates_channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<ulong?> PublicUpdatesChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the preferred locale.
- /// </summary>
- [JsonProperty("preferred_locale")]
- public Optional<string> PreferredLocale { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
- public Optional<string> Description { get; set; }
-
- /// <summary>
- /// Gets or sets the features.
- /// </summary>
- [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
- public List<string> Features { get; set; }
-}
-
-/// <summary>
-/// Represents a guild member add payload.
-/// </summary>
-internal sealed class RestGuildMemberAddPayload : IOAuth2Payload
-{
- /// <summary>
- /// Gets or sets the access token.
- /// </summary>
- [JsonProperty("access_token")]
- public string AccessToken { get; set; }
-
- /// <summary>
- /// Gets or sets the nickname.
- /// </summary>
- [JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
- public string Nickname { get; set; }
-
- /// <summary>
- /// Gets or sets the roles.
- /// </summary>
- [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordRole> Roles { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether mute.
- /// </summary>
- [JsonProperty("mute", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Mute { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether deaf.
- /// </summary>
- [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Deaf { get; set; }
-}
-
-/// <summary>
-/// Represents a guild channel reorder payload.
-/// </summary>
-internal sealed class RestGuildChannelReorderPayload
-{
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the position.
- /// </summary>
- [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
- public int Position { get; set; }
-}
-
-/// <summary>
-/// Represents a guild channel new parent payload.
-/// </summary>
-internal sealed class RestGuildChannelNewParentPayload
-{
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the position.
- /// </summary>
- [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
- public int Position { get; set; }
-
- /// <summary>
- /// Gets or sets the parent id.
- /// </summary>
- [JsonProperty("parent_id", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<ulong?> ParentId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether lock permissions.
- /// </summary>
- [JsonProperty("lock_permissions", NullValueHandling = NullValueHandling.Ignore)]
- public bool? LockPermissions { get; set; }
-}
-
-/// <summary>
-/// Represents a guild channel no parent payload.
-/// </summary>
-internal sealed class RestGuildChannelNoParentPayload
-{
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the position.
- /// </summary>
- [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
- public int Position { get; set; }
-
- /// <summary>
- /// Gets or sets the parent id.
- /// </summary>
- [JsonProperty("parent_id", NullValueHandling = NullValueHandling.Include)]
- public Optional<ulong?> ParentId { get; set; }
-}
-
-/// <summary>
-/// Represents a guild role reorder payload.
-/// </summary>
-internal sealed class RestGuildRoleReorderPayload
-{
- /// <summary>
- /// Gets or sets the role id.
- /// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong RoleId { get; set; }
-
- /// <summary>
- /// Gets or sets the position.
- /// </summary>
- [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
- public int Position { get; set; }
-}
-
-/// <summary>
-/// Represents a guild member modify payload.
-/// </summary>
-internal sealed class RestGuildMemberModifyPayload
-{
- /// <summary>
- /// Gets or sets the nickname.
- /// </summary>
- [JsonProperty("nick")]
- public Optional<string> Nickname { get; set; }
-
- /// <summary>
- /// Gets or sets the role ids.
- /// </summary>
- [JsonProperty("roles")]
- public Optional<IEnumerable<ulong>> RoleIds { get; set; }
-
- /// <summary>
- /// Gets or sets the mute.
- /// </summary>
- [JsonProperty("mute")]
- public Optional<bool> Mute { get; set; }
-
- /// <summary>
- /// Gets or sets the deafen.
- /// </summary>
- [JsonProperty("deaf")]
- public Optional<bool> Deafen { get; set; }
-
- /// <summary>
- /// Gets or sets the voice channel id.
- /// </summary>
- [JsonProperty("channel_id")]
- public Optional<ulong?> VoiceChannelId { get; set; }
-}
-
-/// <summary>
-/// Represents a guild member timeout modify payload.
-/// </summary>
-internal sealed class RestGuildMemberTimeoutModifyPayload
-{
- /// <summary>
- /// Gets or sets the date until the member can communicate again.
- /// </summary>
- [JsonProperty("communication_disabled_until")]
- public DateTimeOffset? CommunicationDisabledUntil { get; internal set; }
-}
-
-/// <summary>
-/// Represents a guild role payload.
-/// </summary>
-internal sealed class RestGuildRolePayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the permissions.
- /// </summary>
- [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions? Permissions { get; set; }
-
- /// <summary>
- /// Gets or sets the color.
- /// </summary>
- [JsonProperty("color", NullValueHandling = NullValueHandling.Ignore)]
- public int? Color { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether hoist.
- /// </summary>
- [JsonProperty("hoist", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Hoist { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether mentionable.
- /// </summary>
- [JsonProperty("mentionable", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Mentionable { get; set; }
-
- /// <summary>
- /// Gets or sets the icon base64.
- /// </summary>
- [JsonProperty("icon")]
- public Optional<string> IconBase64 { get; set; }
-
- /// <summary>
- /// Gets or sets the icon base64.
- /// </summary>
- [JsonProperty("unicode_emoji")]
- public Optional<string> UnicodeEmoji { get; set; }
-}
-
-/// <summary>
-/// Represents a guild prune result payload.
-/// </summary>
-internal sealed class RestGuildPruneResultPayload
-{
- /// <summary>
- /// Gets or sets the pruned.
- /// </summary>
- [JsonProperty("pruned", NullValueHandling = NullValueHandling.Ignore)]
- public int? Pruned { get; set; }
-}
-
-/// <summary>
-/// Represents a guild integration attach payload.
-/// </summary>
-internal sealed class RestGuildIntegrationAttachPayload
-{
- /// <summary>
- /// Gets or sets the type.
- /// </summary>
- [JsonProperty("type")]
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the id.
- /// </summary>
- [JsonProperty("id")]
- public ulong Id { get; set; }
-}
-
-/// <summary>
-/// Represents a guild integration modify payload.
-/// </summary>
-internal sealed class RestGuildIntegrationModifyPayload
-{
- /// <summary>
- /// Gets or sets the expire behavior.
- /// </summary>
- [JsonProperty("expire_behavior", NullValueHandling = NullValueHandling.Ignore)]
- public int? ExpireBehavior { get; set; }
-
- /// <summary>
- /// Gets or sets the expire grace period.
- /// </summary>
- [JsonProperty("expire_grace_period", NullValueHandling = NullValueHandling.Ignore)]
- public int? ExpireGracePeriod { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether enable emoticons.
- /// </summary>
- [JsonProperty("enable_emoticons", NullValueHandling = NullValueHandling.Ignore)]
- public bool? EnableEmoticons { get; set; }
-}
-
-/// <summary>
-/// Represents a guild emoji modify payload.
-/// </summary>
-internal class RestGuildEmojiModifyPayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the roles.
- /// </summary>
- [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
- public ulong[] Roles { get; set; }
-}
-
-/// <summary>
-/// Represents a guild emoji create payload.
-/// </summary>
-internal class RestGuildEmojiCreatePayload : RestGuildEmojiModifyPayload
-{
- /// <summary>
- /// Gets or sets the image b64.
- /// </summary>
- [JsonProperty("image")]
- public string ImageB64 { get; set; }
-}
-
-/// <summary>
-/// Represents a guild widget settings payload.
-/// </summary>
-internal class RestGuildWidgetSettingsPayload
-{
- /// <summary>
- /// Gets or sets a value indicating whether enabled.
- /// </summary>
- [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Enabled { get; set; }
-
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? ChannelId { get; set; }
-}
-
-/// <summary>
-/// Represents a guild template create or modify payload.
-/// </summary>
-internal class RestGuildTemplateCreateOrModifyPayload
-{
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Include)]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
- public string Description { get; set; }
-}
-
-/// <summary>
-/// Represents a guild membership screening form modify payload.
-/// </summary>
-internal class RestGuildMembershipScreeningFormModifyPayload
-{
- /// <summary>
- /// Gets or sets the enabled.
- /// </summary>
- [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<bool> Enabled { get; set; }
-
- /// <summary>
- /// Gets or sets the fields.
- /// </summary>
- [JsonProperty("form_fields", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<DiscordGuildMembershipScreeningField[]> Fields { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<string> Description { get; set; }
-}
-
-/// <summary>
-/// Represents a guild welcome screen modify payload.
-/// </summary>
-internal class RestGuildWelcomeScreenModifyPayload
-{
- /// <summary>
- /// Gets or sets the enabled.
- /// </summary>
- [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<bool> Enabled { get; set; }
-
- /// <summary>
- /// Gets or sets the welcome channels.
- /// </summary>
- [JsonProperty("welcome_channels", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<IEnumerable<DiscordGuildWelcomeScreenChannel>> WelcomeChannels { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<string> Description { get; set; }
-}
-
-/// <summary>
-/// Represents a guild update current user voice state payload.
-/// </summary>
-internal class RestGuildUpdateCurrentUserVoiceStatePayload
-{
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id")]
- public ulong ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether suppress.
- /// </summary>
- [JsonProperty("suppress", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Suppress { get; set; }
-
- /// <summary>
- /// Gets or sets the request to speak timestamp.
- /// </summary>
- [JsonProperty("request_to_speak_timestamp", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset? RequestToSpeakTimestamp { get; set; }
-}
-
-/// <summary>
-/// Represents a guild update user voice state payload.
-/// </summary>
-internal class RestGuildUpdateUserVoiceStatePayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id")]
- public ulong ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether suppress.
- /// </summary>
- [JsonProperty("suppress", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Suppress { get; set; }
+ /// The reason action.
+ /// </summary>
+ internal interface IReasonAction
+ {
+ /// <summary>
+ /// Gets or sets the reason.
+ /// </summary>
+ string Reason { get; set; }
+
+ //[JsonProperty("reason", NullValueHandling = NullValueHandling.Ignore)]
+ //public string Reason { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild create payload.
+ /// </summary>
+ internal class RestGuildCreatePayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the region id.
+ /// </summary>
+ [JsonProperty("region", NullValueHandling = NullValueHandling.Ignore)]
+ public string RegionId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the icon base64.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
+ public Optional<string> IconBase64 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the verification level.
+ /// </summary>
+ [JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
+ public VerificationLevel? VerificationLevel { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default message notifications.
+ /// </summary>
+ [JsonProperty("default_message_notifications", NullValueHandling = NullValueHandling.Ignore)]
+ public DefaultMessageNotifications? DefaultMessageNotifications { get; set; }
+
+ /// <summary>
+ /// Gets or sets the system channel flags.
+ /// </summary>
+ [JsonProperty("system_channel_flags", NullValueHandling = NullValueHandling.Ignore)]
+ public SystemChannelFlags? SystemChannelFlags { get; set; }
+
+ /// <summary>
+ /// Gets or sets the roles.
+ /// </summary>
+ [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordRole> Roles { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channels.
+ /// </summary>
+ [JsonProperty("channels", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<RestChannelCreatePayload> Channels { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild create from template payload.
+ /// </summary>
+ internal sealed class RestGuildCreateFromTemplatePayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the icon base64.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
+ public Optional<string> IconBase64 { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild modify payload.
+ /// </summary>
+ internal sealed class RestGuildModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public Optional<string> Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the icon base64.
+ /// </summary>
+ [JsonProperty("icon")]
+ public Optional<string> IconBase64 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the verification level.
+ /// </summary>
+ [JsonProperty("verification_level")]
+ public Optional<VerificationLevel> VerificationLevel { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default message notifications.
+ /// </summary>
+ [JsonProperty("default_message_notifications")]
+ public Optional<DefaultMessageNotifications> DefaultMessageNotifications { get; set; }
+
+ /// <summary>
+ /// Gets or sets the owner id.
+ /// </summary>
+ [JsonProperty("owner_id")]
+ public Optional<ulong> OwnerId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the splash base64.
+ /// </summary>
+ [JsonProperty("splash")]
+ public Optional<string> SplashBase64 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the banner base64.
+ /// </summary>
+ [JsonProperty("banner")]
+ public Optional<string> BannerBase64 { get; set; }
+
+
+ /// <summary>
+ /// Gets or sets the discovery splash base64.
+ /// </summary>
+ [JsonProperty("discovery_splash")]
+ public Optional<string> DiscoverySplashBase64 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the afk channel id.
+ /// </summary>
+ [JsonProperty("afk_channel_id")]
+ public Optional<ulong?> AfkChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the afk timeout.
+ /// </summary>
+ [JsonProperty("afk_timeout")]
+ public Optional<int> AfkTimeout { get; set; }
+
+ /// <summary>
+ /// Gets or sets the mfa level.
+ /// </summary>
+ [JsonProperty("mfa_level")]
+ public Optional<MfaLevel> MfaLevel { get; set; }
+
+ /// <summary>
+ /// Gets or sets the explicit content filter.
+ /// </summary>
+ [JsonProperty("explicit_content_filter")]
+ public Optional<ExplicitContentFilter> ExplicitContentFilter { get; set; }
+
+ /// <summary>
+ /// Gets or sets the system channel id.
+ /// </summary>
+ [JsonProperty("system_channel_id", NullValueHandling = NullValueHandling.Include)]
+ public Optional<ulong?> SystemChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the system channel flags.
+ /// </summary>
+ [JsonProperty("system_channel_flags", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<SystemChannelFlags> SystemChannelFlags { get; set; }
+
+ /// <summary>
+ /// Gets or sets the rules channel id.
+ /// </summary>
+ [JsonProperty("rules_channel_id")]
+ public Optional<ulong?> RulesChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the public updates channel id.
+ /// </summary>
+ [JsonProperty("public_updates_channel_id")]
+ public Optional<ulong?> PublicUpdatesChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the preferred locale.
+ /// </summary>
+ [JsonProperty("preferred_locale")]
+ public Optional<string> PreferredLocale { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
+ public Optional<string> Description { get; set; }
+
+ /// <summary>
+ /// Gets or sets whether the premium progress bar should be enabled.
+ /// </summary>
+ [JsonProperty("premium_progress_bar_enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<bool> PremiumProgressBarEnabled { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild community modify payload.
+ /// </summary>
+ internal sealed class RestGuildCommunityModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the verification level.
+ /// </summary>
+ [JsonProperty("verification_level", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<VerificationLevel> VerificationLevel { get; set; }
+
+ /// <summary>
+ /// Gets or sets the default message notifications.
+ /// </summary>
+ [JsonProperty("default_message_notifications", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<DefaultMessageNotifications> DefaultMessageNotifications { get; set; }
+
+ /// <summary>
+ /// Gets or sets the explicit content filter.
+ /// </summary>
+ [JsonProperty("explicit_content_filter", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<ExplicitContentFilter> ExplicitContentFilter { get; set; }
+
+ /// <summary>
+ /// Gets or sets the rules channel id.
+ /// </summary>
+ [JsonProperty("rules_channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<ulong?> RulesChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the public updates channel id.
+ /// </summary>
+ [JsonProperty("public_updates_channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<ulong?> PublicUpdatesChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the preferred locale.
+ /// </summary>
+ [JsonProperty("preferred_locale")]
+ public Optional<string> PreferredLocale { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
+ public Optional<string> Description { get; set; }
+
+ /// <summary>
+ /// Gets or sets the features.
+ /// </summary>
+ [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
+ public List<string> Features { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild member add payload.
+ /// </summary>
+ internal sealed class RestGuildMemberAddPayload : IOAuth2Payload
+ {
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ [JsonProperty("access_token")]
+ public string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the nickname.
+ /// </summary>
+ [JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
+ public string Nickname { get; set; }
+
+ /// <summary>
+ /// Gets or sets the roles.
+ /// </summary>
+ [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordRole> Roles { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether mute.
+ /// </summary>
+ [JsonProperty("mute", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Mute { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether deaf.
+ /// </summary>
+ [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Deaf { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild channel reorder payload.
+ /// </summary>
+ internal sealed class RestGuildChannelReorderPayload
+ {
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position.
+ /// </summary>
+ [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
+ public int Position { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild channel new parent payload.
+ /// </summary>
+ internal sealed class RestGuildChannelNewParentPayload
+ {
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position.
+ /// </summary>
+ [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
+ public int Position { get; set; }
+
+ /// <summary>
+ /// Gets or sets the parent id.
+ /// </summary>
+ [JsonProperty("parent_id", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<ulong?> ParentId { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether lock permissions.
+ /// </summary>
+ [JsonProperty("lock_permissions", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? LockPermissions { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild channel no parent payload.
+ /// </summary>
+ internal sealed class RestGuildChannelNoParentPayload
+ {
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position.
+ /// </summary>
+ [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
+ public int Position { get; set; }
+
+ /// <summary>
+ /// Gets or sets the parent id.
+ /// </summary>
+ [JsonProperty("parent_id", NullValueHandling = NullValueHandling.Include)]
+ public Optional<ulong?> ParentId { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild role reorder payload.
+ /// </summary>
+ internal sealed class RestGuildRoleReorderPayload
+ {
+ /// <summary>
+ /// Gets or sets the role id.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong RoleId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the position.
+ /// </summary>
+ [JsonProperty("position", NullValueHandling = NullValueHandling.Ignore)]
+ public int Position { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild member modify payload.
+ /// </summary>
+ internal sealed class RestGuildMemberModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the nickname.
+ /// </summary>
+ [JsonProperty("nick")]
+ public Optional<string> Nickname { get; set; }
+
+ /// <summary>
+ /// Gets or sets the role ids.
+ /// </summary>
+ [JsonProperty("roles")]
+ public Optional<IEnumerable<ulong>> RoleIds { get; set; }
+
+ /// <summary>
+ /// Gets or sets the mute.
+ /// </summary>
+ [JsonProperty("mute")]
+ public Optional<bool> Mute { get; set; }
+
+ /// <summary>
+ /// Gets or sets the deafen.
+ /// </summary>
+ [JsonProperty("deaf")]
+ public Optional<bool> Deafen { get; set; }
+
+ /// <summary>
+ /// Gets or sets the voice channel id.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public Optional<ulong?> VoiceChannelId { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild member timeout modify payload.
+ /// </summary>
+ internal sealed class RestGuildMemberTimeoutModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the date until the member can communicate again.
+ /// </summary>
+ [JsonProperty("communication_disabled_until")]
+ public DateTimeOffset? CommunicationDisabledUntil { get; internal set; }
+ }
+
+ /// <summary>
+ /// Represents a guild role payload.
+ /// </summary>
+ internal sealed class RestGuildRolePayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the permissions.
+ /// </summary>
+ [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions? Permissions { get; set; }
+
+ /// <summary>
+ /// Gets or sets the color.
+ /// </summary>
+ [JsonProperty("color", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Color { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether hoist.
+ /// </summary>
+ [JsonProperty("hoist", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Hoist { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether mentionable.
+ /// </summary>
+ [JsonProperty("mentionable", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Mentionable { get; set; }
+
+ /// <summary>
+ /// Gets or sets the icon base64.
+ /// </summary>
+ [JsonProperty("icon")]
+ public Optional<string> IconBase64 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the icon base64.
+ /// </summary>
+ [JsonProperty("unicode_emoji")]
+ public Optional<string> UnicodeEmoji { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild prune result payload.
+ /// </summary>
+ internal sealed class RestGuildPruneResultPayload
+ {
+ /// <summary>
+ /// Gets or sets the pruned.
+ /// </summary>
+ [JsonProperty("pruned", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Pruned { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild integration attach payload.
+ /// </summary>
+ internal sealed class RestGuildIntegrationAttachPayload
+ {
+ /// <summary>
+ /// Gets or sets the type.
+ /// </summary>
+ [JsonProperty("type")]
+ public string Type { get; set; }
+
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild integration modify payload.
+ /// </summary>
+ internal sealed class RestGuildIntegrationModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the expire behavior.
+ /// </summary>
+ [JsonProperty("expire_behavior", NullValueHandling = NullValueHandling.Ignore)]
+ public int? ExpireBehavior { get; set; }
+
+ /// <summary>
+ /// Gets or sets the expire grace period.
+ /// </summary>
+ [JsonProperty("expire_grace_period", NullValueHandling = NullValueHandling.Ignore)]
+ public int? ExpireGracePeriod { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether enable emoticons.
+ /// </summary>
+ [JsonProperty("enable_emoticons", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? EnableEmoticons { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild emoji modify payload.
+ /// </summary>
+ internal class RestGuildEmojiModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the roles.
+ /// </summary>
+ [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong[] Roles { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild emoji create payload.
+ /// </summary>
+ internal class RestGuildEmojiCreatePayload : RestGuildEmojiModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the image b64.
+ /// </summary>
+ [JsonProperty("image")]
+ public string ImageB64 { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild widget settings payload.
+ /// </summary>
+ internal class RestGuildWidgetSettingsPayload
+ {
+ /// <summary>
+ /// Gets or sets a value indicating whether enabled.
+ /// </summary>
+ [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Enabled { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? ChannelId { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild template create or modify payload.
+ /// </summary>
+ internal class RestGuildTemplateCreateOrModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Include)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
+ public string Description { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild membership screening form modify payload.
+ /// </summary>
+ internal class RestGuildMembershipScreeningFormModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the enabled.
+ /// </summary>
+ [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<bool> Enabled { get; set; }
+
+ /// <summary>
+ /// Gets or sets the fields.
+ /// </summary>
+ [JsonProperty("form_fields", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<DiscordGuildMembershipScreeningField[]> Fields { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<string> Description { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild welcome screen modify payload.
+ /// </summary>
+ internal class RestGuildWelcomeScreenModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the enabled.
+ /// </summary>
+ [JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<bool> Enabled { get; set; }
+
+ /// <summary>
+ /// Gets or sets the welcome channels.
+ /// </summary>
+ [JsonProperty("welcome_channels", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<IEnumerable<DiscordGuildWelcomeScreenChannel>> WelcomeChannels { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<string> Description { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild update current user voice state payload.
+ /// </summary>
+ internal class RestGuildUpdateCurrentUserVoiceStatePayload
+ {
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public ulong ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether suppress.
+ /// </summary>
+ [JsonProperty("suppress", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Suppress { get; set; }
+
+ /// <summary>
+ /// Gets or sets the request to speak timestamp.
+ /// </summary>
+ [JsonProperty("request_to_speak_timestamp", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset? RequestToSpeakTimestamp { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a guild update user voice state payload.
+ /// </summary>
+ internal class RestGuildUpdateUserVoiceStatePayload
+ {
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public ulong ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether suppress.
+ /// </summary>
+ [JsonProperty("suppress", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Suppress { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestGuildScheduledEventPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestGuildScheduledEventPayloads.cs
index 4a3b49e61..88d4f60ac 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestGuildScheduledEventPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestGuildScheduledEventPayloads.cs
@@ -1,149 +1,150 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// The rest guild scheduled event create payload.
-/// </summary>
-internal class RestGuildScheduledEventCreatePayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the entity metadata.
- /// </summary>
- [JsonProperty("entity_metadata", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordScheduledEventEntityMetadata EntityMetadata { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public string Description { get; set; }
-
- /// <summary>
- /// Gets or sets the privacy level of the scheduled event.
- /// </summary>
- [JsonProperty("privacy_level")]
- public ScheduledEventPrivacyLevel PrivacyLevel { get; set; }
-
- /// <summary>
- /// Gets or sets the time to schedule the scheduled event.
- /// </summary>
- [JsonProperty("scheduled_start_time")]
- public DateTimeOffset ScheduledStartTime { get; internal set; }
-
- /// <summary>
- /// Gets or sets the time when the scheduled event is scheduled to end.
- /// </summary>
- [JsonProperty("scheduled_end_time", NullValueHandling = NullValueHandling.Ignore)]
- public DateTimeOffset? ScheduledEndTime { get; internal set; }
-
- /// <summary>
- /// Gets or sets the entity type of the scheduled event.
- /// </summary>
- [JsonProperty("entity_type")]
- public ScheduledEventEntityType EntityType { get; set; }
-
- /// <summary>
- /// Gets or sets the image as base64.
- /// </summary>
- [JsonProperty("image", NullValueHandling = NullValueHandling.Include)]
- public Optional<string> CoverBase64 { get; set; }
-}
-
-/// <summary>
-/// The rest guild scheduled event modify payload.
-/// </summary>
-internal class RestGuildScheduledEventModifyPayload
-{
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id")]
- public Optional<ulong?> ChannelId { get; set; }
-
- /// <summary>
- /// Gets or sets the entity metadata.
- /// </summary>
- [JsonProperty("entity_metadata")]
- public Optional<DiscordScheduledEventEntityMetadata> EntityMetadata { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name")]
- public Optional<string> Name { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- [JsonProperty("description")]
- public Optional<string> Description { get; set; }
-
- /// <summary>
- /// Gets or sets the time to schedule the scheduled event.
- /// </summary>
- [JsonProperty("scheduled_start_time")]
- public Optional<DateTimeOffset> ScheduledStartTime { get; internal set; }
-
- /// <summary>
- /// Gets or sets the time when the scheduled event is scheduled to end.
- /// </summary>
- [JsonProperty("scheduled_end_time")]
- public Optional<DateTimeOffset> ScheduledEndTime { get; internal set; }
-
- /// <summary>
- /// Gets or sets the entity type of the scheduled event.
- /// </summary>
- [JsonProperty("entity_type")]
- public Optional<ScheduledEventEntityType> EntityType { get; set; }
-
- /// <summary>
- /// Gets or sets the cover image as base64.
- /// </summary>
- [JsonProperty("image")]
- public Optional<string> CoverBase64 { get; set; }
-
- /// <summary>
- /// Gets or sets the status of the scheduled event.
- /// </summary>
- [JsonProperty("status")]
- public Optional<ScheduledEventStatus> Status { get; set; }
+ /// The rest guild scheduled event create payload.
+ /// </summary>
+ internal class RestGuildScheduledEventCreatePayload
+ {
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the entity metadata.
+ /// </summary>
+ [JsonProperty("entity_metadata", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordScheduledEventEntityMetadata EntityMetadata { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string Description { get; set; }
+
+ /// <summary>
+ /// Gets or sets the privacy level of the scheduled event.
+ /// </summary>
+ [JsonProperty("privacy_level")]
+ public ScheduledEventPrivacyLevel PrivacyLevel { get; set; }
+
+ /// <summary>
+ /// Gets or sets the time to schedule the scheduled event.
+ /// </summary>
+ [JsonProperty("scheduled_start_time")]
+ public DateTimeOffset ScheduledStartTime { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets the time when the scheduled event is scheduled to end.
+ /// </summary>
+ [JsonProperty("scheduled_end_time", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset? ScheduledEndTime { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets the entity type of the scheduled event.
+ /// </summary>
+ [JsonProperty("entity_type")]
+ public ScheduledEventEntityType EntityType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the image as base64.
+ /// </summary>
+ [JsonProperty("image", NullValueHandling = NullValueHandling.Include)]
+ public Optional<string> CoverBase64 { get; set; }
+ }
+
+ /// <summary>
+ /// The rest guild scheduled event modify payload.
+ /// </summary>
+ internal class RestGuildScheduledEventModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public Optional<ulong?> ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the entity metadata.
+ /// </summary>
+ [JsonProperty("entity_metadata")]
+ public Optional<DiscordScheduledEventEntityMetadata> EntityMetadata { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public Optional<string> Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonProperty("description")]
+ public Optional<string> Description { get; set; }
+
+ /// <summary>
+ /// Gets or sets the time to schedule the scheduled event.
+ /// </summary>
+ [JsonProperty("scheduled_start_time")]
+ public Optional<DateTimeOffset> ScheduledStartTime { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets the time when the scheduled event is scheduled to end.
+ /// </summary>
+ [JsonProperty("scheduled_end_time")]
+ public Optional<DateTimeOffset> ScheduledEndTime { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets the entity type of the scheduled event.
+ /// </summary>
+ [JsonProperty("entity_type")]
+ public Optional<ScheduledEventEntityType> EntityType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the cover image as base64.
+ /// </summary>
+ [JsonProperty("image")]
+ public Optional<string> CoverBase64 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the status of the scheduled event.
+ /// </summary>
+ [JsonProperty("status")]
+ public Optional<ScheduledEventStatus> Status { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestStageInstancePayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestStageInstancePayloads.cs
index 55ca591ca..33aee1f67 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestStageInstancePayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestStageInstancePayloads.cs
@@ -1,75 +1,76 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a stage instance create payload.
-/// </summary>
-internal sealed class RestStageInstanceCreatePayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the channel id.
+ /// Represents a stage instance create payload.
/// </summary>
- [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong ChannelId { get; set; }
+ internal sealed class RestStageInstanceCreatePayload
+ {
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong ChannelId { get; set; }
- /// <summary>
- /// Gets or sets the topic.
- /// </summary>
- [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
- public string Topic { get; set; }
+ /// <summary>
+ /// Gets or sets the topic.
+ /// </summary>
+ [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
+ public string Topic { get; set; }
- /// <summary>
- /// Gets or sets the privacy level.
- /// </summary>
- [JsonProperty("privacy_level", NullValueHandling = NullValueHandling.Ignore)]
- public StagePrivacyLevel PrivacyLevel { get; set; }
+ /// <summary>
+ /// Gets or sets the privacy level.
+ /// </summary>
+ [JsonProperty("privacy_level", NullValueHandling = NullValueHandling.Ignore)]
+ public StagePrivacyLevel PrivacyLevel { get; set; }
- /// <summary>
- /// Whether everyone should be notified about the start.
- /// </summary>
- [JsonProperty("send_start_notification", NullValueHandling = NullValueHandling.Ignore)]
- public bool SendStartNotification { get; set; }
-}
+ /// <summary>
+ /// Whether everyone should be notified about the start.
+ /// </summary>
+ [JsonProperty("send_start_notification", NullValueHandling = NullValueHandling.Ignore)]
+ public bool SendStartNotification { get; set; }
+ }
-/// <summary>
-/// Represents a stage instance modify payload.
-/// </summary>
-internal sealed class RestStageInstanceModifyPayload
-{
/// <summary>
- /// Gets or sets the topic.
+ /// Represents a stage instance modify payload.
/// </summary>
- [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<string> Topic { get; set; }
+ internal sealed class RestStageInstanceModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the topic.
+ /// </summary>
+ [JsonProperty("topic", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<string> Topic { get; set; }
- /// <summary>
- /// Gets or sets the privacy level.
- /// </summary>
- [JsonProperty("privacy_level", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<StagePrivacyLevel> PrivacyLevel { get; set; }
+ /// <summary>
+ /// Gets or sets the privacy level.
+ /// </summary>
+ [JsonProperty("privacy_level", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<StagePrivacyLevel> PrivacyLevel { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestStickerPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestStickerPayloads.cs
index 712bcbf2f..100bad79e 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestStickerPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestStickerPayloads.cs
@@ -1,51 +1,52 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a sticker modify payload.
-/// </summary>
-internal class RestStickerModifyPayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the name.
+ /// Represents a sticker modify payload.
/// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<string> Name { get; set; }
+ internal class RestStickerModifyPayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<string> Name { get; set; }
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<string> Description { get; set; }
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<string> Description { get; set; }
- /// <summary>
- /// Gets or sets the tags.
- /// </summary>
- [JsonProperty("tags", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<string> Tags { get; set; }
+ /// <summary>
+ /// Gets or sets the tags.
+ /// </summary>
+ [JsonProperty("tags", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<string> Tags { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestUserPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestUserPayloads.cs
index 7cb21d499..693cc976a 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestUserPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestUserPayloads.cs
@@ -1,153 +1,154 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a user dm create payload.
-/// </summary>
-internal sealed class RestUserDmCreatePayload
-{
- /// <summary>
- /// Gets or sets the recipient.
- /// </summary>
- [JsonProperty("recipient_id")]
- public ulong Recipient { get; set; }
-}
-
-/// <summary>
-/// Represents a user group dm create payload.
-/// </summary>
-internal sealed class RestUserGroupDmCreatePayload
-{
- /// <summary>
- /// Gets or sets the access tokens.
- /// </summary>
- [JsonProperty("access_tokens")]
- public IEnumerable<string> AccessTokens { get; set; }
-
- /// <summary>
- /// Gets or sets the nicknames.
- /// </summary>
- [JsonProperty("nicks")]
- public IDictionary<ulong, string> Nicknames { get; set; }
-}
-
-/// <summary>
-/// Represents a user update current payload.
-/// </summary>
-internal sealed class RestUserUpdateCurrentPayload
-{
- /// <summary>
- /// Gets or sets the username.
- /// </summary>
- [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
- public string Username { get; set; }
-
- /// <summary>
- /// Gets or sets the avatar base64.
- /// </summary>
- [JsonProperty("avatar", NullValueHandling = NullValueHandling.Include)]
- public string AvatarBase64 { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether avatar set.
- /// </summary>
- [JsonIgnore]
- public bool AvatarSet { get; set; }
-
- /// <summary>
- /// Gets whether the avatar should be serialized.
- /// </summary>
- public bool ShouldSerializeAvatarBase64()
- => this.AvatarSet;
-}
-
-/// <summary>
-/// Represents a user guild.
-/// </summary>
-internal sealed class RestUserGuild
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets the id.
- /// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public ulong Id { get; set; }
-
- /// <summary>
- /// Gets the name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets the icon hash.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
- public string IconHash { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether is owner.
- /// </summary>
- [JsonProperty("owner", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsOwner { get; set; }
-
- /// <summary>
- /// Gets the permissions.
- /// </summary>
- [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
- public Permissions Permissions { get; set; }
-
- /// <summary>
- /// Gets the guild features.
- /// </summary>
- [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
- public List<string> Features { get; set; }
-}
-
-/// <summary>
-/// Represents a user guild list payload.
-/// </summary>
-internal sealed class RestUserGuildListPayload
-{
- /// <summary>
- /// Gets or sets the limit.
- /// </summary>
- [JsonProperty("limit", NullValueHandling = NullValueHandling.Ignore)]
- public int Limit { get; set; }
-
- /// <summary>
- /// Gets or sets the before.
- /// </summary>
- [JsonProperty("before", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? Before { get; set; }
-
- /// <summary>
- /// Gets or sets the after.
- /// </summary>
- [JsonProperty("after", NullValueHandling = NullValueHandling.Ignore)]
- public ulong? After { get; set; }
+ /// Represents a user dm create payload.
+ /// </summary>
+ internal sealed class RestUserDmCreatePayload
+ {
+ /// <summary>
+ /// Gets or sets the recipient.
+ /// </summary>
+ [JsonProperty("recipient_id")]
+ public ulong Recipient { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a user group dm create payload.
+ /// </summary>
+ internal sealed class RestUserGroupDmCreatePayload
+ {
+ /// <summary>
+ /// Gets or sets the access tokens.
+ /// </summary>
+ [JsonProperty("access_tokens")]
+ public IEnumerable<string> AccessTokens { get; set; }
+
+ /// <summary>
+ /// Gets or sets the nicknames.
+ /// </summary>
+ [JsonProperty("nicks")]
+ public IDictionary<ulong, string> Nicknames { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a user update current payload.
+ /// </summary>
+ internal sealed class RestUserUpdateCurrentPayload
+ {
+ /// <summary>
+ /// Gets or sets the username.
+ /// </summary>
+ [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
+ public string Username { get; set; }
+
+ /// <summary>
+ /// Gets or sets the avatar base64.
+ /// </summary>
+ [JsonProperty("avatar", NullValueHandling = NullValueHandling.Include)]
+ public string AvatarBase64 { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether avatar set.
+ /// </summary>
+ [JsonIgnore]
+ public bool AvatarSet { get; set; }
+
+ /// <summary>
+ /// Gets whether the avatar should be serialized.
+ /// </summary>
+ public bool ShouldSerializeAvatarBase64()
+ => this.AvatarSet;
+ }
+
+ /// <summary>
+ /// Represents a user guild.
+ /// </summary>
+ internal sealed class RestUserGuild
+ {
+ /// <summary>
+ /// Gets the id.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong Id { get; set; }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets the icon hash.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Ignore)]
+ public string IconHash { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether is owner.
+ /// </summary>
+ [JsonProperty("owner", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsOwner { get; set; }
+
+ /// <summary>
+ /// Gets the permissions.
+ /// </summary>
+ [JsonProperty("permissions", NullValueHandling = NullValueHandling.Ignore)]
+ public Permissions Permissions { get; set; }
+
+ /// <summary>
+ /// Gets the guild features.
+ /// </summary>
+ [JsonProperty("features", NullValueHandling = NullValueHandling.Ignore)]
+ public List<string> Features { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a user guild list payload.
+ /// </summary>
+ internal sealed class RestUserGuildListPayload
+ {
+ /// <summary>
+ /// Gets or sets the limit.
+ /// </summary>
+ [JsonProperty("limit", NullValueHandling = NullValueHandling.Ignore)]
+ public int Limit { get; set; }
+
+ /// <summary>
+ /// Gets or sets the before.
+ /// </summary>
+ [JsonProperty("before", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? Before { get; set; }
+
+ /// <summary>
+ /// Gets or sets the after.
+ /// </summary>
+ [JsonProperty("after", NullValueHandling = NullValueHandling.Ignore)]
+ public ulong? After { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestWebhookPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestWebhookPayloads.cs
index 007b3b864..0ad29519c 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestWebhookPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestWebhookPayloads.cs
@@ -1,155 +1,156 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a webhook payload.
-/// </summary>
-internal sealed class RestWebhookPayload
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name")]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the avatar base64.
- /// </summary>
- [JsonProperty("avatar", NullValueHandling = NullValueHandling.Include)]
- public string AvatarBase64 { get; set; }
-
- /// <summary>
- /// Gets or sets the channel id.
- /// </summary>
- [JsonProperty("channel_id")]
- public ulong ChannelId { get; set; }
-
- /// <summary>
- /// Gets whether an avatar is set.
- /// </summary>
- [JsonProperty]
- public bool AvatarSet { get; set; }
-
- /// <summary>
- /// Gets whether the avatar should be serialized.
- /// </summary>
- public bool ShouldSerializeAvatarBase64()
- => this.AvatarSet;
-}
-
-/// <summary>
-/// Represents a webhook execute payload.
-/// </summary>
-internal sealed class RestWebhookExecutePayload
-{
- /// <summary>
- /// Gets or sets the content.
- /// </summary>
- [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
- public string Content { get; set; }
-
- /// <summary>
- /// Gets or sets the username.
- /// </summary>
- [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
- public string Username { get; set; }
-
- /// <summary>
- /// Gets or sets the avatar url.
- /// </summary>
- [JsonProperty("avatar_url", NullValueHandling = NullValueHandling.Ignore)]
- public string AvatarUrl { get; set; }
-
- /// <summary>
- /// Whether this message is tts.
- /// </summary>
- [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
- public bool? IsTts { get; set; }
-
- /// <summary>
- /// Gets or sets the embeds.
- /// </summary>
- [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordEmbed> Embeds { get; set; }
-
- /// <summary>
- /// Gets or sets the mentions.
- /// </summary>
- [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMentions Mentions { get; set; }
-
- /// <summary>
- /// Gets or sets the components.
- /// </summary>
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordActionRowComponent> Components { get; set; }
-
- /// <summary>
- /// Gets or sets the attachments.
- /// </summary>
- [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
- public List<DiscordAttachment> Attachments { get; set; }
-}
-
-/// <summary>
-/// Represents a webhook message edit payload.
-/// </summary>
-internal sealed class RestWebhookMessageEditPayload
-{
- /// <summary>
- /// Gets or sets the content.
- /// </summary>
- [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
- public Optional<string> Content { get; set; }
-
- /// <summary>
- /// Gets or sets the embeds.
- /// </summary>
- [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordEmbed> Embeds { get; set; }
-
- /// <summary>
- /// Gets or sets the mentions.
- /// </summary>
- [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<IMention> Mentions { get; set; }
-
- /// <summary>
- /// Gets or sets the attachments.
- /// </summary>
- [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordAttachment> Attachments { get; set; }
-
- /// <summary>
- /// Gets or sets the components.
- /// </summary>
- [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable<DiscordActionRowComponent> Components { get; set; }
+ /// Represents a webhook payload.
+ /// </summary>
+ internal sealed class RestWebhookPayload
+ {
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the avatar base64.
+ /// </summary>
+ [JsonProperty("avatar", NullValueHandling = NullValueHandling.Include)]
+ public string AvatarBase64 { get; set; }
+
+ /// <summary>
+ /// Gets or sets the channel id.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public ulong ChannelId { get; set; }
+
+ /// <summary>
+ /// Gets whether an avatar is set.
+ /// </summary>
+ [JsonProperty]
+ public bool AvatarSet { get; set; }
+
+ /// <summary>
+ /// Gets whether the avatar should be serialized.
+ /// </summary>
+ public bool ShouldSerializeAvatarBase64()
+ => this.AvatarSet;
+ }
+
+ /// <summary>
+ /// Represents a webhook execute payload.
+ /// </summary>
+ internal sealed class RestWebhookExecutePayload
+ {
+ /// <summary>
+ /// Gets or sets the content.
+ /// </summary>
+ [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+ public string Content { get; set; }
+
+ /// <summary>
+ /// Gets or sets the username.
+ /// </summary>
+ [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
+ public string Username { get; set; }
+
+ /// <summary>
+ /// Gets or sets the avatar url.
+ /// </summary>
+ [JsonProperty("avatar_url", NullValueHandling = NullValueHandling.Ignore)]
+ public string AvatarUrl { get; set; }
+
+ /// <summary>
+ /// Whether this message is tts.
+ /// </summary>
+ [JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsTts { get; set; }
+
+ /// <summary>
+ /// Gets or sets the embeds.
+ /// </summary>
+ [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordEmbed> Embeds { get; set; }
+
+ /// <summary>
+ /// Gets or sets the mentions.
+ /// </summary>
+ [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordMentions Mentions { get; set; }
+
+ /// <summary>
+ /// Gets or sets the components.
+ /// </summary>
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordActionRowComponent> Components { get; set; }
+
+ /// <summary>
+ /// Gets or sets the attachments.
+ /// </summary>
+ [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
+ public List<DiscordAttachment> Attachments { get; set; }
+ }
+
+ /// <summary>
+ /// Represents a webhook message edit payload.
+ /// </summary>
+ internal sealed class RestWebhookMessageEditPayload
+ {
+ /// <summary>
+ /// Gets or sets the content.
+ /// </summary>
+ [JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
+ public Optional<string> Content { get; set; }
+
+ /// <summary>
+ /// Gets or sets the embeds.
+ /// </summary>
+ [JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordEmbed> Embeds { get; set; }
+
+ /// <summary>
+ /// Gets or sets the mentions.
+ /// </summary>
+ [JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<IMention> Mentions { get; set; }
+
+ /// <summary>
+ /// Gets or sets the attachments.
+ /// </summary>
+ [JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordAttachment> Attachments { get; set; }
+
+ /// <summary>
+ /// Gets or sets the components.
+ /// </summary>
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IEnumerable<DiscordActionRowComponent> Components { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/ShardInfo.cs b/DisCatSharp/Net/Abstractions/ShardInfo.cs
index c574798fb..8344dbf80 100644
--- a/DisCatSharp/Net/Abstractions/ShardInfo.cs
+++ b/DisCatSharp/Net/Abstractions/ShardInfo.cs
@@ -1,97 +1,98 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents data for identify payload's shard info.
-/// </summary>
-[JsonConverter(typeof(ShardInfoConverter))]
-internal sealed class ShardInfo
-{
- /// <summary>
- /// Gets or sets this client's shard id.
- /// </summary>
- public int ShardId { get; set; }
-
- /// <summary>
- /// Gets or sets the total shard count for this token.
- /// </summary>
- public int ShardCount { get; set; }
-}
-
-/// <summary>
-/// Represents a shard info converter.
-/// </summary>
-internal sealed class ShardInfoConverter : JsonConverter
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Writes the json.
+ /// Represents data for identify payload's shard info.
/// </summary>
- /// <param name="writer">The writer.</param>
- /// <param name="value">The value.</param>
- /// <param name="serializer">The serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ [JsonConverter(typeof(ShardInfoConverter))]
+ internal sealed class ShardInfo
{
- var sinfo = value as ShardInfo;
- var obj = new object[] { sinfo.ShardId, sinfo.ShardCount };
- serializer.Serialize(writer, obj);
+ /// <summary>
+ /// Gets or sets this client's shard id.
+ /// </summary>
+ public int ShardId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the total shard count for this token.
+ /// </summary>
+ public int ShardCount { get; set; }
}
/// <summary>
- /// Reads the json.
+ /// Represents a shard info converter.
/// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="objectType">The object type.</param>
- /// <param name="existingValue">The existing value.</param>
- /// <param name="serializer">The serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ internal sealed class ShardInfoConverter : JsonConverter
{
- var arr = this.ReadArrayObject(reader, serializer);
- return new ShardInfo
+ /// <summary>
+ /// Writes the json.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="value">The value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
- ShardId = (int)arr[0],
- ShardCount = (int)arr[1],
- };
- }
+ var sinfo = value as ShardInfo;
+ var obj = new object[] { sinfo.ShardId, sinfo.ShardCount };
+ serializer.Serialize(writer, obj);
+ }
- /// <summary>
- /// Reads the array object.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="serializer">The serializer.</param>
- private JArray ReadArrayObject(JsonReader reader, JsonSerializer serializer) =>
- serializer.Deserialize<JToken>(reader) is not JArray arr || arr.Count != 2
- ? throw new JsonSerializationException("Expected array of length 2")
- : arr;
+ /// <summary>
+ /// Reads the json.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="objectType">The object type.</param>
+ /// <param name="existingValue">The existing value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var arr = this.ReadArrayObject(reader, serializer);
+ return new ShardInfo
+ {
+ ShardId = (int)arr[0],
+ ShardCount = (int)arr[1],
+ };
+ }
- /// <summary>
- /// Whether this can be converted.
- /// </summary>
- /// <param name="objectType">The object type.</param>
- public override bool CanConvert(Type objectType) => objectType == typeof(ShardInfo);
+ /// <summary>
+ /// Reads the array object.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="serializer">The serializer.</param>
+ private JArray ReadArrayObject(JsonReader reader, JsonSerializer serializer) =>
+ serializer.Deserialize<JToken>(reader) is not JArray arr || arr.Count != 2
+ ? throw new JsonSerializationException("Expected array of length 2")
+ : arr;
+
+ /// <summary>
+ /// Whether this can be converted.
+ /// </summary>
+ /// <param name="objectType">The object type.</param>
+ public override bool CanConvert(Type objectType) => objectType == typeof(ShardInfo);
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/StatusUpdate.cs b/DisCatSharp/Net/Abstractions/StatusUpdate.cs
index 76ca6b85e..724bc737f 100644
--- a/DisCatSharp/Net/Abstractions/StatusUpdate.cs
+++ b/DisCatSharp/Net/Abstractions/StatusUpdate.cs
@@ -1,74 +1,75 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents data for websocket status update payload.
-/// </summary>
-internal sealed class StatusUpdate
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the unix millisecond timestamp of when the user went idle.
+ /// Represents data for websocket status update payload.
/// </summary>
- [JsonProperty("since", NullValueHandling = NullValueHandling.Include)]
- public long? IdleSince { get; set; }
+ internal sealed class StatusUpdate
+ {
+ /// <summary>
+ /// Gets or sets the unix millisecond timestamp of when the user went idle.
+ /// </summary>
+ [JsonProperty("since", NullValueHandling = NullValueHandling.Include)]
+ public long? IdleSince { get; set; }
- /// <summary>
- /// Gets or sets whether the user is AFK.
- /// </summary>
- [JsonProperty("afk")]
- public bool IsAfk { get; set; }
+ /// <summary>
+ /// Gets or sets whether the user is AFK.
+ /// </summary>
+ [JsonProperty("afk")]
+ public bool IsAfk { get; set; }
- /// <summary>
- /// Gets or sets the status of the user.
- /// </summary>
- [JsonIgnore]
- public UserStatus Status { get; set; } = UserStatus.Online;
+ /// <summary>
+ /// Gets or sets the status of the user.
+ /// </summary>
+ [JsonIgnore]
+ public UserStatus Status { get; set; } = UserStatus.Online;
- /// <summary>
- /// Gets the status string of the user.
- /// </summary>
- [JsonProperty("status")]
- internal string StatusString =>
- this.Status switch
- {
- UserStatus.Online => "online",
- UserStatus.Idle => "idle",
- UserStatus.DoNotDisturb => "dnd",
- UserStatus.Invisible or UserStatus.Offline => "invisible",
- UserStatus.Streaming => "streaming",
- _ => "online",
- };
+ /// <summary>
+ /// Gets the status string of the user.
+ /// </summary>
+ [JsonProperty("status")]
+ internal string StatusString =>
+ this.Status switch
+ {
+ UserStatus.Online => "online",
+ UserStatus.Idle => "idle",
+ UserStatus.DoNotDisturb => "dnd",
+ UserStatus.Invisible or UserStatus.Offline => "invisible",
+ UserStatus.Streaming => "streaming",
+ _ => "online",
+ };
- /// <summary>
- /// Gets or sets the game the user is playing.
- /// </summary>
- [JsonProperty("game", NullValueHandling = NullValueHandling.Ignore)]
- public TransportActivity Activity { get; set; }
+ /// <summary>
+ /// Gets or sets the game the user is playing.
+ /// </summary>
+ [JsonProperty("game", NullValueHandling = NullValueHandling.Ignore)]
+ public TransportActivity Activity { get; set; }
- internal DiscordActivity ActivityInternal;
+ internal DiscordActivity ActivityInternal;
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Transport/TransportActivity.cs b/DisCatSharp/Net/Abstractions/Transport/TransportActivity.cs
index 361ce32eb..62206ef7f 100644
--- a/DisCatSharp/Net/Abstractions/Transport/TransportActivity.cs
+++ b/DisCatSharp/Net/Abstractions/Transport/TransportActivity.cs
@@ -1,373 +1,374 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using DisCatSharp.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a game a user is playing.
-/// </summary>
-internal sealed class TransportActivity
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the id of user's activity.
- /// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public string Id { get; internal set; }
-
- /// <summary>
- /// Gets or sets the name of the game the user is playing.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Include)]
- public string Name { get; internal set; }
-
- /// <summary>
- /// Gets or sets the stream URI, if applicable.
- /// </summary>
- [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
- public string StreamUrl { get; internal set; }
-
- /// <summary>
- /// Gets or sets the livestream type.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public ActivityType ActivityType { get; internal set; }
-
- /// <summary>
- /// Gets or sets the details.
- ///
- /// <para>This is a component of the rich presence, and, as such, can only be used by regular users.</para>
- /// </summary>
- [JsonProperty("details", NullValueHandling = NullValueHandling.Ignore)]
- public string Details { get; internal set; }
-
- /// <summary>
- /// Gets or sets game state.
- ///
- /// <para>This is a component of the rich presence, and, as such, can only be used by regular users.</para>
- /// </summary>
- [JsonProperty("state", NullValueHandling = NullValueHandling.Ignore)]
- public string State { get; internal set; }
-
- /// <summary>
- /// Gets the emoji details for a custom status, if any.
- /// </summary>
- [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordEmoji Emoji { get; internal set; }
-
- /// <summary>
- /// Gets ID of the application for which this rich presence is for.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
+ /// Represents a game a user is playing.
/// </summary>
- [JsonIgnore]
- public ulong? ApplicationId
+ internal sealed class TransportActivity
{
- get => this.ApplicationIdStr != null ? (ulong?)ulong.Parse(this.ApplicationIdStr, CultureInfo.InvariantCulture) : null;
- internal set => this.ApplicationIdStr = value?.ToString(CultureInfo.InvariantCulture);
- }
+ /// <summary>
+ /// Gets or sets the id of user's activity.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public string Id { get; internal set; }
- /// <summary>
- /// Gets or sets the application id string.
- /// </summary>
- [JsonProperty("application_id", NullValueHandling = NullValueHandling.Ignore)]
- internal string ApplicationIdStr { get; set; }
+ /// <summary>
+ /// Gets or sets the name of the game the user is playing.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Include)]
+ public string Name { get; internal set; }
- /// <summary>
- /// Gets or sets instance status.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
- /// </summary>
- [JsonProperty("instance", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Instance { get; internal set; }
+ /// <summary>
+ /// Gets or sets the stream URI, if applicable.
+ /// </summary>
+ [JsonProperty("url", NullValueHandling = NullValueHandling.Ignore)]
+ public string StreamUrl { get; internal set; }
- /// <summary>
- /// Gets or sets information about the current game's party.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
- /// </summary>
- [JsonProperty("party", NullValueHandling = NullValueHandling.Ignore)]
- public GameParty Party { get; internal set; }
+ /// <summary>
+ /// Gets or sets the livestream type.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public ActivityType ActivityType { get; internal set; }
- /// <summary>
- /// Gets or sets information about assets related to this rich presence.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
- /// </summary>
- [JsonProperty("assets", NullValueHandling = NullValueHandling.Ignore)]
- public PresenceAssets Assets { get; internal set; }
+ /// <summary>
+ /// Gets or sets the details.
+ ///
+ /// <para>This is a component of the rich presence, and, as such, can only be used by regular users.</para>
+ /// </summary>
+ [JsonProperty("details", NullValueHandling = NullValueHandling.Ignore)]
+ public string Details { get; internal set; }
- /// <summary>
- /// Gets or sets information about buttons in this rich presence.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
- /// </summary>
- [JsonProperty("buttons", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList<string> Buttons { get; internal set; }
+ /// <summary>
+ /// Gets or sets game state.
+ ///
+ /// <para>This is a component of the rich presence, and, as such, can only be used by regular users.</para>
+ /// </summary>
+ [JsonProperty("state", NullValueHandling = NullValueHandling.Ignore)]
+ public string State { get; internal set; }
- /// <summary>
- /// Gets or sets platform in this rich presence.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
- /// </summary>
- [JsonProperty("platform", NullValueHandling = NullValueHandling.Ignore)]
- public string Platform { get; internal set; }
+ /// <summary>
+ /// Gets the emoji details for a custom status, if any.
+ /// </summary>
+ [JsonProperty("emoji", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordEmoji Emoji { get; internal set; }
- /// <summary>
- /// Gets or sets sync_id in this rich presence.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
- /// </summary>
- [JsonProperty("sync_id", NullValueHandling = NullValueHandling.Ignore)]
- public string SyncId { get; internal set; }
+ /// <summary>
+ /// Gets ID of the application for which this rich presence is for.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
+ /// </summary>
+ [JsonIgnore]
+ public ulong? ApplicationId
+ {
+ get => this.ApplicationIdStr != null ? (ulong?)ulong.Parse(this.ApplicationIdStr, CultureInfo.InvariantCulture) : null;
+ internal set => this.ApplicationIdStr = value?.ToString(CultureInfo.InvariantCulture);
+ }
- /// <summary>
- /// Gets or sets session_id in this rich presence.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
- /// </summary>
- [JsonProperty("session_id", NullValueHandling = NullValueHandling.Ignore)]
- public string SessionId { get; internal set; }
+ /// <summary>
+ /// Gets or sets the application id string.
+ /// </summary>
+ [JsonProperty("application_id", NullValueHandling = NullValueHandling.Ignore)]
+ internal string ApplicationIdStr { get; set; }
- /// <summary>
- /// Gets or sets information about current game's timestamps.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
- /// </summary>
- [JsonProperty("timestamps", NullValueHandling = NullValueHandling.Ignore)]
- public GameTimestamps Timestamps { get; internal set; }
+ /// <summary>
+ /// Gets or sets instance status.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
+ /// </summary>
+ [JsonProperty("instance", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Instance { get; internal set; }
- /// <summary>
- /// Gets or sets information about current game's secret values.
- ///
- /// This is a component of the rich presence, and, as such, can only be used by regular users.
- /// </summary>
- [JsonProperty("secrets", NullValueHandling = NullValueHandling.Ignore)]
- public GameSecrets Secrets { get; internal set; }
+ /// <summary>
+ /// Gets or sets information about the current game's party.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
+ /// </summary>
+ [JsonProperty("party", NullValueHandling = NullValueHandling.Ignore)]
+ public GameParty Party { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="TransportActivity"/> class.
- /// </summary>
- internal TransportActivity() { }
+ /// <summary>
+ /// Gets or sets information about assets related to this rich presence.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
+ /// </summary>
+ [JsonProperty("assets", NullValueHandling = NullValueHandling.Ignore)]
+ public PresenceAssets Assets { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="TransportActivity"/> class.
- /// </summary>
- /// <param name="game">The game.</param>
- internal TransportActivity(DiscordActivity game)
- {
- if (game == null)
- return;
+ /// <summary>
+ /// Gets or sets information about buttons in this rich presence.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
+ /// </summary>
+ [JsonProperty("buttons", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList<string> Buttons { get; internal set; }
- this.Name = game.Name;
- this.ActivityType = game.ActivityType;
- this.StreamUrl = game.StreamUrl;
- }
+ /// <summary>
+ /// Gets or sets platform in this rich presence.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
+ /// </summary>
+ [JsonProperty("platform", NullValueHandling = NullValueHandling.Ignore)]
+ public string Platform { get; internal set; }
- /// <summary>
- /// Whether this activity is a rich presence.
- /// </summary>
- public bool IsRichPresence()
- => this.Details != null || this.State != null || this.ApplicationId != null || this.Instance != null || this.Party != null || this.Assets != null || this.Secrets != null || this.Timestamps != null || this.Buttons != null;
+ /// <summary>
+ /// Gets or sets sync_id in this rich presence.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
+ /// </summary>
+ [JsonProperty("sync_id", NullValueHandling = NullValueHandling.Ignore)]
+ public string SyncId { get; internal set; }
- /// <summary>
- /// Whether this activity is a custom status.
- /// </summary>
- public bool IsCustomStatus()
- => this.Name == "Custom Status";
+ /// <summary>
+ /// Gets or sets session_id in this rich presence.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
+ /// </summary>
+ [JsonProperty("session_id", NullValueHandling = NullValueHandling.Ignore)]
+ public string SessionId { get; internal set; }
- /// <summary>
- /// Represents information about assets attached to a rich presence.
- /// </summary>
- public class PresenceAssets
- {
/// <summary>
- /// Gets the large image asset ID.
+ /// Gets or sets information about current game's timestamps.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
/// </summary>
- [JsonProperty("large_image")]
- public string LargeImage { get; set; }
+ [JsonProperty("timestamps", NullValueHandling = NullValueHandling.Ignore)]
+ public GameTimestamps Timestamps { get; internal set; }
/// <summary>
- /// Gets the large image text.
+ /// Gets or sets information about current game's secret values.
+ ///
+ /// This is a component of the rich presence, and, as such, can only be used by regular users.
/// </summary>
- [JsonProperty("large_text", NullValueHandling = NullValueHandling.Ignore)]
- public string LargeImageText { get; internal set; }
+ [JsonProperty("secrets", NullValueHandling = NullValueHandling.Ignore)]
+ public GameSecrets Secrets { get; internal set; }
/// <summary>
- /// Gets the small image asset ID.
+ /// Initializes a new instance of the <see cref="TransportActivity"/> class.
/// </summary>
- [JsonProperty("small_image")]
- internal string SmallImage { get; set; }
+ internal TransportActivity() { }
/// <summary>
- /// Gets the small image text.
+ /// Initializes a new instance of the <see cref="TransportActivity"/> class.
/// </summary>
- [JsonProperty("small_text", NullValueHandling = NullValueHandling.Ignore)]
- public string SmallImageText { get; internal set; }
- }
+ /// <param name="game">The game.</param>
+ internal TransportActivity(DiscordActivity game)
+ {
+ if (game == null)
+ return;
+
+ this.Name = game.Name;
+ this.ActivityType = game.ActivityType;
+ this.StreamUrl = game.StreamUrl;
+ }
- /// <summary>
- /// Represents information about rich presence game party.
- /// </summary>
- public class GameParty
- {
/// <summary>
- /// Gets the game party ID.
+ /// Whether this activity is a rich presence.
/// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
- public string Id { get; internal set; }
+ public bool IsRichPresence()
+ => this.Details != null || this.State != null || this.ApplicationId != null || this.Instance != null || this.Party != null || this.Assets != null || this.Secrets != null || this.Timestamps != null || this.Buttons != null;
/// <summary>
- /// Gets the size of the party.
+ /// Whether this activity is a custom status.
/// </summary>
- [JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)]
- public GamePartySize Size { get; internal set; }
+ public bool IsCustomStatus()
+ => this.Name == "Custom Status";
/// <summary>
- /// Represents information about party size.
+ /// Represents information about assets attached to a rich presence.
/// </summary>
- [JsonConverter(typeof(GamePartySizeConverter))]
- public class GamePartySize
+ public class PresenceAssets
{
/// <summary>
- /// Gets the current number of players in the party.
+ /// Gets the large image asset ID.
+ /// </summary>
+ [JsonProperty("large_image")]
+ public string LargeImage { get; set; }
+
+ /// <summary>
+ /// Gets the large image text.
/// </summary>
- public long Current { get; internal set; }
+ [JsonProperty("large_text", NullValueHandling = NullValueHandling.Ignore)]
+ public string LargeImageText { get; internal set; }
/// <summary>
- /// Gets the maximum party size.
+ /// Gets the small image asset ID.
/// </summary>
- public long Maximum { get; internal set; }
+ [JsonProperty("small_image")]
+ internal string SmallImage { get; set; }
+
+ /// <summary>
+ /// Gets the small image text.
+ /// </summary>
+ [JsonProperty("small_text", NullValueHandling = NullValueHandling.Ignore)]
+ public string SmallImageText { get; internal set; }
}
- }
- /// <summary>
- /// Represents information about the game state's timestamps.
- /// </summary>
- public class GameTimestamps
- {
/// <summary>
- /// Gets the time the game has started.
+ /// Represents information about rich presence game party.
/// </summary>
- [JsonIgnore]
- public DateTimeOffset? Start
- => this.StartInternal != null ? (DateTimeOffset?)Utilities.GetDateTimeOffsetFromMilliseconds(this.StartInternal.Value, false) : null;
+ public class GameParty
+ {
+ /// <summary>
+ /// Gets the game party ID.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public string Id { get; internal set; }
- [JsonProperty("start", NullValueHandling = NullValueHandling.Ignore)]
- internal long? StartInternal;
+ /// <summary>
+ /// Gets the size of the party.
+ /// </summary>
+ [JsonProperty("size", NullValueHandling = NullValueHandling.Ignore)]
+ public GamePartySize Size { get; internal set; }
+
+ /// <summary>
+ /// Represents information about party size.
+ /// </summary>
+ [JsonConverter(typeof(GamePartySizeConverter))]
+ public class GamePartySize
+ {
+ /// <summary>
+ /// Gets the current number of players in the party.
+ /// </summary>
+ public long Current { get; internal set; }
+
+ /// <summary>
+ /// Gets the maximum party size.
+ /// </summary>
+ public long Maximum { get; internal set; }
+ }
+ }
/// <summary>
- /// Gets the time the game is going to end.
+ /// Represents information about the game state's timestamps.
/// </summary>
- [JsonIgnore]
- public DateTimeOffset? End
- => this.EndInternal != null ? (DateTimeOffset?)Utilities.GetDateTimeOffsetFromMilliseconds(this.EndInternal.Value, false) : null;
+ public class GameTimestamps
+ {
+ /// <summary>
+ /// Gets the time the game has started.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset? Start
+ => this.StartInternal != null ? (DateTimeOffset?)Utilities.GetDateTimeOffsetFromMilliseconds(this.StartInternal.Value, false) : null;
- [JsonProperty("end", NullValueHandling = NullValueHandling.Ignore)]
- internal long? EndInternal;
+ [JsonProperty("start", NullValueHandling = NullValueHandling.Ignore)]
+ internal long? StartInternal;
+
+ /// <summary>
+ /// Gets the time the game is going to end.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset? End
+ => this.EndInternal != null ? (DateTimeOffset?)Utilities.GetDateTimeOffsetFromMilliseconds(this.EndInternal.Value, false) : null;
+
+ [JsonProperty("end", NullValueHandling = NullValueHandling.Ignore)]
+ internal long? EndInternal;
+ }
+
+ /// <summary>
+ /// Represents information about secret values for the Join, Spectate, and Match actions.
+ /// </summary>
+ public class GameSecrets
+ {
+ /// <summary>
+ /// Gets the secret value for join action.
+ /// </summary>
+ [JsonProperty("join", NullValueHandling = NullValueHandling.Ignore)]
+ public string Join { get; internal set; }
+
+ /// <summary>
+ /// Gets the secret value for match action.
+ /// </summary>
+ [JsonProperty("match", NullValueHandling = NullValueHandling.Ignore)]
+ public string Match { get; internal set; }
+
+ /// <summary>
+ /// Gets the secret value for spectate action.
+ /// </summary>
+ [JsonProperty("spectate", NullValueHandling = NullValueHandling.Ignore)]
+ public string Spectate { get; internal set; }
+ }
}
/// <summary>
- /// Represents information about secret values for the Join, Spectate, and Match actions.
+ /// Represents a game party size converter.
/// </summary>
- public class GameSecrets
+ internal sealed class GamePartySizeConverter : JsonConverter
{
/// <summary>
- /// Gets the secret value for join action.
+ /// Writes the json.
/// </summary>
- [JsonProperty("join", NullValueHandling = NullValueHandling.Ignore)]
- public string Join { get; internal set; }
+ /// <param name="writer">The writer.</param>
+ /// <param name="value">The value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var obj = value is TransportActivity.GameParty.GamePartySize sinfo
+ ? new object[] { sinfo.Current, sinfo.Maximum }
+ : null;
+ serializer.Serialize(writer, obj);
+ }
/// <summary>
- /// Gets the secret value for match action.
+ /// Reads the json.
/// </summary>
- [JsonProperty("match", NullValueHandling = NullValueHandling.Ignore)]
- public string Match { get; internal set; }
+ /// <param name="reader">The reader.</param>
+ /// <param name="objectType">The object type.</param>
+ /// <param name="existingValue">The existing value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var arr = this.ReadArrayObject(reader, serializer);
+ return new TransportActivity.GameParty.GamePartySize
+ {
+ Current = (long)arr[0],
+ Maximum = (long)arr[1],
+ };
+ }
/// <summary>
- /// Gets the secret value for spectate action.
+ /// Reads the array object.
/// </summary>
- [JsonProperty("spectate", NullValueHandling = NullValueHandling.Ignore)]
- public string Spectate { get; internal set; }
- }
-}
+ /// <param name="reader">The reader.</param>
+ /// <param name="serializer">The serializer.</param>
+ private JArray ReadArrayObject(JsonReader reader, JsonSerializer serializer) =>
+ serializer.Deserialize<JToken>(reader) is not JArray arr || arr.Count != 2
+ ? throw new JsonSerializationException("Expected array of length 2")
+ : arr;
-/// <summary>
-/// Represents a game party size converter.
-/// </summary>
-internal sealed class GamePartySizeConverter : JsonConverter
-{
- /// <summary>
- /// Writes the json.
- /// </summary>
- /// <param name="writer">The writer.</param>
- /// <param name="value">The value.</param>
- /// <param name="serializer">The serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- var obj = value is TransportActivity.GameParty.GamePartySize sinfo
- ? new object[] { sinfo.Current, sinfo.Maximum }
- : null;
- serializer.Serialize(writer, obj);
- }
-
- /// <summary>
- /// Reads the json.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="objectType">The object type.</param>
- /// <param name="existingValue">The existing value.</param>
- /// <param name="serializer">The serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- var arr = this.ReadArrayObject(reader, serializer);
- return new TransportActivity.GameParty.GamePartySize
- {
- Current = (long)arr[0],
- Maximum = (long)arr[1],
- };
+ /// <summary>
+ /// Whether it can convert.
+ /// </summary>
+ /// <param name="objectType">The object type.</param>
+ public override bool CanConvert(Type objectType) => objectType == typeof(TransportActivity.GameParty.GamePartySize);
}
-
- /// <summary>
- /// Reads the array object.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="serializer">The serializer.</param>
- private JArray ReadArrayObject(JsonReader reader, JsonSerializer serializer) =>
- serializer.Deserialize<JToken>(reader) is not JArray arr || arr.Count != 2
- ? throw new JsonSerializationException("Expected array of length 2")
- : arr;
-
- /// <summary>
- /// Whether it can convert.
- /// </summary>
- /// <param name="objectType">The object type.</param>
- public override bool CanConvert(Type objectType) => objectType == typeof(TransportActivity.GameParty.GamePartySize);
}
diff --git a/DisCatSharp/Net/Abstractions/Transport/TransportApplication.cs b/DisCatSharp/Net/Abstractions/Transport/TransportApplication.cs
index c5ffca6c0..e73a7c0e3 100644
--- a/DisCatSharp/Net/Abstractions/Transport/TransportApplication.cs
+++ b/DisCatSharp/Net/Abstractions/Transport/TransportApplication.cs
@@ -1,179 +1,180 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// The transport application.
-/// </summary>
-internal sealed class TransportApplication
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the id.
- /// </summary>
- [JsonProperty("id", NullValueHandling = NullValueHandling.Include)]
- public ulong Id { get; set; }
-
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Include)]
- public string Name { get; set; }
-
- /// <summary>
- /// Gets or sets the icon hash.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
- public string IconHash { get; set; }
-
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- [JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
- public string Description { get; set; }
-
- /// <summary>
- /// Gets or sets the summary.
- /// </summary>
- [JsonProperty("summary", NullValueHandling = NullValueHandling.Include)]
- public string Summary { get; set; }
-
- /// <summary>
- /// Whether the bot is public.
- /// </summary>
- [JsonProperty("bot_public", NullValueHandling = NullValueHandling.Include)]
- public bool IsPublicBot { get; set; }
-
- /// <summary>
- /// Gets or sets the flags.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Include)]
- public ApplicationFlags Flags { get; set; }
-
- /// <summary>
- /// Gets or sets the terms of service url.
- /// </summary>
- [JsonProperty("terms_of_service_url", NullValueHandling = NullValueHandling.Include)]
- public string TermsOfServiceUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the privacy policy url.
- /// </summary>
- [JsonProperty("privacy_policy_url", NullValueHandling = NullValueHandling.Include)]
- public string PrivacyPolicyUrl { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether the bot requires code grant.
- /// </summary>
- [JsonProperty("bot_require_code_grant", NullValueHandling = NullValueHandling.Include)]
- public bool BotRequiresCodeGrant { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether the bot is a hook.
- /// </summary>
- [JsonProperty("hook", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsHook { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether the bot requires code grant.
- /// </summary>
- [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
- public string Type { get; set; }
-
- /// <summary>
- /// Gets or sets the rpc origins.
- /// </summary>
- [JsonProperty("rpc_origins", NullValueHandling = NullValueHandling.Ignore)]
- public IList<string> RpcOrigins { get; set; }
-
- /// <summary>
- /// Gets or sets the owner.
- /// </summary>
- [JsonProperty("owner", NullValueHandling = NullValueHandling.Include)]
- public TransportUser Owner { get; set; }
-
- /// <summary>
- /// Gets or sets the team.
- /// </summary>
- [JsonProperty("team", NullValueHandling = NullValueHandling.Include)]
- public TransportTeam Team { get; set; }
-
- /// <summary>
- /// Gets or sets the verify key.
- /// </summary>
- [JsonProperty("verify_key", NullValueHandling = NullValueHandling.Include)]
- public Optional<string> VerifyKey { get; set; }
-
- /// <summary>
- /// Gets or sets the guild id.
- /// </summary>
- [JsonProperty("guild_id")]
- public Optional<ulong> GuildId { get; set; }
-
- /// <summary>
- /// Gets or sets the primary sku id.
- /// </summary>
- [JsonProperty("primary_sku_id")]
- public Optional<ulong> PrimarySkuId { get; set; }
-
- /// <summary>
- /// Gets or sets the slug.
- /// </summary>
- [JsonProperty("slug")]
- public Optional<string> Slug { get; set; }
-
- /// <summary>
- /// Gets or sets the cover image hash.
- /// </summary>
- [JsonProperty("cover_image")]
- public Optional<string> CoverImageHash { get; set; }
-
- /// <summary>
- /// Gets or sets the custom install url.
- /// </summary>
- [JsonProperty("custom_install_url")]
- public string CustomInstallUrl { get; set; }
-
- /// <summary>
- /// Gets or sets the install params.
- /// </summary>
- [JsonProperty("install_params", NullValueHandling = NullValueHandling.Include)]
- public DiscordApplicationInstallParams InstallParams { get; set; }
-
- /// <summary>
- /// Gets or sets the tags.
- /// </summary>
- [JsonProperty("tags", NullValueHandling = NullValueHandling.Include)]
- public IEnumerable<string> Tags { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TransportApplication"/> class.
- /// </summary>
- internal TransportApplication()
- { }
+ /// The transport application.
+ /// </summary>
+ internal sealed class TransportApplication
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Include)]
+ public ulong Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Include)]
+ public string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the icon hash.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
+ public string IconHash { get; set; }
+
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Include)]
+ public string Description { get; set; }
+
+ /// <summary>
+ /// Gets or sets the summary.
+ /// </summary>
+ [JsonProperty("summary", NullValueHandling = NullValueHandling.Include)]
+ public string Summary { get; set; }
+
+ /// <summary>
+ /// Whether the bot is public.
+ /// </summary>
+ [JsonProperty("bot_public", NullValueHandling = NullValueHandling.Include)]
+ public bool IsPublicBot { get; set; }
+
+ /// <summary>
+ /// Gets or sets the flags.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Include)]
+ public ApplicationFlags Flags { get; set; }
+
+ /// <summary>
+ /// Gets or sets the terms of service url.
+ /// </summary>
+ [JsonProperty("terms_of_service_url", NullValueHandling = NullValueHandling.Include)]
+ public string TermsOfServiceUrl { get; set; }
+
+ /// <summary>
+ /// Gets or sets the privacy policy url.
+ /// </summary>
+ [JsonProperty("privacy_policy_url", NullValueHandling = NullValueHandling.Include)]
+ public string PrivacyPolicyUrl { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the bot requires code grant.
+ /// </summary>
+ [JsonProperty("bot_require_code_grant", NullValueHandling = NullValueHandling.Include)]
+ public bool BotRequiresCodeGrant { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the bot is a hook.
+ /// </summary>
+ [JsonProperty("hook", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsHook { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the bot requires code grant.
+ /// </summary>
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public string Type { get; set; }
+
+ /// <summary>
+ /// Gets or sets the rpc origins.
+ /// </summary>
+ [JsonProperty("rpc_origins", NullValueHandling = NullValueHandling.Ignore)]
+ public IList<string> RpcOrigins { get; set; }
+
+ /// <summary>
+ /// Gets or sets the owner.
+ /// </summary>
+ [JsonProperty("owner", NullValueHandling = NullValueHandling.Include)]
+ public TransportUser Owner { get; set; }
+
+ /// <summary>
+ /// Gets or sets the team.
+ /// </summary>
+ [JsonProperty("team", NullValueHandling = NullValueHandling.Include)]
+ public TransportTeam Team { get; set; }
+
+ /// <summary>
+ /// Gets or sets the verify key.
+ /// </summary>
+ [JsonProperty("verify_key", NullValueHandling = NullValueHandling.Include)]
+ public Optional<string> VerifyKey { get; set; }
+
+ /// <summary>
+ /// Gets or sets the guild id.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public Optional<ulong> GuildId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the primary sku id.
+ /// </summary>
+ [JsonProperty("primary_sku_id")]
+ public Optional<ulong> PrimarySkuId { get; set; }
+
+ /// <summary>
+ /// Gets or sets the slug.
+ /// </summary>
+ [JsonProperty("slug")]
+ public Optional<string> Slug { get; set; }
+
+ /// <summary>
+ /// Gets or sets the cover image hash.
+ /// </summary>
+ [JsonProperty("cover_image")]
+ public Optional<string> CoverImageHash { get; set; }
+
+ /// <summary>
+ /// Gets or sets the custom install url.
+ /// </summary>
+ [JsonProperty("custom_install_url")]
+ public string CustomInstallUrl { get; set; }
+
+ /// <summary>
+ /// Gets or sets the install params.
+ /// </summary>
+ [JsonProperty("install_params", NullValueHandling = NullValueHandling.Include)]
+ public DiscordApplicationInstallParams InstallParams { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tags.
+ /// </summary>
+ [JsonProperty("tags", NullValueHandling = NullValueHandling.Include)]
+ public IEnumerable<string> Tags { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TransportApplication"/> class.
+ /// </summary>
+ internal TransportApplication()
+ { }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Transport/TransportMember.cs b/DisCatSharp/Net/Abstractions/Transport/TransportMember.cs
index 783098d2e..0120eaba7 100644
--- a/DisCatSharp/Net/Abstractions/Transport/TransportMember.cs
+++ b/DisCatSharp/Net/Abstractions/Transport/TransportMember.cs
@@ -1,121 +1,122 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Enums;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a transport member.
-/// </summary>
-internal class TransportMember
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets the avatar hash.
- /// </summary>
- [JsonIgnore]
- public string AvatarHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild avatar hash.
- /// </summary>
- [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
- public string GuildAvatarHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild banner hash.
- /// </summary>
- [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
- public string GuildBannerHash { get; internal set; }
-
- /// <summary>
- /// Gets the guild bio.
- /// This is not available to bots tho.
- /// </summary>
- [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
- public string GuildBio { get; internal set; }
-
- /// <summary>
- /// Gets the user.
- /// </summary>
- [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
- public TransportUser User { get; internal set; }
-
- /// <summary>
- /// Gets the nickname.
- /// </summary>
- [JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
- public string Nickname { get; internal set; }
-
- /// <summary>
- /// Gets the roles.
- /// </summary>
- [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
- public List<ulong> Roles { get; internal set; }
-
- /// <summary>
- /// Gets the joined at.
- /// </summary>
- [JsonProperty("joined_at", NullValueHandling = NullValueHandling.Ignore)]
- public DateTime JoinedAt { get; internal set; }
-
- /// <summary>
- /// Whether this member is deafened.
- /// </summary>
- [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsDeafened { get; internal set; }
-
- /// <summary>
- /// Whether this member is muted.
- /// </summary>
- [JsonProperty("mute", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsMuted { get; internal set; }
-
- /// <summary>
- /// Gets the premium since.
- /// </summary>
- [JsonProperty("premium_since", NullValueHandling = NullValueHandling.Ignore)]
- public DateTime? PremiumSince { get; internal set; }
-
- /// <summary>
- /// Whether this member is marked as pending.
- /// </summary>
- [JsonProperty("pending", NullValueHandling = NullValueHandling.Ignore)]
- public bool? IsPending { get; internal set; }
-
- /// <summary>
- /// Gets the timeout time.
- /// </summary>
- [JsonProperty("communication_disabled_until", NullValueHandling = NullValueHandling.Include)]
- public DateTime? CommunicationDisabledUntil { get; internal set; }
-
- /// <summary>
- /// Gets the members flags.
+ /// Represents a transport member.
/// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public MemberFlags MemberFlags { get; internal set; }
+ internal class TransportMember
+ {
+ /// <summary>
+ /// Gets the avatar hash.
+ /// </summary>
+ [JsonIgnore]
+ public string AvatarHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild avatar hash.
+ /// </summary>
+ [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
+ public string GuildAvatarHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild banner hash.
+ /// </summary>
+ [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
+ public string GuildBannerHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the guild bio.
+ /// This is not available to bots tho.
+ /// </summary>
+ [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
+ public string GuildBio { get; internal set; }
+
+ /// <summary>
+ /// Gets the user.
+ /// </summary>
+ [JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
+ public TransportUser User { get; internal set; }
+
+ /// <summary>
+ /// Gets the nickname.
+ /// </summary>
+ [JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
+ public string Nickname { get; internal set; }
+
+ /// <summary>
+ /// Gets the roles.
+ /// </summary>
+ [JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
+ public List<ulong> Roles { get; internal set; }
+
+ /// <summary>
+ /// Gets the joined at.
+ /// </summary>
+ [JsonProperty("joined_at", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTime JoinedAt { get; internal set; }
+
+ /// <summary>
+ /// Whether this member is deafened.
+ /// </summary>
+ [JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsDeafened { get; internal set; }
+
+ /// <summary>
+ /// Whether this member is muted.
+ /// </summary>
+ [JsonProperty("mute", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsMuted { get; internal set; }
+
+ /// <summary>
+ /// Gets the premium since.
+ /// </summary>
+ [JsonProperty("premium_since", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTime? PremiumSince { get; internal set; }
+
+ /// <summary>
+ /// Whether this member is marked as pending.
+ /// </summary>
+ [JsonProperty("pending", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? IsPending { get; internal set; }
+
+ /// <summary>
+ /// Gets the timeout time.
+ /// </summary>
+ [JsonProperty("communication_disabled_until", NullValueHandling = NullValueHandling.Include)]
+ public DateTime? CommunicationDisabledUntil { get; internal set; }
+
+ /// <summary>
+ /// Gets the members flags.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public MemberFlags MemberFlags { get; internal set; }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Transport/TransportTeam.cs b/DisCatSharp/Net/Abstractions/Transport/TransportTeam.cs
index 04995c243..f2370a8b3 100644
--- a/DisCatSharp/Net/Abstractions/Transport/TransportTeam.cs
+++ b/DisCatSharp/Net/Abstractions/Transport/TransportTeam.cs
@@ -1,103 +1,104 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// The transport team.
-/// </summary>
-internal sealed class TransportTeam
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the id.
+ /// The transport team.
/// </summary>
- [JsonProperty("id")]
- public ulong Id { get; set; }
+ internal sealed class TransportTeam
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- [JsonProperty("name", NullValueHandling = NullValueHandling.Include)]
- public string Name { get; set; }
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ [JsonProperty("name", NullValueHandling = NullValueHandling.Include)]
+ public string Name { get; set; }
- /// <summary>
- /// Gets or sets the icon hash.
- /// </summary>
- [JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
- public string IconHash { get; set; }
+ /// <summary>
+ /// Gets or sets the icon hash.
+ /// </summary>
+ [JsonProperty("icon", NullValueHandling = NullValueHandling.Include)]
+ public string IconHash { get; set; }
- /// <summary>
- /// Gets or sets the owner id.
- /// </summary>
- [JsonProperty("owner_user_id")]
- public ulong OwnerId { get; set; }
+ /// <summary>
+ /// Gets or sets the owner id.
+ /// </summary>
+ [JsonProperty("owner_user_id")]
+ public ulong OwnerId { get; set; }
- /// <summary>
- /// Gets or sets the members.
- /// </summary>
- [JsonProperty("members", NullValueHandling = NullValueHandling.Include)]
- public IEnumerable<TransportTeamMember> Members { get; set; }
+ /// <summary>
+ /// Gets or sets the members.
+ /// </summary>
+ [JsonProperty("members", NullValueHandling = NullValueHandling.Include)]
+ public IEnumerable<TransportTeamMember> Members { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="TransportTeam"/> class.
- /// </summary>
- internal TransportTeam() { }
-}
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TransportTeam"/> class.
+ /// </summary>
+ internal TransportTeam() { }
+ }
-/// <summary>
-/// The transport team member.
-/// </summary>
-internal sealed class TransportTeamMember
-{
/// <summary>
- /// Gets or sets the membership state.
+ /// The transport team member.
/// </summary>
- [JsonProperty("membership_state")]
- public int MembershipState { get; set; }
+ internal sealed class TransportTeamMember
+ {
+ /// <summary>
+ /// Gets or sets the membership state.
+ /// </summary>
+ [JsonProperty("membership_state")]
+ public int MembershipState { get; set; }
- /// <summary>
- /// Gets or sets the permissions.
- /// </summary>
- [JsonProperty("permissions", NullValueHandling = NullValueHandling.Include)]
- public IEnumerable<string> Permissions { get; set; }
+ /// <summary>
+ /// Gets or sets the permissions.
+ /// </summary>
+ [JsonProperty("permissions", NullValueHandling = NullValueHandling.Include)]
+ public IEnumerable<string> Permissions { get; set; }
- /// <summary>
- /// Gets or sets the team id.
- /// </summary>
- [JsonProperty("team_id")]
- public ulong TeamId { get; set; }
+ /// <summary>
+ /// Gets or sets the team id.
+ /// </summary>
+ [JsonProperty("team_id")]
+ public ulong TeamId { get; set; }
- /// <summary>
- /// Gets or sets the user.
- /// </summary>
- [JsonProperty("user", NullValueHandling = NullValueHandling.Include)]
- public TransportUser User { get; set; }
+ /// <summary>
+ /// Gets or sets the user.
+ /// </summary>
+ [JsonProperty("user", NullValueHandling = NullValueHandling.Include)]
+ public TransportUser User { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="TransportTeamMember"/> class.
- /// </summary>
- internal TransportTeamMember() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TransportTeamMember"/> class.
+ /// </summary>
+ internal TransportTeamMember() { }
+ }
}
diff --git a/DisCatSharp/Net/Abstractions/Transport/TransportUser.cs b/DisCatSharp/Net/Abstractions/Transport/TransportUser.cs
index 4d342da07..4f59ec022 100644
--- a/DisCatSharp/Net/Abstractions/Transport/TransportUser.cs
+++ b/DisCatSharp/Net/Abstractions/Transport/TransportUser.cs
@@ -1,156 +1,157 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents a transport user.
-/// </summary>
-internal class TransportUser
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets the id.
- /// </summary>
- [JsonProperty("id")]
- public ulong Id { get; internal set; }
-
- /// <summary>
- /// Gets the username.
- /// </summary>
- [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
- public string Username { get; internal set; }
-
- /// <summary>
- /// Gets or sets the discriminator.
- /// </summary>
- [JsonProperty("discriminator", NullValueHandling = NullValueHandling.Ignore)]
- internal string Discriminator { get; set; }
-
- /// <summary>
- /// Gets the username with discriminator.
- /// </summary>
- internal string UsernameWithDiscriminator
- => $"{this.Username}#{this.Discriminator}";
-
- /// <summary>
- /// Gets the avatar hash.
- /// </summary>
- [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
- public string AvatarHash { get; internal set; }
-
- /// <summary>
- /// Gets the banner hash.
- /// </summary>
- [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
- public string BannerHash { get; internal set; }
-
- /// <summary>
- /// Gets the banner color.
- /// </summary>
- [JsonProperty("accent_color")]
- public int? BannerColor { get; internal set; }
-
- /// <summary>
- /// Gets a value indicating whether is bot.
- /// </summary>
- [JsonProperty("bot", NullValueHandling = NullValueHandling.Ignore)]
- public bool IsBot { get; internal set; }
-
- /// <summary>
- /// Gets a value indicating whether mfa enabled.
- /// </summary>
- [JsonProperty("mfa_enabled", NullValueHandling = NullValueHandling.Ignore)]
- public bool? MfaEnabled { get; internal set; }
-
- /// <summary>
- /// Gets a value indicating whether verified.
- /// </summary>
- [JsonProperty("verified", NullValueHandling = NullValueHandling.Ignore)]
- public bool? Verified { get; internal set; }
-
- /// <summary>
- /// Gets the email.
- /// </summary>
- [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)]
- public string Email { get; internal set; }
-
- /// <summary>
- /// Gets the premium type.
- /// </summary>
- [JsonProperty("premium_type", NullValueHandling = NullValueHandling.Ignore)]
- public PremiumType? PremiumType { get; internal set; }
-
- /// <summary>
- /// Gets the locale.
- /// </summary>
- [JsonProperty("locale", NullValueHandling = NullValueHandling.Ignore)]
- public string Locale { get; internal set; }
-
- /// <summary>
- /// Gets the OAuth flags.
- /// </summary>
- [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
- public UserFlags? OAuthFlags { get; internal set; }
-
- /// <summary>
- /// Gets the flags.
- /// </summary>
- [JsonProperty("public_flags", NullValueHandling = NullValueHandling.Ignore)]
- public UserFlags? Flags { get; internal set; }
-
- /// <summary>
- /// Gets the users bio.
- /// This is not available to bots tho.
- /// </summary>
- [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
- public string Bio { get; internal set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TransportUser"/> class.
- /// </summary>
- internal TransportUser() { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TransportUser"/> class from an existing <see cref="TransportUser"/>.
+ /// Represents a transport user.
/// </summary>
- /// <param name="other">The other transport user.</param>
- internal TransportUser(TransportUser other)
+ internal class TransportUser
{
- this.Id = other.Id;
- this.Username = other.Username;
- this.Discriminator = other.Discriminator;
- this.AvatarHash = other.AvatarHash;
- this.BannerHash = other.BannerHash;
- this.BannerColor = other.BannerColor;
- this.IsBot = other.IsBot;
- this.MfaEnabled = other.MfaEnabled;
- this.Verified = other.Verified;
- this.Email = other.Email;
- this.PremiumType = other.PremiumType;
- this.Locale = other.Locale;
- this.Flags = other.Flags;
- this.OAuthFlags = other.OAuthFlags;
- this.Bio = other.Bio;
+ /// <summary>
+ /// Gets the id.
+ /// </summary>
+ [JsonProperty("id")]
+ public ulong Id { get; internal set; }
+
+ /// <summary>
+ /// Gets the username.
+ /// </summary>
+ [JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
+ public string Username { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets the discriminator.
+ /// </summary>
+ [JsonProperty("discriminator", NullValueHandling = NullValueHandling.Ignore)]
+ internal string Discriminator { get; set; }
+
+ /// <summary>
+ /// Gets the username with discriminator.
+ /// </summary>
+ internal string UsernameWithDiscriminator
+ => $"{this.Username}#{this.Discriminator}";
+
+ /// <summary>
+ /// Gets the avatar hash.
+ /// </summary>
+ [JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
+ public string AvatarHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the banner hash.
+ /// </summary>
+ [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
+ public string BannerHash { get; internal set; }
+
+ /// <summary>
+ /// Gets the banner color.
+ /// </summary>
+ [JsonProperty("accent_color")]
+ public int? BannerColor { get; internal set; }
+
+ /// <summary>
+ /// Gets a value indicating whether is bot.
+ /// </summary>
+ [JsonProperty("bot", NullValueHandling = NullValueHandling.Ignore)]
+ public bool IsBot { get; internal set; }
+
+ /// <summary>
+ /// Gets a value indicating whether mfa enabled.
+ /// </summary>
+ [JsonProperty("mfa_enabled", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? MfaEnabled { get; internal set; }
+
+ /// <summary>
+ /// Gets a value indicating whether verified.
+ /// </summary>
+ [JsonProperty("verified", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Verified { get; internal set; }
+
+ /// <summary>
+ /// Gets the email.
+ /// </summary>
+ [JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)]
+ public string Email { get; internal set; }
+
+ /// <summary>
+ /// Gets the premium type.
+ /// </summary>
+ [JsonProperty("premium_type", NullValueHandling = NullValueHandling.Ignore)]
+ public PremiumType? PremiumType { get; internal set; }
+
+ /// <summary>
+ /// Gets the locale.
+ /// </summary>
+ [JsonProperty("locale", NullValueHandling = NullValueHandling.Ignore)]
+ public string Locale { get; internal set; }
+
+ /// <summary>
+ /// Gets the OAuth flags.
+ /// </summary>
+ [JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
+ public UserFlags? OAuthFlags { get; internal set; }
+
+ /// <summary>
+ /// Gets the flags.
+ /// </summary>
+ [JsonProperty("public_flags", NullValueHandling = NullValueHandling.Ignore)]
+ public UserFlags? Flags { get; internal set; }
+
+ /// <summary>
+ /// Gets the users bio.
+ /// This is not available to bots tho.
+ /// </summary>
+ [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
+ public string Bio { get; internal set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TransportUser"/> class.
+ /// </summary>
+ internal TransportUser() { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TransportUser"/> class from an existing <see cref="TransportUser"/>.
+ /// </summary>
+ /// <param name="other">The other transport user.</param>
+ internal TransportUser(TransportUser other)
+ {
+ this.Id = other.Id;
+ this.Username = other.Username;
+ this.Discriminator = other.Discriminator;
+ this.AvatarHash = other.AvatarHash;
+ this.BannerHash = other.BannerHash;
+ this.BannerColor = other.BannerColor;
+ this.IsBot = other.IsBot;
+ this.MfaEnabled = other.MfaEnabled;
+ this.Verified = other.Verified;
+ this.Email = other.Email;
+ this.PremiumType = other.PremiumType;
+ this.Locale = other.Locale;
+ this.Flags = other.Flags;
+ this.OAuthFlags = other.OAuthFlags;
+ this.Bio = other.Bio;
+ }
}
}
diff --git a/DisCatSharp/Net/Abstractions/VoiceStateUpdate.cs b/DisCatSharp/Net/Abstractions/VoiceStateUpdate.cs
index 5783f2eef..f2ec93b9b 100644
--- a/DisCatSharp/Net/Abstractions/VoiceStateUpdate.cs
+++ b/DisCatSharp/Net/Abstractions/VoiceStateUpdate.cs
@@ -1,55 +1,56 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Net.Abstractions;
-
-/// <summary>
-/// Represents data for websocket voice state update payload.
-/// </summary>
-internal sealed class VoiceStateUpdate
+namespace DisCatSharp.Net.Abstractions
{
/// <summary>
- /// Gets or sets the guild for which the user is updating their voice state.
+ /// Represents data for websocket voice state update payload.
/// </summary>
- [JsonProperty("guild_id")]
- public ulong GuildId { get; set; }
+ internal sealed class VoiceStateUpdate
+ {
+ /// <summary>
+ /// Gets or sets the guild for which the user is updating their voice state.
+ /// </summary>
+ [JsonProperty("guild_id")]
+ public ulong GuildId { get; set; }
- /// <summary>
- /// Gets or sets the channel user wants to connect to. Null if disconnecting.
- /// </summary>
- [JsonProperty("channel_id")]
- public ulong? ChannelId { get; set; }
+ /// <summary>
+ /// Gets or sets the channel user wants to connect to. Null if disconnecting.
+ /// </summary>
+ [JsonProperty("channel_id")]
+ public ulong? ChannelId { get; set; }
- /// <summary>
- /// Gets or sets whether the client is muted.
- /// </summary>
- [JsonProperty("self_mute")]
- public bool Mute { get; set; }
+ /// <summary>
+ /// Gets or sets whether the client is muted.
+ /// </summary>
+ [JsonProperty("self_mute")]
+ public bool Mute { get; set; }
- /// <summary>
- /// Gets or sets whether the client is deafened.
- /// </summary>
- [JsonProperty("self_deaf")]
- public bool Deafen { get; set; }
+ /// <summary>
+ /// Gets or sets whether the client is deafened.
+ /// </summary>
+ [JsonProperty("self_deaf")]
+ public bool Deafen { get; set; }
+ }
}
diff --git a/DisCatSharp/Net/ConnectionEndpoint.cs b/DisCatSharp/Net/ConnectionEndpoint.cs
index fe826fd27..d5de6ce78 100644
--- a/DisCatSharp/Net/ConnectionEndpoint.cs
+++ b/DisCatSharp/Net/ConnectionEndpoint.cs
@@ -1,87 +1,88 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents a network connection endpoint.
-/// </summary>
-public struct ConnectionEndpoint
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets or sets the hostname associated with this endpoint.
+ /// Represents a network connection endpoint.
/// </summary>
- public string Hostname { get; set; }
+ public struct ConnectionEndpoint
+ {
+ /// <summary>
+ /// Gets or sets the hostname associated with this endpoint.
+ /// </summary>
+ public string Hostname { get; set; }
- /// <summary>
- /// Gets or sets the port associated with this endpoint.
- /// </summary>
- public int Port { get; set; }
+ /// <summary>
+ /// Gets or sets the port associated with this endpoint.
+ /// </summary>
+ public int Port { get; set; }
- /// <summary>
- /// Gets or sets the secured status of this connection.
- /// </summary>
- public bool Secured { get; set; }
+ /// <summary>
+ /// Gets or sets the secured status of this connection.
+ /// </summary>
+ public bool Secured { get; set; }
- /// <summary>
- /// Creates a new endpoint structure.
- /// </summary>
- /// <param name="hostname">Hostname to connect to.</param>
- /// <param name="port">Port to use for connection.</param>
- /// <param name="secured">Whether the connection should be secured (https/wss).</param>
- public ConnectionEndpoint(string hostname, int port, bool secured = false)
- {
- this.Hostname = hostname;
- this.Port = port;
- this.Secured = secured;
- }
+ /// <summary>
+ /// Creates a new endpoint structure.
+ /// </summary>
+ /// <param name="hostname">Hostname to connect to.</param>
+ /// <param name="port">Port to use for connection.</param>
+ /// <param name="secured">Whether the connection should be secured (https/wss).</param>
+ public ConnectionEndpoint(string hostname, int port, bool secured = false)
+ {
+ this.Hostname = hostname;
+ this.Port = port;
+ this.Secured = secured;
+ }
- /// <summary>
- /// Gets the hash code of this endpoint.
- /// </summary>
- /// <returns>Hash code of this endpoint.</returns>
- public override int GetHashCode() => 13 + (7 * this.Hostname.GetHashCode()) + (7 * this.Port);
+ /// <summary>
+ /// Gets the hash code of this endpoint.
+ /// </summary>
+ /// <returns>Hash code of this endpoint.</returns>
+ public override int GetHashCode() => 13 + (7 * this.Hostname.GetHashCode()) + (7 * this.Port);
- /// <summary>
- /// Gets the string representation of this connection endpoint.
- /// </summary>
- /// <returns>String representation of this endpoint.</returns>
- public override string ToString() => $"{this.Hostname}:{this.Port}";
+ /// <summary>
+ /// Gets the string representation of this connection endpoint.
+ /// </summary>
+ /// <returns>String representation of this endpoint.</returns>
+ public override string ToString() => $"{this.Hostname}:{this.Port}";
- /// <summary>
- /// Returns a http string.
- /// </summary>
- internal string ToHttpString()
- {
- var secure = this.Secured ? "s" : "";
- return $"http{secure}://{this}";
- }
+ /// <summary>
+ /// Returns a http string.
+ /// </summary>
+ internal string ToHttpString()
+ {
+ var secure = this.Secured ? "s" : "";
+ return $"http{secure}://{this}";
+ }
- /// <summary>
- /// Returns a web socket string.
- /// </summary>
- internal string ToWebSocketString()
- {
- var secure = this.Secured ? "s" : "";
- return $"ws{secure}://{this}/";
+ /// <summary>
+ /// Returns a web socket string.
+ /// </summary>
+ internal string ToWebSocketString()
+ {
+ var secure = this.Secured ? "s" : "";
+ return $"ws{secure}://{this}/";
+ }
}
}
diff --git a/DisCatSharp/Net/Models/ApplicationCommandEditModel.cs b/DisCatSharp/Net/Models/ApplicationCommandEditModel.cs
index e4cfd4a97..99f41fd48 100644
--- a/DisCatSharp/Net/Models/ApplicationCommandEditModel.cs
+++ b/DisCatSharp/Net/Models/ApplicationCommandEditModel.cs
@@ -1,89 +1,90 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net.Models;
-
-/// <summary>
-/// Represents a application command edit model.
-/// </summary>
-public class ApplicationCommandEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// Sets the command's new name.
+ /// Represents a application command edit model.
/// </summary>
- public Optional<string> Name
+ public class ApplicationCommandEditModel
{
- internal get => this._name;
- set
+ /// <summary>
+ /// Sets the command's new name.
+ /// </summary>
+ public Optional<string> Name
{
- if (value.Value.Length > 32)
- throw new ArgumentException("Application command name cannot exceed 32 characters.", nameof(value));
- this._name = value;
+ internal get => this._name;
+ set
+ {
+ if (value.Value.Length > 32)
+ throw new ArgumentException("Application command name cannot exceed 32 characters.", nameof(value));
+ this._name = value;
+ }
}
- }
- private Optional<string> _name;
+ private Optional<string> _name;
- /// <summary>
- /// Sets the command's new description
- /// </summary>
- public Optional<string> Description
- {
- internal get => this._description;
- set
+ /// <summary>
+ /// Sets the command's new description
+ /// </summary>
+ public Optional<string> Description
{
- if (value.Value.Length > 100)
- throw new ArgumentException("Application command description cannot exceed 100 characters.", nameof(value));
- this._description = value;
+ internal get => this._description;
+ set
+ {
+ if (value.Value.Length > 100)
+ throw new ArgumentException("Application command description cannot exceed 100 characters.", nameof(value));
+ this._description = value;
+ }
}
- }
- private Optional<string> _description;
+ private Optional<string> _description;
- /// <summary>
- /// Sets the command's name localizations.
- /// </summary>
- public Optional<DiscordApplicationCommandLocalization> NameLocalizations { internal get; set; }
+ /// <summary>
+ /// Sets the command's name localizations.
+ /// </summary>
+ public Optional<DiscordApplicationCommandLocalization> NameLocalizations { internal get; set; }
- /// <summary>
- /// Sets the command's description localizations.
- /// </summary>
- public Optional<DiscordApplicationCommandLocalization> DescriptionLocalizations { internal get; set; }
+ /// <summary>
+ /// Sets the command's description localizations.
+ /// </summary>
+ public Optional<DiscordApplicationCommandLocalization> DescriptionLocalizations { internal get; set; }
- /// <summary>
- /// Sets the command's new options.
- /// </summary>
- public Optional<IReadOnlyCollection<DiscordApplicationCommandOption>> Options { internal get; set; }
+ /// <summary>
+ /// Sets the command's new options.
+ /// </summary>
+ public Optional<IReadOnlyCollection<DiscordApplicationCommandOption>> Options { internal get; set; }
- /// <summary>
- /// Sets the command's needed permissions.
- /// </summary>
- public Optional<Permissions> DefaultMemberPermissions { internal get; set; }
+ /// <summary>
+ /// Sets the command's needed permissions.
+ /// </summary>
+ public Optional<Permissions> DefaultMemberPermissions { internal get; set; }
- /// <summary>
- /// Sets whether the command can be used in direct messages.
- /// </summary>
- public Optional<bool> DmPermission { internal get; set; }
+ /// <summary>
+ /// Sets whether the command can be used in direct messages.
+ /// </summary>
+ public Optional<bool> DmPermission { internal get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Models/BaseEditModel.cs b/DisCatSharp/Net/Models/BaseEditModel.cs
index 0e6a39487..5fef7d2ff 100644
--- a/DisCatSharp/Net/Models/BaseEditModel.cs
+++ b/DisCatSharp/Net/Models/BaseEditModel.cs
@@ -1,34 +1,35 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Net.Models;
-
-/// <summary>
-/// Represents the base edit model.
-/// </summary>
-public class BaseEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// Reason given in audit logs
+ /// Represents the base edit model.
/// </summary>
- public string AuditLogReason { internal get; set; }
+ public class BaseEditModel
+ {
+ /// <summary>
+ /// Reason given in audit logs
+ /// </summary>
+ public string AuditLogReason { internal get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Models/ChannelEditModel.cs b/DisCatSharp/Net/Models/ChannelEditModel.cs
index ac3941657..9adcf05c8 100644
--- a/DisCatSharp/Net/Models/ChannelEditModel.cs
+++ b/DisCatSharp/Net/Models/ChannelEditModel.cs
@@ -1,119 +1,120 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using DisCatSharp.Entities;
-namespace DisCatSharp.Net.Models;
-
-/// <summary>
-/// Represents a channel edit model.
-/// </summary>
-public class ChannelEditModel : BaseEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// Sets the channel's new name.
- /// </summary>
- public string Name { internal get; set; }
-
- /// <summary>
- /// Sets the channel's type.
- /// This can only be used to convert between text and news channels.
- /// </summary>
- public Optional<ChannelType> Type { internal get; set; }
-
- /// <summary>
- /// Sets the channel's new position.
- /// </summary>
- public int? Position { internal get; set; }
-
- /// <summary>
- /// Sets the channel's new topic.
- /// </summary>
- public Optional<string> Topic { internal get; set; }
-
- /// <summary>
- /// Sets whether the channel is to be marked as NSFW.
- /// </summary>
- public bool? Nsfw { internal get; set; }
-
- /// <summary>
- /// <para>Sets the parent of this channel.</para>
- /// <para>This should be channel with <see cref="DisCatSharp.Entities.DiscordChannel.Type"/> set to <see cref="ChannelType.Category"/>.</para>
- /// </summary>
- public Optional<DiscordChannel> Parent { internal get; set; }
-
- /// <summary>
- /// Sets the voice channel's new bitrate.
- /// </summary>
- public int? Bitrate { internal get; set; }
-
- [Obsolete("Use properly capitalized UserLimit property.")]
- public int? Userlimit { set => this.UserLimit = value; }
-
- /// <summary>
- /// <para>Sets the voice channel's new user limit.</para>
- /// <para>Setting this to 0 will disable the user limit.</para>
- /// </summary>
- public int? UserLimit { internal get; set; }
-
- /// <summary>
- /// <para>Sets the channel's new slow mode timeout.</para>
- /// <para>Setting this to null or 0 will disable slow mode.</para>
- /// </summary>
- public Optional<int?> PerUserRateLimit { internal get; set; }
-
- /// <summary>
- /// <para>Sets the voice channel's region override.</para>
- /// <para>Setting this to null will set it to automatic.</para>
- /// </summary>
- public Optional<DiscordVoiceRegion> RtcRegion { internal get; set; }
-
- /// <summary>
- /// <para>Sets the voice channel's video quality.</para>
- /// </summary>
- public VideoQualityMode? QualityMode { internal get; set; }
-
- /// <summary>
- /// Sets this channel's default duration for newly created threads, in minutes, to automatically archive the thread after recent activity.
- /// </summary>
- public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { internal get; set; }
-
- /// <summary>
- /// Sets the channel's permission overwrites.
- /// </summary>
- public IEnumerable<DiscordOverwriteBuilder> PermissionOverwrites { internal get; set; }
-
- /// <summary>
- /// The new banner of the channel
- /// </summary>
- public Optional<Stream> Banner { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ChannelEditModel"/> class.
+ /// Represents a channel edit model.
/// </summary>
- internal ChannelEditModel()
- { }
+ public class ChannelEditModel : BaseEditModel
+ {
+ /// <summary>
+ /// Sets the channel's new name.
+ /// </summary>
+ public string Name { internal get; set; }
+
+ /// <summary>
+ /// Sets the channel's type.
+ /// This can only be used to convert between text and news channels.
+ /// </summary>
+ public Optional<ChannelType> Type { internal get; set; }
+
+ /// <summary>
+ /// Sets the channel's new position.
+ /// </summary>
+ public int? Position { internal get; set; }
+
+ /// <summary>
+ /// Sets the channel's new topic.
+ /// </summary>
+ public Optional<string> Topic { internal get; set; }
+
+ /// <summary>
+ /// Sets whether the channel is to be marked as NSFW.
+ /// </summary>
+ public bool? Nsfw { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets the parent of this channel.</para>
+ /// <para>This should be channel with <see cref="DisCatSharp.Entities.DiscordChannel.Type"/> set to <see cref="ChannelType.Category"/>.</para>
+ /// </summary>
+ public Optional<DiscordChannel> Parent { internal get; set; }
+
+ /// <summary>
+ /// Sets the voice channel's new bitrate.
+ /// </summary>
+ public int? Bitrate { internal get; set; }
+
+ [Obsolete("Use properly capitalized UserLimit property.")]
+ public int? Userlimit { set => this.UserLimit = value; }
+
+ /// <summary>
+ /// <para>Sets the voice channel's new user limit.</para>
+ /// <para>Setting this to 0 will disable the user limit.</para>
+ /// </summary>
+ public int? UserLimit { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets the channel's new slow mode timeout.</para>
+ /// <para>Setting this to null or 0 will disable slow mode.</para>
+ /// </summary>
+ public Optional<int?> PerUserRateLimit { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets the voice channel's region override.</para>
+ /// <para>Setting this to null will set it to automatic.</para>
+ /// </summary>
+ public Optional<DiscordVoiceRegion> RtcRegion { internal get; set; }
+
+ /// <summary>
+ /// <para>Sets the voice channel's video quality.</para>
+ /// </summary>
+ public VideoQualityMode? QualityMode { internal get; set; }
+
+ /// <summary>
+ /// Sets this channel's default duration for newly created threads, in minutes, to automatically archive the thread after recent activity.
+ /// </summary>
+ public ThreadAutoArchiveDuration? DefaultAutoArchiveDuration { internal get; set; }
+
+ /// <summary>
+ /// Sets the channel's permission overwrites.
+ /// </summary>
+ public IEnumerable<DiscordOverwriteBuilder> PermissionOverwrites { internal get; set; }
+
+ /// <summary>
+ /// The new banner of the channel
+ /// </summary>
+ public Optional<Stream> Banner { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ChannelEditModel"/> class.
+ /// </summary>
+ internal ChannelEditModel()
+ { }
+ }
}
diff --git a/DisCatSharp/Net/Models/GuildEditModel.cs b/DisCatSharp/Net/Models/GuildEditModel.cs
index b25d2a3c7..14ba92f05 100644
--- a/DisCatSharp/Net/Models/GuildEditModel.cs
+++ b/DisCatSharp/Net/Models/GuildEditModel.cs
@@ -1,134 +1,135 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using DisCatSharp.Entities;
-namespace DisCatSharp.Net.Models;
-
-/// <summary>
-/// Represents a guild edit model.
-/// </summary>
-public class GuildEditModel : BaseEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// The new guild name.
- /// </summary>
- public Optional<string> Name { internal get; set; }
-
- /// <summary>
- /// The new guild icon.
- /// </summary>
- public Optional<Stream> Icon { internal get; set; }
-
- /// <summary>
- /// The new guild verification level.
- /// </summary>
- public Optional<VerificationLevel> VerificationLevel { internal get; set; }
-
- /// <summary>
- /// The new guild default message notification level.
- /// </summary>
- public Optional<DefaultMessageNotifications> DefaultMessageNotifications { internal get; set; }
-
- /// <summary>
- /// The new guild MFA level.
- /// </summary>
- public Optional<MfaLevel> MfaLevel { internal get; set; }
-
- /// <summary>
- /// The new guild explicit content filter level.
- /// </summary>
- public Optional<ExplicitContentFilter> ExplicitContentFilter { internal get; set; }
-
- /// <summary>
- /// The new AFK voice channel.
- /// </summary>
- public Optional<DiscordChannel> AfkChannel { internal get; set; }
-
- /// <summary>
- /// The new AFK timeout time in seconds.
- /// </summary>
- public Optional<int> AfkTimeout { internal get; set; }
-
- /// <summary>
- /// The new guild owner.
- /// </summary>
- public Optional<DiscordMember> Owner { internal get; set; }
-
- /// <summary>
- /// The new guild splash.
- /// </summary>
- public Optional<Stream> Splash { internal get; set; }
-
- /// <summary>
- /// The new guild system channel.
- /// </summary>
- public Optional<DiscordChannel> SystemChannel { internal get; set; }
-
- /// <summary>
- /// The guild system channel flags.
- /// </summary>
- public Optional<SystemChannelFlags> SystemChannelFlags { internal get; set; }
-
- /// <summary>
- /// The guild description.
- /// </summary>
- public Optional<string> Description { internal get; set; }
-
- /// <summary>
- /// The new guild rules channel.
- /// </summary>
- public Optional<DiscordChannel> RulesChannel { internal get; set; }
-
- /// <summary>
- /// The new guild public updates channel.
- /// </summary>
- public Optional<DiscordChannel> PublicUpdatesChannel { internal get; set; }
-
- /// <summary>
- /// The new guild preferred locale.
- /// </summary>
- public Optional<string> PreferredLocale { internal get; set; }
-
- /// <summary>
- /// The new banner of the guild
- /// </summary>
- public Optional<Stream> Banner { get; set; }
-
- /// <summary>
- /// The new discovery splash image of the guild
- /// </summary>
- public Optional<Stream> DiscoverySplash { get; set; }
-
- /// <summary>
- /// Whether the premium progress bar should be enabled
- /// </summary>
- public Optional<bool> PremiumProgressBarEnabled { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GuildEditModel"/> class.
- /// </summary>
- internal GuildEditModel()
- { }
+ /// Represents a guild edit model.
+ /// </summary>
+ public class GuildEditModel : BaseEditModel
+ {
+ /// <summary>
+ /// The new guild name.
+ /// </summary>
+ public Optional<string> Name { internal get; set; }
+
+ /// <summary>
+ /// The new guild icon.
+ /// </summary>
+ public Optional<Stream> Icon { internal get; set; }
+
+ /// <summary>
+ /// The new guild verification level.
+ /// </summary>
+ public Optional<VerificationLevel> VerificationLevel { internal get; set; }
+
+ /// <summary>
+ /// The new guild default message notification level.
+ /// </summary>
+ public Optional<DefaultMessageNotifications> DefaultMessageNotifications { internal get; set; }
+
+ /// <summary>
+ /// The new guild MFA level.
+ /// </summary>
+ public Optional<MfaLevel> MfaLevel { internal get; set; }
+
+ /// <summary>
+ /// The new guild explicit content filter level.
+ /// </summary>
+ public Optional<ExplicitContentFilter> ExplicitContentFilter { internal get; set; }
+
+ /// <summary>
+ /// The new AFK voice channel.
+ /// </summary>
+ public Optional<DiscordChannel> AfkChannel { internal get; set; }
+
+ /// <summary>
+ /// The new AFK timeout time in seconds.
+ /// </summary>
+ public Optional<int> AfkTimeout { internal get; set; }
+
+ /// <summary>
+ /// The new guild owner.
+ /// </summary>
+ public Optional<DiscordMember> Owner { internal get; set; }
+
+ /// <summary>
+ /// The new guild splash.
+ /// </summary>
+ public Optional<Stream> Splash { internal get; set; }
+
+ /// <summary>
+ /// The new guild system channel.
+ /// </summary>
+ public Optional<DiscordChannel> SystemChannel { internal get; set; }
+
+ /// <summary>
+ /// The guild system channel flags.
+ /// </summary>
+ public Optional<SystemChannelFlags> SystemChannelFlags { internal get; set; }
+
+ /// <summary>
+ /// The guild description.
+ /// </summary>
+ public Optional<string> Description { internal get; set; }
+
+ /// <summary>
+ /// The new guild rules channel.
+ /// </summary>
+ public Optional<DiscordChannel> RulesChannel { internal get; set; }
+
+ /// <summary>
+ /// The new guild public updates channel.
+ /// </summary>
+ public Optional<DiscordChannel> PublicUpdatesChannel { internal get; set; }
+
+ /// <summary>
+ /// The new guild preferred locale.
+ /// </summary>
+ public Optional<string> PreferredLocale { internal get; set; }
+
+ /// <summary>
+ /// The new banner of the guild
+ /// </summary>
+ public Optional<Stream> Banner { get; set; }
+
+ /// <summary>
+ /// The new discovery splash image of the guild
+ /// </summary>
+ public Optional<Stream> DiscoverySplash { get; set; }
+
+ /// <summary>
+ /// Whether the premium progress bar should be enabled
+ /// </summary>
+ public Optional<bool> PremiumProgressBarEnabled { get; set; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GuildEditModel"/> class.
+ /// </summary>
+ internal GuildEditModel()
+ { }
+ }
}
diff --git a/DisCatSharp/Net/Models/MemberEditModel.cs b/DisCatSharp/Net/Models/MemberEditModel.cs
index c1ed2e40e..2cb27c16c 100644
--- a/DisCatSharp/Net/Models/MemberEditModel.cs
+++ b/DisCatSharp/Net/Models/MemberEditModel.cs
@@ -1,64 +1,65 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
-namespace DisCatSharp.Net.Models;
-
-/// <summary>
-/// Represents a member edit model.
-/// </summary>
-public class MemberEditModel : BaseEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// New nickname
+ /// Represents a member edit model.
/// </summary>
- public Optional<string> Nickname { internal get; set; }
+ public class MemberEditModel : BaseEditModel
+ {
+ /// <summary>
+ /// New nickname
+ /// </summary>
+ public Optional<string> Nickname { internal get; set; }
- /// <summary>
- /// New roles
- /// </summary>
- public Optional<List<DiscordRole>> Roles { internal get; set; }
+ /// <summary>
+ /// New roles
+ /// </summary>
+ public Optional<List<DiscordRole>> Roles { internal get; set; }
- /// <summary>
- /// Whether this user should be muted
- /// </summary>
- public Optional<bool> Muted { internal get; set; }
+ /// <summary>
+ /// Whether this user should be muted
+ /// </summary>
+ public Optional<bool> Muted { internal get; set; }
- /// <summary>
- /// Whether this user should be deafened
- /// </summary>
- public Optional<bool> Deafened { internal get; set; }
+ /// <summary>
+ /// Whether this user should be deafened
+ /// </summary>
+ public Optional<bool> Deafened { internal get; set; }
- /// <summary>
- /// Voice channel to move this user to, set to null to kick
- /// </summary>
- public Optional<DiscordChannel> VoiceChannel { internal get; set; }
+ /// <summary>
+ /// Voice channel to move this user to, set to null to kick
+ /// </summary>
+ public Optional<DiscordChannel> VoiceChannel { internal get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MemberEditModel"/> class.
- /// </summary>
- internal MemberEditModel()
- { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MemberEditModel"/> class.
+ /// </summary>
+ internal MemberEditModel()
+ { }
+ }
}
diff --git a/DisCatSharp/Net/Models/MembershipScreeningEditModel.cs b/DisCatSharp/Net/Models/MembershipScreeningEditModel.cs
index 3defb05d4..3f5405aaa 100644
--- a/DisCatSharp/Net/Models/MembershipScreeningEditModel.cs
+++ b/DisCatSharp/Net/Models/MembershipScreeningEditModel.cs
@@ -1,51 +1,52 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
-namespace DisCatSharp.Net.Models;
-
-/// <summary>
-/// Represents a membership screening edit model.
-/// </summary>
-public class MembershipScreeningEditModel : BaseEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// Sets whether membership screening should be enabled for this guild
+ /// Represents a membership screening edit model.
/// </summary>
- public Optional<bool> Enabled { internal get; set; }
+ public class MembershipScreeningEditModel : BaseEditModel
+ {
+ /// <summary>
+ /// Sets whether membership screening should be enabled for this guild
+ /// </summary>
+ public Optional<bool> Enabled { internal get; set; }
- /// <summary>
- /// Sets the server description shown in the membership screening form
- /// </summary>
- public Optional<string> Description { internal get; set; }
+ /// <summary>
+ /// Sets the server description shown in the membership screening form
+ /// </summary>
+ public Optional<string> Description { internal get; set; }
- /// <summary>
- /// Sets the fields in this membership screening form
- /// </summary>
- public Optional<DiscordGuildMembershipScreeningField[]> Fields { internal get; set; }
+ /// <summary>
+ /// Sets the fields in this membership screening form
+ /// </summary>
+ public Optional<DiscordGuildMembershipScreeningField[]> Fields { internal get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MembershipScreeningEditModel"/> class.
- /// </summary>
- internal MembershipScreeningEditModel() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MembershipScreeningEditModel"/> class.
+ /// </summary>
+ internal MembershipScreeningEditModel() { }
+ }
}
diff --git a/DisCatSharp/Net/Models/RoleEditModel.cs b/DisCatSharp/Net/Models/RoleEditModel.cs
index 5c8054ced..c55f40b08 100644
--- a/DisCatSharp/Net/Models/RoleEditModel.cs
+++ b/DisCatSharp/Net/Models/RoleEditModel.cs
@@ -1,82 +1,83 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using DisCatSharp.Entities;
-namespace DisCatSharp.Net.Models;
-
-/// <summary>
-/// Represents a role edit model.
-/// </summary>
-public class RoleEditModel : BaseEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// New role name
+ /// Represents a role edit model.
/// </summary>
- public string Name { internal get; set; }
+ public class RoleEditModel : BaseEditModel
+ {
+ /// <summary>
+ /// New role name
+ /// </summary>
+ public string Name { internal get; set; }
- /// <summary>
- /// New role permissions
- /// </summary>
- public Permissions? Permissions { internal get; set; }
+ /// <summary>
+ /// New role permissions
+ /// </summary>
+ public Permissions? Permissions { internal get; set; }
- /// <summary>
- /// New role color
- /// </summary>
- public DiscordColor? Color { internal get; set; }
+ /// <summary>
+ /// New role color
+ /// </summary>
+ public DiscordColor? Color { internal get; set; }
- /// <summary>
- /// Whether new role should be hoisted (Shown in the sidebar)
- /// </summary>
- public bool? Hoist { internal get; set; }
+ /// <summary>
+ /// Whether new role should be hoisted (Shown in the sidebar)
+ /// </summary>
+ public bool? Hoist { internal get; set; }
- /// <summary>
- /// Whether new role should be mentionable
- /// </summary>
- public bool? Mentionable { internal get; set; }
+ /// <summary>
+ /// Whether new role should be mentionable
+ /// </summary>
+ public bool? Mentionable { internal get; set; }
- /// <summary>
- /// The new role icon.
- /// </summary>
- public Optional<Stream> Icon { internal get; set; }
+ /// <summary>
+ /// The new role icon.
+ /// </summary>
+ public Optional<Stream> Icon { internal get; set; }
- /// <summary>
- /// The new role icon from unicode emoji.
- /// </summary>
- public Optional<DiscordEmoji> UnicodeEmoji { internal get; set; }
+ /// <summary>
+ /// The new role icon from unicode emoji.
+ /// </summary>
+ public Optional<DiscordEmoji> UnicodeEmoji { internal get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="RoleEditModel"/> class.
- /// </summary>
- internal RoleEditModel()
- {
- this.Name = null;
- this.Permissions = null;
- this.Color = null;
- this.Hoist = null;
- this.Mentionable = null;
- this.Icon = null;
- this.UnicodeEmoji = null;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RoleEditModel"/> class.
+ /// </summary>
+ internal RoleEditModel()
+ {
+ this.Name = null;
+ this.Permissions = null;
+ this.Color = null;
+ this.Hoist = null;
+ this.Mentionable = null;
+ this.Icon = null;
+ this.UnicodeEmoji = null;
+ }
}
}
diff --git a/DisCatSharp/Net/Models/ScheduledEventEditModel.cs b/DisCatSharp/Net/Models/ScheduledEventEditModel.cs
index b3833f301..567aaa7ef 100644
--- a/DisCatSharp/Net/Models/ScheduledEventEditModel.cs
+++ b/DisCatSharp/Net/Models/ScheduledEventEditModel.cs
@@ -1,84 +1,85 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using DisCatSharp.Entities;
-namespace DisCatSharp.Net.Models;
-
-/// <summary>
-/// Represents a scheduled event edit model.
-/// </summary>
-public class ScheduledEventEditModel : BaseEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// Gets or sets the channel.
+ /// Represents a scheduled event edit model.
/// </summary>
- public Optional<DiscordChannel> Channel { get; set; }
+ public class ScheduledEventEditModel : BaseEditModel
+ {
+ /// <summary>
+ /// Gets or sets the channel.
+ /// </summary>
+ public Optional<DiscordChannel> Channel { get; set; }
- /// <summary>
- /// Gets or sets the location.
- /// </summary>
- public Optional<string> Location { get; set; }
+ /// <summary>
+ /// Gets or sets the location.
+ /// </summary>
+ public Optional<string> Location { get; set; }
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- public Optional<string> Name { get; set; }
+ /// <summary>
+ /// Gets or sets the name.
+ /// </summary>
+ public Optional<string> Name { get; set; }
- /// <summary>
- /// Gets or sets the description.
- /// </summary>
- public Optional<string> Description { get; set; }
+ /// <summary>
+ /// Gets or sets the description.
+ /// </summary>
+ public Optional<string> Description { get; set; }
- /// <summary>
- /// Gets or sets the time to schedule the scheduled event.
- /// </summary>
- public Optional<DateTimeOffset> ScheduledStartTime { get; internal set; }
+ /// <summary>
+ /// Gets or sets the time to schedule the scheduled event.
+ /// </summary>
+ public Optional<DateTimeOffset> ScheduledStartTime { get; internal set; }
- /// <summary>
- /// Gets or sets the time when the scheduled event is scheduled to end.
- /// </summary>
- public Optional<DateTimeOffset> ScheduledEndTime { get; internal set; }
+ /// <summary>
+ /// Gets or sets the time when the scheduled event is scheduled to end.
+ /// </summary>
+ public Optional<DateTimeOffset> ScheduledEndTime { get; internal set; }
- /// <summary>
- /// Gets or sets the entity type of the scheduled event.
- /// </summary>
- public Optional<ScheduledEventEntityType> EntityType { get; set; }
+ /// <summary>
+ /// Gets or sets the entity type of the scheduled event.
+ /// </summary>
+ public Optional<ScheduledEventEntityType> EntityType { get; set; }
- /// <summary>
- /// Gets or sets the cover image as base64.
- /// </summary>
- public Optional<Stream> CoverImage { get; set; }
+ /// <summary>
+ /// Gets or sets the cover image as base64.
+ /// </summary>
+ public Optional<Stream> CoverImage { get; set; }
- /// <summary>
- /// Gets or sets the status of the scheduled event.
- /// </summary>
- public Optional<ScheduledEventStatus> Status { get; set; }
+ /// <summary>
+ /// Gets or sets the status of the scheduled event.
+ /// </summary>
+ public Optional<ScheduledEventStatus> Status { get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ScheduledEventEditModel"/> class.
- /// </summary>
- internal ScheduledEventEditModel() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ScheduledEventEditModel"/> class.
+ /// </summary>
+ internal ScheduledEventEditModel() { }
+ }
}
diff --git a/DisCatSharp/Net/Models/ThreadEditModel.cs b/DisCatSharp/Net/Models/ThreadEditModel.cs
index 45ed9c466..ba55cac10 100644
--- a/DisCatSharp/Net/Models/ThreadEditModel.cs
+++ b/DisCatSharp/Net/Models/ThreadEditModel.cs
@@ -1,66 +1,67 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 DisCatSharp.Entities;
-namespace DisCatSharp.Net.Models;
-
-/// <summary>
-/// Represents a thread edit model.
-/// </summary>
-public class ThreadEditModel : BaseEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// Sets the thread's new name.
+ /// Represents a thread edit model.
/// </summary>
- public string Name { internal get; set; }
+ public class ThreadEditModel : BaseEditModel
+ {
+ /// <summary>
+ /// Sets the thread's new name.
+ /// </summary>
+ public string Name { internal get; set; }
- /// <summary>
- /// Sets the thread's locked state.
- /// </summary>
- public Optional<bool?> Locked { internal get; set; }
+ /// <summary>
+ /// Sets the thread's locked state.
+ /// </summary>
+ public Optional<bool?> Locked { internal get; set; }
- /// <summary>
- /// Sets the thread's archived state.
- /// </summary>
- public Optional<bool?> Archived { internal get; set; }
+ /// <summary>
+ /// Sets the thread's archived state.
+ /// </summary>
+ public Optional<bool?> Archived { internal get; set; }
- /// <summary>
- /// Sets the thread's auto archive duration.
- /// </summary>
- public Optional<ThreadAutoArchiveDuration?> AutoArchiveDuration { internal get; set; }
+ /// <summary>
+ /// Sets the thread's auto archive duration.
+ /// </summary>
+ public Optional<ThreadAutoArchiveDuration?> AutoArchiveDuration { internal get; set; }
- /// <summary>
- /// Sets the thread's new user rate limit.
- /// </summary>
- public Optional<int?> PerUserRateLimit { internal get; set; }
+ /// <summary>
+ /// Sets the thread's new user rate limit.
+ /// </summary>
+ public Optional<int?> PerUserRateLimit { internal get; set; }
- /// <summary>
- /// Sets the thread's invitable state.
- /// </summary>
- public Optional<bool?> Invitable { internal get; set; }
+ /// <summary>
+ /// Sets the thread's invitable state.
+ /// </summary>
+ public Optional<bool?> Invitable { internal get; set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="ThreadEditModel"/> class.
- /// </summary>
- internal ThreadEditModel() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ThreadEditModel"/> class.
+ /// </summary>
+ internal ThreadEditModel() { }
+ }
}
diff --git a/DisCatSharp/Net/Models/WelcomeScreenEditModel.cs b/DisCatSharp/Net/Models/WelcomeScreenEditModel.cs
index cac4e2e6d..05f821676 100644
--- a/DisCatSharp/Net/Models/WelcomeScreenEditModel.cs
+++ b/DisCatSharp/Net/Models/WelcomeScreenEditModel.cs
@@ -1,48 +1,49 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
using DisCatSharp.Entities;
-namespace DisCatSharp.Net.Models;
-
-/// <summary>
-/// Represents a welcome screen edit model.
-/// </summary>
-public class WelcomeScreenEditModel
+namespace DisCatSharp.Net.Models
{
/// <summary>
- /// Sets whether the welcome screen should be enabled.
+ /// Represents a welcome screen edit model.
/// </summary>
- public Optional<bool> Enabled { internal get; set; }
+ public class WelcomeScreenEditModel
+ {
+ /// <summary>
+ /// Sets whether the welcome screen should be enabled.
+ /// </summary>
+ public Optional<bool> Enabled { internal get; set; }
- /// <summary>
- /// Sets the welcome channels.
- /// </summary>
- public Optional<IEnumerable<DiscordGuildWelcomeScreenChannel>> WelcomeChannels { internal get; set; }
+ /// <summary>
+ /// Sets the welcome channels.
+ /// </summary>
+ public Optional<IEnumerable<DiscordGuildWelcomeScreenChannel>> WelcomeChannels { internal get; set; }
- /// <summary>
- /// Sets the serer description shown.
- /// </summary>
- public Optional<string> Description { internal get; set; }
+ /// <summary>
+ /// Sets the serer description shown.
+ /// </summary>
+ public Optional<string> Description { internal get; set; }
+ }
}
diff --git a/DisCatSharp/Net/Rest/BaseRestRequest.cs b/DisCatSharp/Net/Rest/BaseRestRequest.cs
index 67bc93493..5cf564f54 100644
--- a/DisCatSharp/Net/Rest/BaseRestRequest.cs
+++ b/DisCatSharp/Net/Rest/BaseRestRequest.cs
@@ -1,131 +1,132 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
using System.Threading.Tasks;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents a request sent over HTTP.
-/// </summary>
-public abstract class BaseRestRequest
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets the discord client.
- /// </summary>
- protected internal BaseDiscordClient Discord { get; }
-
- /// <summary>
- /// Gets the request task source.
- /// </summary>
- protected internal TaskCompletionSource<RestResponse> RequestTaskSource { get; }
-
- /// <summary>
- /// Gets the url to which this request is going to be made.
- /// </summary>
- public Uri Url { get; }
-
- /// <summary>
- /// Gets the HTTP method used for this request.
- /// </summary>
- public RestRequestMethod Method { get; }
-
- /// <summary>
- /// Gets the generic path (no parameters) for this request.
- /// </summary>
- public string Route { get; }
-
- /// <summary>
- /// Gets the headers sent with this request.
- /// </summary>
- public IReadOnlyDictionary<string, string> Headers { get; }
-
- /// <summary>
- /// Gets the override for the rate limit bucket wait time.
+ /// Represents a request sent over HTTP.
/// </summary>
- public double? RateLimitWaitOverride { get; }
-
- /// <summary>
- /// Gets the rate limit bucket this request is in.
- /// </summary>
- internal RateLimitBucket RateLimitBucket { get; }
-
- /// <summary>
- /// Creates a new <see cref="BaseRestRequest"/> with specified parameters.
- /// </summary>
- /// <param name="client"><see cref="DiscordClient"/> from which this request originated.</param>
- /// <param name="bucket">Rate limit bucket to place this request in.</param>
- /// <param name="url">Uri to which this request is going to be sent to.</param>
- /// <param name="method">Method to use for this request,</param>
- /// <param name="route">The generic route the request url will use.</param>
- /// <param name="headers">Additional headers for this request.</param>
- /// <param name="ratelimitWaitOverride">Override for ratelimit bucket wait time.</param>
- internal BaseRestRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, double? ratelimitWaitOverride = null)
+ public abstract class BaseRestRequest
{
- this.Discord = client;
- this.RateLimitBucket = bucket;
- this.RequestTaskSource = new TaskCompletionSource<RestResponse>();
- this.Url = url;
- this.Method = method;
- this.Route = route;
- this.RateLimitWaitOverride = ratelimitWaitOverride;
-
- if (headers != null)
+ /// <summary>
+ /// Gets the discord client.
+ /// </summary>
+ protected internal BaseDiscordClient Discord { get; }
+
+ /// <summary>
+ /// Gets the request task source.
+ /// </summary>
+ protected internal TaskCompletionSource<RestResponse> RequestTaskSource { get; }
+
+ /// <summary>
+ /// Gets the url to which this request is going to be made.
+ /// </summary>
+ public Uri Url { get; }
+
+ /// <summary>
+ /// Gets the HTTP method used for this request.
+ /// </summary>
+ public RestRequestMethod Method { get; }
+
+ /// <summary>
+ /// Gets the generic path (no parameters) for this request.
+ /// </summary>
+ public string Route { get; }
+
+ /// <summary>
+ /// Gets the headers sent with this request.
+ /// </summary>
+ public IReadOnlyDictionary<string, string> Headers { get; }
+
+ /// <summary>
+ /// Gets the override for the rate limit bucket wait time.
+ /// </summary>
+ public double? RateLimitWaitOverride { get; }
+
+ /// <summary>
+ /// Gets the rate limit bucket this request is in.
+ /// </summary>
+ internal RateLimitBucket RateLimitBucket { get; }
+
+ /// <summary>
+ /// Creates a new <see cref="BaseRestRequest"/> with specified parameters.
+ /// </summary>
+ /// <param name="client"><see cref="DiscordClient"/> from which this request originated.</param>
+ /// <param name="bucket">Rate limit bucket to place this request in.</param>
+ /// <param name="url">Uri to which this request is going to be sent to.</param>
+ /// <param name="method">Method to use for this request,</param>
+ /// <param name="route">The generic route the request url will use.</param>
+ /// <param name="headers">Additional headers for this request.</param>
+ /// <param name="ratelimitWaitOverride">Override for ratelimit bucket wait time.</param>
+ internal BaseRestRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, double? ratelimitWaitOverride = null)
{
- headers = headers.Select(x => new KeyValuePair<string, string>(x.Key, Uri.EscapeDataString(x.Value)))
- .ToDictionary(x => x.Key, x => x.Value);
- this.Headers = headers;
+ this.Discord = client;
+ this.RateLimitBucket = bucket;
+ this.RequestTaskSource = new TaskCompletionSource<RestResponse>();
+ this.Url = url;
+ this.Method = method;
+ this.Route = route;
+ this.RateLimitWaitOverride = ratelimitWaitOverride;
+
+ if (headers != null)
+ {
+ headers = headers.Select(x => new KeyValuePair<string, string>(x.Key, Uri.EscapeDataString(x.Value)))
+ .ToDictionary(x => x.Key, x => x.Value);
+ this.Headers = headers;
+ }
}
- }
- /// <summary>
- /// Asynchronously waits for this request to complete.
- /// </summary>
- /// <returns>HTTP response to this request.</returns>
- public Task<RestResponse> WaitForCompletionAsync()
- => this.RequestTaskSource.Task;
-
- /// <summary>
- /// Sets as completed.
- /// </summary>
- /// <param name="response">The response to set.</param>
- protected internal void SetCompleted(RestResponse response)
- => this.RequestTaskSource.SetResult(response);
-
- /// <summary>
- /// Sets as faulted.
- /// </summary>
- /// <param name="ex">The exception to set.</param>
- protected internal void SetFaulted(Exception ex)
- => this.RequestTaskSource.SetException(ex);
-
- /// <summary>
- /// Tries to set as faulted.
- /// </summary>
- /// <param name="ex">The exception to set.</param>
- protected internal bool TrySetFaulted(Exception ex)
- => this.RequestTaskSource.TrySetException(ex);
+ /// <summary>
+ /// Asynchronously waits for this request to complete.
+ /// </summary>
+ /// <returns>HTTP response to this request.</returns>
+ public Task<RestResponse> WaitForCompletionAsync()
+ => this.RequestTaskSource.Task;
+
+ /// <summary>
+ /// Sets as completed.
+ /// </summary>
+ /// <param name="response">The response to set.</param>
+ protected internal void SetCompleted(RestResponse response)
+ => this.RequestTaskSource.SetResult(response);
+
+ /// <summary>
+ /// Sets as faulted.
+ /// </summary>
+ /// <param name="ex">The exception to set.</param>
+ protected internal void SetFaulted(Exception ex)
+ => this.RequestTaskSource.SetException(ex);
+
+ /// <summary>
+ /// Tries to set as faulted.
+ /// </summary>
+ /// <param name="ex">The exception to set.</param>
+ protected internal bool TrySetFaulted(Exception ex)
+ => this.RequestTaskSource.TrySetException(ex);
+ }
}
diff --git a/DisCatSharp/Net/Rest/DiscordApiClient.cs b/DisCatSharp/Net/Rest/DiscordApiClient.cs
index 68e7f450e..6ca4a71c7 100644
--- a/DisCatSharp/Net/Rest/DiscordApiClient.cs
+++ b/DisCatSharp/Net/Rest/DiscordApiClient.cs
@@ -1,5272 +1,5273 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using DisCatSharp.Entities;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Serialization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents a discord api client.
-/// </summary>
-public sealed class DiscordApiClient
+namespace DisCatSharp.Net
{
/// <summary>
- /// The audit log reason header name.
- /// </summary>
- private const string REASON_HEADER_NAME = "X-Audit-Log-Reason";
-
- /// <summary>
- /// Gets the discord client.
- /// </summary>
- internal BaseDiscordClient Discord { get; }
-
- /// <summary>
- /// Gets the rest client.
- /// </summary>
- internal RestClient Rest { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordApiClient"/> class.
+ /// Represents a discord api client.
/// </summary>
- /// <param name="client">The client.</param>
- internal DiscordApiClient(BaseDiscordClient client)
+ public sealed class DiscordApiClient
{
- this.Discord = client;
- this.Rest = new RestClient(client);
- }
+ /// <summary>
+ /// The audit log reason header name.
+ /// </summary>
+ private const string REASON_HEADER_NAME = "X-Audit-Log-Reason";
- /// <summary>
- /// Initializes a new instance of the <see cref="DiscordApiClient"/> class.
- /// </summary>
- /// <param name="proxy">The proxy.</param>
- /// <param name="timeout">The timeout.</param>
- /// <param name="useRelativeRateLimit">If true, use relative rate limit.</param>
- /// <param name="logger">The logger.</param>
- internal DiscordApiClient(IWebProxy proxy, TimeSpan timeout, bool useRelativeRateLimit, ILogger logger) // This is for meta-clients, such as the webhook client
- {
- this.Rest = new RestClient(proxy, timeout, useRelativeRateLimit, logger);
- }
+ /// <summary>
+ /// Gets the discord client.
+ /// </summary>
+ internal BaseDiscordClient Discord { get; }
- /// <summary>
- /// Builds the query string.
- /// </summary>
- /// <param name="values">The values.</param>
- /// <param name="post">Whether this query will be transmitted via POST.</param>
- private static string BuildQueryString(IDictionary<string, string> values, bool post = false)
- {
- if (values == null || values.Count == 0)
- return string.Empty;
+ /// <summary>
+ /// Gets the rest client.
+ /// </summary>
+ internal RestClient Rest { get; }
- var valsCollection = values.Select(xkvp =>
- $"{WebUtility.UrlEncode(xkvp.Key)}={WebUtility.UrlEncode(xkvp.Value)}");
- var vals = string.Join("&", valsCollection);
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordApiClient"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ internal DiscordApiClient(BaseDiscordClient client)
+ {
+ this.Discord = client;
+ this.Rest = new RestClient(client);
+ }
- return !post ? $"?{vals}" : vals;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DiscordApiClient"/> class.
+ /// </summary>
+ /// <param name="proxy">The proxy.</param>
+ /// <param name="timeout">The timeout.</param>
+ /// <param name="useRelativeRateLimit">If true, use relative rate limit.</param>
+ /// <param name="logger">The logger.</param>
+ internal DiscordApiClient(IWebProxy proxy, TimeSpan timeout, bool useRelativeRateLimit, ILogger logger) // This is for meta-clients, such as the webhook client
+ {
+ this.Rest = new RestClient(proxy, timeout, useRelativeRateLimit, logger);
+ }
- /// <summary>
- /// Prepares the message.
- /// </summary>
- /// <param name="msgRaw">The msg_raw.</param>
- /// <returns>A DiscordMessage.</returns>
- private DiscordMessage PrepareMessage(JToken msgRaw)
- {
- var author = msgRaw["author"].ToObject<TransportUser>();
- var ret = msgRaw.ToDiscordObject<DiscordMessage>();
- ret.Discord = this.Discord;
+ /// <summary>
+ /// Builds the query string.
+ /// </summary>
+ /// <param name="values">The values.</param>
+ /// <param name="post">Whether this query will be transmitted via POST.</param>
+ private static string BuildQueryString(IDictionary<string, string> values, bool post = false)
+ {
+ if (values == null || values.Count == 0)
+ return string.Empty;
- this.PopulateMessage(author, ret);
+ var valsCollection = values.Select(xkvp =>
+ $"{WebUtility.UrlEncode(xkvp.Key)}={WebUtility.UrlEncode(xkvp.Value)}");
+ var vals = string.Join("&", valsCollection);
- var referencedMsg = msgRaw["referenced_message"];
- if (ret.MessageType == MessageType.Reply && !string.IsNullOrWhiteSpace(referencedMsg?.ToString()))
- {
- author = referencedMsg["author"].ToObject<TransportUser>();
- ret.ReferencedMessage.Discord = this.Discord;
- this.PopulateMessage(author, ret.ReferencedMessage);
+ return !post ? $"?{vals}" : vals;
}
- if (ret.Channel != null)
- return ret;
+ /// <summary>
+ /// Prepares the message.
+ /// </summary>
+ /// <param name="msgRaw">The msg_raw.</param>
+ /// <returns>A DiscordMessage.</returns>
+ private DiscordMessage PrepareMessage(JToken msgRaw)
+ {
+ var author = msgRaw["author"].ToObject<TransportUser>();
+ var ret = msgRaw.ToDiscordObject<DiscordMessage>();
+ ret.Discord = this.Discord;
+
+ this.PopulateMessage(author, ret);
- var channel = !ret.GuildId.HasValue
- ? new DiscordDmChannel
+ var referencedMsg = msgRaw["referenced_message"];
+ if (ret.MessageType == MessageType.Reply && !string.IsNullOrWhiteSpace(referencedMsg?.ToString()))
{
- Id = ret.ChannelId,
- Discord = this.Discord,
- Type = ChannelType.Private
+ author = referencedMsg["author"].ToObject<TransportUser>();
+ ret.ReferencedMessage.Discord = this.Discord;
+ this.PopulateMessage(author, ret.ReferencedMessage);
}
- : new DiscordChannel
- {
- Id = ret.ChannelId,
- GuildId = ret.GuildId,
- Discord = this.Discord
- };
- ret.Channel = channel;
+ if (ret.Channel != null)
+ return ret;
- return ret;
- }
+ var channel = !ret.GuildId.HasValue
+ ? new DiscordDmChannel
+ {
+ Id = ret.ChannelId,
+ Discord = this.Discord,
+ Type = ChannelType.Private
+ }
+ : new DiscordChannel
+ {
+ Id = ret.ChannelId,
+ GuildId = ret.GuildId,
+ Discord = this.Discord
+ };
- /// <summary>
- /// Populates the message.
- /// </summary>
- /// <param name="author">The author.</param>
- /// <param name="ret">The message.</param>
- private void PopulateMessage(TransportUser author, DiscordMessage ret)
- {
- var guild = ret.Channel?.Guild;
+ ret.Channel = channel;
- //If this is a webhook, it shouldn't be in the user cache.
- if (author.IsBot && int.Parse(author.Discriminator) == 0)
- {
- ret.Author = new DiscordUser(author) { Discord = this.Discord };
+ return ret;
}
- else
+
+ /// <summary>
+ /// Populates the message.
+ /// </summary>
+ /// <param name="author">The author.</param>
+ /// <param name="ret">The message.</param>
+ private void PopulateMessage(TransportUser author, DiscordMessage ret)
{
- if (!this.Discord.UserCache.TryGetValue(author.Id, out var usr))
- {
- this.Discord.UserCache[author.Id] = usr = new DiscordUser(author) { Discord = this.Discord };
- }
+ var guild = ret.Channel?.Guild;
- if (guild != null)
+ //If this is a webhook, it shouldn't be in the user cache.
+ if (author.IsBot && int.Parse(author.Discriminator) == 0)
{
- if (!guild.Members.TryGetValue(author.Id, out var mbr))
- mbr = new DiscordMember(usr) { Discord = this.Discord, GuildId = guild.Id };
- ret.Author = mbr;
+ ret.Author = new DiscordUser(author) { Discord = this.Discord };
}
else
{
- ret.Author = usr;
- }
- }
+ if (!this.Discord.UserCache.TryGetValue(author.Id, out var usr))
+ {
+ this.Discord.UserCache[author.Id] = usr = new DiscordUser(author) { Discord = this.Discord };
+ }
- ret.PopulateMentions();
+ if (guild != null)
+ {
+ if (!guild.Members.TryGetValue(author.Id, out var mbr))
+ mbr = new DiscordMember(usr) { Discord = this.Discord, GuildId = guild.Id };
+ ret.Author = mbr;
+ }
+ else
+ {
+ ret.Author = usr;
+ }
+ }
- if (ret.ReactionsInternal == null)
- ret.ReactionsInternal = new List<DiscordReaction>();
- foreach (var xr in ret.ReactionsInternal)
- xr.Emoji.Discord = this.Discord;
- }
+ ret.PopulateMentions();
- /// <summary>
- /// Executes a rest request.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="bucket">The bucket.</param>
- /// <param name="url">The url.</param>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <param name="headers">The headers.</param>
- /// <param name="payload">The payload.</param>
- /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
- internal Task<RestResponse> DoRequestAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, string payload = null, double? ratelimitWaitOverride = null)
- {
- var req = new RestRequest(client, bucket, url, method, route, headers, payload, ratelimitWaitOverride);
+ if (ret.ReactionsInternal == null)
+ ret.ReactionsInternal = new List<DiscordReaction>();
+ foreach (var xr in ret.ReactionsInternal)
+ xr.Emoji.Discord = this.Discord;
+ }
- if (this.Discord != null)
- this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
- else
- _ = this.Rest.ExecuteRequestAsync(req);
+ /// <summary>
+ /// Executes a rest request.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="bucket">The bucket.</param>
+ /// <param name="url">The url.</param>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <param name="headers">The headers.</param>
+ /// <param name="payload">The payload.</param>
+ /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
+ internal Task<RestResponse> DoRequestAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, string payload = null, double? ratelimitWaitOverride = null)
+ {
+ var req = new RestRequest(client, bucket, url, method, route, headers, payload, ratelimitWaitOverride);
- return req.WaitForCompletionAsync();
- }
+ if (this.Discord != null)
+ this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
+ else
+ _ = this.Rest.ExecuteRequestAsync(req);
- /// <summary>
- /// Executes a multipart rest request for stickers.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="bucket">The bucket.</param>
- /// <param name="url">The url.</param>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <param name="headers">The headers.</param>
- /// <param name="file">The file.</param>
- /// <param name="name">The sticker name.</param>
- /// <param name="tags">The sticker tag.</param>
- /// <param name="description">The sticker description.</param>
- /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
- private Task<RestResponse> DoStickerMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null,
- DiscordMessageFile file = null, string name = "", string tags = "", string description = "", double? ratelimitWaitOverride = null)
- {
- var req = new MultipartStickerWebRequest(client, bucket, url, method, route, headers, file, name, tags, description, ratelimitWaitOverride);
+ return req.WaitForCompletionAsync();
+ }
- if (this.Discord != null)
- this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
- else
- _ = this.Rest.ExecuteRequestAsync(req);
+ /// <summary>
+ /// Executes a multipart rest request for stickers.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="bucket">The bucket.</param>
+ /// <param name="url">The url.</param>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <param name="headers">The headers.</param>
+ /// <param name="file">The file.</param>
+ /// <param name="name">The sticker name.</param>
+ /// <param name="tags">The sticker tag.</param>
+ /// <param name="description">The sticker description.</param>
+ /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
+ private Task<RestResponse> DoStickerMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null,
+ DiscordMessageFile file = null, string name = "", string tags = "", string description = "", double? ratelimitWaitOverride = null)
+ {
+ var req = new MultipartStickerWebRequest(client, bucket, url, method, route, headers, file, name, tags, description, ratelimitWaitOverride);
- return req.WaitForCompletionAsync();
- }
+ if (this.Discord != null)
+ this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
+ else
+ _ = this.Rest.ExecuteRequestAsync(req);
- /// <summary>
- /// Executes a multipart request.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="bucket">The bucket.</param>
- /// <param name="url">The url.</param>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <param name="headers">The headers.</param>
- /// <param name="values">The values.</param>
- /// <param name="files">The files.</param>
- /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
- private Task<RestResponse> DoMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, IReadOnlyDictionary<string, string> values = null,
- IReadOnlyCollection<DiscordMessageFile> files = null, double? ratelimitWaitOverride = null)
- {
- var req = new MultipartWebRequest(client, bucket, url, method, route, headers, values, files, ratelimitWaitOverride);
+ return req.WaitForCompletionAsync();
+ }
- if (this.Discord != null)
- this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
- else
- _ = this.Rest.ExecuteRequestAsync(req);
+ /// <summary>
+ /// Executes a multipart request.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="bucket">The bucket.</param>
+ /// <param name="url">The url.</param>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <param name="headers">The headers.</param>
+ /// <param name="values">The values.</param>
+ /// <param name="files">The files.</param>
+ /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
+ private Task<RestResponse> DoMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, IReadOnlyDictionary<string, string> values = null,
+ IReadOnlyCollection<DiscordMessageFile> files = null, double? ratelimitWaitOverride = null)
+ {
+ var req = new MultipartWebRequest(client, bucket, url, method, route, headers, values, files, ratelimitWaitOverride);
- return req.WaitForCompletionAsync();
- }
+ if (this.Discord != null)
+ this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
+ else
+ _ = this.Rest.ExecuteRequestAsync(req);
- #region Guild
+ return req.WaitForCompletionAsync();
+ }
- /// <summary>
- /// Searches the members async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="limit">The limit.</param>
- internal async Task<IReadOnlyList<DiscordMember>> SearchMembersAsync(ulong guildId, string name, int? limit)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}{Endpoints.SEARCH}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var querydict = new Dictionary<string, string>
- {
- ["query"] = name,
- ["limit"] = limit.ToString()
- };
- var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var json = JArray.Parse(res.Response);
- var tms = json.ToObject<IReadOnlyList<TransportMember>>();
+ #region Guild
- var mbrs = new List<DiscordMember>();
- foreach (var xtm in tms)
+ /// <summary>
+ /// Searches the members async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="limit">The limit.</param>
+ internal async Task<IReadOnlyList<DiscordMember>> SearchMembersAsync(ulong guildId, string name, int? limit)
{
- var usr = new DiscordUser(xtm.User) { Discord = this.Discord };
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}{Endpoints.SEARCH}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var querydict = new Dictionary<string, string>
+ {
+ ["query"] = name,
+ ["limit"] = limit.ToString()
+ };
+ var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var json = JArray.Parse(res.Response);
+ var tms = json.ToObject<IReadOnlyList<TransportMember>>();
- this.Discord.UserCache.AddOrUpdate(xtm.User.Id, usr, (id, old) =>
+ var mbrs = new List<DiscordMember>();
+ foreach (var xtm in tms)
{
- old.Username = usr.Username;
- old.Discord = usr.Discord;
- old.AvatarHash = usr.AvatarHash;
+ var usr = new DiscordUser(xtm.User) { Discord = this.Discord };
- return old;
- });
+ this.Discord.UserCache.AddOrUpdate(xtm.User.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discord = usr.Discord;
+ old.AvatarHash = usr.AvatarHash;
- mbrs.Add(new DiscordMember(xtm) { Discord = this.Discord, GuildId = guildId });
- }
+ return old;
+ });
- return mbrs;
- }
+ mbrs.Add(new DiscordMember(xtm) { Discord = this.Discord, GuildId = guildId });
+ }
- /// <summary>
- /// Gets the guild ban async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- internal async Task<DiscordBan> GetGuildBanAsync(ulong guildId, ulong userId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, user_id = userId}, out var path);
- var uri = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, uri, RestRequestMethod.GET, route).ConfigureAwait(false);
- var json = JObject.Parse(res.Response);
+ return mbrs;
+ }
+
+ /// <summary>
+ /// Gets the guild ban async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ internal async Task<DiscordBan> GetGuildBanAsync(ulong guildId, ulong userId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, user_id = userId}, out var path);
+ var uri = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, uri, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var json = JObject.Parse(res.Response);
- var ban = json.ToObject<DiscordBan>();
+ var ban = json.ToObject<DiscordBan>();
- return ban;
- }
+ return ban;
+ }
- /// <summary>
- /// Creates the guild async.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="regionId">The region_id.</param>
- /// <param name="iconb64">The iconb64.</param>
- /// <param name="verificationLevel">The verification_level.</param>
- /// <param name="defaultMessageNotifications">The default_message_notifications.</param>
- /// <param name="systemChannelFlags">The system_channel_flags.</param>
- internal async Task<DiscordGuild> CreateGuildAsync(string name, string regionId, Optional<string> iconb64, VerificationLevel? verificationLevel,
- DefaultMessageNotifications? defaultMessageNotifications, SystemChannelFlags? systemChannelFlags)
- {
- var pld = new RestGuildCreatePayload
+ /// <summary>
+ /// Creates the guild async.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <param name="regionId">The region_id.</param>
+ /// <param name="iconb64">The iconb64.</param>
+ /// <param name="verificationLevel">The verification_level.</param>
+ /// <param name="defaultMessageNotifications">The default_message_notifications.</param>
+ /// <param name="systemChannelFlags">The system_channel_flags.</param>
+ internal async Task<DiscordGuild> CreateGuildAsync(string name, string regionId, Optional<string> iconb64, VerificationLevel? verificationLevel,
+ DefaultMessageNotifications? defaultMessageNotifications, SystemChannelFlags? systemChannelFlags)
{
- Name = name,
- RegionId = regionId,
- DefaultMessageNotifications = defaultMessageNotifications,
- VerificationLevel = verificationLevel,
- IconBase64 = iconb64,
- SystemChannelFlags = systemChannelFlags
+ var pld = new RestGuildCreatePayload
+ {
+ Name = name,
+ RegionId = regionId,
+ DefaultMessageNotifications = defaultMessageNotifications,
+ VerificationLevel = verificationLevel,
+ IconBase64 = iconb64,
+ SystemChannelFlags = systemChannelFlags
- };
+ };
- var route = $"{Endpoints.GUILDS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
+ var route = $"{Endpoints.GUILDS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var json = JObject.Parse(res.Response);
- var rawMembers = (JArray)json["members"];
- var guild = json.ToDiscordObject<DiscordGuild>();
+ var json = JObject.Parse(res.Response);
+ var rawMembers = (JArray)json["members"];
+ var guild = json.ToDiscordObject<DiscordGuild>();
- if (this.Discord is DiscordClient dc)
- await dc.OnGuildCreateEventAsync(guild, rawMembers, null).ConfigureAwait(false);
- return guild;
- }
+ if (this.Discord is DiscordClient dc)
+ await dc.OnGuildCreateEventAsync(guild, rawMembers, null).ConfigureAwait(false);
+ return guild;
+ }
- /// <summary>
- /// Creates the guild from template async.
- /// </summary>
- /// <param name="templateCode">The template_code.</param>
- /// <param name="name">The name.</param>
- /// <param name="iconb64">The iconb64.</param>
- internal async Task<DiscordGuild> CreateGuildFromTemplateAsync(string templateCode, string name, Optional<string> iconb64)
- {
- var pld = new RestGuildCreateFromTemplatePayload
+ /// <summary>
+ /// Creates the guild from template async.
+ /// </summary>
+ /// <param name="templateCode">The template_code.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="iconb64">The iconb64.</param>
+ internal async Task<DiscordGuild> CreateGuildFromTemplateAsync(string templateCode, string name, Optional<string> iconb64)
{
- Name = name,
- IconBase64 = iconb64
- };
+ var pld = new RestGuildCreateFromTemplatePayload
+ {
+ Name = name,
+ IconBase64 = iconb64
+ };
- var route = $"{Endpoints.GUILDS}{Endpoints.TEMPLATES}/:template_code";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {template_code = templateCode }, out var path);
+ var route = $"{Endpoints.GUILDS}{Endpoints.TEMPLATES}/:template_code";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {template_code = templateCode }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var json = JObject.Parse(res.Response);
- var rawMembers = (JArray)json["members"];
- var guild = json.ToDiscordObject<DiscordGuild>();
+ var json = JObject.Parse(res.Response);
+ var rawMembers = (JArray)json["members"];
+ var guild = json.ToDiscordObject<DiscordGuild>();
- if (this.Discord is DiscordClient dc)
- await dc.OnGuildCreateEventAsync(guild, rawMembers, null).ConfigureAwait(false);
- return guild;
- }
+ if (this.Discord is DiscordClient dc)
+ await dc.OnGuildCreateEventAsync(guild, rawMembers, null).ConfigureAwait(false);
+ return guild;
+ }
- /// <summary>
- /// Deletes the guild async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- internal async Task DeleteGuildAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId }, out var path);
+ /// <summary>
+ /// Deletes the guild async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ internal async Task DeleteGuildAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route).ConfigureAwait(false);
- if (this.Discord is DiscordClient dc)
- {
- var gld = dc.GuildsInternal[guildId];
- await dc.OnGuildDeleteEventAsync(gld).ConfigureAwait(false);
+ if (this.Discord is DiscordClient dc)
+ {
+ var gld = dc.GuildsInternal[guildId];
+ await dc.OnGuildDeleteEventAsync(gld).ConfigureAwait(false);
+ }
}
- }
- /// <summary>
- /// Modifies the guild.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- /// <param name="name">The name.</param>
- /// <param name="verificationLevel">The verification level.</param>
- /// <param name="defaultMessageNotifications">The default message notifications.</param>
- /// <param name="mfaLevel">The mfa level.</param>
- /// <param name="explicitContentFilter">The explicit content filter.</param>
- /// <param name="afkChannelId">The afk channel id.</param>
- /// <param name="afkTimeout">The afk timeout.</param>
- /// <param name="iconb64">The iconb64.</param>
- /// <param name="ownerId">The owner id.</param>
- /// <param name="splashb64">The splashb64.</param>
- /// <param name="systemChannelId">The system channel id.</param>
- /// <param name="systemChannelFlags">The system channel flags.</param>
- /// <param name="publicUpdatesChannelId">The public updates channel id.</param>
- /// <param name="rulesChannelId">The rules channel id.</param>
- /// <param name="description">The description.</param>
- /// <param name="bannerb64">The banner base64.</param>
- /// <param name="discoverySplashb64">The discovery base64.</param>
- /// <param name="preferredLocale">The preferred locale.</param>
- /// <param name="premiumProgressBarEnabled">Whether the premium progress bar should be enabled.</param>
- /// <param name="reason">The reason.</param>
- internal async Task<DiscordGuild> ModifyGuildAsync(ulong guildId, Optional<string> name, Optional<VerificationLevel> verificationLevel,
- Optional<DefaultMessageNotifications> defaultMessageNotifications, Optional<MfaLevel> mfaLevel,
- Optional<ExplicitContentFilter> explicitContentFilter, Optional<ulong?> afkChannelId,
- Optional<int> afkTimeout, Optional<string> iconb64, Optional<ulong> ownerId, Optional<string> splashb64,
- Optional<ulong?> systemChannelId, Optional<SystemChannelFlags> systemChannelFlags,
- Optional<ulong?> publicUpdatesChannelId, Optional<ulong?> rulesChannelId, Optional<string> description,
- Optional<string> bannerb64, Optional<string> discoverySplashb64, Optional<string> preferredLocale, Optional<bool> premiumProgressBarEnabled, string reason)
- {
- var pld = new RestGuildModifyPayload
- {
- Name = name,
- VerificationLevel = verificationLevel,
- DefaultMessageNotifications = defaultMessageNotifications,
- MfaLevel = mfaLevel,
- ExplicitContentFilter = explicitContentFilter,
- AfkChannelId = afkChannelId,
- AfkTimeout = afkTimeout,
- IconBase64 = iconb64,
- SplashBase64 = splashb64,
- BannerBase64 = bannerb64,
- DiscoverySplashBase64 = discoverySplashb64,
- OwnerId = ownerId,
- SystemChannelId = systemChannelId,
- SystemChannelFlags = systemChannelFlags,
- RulesChannelId = rulesChannelId,
- PublicUpdatesChannelId = publicUpdatesChannelId,
- PreferredLocale = preferredLocale,
- Description = description,
- PremiumProgressBarEnabled = premiumProgressBarEnabled
- };
-
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
-
- var route = $"{Endpoints.GUILDS}/:guild_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id = guildId }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
-
- var json = JObject.Parse(res.Response);
- var rawMembers = (JArray)json["members"];
- var guild = json.ToDiscordObject<DiscordGuild>();
- foreach (var r in guild.RolesInternal.Values)
- r.GuildId = guild.Id;
-
- if (this.Discord is DiscordClient dc)
- await dc.OnGuildUpdateEventAsync(guild, rawMembers).ConfigureAwait(false);
- return guild;
- }
+ /// <summary>
+ /// Modifies the guild.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="verificationLevel">The verification level.</param>
+ /// <param name="defaultMessageNotifications">The default message notifications.</param>
+ /// <param name="mfaLevel">The mfa level.</param>
+ /// <param name="explicitContentFilter">The explicit content filter.</param>
+ /// <param name="afkChannelId">The afk channel id.</param>
+ /// <param name="afkTimeout">The afk timeout.</param>
+ /// <param name="iconb64">The iconb64.</param>
+ /// <param name="ownerId">The owner id.</param>
+ /// <param name="splashb64">The splashb64.</param>
+ /// <param name="systemChannelId">The system channel id.</param>
+ /// <param name="systemChannelFlags">The system channel flags.</param>
+ /// <param name="publicUpdatesChannelId">The public updates channel id.</param>
+ /// <param name="rulesChannelId">The rules channel id.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="bannerb64">The banner base64.</param>
+ /// <param name="discoverySplashb64">The discovery base64.</param>
+ /// <param name="preferredLocale">The preferred locale.</param>
+ /// <param name="premiumProgressBarEnabled">Whether the premium progress bar should be enabled.</param>
+ /// <param name="reason">The reason.</param>
+ internal async Task<DiscordGuild> ModifyGuildAsync(ulong guildId, Optional<string> name, Optional<VerificationLevel> verificationLevel,
+ Optional<DefaultMessageNotifications> defaultMessageNotifications, Optional<MfaLevel> mfaLevel,
+ Optional<ExplicitContentFilter> explicitContentFilter, Optional<ulong?> afkChannelId,
+ Optional<int> afkTimeout, Optional<string> iconb64, Optional<ulong> ownerId, Optional<string> splashb64,
+ Optional<ulong?> systemChannelId, Optional<SystemChannelFlags> systemChannelFlags,
+ Optional<ulong?> publicUpdatesChannelId, Optional<ulong?> rulesChannelId, Optional<string> description,
+ Optional<string> bannerb64, Optional<string> discoverySplashb64, Optional<string> preferredLocale, Optional<bool> premiumProgressBarEnabled, string reason)
+ {
+ var pld = new RestGuildModifyPayload
+ {
+ Name = name,
+ VerificationLevel = verificationLevel,
+ DefaultMessageNotifications = defaultMessageNotifications,
+ MfaLevel = mfaLevel,
+ ExplicitContentFilter = explicitContentFilter,
+ AfkChannelId = afkChannelId,
+ AfkTimeout = afkTimeout,
+ IconBase64 = iconb64,
+ SplashBase64 = splashb64,
+ BannerBase64 = bannerb64,
+ DiscoverySplashBase64 = discoverySplashb64,
+ OwnerId = ownerId,
+ SystemChannelId = systemChannelId,
+ SystemChannelFlags = systemChannelFlags,
+ RulesChannelId = rulesChannelId,
+ PublicUpdatesChannelId = publicUpdatesChannelId,
+ PreferredLocale = preferredLocale,
+ Description = description,
+ PremiumProgressBarEnabled = premiumProgressBarEnabled
+ };
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
+ var route = $"{Endpoints.GUILDS}/:guild_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id = guildId }, out var path);
- /// <summary>
- /// Modifies the guild community settings.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- /// <param name="features">The guild features.</param>
- /// <param name="rulesChannelId">The rules channel id.</param>
- /// <param name="publicUpdatesChannelId">The public updates channel id.</param>
- /// <param name="preferredLocale">The preferred locale.</param>
- /// <param name="description">The description.</param>
- /// <param name="defaultMessageNotifications">The default message notifications.</param>
- /// <param name="explicitContentFilter">The explicit content filter.</param>
- /// <param name="verificationLevel">The verification level.</param>
- /// <param name="reason">The reason.</param>
- internal async Task<DiscordGuild> ModifyGuildCommunitySettingsAsync(ulong guildId, List<string> features, Optional<ulong?> rulesChannelId, Optional<ulong?> publicUpdatesChannelId, string preferredLocale, string description, DefaultMessageNotifications defaultMessageNotifications, ExplicitContentFilter explicitContentFilter, VerificationLevel verificationLevel, string reason)
- {
- var pld = new RestGuildCommunityModifyPayload
- {
- VerificationLevel = verificationLevel,
- DefaultMessageNotifications = defaultMessageNotifications,
- ExplicitContentFilter = explicitContentFilter,
- RulesChannelId = rulesChannelId,
- PublicUpdatesChannelId = publicUpdatesChannelId,
- PreferredLocale = preferredLocale,
- Description = Optional.FromNullable(description),
- Features = features
- };
-
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
-
- var route = $"{Endpoints.GUILDS}/:guild_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id = guildId }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
-
- var json = JObject.Parse(res.Response);
- var rawMembers = (JArray)json["members"];
- var guild = json.ToDiscordObject<DiscordGuild>();
- foreach (var r in guild.RolesInternal.Values)
- r.GuildId = guild.Id;
-
- if (this.Discord is DiscordClient dc)
- await dc.OnGuildUpdateEventAsync(guild, rawMembers).ConfigureAwait(false);
- return guild;
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- /// <summary>
- /// Implements https://discord.com/developers/docs/resources/guild#get-guild-bans.
- /// </summary>
- internal async Task<IReadOnlyList<DiscordBan>> GetGuildBansAsync(ulong guildId, int? limit, ulong? before, ulong? after)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var json = JObject.Parse(res.Response);
+ var rawMembers = (JArray)json["members"];
+ var guild = json.ToDiscordObject<DiscordGuild>();
+ foreach (var r in guild.RolesInternal.Values)
+ r.GuildId = guild.Id;
+
+ if (this.Discord is DiscordClient dc)
+ await dc.OnGuildUpdateEventAsync(guild, rawMembers).ConfigureAwait(false);
+ return guild;
+ }
- var urlParams = new Dictionary<string, string>();
- if (limit != null)
- urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
- if (before != null)
- urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
- if (after != null)
- urlParams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
- var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var bansRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordBan>>(res.Response).Select(xb =>
+ /// <summary>
+ /// Modifies the guild community settings.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="features">The guild features.</param>
+ /// <param name="rulesChannelId">The rules channel id.</param>
+ /// <param name="publicUpdatesChannelId">The public updates channel id.</param>
+ /// <param name="preferredLocale">The preferred locale.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="defaultMessageNotifications">The default message notifications.</param>
+ /// <param name="explicitContentFilter">The explicit content filter.</param>
+ /// <param name="verificationLevel">The verification level.</param>
+ /// <param name="reason">The reason.</param>
+ internal async Task<DiscordGuild> ModifyGuildCommunitySettingsAsync(ulong guildId, List<string> features, Optional<ulong?> rulesChannelId, Optional<ulong?> publicUpdatesChannelId, string preferredLocale, string description, DefaultMessageNotifications defaultMessageNotifications, ExplicitContentFilter explicitContentFilter, VerificationLevel verificationLevel, string reason)
{
- if (!this.Discord.TryGetCachedUserInternal(xb.RawUser.Id, out var usr))
+ var pld = new RestGuildCommunityModifyPayload
{
- usr = new DiscordUser(xb.RawUser) { Discord = this.Discord };
- usr = this.Discord.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
- {
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
- }
-
- xb.User = usr;
- return xb;
- });
- var bans = new ReadOnlyCollection<DiscordBan>(new List<DiscordBan>(bansRaw));
-
- return bans;
- }
+ VerificationLevel = verificationLevel,
+ DefaultMessageNotifications = defaultMessageNotifications,
+ ExplicitContentFilter = explicitContentFilter,
+ RulesChannelId = rulesChannelId,
+ PublicUpdatesChannelId = publicUpdatesChannelId,
+ PreferredLocale = preferredLocale,
+ Description = Optional.FromNullable(description),
+ Features = features
+ };
- /// <summary>
- /// Creates the guild ban async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="deleteMessageDays">The delete_message_days.</param>
- /// <param name="reason">The reason.</param>
- internal Task CreateGuildBanAsync(ulong guildId, ulong userId, int deleteMessageDays, string reason)
- {
- if (deleteMessageDays < 0 || deleteMessageDays > 7)
- throw new ArgumentException("Delete message days must be a number between 0 and 7.", nameof(deleteMessageDays));
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var urlParams = new Dictionary<string, string>
- {
- ["delete_message_days"] = deleteMessageDays.ToString(CultureInfo.InvariantCulture)
- };
+ var route = $"{Endpoints.GUILDS}/:guild_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id = guildId }, out var path);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, user_id = userId }, out var path);
+ var json = JObject.Parse(res.Response);
+ var rawMembers = (JArray)json["members"];
+ var guild = json.ToDiscordObject<DiscordGuild>();
+ foreach (var r in guild.RolesInternal.Values)
+ r.GuildId = guild.Id;
- var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers);
- }
+ if (this.Discord is DiscordClient dc)
+ await dc.OnGuildUpdateEventAsync(guild, rawMembers).ConfigureAwait(false);
+ return guild;
+ }
- /// <summary>
- /// Removes the guild ban async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="reason">The reason.</param>
- internal Task RemoveGuildBanAsync(ulong guildId, ulong userId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ /// <summary>
+ /// Implements https://discord.com/developers/docs/resources/guild#get-guild-bans.
+ /// </summary>
+ internal async Task<IReadOnlyList<DiscordBan>> GetGuildBansAsync(ulong guildId, int? limit, ulong? before, ulong? after)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, user_id = userId }, out var path);
+ var urlParams = new Dictionary<string, string>();
+ if (limit != null)
+ urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
+ if (before != null)
+ urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
+ if (after != null)
+ urlParams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- /// <summary>
- /// Leaves the guild async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ var bansRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordBan>>(res.Response).Select(xb =>
+ {
+ if (!this.Discord.TryGetCachedUserInternal(xb.RawUser.Id, out var usr))
+ {
+ usr = new DiscordUser(xb.RawUser) { Discord = this.Discord };
+ usr = this.Discord.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ return old;
+ });
+ }
- internal Task LeaveGuildAsync(ulong guildId)
- {
- var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.GUILDS}/:guild_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId }, out var path);
+ xb.User = usr;
+ return xb;
+ });
+ var bans = new ReadOnlyCollection<DiscordBan>(new List<DiscordBan>(bansRaw));
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
- }
+ return bans;
+ }
- /// <summary>
- /// Adds the guild member async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="accessToken">The access_token.</param>
- /// <param name="nick">The nick.</param>
- /// <param name="roles">The roles.</param>
- /// <param name="muted">If true, muted.</param>
- /// <param name="deafened">If true, deafened.</param>
-
- internal async Task<DiscordMember> AddGuildMemberAsync(ulong guildId, ulong userId, string accessToken, string nick, IEnumerable<DiscordRole> roles, bool muted, bool deafened)
- {
- var pld = new RestGuildMemberAddPayload
+ /// <summary>
+ /// Creates the guild ban async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="deleteMessageDays">The delete_message_days.</param>
+ /// <param name="reason">The reason.</param>
+ internal Task CreateGuildBanAsync(ulong guildId, ulong userId, int deleteMessageDays, string reason)
{
- AccessToken = accessToken,
- Nickname = nick ?? "",
- Roles = roles ?? new List<DiscordRole>(),
- Deaf = deafened,
- Mute = muted
- };
+ if (deleteMessageDays < 0 || deleteMessageDays > 7)
+ throw new ArgumentException("Delete message days must be a number between 0 and 7.", nameof(deleteMessageDays));
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, user_id = userId }, out var path);
+ var urlParams = new Dictionary<string, string>
+ {
+ ["delete_message_days"] = deleteMessageDays.ToString(CultureInfo.InvariantCulture)
+ };
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var tm = JsonConvert.DeserializeObject<TransportMember>(res.Response);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, user_id = userId }, out var path);
- return new DiscordMember(tm) { Discord = this.Discord, GuildId = guildId };
- }
+ var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers);
+ }
- /// <summary>
- /// Lists the guild members async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="limit">The limit.</param>
- /// <param name="after">The after.</param>
+ /// <summary>
+ /// Removes the guild ban async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="reason">The reason.</param>
+ internal Task RemoveGuildBanAsync(ulong guildId, ulong userId, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- internal async Task<IReadOnlyList<TransportMember>> ListGuildMembersAsync(ulong guildId, int? limit, ulong? after)
- {
- var urlParams = new Dictionary<string, string>();
- if (limit != null && limit > 0)
- urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
- if (after != null)
- urlParams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, user_id = userId }, out var path);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ /// <summary>
+ /// Leaves the guild async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- var membersRaw = JsonConvert.DeserializeObject<List<TransportMember>>(res.Response);
- return new ReadOnlyCollection<TransportMember>(membersRaw);
- }
+ internal Task LeaveGuildAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.GUILDS}/:guild_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId }, out var path);
- /// <summary>
- /// Adds the guild member role async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="roleId">The role_id.</param>
- /// <param name="reason">The reason.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ }
- internal Task AddGuildMemberRoleAsync(ulong guildId, ulong userId, ulong roleId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ /// <summary>
+ /// Adds the guild member async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="accessToken">The access_token.</param>
+ /// <param name="nick">The nick.</param>
+ /// <param name="roles">The roles.</param>
+ /// <param name="muted">If true, muted.</param>
+ /// <param name="deafened">If true, deafened.</param>
+
+ internal async Task<DiscordMember> AddGuildMemberAsync(ulong guildId, ulong userId, string accessToken, string nick, IEnumerable<DiscordRole> roles, bool muted, bool deafened)
+ {
+ var pld = new RestGuildMemberAddPayload
+ {
+ AccessToken = accessToken,
+ Nickname = nick ?? "",
+ Roles = roles ?? new List<DiscordRole>(),
+ Deaf = deafened,
+ Mute = muted
+ };
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id{Endpoints.ROLES}/:role_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, user_id = userId, role_id = roleId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, user_id = userId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers);
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- /// <summary>
- /// Removes the guild member role async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="roleId">The role_id.</param>
- /// <param name="reason">The reason.</param>
+ var tm = JsonConvert.DeserializeObject<TransportMember>(res.Response);
- internal Task RemoveGuildMemberRoleAsync(ulong guildId, ulong userId, ulong roleId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ return new DiscordMember(tm) { Discord = this.Discord, GuildId = guildId };
+ }
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id{Endpoints.ROLES}/:role_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, user_id = userId, role_id = roleId }, out var path);
+ /// <summary>
+ /// Lists the guild members async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="limit">The limit.</param>
+ /// <param name="after">The after.</param>
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ internal async Task<IReadOnlyList<TransportMember>> ListGuildMembersAsync(ulong guildId, int? limit, ulong? after)
+ {
+ var urlParams = new Dictionary<string, string>();
+ if (limit != null && limit > 0)
+ urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
+ if (after != null)
+ urlParams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
- /// <summary>
- /// Modifies the guild channel position async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="pld">The pld.</param>
- /// <param name="reason">The reason.</param>
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- internal Task ModifyGuildChannelPositionAsync(ulong guildId, IEnumerable<RestGuildChannelReorderPayload> pld, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
+ var membersRaw = JsonConvert.DeserializeObject<List<TransportMember>>(res.Response);
+ return new ReadOnlyCollection<TransportMember>(membersRaw);
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
- }
+ /// <summary>
+ /// Adds the guild member role async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="roleId">The role_id.</param>
+ /// <param name="reason">The reason.</param>
- /// <summary>
- /// Modifies the guild channel parent async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="pld">The pld.</param>
- /// <param name="reason">The reason.</param>
+ internal Task AddGuildMemberRoleAsync(ulong guildId, ulong userId, ulong roleId, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- internal Task ModifyGuildChannelParentAsync(ulong guildId, IEnumerable<RestGuildChannelNewParentPayload> pld, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id{Endpoints.ROLES}/:role_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, user_id = userId, role_id = roleId }, out var path);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers);
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
- }
+ /// <summary>
+ /// Removes the guild member role async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="roleId">The role_id.</param>
+ /// <param name="reason">The reason.</param>
- /// <summary>
- /// Detaches the guild channel parent async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="pld">The pld.</param>
- /// <param name="reason">The reason.</param>
+ internal Task RemoveGuildMemberRoleAsync(ulong guildId, ulong userId, ulong roleId, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- internal Task DetachGuildChannelParentAsync(ulong guildId, IEnumerable<RestGuildChannelNoParentPayload> pld, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id{Endpoints.ROLES}/:role_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, user_id = userId, role_id = roleId }, out var path);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
- }
+ /// <summary>
+ /// Modifies the guild channel position async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="pld">The pld.</param>
+ /// <param name="reason">The reason.</param>
- /// <summary>
- /// Modifies the guild role position async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="pld">The pld.</param>
- /// <param name="reason">The reason.</param>
+ internal Task ModifyGuildChannelPositionAsync(ulong guildId, IEnumerable<RestGuildChannelReorderPayload> pld, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- internal Task ModifyGuildRolePositionAsync(ulong guildId, IEnumerable<RestGuildRoleReorderPayload> pld, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
- }
+ /// <summary>
+ /// Modifies the guild channel parent async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="pld">The pld.</param>
+ /// <param name="reason">The reason.</param>
- /// <summary>
- /// Gets the audit logs async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="limit">The limit.</param>
- /// <param name="after">The after.</param>
- /// <param name="before">The before.</param>
- /// <param name="responsible">The responsible.</param>
- /// <param name="actionType">The action_type.</param>
-
- internal async Task<AuditLog> GetAuditLogsAsync(ulong guildId, int limit, ulong? after, ulong? before, ulong? responsible, int? actionType)
- {
- var urlParams = new Dictionary<string, string>
+ internal Task ModifyGuildChannelParentAsync(ulong guildId, IEnumerable<RestGuildChannelNewParentPayload> pld, string reason)
{
- ["limit"] = limit.ToString(CultureInfo.InvariantCulture)
- };
- if (after != null)
- urlParams["after"] = after?.ToString(CultureInfo.InvariantCulture);
- if (before != null)
- urlParams["before"] = before?.ToString(CultureInfo.InvariantCulture);
- if (responsible != null)
- urlParams["user_id"] = responsible?.ToString(CultureInfo.InvariantCulture);
- if (actionType != null)
- urlParams["action_type"] = actionType?.ToString(CultureInfo.InvariantCulture);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.AUDIT_LOGS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
+ }
- var auditLogDataRaw = JsonConvert.DeserializeObject<AuditLog>(res.Response);
+ /// <summary>
+ /// Detaches the guild channel parent async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="pld">The pld.</param>
+ /// <param name="reason">The reason.</param>
- return auditLogDataRaw;
- }
+ internal Task DetachGuildChannelParentAsync(ulong guildId, IEnumerable<RestGuildChannelNoParentPayload> pld, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- /// <summary>
- /// Gets the guild vanity url async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
- internal async Task<DiscordInvite> GetGuildVanityUrlAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VANITY_URL}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ /// <summary>
+ /// Modifies the guild role position async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="pld">The pld.</param>
+ /// <param name="reason">The reason.</param>
- var invite = JsonConvert.DeserializeObject<DiscordInvite>(res.Response);
+ internal Task ModifyGuildRolePositionAsync(ulong guildId, IEnumerable<RestGuildRoleReorderPayload> pld, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- return invite;
- }
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
- /// <summary>
- /// Gets the guild widget async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
-
- internal async Task<DiscordWidget> GetGuildWidgetAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET_JSON}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ /// <summary>
+ /// Gets the audit logs async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="limit">The limit.</param>
+ /// <param name="after">The after.</param>
+ /// <param name="before">The before.</param>
+ /// <param name="responsible">The responsible.</param>
+ /// <param name="actionType">The action_type.</param>
+
+ internal async Task<AuditLog> GetAuditLogsAsync(ulong guildId, int limit, ulong? after, ulong? before, ulong? responsible, int? actionType)
+ {
+ var urlParams = new Dictionary<string, string>
+ {
+ ["limit"] = limit.ToString(CultureInfo.InvariantCulture)
+ };
+ if (after != null)
+ urlParams["after"] = after?.ToString(CultureInfo.InvariantCulture);
+ if (before != null)
+ urlParams["before"] = before?.ToString(CultureInfo.InvariantCulture);
+ if (responsible != null)
+ urlParams["user_id"] = responsible?.ToString(CultureInfo.InvariantCulture);
+ if (actionType != null)
+ urlParams["action_type"] = actionType?.ToString(CultureInfo.InvariantCulture);
- var json = JObject.Parse(res.Response);
- var rawChannels = (JArray)json["channels"];
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.AUDIT_LOGS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var ret = json.ToDiscordObject<DiscordWidget>();
- ret.Discord = this.Discord;
- ret.Guild = this.Discord.Guilds[guildId];
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- ret.Channels = ret.Guild == null
- ? rawChannels.Select(r => new DiscordChannel
- {
- Id = (ulong)r["id"],
- Name = r["name"].ToString(),
- Position = (int)r["position"]
- }).ToList()
- : rawChannels.Select(r =>
- {
- var c = ret.Guild.GetChannel((ulong)r["id"]);
- c.Position = (int)r["position"];
- return c;
- }).ToList();
+ var auditLogDataRaw = JsonConvert.DeserializeObject<AuditLog>(res.Response);
- return ret;
- }
+ return auditLogDataRaw;
+ }
- /// <summary>
- /// Gets the guild widget settings async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ /// <summary>
+ /// Gets the guild vanity url async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- internal async Task<DiscordWidgetSettings> GetGuildWidgetSettingsAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ internal async Task<DiscordInvite> GetGuildVanityUrlAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VANITY_URL}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordWidgetSettings>(res.Response);
- ret.Guild = this.Discord.Guilds[guildId];
+ var invite = JsonConvert.DeserializeObject<DiscordInvite>(res.Response);
- return ret;
- }
+ return invite;
+ }
- /// <summary>
- /// Modifies the guild widget settings async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="isEnabled">If true, is enabled.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="reason">The reason.</param>
+ /// <summary>
+ /// Gets the guild widget async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- internal async Task<DiscordWidgetSettings> ModifyGuildWidgetSettingsAsync(ulong guildId, bool? isEnabled, ulong? channelId, string reason)
- {
- var pld = new RestGuildWidgetSettingsPayload
+ internal async Task<DiscordWidget> GetGuildWidgetAsync(ulong guildId)
{
- Enabled = isEnabled,
- ChannelId = channelId
- };
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET_JSON}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
+ var json = JObject.Parse(res.Response);
+ var rawChannels = (JArray)json["channels"];
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var ret = json.ToDiscordObject<DiscordWidget>();
+ ret.Discord = this.Discord;
+ ret.Guild = this.Discord.Guilds[guildId];
- var ret = JsonConvert.DeserializeObject<DiscordWidgetSettings>(res.Response);
- ret.Guild = this.Discord.Guilds[guildId];
+ ret.Channels = ret.Guild == null
+ ? rawChannels.Select(r => new DiscordChannel
+ {
+ Id = (ulong)r["id"],
+ Name = r["name"].ToString(),
+ Position = (int)r["position"]
+ }).ToList()
+ : rawChannels.Select(r =>
+ {
+ var c = ret.Guild.GetChannel((ulong)r["id"]);
+ c.Position = (int)r["position"];
+ return c;
+ }).ToList();
- return ret;
- }
+ return ret;
+ }
- /// <summary>
- /// Gets the guild templates async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ /// <summary>
+ /// Gets the guild widget settings async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- internal async Task<IReadOnlyList<DiscordGuildTemplate>> GetGuildTemplatesAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ internal async Task<DiscordWidgetSettings> GetGuildWidgetSettingsAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var templatesRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordGuildTemplate>>(res.Response);
+ var ret = JsonConvert.DeserializeObject<DiscordWidgetSettings>(res.Response);
+ ret.Guild = this.Discord.Guilds[guildId];
- return new ReadOnlyCollection<DiscordGuildTemplate>(new List<DiscordGuildTemplate>(templatesRaw));
- }
+ return ret;
+ }
- /// <summary>
- /// Creates the guild template async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="description">The description.</param>
+ /// <summary>
+ /// Modifies the guild widget settings async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="isEnabled">If true, is enabled.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="reason">The reason.</param>
- internal async Task<DiscordGuildTemplate> CreateGuildTemplateAsync(ulong guildId, string name, string description)
- {
- var pld = new RestGuildTemplateCreateOrModifyPayload
+ internal async Task<DiscordWidgetSettings> ModifyGuildWidgetSettingsAsync(ulong guildId, bool? isEnabled, ulong? channelId, string reason)
{
- Name = name,
- Description = description
- };
+ var pld = new RestGuildWidgetSettingsPayload
+ {
+ Enabled = isEnabled,
+ ChannelId = channelId
+ };
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
- var ret = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- return ret;
- }
+ var ret = JsonConvert.DeserializeObject<DiscordWidgetSettings>(res.Response);
+ ret.Guild = this.Discord.Guilds[guildId];
- /// <summary>
- /// Syncs the guild template async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="templateCode">The template_code.</param>
+ return ret;
+ }
- internal async Task<DiscordGuildTemplate> SyncGuildTemplateAsync(ulong guildId, string templateCode)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, template_code = templateCode }, out var path);
+ /// <summary>
+ /// Gets the guild templates async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route).ConfigureAwait(false);
+ internal async Task<IReadOnlyList<DiscordGuildTemplate>> GetGuildTemplatesAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var templateRaw = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- return templateRaw;
- }
+ var templatesRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordGuildTemplate>>(res.Response);
- /// <summary>
- /// Modifies the guild template async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="templateCode">The template_code.</param>
- /// <param name="name">The name.</param>
- /// <param name="description">The description.</param>
+ return new ReadOnlyCollection<DiscordGuildTemplate>(new List<DiscordGuildTemplate>(templatesRaw));
+ }
- internal async Task<DiscordGuildTemplate> ModifyGuildTemplateAsync(ulong guildId, string templateCode, string name, string description)
- {
- var pld = new RestGuildTemplateCreateOrModifyPayload
+ /// <summary>
+ /// Creates the guild template async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="description">The description.</param>
+
+ internal async Task<DiscordGuildTemplate> CreateGuildTemplateAsync(ulong guildId, string name, string description)
{
- Name = name,
- Description = description
- };
+ var pld = new RestGuildTemplateCreateOrModifyPayload
+ {
+ Name = name,
+ Description = description
+ };
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, template_code = templateCode }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var templateRaw = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
+ var ret = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
- return templateRaw;
- }
+ return ret;
+ }
- /// <summary>
- /// Deletes the guild template async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="templateCode">The template_code.</param>
+ /// <summary>
+ /// Syncs the guild template async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="templateCode">The template_code.</param>
- internal async Task<DiscordGuildTemplate> DeleteGuildTemplateAsync(ulong guildId, string templateCode)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, template_code = templateCode }, out var path);
+ internal async Task<DiscordGuildTemplate> SyncGuildTemplateAsync(ulong guildId, string templateCode)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, template_code = templateCode }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route).ConfigureAwait(false);
- var templateRaw = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
+ var templateRaw = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
- return templateRaw;
- }
+ return templateRaw;
+ }
- /// <summary>
- /// Gets the guild membership screening form async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ /// <summary>
+ /// Modifies the guild template async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="templateCode">The template_code.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="description">The description.</param>
- internal async Task<DiscordGuildMembershipScreening> GetGuildMembershipScreeningFormAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBER_VERIFICATION}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ internal async Task<DiscordGuildTemplate> ModifyGuildTemplateAsync(ulong guildId, string templateCode, string name, string description)
+ {
+ var pld = new RestGuildTemplateCreateOrModifyPayload
+ {
+ Name = name,
+ Description = description
+ };
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, template_code = templateCode }, out var path);
- var screeningRaw = JsonConvert.DeserializeObject<DiscordGuildMembershipScreening>(res.Response);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- return screeningRaw;
- }
+ var templateRaw = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
- /// <summary>
- /// Modifies the guild membership screening form async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="enabled">The enabled.</param>
- /// <param name="fields">The fields.</param>
- /// <param name="description">The description.</param>
+ return templateRaw;
+ }
- internal async Task<DiscordGuildMembershipScreening> ModifyGuildMembershipScreeningFormAsync(ulong guildId, Optional<bool> enabled, Optional<DiscordGuildMembershipScreeningField[]> fields, Optional<string> description)
- {
- var pld = new RestGuildMembershipScreeningFormModifyPayload
+ /// <summary>
+ /// Deletes the guild template async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="templateCode">The template_code.</param>
+
+ internal async Task<DiscordGuildTemplate> DeleteGuildTemplateAsync(ulong guildId, string templateCode)
{
- Enabled = enabled,
- Description = description,
- Fields = fields
- };
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, template_code = templateCode }, out var path);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBER_VERIFICATION}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route).ConfigureAwait(false);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var templateRaw = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
- var screeningRaw = JsonConvert.DeserializeObject<DiscordGuildMembershipScreening>(res.Response);
+ return templateRaw;
+ }
- return screeningRaw;
- }
+ /// <summary>
+ /// Gets the guild membership screening form async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- /// <summary>
- /// Gets the guild welcome screen async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ internal async Task<DiscordGuildMembershipScreening> GetGuildMembershipScreeningFormAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBER_VERIFICATION}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- internal async Task<DiscordGuildWelcomeScreen> GetGuildWelcomeScreenAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WELCOME_SCREEN}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var screeningRaw = JsonConvert.DeserializeObject<DiscordGuildMembershipScreening>(res.Response);
- var ret = JsonConvert.DeserializeObject<DiscordGuildWelcomeScreen>(res.Response);
- return ret;
- }
+ return screeningRaw;
+ }
- /// <summary>
- /// Modifies the guild welcome screen async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="enabled">The enabled.</param>
- /// <param name="welcomeChannels">The welcome channels.</param>
- /// <param name="description">The description.</param>
+ /// <summary>
+ /// Modifies the guild membership screening form async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="enabled">The enabled.</param>
+ /// <param name="fields">The fields.</param>
+ /// <param name="description">The description.</param>
- internal async Task<DiscordGuildWelcomeScreen> ModifyGuildWelcomeScreenAsync(ulong guildId, Optional<bool> enabled, Optional<IEnumerable<DiscordGuildWelcomeScreenChannel>> welcomeChannels, Optional<string> description)
- {
- var pld = new RestGuildWelcomeScreenModifyPayload
+ internal async Task<DiscordGuildMembershipScreening> ModifyGuildMembershipScreeningFormAsync(ulong guildId, Optional<bool> enabled, Optional<DiscordGuildMembershipScreeningField[]> fields, Optional<string> description)
{
- Enabled = enabled,
- WelcomeChannels = welcomeChannels,
- Description = description
- };
+ var pld = new RestGuildMembershipScreeningFormModifyPayload
+ {
+ Enabled = enabled,
+ Description = description,
+ Fields = fields
+ };
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WELCOME_SCREEN}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBER_VERIFICATION}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordGuildWelcomeScreen>(res.Response);
- return ret;
- }
+ var screeningRaw = JsonConvert.DeserializeObject<DiscordGuildMembershipScreening>(res.Response);
- /// <summary>
- /// Updates the current user voice state async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="suppress">If true, suppress.</param>
- /// <param name="requestToSpeakTimestamp">The request to speak timestamp.</param>
+ return screeningRaw;
+ }
- internal async Task UpdateCurrentUserVoiceStateAsync(ulong guildId, ulong channelId, bool? suppress, DateTimeOffset? requestToSpeakTimestamp)
- {
- var pld = new RestGuildUpdateCurrentUserVoiceStatePayload
+ /// <summary>
+ /// Gets the guild welcome screen async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+
+ internal async Task<DiscordGuildWelcomeScreen> GetGuildWelcomeScreenAsync(ulong guildId)
{
- ChannelId = channelId,
- Suppress = suppress,
- RequestToSpeakTimestamp = requestToSpeakTimestamp
- };
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WELCOME_SCREEN}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VOICE_STATES}/@me";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
- }
+ var ret = JsonConvert.DeserializeObject<DiscordGuildWelcomeScreen>(res.Response);
+ return ret;
+ }
- /// <summary>
- /// Updates the user voice state async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="suppress">If true, suppress.</param>
+ /// <summary>
+ /// Modifies the guild welcome screen async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="enabled">The enabled.</param>
+ /// <param name="welcomeChannels">The welcome channels.</param>
+ /// <param name="description">The description.</param>
- internal async Task UpdateUserVoiceStateAsync(ulong guildId, ulong userId, ulong channelId, bool? suppress)
- {
- var pld = new RestGuildUpdateUserVoiceStatePayload
+ internal async Task<DiscordGuildWelcomeScreen> ModifyGuildWelcomeScreenAsync(ulong guildId, Optional<bool> enabled, Optional<IEnumerable<DiscordGuildWelcomeScreenChannel>> welcomeChannels, Optional<string> description)
{
- ChannelId = channelId,
- Suppress = suppress
- };
+ var pld = new RestGuildWelcomeScreenModifyPayload
+ {
+ Enabled = enabled,
+ WelcomeChannels = welcomeChannels,
+ Description = description
+ };
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VOICE_STATES}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, user_id = userId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WELCOME_SCREEN}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
- }
- #endregion
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
- #region Guild Scheduled Events
+ var ret = JsonConvert.DeserializeObject<DiscordGuildWelcomeScreen>(res.Response);
+ return ret;
+ }
- /// <summary>
- /// Creates a scheduled event.
- /// </summary>
- internal async Task<DiscordScheduledEvent> CreateGuildScheduledEventAsync(ulong guildId, ulong? channelId, DiscordScheduledEventEntityMetadata metadata, string name, DateTimeOffset scheduledStartTime, DateTimeOffset? scheduledEndTime, string description, ScheduledEventEntityType type, Optional<string> coverb64, string reason = null)
- {
- var pld = new RestGuildScheduledEventCreatePayload
- {
- ChannelId = channelId,
- EntityMetadata = metadata,
- Name = name,
- ScheduledStartTime = scheduledStartTime,
- ScheduledEndTime = scheduledEndTime,
- Description = description,
- EntityType = type,
- CoverBase64 = coverb64
- };
+ /// <summary>
+ /// Updates the current user voice state async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="suppress">If true, suppress.</param>
+ /// <param name="requestToSpeakTimestamp">The request to speak timestamp.</param>
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ internal async Task UpdateCurrentUserVoiceStateAsync(ulong guildId, ulong channelId, bool? suppress, DateTimeOffset? requestToSpeakTimestamp)
+ {
+ var pld = new RestGuildUpdateCurrentUserVoiceStatePayload
+ {
+ ChannelId = channelId,
+ Suppress = suppress,
+ RequestToSpeakTimestamp = requestToSpeakTimestamp
+ };
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VOICE_STATES}/@me";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld));
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
+ }
- var scheduledEvent = JsonConvert.DeserializeObject<DiscordScheduledEvent>(res.Response);
- var guild = this.Discord.Guilds[guildId];
+ /// <summary>
+ /// Updates the user voice state async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="suppress">If true, suppress.</param>
- scheduledEvent.Discord = this.Discord;
+ internal async Task UpdateUserVoiceStateAsync(ulong guildId, ulong userId, ulong channelId, bool? suppress)
+ {
+ var pld = new RestGuildUpdateUserVoiceStatePayload
+ {
+ ChannelId = channelId,
+ Suppress = suppress
+ };
- if (scheduledEvent.Creator != null)
- scheduledEvent.Creator.Discord = this.Discord;
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VOICE_STATES}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, user_id = userId }, out var path);
- if (this.Discord is DiscordClient dc)
- await dc.OnGuildScheduledEventCreateEventAsync(scheduledEvent, guild);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
+ }
+ #endregion
- return scheduledEvent;
- }
+ #region Guild Scheduled Events
- /// <summary>
- /// Modifies a scheduled event.
- /// </summary>
- internal async Task<DiscordScheduledEvent> ModifyGuildScheduledEventAsync(ulong guildId, ulong scheduledEventId, Optional<ulong?> channelId, Optional<DiscordScheduledEventEntityMetadata> metadata, Optional<string> name, Optional<DateTimeOffset> scheduledStartTime, Optional<DateTimeOffset> scheduledEndTime, Optional<string> description, Optional<ScheduledEventEntityType> type, Optional<ScheduledEventStatus> status, Optional<string> coverb64, string reason = null)
- {
- var pld = new RestGuildScheduledEventModifyPayload
+ /// <summary>
+ /// Creates a scheduled event.
+ /// </summary>
+ internal async Task<DiscordScheduledEvent> CreateGuildScheduledEventAsync(ulong guildId, ulong? channelId, DiscordScheduledEventEntityMetadata metadata, string name, DateTimeOffset scheduledStartTime, DateTimeOffset? scheduledEndTime, string description, ScheduledEventEntityType type, Optional<string> coverb64, string reason = null)
{
- ChannelId = channelId,
- EntityMetadata = metadata,
- Name = name,
- ScheduledStartTime = scheduledStartTime,
- ScheduledEndTime = scheduledEndTime,
- Description = description,
- EntityType = type,
- Status = status,
- CoverBase64 = coverb64
- };
+ var pld = new RestGuildScheduledEventCreatePayload
+ {
+ ChannelId = channelId,
+ EntityMetadata = metadata,
+ Name = name,
+ ScheduledStartTime = scheduledStartTime,
+ ScheduledEndTime = scheduledEndTime,
+ Description = description,
+ EntityType = type,
+ CoverBase64 = coverb64
+ };
+
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
+
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld));
+
+ var scheduledEvent = JsonConvert.DeserializeObject<DiscordScheduledEvent>(res.Response);
+ var guild = this.Discord.Guilds[guildId];
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
+ scheduledEvent.Discord = this.Discord;
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
+ if (scheduledEvent.Creator != null)
+ scheduledEvent.Creator.Discord = this.Discord;
- var scheduledEvent = JsonConvert.DeserializeObject<DiscordScheduledEvent>(res.Response);
- var guild = this.Discord.Guilds[guildId];
+ if (this.Discord is DiscordClient dc)
+ await dc.OnGuildScheduledEventCreateEventAsync(scheduledEvent, guild);
- scheduledEvent.Discord = this.Discord;
+ return scheduledEvent;
+ }
- if (scheduledEvent.Creator != null)
+ /// <summary>
+ /// Modifies a scheduled event.
+ /// </summary>
+ internal async Task<DiscordScheduledEvent> ModifyGuildScheduledEventAsync(ulong guildId, ulong scheduledEventId, Optional<ulong?> channelId, Optional<DiscordScheduledEventEntityMetadata> metadata, Optional<string> name, Optional<DateTimeOffset> scheduledStartTime, Optional<DateTimeOffset> scheduledEndTime, Optional<string> description, Optional<ScheduledEventEntityType> type, Optional<ScheduledEventStatus> status, Optional<string> coverb64, string reason = null)
{
- scheduledEvent.Creator.Discord = this.Discord;
- this.Discord.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ var pld = new RestGuildScheduledEventModifyPayload
{
- old.Username = scheduledEvent.Creator.Username;
- old.Discriminator = scheduledEvent.Creator.Discriminator;
- old.AvatarHash = scheduledEvent.Creator.AvatarHash;
- old.Flags = scheduledEvent.Creator.Flags;
- return old;
- });
- }
+ ChannelId = channelId,
+ EntityMetadata = metadata,
+ Name = name,
+ ScheduledStartTime = scheduledStartTime,
+ ScheduledEndTime = scheduledEndTime,
+ Description = description,
+ EntityType = type,
+ Status = status,
+ CoverBase64 = coverb64
+ };
- if (this.Discord is DiscordClient dc)
- await dc.OnGuildScheduledEventUpdateEventAsync(scheduledEvent, guild);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- return scheduledEvent;
- }
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
- /// <summary>
- /// Modifies a scheduled event.
- /// </summary>
- internal async Task<DiscordScheduledEvent> ModifyGuildScheduledEventStatusAsync(ulong guildId, ulong scheduledEventId, ScheduledEventStatus status, string reason = null)
- {
- var pld = new RestGuildScheduledEventModifyPayload
- {
- Status = status
- };
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var scheduledEvent = JsonConvert.DeserializeObject<DiscordScheduledEvent>(res.Response);
+ var guild = this.Discord.Guilds[guildId];
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
+ scheduledEvent.Discord = this.Discord;
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
+ if (scheduledEvent.Creator != null)
+ {
+ scheduledEvent.Creator.Discord = this.Discord;
+ this.Discord.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ {
+ old.Username = scheduledEvent.Creator.Username;
+ old.Discriminator = scheduledEvent.Creator.Discriminator;
+ old.AvatarHash = scheduledEvent.Creator.AvatarHash;
+ old.Flags = scheduledEvent.Creator.Flags;
+ return old;
+ });
+ }
- var scheduledEvent = JsonConvert.DeserializeObject<DiscordScheduledEvent>(res.Response);
- var guild = this.Discord.Guilds[guildId];
+ if (this.Discord is DiscordClient dc)
+ await dc.OnGuildScheduledEventUpdateEventAsync(scheduledEvent, guild);
- scheduledEvent.Discord = this.Discord;
+ return scheduledEvent;
+ }
- if (scheduledEvent.Creator != null)
+ /// <summary>
+ /// Modifies a scheduled event.
+ /// </summary>
+ internal async Task<DiscordScheduledEvent> ModifyGuildScheduledEventStatusAsync(ulong guildId, ulong scheduledEventId, ScheduledEventStatus status, string reason = null)
{
- scheduledEvent.Creator.Discord = this.Discord;
- this.Discord.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ var pld = new RestGuildScheduledEventModifyPayload
{
- old.Username = scheduledEvent.Creator.Username;
- old.Discriminator = scheduledEvent.Creator.Discriminator;
- old.AvatarHash = scheduledEvent.Creator.AvatarHash;
- old.Flags = scheduledEvent.Creator.Flags;
- return old;
- });
- }
+ Status = status
+ };
- if (this.Discord is DiscordClient dc)
- await dc.OnGuildScheduledEventUpdateEventAsync(scheduledEvent, guild);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- return scheduledEvent;
- }
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
- /// <summary>
- /// Gets a scheduled event.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="scheduledEventId">The event id.</param>
- /// <param name="withUserCount">Whether to include user count.</param>
- internal async Task<DiscordScheduledEvent> GetGuildScheduledEventAsync(ulong guildId, ulong scheduledEventId, bool? withUserCount)
- {
- var urlParams = new Dictionary<string, string>();
- if (withUserCount.HasValue)
- urlParams["with_user_count"] = withUserCount?.ToString();
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
+ var scheduledEvent = JsonConvert.DeserializeObject<DiscordScheduledEvent>(res.Response);
+ var guild = this.Discord.Guilds[guildId];
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ scheduledEvent.Discord = this.Discord;
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ if (scheduledEvent.Creator != null)
+ {
+ scheduledEvent.Creator.Discord = this.Discord;
+ this.Discord.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
+ {
+ old.Username = scheduledEvent.Creator.Username;
+ old.Discriminator = scheduledEvent.Creator.Discriminator;
+ old.AvatarHash = scheduledEvent.Creator.AvatarHash;
+ old.Flags = scheduledEvent.Creator.Flags;
+ return old;
+ });
+ }
- var scheduledEvent = JsonConvert.DeserializeObject<DiscordScheduledEvent>(res.Response);
- var guild = this.Discord.Guilds[guildId];
+ if (this.Discord is DiscordClient dc)
+ await dc.OnGuildScheduledEventUpdateEventAsync(scheduledEvent, guild);
- scheduledEvent.Discord = this.Discord;
+ return scheduledEvent;
+ }
- if (scheduledEvent.Creator != null)
+ /// <summary>
+ /// Gets a scheduled event.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="scheduledEventId">The event id.</param>
+ /// <param name="withUserCount">Whether to include user count.</param>
+ internal async Task<DiscordScheduledEvent> GetGuildScheduledEventAsync(ulong guildId, ulong scheduledEventId, bool? withUserCount)
{
- scheduledEvent.Creator.Discord = this.Discord;
- this.Discord.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
- {
- old.Username = scheduledEvent.Creator.Username;
- old.Discriminator = scheduledEvent.Creator.Discriminator;
- old.AvatarHash = scheduledEvent.Creator.AvatarHash;
- old.Flags = scheduledEvent.Creator.Flags;
- return old;
- });
- }
+ var urlParams = new Dictionary<string, string>();
+ if (withUserCount.HasValue)
+ urlParams["with_user_count"] = withUserCount?.ToString();
- return scheduledEvent;
- }
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
- /// <summary>
- /// Gets the guilds scheduled events.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="withUserCount">Whether to include the count of users subscribed to the scheduled event.</param>
- internal async Task<IReadOnlyDictionary<ulong, DiscordScheduledEvent>> ListGuildScheduledEventsAsync(ulong guildId, bool? withUserCount)
- {
- var urlParams = new Dictionary<string, string>();
- if (withUserCount.HasValue)
- urlParams["with_user_count"] = withUserCount?.ToString();
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var scheduledEvent = JsonConvert.DeserializeObject<DiscordScheduledEvent>(res.Response);
+ var guild = this.Discord.Guilds[guildId];
- var events = new Dictionary<ulong, DiscordScheduledEvent>();
- var eventsRaw = JsonConvert.DeserializeObject<List<DiscordScheduledEvent>>(res.Response);
- var guild = this.Discord.Guilds[guildId];
+ scheduledEvent.Discord = this.Discord;
- foreach (var ev in eventsRaw)
- {
- ev.Discord = this.Discord;
- if (ev.Creator != null)
+ if (scheduledEvent.Creator != null)
{
- ev.Creator.Discord = this.Discord;
- this.Discord.UserCache.AddOrUpdate(ev.Creator.Id, ev.Creator, (id, old) =>
+ scheduledEvent.Creator.Discord = this.Discord;
+ this.Discord.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
{
- old.Username = ev.Creator.Username;
- old.Discriminator = ev.Creator.Discriminator;
- old.AvatarHash = ev.Creator.AvatarHash;
- old.Flags = ev.Creator.Flags;
+ old.Username = scheduledEvent.Creator.Username;
+ old.Discriminator = scheduledEvent.Creator.Discriminator;
+ old.AvatarHash = scheduledEvent.Creator.AvatarHash;
+ old.Flags = scheduledEvent.Creator.Flags;
return old;
});
}
- events.Add(ev.Id, ev);
+ return scheduledEvent;
}
- return new ReadOnlyDictionary<ulong, DiscordScheduledEvent>(new Dictionary<ulong, DiscordScheduledEvent>(events));
- }
-
- /// <summary>
- /// Deletes a guild scheduled event.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="scheduledEventId">The scheduled event id.</param>
- /// <param name="reason">The reason.</param>
- internal Task DeleteGuildScheduledEventAsync(ulong guildId, ulong scheduledEventId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ /// <summary>
+ /// Gets the guilds scheduled events.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="withUserCount">Whether to include the count of users subscribed to the scheduled event.</param>
+ internal async Task<IReadOnlyDictionary<ulong, DiscordScheduledEvent>> ListGuildScheduledEventsAsync(ulong guildId, bool? withUserCount)
+ {
+ var urlParams = new Dictionary<string, string>();
+ if (withUserCount.HasValue)
+ urlParams["with_user_count"] = withUserCount?.ToString();
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- /// <summary>
- /// Gets the users who RSVP'd to a scheduled event.
- /// Optional with member objects.
- /// This endpoint is paginated.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="scheduledEventId">The scheduled event id.</param>
- /// <param name="limit">The limit how many users to receive from the event.</param>
- /// <param name="before">Get results before the given id.</param>
- /// <param name="after">Get results after the given id.</param>
- /// <param name="withMember">Whether to include guild member data. attaches guild_member property to the user object.</param>
- internal async Task<IReadOnlyDictionary<ulong, DiscordScheduledEventUser>> GetGuildScheduledEventRspvUsersAsync(ulong guildId, ulong scheduledEventId, int? limit, ulong? before, ulong? after, bool? withMember)
- {
- var urlParams = new Dictionary<string, string>();
- if (limit != null && limit > 0)
- urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
- if (before != null)
- urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
- if (after != null)
- urlParams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
- if (withMember != null)
- urlParams["with_member"] = withMember.Value.ToString(CultureInfo.InvariantCulture);
+ var events = new Dictionary<ulong, DiscordScheduledEvent>();
+ var eventsRaw = JsonConvert.DeserializeObject<List<DiscordScheduledEvent>>(res.Response);
+ var guild = this.Discord.Guilds[guildId];
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id{Endpoints.USERS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
+ foreach (var ev in eventsRaw)
+ {
+ ev.Discord = this.Discord;
+ if (ev.Creator != null)
+ {
+ ev.Creator.Discord = this.Discord;
+ this.Discord.UserCache.AddOrUpdate(ev.Creator.Id, ev.Creator, (id, old) =>
+ {
+ old.Username = ev.Creator.Username;
+ old.Discriminator = ev.Creator.Discriminator;
+ old.AvatarHash = ev.Creator.AvatarHash;
+ old.Flags = ev.Creator.Flags;
+ return old;
+ });
+ }
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ events.Add(ev.Id, ev);
+ }
- var rspvUsers = JsonConvert.DeserializeObject<IEnumerable<DiscordScheduledEventUser>>(res.Response);
- Dictionary<ulong, DiscordScheduledEventUser> rspv = new();
+ return new ReadOnlyDictionary<ulong, DiscordScheduledEvent>(new Dictionary<ulong, DiscordScheduledEvent>(events));
+ }
- foreach (var rspvUser in rspvUsers)
+ /// <summary>
+ /// Deletes a guild scheduled event.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="scheduledEventId">The scheduled event id.</param>
+ /// <param name="reason">The reason.</param>
+ internal Task DeleteGuildScheduledEventAsync(ulong guildId, ulong scheduledEventId, string reason)
{
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
+
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
- rspvUser.Discord = this.Discord;
- rspvUser.GuildId = guildId;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
- rspvUser.User.Discord = this.Discord;
- rspvUser.User = this.Discord.UserCache.AddOrUpdate(rspvUser.User.Id, rspvUser.User, (id, old) =>
+ /// <summary>
+ /// Gets the users who RSVP'd to a scheduled event.
+ /// Optional with member objects.
+ /// This endpoint is paginated.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="scheduledEventId">The scheduled event id.</param>
+ /// <param name="limit">The limit how many users to receive from the event.</param>
+ /// <param name="before">Get results before the given id.</param>
+ /// <param name="after">Get results after the given id.</param>
+ /// <param name="withMember">Whether to include guild member data. attaches guild_member property to the user object.</param>
+ internal async Task<IReadOnlyDictionary<ulong, DiscordScheduledEventUser>> GetGuildScheduledEventRspvUsersAsync(ulong guildId, ulong scheduledEventId, int? limit, ulong? before, ulong? after, bool? withMember)
+ {
+ var urlParams = new Dictionary<string, string>();
+ if (limit != null && limit > 0)
+ urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
+ if (before != null)
+ urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
+ if (after != null)
+ urlParams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
+ if (withMember != null)
+ urlParams["with_member"] = withMember.Value.ToString(CultureInfo.InvariantCulture);
+
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id{Endpoints.USERS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+
+ var rspvUsers = JsonConvert.DeserializeObject<IEnumerable<DiscordScheduledEventUser>>(res.Response);
+ Dictionary<ulong, DiscordScheduledEventUser> rspv = new();
+
+ foreach (var rspvUser in rspvUsers)
{
- old.Username = rspvUser.User.Username;
- old.Discriminator = rspvUser.User.Discriminator;
- old.AvatarHash = rspvUser.User.AvatarHash;
- old.BannerHash = rspvUser.User.BannerHash;
- old.BannerColorInternal = rspvUser.User.BannerColorInternal;
- return old;
- });
- /*if (with_member.HasValue && with_member.Value && rspv_user.Member != null)
+ rspvUser.Discord = this.Discord;
+ rspvUser.GuildId = guildId;
+
+ rspvUser.User.Discord = this.Discord;
+ rspvUser.User = this.Discord.UserCache.AddOrUpdate(rspvUser.User.Id, rspvUser.User, (id, old) =>
+ {
+ old.Username = rspvUser.User.Username;
+ old.Discriminator = rspvUser.User.Discriminator;
+ old.AvatarHash = rspvUser.User.AvatarHash;
+ old.BannerHash = rspvUser.User.BannerHash;
+ old.BannerColorInternal = rspvUser.User.BannerColorInternal;
+ return old;
+ });
+
+ /*if (with_member.HasValue && with_member.Value && rspv_user.Member != null)
{
rspv_user.Member.Discord = this.Discord;
}*/
- rspv.Add(rspvUser.User.Id, rspvUser);
+ rspv.Add(rspvUser.User.Id, rspvUser);
+ }
+
+ return new ReadOnlyDictionary<ulong, DiscordScheduledEventUser>(new Dictionary<ulong, DiscordScheduledEventUser>(rspv));
}
+ #endregion
+
+ #region Channel
+ /// <summary>
+ /// Creates the guild channel async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="parent">The parent.</param>
+ /// <param name="topic">The topic.</param>
+ /// <param name="bitrate">The bitrate.</param>
+ /// <param name="userLimit">The user_limit.</param>
+ /// <param name="overwrites">The overwrites.</param>
+ /// <param name="nsfw">If true, nsfw.</param>
+ /// <param name="perUserRateLimit">The per user rate limit.</param>
+ /// <param name="qualityMode">The quality mode.</param>
+ /// <param name="defaultAutoArchiveDuration">The default auto archive duration.</param>
+ /// <param name="reason">The reason.</param>
+ internal async Task<DiscordChannel> CreateGuildChannelAsync(ulong guildId, string name, ChannelType type, ulong? parent, Optional<string> topic, int? bitrate, int? userLimit, IEnumerable<DiscordOverwriteBuilder> overwrites, bool? nsfw, Optional<int?> perUserRateLimit, VideoQualityMode? qualityMode, ThreadAutoArchiveDuration? defaultAutoArchiveDuration, string reason)
+ {
+ var restOverwrites = new List<DiscordRestOverwrite>();
+ if (overwrites != null)
+ foreach (var ow in overwrites)
+ restOverwrites.Add(ow.Build());
- return new ReadOnlyDictionary<ulong, DiscordScheduledEventUser>(new Dictionary<ulong, DiscordScheduledEventUser>(rspv));
- }
- #endregion
+ var pld = new RestChannelCreatePayload
+ {
+ Name = name,
+ Type = type,
+ Parent = parent,
+ Topic = topic,
+ Bitrate = bitrate,
+ UserLimit = userLimit,
+ PermissionOverwrites = restOverwrites,
+ Nsfw = nsfw,
+ PerUserRateLimit = perUserRateLimit,
+ QualityMode = qualityMode,
+ DefaultAutoArchiveDuration = defaultAutoArchiveDuration
+ };
- #region Channel
- /// <summary>
- /// Creates the guild channel async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="type">The type.</param>
- /// <param name="parent">The parent.</param>
- /// <param name="topic">The topic.</param>
- /// <param name="bitrate">The bitrate.</param>
- /// <param name="userLimit">The user_limit.</param>
- /// <param name="overwrites">The overwrites.</param>
- /// <param name="nsfw">If true, nsfw.</param>
- /// <param name="perUserRateLimit">The per user rate limit.</param>
- /// <param name="qualityMode">The quality mode.</param>
- /// <param name="defaultAutoArchiveDuration">The default auto archive duration.</param>
- /// <param name="reason">The reason.</param>
- internal async Task<DiscordChannel> CreateGuildChannelAsync(ulong guildId, string name, ChannelType type, ulong? parent, Optional<string> topic, int? bitrate, int? userLimit, IEnumerable<DiscordOverwriteBuilder> overwrites, bool? nsfw, Optional<int?> perUserRateLimit, VideoQualityMode? qualityMode, ThreadAutoArchiveDuration? defaultAutoArchiveDuration, string reason)
- {
- var restOverwrites = new List<DiscordRestOverwrite>();
- if (overwrites != null)
- foreach (var ow in overwrites)
- restOverwrites.Add(ow.Build());
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var pld = new RestChannelCreatePayload
- {
- Name = name,
- Type = type,
- Parent = parent,
- Topic = topic,
- Bitrate = bitrate,
- UserLimit = userLimit,
- PermissionOverwrites = restOverwrites,
- Nsfw = nsfw,
- PerUserRateLimit = perUserRateLimit,
- QualityMode = qualityMode,
- DefaultAutoArchiveDuration = defaultAutoArchiveDuration
- };
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
+ var ret = JsonConvert.DeserializeObject<DiscordChannel>(res.Response);
+ ret.Discord = this.Discord;
+ foreach (var xo in ret.PermissionOverwritesInternal)
+ {
+ xo.Discord = this.Discord;
+ xo.ChannelId = ret.Id;
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ return ret;
+ }
- var ret = JsonConvert.DeserializeObject<DiscordChannel>(res.Response);
- ret.Discord = this.Discord;
- foreach (var xo in ret.PermissionOverwritesInternal)
+ /// <summary>
+ /// Modifies the channel async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="position">The position.</param>
+ /// <param name="topic">The topic.</param>
+ /// <param name="nsfw">If true, nsfw.</param>
+ /// <param name="parent">The parent.</param>
+ /// <param name="bitrate">The bitrate.</param>
+ /// <param name="userLimit">The user_limit.</param>
+ /// <param name="perUserRateLimit">The per user rate limit.</param>
+ /// <param name="rtcRegion">The rtc region.</param>
+ /// <param name="qualityMode">The quality mode.</param>
+ /// <param name="autoArchiveDuration">The default auto archive duration.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="permissionOverwrites">The permission overwrites.</param>
+ /// <param name="bannerb64">The banner.</param>
+ /// <param name="reason">The reason.</param>
+ internal Task ModifyChannelAsync(ulong channelId, string name, int? position, Optional<string> topic, bool? nsfw, Optional<ulong?> parent, int? bitrate, int? userLimit, Optional<int?> perUserRateLimit, Optional<string> rtcRegion, VideoQualityMode? qualityMode, ThreadAutoArchiveDuration? autoArchiveDuration, Optional<ChannelType> type, IEnumerable<DiscordOverwriteBuilder> permissionOverwrites, Optional<string> bannerb64, string reason)
{
- xo.Discord = this.Discord;
- xo.ChannelId = ret.Id;
- }
- return ret;
- }
+ List<DiscordRestOverwrite> restoverwrites = null;
+ if (permissionOverwrites != null)
+ {
+ restoverwrites = new List<DiscordRestOverwrite>();
+ foreach (var ow in permissionOverwrites)
+ restoverwrites.Add(ow.Build());
+ }
- /// <summary>
- /// Modifies the channel async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="position">The position.</param>
- /// <param name="topic">The topic.</param>
- /// <param name="nsfw">If true, nsfw.</param>
- /// <param name="parent">The parent.</param>
- /// <param name="bitrate">The bitrate.</param>
- /// <param name="userLimit">The user_limit.</param>
- /// <param name="perUserRateLimit">The per user rate limit.</param>
- /// <param name="rtcRegion">The rtc region.</param>
- /// <param name="qualityMode">The quality mode.</param>
- /// <param name="autoArchiveDuration">The default auto archive duration.</param>
- /// <param name="type">The type.</param>
- /// <param name="permissionOverwrites">The permission overwrites.</param>
- /// <param name="bannerb64">The banner.</param>
- /// <param name="reason">The reason.</param>
- internal Task ModifyChannelAsync(ulong channelId, string name, int? position, Optional<string> topic, bool? nsfw, Optional<ulong?> parent, int? bitrate, int? userLimit, Optional<int?> perUserRateLimit, Optional<string> rtcRegion, VideoQualityMode? qualityMode, ThreadAutoArchiveDuration? autoArchiveDuration, Optional<ChannelType> type, IEnumerable<DiscordOverwriteBuilder> permissionOverwrites, Optional<string> bannerb64, string reason)
- {
+ var pld = new RestChannelModifyPayload
+ {
+ Name = name,
+ Position = position,
+ Topic = topic,
+ Nsfw = nsfw,
+ Parent = parent,
+ Bitrate = bitrate,
+ UserLimit = userLimit,
+ PerUserRateLimit = perUserRateLimit,
+ RtcRegion = rtcRegion,
+ QualityMode = qualityMode,
+ DefaultAutoArchiveDuration = autoArchiveDuration,
+ Type = type,
+ PermissionOverwrites = restoverwrites,
+ BannerBase64 = bannerb64
+ };
- List<DiscordRestOverwrite> restoverwrites = null;
- if (permissionOverwrites != null)
- {
- restoverwrites = new List<DiscordRestOverwrite>();
- foreach (var ow in permissionOverwrites)
- restoverwrites.Add(ow.Build());
- }
-
- var pld = new RestChannelModifyPayload
- {
- Name = name,
- Position = position,
- Topic = topic,
- Nsfw = nsfw,
- Parent = parent,
- Bitrate = bitrate,
- UserLimit = userLimit,
- PerUserRateLimit = perUserRateLimit,
- RtcRegion = rtcRegion,
- QualityMode = qualityMode,
- DefaultAutoArchiveDuration = autoArchiveDuration,
- Type = type,
- PermissionOverwrites = restoverwrites,
- BannerBase64 = bannerb64
- };
-
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
-
- var route = $"{Endpoints.CHANNELS}/:channel_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
- }
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- /// <summary>
- /// Gets the channel async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
+ var route = $"{Endpoints.CHANNELS}/:channel_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId }, out var path);
- internal async Task<DiscordChannel> GetChannelAsync(ulong channelId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ /// <summary>
+ /// Gets the channel async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
- var ret = JsonConvert.DeserializeObject<DiscordChannel>(res.Response);
- ret.Discord = this.Discord;
- foreach (var xo in ret.PermissionOverwritesInternal)
+ internal async Task<DiscordChannel> GetChannelAsync(ulong channelId)
{
- xo.Discord = this.Discord;
- xo.ChannelId = ret.Id;
- }
-
- return ret;
- }
+ var route = $"{Endpoints.CHANNELS}/:channel_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
- /// <summary>
- /// Deletes the channel async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="reason">The reason.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- internal Task DeleteChannelAsync(ulong channelId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
-
- var route = $"{Endpoints.CHANNELS}/:channel_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
-
- /// <summary>
- /// Gets the message async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
-
- internal async Task<DiscordMessage> GetMessageAsync(ulong channelId, ulong messageId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, message_id = messageId }, out var path);
+ var ret = JsonConvert.DeserializeObject<DiscordChannel>(res.Response);
+ ret.Discord = this.Discord;
+ foreach (var xo in ret.PermissionOverwritesInternal)
+ {
+ xo.Discord = this.Discord;
+ xo.ChannelId = ret.Id;
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ return ret;
+ }
- var ret = this.PrepareMessage(JObject.Parse(res.Response));
+ /// <summary>
+ /// Deletes the channel async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="reason">The reason.</param>
- return ret;
- }
+ internal Task DeleteChannelAsync(ulong channelId, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- /// <summary>
- /// Creates the message async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="content">The content.</param>
- /// <param name="embeds">The embeds.</param>
- /// <param name="sticker">The sticker.</param>
- /// <param name="replyMessageId">The reply message id.</param>
- /// <param name="mentionReply">If true, mention reply.</param>
- /// <param name="failOnInvalidReply">If true, fail on invalid reply.</param>
-
- internal async Task<DiscordMessage> CreateMessageAsync(ulong channelId, string content, IEnumerable<DiscordEmbed> embeds, DiscordSticker sticker, ulong? replyMessageId, bool mentionReply, bool failOnInvalidReply)
- {
- if (content != null && content.Length > 2000)
- throw new ArgumentException("Message content length cannot exceed 2000 characters.");
+ var route = $"{Endpoints.CHANNELS}/:channel_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId }, out var path);
- if (!embeds?.Any() ?? true)
- {
- if (content == null && sticker == null)
- throw new ArgumentException("You must specify message content, a sticker or an embed.");
- if (content.Length == 0)
- throw new ArgumentException("Message content must not be empty.");
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
- if (embeds != null)
- foreach (var embed in embeds)
- if (embed.Timestamp != null)
- embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
+ /// <summary>
+ /// Gets the message async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
- var pld = new RestChannelMessageCreatePayload
+ internal async Task<DiscordMessage> GetMessageAsync(ulong channelId, ulong messageId)
{
- HasContent = content != null,
- Content = content,
- StickersIds = sticker is null ? Array.Empty<ulong>() : new[] {sticker.Id},
- IsTts = false,
- HasEmbed = embeds?.Any() ?? false,
- Embeds = embeds
- };
-
- if (replyMessageId != null)
- pld.MessageReference = new InternalDiscordMessageReference { MessageId = replyMessageId, FailIfNotExists = failOnInvalidReply };
-
- if (replyMessageId != null)
- pld.Mentions = new DiscordMentions(Mentions.All, true, mentionReply);
-
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, message_id = messageId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var ret = this.PrepareMessage(JObject.Parse(res.Response));
+ var ret = this.PrepareMessage(JObject.Parse(res.Response));
- return ret;
- }
+ return ret;
+ }
- /// <summary>
- /// Creates the message async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="builder">The builder.</param>
+ /// <summary>
+ /// Creates the message async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="content">The content.</param>
+ /// <param name="embeds">The embeds.</param>
+ /// <param name="sticker">The sticker.</param>
+ /// <param name="replyMessageId">The reply message id.</param>
+ /// <param name="mentionReply">If true, mention reply.</param>
+ /// <param name="failOnInvalidReply">If true, fail on invalid reply.</param>
+
+ internal async Task<DiscordMessage> CreateMessageAsync(ulong channelId, string content, IEnumerable<DiscordEmbed> embeds, DiscordSticker sticker, ulong? replyMessageId, bool mentionReply, bool failOnInvalidReply)
+ {
+ if (content != null && content.Length > 2000)
+ throw new ArgumentException("Message content length cannot exceed 2000 characters.");
- internal async Task<DiscordMessage> CreateMessageAsync(ulong channelId, DiscordMessageBuilder builder)
- {
- builder.Validate();
+ if (!embeds?.Any() ?? true)
+ {
+ if (content == null && sticker == null)
+ throw new ArgumentException("You must specify message content, a sticker or an embed.");
+ if (content.Length == 0)
+ throw new ArgumentException("Message content must not be empty.");
+ }
- if (builder.Embeds != null)
- foreach (var embed in builder.Embeds)
- if (embed?.Timestamp != null)
- embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
+ if (embeds != null)
+ foreach (var embed in embeds)
+ if (embed.Timestamp != null)
+ embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
- var pld = new RestChannelMessageCreatePayload
- {
- HasContent = builder.Content != null,
- Content = builder.Content,
- StickersIds = builder.Sticker is null ? Array.Empty<ulong>() : new[] {builder.Sticker.Id},
- IsTts = builder.IsTts,
- HasEmbed = builder.Embeds != null,
- Embeds = builder.Embeds,
- Components = builder.Components
- };
+ var pld = new RestChannelMessageCreatePayload
+ {
+ HasContent = content != null,
+ Content = content,
+ StickersIds = sticker is null ? Array.Empty<ulong>() : new[] {sticker.Id},
+ IsTts = false,
+ HasEmbed = embeds?.Any() ?? false,
+ Embeds = embeds
+ };
- if (builder.ReplyId != null)
- pld.MessageReference = new InternalDiscordMessageReference { MessageId = builder.ReplyId, FailIfNotExists = builder.FailOnInvalidReply };
+ if (replyMessageId != null)
+ pld.MessageReference = new InternalDiscordMessageReference { MessageId = replyMessageId, FailIfNotExists = failOnInvalidReply };
- pld.Mentions = new DiscordMentions(builder.Mentions ?? Mentions.All, builder.Mentions?.Any() ?? false, builder.MentionOnReply);
+ if (replyMessageId != null)
+ pld.Mentions = new DiscordMentions(Mentions.All, true, mentionReply);
- if (builder.Files.Count == 0)
- {
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = this.PrepareMessage(JObject.Parse(res.Response));
+
return ret;
}
- else
+
+ /// <summary>
+ /// Creates the message async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="builder">The builder.</param>
+
+ internal async Task<DiscordMessage> CreateMessageAsync(ulong channelId, DiscordMessageBuilder builder)
{
- ulong fileId = 0;
- List<DiscordAttachment> attachments = new(builder.Files.Count);
- foreach (var file in builder.Files)
- {
- DiscordAttachment att = new()
- {
- Id = fileId,
- Discord = this.Discord,
- Description = file.Description,
- FileName = file.FileName
- };
- attachments.Add(att);
- fileId++;
- }
- pld.Attachments = attachments;
+ builder.Validate();
- var values = new Dictionary<string, string>
+ if (builder.Embeds != null)
+ foreach (var embed in builder.Embeds)
+ if (embed?.Timestamp != null)
+ embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
+
+ var pld = new RestChannelMessageCreatePayload
{
- ["payload_json"] = DiscordJson.SerializeObject(pld)
+ HasContent = builder.Content != null,
+ Content = builder.Content,
+ StickersIds = builder.Sticker is null ? Array.Empty<ulong>() : new[] {builder.Sticker.Id},
+ IsTts = builder.IsTts,
+ HasEmbed = builder.Embeds != null,
+ Embeds = builder.Embeds,
+ Components = builder.Components
};
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
+ if (builder.ReplyId != null)
+ pld.MessageReference = new InternalDiscordMessageReference { MessageId = builder.ReplyId, FailIfNotExists = builder.FailOnInvalidReply };
- var ret = this.PrepareMessage(JObject.Parse(res.Response));
+ pld.Mentions = new DiscordMentions(builder.Mentions ?? Mentions.All, builder.Mentions?.Any() ?? false, builder.MentionOnReply);
- foreach (var file in builder.FilesInternal.Where(x => x.ResetPositionTo.HasValue))
+ if (builder.Files.Count == 0)
{
- file.Stream.Position = file.ResetPositionTo.Value;
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+
+ var ret = this.PrepareMessage(JObject.Parse(res.Response));
+ return ret;
}
+ else
+ {
+ ulong fileId = 0;
+ List<DiscordAttachment> attachments = new(builder.Files.Count);
+ foreach (var file in builder.Files)
+ {
+ DiscordAttachment att = new()
+ {
+ Id = fileId,
+ Discord = this.Discord,
+ Description = file.Description,
+ FileName = file.FileName
+ };
+ attachments.Add(att);
+ fileId++;
+ }
+ pld.Attachments = attachments;
- return ret;
- }
- }
+ var values = new Dictionary<string, string>
+ {
+ ["payload_json"] = DiscordJson.SerializeObject(pld)
+ };
- /// <summary>
- /// Gets the guild channels async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
- internal async Task<IReadOnlyList<DiscordChannel>> GetGuildChannelsAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var ret = this.PrepareMessage(JObject.Parse(res.Response));
- var channelsRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordChannel>>(res.Response).Select(xc => { xc.Discord = this.Discord; return xc; });
+ foreach (var file in builder.FilesInternal.Where(x => x.ResetPositionTo.HasValue))
+ {
+ file.Stream.Position = file.ResetPositionTo.Value;
+ }
- foreach (var ret in channelsRaw)
- foreach (var xo in ret.PermissionOverwritesInternal)
- {
- xo.Discord = this.Discord;
- xo.ChannelId = ret.Id;
+ return ret;
}
+ }
- return new ReadOnlyCollection<DiscordChannel>(new List<DiscordChannel>(channelsRaw));
- }
+ /// <summary>
+ /// Gets the guild channels async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- /// <summary>
- /// Creates the stage instance async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="topic">The topic.</param>
- /// <param name="sendStartNotification">Whether everyone should be notified about the stage.</param>
- /// <param name="privacyLevel">The privacy_level.</param>
- /// <param name="reason">The reason.</param>
- internal async Task<DiscordStageInstance> CreateStageInstanceAsync(ulong channelId, string topic, bool sendStartNotification, StagePrivacyLevel privacyLevel, string reason)
- {
- var pld = new RestStageInstanceCreatePayload
+ internal async Task<IReadOnlyList<DiscordChannel>> GetGuildChannelsAsync(ulong guildId)
{
- ChannelId = channelId,
- Topic = topic,
- PrivacyLevel = privacyLevel,
- SendStartNotification = sendStartNotification
- };
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var route = $"{Endpoints.STAGE_INSTANCES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var stageInstance = JsonConvert.DeserializeObject<DiscordStageInstance>(res.Response);
+ var channelsRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordChannel>>(res.Response).Select(xc => { xc.Discord = this.Discord; return xc; });
- return stageInstance;
- }
+ foreach (var ret in channelsRaw)
+ foreach (var xo in ret.PermissionOverwritesInternal)
+ {
+ xo.Discord = this.Discord;
+ xo.ChannelId = ret.Id;
+ }
- /// <summary>
- /// Gets the stage instance async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- internal async Task<DiscordStageInstance> GetStageInstanceAsync(ulong channelId)
- {
- var route = $"{Endpoints.STAGE_INSTANCES}/:channel_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
+ return new ReadOnlyCollection<DiscordChannel>(new List<DiscordChannel>(channelsRaw));
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ /// <summary>
+ /// Creates the stage instance async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="topic">The topic.</param>
+ /// <param name="sendStartNotification">Whether everyone should be notified about the stage.</param>
+ /// <param name="privacyLevel">The privacy_level.</param>
+ /// <param name="reason">The reason.</param>
+ internal async Task<DiscordStageInstance> CreateStageInstanceAsync(ulong channelId, string topic, bool sendStartNotification, StagePrivacyLevel privacyLevel, string reason)
+ {
+ var pld = new RestStageInstanceCreatePayload
+ {
+ ChannelId = channelId,
+ Topic = topic,
+ PrivacyLevel = privacyLevel,
+ SendStartNotification = sendStartNotification
+ };
- var stageInstance = JsonConvert.DeserializeObject<DiscordStageInstance>(res.Response);
+ var route = $"{Endpoints.STAGE_INSTANCES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- return stageInstance;
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- /// <summary>
- /// Modifies the stage instance async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="topic">The topic.</param>
- /// <param name="privacyLevel">The privacy_level.</param>
- /// <param name="reason">The reason.</param>
- internal Task ModifyStageInstanceAsync(ulong channelId, Optional<string> topic, Optional<StagePrivacyLevel> privacyLevel, string reason)
- {
- var pld = new RestStageInstanceModifyPayload
- {
- Topic = topic,
- PrivacyLevel = privacyLevel
- };
+ var stageInstance = JsonConvert.DeserializeObject<DiscordStageInstance>(res.Response);
- var route = $"{Endpoints.STAGE_INSTANCES}/:channel_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId }, out var path);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ return stageInstance;
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
- }
+ /// <summary>
+ /// Gets the stage instance async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ internal async Task<DiscordStageInstance> GetStageInstanceAsync(ulong channelId)
+ {
+ var route = $"{Endpoints.STAGE_INSTANCES}/:channel_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
- /// <summary>
- /// Deletes the stage instance async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="reason">The reason.</param>
- internal Task DeleteStageInstanceAsync(ulong channelId, string reason)
- {
- var route = $"{Endpoints.STAGE_INSTANCES}/:channel_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId }, out var path);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- /// <summary>
- /// Gets the channel messages async.
- /// </summary>
- /// <param name="channelId">The channel id.</param>
- /// <param name="limit">The limit.</param>
- /// <param name="before">The before.</param>
- /// <param name="after">The after.</param>
- /// <param name="around">The around.</param>
+ var stageInstance = JsonConvert.DeserializeObject<DiscordStageInstance>(res.Response);
- internal async Task<IReadOnlyList<DiscordMessage>> GetChannelMessagesAsync(ulong channelId, int limit, ulong? before, ulong? after, ulong? around)
- {
- var urlParams = new Dictionary<string, string>();
- if (around != null)
- urlParams["around"] = around?.ToString(CultureInfo.InvariantCulture);
- if (before != null)
- urlParams["before"] = before?.ToString(CultureInfo.InvariantCulture);
- if (after != null)
- urlParams["after"] = after?.ToString(CultureInfo.InvariantCulture);
- if (limit > 0)
- urlParams["limit"] = limit.ToString(CultureInfo.InvariantCulture);
+ return stageInstance;
+ }
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
+ /// <summary>
+ /// Modifies the stage instance async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="topic">The topic.</param>
+ /// <param name="privacyLevel">The privacy_level.</param>
+ /// <param name="reason">The reason.</param>
+ internal Task ModifyStageInstanceAsync(ulong channelId, Optional<string> topic, Optional<StagePrivacyLevel> privacyLevel, string reason)
+ {
+ var pld = new RestStageInstanceModifyPayload
+ {
+ Topic = topic,
+ PrivacyLevel = privacyLevel
+ };
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var route = $"{Endpoints.STAGE_INSTANCES}/:channel_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId }, out var path);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var msgsRaw = JArray.Parse(res.Response);
- var msgs = new List<DiscordMessage>();
- foreach (var xj in msgsRaw)
- msgs.Add(this.PrepareMessage(xj));
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
+ }
- return new ReadOnlyCollection<DiscordMessage>(new List<DiscordMessage>(msgs));
- }
+ /// <summary>
+ /// Deletes the stage instance async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="reason">The reason.</param>
+ internal Task DeleteStageInstanceAsync(ulong channelId, string reason)
+ {
+ var route = $"{Endpoints.STAGE_INSTANCES}/:channel_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId }, out var path);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- /// <summary>
- /// Gets the channel message async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
- internal async Task<DiscordMessage> GetChannelMessageAsync(ulong channelId, ulong messageId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, message_id = messageId }, out var path);
+ /// <summary>
+ /// Gets the channel messages async.
+ /// </summary>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="limit">The limit.</param>
+ /// <param name="before">The before.</param>
+ /// <param name="after">The after.</param>
+ /// <param name="around">The around.</param>
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ internal async Task<IReadOnlyList<DiscordMessage>> GetChannelMessagesAsync(ulong channelId, int limit, ulong? before, ulong? after, ulong? around)
+ {
+ var urlParams = new Dictionary<string, string>();
+ if (around != null)
+ urlParams["around"] = around?.ToString(CultureInfo.InvariantCulture);
+ if (before != null)
+ urlParams["before"] = before?.ToString(CultureInfo.InvariantCulture);
+ if (after != null)
+ urlParams["after"] = after?.ToString(CultureInfo.InvariantCulture);
+ if (limit > 0)
+ urlParams["limit"] = limit.ToString(CultureInfo.InvariantCulture);
- var ret = this.PrepareMessage(JObject.Parse(res.Response));
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
- return ret;
- }
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- /// <summary>
- /// Edits the message async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="content">The content.</param>
- /// <param name="embeds">The embeds.</param>
- /// <param name="mentions">The mentions.</param>
- /// <param name="components">The components.</param>
- /// <param name="suppressEmbed">The suppress_embed.</param>
- /// <param name="files">The files.</param>
- /// <param name="attachments">The attachments to keep.</param>
-
- internal async Task<DiscordMessage> EditMessageAsync(ulong channelId, ulong messageId, Optional<string> content, Optional<IEnumerable<DiscordEmbed>> embeds, Optional<IEnumerable<IMention>> mentions, IReadOnlyList<DiscordActionRowComponent> components, Optional<bool> suppressEmbed, IReadOnlyCollection<DiscordMessageFile> files, Optional<IEnumerable<DiscordAttachment>> attachments)
- {
- if (embeds.HasValue && embeds.Value != null)
- foreach (var embed in embeds.Value)
- if (embed.Timestamp != null)
- embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
-
- var pld = new RestChannelMessageEditPayload
- {
- HasContent = content.HasValue,
- Content = content.ValueOrDefault(),
- HasEmbed = embeds.HasValue && (embeds.Value?.Any() ?? false),
- Embeds = embeds.HasValue && (embeds.Value?.Any() ?? false) ? embeds.Value : null,
- Components = components,
- Flags = suppressEmbed.HasValue && (bool)suppressEmbed ? MessageFlags.SuppressedEmbeds : null,
- Mentions = mentions
- .Map(m => new DiscordMentions(m ?? Mentions.None, false, mentions.Value?.OfType<RepliedUserMention>().Any() ?? false))
- .ValueOrDefault()
- };
-
- if (files?.Count > 0)
- {
- ulong fileId = 0;
- List<DiscordAttachment> attachmentsNew = new();
- foreach (var file in files)
- {
- DiscordAttachment att = new()
- {
- Id = fileId,
- Discord = this.Discord,
- Description = file.Description,
- FileName = file.FileName
- };
- attachmentsNew.Add(att);
- fileId++;
- }
- if (attachments.HasValue && attachments.Value.Any())
- attachmentsNew.AddRange(attachments.Value);
+ var msgsRaw = JArray.Parse(res.Response);
+ var msgs = new List<DiscordMessage>();
+ foreach (var xj in msgsRaw)
+ msgs.Add(this.PrepareMessage(xj));
- pld.Attachments = attachmentsNew;
+ return new ReadOnlyCollection<DiscordMessage>(new List<DiscordMessage>(msgs));
+ }
- var values = new Dictionary<string, string>
- {
- ["payload_json"] = DiscordJson.SerializeObject(pld)
- };
+ /// <summary>
+ /// Gets the channel message async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
+ internal async Task<DiscordMessage> GetChannelMessageAsync(ulong channelId, ulong messageId)
+ {
var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId, message_id = messageId }, out var path);
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, message_id = messageId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, values: values, files: files).ConfigureAwait(false);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = this.PrepareMessage(JObject.Parse(res.Response));
- foreach (var file in files.Where(x => x.ResetPositionTo.HasValue))
- {
- file.Stream.Position = file.ResetPositionTo.Value;
- }
-
return ret;
}
- else
+
+ /// <summary>
+ /// Edits the message async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="content">The content.</param>
+ /// <param name="embeds">The embeds.</param>
+ /// <param name="mentions">The mentions.</param>
+ /// <param name="components">The components.</param>
+ /// <param name="suppressEmbed">The suppress_embed.</param>
+ /// <param name="files">The files.</param>
+ /// <param name="attachments">The attachments to keep.</param>
+
+ internal async Task<DiscordMessage> EditMessageAsync(ulong channelId, ulong messageId, Optional<string> content, Optional<IEnumerable<DiscordEmbed>> embeds, Optional<IEnumerable<IMention>> mentions, IReadOnlyList<DiscordActionRowComponent> components, Optional<bool> suppressEmbed, IReadOnlyCollection<DiscordMessageFile> files, Optional<IEnumerable<DiscordAttachment>> attachments)
{
- pld.Attachments = attachments.ValueOrDefault();
+ if (embeds.HasValue && embeds.Value != null)
+ foreach (var embed in embeds.Value)
+ if (embed.Timestamp != null)
+ embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId, message_id = messageId }, out var path);
+ var pld = new RestChannelMessageEditPayload
+ {
+ HasContent = content.HasValue,
+ Content = content.ValueOrDefault(),
+ HasEmbed = embeds.HasValue && (embeds.Value?.Any() ?? false),
+ Embeds = embeds.HasValue && (embeds.Value?.Any() ?? false) ? embeds.Value : null,
+ Components = components,
+ Flags = suppressEmbed.HasValue && (bool)suppressEmbed ? MessageFlags.SuppressedEmbeds : null,
+ Mentions = mentions
+ .Map(m => new DiscordMentions(m ?? Mentions.None, false, mentions.Value?.OfType<RepliedUserMention>().Any() ?? false))
+ .ValueOrDefault()
+ };
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ if (files?.Count > 0)
+ {
+ ulong fileId = 0;
+ List<DiscordAttachment> attachmentsNew = new();
+ foreach (var file in files)
+ {
+ DiscordAttachment att = new()
+ {
+ Id = fileId,
+ Discord = this.Discord,
+ Description = file.Description,
+ FileName = file.FileName
+ };
+ attachmentsNew.Add(att);
+ fileId++;
+ }
+ if (attachments.HasValue && attachments.Value.Any())
+ attachmentsNew.AddRange(attachments.Value);
- var ret = this.PrepareMessage(JObject.Parse(res.Response));
+ pld.Attachments = attachmentsNew;
- return ret;
- }
- }
+ var values = new Dictionary<string, string>
+ {
+ ["payload_json"] = DiscordJson.SerializeObject(pld)
+ };
- /// <summary>
- /// Deletes the message async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="reason">The reason.</param>
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId, message_id = messageId }, out var path);
- internal Task DeleteMessageAsync(ulong channelId, ulong messageId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, values: values, files: files).ConfigureAwait(false);
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId }, out var path);
+ var ret = this.PrepareMessage(JObject.Parse(res.Response));
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ foreach (var file in files.Where(x => x.ResetPositionTo.HasValue))
+ {
+ file.Stream.Position = file.ResetPositionTo.Value;
+ }
- /// <summary>
- /// Deletes the messages async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageIds">The message_ids.</param>
- /// <param name="reason">The reason.</param>
+ return ret;
+ }
+ else
+ {
+ pld.Attachments = attachments.ValueOrDefault();
- internal Task DeleteMessagesAsync(ulong channelId, IEnumerable<ulong> messageIds, string reason)
- {
- var pld = new RestChannelMessageBulkDeletePayload
- {
- Messages = messageIds
- };
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {channel_id = channelId, message_id = messageId }, out var path);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}{Endpoints.BULK_DELETE}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
+ var ret = this.PrepareMessage(JObject.Parse(res.Response));
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld));
- }
+ return ret;
+ }
+ }
- /// <summary>
- /// Gets the channel invites async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
+ /// <summary>
+ /// Deletes the message async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="reason">The reason.</param>
- internal async Task<IReadOnlyList<DiscordInvite>> GetChannelInvitesAsync(ulong channelId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.INVITES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
+ internal Task DeleteMessageAsync(ulong channelId, ulong messageId, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId }, out var path);
- var invitesRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordInvite>>(res.Response).Select(xi => { xi.Discord = this.Discord; return xi; });
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
- return new ReadOnlyCollection<DiscordInvite>(new List<DiscordInvite>(invitesRaw));
- }
+ /// <summary>
+ /// Deletes the messages async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageIds">The message_ids.</param>
+ /// <param name="reason">The reason.</param>
- /// <summary>
- /// Creates the channel invite async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="maxAge">The max_age.</param>
- /// <param name="maxUses">The max_uses.</param>
- /// <param name="targetType">The target_type.</param>
- /// <param name="targetApplication">The target_application.</param>
- /// <param name="targetUser">The target_user.</param>
- /// <param name="temporary">If true, temporary.</param>
- /// <param name="unique">If true, unique.</param>
- /// <param name="reason">The reason.</param>
-
- internal async Task<DiscordInvite> CreateChannelInviteAsync(ulong channelId, int maxAge, int maxUses, TargetType? targetType, TargetActivity? targetApplication, ulong? targetUser, bool temporary, bool unique, string reason)
- {
- var pld = new RestChannelInviteCreatePayload
+ internal Task DeleteMessagesAsync(ulong channelId, IEnumerable<ulong> messageIds, string reason)
{
- MaxAge = maxAge,
- MaxUses = maxUses,
- TargetType = targetType,
- TargetApplication = targetApplication,
- TargetUserId = targetUser,
- Temporary = temporary,
- Unique = unique
- };
-
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var pld = new RestChannelMessageBulkDeletePayload
+ {
+ Messages = messageIds
+ };
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.INVITES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}{Endpoints.BULK_DELETE}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
- var ret = JsonConvert.DeserializeObject<DiscordInvite>(res.Response);
- ret.Discord = this.Discord;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld));
+ }
- return ret;
- }
+ /// <summary>
+ /// Gets the channel invites async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
- /// <summary>
- /// Deletes the channel permission async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="overwriteId">The overwrite_id.</param>
- /// <param name="reason">The reason.</param>
+ internal async Task<IReadOnlyList<DiscordInvite>> GetChannelInvitesAsync(ulong channelId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.INVITES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
- internal Task DeleteChannelPermissionAsync(ulong channelId, ulong overwriteId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PERMISSIONS}/:overwrite_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, overwrite_id = overwriteId }, out var path);
+ var invitesRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordInvite>>(res.Response).Select(xi => { xi.Discord = this.Discord; return xi; });
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ return new ReadOnlyCollection<DiscordInvite>(new List<DiscordInvite>(invitesRaw));
+ }
- /// <summary>
- /// Edits the channel permissions async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="overwriteId">The overwrite_id.</param>
- /// <param name="allow">The allow.</param>
- /// <param name="deny">The deny.</param>
- /// <param name="type">The type.</param>
- /// <param name="reason">The reason.</param>
-
- internal Task EditChannelPermissionsAsync(ulong channelId, ulong overwriteId, Permissions allow, Permissions deny, string type, string reason)
- {
- var pld = new RestChannelPermissionEditPayload
+ /// <summary>
+ /// Creates the channel invite async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="maxAge">The max_age.</param>
+ /// <param name="maxUses">The max_uses.</param>
+ /// <param name="targetType">The target_type.</param>
+ /// <param name="targetApplication">The target_application.</param>
+ /// <param name="targetUser">The target_user.</param>
+ /// <param name="temporary">If true, temporary.</param>
+ /// <param name="unique">If true, unique.</param>
+ /// <param name="reason">The reason.</param>
+
+ internal async Task<DiscordInvite> CreateChannelInviteAsync(ulong channelId, int maxAge, int maxUses, TargetType? targetType, TargetActivity? targetApplication, ulong? targetUser, bool temporary, bool unique, string reason)
{
- Type = type,
- Allow = allow & PermissionMethods.FullPerms,
- Deny = deny & PermissionMethods.FullPerms
- };
-
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var pld = new RestChannelInviteCreatePayload
+ {
+ MaxAge = maxAge,
+ MaxUses = maxUses,
+ TargetType = targetType,
+ TargetApplication = targetApplication,
+ TargetUserId = targetUser,
+ Temporary = temporary,
+ Unique = unique
+ };
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PERMISSIONS}/:overwrite_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, overwrite_id = overwriteId }, out var path);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers, DiscordJson.SerializeObject(pld));
- }
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.INVITES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
- /// <summary>
- /// Triggers the typing async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- internal Task TriggerTypingAsync(ulong channelId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.TYPING}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
+ var ret = JsonConvert.DeserializeObject<DiscordInvite>(res.Response);
+ ret.Discord = this.Discord;
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route);
- }
+ return ret;
+ }
- /// <summary>
- /// Gets the pinned messages async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
+ /// <summary>
+ /// Deletes the channel permission async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="overwriteId">The overwrite_id.</param>
+ /// <param name="reason">The reason.</param>
- internal async Task<IReadOnlyList<DiscordMessage>> GetPinnedMessagesAsync(ulong channelId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PINS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
+ internal Task DeleteChannelPermissionAsync(ulong channelId, ulong overwriteId, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PERMISSIONS}/:overwrite_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, overwrite_id = overwriteId }, out var path);
- var msgsRaw = JArray.Parse(res.Response);
- var msgs = new List<DiscordMessage>();
- foreach (var xj in msgsRaw)
- msgs.Add(this.PrepareMessage(xj));
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
- return new ReadOnlyCollection<DiscordMessage>(new List<DiscordMessage>(msgs));
- }
+ /// <summary>
+ /// Edits the channel permissions async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="overwriteId">The overwrite_id.</param>
+ /// <param name="allow">The allow.</param>
+ /// <param name="deny">The deny.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="reason">The reason.</param>
+
+ internal Task EditChannelPermissionsAsync(ulong channelId, ulong overwriteId, Permissions allow, Permissions deny, string type, string reason)
+ {
+ var pld = new RestChannelPermissionEditPayload
+ {
+ Type = type,
+ Allow = allow & PermissionMethods.FullPerms,
+ Deny = deny & PermissionMethods.FullPerms
+ };
- /// <summary>
- /// Pins the message async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- internal Task PinMessageAsync(ulong channelId, ulong messageId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PINS}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, message_id = messageId }, out var path);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PERMISSIONS}/:overwrite_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, overwrite_id = overwriteId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route);
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers, DiscordJson.SerializeObject(pld));
+ }
- /// <summary>
- /// Unpins the message async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
+ /// <summary>
+ /// Triggers the typing async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
- internal Task UnpinMessageAsync(ulong channelId, ulong messageId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PINS}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId }, out var path);
+ internal Task TriggerTypingAsync(ulong channelId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.TYPING}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route);
+ }
- /// <summary>
- /// Adds the group dm recipient async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="accessToken">The access_token.</param>
- /// <param name="nickname">The nickname.</param>
+ /// <summary>
+ /// Gets the pinned messages async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
- internal Task AddGroupDmRecipientAsync(ulong channelId, ulong userId, string accessToken, string nickname)
- {
- var pld = new RestChannelGroupDmRecipientAddPayload
+ internal async Task<IReadOnlyList<DiscordMessage>> GetPinnedMessagesAsync(ulong channelId)
{
- AccessToken = accessToken,
- Nickname = nickname
- };
-
- var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}/:channel_id{Endpoints.RECIPIENTS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, user_id = userId }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
- }
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PINS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
- /// <summary>
- /// Removes the group dm recipient async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="userId">The user_id.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- internal Task RemoveGroupDmRecipientAsync(ulong channelId, ulong userId)
- {
- var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}/:channel_id{Endpoints.RECIPIENTS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, user_id = userId }, out var path);
+ var msgsRaw = JArray.Parse(res.Response);
+ var msgs = new List<DiscordMessage>();
+ foreach (var xj in msgsRaw)
+ msgs.Add(this.PrepareMessage(xj));
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
- }
+ return new ReadOnlyCollection<DiscordMessage>(new List<DiscordMessage>(msgs));
+ }
- /// <summary>
- /// Creates the group dm async.
- /// </summary>
- /// <param name="accessTokens">The access_tokens.</param>
- /// <param name="nicks">The nicks.</param>
+ /// <summary>
+ /// Pins the message async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
- internal async Task<DiscordDmChannel> CreateGroupDmAsync(IEnumerable<string> accessTokens, IDictionary<ulong, string> nicks)
- {
- var pld = new RestUserGroupDmCreatePayload
+ internal Task PinMessageAsync(ulong channelId, ulong messageId)
{
- AccessTokens = accessTokens,
- Nicknames = nicks
- };
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PINS}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, message_id = messageId }, out var path);
- var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route);
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ /// <summary>
+ /// Unpins the message async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
- var ret = JsonConvert.DeserializeObject<DiscordDmChannel>(res.Response);
- ret.Discord = this.Discord;
+ internal Task UnpinMessageAsync(ulong channelId, ulong messageId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.PINS}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId }, out var path);
- return ret;
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ }
- /// <summary>
- /// Creates the dm async.
- /// </summary>
- /// <param name="recipientId">The recipient_id.</param>
+ /// <summary>
+ /// Adds the group dm recipient async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="accessToken">The access_token.</param>
+ /// <param name="nickname">The nickname.</param>
- internal async Task<DiscordDmChannel> CreateDmAsync(ulong recipientId)
- {
- var pld = new RestUserDmCreatePayload
+ internal Task AddGroupDmRecipientAsync(ulong channelId, ulong userId, string accessToken, string nickname)
{
- Recipient = recipientId
- };
-
- var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var pld = new RestChannelGroupDmRecipientAddPayload
+ {
+ AccessToken = accessToken,
+ Nickname = nickname
+ };
- var ret = JsonConvert.DeserializeObject<DiscordDmChannel>(res.Response);
- ret.Discord = this.Discord;
+ var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}/:channel_id{Endpoints.RECIPIENTS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, user_id = userId }, out var path);
- return ret;
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
+ }
- /// <summary>
- /// Follows the channel async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="webhookChannelId">The webhook_channel_id.</param>
+ /// <summary>
+ /// Removes the group dm recipient async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="userId">The user_id.</param>
- internal async Task<DiscordFollowedChannel> FollowChannelAsync(ulong channelId, ulong webhookChannelId)
- {
- var pld = new FollowedChannelAddPayload
+ internal Task RemoveGroupDmRecipientAsync(ulong channelId, ulong userId)
{
- WebhookChannelId = webhookChannelId
- };
-
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.FOLLOWERS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
+ var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}/:channel_id{Endpoints.RECIPIENTS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, user_id = userId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var response = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ }
- return JsonConvert.DeserializeObject<DiscordFollowedChannel>(response.Response);
- }
+ /// <summary>
+ /// Creates the group dm async.
+ /// </summary>
+ /// <param name="accessTokens">The access_tokens.</param>
+ /// <param name="nicks">The nicks.</param>
- /// <summary>
- /// Crossposts the message async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
+ internal async Task<DiscordDmChannel> CreateGroupDmAsync(IEnumerable<string> accessTokens, IDictionary<ulong, string> nicks)
+ {
+ var pld = new RestUserGroupDmCreatePayload
+ {
+ AccessTokens = accessTokens,
+ Nicknames = nicks
+ };
- internal async Task<DiscordMessage> CrosspostMessageAsync(ulong channelId, ulong messageId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.CROSSPOST}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId, message_id = messageId }, out var path);
+ var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var response = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route).ConfigureAwait(false);
- return JsonConvert.DeserializeObject<DiscordMessage>(response.Response);
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- #endregion
+ var ret = JsonConvert.DeserializeObject<DiscordDmChannel>(res.Response);
+ ret.Discord = this.Discord;
- #region Member
- /// <summary>
- /// Gets the current user async.
- /// </summary>
+ return ret;
+ }
- internal Task<DiscordUser> GetCurrentUserAsync()
- => this.GetUserAsync("@me");
+ /// <summary>
+ /// Creates the dm async.
+ /// </summary>
+ /// <param name="recipientId">The recipient_id.</param>
- /// <summary>
- /// Gets the user async.
- /// </summary>
- /// <param name="userId">The user_id.</param>
+ internal async Task<DiscordDmChannel> CreateDmAsync(ulong recipientId)
+ {
+ var pld = new RestUserDmCreatePayload
+ {
+ Recipient = recipientId
+ };
- internal Task<DiscordUser> GetUserAsync(ulong userId)
- => this.GetUserAsync(userId.ToString(CultureInfo.InvariantCulture));
+ var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CHANNELS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
- /// <summary>
- /// Gets the user async.
- /// </summary>
- /// <param name="userId">The user_id.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- internal async Task<DiscordUser> GetUserAsync(string userId)
- {
- var route = $"{Endpoints.USERS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {user_id = userId }, out var path);
+ var ret = JsonConvert.DeserializeObject<DiscordDmChannel>(res.Response);
+ ret.Discord = this.Discord;
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ return ret;
+ }
- var userRaw = JsonConvert.DeserializeObject<TransportUser>(res.Response);
- var duser = new DiscordUser(userRaw) { Discord = this.Discord };
+ /// <summary>
+ /// Follows the channel async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="webhookChannelId">The webhook_channel_id.</param>
- return duser;
- }
+ internal async Task<DiscordFollowedChannel> FollowChannelAsync(ulong channelId, ulong webhookChannelId)
+ {
+ var pld = new FollowedChannelAddPayload
+ {
+ WebhookChannelId = webhookChannelId
+ };
- /// <summary>
- /// Gets the guild member async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.FOLLOWERS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
- internal async Task<DiscordMember> GetGuildMemberAsync(ulong guildId, ulong userId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, user_id = userId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var response = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ return JsonConvert.DeserializeObject<DiscordFollowedChannel>(response.Response);
+ }
- var tm = JsonConvert.DeserializeObject<TransportMember>(res.Response);
+ /// <summary>
+ /// Crossposts the message async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
- var usr = new DiscordUser(tm.User) { Discord = this.Discord };
- usr = this.Discord.UserCache.AddOrUpdate(tm.User.Id, usr, (id, old) =>
+ internal async Task<DiscordMessage> CrosspostMessageAsync(ulong channelId, ulong messageId)
{
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.CROSSPOST}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId, message_id = messageId }, out var path);
- return new DiscordMember(tm)
- {
- Discord = this.Discord,
- GuildId = guildId
- };
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var response = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route).ConfigureAwait(false);
+ return JsonConvert.DeserializeObject<DiscordMessage>(response.Response);
+ }
- /// <summary>
- /// Removes the guild member async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="reason">The reason.</param>
+ #endregion
- internal Task RemoveGuildMemberAsync(ulong guildId, ulong userId, string reason)
- {
- var urlParams = new Dictionary<string, string>();
- if (reason != null)
- urlParams["reason"] = reason;
+ #region Member
+ /// <summary>
+ /// Gets the current user async.
+ /// </summary>
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, user_id = userId }, out var path);
+ internal Task<DiscordUser> GetCurrentUserAsync()
+ => this.GetUserAsync("@me");
- var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
- }
+ /// <summary>
+ /// Gets the user async.
+ /// </summary>
+ /// <param name="userId">The user_id.</param>
- /// <summary>
- /// Modifies the current user async.
- /// </summary>
- /// <param name="username">The username.</param>
- /// <param name="base64Avatar">The base64_avatar.</param>
+ internal Task<DiscordUser> GetUserAsync(ulong userId)
+ => this.GetUserAsync(userId.ToString(CultureInfo.InvariantCulture));
- internal async Task<TransportUser> ModifyCurrentUserAsync(string username, Optional<string> base64Avatar)
- {
- var pld = new RestUserUpdateCurrentPayload
+ /// <summary>
+ /// Gets the user async.
+ /// </summary>
+ /// <param name="userId">The user_id.</param>
+
+ internal async Task<DiscordUser> GetUserAsync(string userId)
{
- Username = username,
- AvatarBase64 = base64Avatar.ValueOrDefault(),
- AvatarSet = base64Avatar.HasValue
- };
+ var route = $"{Endpoints.USERS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {user_id = userId }, out var path);
- var route = $"{Endpoints.USERS}{Endpoints.ME}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var userRaw = JsonConvert.DeserializeObject<TransportUser>(res.Response);
+ var duser = new DiscordUser(userRaw) { Discord = this.Discord };
- var userRaw = JsonConvert.DeserializeObject<TransportUser>(res.Response);
+ return duser;
+ }
- return userRaw;
- }
+ /// <summary>
+ /// Gets the guild member async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
- /// <summary>
- /// Gets the current user guilds async.
- /// </summary>
- /// <param name="limit">The limit.</param>
- /// <param name="before">The before.</param>
- /// <param name="after">The after.</param>
+ internal async Task<DiscordMember> GetGuildMemberAsync(ulong guildId, ulong userId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, user_id = userId }, out var path);
- internal async Task<IReadOnlyList<DiscordGuild>> GetCurrentUserGuildsAsync(int limit = 100, ulong? before = null, ulong? after = null)
- {
- var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.GUILDS}";
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
+ var tm = JsonConvert.DeserializeObject<TransportMember>(res.Response);
- var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration)
- .AddParameter($"limit", limit.ToString(CultureInfo.InvariantCulture));
+ var usr = new DiscordUser(tm.User) { Discord = this.Discord };
+ usr = this.Discord.UserCache.AddOrUpdate(tm.User.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ return old;
+ });
- if (before != null)
- url.AddParameter("before", before.Value.ToString(CultureInfo.InvariantCulture));
- if (after != null)
- url.AddParameter("after", after.Value.ToString(CultureInfo.InvariantCulture));
+ return new DiscordMember(tm)
+ {
+ Discord = this.Discord,
+ GuildId = guildId
+ };
+ }
- var res = await this.DoRequestAsync(this.Discord, bucket, url.Build(), RestRequestMethod.GET, route).ConfigureAwait(false);
+ /// <summary>
+ /// Removes the guild member async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="reason">The reason.</param>
- if (this.Discord is DiscordClient)
+ internal Task RemoveGuildMemberAsync(ulong guildId, ulong userId, string reason)
{
- var guildsRaw = JsonConvert.DeserializeObject<IEnumerable<RestUserGuild>>(res.Response);
- var glds = guildsRaw.Select(xug => (this.Discord as DiscordClient)?.GuildsInternal[xug.Id]);
- return new ReadOnlyCollection<DiscordGuild>(new List<DiscordGuild>(glds));
- }
- else
- {
- return new ReadOnlyCollection<DiscordGuild>(JsonConvert.DeserializeObject<List<DiscordGuild>>(res.Response));
+ var urlParams = new Dictionary<string, string>();
+ if (reason != null)
+ urlParams["reason"] = reason;
+
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, user_id = userId }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
}
- }
- /// <summary>
- /// Modifies the guild member async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="nick">The nick.</param>
- /// <param name="roleIds">The role_ids.</param>
- /// <param name="mute">The mute.</param>
- /// <param name="deaf">The deaf.</param>
- /// <param name="voiceChannelId">The voice_channel_id.</param>
- /// <param name="reason">The reason.</param>
-
- internal Task ModifyGuildMemberAsync(ulong guildId, ulong userId, Optional<string> nick,
- Optional<IEnumerable<ulong>> roleIds, Optional<bool> mute, Optional<bool> deaf,
- Optional<ulong?> voiceChannelId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ /// <summary>
+ /// Modifies the current user async.
+ /// </summary>
+ /// <param name="username">The username.</param>
+ /// <param name="base64Avatar">The base64_avatar.</param>
- var pld = new RestGuildMemberModifyPayload
+ internal async Task<TransportUser> ModifyCurrentUserAsync(string username, Optional<string> base64Avatar)
{
- Nickname = nick,
- RoleIds = roleIds,
- Deafen = deaf,
- Mute = mute,
- VoiceChannelId = voiceChannelId
- };
+ var pld = new RestUserUpdateCurrentPayload
+ {
+ Username = username,
+ AvatarBase64 = base64Avatar.ValueOrDefault(),
+ AvatarSet = base64Avatar.HasValue
+ };
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, user_id = userId }, out var path);
+ var route = $"{Endpoints.USERS}{Endpoints.ME}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, payload: DiscordJson.SerializeObject(pld));
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- /// <summary>
- /// Modifies the time out of a guild member.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="until">Datetime offset.</param>
- /// <param name="reason">The reason.</param>
+ var userRaw = JsonConvert.DeserializeObject<TransportUser>(res.Response);
- internal Task ModifyTimeoutAsync(ulong guildId, ulong userId, DateTimeOffset? until, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ return userRaw;
+ }
+
+ /// <summary>
+ /// Gets the current user guilds async.
+ /// </summary>
+ /// <param name="limit">The limit.</param>
+ /// <param name="before">The before.</param>
+ /// <param name="after">The after.</param>
- var pld = new RestGuildMemberTimeoutModifyPayload
+ internal async Task<IReadOnlyList<DiscordGuild>> GetCurrentUserGuildsAsync(int limit = 100, ulong? before = null, ulong? after = null)
{
- CommunicationDisabledUntil = until
- };
+ var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.GUILDS}";
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, user_id = userId }, out var path);
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, payload: DiscordJson.SerializeObject(pld));
- }
+ var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration)
+ .AddParameter($"limit", limit.ToString(CultureInfo.InvariantCulture));
- /// <summary>
- /// Modifies the current member nickname async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="nick">The nick.</param>
- /// <param name="reason">The reason.</param>
+ if (before != null)
+ url.AddParameter("before", before.Value.ToString(CultureInfo.InvariantCulture));
+ if (after != null)
+ url.AddParameter("after", after.Value.ToString(CultureInfo.InvariantCulture));
- internal Task ModifyCurrentMemberNicknameAsync(ulong guildId, string nick, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var res = await this.DoRequestAsync(this.Discord, bucket, url.Build(), RestRequestMethod.GET, route).ConfigureAwait(false);
+
+ if (this.Discord is DiscordClient)
+ {
+ var guildsRaw = JsonConvert.DeserializeObject<IEnumerable<RestUserGuild>>(res.Response);
+ var glds = guildsRaw.Select(xug => (this.Discord as DiscordClient)?.GuildsInternal[xug.Id]);
+ return new ReadOnlyCollection<DiscordGuild>(new List<DiscordGuild>(glds));
+ }
+ else
+ {
+ return new ReadOnlyCollection<DiscordGuild>(JsonConvert.DeserializeObject<List<DiscordGuild>>(res.Response));
+ }
+ }
- var pld = new RestGuildMemberModifyPayload
+ /// <summary>
+ /// Modifies the guild member async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="nick">The nick.</param>
+ /// <param name="roleIds">The role_ids.</param>
+ /// <param name="mute">The mute.</param>
+ /// <param name="deaf">The deaf.</param>
+ /// <param name="voiceChannelId">The voice_channel_id.</param>
+ /// <param name="reason">The reason.</param>
+
+ internal Task ModifyGuildMemberAsync(ulong guildId, ulong userId, Optional<string> nick,
+ Optional<IEnumerable<ulong>> roleIds, Optional<bool> mute, Optional<bool> deaf,
+ Optional<ulong?> voiceChannelId, string reason)
{
- Nickname = nick
- };
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}{Endpoints.ME}{Endpoints.NICK}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
+ var pld = new RestGuildMemberModifyPayload
+ {
+ Nickname = nick,
+ RoleIds = roleIds,
+ Deafen = deaf,
+ Mute = mute,
+ VoiceChannelId = voiceChannelId
+ };
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, payload: DiscordJson.SerializeObject(pld));
- }
- #endregion
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, user_id = userId }, out var path);
- #region Roles
- /// <summary>
- /// Gets the guild roles async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, payload: DiscordJson.SerializeObject(pld));
+ }
- internal async Task<IReadOnlyList<DiscordRole>> GetGuildRolesAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ /// <summary>
+ /// Modifies the time out of a guild member.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="until">Datetime offset.</param>
+ /// <param name="reason">The reason.</param>
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ internal Task ModifyTimeoutAsync(ulong guildId, ulong userId, DateTimeOffset? until, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- var rolesRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordRole>>(res.Response).Select(xr => { xr.Discord = this.Discord; xr.GuildId = guildId; return xr; });
+ var pld = new RestGuildMemberTimeoutModifyPayload
+ {
+ CommunicationDisabledUntil = until
+ };
- return new ReadOnlyCollection<DiscordRole>(new List<DiscordRole>(rolesRaw));
- }
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, user_id = userId }, out var path);
- /// <summary>
- /// Gets the guild async.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- /// <param name="withCounts">If true, with_counts.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, payload: DiscordJson.SerializeObject(pld));
+ }
- internal async Task<DiscordGuild> GetGuildAsync(ulong guildId, bool? withCounts)
- {
- var urlParams = new Dictionary<string, string>();
- if (withCounts.HasValue)
- urlParams["with_counts"] = withCounts?.ToString();
+ /// <summary>
+ /// Modifies the current member nickname async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="nick">The nick.</param>
+ /// <param name="reason">The reason.</param>
- var route = $"{Endpoints.GUILDS}/:guild_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id = guildId }, out var path);
+ internal Task ModifyCurrentMemberNicknameAsync(ulong guildId, string nick, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route, urlParams).ConfigureAwait(false);
+ var pld = new RestGuildMemberModifyPayload
+ {
+ Nickname = nick
+ };
- var json = JObject.Parse(res.Response);
- var rawMembers = (JArray)json["members"];
- var guildRest = json.ToDiscordObject<DiscordGuild>();
- foreach (var r in guildRest.RolesInternal.Values)
- r.GuildId = guildRest.Id;
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}{Endpoints.ME}{Endpoints.NICK}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
- if (this.Discord is DiscordClient dc)
- {
- await dc.OnGuildUpdateEventAsync(guildRest, rawMembers).ConfigureAwait(false);
- return dc.GuildsInternal[guildRest.Id];
- }
- else
- {
- guildRest.Discord = this.Discord;
- return guildRest;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, payload: DiscordJson.SerializeObject(pld));
}
- }
-
- /// <summary>
- /// Modifies the guild role async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="roleId">The role_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="permissions">The permissions.</param>
- /// <param name="color">The color.</param>
- /// <param name="hoist">If true, hoist.</param>
- /// <param name="mentionable">If true, mentionable.</param>
- /// <param name="iconb64">The icon.</param>
- /// <param name="emoji">The unicode emoji icon.</param>
- /// <param name="reason">The reason.</param>
- internal async Task<DiscordRole> ModifyGuildRoleAsync(ulong guildId, ulong roleId, string name, Permissions? permissions, int? color, bool? hoist, bool? mentionable, Optional<string> iconb64, Optional<string> emoji, string reason)
- {
- var pld = new RestGuildRolePayload
- {
- Name = name,
- Permissions = permissions & PermissionMethods.FullPerms,
- Color = color,
- Hoist = hoist,
- Mentionable = mentionable,
- };
+ #endregion
- if (emoji.HasValue && !iconb64.HasValue)
- pld.UnicodeEmoji = emoji;
+ #region Roles
+ /// <summary>
+ /// Gets the guild roles async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- if (emoji.HasValue && iconb64.HasValue)
+ internal async Task<IReadOnlyList<DiscordRole>> GetGuildRolesAsync(ulong guildId)
{
- pld.IconBase64 = null;
- pld.UnicodeEmoji = emoji;
- }
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- if (iconb64.HasValue)
- pld.IconBase64 = iconb64;
-
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}/:role_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, role_id = roleId }, out var path);
+ var rolesRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordRole>>(res.Response).Select(xr => { xr.Discord = this.Discord; xr.GuildId = guildId; return xr; });
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ return new ReadOnlyCollection<DiscordRole>(new List<DiscordRole>(rolesRaw));
+ }
- var ret = JsonConvert.DeserializeObject<DiscordRole>(res.Response);
- ret.Discord = this.Discord;
- ret.GuildId = guildId;
+ /// <summary>
+ /// Gets the guild async.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="withCounts">If true, with_counts.</param>
- return ret;
- }
+ internal async Task<DiscordGuild> GetGuildAsync(ulong guildId, bool? withCounts)
+ {
+ var urlParams = new Dictionary<string, string>();
+ if (withCounts.HasValue)
+ urlParams["with_counts"] = withCounts?.ToString();
- /// <summary>
- /// Deletes the role async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="roleId">The role_id.</param>
- /// <param name="reason">The reason.</param>
+ var route = $"{Endpoints.GUILDS}/:guild_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id = guildId }, out var path);
- internal Task DeleteRoleAsync(ulong guildId, ulong roleId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route, urlParams).ConfigureAwait(false);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}/:role_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, role_id = roleId }, out var path);
+ var json = JObject.Parse(res.Response);
+ var rawMembers = (JArray)json["members"];
+ var guildRest = json.ToDiscordObject<DiscordGuild>();
+ foreach (var r in guildRest.RolesInternal.Values)
+ r.GuildId = guildRest.Id;
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ if (this.Discord is DiscordClient dc)
+ {
+ await dc.OnGuildUpdateEventAsync(guildRest, rawMembers).ConfigureAwait(false);
+ return dc.GuildsInternal[guildRest.Id];
+ }
+ else
+ {
+ guildRest.Discord = this.Discord;
+ return guildRest;
+ }
+ }
- /// <summary>
- /// Creates the guild role async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="permissions">The permissions.</param>
- /// <param name="color">The color.</param>
- /// <param name="hoist">If true, hoist.</param>
- /// <param name="mentionable">If true, mentionable.</param>
- /// <param name="reason">The reason.</param>
-
- internal async Task<DiscordRole> CreateGuildRoleAsync(ulong guildId, string name, Permissions? permissions, int? color, bool? hoist, bool? mentionable, string reason)
- {
- var pld = new RestGuildRolePayload
+ /// <summary>
+ /// Modifies the guild role async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="roleId">The role_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="permissions">The permissions.</param>
+ /// <param name="color">The color.</param>
+ /// <param name="hoist">If true, hoist.</param>
+ /// <param name="mentionable">If true, mentionable.</param>
+ /// <param name="iconb64">The icon.</param>
+ /// <param name="emoji">The unicode emoji icon.</param>
+ /// <param name="reason">The reason.</param>
+ internal async Task<DiscordRole> ModifyGuildRoleAsync(ulong guildId, ulong roleId, string name, Permissions? permissions, int? color, bool? hoist, bool? mentionable, Optional<string> iconb64, Optional<string> emoji, string reason)
{
- Name = name,
- Permissions = permissions & PermissionMethods.FullPerms,
- Color = color,
- Hoist = hoist,
- Mentionable = mentionable
- };
+ var pld = new RestGuildRolePayload
+ {
+ Name = name,
+ Permissions = permissions & PermissionMethods.FullPerms,
+ Color = color,
+ Hoist = hoist,
+ Mentionable = mentionable,
+ };
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ if (emoji.HasValue && !iconb64.HasValue)
+ pld.UnicodeEmoji = emoji;
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
+ if (emoji.HasValue && iconb64.HasValue)
+ {
+ pld.IconBase64 = null;
+ pld.UnicodeEmoji = emoji;
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ if (iconb64.HasValue)
+ pld.IconBase64 = iconb64;
- var ret = JsonConvert.DeserializeObject<DiscordRole>(res.Response);
- ret.Discord = this.Discord;
- ret.GuildId = guildId;
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- return ret;
- }
- #endregion
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}/:role_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, role_id = roleId }, out var path);
- #region Prune
- /// <summary>
- /// Gets the guild prune count async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="days">The days.</param>
- /// <param name="includeRoles">The include_roles.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- internal async Task<int> GetGuildPruneCountAsync(ulong guildId, int days, IEnumerable<ulong> includeRoles)
- {
- if (days < 0 || days > 30)
- throw new ArgumentException("Prune inactivity days must be a number between 0 and 30.", nameof(days));
+ var ret = JsonConvert.DeserializeObject<DiscordRole>(res.Response);
+ ret.Discord = this.Discord;
+ ret.GuildId = guildId;
- var urlParams = new Dictionary<string, string>
- {
- ["days"] = days.ToString(CultureInfo.InvariantCulture)
- };
+ return ret;
+ }
- var sb = new StringBuilder();
+ /// <summary>
+ /// Deletes the role async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="roleId">The role_id.</param>
+ /// <param name="reason">The reason.</param>
- if (includeRoles != null)
+ internal Task DeleteRoleAsync(ulong guildId, ulong roleId, string reason)
{
- var roleArray = includeRoles.ToArray();
- var roleArrayCount = roleArray.Length;
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
+
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}/:role_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, role_id = roleId }, out var path);
- for (var i = 0; i < roleArrayCount; i++)
- sb.Append($"&include_roles={roleArray[i]}");
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.PRUNE}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, $"{BuildQueryString(urlParams)}{sb}", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ /// <summary>
+ /// Creates the guild role async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="permissions">The permissions.</param>
+ /// <param name="color">The color.</param>
+ /// <param name="hoist">If true, hoist.</param>
+ /// <param name="mentionable">If true, mentionable.</param>
+ /// <param name="reason">The reason.</param>
+
+ internal async Task<DiscordRole> CreateGuildRoleAsync(ulong guildId, string name, Permissions? permissions, int? color, bool? hoist, bool? mentionable, string reason)
+ {
+ var pld = new RestGuildRolePayload
+ {
+ Name = name,
+ Permissions = permissions & PermissionMethods.FullPerms,
+ Color = color,
+ Hoist = hoist,
+ Mentionable = mentionable
+ };
- var pruned = JsonConvert.DeserializeObject<RestGuildPruneResultPayload>(res.Response);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- return pruned.Pruned.Value;
- }
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
- /// <summary>
- /// Begins the guild prune async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="days">The days.</param>
- /// <param name="computePruneCount">If true, compute_prune_count.</param>
- /// <param name="includeRoles">The include_roles.</param>
- /// <param name="reason">The reason.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- internal async Task<int?> BeginGuildPruneAsync(ulong guildId, int days, bool computePruneCount, IEnumerable<ulong> includeRoles, string reason)
- {
- if (days < 0 || days > 30)
- throw new ArgumentException("Prune inactivity days must be a number between 0 and 30.", nameof(days));
+ var ret = JsonConvert.DeserializeObject<DiscordRole>(res.Response);
+ ret.Discord = this.Discord;
+ ret.GuildId = guildId;
- var urlParams = new Dictionary<string, string>
- {
- ["days"] = days.ToString(CultureInfo.InvariantCulture),
- ["compute_prune_count"] = computePruneCount.ToString()
- };
+ return ret;
+ }
+ #endregion
- var sb = new StringBuilder();
+ #region Prune
+ /// <summary>
+ /// Gets the guild prune count async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="days">The days.</param>
+ /// <param name="includeRoles">The include_roles.</param>
- if (includeRoles != null)
+ internal async Task<int> GetGuildPruneCountAsync(ulong guildId, int days, IEnumerable<ulong> includeRoles)
{
- var roleArray = includeRoles.ToArray();
- var roleArrayCount = roleArray.Length;
+ if (days < 0 || days > 30)
+ throw new ArgumentException("Prune inactivity days must be a number between 0 and 30.", nameof(days));
- for (var i = 0; i < roleArrayCount; i++)
- sb.Append($"&include_roles={roleArray[i]}");
- }
+ var urlParams = new Dictionary<string, string>
+ {
+ ["days"] = days.ToString(CultureInfo.InvariantCulture)
+ };
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var sb = new StringBuilder();
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.PRUNE}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
+ if (includeRoles != null)
+ {
+ var roleArray = includeRoles.ToArray();
+ var roleArrayCount = roleArray.Length;
- var url = Utilities.GetApiUriFor(path, $"{BuildQueryString(urlParams)}{sb}", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers).ConfigureAwait(false);
+ for (var i = 0; i < roleArrayCount; i++)
+ sb.Append($"&include_roles={roleArray[i]}");
+ }
- var pruned = JsonConvert.DeserializeObject<RestGuildPruneResultPayload>(res.Response);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.PRUNE}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, $"{BuildQueryString(urlParams)}{sb}", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- return pruned.Pruned;
- }
- #endregion
+ var pruned = JsonConvert.DeserializeObject<RestGuildPruneResultPayload>(res.Response);
- #region GuildVarious
- /// <summary>
- /// Gets the template async.
- /// </summary>
- /// <param name="code">The code.</param>
+ return pruned.Pruned.Value;
+ }
- internal async Task<DiscordGuildTemplate> GetTemplateAsync(string code)
- {
- var route = $"{Endpoints.GUILDS}{Endpoints.TEMPLATES}/:code";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { code }, out var path);
+ /// <summary>
+ /// Begins the guild prune async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="days">The days.</param>
+ /// <param name="computePruneCount">If true, compute_prune_count.</param>
+ /// <param name="includeRoles">The include_roles.</param>
+ /// <param name="reason">The reason.</param>
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ internal async Task<int?> BeginGuildPruneAsync(ulong guildId, int days, bool computePruneCount, IEnumerable<ulong> includeRoles, string reason)
+ {
+ if (days < 0 || days > 30)
+ throw new ArgumentException("Prune inactivity days must be a number between 0 and 30.", nameof(days));
- var templatesRaw = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
+ var urlParams = new Dictionary<string, string>
+ {
+ ["days"] = days.ToString(CultureInfo.InvariantCulture),
+ ["compute_prune_count"] = computePruneCount.ToString()
+ };
- return templatesRaw;
- }
+ var sb = new StringBuilder();
- /// <summary>
- /// Gets the guild integrations async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ if (includeRoles != null)
+ {
+ var roleArray = includeRoles.ToArray();
+ var roleArrayCount = roleArray.Length;
- internal async Task<IReadOnlyList<DiscordIntegration>> GetGuildIntegrationsAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ for (var i = 0; i < roleArrayCount; i++)
+ sb.Append($"&include_roles={roleArray[i]}");
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var integrationsRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordIntegration>>(res.Response).Select(xi => { xi.Discord = this.Discord; return xi; });
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.PRUNE}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
- return new ReadOnlyCollection<DiscordIntegration>(new List<DiscordIntegration>(integrationsRaw));
- }
+ var url = Utilities.GetApiUriFor(path, $"{BuildQueryString(urlParams)}{sb}", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers).ConfigureAwait(false);
- /// <summary>
- /// Gets the guild preview async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ var pruned = JsonConvert.DeserializeObject<RestGuildPruneResultPayload>(res.Response);
- internal async Task<DiscordGuildPreview> GetGuildPreviewAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.PREVIEW}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ return pruned.Pruned;
+ }
+ #endregion
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ #region GuildVarious
+ /// <summary>
+ /// Gets the template async.
+ /// </summary>
+ /// <param name="code">The code.</param>
- var ret = JsonConvert.DeserializeObject<DiscordGuildPreview>(res.Response);
- ret.Discord = this.Discord;
+ internal async Task<DiscordGuildTemplate> GetTemplateAsync(string code)
+ {
+ var route = $"{Endpoints.GUILDS}{Endpoints.TEMPLATES}/:code";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { code }, out var path);
- return ret;
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- /// <summary>
- /// Creates the guild integration async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="type">The type.</param>
- /// <param name="id">The id.</param>
+ var templatesRaw = JsonConvert.DeserializeObject<DiscordGuildTemplate>(res.Response);
- internal async Task<DiscordIntegration> CreateGuildIntegrationAsync(ulong guildId, string type, ulong id)
- {
- var pld = new RestGuildIntegrationAttachPayload
- {
- Type = type,
- Id = id
- };
+ return templatesRaw;
+ }
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
+ /// <summary>
+ /// Gets the guild integrations async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ internal async Task<IReadOnlyList<DiscordIntegration>> GetGuildIntegrationsAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var ret = JsonConvert.DeserializeObject<DiscordIntegration>(res.Response);
- ret.Discord = this.Discord;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- return ret;
- }
+ var integrationsRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordIntegration>>(res.Response).Select(xi => { xi.Discord = this.Discord; return xi; });
- /// <summary>
- /// Modifies the guild integration async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="integrationId">The integration_id.</param>
- /// <param name="expireBehaviour">The expire_behaviour.</param>
- /// <param name="expireGracePeriod">The expire_grace_period.</param>
- /// <param name="enableEmoticons">If true, enable_emoticons.</param>
+ return new ReadOnlyCollection<DiscordIntegration>(new List<DiscordIntegration>(integrationsRaw));
+ }
- internal async Task<DiscordIntegration> ModifyGuildIntegrationAsync(ulong guildId, ulong integrationId, int expireBehaviour, int expireGracePeriod, bool enableEmoticons)
- {
- var pld = new RestGuildIntegrationModifyPayload
+ /// <summary>
+ /// Gets the guild preview async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+
+ internal async Task<DiscordGuildPreview> GetGuildPreviewAsync(ulong guildId)
{
- ExpireBehavior = expireBehaviour,
- ExpireGracePeriod = expireGracePeriod,
- EnableEmoticons = enableEmoticons
- };
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.PREVIEW}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}/:integration_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, integration_id = integrationId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var ret = JsonConvert.DeserializeObject<DiscordGuildPreview>(res.Response);
+ ret.Discord = this.Discord;
- var ret = JsonConvert.DeserializeObject<DiscordIntegration>(res.Response);
- ret.Discord = this.Discord;
+ return ret;
+ }
- return ret;
- }
+ /// <summary>
+ /// Creates the guild integration async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="id">The id.</param>
- /// <summary>
- /// Deletes the guild integration async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="integration">The integration.</param>
+ internal async Task<DiscordIntegration> CreateGuildIntegrationAsync(ulong guildId, string type, ulong id)
+ {
+ var pld = new RestGuildIntegrationAttachPayload
+ {
+ Type = type,
+ Id = id
+ };
- internal Task DeleteGuildIntegrationAsync(ulong guildId, DiscordIntegration integration)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}/:integration_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, integration_id = integration.Id }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, payload: DiscordJson.SerializeObject(integration));
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- /// <summary>
- /// Syncs the guild integration async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="integrationId">The integration_id.</param>
+ var ret = JsonConvert.DeserializeObject<DiscordIntegration>(res.Response);
+ ret.Discord = this.Discord;
- internal Task SyncGuildIntegrationAsync(ulong guildId, ulong integrationId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}/:integration_id{Endpoints.SYNC}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId, integration_id = integrationId }, out var path);
+ return ret;
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route);
- }
+ /// <summary>
+ /// Modifies the guild integration async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="integrationId">The integration_id.</param>
+ /// <param name="expireBehaviour">The expire_behaviour.</param>
+ /// <param name="expireGracePeriod">The expire_grace_period.</param>
+ /// <param name="enableEmoticons">If true, enable_emoticons.</param>
- /// <summary>
- /// Gets the guild voice regions async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ internal async Task<DiscordIntegration> ModifyGuildIntegrationAsync(ulong guildId, ulong integrationId, int expireBehaviour, int expireGracePeriod, bool enableEmoticons)
+ {
+ var pld = new RestGuildIntegrationModifyPayload
+ {
+ ExpireBehavior = expireBehaviour,
+ ExpireGracePeriod = expireGracePeriod,
+ EnableEmoticons = enableEmoticons
+ };
- internal async Task<IReadOnlyList<DiscordVoiceRegion>> GetGuildVoiceRegionsAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.REGIONS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}/:integration_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, integration_id = integrationId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var regionsRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordVoiceRegion>>(res.Response);
+ var ret = JsonConvert.DeserializeObject<DiscordIntegration>(res.Response);
+ ret.Discord = this.Discord;
- return new ReadOnlyCollection<DiscordVoiceRegion>(new List<DiscordVoiceRegion>(regionsRaw));
- }
+ return ret;
+ }
- /// <summary>
- /// Gets the guild invites async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ /// <summary>
+ /// Deletes the guild integration async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="integration">The integration.</param>
- internal async Task<IReadOnlyList<DiscordInvite>> GetGuildInvitesAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INVITES}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ internal Task DeleteGuildIntegrationAsync(ulong guildId, DiscordIntegration integration)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}/:integration_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, integration_id = integration.Id }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, payload: DiscordJson.SerializeObject(integration));
+ }
- var invitesRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordInvite>>(res.Response).Select(xi => { xi.Discord = this.Discord; return xi; });
+ /// <summary>
+ /// Syncs the guild integration async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="integrationId">The integration_id.</param>
- return new ReadOnlyCollection<DiscordInvite>(new List<DiscordInvite>(invitesRaw));
- }
- #endregion
+ internal Task SyncGuildIntegrationAsync(ulong guildId, ulong integrationId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INTEGRATIONS}/:integration_id{Endpoints.SYNC}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId, integration_id = integrationId }, out var path);
- #region Invite
- /// <summary>
- /// Gets the invite async.
- /// </summary>
- /// <param name="inviteCode">The invite_code.</param>
- /// <param name="withCounts">If true, with_counts.</param>
- /// <param name="withExpiration">If true, with_expiration.</param>
- /// <param name="guildScheduledEventId">The scheduled event id to get.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route);
+ }
- internal async Task<DiscordInvite> GetInviteAsync(string inviteCode, bool? withCounts, bool? withExpiration, ulong? guildScheduledEventId)
- {
- var urlParams = new Dictionary<string, string>();
- if (withCounts.HasValue)
- urlParams["with_counts"] = withCounts?.ToString();
- if (withExpiration.HasValue)
- urlParams["with_expiration"] = withExpiration?.ToString();
- if (guildScheduledEventId.HasValue)
- urlParams["guild_scheduled_event_id"] = guildScheduledEventId?.ToString();
+ /// <summary>
+ /// Gets the guild voice regions async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- var route = $"{Endpoints.INVITES}/:invite_code";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {invite_code = inviteCode }, out var path);
+ internal async Task<IReadOnlyList<DiscordVoiceRegion>> GetGuildVoiceRegionsAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.REGIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordInvite>(res.Response);
- ret.Discord = this.Discord;
+ var regionsRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordVoiceRegion>>(res.Response);
- return ret;
- }
+ return new ReadOnlyCollection<DiscordVoiceRegion>(new List<DiscordVoiceRegion>(regionsRaw));
+ }
- /// <summary>
- /// Deletes the invite async.
- /// </summary>
- /// <param name="inviteCode">The invite_code.</param>
- /// <param name="reason">The reason.</param>
+ /// <summary>
+ /// Gets the guild invites async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- internal async Task<DiscordInvite> DeleteInviteAsync(string inviteCode, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ internal async Task<IReadOnlyList<DiscordInvite>> GetGuildInvitesAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.INVITES}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var route = $"{Endpoints.INVITES}/:invite_code";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {invite_code = inviteCode }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers).ConfigureAwait(false);
+ var invitesRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordInvite>>(res.Response).Select(xi => { xi.Discord = this.Discord; return xi; });
- var ret = JsonConvert.DeserializeObject<DiscordInvite>(res.Response);
- ret.Discord = this.Discord;
+ return new ReadOnlyCollection<DiscordInvite>(new List<DiscordInvite>(invitesRaw));
+ }
+ #endregion
+
+ #region Invite
+ /// <summary>
+ /// Gets the invite async.
+ /// </summary>
+ /// <param name="inviteCode">The invite_code.</param>
+ /// <param name="withCounts">If true, with_counts.</param>
+ /// <param name="withExpiration">If true, with_expiration.</param>
+ /// <param name="guildScheduledEventId">The scheduled event id to get.</param>
+
+ internal async Task<DiscordInvite> GetInviteAsync(string inviteCode, bool? withCounts, bool? withExpiration, ulong? guildScheduledEventId)
+ {
+ var urlParams = new Dictionary<string, string>();
+ if (withCounts.HasValue)
+ urlParams["with_counts"] = withCounts?.ToString();
+ if (withExpiration.HasValue)
+ urlParams["with_expiration"] = withExpiration?.ToString();
+ if (guildScheduledEventId.HasValue)
+ urlParams["guild_scheduled_event_id"] = guildScheduledEventId?.ToString();
- return ret;
- }
+ var route = $"{Endpoints.INVITES}/:invite_code";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {invite_code = inviteCode }, out var path);
- /*
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+
+ var ret = JsonConvert.DeserializeObject<DiscordInvite>(res.Response);
+ ret.Discord = this.Discord;
+
+ return ret;
+ }
+
+ /// <summary>
+ /// Deletes the invite async.
+ /// </summary>
+ /// <param name="inviteCode">The invite_code.</param>
+ /// <param name="reason">The reason.</param>
+
+ internal async Task<DiscordInvite> DeleteInviteAsync(string inviteCode, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
+
+ var route = $"{Endpoints.INVITES}/:invite_code";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {invite_code = inviteCode }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers).ConfigureAwait(false);
+
+ var ret = JsonConvert.DeserializeObject<DiscordInvite>(res.Response);
+ ret.Discord = this.Discord;
+
+ return ret;
+ }
+
+ /*
* Disabled due to API restrictions
*
* internal async Task<DiscordInvite> InternalAcceptInvite(string invite_code)
* {
* this.Discord.DebugLogger.LogMessage(LogLevel.Warning, "REST API", "Invite accept endpoint was used; this account is now likely unverified", DateTime.Now);
*
* var url = new Uri($"{Utils.GetApiBaseUri(this.Configuration), Endpoints.INVITES}/{invite_code));
* var bucket = this.Rest.GetBucket(0, MajorParameterType.Unbucketed, url, HttpRequestMethod.POST);
* var res = await this.DoRequestAsync(this.Discord, bucket, url, HttpRequestMethod.POST).ConfigureAwait(false);
*
* var ret = JsonConvert.DeserializeObject<DiscordInvite>(res.Response);
* ret.Discord = this.Discord;
*
* return ret;
* }
*/
- #endregion
-
- #region Connections
- /// <summary>
- /// Gets the users connections async.
- /// </summary>
-
- internal async Task<IReadOnlyList<DiscordConnection>> GetUserConnectionsAsync()
- {
- var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CONNECTIONS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
-
- var connectionsRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordConnection>>(res.Response).Select(xc => { xc.Discord = this.Discord; return xc; });
+ #endregion
- return new ReadOnlyCollection<DiscordConnection>(new List<DiscordConnection>(connectionsRaw));
- }
- #endregion
-
- #region Voice
- /// <summary>
- /// Lists the voice regions async.
- /// </summary>
+ #region Connections
+ /// <summary>
+ /// Gets the users connections async.
+ /// </summary>
- internal async Task<IReadOnlyList<DiscordVoiceRegion>> ListVoiceRegionsAsync()
- {
- var route = $"{Endpoints.VOICE}{Endpoints.REGIONS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
+ internal async Task<IReadOnlyList<DiscordConnection>> GetUserConnectionsAsync()
+ {
+ var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.CONNECTIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var regions = JsonConvert.DeserializeObject<IEnumerable<DiscordVoiceRegion>>(res.Response);
+ var connectionsRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordConnection>>(res.Response).Select(xc => { xc.Discord = this.Discord; return xc; });
- return new ReadOnlyCollection<DiscordVoiceRegion>(new List<DiscordVoiceRegion>(regions));
- }
- #endregion
+ return new ReadOnlyCollection<DiscordConnection>(new List<DiscordConnection>(connectionsRaw));
+ }
+ #endregion
- #region Webhooks
- /// <summary>
- /// Creates the webhook async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="base64Avatar">The base64_avatar.</param>
- /// <param name="reason">The reason.</param>
+ #region Voice
+ /// <summary>
+ /// Lists the voice regions async.
+ /// </summary>
- internal async Task<DiscordWebhook> CreateWebhookAsync(ulong channelId, string name, Optional<string> base64Avatar, string reason)
- {
- var pld = new RestWebhookPayload
+ internal async Task<IReadOnlyList<DiscordVoiceRegion>> ListVoiceRegionsAsync()
{
- Name = name,
- AvatarBase64 = base64Avatar.ValueOrDefault(),
- AvatarSet = base64Avatar.HasValue
- };
+ var route = $"{Endpoints.VOICE}{Endpoints.REGIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
-
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.WEBHOOKS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
- ret.Discord = this.Discord;
- ret.ApiClient = this;
+ var regions = JsonConvert.DeserializeObject<IEnumerable<DiscordVoiceRegion>>(res.Response);
- return ret;
- }
+ return new ReadOnlyCollection<DiscordVoiceRegion>(new List<DiscordVoiceRegion>(regions));
+ }
+ #endregion
+
+ #region Webhooks
+ /// <summary>
+ /// Creates the webhook async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="base64Avatar">The base64_avatar.</param>
+ /// <param name="reason">The reason.</param>
+
+ internal async Task<DiscordWebhook> CreateWebhookAsync(ulong channelId, string name, Optional<string> base64Avatar, string reason)
+ {
+ var pld = new RestWebhookPayload
+ {
+ Name = name,
+ AvatarBase64 = base64Avatar.ValueOrDefault(),
+ AvatarSet = base64Avatar.HasValue
+ };
- /// <summary>
- /// Gets the channel webhooks async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- internal async Task<IReadOnlyList<DiscordWebhook>> GetChannelWebhooksAsync(ulong channelId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.WEBHOOKS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.WEBHOOKS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {channel_id = channelId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- var webhooksRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordWebhook>>(res.Response).Select(xw => { xw.Discord = this.Discord; xw.ApiClient = this; return xw; });
+ var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
+ ret.Discord = this.Discord;
+ ret.ApiClient = this;
- return new ReadOnlyCollection<DiscordWebhook>(new List<DiscordWebhook>(webhooksRaw));
- }
+ return ret;
+ }
- /// <summary>
- /// Gets the guild webhooks async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ /// <summary>
+ /// Gets the channel webhooks async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
- internal async Task<IReadOnlyList<DiscordWebhook>> GetGuildWebhooksAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WEBHOOKS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ internal async Task<IReadOnlyList<DiscordWebhook>> GetChannelWebhooksAsync(ulong channelId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.WEBHOOKS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var webhooksRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordWebhook>>(res.Response).Select(xw => { xw.Discord = this.Discord; xw.ApiClient = this; return xw; });
+ var webhooksRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordWebhook>>(res.Response).Select(xw => { xw.Discord = this.Discord; xw.ApiClient = this; return xw; });
- return new ReadOnlyCollection<DiscordWebhook>(new List<DiscordWebhook>(webhooksRaw));
- }
+ return new ReadOnlyCollection<DiscordWebhook>(new List<DiscordWebhook>(webhooksRaw));
+ }
- /// <summary>
- /// Gets the webhook async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
+ /// <summary>
+ /// Gets the guild webhooks async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- internal async Task<DiscordWebhook> GetWebhookAsync(ulong webhookId)
- {
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {webhook_id = webhookId }, out var path);
+ internal async Task<IReadOnlyList<DiscordWebhook>> GetGuildWebhooksAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WEBHOOKS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
- ret.Discord = this.Discord;
- ret.ApiClient = this;
+ var webhooksRaw = JsonConvert.DeserializeObject<IEnumerable<DiscordWebhook>>(res.Response).Select(xw => { xw.Discord = this.Discord; xw.ApiClient = this; return xw; });
- return ret;
- }
+ return new ReadOnlyCollection<DiscordWebhook>(new List<DiscordWebhook>(webhooksRaw));
+ }
- /// <summary>
- /// Gets the webhook with token async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
+ /// <summary>
+ /// Gets the webhook async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
- internal async Task<DiscordWebhook> GetWebhookWithTokenAsync(ulong webhookId, string webhookToken)
- {
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
+ internal async Task<DiscordWebhook> GetWebhookAsync(ulong webhookId)
+ {
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {webhook_id = webhookId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
- ret.Token = webhookToken;
- ret.Id = webhookId;
- ret.Discord = this.Discord;
- ret.ApiClient = this;
+ var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
+ ret.Discord = this.Discord;
+ ret.ApiClient = this;
- return ret;
- }
+ return ret;
+ }
- /// <summary>
- /// Modifies the webhook async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="channelId">The channel id.</param>
- /// <param name="name">The name.</param>
- /// <param name="base64Avatar">The base64_avatar.</param>
- /// <param name="reason">The reason.</param>
+ /// <summary>
+ /// Gets the webhook with token async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
- internal async Task<DiscordWebhook> ModifyWebhookAsync(ulong webhookId, ulong channelId, string name, Optional<string> base64Avatar, string reason)
- {
- var pld = new RestWebhookPayload
+ internal async Task<DiscordWebhook> GetWebhookWithTokenAsync(ulong webhookId, string webhookToken)
{
- Name = name,
- AvatarBase64 = base64Avatar.ValueOrDefault(),
- AvatarSet = base64Avatar.HasValue,
- ChannelId = channelId
- };
-
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
- ret.Discord = this.Discord;
- ret.ApiClient = this;
+ var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
+ ret.Token = webhookToken;
+ ret.Id = webhookId;
+ ret.Discord = this.Discord;
+ ret.ApiClient = this;
- return ret;
- }
+ return ret;
+ }
- /// <summary>
- /// Modifies the webhook async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="base64Avatar">The base64_avatar.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="reason">The reason.</param>
+ /// <summary>
+ /// Modifies the webhook async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="base64Avatar">The base64_avatar.</param>
+ /// <param name="reason">The reason.</param>
- internal async Task<DiscordWebhook> ModifyWebhookAsync(ulong webhookId, string name, string base64Avatar, string webhookToken, string reason)
- {
- var pld = new RestWebhookPayload
+ internal async Task<DiscordWebhook> ModifyWebhookAsync(ulong webhookId, ulong channelId, string name, Optional<string> base64Avatar, string reason)
{
- Name = name,
- AvatarBase64 = base64Avatar
- };
-
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
-
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var pld = new RestWebhookPayload
+ {
+ Name = name,
+ AvatarBase64 = base64Avatar.ValueOrDefault(),
+ AvatarSet = base64Avatar.HasValue,
+ ChannelId = channelId
+ };
- var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
- ret.Discord = this.Discord;
- ret.ApiClient = this;
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- return ret;
- }
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId }, out var path);
- /// <summary>
- /// Deletes the webhook async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="reason">The reason.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- internal Task DeleteWebhookAsync(ulong webhookId, string reason)
- {
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
+ ret.Discord = this.Discord;
+ ret.ApiClient = this;
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {webhook_id = webhookId }, out var path);
+ return ret;
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ /// <summary>
+ /// Modifies the webhook async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="base64Avatar">The base64_avatar.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="reason">The reason.</param>
- /// <summary>
- /// Deletes the webhook async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="reason">The reason.</param>
+ internal async Task<DiscordWebhook> ModifyWebhookAsync(ulong webhookId, string name, string base64Avatar, string webhookToken, string reason)
+ {
+ var pld = new RestWebhookPayload
+ {
+ Name = name,
+ AvatarBase64 = base64Avatar
+ };
- internal Task DeleteWebhookAsync(ulong webhookId, string webhookToken, string reason)
- {
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- /// <summary>
- /// Executes the webhook async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="builder">The builder.</param>
- /// <param name="threadId">The thread_id.</param>
+ var ret = JsonConvert.DeserializeObject<DiscordWebhook>(res.Response);
+ ret.Discord = this.Discord;
+ ret.ApiClient = this;
- internal async Task<DiscordMessage> ExecuteWebhookAsync(ulong webhookId, string webhookToken, DiscordWebhookBuilder builder, string threadId)
- {
- builder.Validate();
+ return ret;
+ }
- if (builder.Embeds != null)
- foreach (var embed in builder.Embeds)
- if (embed.Timestamp != null)
- embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
+ /// <summary>
+ /// Deletes the webhook async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="reason">The reason.</param>
- var values = new Dictionary<string, string>();
- var pld = new RestWebhookExecutePayload
+ internal Task DeleteWebhookAsync(ulong webhookId, string reason)
{
- Content = builder.Content,
- Username = builder.Username.ValueOrDefault(),
- AvatarUrl = builder.AvatarUrl.ValueOrDefault(),
- IsTts = builder.IsTts,
- Embeds = builder.Embeds,
- Components = builder.Components
- };
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- if (builder.Mentions != null)
- pld.Mentions = new DiscordMentions(builder.Mentions, builder.Mentions.Any());
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {webhook_id = webhookId }, out var path);
- if (builder.Files?.Count > 0)
- {
- ulong fileId = 0;
- List<DiscordAttachment> attachments = new();
- foreach (var file in builder.Files)
- {
- DiscordAttachment att = new()
- {
- Id = fileId,
- Discord = this.Discord,
- Description = file.Description,
- FileName = file.FileName,
- FileSize = null
- };
- attachments.Add(att);
- fileId++;
- }
- pld.Attachments = attachments;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
- if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.Files?.Count > 0 || builder.IsTts == true || builder.Mentions != null)
- values["payload_json"] = DiscordJson.SerializeObject(pld);
-
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
-
- var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true");
- if (threadId != null)
- qub.AddParameter("thread_id", threadId);
-
- var url = qub.Build();
-
- var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
+ /// <summary>
+ /// Deletes the webhook async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="reason">The reason.</param>
- foreach (var att in ret.Attachments)
- att.Discord = this.Discord;
-
- foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
+ internal Task DeleteWebhookAsync(ulong webhookId, string webhookToken, string reason)
{
- file.Stream.Position = file.ResetPositionTo.Value;
- }
-
- ret.Discord = this.Discord;
- return ret;
- }
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- /// <summary>
- /// Executes the webhook slack async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="jsonPayload">The json_payload.</param>
- /// <param name="threadId">The thread_id.</param>
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
- internal async Task<DiscordMessage> ExecuteWebhookSlackAsync(ulong webhookId, string webhookToken, string jsonPayload, string threadId)
- {
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.SLACK}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
-
- var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true");
- if (threadId != null)
- qub.AddParameter("thread_id", threadId);
- var url = qub.Build();
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: jsonPayload).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
- ret.Discord = this.Discord;
- return ret;
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
- /// <summary>
- /// Executes the webhook github async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="jsonPayload">The json_payload.</param>
- /// <param name="threadId">The thread_id.</param>
+ /// <summary>
+ /// Executes the webhook async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="builder">The builder.</param>
+ /// <param name="threadId">The thread_id.</param>
- internal async Task<DiscordMessage> ExecuteWebhookGithubAsync(ulong webhookId, string webhookToken, string jsonPayload, string threadId)
- {
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.GITHUB}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
-
- var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true");
- if (threadId != null)
- qub.AddParameter("thread_id", threadId);
- var url = qub.Build();
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: jsonPayload).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
- ret.Discord = this.Discord;
- return ret;
- }
+ internal async Task<DiscordMessage> ExecuteWebhookAsync(ulong webhookId, string webhookToken, DiscordWebhookBuilder builder, string threadId)
+ {
+ builder.Validate();
- /// <summary>
- /// Edits the webhook message async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="builder">The builder.</param>
- /// <param name="threadId">The thread_id.</param>
+ if (builder.Embeds != null)
+ foreach (var embed in builder.Embeds)
+ if (embed.Timestamp != null)
+ embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
- internal async Task<DiscordMessage> EditWebhookMessageAsync(ulong webhookId, string webhookToken, string messageId, DiscordWebhookBuilder builder, string threadId)
- {
- builder.Validate(true);
+ var values = new Dictionary<string, string>();
+ var pld = new RestWebhookExecutePayload
+ {
+ Content = builder.Content,
+ Username = builder.Username.ValueOrDefault(),
+ AvatarUrl = builder.AvatarUrl.ValueOrDefault(),
+ IsTts = builder.IsTts,
+ Embeds = builder.Embeds,
+ Components = builder.Components
+ };
- var pld = new RestWebhookMessageEditPayload
- {
- Content = builder.Content,
- Embeds = builder.Embeds,
- Mentions = builder.Mentions,
- Components = builder.Components,
- };
+ if (builder.Mentions != null)
+ pld.Mentions = new DiscordMentions(builder.Mentions, builder.Mentions.Any());
- if (builder.Files?.Count > 0)
- {
- ulong fileId = 0;
- List<DiscordAttachment> attachments = new();
- foreach (var file in builder.Files)
+ if (builder.Files?.Count > 0)
{
- DiscordAttachment att = new()
+ ulong fileId = 0;
+ List<DiscordAttachment> attachments = new();
+ foreach (var file in builder.Files)
{
- Id = fileId,
- Discord = this.Discord,
- Description = file.Description,
- FileName = file.FileName,
- FileSize = null
- };
- attachments.Add(att);
- fileId++;
+ DiscordAttachment att = new()
+ {
+ Id = fileId,
+ Discord = this.Discord,
+ Description = file.Description,
+ FileName = file.FileName,
+ FileSize = null
+ };
+ attachments.Add(att);
+ fileId++;
+ }
+ pld.Attachments = attachments;
}
- if (builder.Attachments != null && builder.Attachments?.Count() > 0)
- attachments.AddRange(builder.Attachments);
- pld.Attachments = attachments;
+ if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.Files?.Count > 0 || builder.IsTts == true || builder.Mentions != null)
+ values["payload_json"] = DiscordJson.SerializeObject(pld);
- var values = new Dictionary<string, string>
- {
- ["payload_json"] = DiscordJson.SerializeObject(pld)
- };
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
- var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
+ var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true");
if (threadId != null)
qub.AddParameter("thread_id", threadId);
var url = qub.Build();
- var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, values: values, files: builder.Files);
+ var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
- ret.Discord = this.Discord;
-
- foreach (var att in ret.AttachmentsInternal)
+ foreach (var att in ret.Attachments)
att.Discord = this.Discord;
foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
{
file.Stream.Position = file.ResetPositionTo.Value;
}
+ ret.Discord = this.Discord;
return ret;
}
- else
- {
- pld.Attachments = builder.Attachments;
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
+ /// <summary>
+ /// Executes the webhook slack async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="jsonPayload">The json_payload.</param>
+ /// <param name="threadId">The thread_id.</param>
- var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
+ internal async Task<DiscordMessage> ExecuteWebhookSlackAsync(ulong webhookId, string webhookToken, string jsonPayload, string threadId)
+ {
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.SLACK}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
+
+ var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true");
if (threadId != null)
qub.AddParameter("thread_id", threadId);
-
var url = qub.Build();
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
-
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: jsonPayload).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
-
ret.Discord = this.Discord;
+ return ret;
+ }
- foreach (var att in ret.AttachmentsInternal)
- att.Discord = this.Discord;
+ /// <summary>
+ /// Executes the webhook github async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="jsonPayload">The json_payload.</param>
+ /// <param name="threadId">The thread_id.</param>
+
+ internal async Task<DiscordMessage> ExecuteWebhookGithubAsync(ulong webhookId, string webhookToken, string jsonPayload, string threadId)
+ {
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.GITHUB}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {webhook_id = webhookId, webhook_token = webhookToken }, out var path);
+ var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true");
+ if (threadId != null)
+ qub.AddParameter("thread_id", threadId);
+ var url = qub.Build();
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: jsonPayload).ConfigureAwait(false);
+ var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
+ ret.Discord = this.Discord;
return ret;
}
- }
- /// <summary>
- /// Edits the webhook message async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="builder">The builder.</param>
- /// <param name="threadId">The thread_id.</param>
+ /// <summary>
+ /// Edits the webhook message async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="builder">The builder.</param>
+ /// <param name="threadId">The thread_id.</param>
- internal Task<DiscordMessage> EditWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId, DiscordWebhookBuilder builder, ulong threadId) =>
- this.EditWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), builder, threadId.ToString());
+ internal async Task<DiscordMessage> EditWebhookMessageAsync(ulong webhookId, string webhookToken, string messageId, DiscordWebhookBuilder builder, string threadId)
+ {
+ builder.Validate(true);
- /// <summary>
- /// Gets the webhook message async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="threadId">The thread_id.</param>
+ var pld = new RestWebhookMessageEditPayload
+ {
+ Content = builder.Content,
+ Embeds = builder.Embeds,
+ Mentions = builder.Mentions,
+ Components = builder.Components,
+ };
- internal async Task<DiscordMessage> GetWebhookMessageAsync(ulong webhookId, string webhookToken, string messageId, string threadId)
- {
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
-
- var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
- if (threadId != null)
- qub.AddParameter("thread_id", threadId);
- var url = qub.Build();
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
-
- var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
- ret.Discord = this.Discord;
- return ret;
- }
+ if (builder.Files?.Count > 0)
+ {
+ ulong fileId = 0;
+ List<DiscordAttachment> attachments = new();
+ foreach (var file in builder.Files)
+ {
+ DiscordAttachment att = new()
+ {
+ Id = fileId,
+ Discord = this.Discord,
+ Description = file.Description,
+ FileName = file.FileName,
+ FileSize = null
+ };
+ attachments.Add(att);
+ fileId++;
+ }
+ if (builder.Attachments != null && builder.Attachments?.Count() > 0)
+ attachments.AddRange(builder.Attachments);
- /// <summary>
- /// Gets the webhook message async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="messageId">The message_id.</param>
+ pld.Attachments = attachments;
- internal Task<DiscordMessage> GetWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId) =>
- this.GetWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), null);
+ var values = new Dictionary<string, string>
+ {
+ ["payload_json"] = DiscordJson.SerializeObject(pld)
+ };
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
- /// <summary>
- /// Gets the webhook message async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="threadId">The thread_id.</param>
+ var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
+ if (threadId != null)
+ qub.AddParameter("thread_id", threadId);
- internal Task<DiscordMessage> GetWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId, ulong threadId) =>
- this.GetWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), threadId.ToString());
+ var url = qub.Build();
+ var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, values: values, files: builder.Files);
- /// <summary>
- /// Deletes the webhook message async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="threadId">The thread_id.</param>
+ var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
- internal async Task DeleteWebhookMessageAsync(ulong webhookId, string webhookToken, string messageId, string threadId)
- {
- var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
-
- var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
- if (threadId != null)
- qub.AddParameter("thread_id", threadId);
- var url = qub.Build();
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
- }
+ ret.Discord = this.Discord;
- /// <summary>
- /// Deletes the webhook message async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="messageId">The message_id.</param>
+ foreach (var att in ret.AttachmentsInternal)
+ att.Discord = this.Discord;
- internal Task DeleteWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId) =>
- this.DeleteWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), null);
+ foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
+ {
+ file.Stream.Position = file.ResetPositionTo.Value;
+ }
- /// <summary>
- /// Deletes the webhook message async.
- /// </summary>
- /// <param name="webhookId">The webhook_id.</param>
- /// <param name="webhookToken">The webhook_token.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="threadId">The thread_id.</param>
+ return ret;
+ }
+ else
+ {
+ pld.Attachments = builder.Attachments;
- internal Task DeleteWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId, ulong threadId) =>
- this.DeleteWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), threadId.ToString());
- #endregion
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
- #region Reactions
- /// <summary>
- /// Creates the reaction async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="emoji">The emoji.</param>
+ var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
+ if (threadId != null)
+ qub.AddParameter("thread_id", threadId);
- internal Task CreateReactionAsync(ulong channelId, ulong messageId, string emoji)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji{Endpoints.ME}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
+ var url = qub.Build();
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
- }
+ var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
- /// <summary>
- /// Deletes the own reaction async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="emoji">The emoji.</param>
+ ret.Discord = this.Discord;
- internal Task DeleteOwnReactionAsync(ulong channelId, ulong messageId, string emoji)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji{Endpoints.ME}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
+ foreach (var att in ret.AttachmentsInternal)
+ att.Discord = this.Discord;
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
- }
+ return ret;
+ }
+ }
- /// <summary>
- /// Deletes the user reaction async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="userId">The user_id.</param>
- /// <param name="emoji">The emoji.</param>
- /// <param name="reason">The reason.</param>
+ /// <summary>
+ /// Edits the webhook message async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="builder">The builder.</param>
+ /// <param name="threadId">The thread_id.</param>
+
+ internal Task<DiscordMessage> EditWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId, DiscordWebhookBuilder builder, ulong threadId) =>
+ this.EditWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), builder, threadId.ToString());
+
+ /// <summary>
+ /// Gets the webhook message async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="threadId">The thread_id.</param>
+
+ internal async Task<DiscordMessage> GetWebhookMessageAsync(ulong webhookId, string webhookToken, string messageId, string threadId)
+ {
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
- internal Task DeleteUserReactionAsync(ulong channelId, ulong messageId, ulong userId, string emoji, string reason)
- {
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
+ if (threadId != null)
+ qub.AddParameter("thread_id", threadId);
+ var url = qub.Build();
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId, emoji, user_id = userId }, out var path);
+ var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
+ ret.Discord = this.Discord;
+ return ret;
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
- }
+ /// <summary>
+ /// Gets the webhook message async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="messageId">The message_id.</param>
+
+ internal Task<DiscordMessage> GetWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId) =>
+ this.GetWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), null);
+
+ /// <summary>
+ /// Gets the webhook message async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="threadId">The thread_id.</param>
+
+ internal Task<DiscordMessage> GetWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId, ulong threadId) =>
+ this.GetWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), threadId.ToString());
+
+ /// <summary>
+ /// Deletes the webhook message async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="threadId">The thread_id.</param>
+
+ internal async Task DeleteWebhookMessageAsync(ulong webhookId, string webhookToken, string messageId, string threadId)
+ {
+ var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token{Endpoints.MESSAGES}/:message_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {webhook_id = webhookId, webhook_token = webhookToken, message_id = messageId }, out var path);
- /// <summary>
- /// Gets the reactions async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="emoji">The emoji.</param>
- /// <param name="afterId">The after_id.</param>
- /// <param name="limit">The limit.</param>
+ var qub = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration);
+ if (threadId != null)
+ qub.AddParameter("thread_id", threadId);
+ var url = qub.Build();
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ }
- internal async Task<IReadOnlyList<DiscordUser>> GetReactionsAsync(ulong channelId, ulong messageId, string emoji, ulong? afterId = null, int limit = 25)
- {
- var urlParams = new Dictionary<string, string>();
- if (afterId.HasValue)
- urlParams["after"] = afterId.Value.ToString(CultureInfo.InvariantCulture);
+ /// <summary>
+ /// Deletes the webhook message async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="messageId">The message_id.</param>
+
+ internal Task DeleteWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId) =>
+ this.DeleteWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), null);
+
+ /// <summary>
+ /// Deletes the webhook message async.
+ /// </summary>
+ /// <param name="webhookId">The webhook_id.</param>
+ /// <param name="webhookToken">The webhook_token.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="threadId">The thread_id.</param>
+
+ internal Task DeleteWebhookMessageAsync(ulong webhookId, string webhookToken, ulong messageId, ulong threadId) =>
+ this.DeleteWebhookMessageAsync(webhookId, webhookToken, messageId.ToString(), threadId.ToString());
+ #endregion
+
+ #region Reactions
+ /// <summary>
+ /// Creates the reaction async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="emoji">The emoji.</param>
+
+ internal Task CreateReactionAsync(ulong channelId, ulong messageId, string emoji)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji{Endpoints.ME}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
+ }
- urlParams["limit"] = limit.ToString(CultureInfo.InvariantCulture);
+ /// <summary>
+ /// Deletes the own reaction async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="emoji">The emoji.</param>
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
+ internal Task DeleteOwnReactionAsync(ulong channelId, ulong messageId, string emoji)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji{Endpoints.ME}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
+ }
- var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ /// <summary>
+ /// Deletes the user reaction async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="userId">The user_id.</param>
+ /// <param name="emoji">The emoji.</param>
+ /// <param name="reason">The reason.</param>
- var reactersRaw = JsonConvert.DeserializeObject<IEnumerable<TransportUser>>(res.Response);
- var reacters = new List<DiscordUser>();
- foreach (var xr in reactersRaw)
+ internal Task DeleteUserReactionAsync(ulong channelId, ulong messageId, ulong userId, string emoji, string reason)
{
- var usr = new DiscordUser(xr) { Discord = this.Discord };
- usr = this.Discord.UserCache.AddOrUpdate(xr.Id, usr, (id, old) =>
- {
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- reacters.Add(usr);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId, emoji, user_id = userId }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
}
- return new ReadOnlyCollection<DiscordUser>(new List<DiscordUser>(reacters));
- }
+ /// <summary>
+ /// Gets the reactions async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="emoji">The emoji.</param>
+ /// <param name="afterId">The after_id.</param>
+ /// <param name="limit">The limit.</param>
- /// <summary>
- /// Deletes the all reactions async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="reason">The reason.</param>
+ internal async Task<IReadOnlyList<DiscordUser>> GetReactionsAsync(ulong channelId, ulong messageId, string emoji, ulong? afterId = null, int limit = 25)
+ {
+ var urlParams = new Dictionary<string, string>();
+ if (afterId.HasValue)
+ urlParams["after"] = afterId.Value.ToString(CultureInfo.InvariantCulture);
- internal Task DeleteAllReactionsAsync(ulong channelId, ulong messageId, string reason)
- {
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ urlParams["limit"] = limit.ToString(CultureInfo.InvariantCulture);
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId }, out var path);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
- }
+ var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- /// <summary>
- /// Deletes the reactions emoji async.
- /// </summary>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="messageId">The message_id.</param>
- /// <param name="emoji">The emoji.</param>
+ var reactersRaw = JsonConvert.DeserializeObject<IEnumerable<TransportUser>>(res.Response);
+ var reacters = new List<DiscordUser>();
+ foreach (var xr in reactersRaw)
+ {
+ var usr = new DiscordUser(xr) { Discord = this.Discord };
+ usr = this.Discord.UserCache.AddOrUpdate(xr.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ return old;
+ });
- internal Task DeleteReactionsEmojiAsync(ulong channelId, ulong messageId, string emoji)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
+ reacters.Add(usr);
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
- }
- #endregion
+ return new ReadOnlyCollection<DiscordUser>(new List<DiscordUser>(reacters));
+ }
- #region Threads
+ /// <summary>
+ /// Deletes the all reactions async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="reason">The reason.</param>
- /// <summary>
- /// Creates the thread.
- /// </summary>
- /// <param name="channelId">The channel id to create the thread in.</param>
- /// <param name="messageId">The optional message id to create the thread from.</param>
- /// <param name="name">The name of the thread.</param>
- /// <param name="autoArchiveDuration">The auto_archive_duration for the thread.</param>
- /// <param name="type">Can be either <see cref="ChannelType.PublicThread"/> or <see cref="ChannelType.PrivateThread"/>.</param>
- /// <param name="rateLimitPerUser">The rate limit per user.</param>
- /// <param name="reason">The reason.</param>
- internal async Task<DiscordThreadChannel> CreateThreadAsync(ulong channelId, ulong? messageId, string name,
- ThreadAutoArchiveDuration autoArchiveDuration, ChannelType type, int? rateLimitPerUser, string reason)
- {
- var pld = new RestThreadChannelCreatePayload
+ internal Task DeleteAllReactionsAsync(ulong channelId, ulong messageId, string reason)
{
- Name = name,
- AutoArchiveDuration = autoArchiveDuration,
- PerUserRateLimit = rateLimitPerUser,
- Type = type
- };
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId }, out var path);
- var route = $"{Endpoints.CHANNELS}/:channel_id";
- if (messageId is not null)
- route += $"{Endpoints.MESSAGES}/:message_id";
- route += Endpoints.THREADS;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
+ }
- object param = messageId is null
- ? new {channel_id = channelId}
- : new {channel_id = channelId, message_id = messageId};
+ /// <summary>
+ /// Deletes the reactions emoji async.
+ /// </summary>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="messageId">The message_id.</param>
+ /// <param name="emoji">The emoji.</param>
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, param, out var path);
+ internal Task DeleteReactionsEmojiAsync(ulong channelId, ulong messageId, string emoji)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.MESSAGES}/:message_id{Endpoints.REACTIONS}/:emoji";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, message_id = messageId, emoji }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld));
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, ratelimitWaitOverride: this.Discord.Configuration.UseRelativeRatelimit ? null : (double?)0.26);
+ }
+ #endregion
+
+ #region Threads
+
+ /// <summary>
+ /// Creates the thread.
+ /// </summary>
+ /// <param name="channelId">The channel id to create the thread in.</param>
+ /// <param name="messageId">The optional message id to create the thread from.</param>
+ /// <param name="name">The name of the thread.</param>
+ /// <param name="autoArchiveDuration">The auto_archive_duration for the thread.</param>
+ /// <param name="type">Can be either <see cref="ChannelType.PublicThread"/> or <see cref="ChannelType.PrivateThread"/>.</param>
+ /// <param name="rateLimitPerUser">The rate limit per user.</param>
+ /// <param name="reason">The reason.</param>
+ internal async Task<DiscordThreadChannel> CreateThreadAsync(ulong channelId, ulong? messageId, string name,
+ ThreadAutoArchiveDuration autoArchiveDuration, ChannelType type, int? rateLimitPerUser, string reason)
+ {
+ var pld = new RestThreadChannelCreatePayload
+ {
+ Name = name,
+ AutoArchiveDuration = autoArchiveDuration,
+ PerUserRateLimit = rateLimitPerUser,
+ Type = type
+ };
- var threadChannel = JsonConvert.DeserializeObject<DiscordThreadChannel>(res.Response);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- threadChannel.Discord = this.Discord;
+ var route = $"{Endpoints.CHANNELS}/:channel_id";
+ if (messageId is not null)
+ route += $"{Endpoints.MESSAGES}/:message_id";
+ route += Endpoints.THREADS;
- return threadChannel;
- }
+ object param = messageId is null
+ ? new {channel_id = channelId}
+ : new {channel_id = channelId, message_id = messageId};
- /// <summary>
- /// Gets the thread.
- /// </summary>
- /// <param name="threadId">The thread id.</param>
- internal async Task<DiscordThreadChannel> GetThreadAsync(ulong threadId)
- {
- var route = $"{Endpoints.CHANNELS}/:thread_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {thread_id = threadId }, out var path);
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, param, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld));
- var ret = JsonConvert.DeserializeObject<DiscordThreadChannel>(res.Response);
- ret.Discord = this.Discord;
+ var threadChannel = JsonConvert.DeserializeObject<DiscordThreadChannel>(res.Response);
- return ret;
- }
+ threadChannel.Discord = this.Discord;
- /// <summary>
- /// Joins the thread.
- /// </summary>
- /// <param name="channelId">The channel id.</param>
- internal async Task JoinThreadAsync(ulong channelId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}{Endpoints.ME}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId }, out var path);
+ return threadChannel;
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route);
- }
+ /// <summary>
+ /// Gets the thread.
+ /// </summary>
+ /// <param name="threadId">The thread id.</param>
+ internal async Task<DiscordThreadChannel> GetThreadAsync(ulong threadId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:thread_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {thread_id = threadId }, out var path);
- /// <summary>
- /// Leaves the thread.
- /// </summary>
- /// <param name="channelId">The channel id.</param>
- internal async Task LeaveThreadAsync(ulong channelId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}{Endpoints.ME}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
- }
+ var ret = JsonConvert.DeserializeObject<DiscordThreadChannel>(res.Response);
+ ret.Discord = this.Discord;
- /// <summary>
- /// Adds a thread member.
- /// </summary>
- /// <param name="channelId">The channel id to add the member to.</param>
- /// <param name="userId">The user id to add.</param>
- internal async Task AddThreadMemberAsync(ulong channelId, ulong userId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, user_id = userId }, out var path);
+ return ret;
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route);
- }
+ /// <summary>
+ /// Joins the thread.
+ /// </summary>
+ /// <param name="channelId">The channel id.</param>
+ internal async Task JoinThreadAsync(ulong channelId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}{Endpoints.ME}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId }, out var path);
- /// <summary>
- /// Gets a thread member.
- /// </summary>
- /// <param name="channelId">The channel id to get the member from.</param>
- /// <param name="userId">The user id to get.</param>
- internal async Task<DiscordThreadChannelMember> GetThreadMemberAsync(ulong channelId, ulong userId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, user_id = userId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route);
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ /// <summary>
+ /// Leaves the thread.
+ /// </summary>
+ /// <param name="channelId">The channel id.</param>
+ internal async Task LeaveThreadAsync(ulong channelId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}{Endpoints.ME}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId }, out var path);
- var threadMember = JsonConvert.DeserializeObject<DiscordThreadChannelMember>(res.Response);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ }
- return threadMember;
- }
+ /// <summary>
+ /// Adds a thread member.
+ /// </summary>
+ /// <param name="channelId">The channel id to add the member to.</param>
+ /// <param name="userId">The user id to add.</param>
+ internal async Task AddThreadMemberAsync(ulong channelId, ulong userId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {channel_id = channelId, user_id = userId }, out var path);
- /// <summary>
- /// Removes a thread member.
- /// </summary>
- /// <param name="channelId">The channel id to remove the member from.</param>
- /// <param name="userId">The user id to remove.</param>
- internal async Task RemoveThreadMemberAsync(ulong channelId, ulong userId)
- {
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}/:user_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, user_id = userId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route);
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
- }
+ /// <summary>
+ /// Gets a thread member.
+ /// </summary>
+ /// <param name="channelId">The channel id to get the member from.</param>
+ /// <param name="userId">The user id to get.</param>
+ internal async Task<DiscordThreadChannelMember> GetThreadMemberAsync(ulong channelId, ulong userId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId, user_id = userId }, out var path);
- /// <summary>
- /// Gets the thread members.
- /// </summary>
- /// <param name="threadId">The thread id.</param>
- internal async Task<IReadOnlyList<DiscordThreadChannelMember>> GetThreadMembersAsync(ulong threadId)
- {
- var route = $"{Endpoints.CHANNELS}/:thread_id{Endpoints.THREAD_MEMBERS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {thread_id = threadId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var threadMember = JsonConvert.DeserializeObject<DiscordThreadChannelMember>(res.Response);
- var threadMembersRaw = JsonConvert.DeserializeObject<List<DiscordThreadChannelMember>>(res.Response);
+ return threadMember;
+ }
- return new ReadOnlyCollection<DiscordThreadChannelMember>(threadMembersRaw);
- }
+ /// <summary>
+ /// Removes a thread member.
+ /// </summary>
+ /// <param name="channelId">The channel id to remove the member from.</param>
+ /// <param name="userId">The user id to remove.</param>
+ internal async Task RemoveThreadMemberAsync(ulong channelId, ulong userId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREAD_MEMBERS}/:user_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {channel_id = channelId, user_id = userId }, out var path);
- /// <summary>
- /// Gets the active threads in a guild.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- internal async Task<DiscordThreadResult> GetActiveThreadsAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.THREADS}{Endpoints.THREAD_ACTIVE}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ /// <summary>
+ /// Gets the thread members.
+ /// </summary>
+ /// <param name="threadId">The thread id.</param>
+ internal async Task<IReadOnlyList<DiscordThreadChannelMember>> GetThreadMembersAsync(ulong threadId)
+ {
+ var route = $"{Endpoints.CHANNELS}/:thread_id{Endpoints.THREAD_MEMBERS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {thread_id = threadId }, out var path);
- var threadReturn = JsonConvert.DeserializeObject<DiscordThreadResult>(res.Response);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- return threadReturn;
- }
+ var threadMembersRaw = JsonConvert.DeserializeObject<List<DiscordThreadChannelMember>>(res.Response);
- /// <summary>
- /// Gets the joined private archived threads in a channel.
- /// </summary>
- /// <param name="channelId">The channel id.</param>
- /// <param name="before">Get threads before snowflake.</param>
- /// <param name="limit">Limit the results.</param>
- internal async Task<DiscordThreadResult> GetJoinedPrivateArchivedThreadsAsync(ulong channelId, ulong? before, int? limit)
- {
- var urlParams = new Dictionary<string, string>();
- if (before != null)
- urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
- if (limit != null && limit > 0)
- urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
+ return new ReadOnlyCollection<DiscordThreadChannelMember>(threadMembersRaw);
+ }
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.USERS}{Endpoints.ME}{Endpoints.THREADS}{Endpoints.THREAD_ARCHIVED}{Endpoints.THREAD_PRIVATE}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
+ /// <summary>
+ /// Gets the active threads in a guild.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ internal async Task<DiscordThreadResult> GetActiveThreadsAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.THREADS}{Endpoints.THREAD_ACTIVE}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var threadReturn = JsonConvert.DeserializeObject<DiscordThreadResult>(res.Response);
+ var threadReturn = JsonConvert.DeserializeObject<DiscordThreadResult>(res.Response);
- return threadReturn;
- }
+ return threadReturn;
+ }
- /// <summary>
- /// Gets the public archived threads in a channel.
- /// </summary>
- /// <param name="channelId">The channel id.</param>
- /// <param name="before">Get threads before snowflake.</param>
- /// <param name="limit">Limit the results.</param>
- internal async Task<DiscordThreadResult> GetPublicArchivedThreadsAsync(ulong channelId, ulong? before, int? limit)
- {
- var urlParams = new Dictionary<string, string>();
- if (before != null)
- urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
- if (limit != null && limit > 0)
- urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
+ /// <summary>
+ /// Gets the joined private archived threads in a channel.
+ /// </summary>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="before">Get threads before snowflake.</param>
+ /// <param name="limit">Limit the results.</param>
+ internal async Task<DiscordThreadResult> GetJoinedPrivateArchivedThreadsAsync(ulong channelId, ulong? before, int? limit)
+ {
+ var urlParams = new Dictionary<string, string>();
+ if (before != null)
+ urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
+ if (limit != null && limit > 0)
+ urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREADS}{Endpoints.THREAD_ARCHIVED}{Endpoints.THREAD_PUBLIC}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.USERS}{Endpoints.ME}{Endpoints.THREADS}{Endpoints.THREAD_ARCHIVED}{Endpoints.THREAD_PRIVATE}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var threadReturn = JsonConvert.DeserializeObject<DiscordThreadResult>(res.Response);
+ var threadReturn = JsonConvert.DeserializeObject<DiscordThreadResult>(res.Response);
- return threadReturn;
- }
+ return threadReturn;
+ }
- /// <summary>
- /// Gets the private archived threads in a channel.
- /// </summary>
- /// <param name="channelId">The channel id.</param>
- /// <param name="before">Get threads before snowflake.</param>
- /// <param name="limit">Limit the results.</param>
- internal async Task<DiscordThreadResult> GetPrivateArchivedThreadsAsync(ulong channelId, ulong? before, int? limit)
- {
- var urlParams = new Dictionary<string, string>();
- if (before != null)
- urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
- if (limit != null && limit > 0)
- urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
+ /// <summary>
+ /// Gets the public archived threads in a channel.
+ /// </summary>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="before">Get threads before snowflake.</param>
+ /// <param name="limit">Limit the results.</param>
+ internal async Task<DiscordThreadResult> GetPublicArchivedThreadsAsync(ulong channelId, ulong? before, int? limit)
+ {
+ var urlParams = new Dictionary<string, string>();
+ if (before != null)
+ urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
+ if (limit != null && limit > 0)
+ urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
- var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREADS}{Endpoints.THREAD_ARCHIVED}{Endpoints.THREAD_PRIVATE}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREADS}{Endpoints.THREAD_ARCHIVED}{Endpoints.THREAD_PUBLIC}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
- var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var threadReturn = JsonConvert.DeserializeObject<DiscordThreadResult>(res.Response);
+ var threadReturn = JsonConvert.DeserializeObject<DiscordThreadResult>(res.Response);
- return threadReturn;
- }
+ return threadReturn;
+ }
- /// <summary>
- /// Modifies a thread.
- /// </summary>
- /// <param name="threadId">The thread to modify.</param>
- /// <param name="name">The new name.</param>
- /// <param name="locked">The new locked state.</param>
- /// <param name="archived">The new archived state.</param>
- /// <param name="perUserRateLimit">The new per user rate limit.</param>
- /// <param name="autoArchiveDuration">The new auto archive duration.</param>
- /// <param name="invitable">The new user invitable state.</param>
- /// <param name="reason">The reason for the modification.</param>
- internal Task ModifyThreadAsync(ulong threadId, string name, Optional<bool?> locked, Optional<bool?> archived, Optional<int?> perUserRateLimit, Optional<ThreadAutoArchiveDuration?> autoArchiveDuration, Optional<bool?> invitable, string reason)
- {
- var pld = new RestThreadChannelModifyPayload
+ /// <summary>
+ /// Gets the private archived threads in a channel.
+ /// </summary>
+ /// <param name="channelId">The channel id.</param>
+ /// <param name="before">Get threads before snowflake.</param>
+ /// <param name="limit">Limit the results.</param>
+ internal async Task<DiscordThreadResult> GetPrivateArchivedThreadsAsync(ulong channelId, ulong? before, int? limit)
{
- Name = name,
- Archived = archived,
- AutoArchiveDuration = autoArchiveDuration,
- Locked = locked,
- PerUserRateLimit = perUserRateLimit,
- Invitable = invitable
- };
+ var urlParams = new Dictionary<string, string>();
+ if (before != null)
+ urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
+ if (limit != null && limit > 0)
+ urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ var route = $"{Endpoints.CHANNELS}/:channel_id{Endpoints.THREADS}{Endpoints.THREAD_ARCHIVED}{Endpoints.THREAD_PRIVATE}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {channel_id = channelId }, out var path);
- var route = $"{Endpoints.CHANNELS}/:thread_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {thread_id = threadId }, out var path);
+ var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
- }
+ var threadReturn = JsonConvert.DeserializeObject<DiscordThreadResult>(res.Response);
- /// <summary>
- /// Deletes a thread.
- /// </summary>
- /// <param name="threadId">The thread to delete.</param>
- /// <param name="reason">The reason for deletion.</param>
- internal Task DeleteThreadAsync(ulong threadId, string reason)
- {
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ return threadReturn;
+ }
- var route = $"{Endpoints.CHANNELS}/:thread_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {thread_id = threadId }, out var path);
+ /// <summary>
+ /// Modifies a thread.
+ /// </summary>
+ /// <param name="threadId">The thread to modify.</param>
+ /// <param name="name">The new name.</param>
+ /// <param name="locked">The new locked state.</param>
+ /// <param name="archived">The new archived state.</param>
+ /// <param name="perUserRateLimit">The new per user rate limit.</param>
+ /// <param name="autoArchiveDuration">The new auto archive duration.</param>
+ /// <param name="invitable">The new user invitable state.</param>
+ /// <param name="reason">The reason for the modification.</param>
+ internal Task ModifyThreadAsync(ulong threadId, string name, Optional<bool?> locked, Optional<bool?> archived, Optional<int?> perUserRateLimit, Optional<ThreadAutoArchiveDuration?> autoArchiveDuration, Optional<bool?> invitable, string reason)
+ {
+ var pld = new RestThreadChannelModifyPayload
+ {
+ Name = name,
+ Archived = archived,
+ AutoArchiveDuration = autoArchiveDuration,
+ Locked = locked,
+ PerUserRateLimit = perUserRateLimit,
+ Invitable = invitable
+ };
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- #endregion
+ var route = $"{Endpoints.CHANNELS}/:thread_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {thread_id = threadId }, out var path);
- #region Emoji
- /// <summary>
- /// Gets the guild emojis async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
+ }
- internal async Task<IReadOnlyList<DiscordGuildEmoji>> GetGuildEmojisAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
+ /// <summary>
+ /// Deletes a thread.
+ /// </summary>
+ /// <param name="threadId">The thread to delete.</param>
+ /// <param name="reason">The reason for deletion.</param>
+ internal Task DeleteThreadAsync(ulong threadId, string reason)
+ {
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
+
+ var route = $"{Endpoints.CHANNELS}/:thread_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {thread_id = threadId }, out var path);
+
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ #endregion
- var emojisRaw = JsonConvert.DeserializeObject<IEnumerable<JObject>>(res.Response);
+ #region Emoji
+ /// <summary>
+ /// Gets the guild emojis async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
- this.Discord.Guilds.TryGetValue(guildId, out var gld);
- var users = new Dictionary<ulong, DiscordUser>();
- var emojis = new List<DiscordGuildEmoji>();
- foreach (var rawEmoji in emojisRaw)
+ internal async Task<IReadOnlyList<DiscordGuildEmoji>> GetGuildEmojisAsync(ulong guildId)
{
- var xge = rawEmoji.ToObject<DiscordGuildEmoji>();
- xge.Guild = gld;
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
- var xtu = rawEmoji["user"]?.ToObject<TransportUser>();
- if (xtu != null)
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+
+ var emojisRaw = JsonConvert.DeserializeObject<IEnumerable<JObject>>(res.Response);
+
+ this.Discord.Guilds.TryGetValue(guildId, out var gld);
+ var users = new Dictionary<ulong, DiscordUser>();
+ var emojis = new List<DiscordGuildEmoji>();
+ foreach (var rawEmoji in emojisRaw)
{
- if (!users.ContainsKey(xtu.Id))
+ var xge = rawEmoji.ToObject<DiscordGuildEmoji>();
+ xge.Guild = gld;
+
+ var xtu = rawEmoji["user"]?.ToObject<TransportUser>();
+ if (xtu != null)
{
- var user = gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu);
- users[user.Id] = user;
+ if (!users.ContainsKey(xtu.Id))
+ {
+ var user = gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu);
+ users[user.Id] = user;
+ }
+
+ xge.User = users[xtu.Id];
}
- xge.User = users[xtu.Id];
+ emojis.Add(xge);
}
- emojis.Add(xge);
+ return new ReadOnlyCollection<DiscordGuildEmoji>(emojis);
}
- return new ReadOnlyCollection<DiscordGuildEmoji>(emojis);
- }
-
- /// <summary>
- /// Gets the guild emoji async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="emojiId">The emoji_id.</param>
+ /// <summary>
+ /// Gets the guild emoji async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="emojiId">The emoji_id.</param>
- internal async Task<DiscordGuildEmoji> GetGuildEmojiAsync(ulong guildId, ulong emojiId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}/:emoji_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, emoji_id = emojiId }, out var path);
+ internal async Task<DiscordGuildEmoji> GetGuildEmojiAsync(ulong guildId, ulong emojiId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}/:emoji_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, emoji_id = emojiId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- this.Discord.Guilds.TryGetValue(guildId, out var gld);
+ this.Discord.Guilds.TryGetValue(guildId, out var gld);
- var emojiRaw = JObject.Parse(res.Response);
- var emoji = emojiRaw.ToObject<DiscordGuildEmoji>();
- emoji.Guild = gld;
+ var emojiRaw = JObject.Parse(res.Response);
+ var emoji = emojiRaw.ToObject<DiscordGuildEmoji>();
+ emoji.Guild = gld;
- var xtu = emojiRaw["user"]?.ToObject<TransportUser>();
- if (xtu != null)
- emoji.User = gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu);
+ var xtu = emojiRaw["user"]?.ToObject<TransportUser>();
+ if (xtu != null)
+ emoji.User = gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu);
- return emoji;
- }
+ return emoji;
+ }
- /// <summary>
- /// Creates the guild emoji async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="imageb64">The imageb64.</param>
- /// <param name="roles">The roles.</param>
- /// <param name="reason">The reason.</param>
+ /// <summary>
+ /// Creates the guild emoji async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="imageb64">The imageb64.</param>
+ /// <param name="roles">The roles.</param>
+ /// <param name="reason">The reason.</param>
- internal async Task<DiscordGuildEmoji> CreateGuildEmojiAsync(ulong guildId, string name, string imageb64, IEnumerable<ulong> roles, string reason)
- {
- var pld = new RestGuildEmojiCreatePayload
+ internal async Task<DiscordGuildEmoji> CreateGuildEmojiAsync(ulong guildId, string name, string imageb64, IEnumerable<ulong> roles, string reason)
{
- Name = name,
- ImageB64 = imageb64,
- Roles = roles?.ToArray()
- };
+ var pld = new RestGuildEmojiCreatePayload
+ {
+ Name = name,
+ ImageB64 = imageb64,
+ Roles = roles?.ToArray()
+ };
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- this.Discord.Guilds.TryGetValue(guildId, out var gld);
+ this.Discord.Guilds.TryGetValue(guildId, out var gld);
- var emojiRaw = JObject.Parse(res.Response);
- var emoji = emojiRaw.ToObject<DiscordGuildEmoji>();
- emoji.Guild = gld;
+ var emojiRaw = JObject.Parse(res.Response);
+ var emoji = emojiRaw.ToObject<DiscordGuildEmoji>();
+ emoji.Guild = gld;
- var xtu = emojiRaw["user"]?.ToObject<TransportUser>();
- emoji.User = xtu != null
- ? gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu)
- : this.Discord.CurrentUser;
+ var xtu = emojiRaw["user"]?.ToObject<TransportUser>();
+ emoji.User = xtu != null
+ ? gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu)
+ : this.Discord.CurrentUser;
- return emoji;
- }
+ return emoji;
+ }
- /// <summary>
- /// Modifies the guild emoji async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="emojiId">The emoji_id.</param>
- /// <param name="name">The name.</param>
- /// <param name="roles">The roles.</param>
- /// <param name="reason">The reason.</param>
+ /// <summary>
+ /// Modifies the guild emoji async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="emojiId">The emoji_id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="roles">The roles.</param>
+ /// <param name="reason">The reason.</param>
- internal async Task<DiscordGuildEmoji> ModifyGuildEmojiAsync(ulong guildId, ulong emojiId, string name, IEnumerable<ulong> roles, string reason)
- {
- var pld = new RestGuildEmojiModifyPayload
+ internal async Task<DiscordGuildEmoji> ModifyGuildEmojiAsync(ulong guildId, ulong emojiId, string name, IEnumerable<ulong> roles, string reason)
{
- Name = name,
- Roles = roles?.ToArray()
- };
+ var pld = new RestGuildEmojiModifyPayload
+ {
+ Name = name,
+ Roles = roles?.ToArray()
+ };
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}/:emoji_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, emoji_id = emojiId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}/:emoji_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, emoji_id = emojiId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
- this.Discord.Guilds.TryGetValue(guildId, out var gld);
+ this.Discord.Guilds.TryGetValue(guildId, out var gld);
- var emojiRaw = JObject.Parse(res.Response);
- var emoji = emojiRaw.ToObject<DiscordGuildEmoji>();
- emoji.Guild = gld;
+ var emojiRaw = JObject.Parse(res.Response);
+ var emoji = emojiRaw.ToObject<DiscordGuildEmoji>();
+ emoji.Guild = gld;
- var xtu = emojiRaw["user"]?.ToObject<TransportUser>();
- if (xtu != null)
- emoji.User = gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu);
+ var xtu = emojiRaw["user"]?.ToObject<TransportUser>();
+ if (xtu != null)
+ emoji.User = gld != null && gld.Members.TryGetValue(xtu.Id, out var member) ? member : new DiscordUser(xtu);
- return emoji;
- }
+ return emoji;
+ }
- /// <summary>
- /// Deletes the guild emoji async.
- /// </summary>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="emojiId">The emoji_id.</param>
- /// <param name="reason">The reason.</param>
+ /// <summary>
+ /// Deletes the guild emoji async.
+ /// </summary>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="emojiId">The emoji_id.</param>
+ /// <param name="reason">The reason.</param>
- internal Task DeleteGuildEmojiAsync(ulong guildId, ulong emojiId, string reason)
- {
- var headers = new Dictionary<string, string>();
- if (!string.IsNullOrWhiteSpace(reason))
- headers[REASON_HEADER_NAME] = reason;
+ internal Task DeleteGuildEmojiAsync(ulong guildId, ulong emojiId, string reason)
+ {
+ var headers = new Dictionary<string, string>();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers[REASON_HEADER_NAME] = reason;
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}/:emoji_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, emoji_id = emojiId }, out var path);
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.EMOJIS}/:emoji_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, emoji_id = emojiId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
- #endregion
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
+ #endregion
- #region Stickers
+ #region Stickers
- /// <summary>
- /// Gets a sticker.
- /// </summary>
- /// <param name="stickerId">The sticker id.</param>
- internal async Task<DiscordSticker> GetStickerAsync(ulong stickerId)
- {
- var route = $"{Endpoints.STICKERS}/:sticker_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {sticker_id = stickerId}, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ /// <summary>
+ /// Gets a sticker.
+ /// </summary>
+ /// <param name="stickerId">The sticker id.</param>
+ internal async Task<DiscordSticker> GetStickerAsync(ulong stickerId)
+ {
+ var route = $"{Endpoints.STICKERS}/:sticker_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {sticker_id = stickerId}, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var ret = JObject.Parse(res.Response).ToDiscordObject<DiscordSticker>();
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var ret = JObject.Parse(res.Response).ToDiscordObject<DiscordSticker>();
- ret.Discord = this.Discord;
- return ret;
- }
+ ret.Discord = this.Discord;
+ return ret;
+ }
- /// <summary>
- /// Gets the sticker packs.
- /// </summary>
- internal async Task<IReadOnlyList<DiscordStickerPack>> GetStickerPacksAsync()
- {
- var route = $"{Endpoints.STICKERPACKS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
+ /// <summary>
+ /// Gets the sticker packs.
+ /// </summary>
+ internal async Task<IReadOnlyList<DiscordStickerPack>> GetStickerPacksAsync()
+ {
+ var route = $"{Endpoints.STICKERPACKS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var json = JObject.Parse(res.Response)["sticker_packs"] as JArray;
- var ret = json.ToDiscordObject<DiscordStickerPack[]>();
+ var json = JObject.Parse(res.Response)["sticker_packs"] as JArray;
+ var ret = json.ToDiscordObject<DiscordStickerPack[]>();
- return ret.ToList();
- }
+ return ret.ToList();
+ }
- /// <summary>
- /// Gets the guild stickers.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- internal async Task<IReadOnlyList<DiscordSticker>> GetGuildStickersAsync(ulong guildId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId}, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ /// <summary>
+ /// Gets the guild stickers.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ internal async Task<IReadOnlyList<DiscordSticker>> GetGuildStickersAsync(ulong guildId)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId}, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var json = JArray.Parse(res.Response);
+ var ret = json.ToDiscordObject<DiscordSticker[]>();
+
+ for (var i = 0; i < ret.Length; i++)
+ {
+ var stkr = ret[i];
+ stkr.Discord = this.Discord;
+
+ if (json[i]["user"] is JObject obj) // Null = Missing stickers perm //
+ {
+ var tsr = obj.ToDiscordObject<TransportUser>();
+ var usr = new DiscordUser(tsr) {Discord = this.Discord};
+ usr = this.Discord.UserCache.AddOrUpdate(tsr.Id, usr, (id, old) =>
+ {
+ old.Username = usr.Username;
+ old.Discriminator = usr.Discriminator;
+ old.AvatarHash = usr.AvatarHash;
+ return old;
+ });
+ stkr.User = usr;
+ }
+ }
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- var json = JArray.Parse(res.Response);
- var ret = json.ToDiscordObject<DiscordSticker[]>();
+ return ret.ToList();
+ }
- for (var i = 0; i < ret.Length; i++)
+ /// <summary>
+ /// Gets a guild sticker.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="stickerId">The sticker id.</param>
+ internal async Task<DiscordSticker> GetGuildStickerAsync(ulong guildId, ulong stickerId)
{
- var stkr = ret[i];
- stkr.Discord = this.Discord;
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}/:sticker_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, sticker_id = stickerId}, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- if (json[i]["user"] is JObject obj) // Null = Missing stickers perm //
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+
+ var json = JObject.Parse(res.Response);
+ var ret = json.ToDiscordObject<DiscordSticker>();
+ if (json["user"] is not null) // Null = Missing stickers perm //
{
- var tsr = obj.ToDiscordObject<TransportUser>();
+ var tsr = json["user"].ToDiscordObject<TransportUser>();
var usr = new DiscordUser(tsr) {Discord = this.Discord};
usr = this.Discord.UserCache.AddOrUpdate(tsr.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
return old;
});
- stkr.User = usr;
+ ret.User = usr;
}
+ ret.Discord = this.Discord;
+ return ret;
}
- return ret.ToList();
- }
+ /// <summary>
+ /// Creates the guild sticker.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="tags">The tags.</param>
+ /// <param name="file">The file.</param>
+ /// <param name="reason">The reason.</param>
+ internal async Task<DiscordSticker> CreateGuildStickerAsync(ulong guildId, string name, string description, string tags, DiscordMessageFile file, string reason)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId}, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- /// <summary>
- /// Gets a guild sticker.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- /// <param name="stickerId">The sticker id.</param>
- internal async Task<DiscordSticker> GetGuildStickerAsync(ulong guildId, ulong stickerId)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}/:sticker_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, sticker_id = stickerId}, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
+ var res = await this.DoStickerMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, file, name, tags, description);
- var json = JObject.Parse(res.Response);
- var ret = json.ToDiscordObject<DiscordSticker>();
- if (json["user"] is not null) // Null = Missing stickers perm //
- {
- var tsr = json["user"].ToDiscordObject<TransportUser>();
- var usr = new DiscordUser(tsr) {Discord = this.Discord};
- usr = this.Discord.UserCache.AddOrUpdate(tsr.Id, usr, (id, old) =>
- {
- old.Username = usr.Username;
- old.Discriminator = usr.Discriminator;
- old.AvatarHash = usr.AvatarHash;
- return old;
- });
- ret.User = usr;
- }
- ret.Discord = this.Discord;
- return ret;
- }
+ var ret = JObject.Parse(res.Response).ToDiscordObject<DiscordSticker>();
- /// <summary>
- /// Creates the guild sticker.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- /// <param name="name">The name.</param>
- /// <param name="description">The description.</param>
- /// <param name="tags">The tags.</param>
- /// <param name="file">The file.</param>
- /// <param name="reason">The reason.</param>
- internal async Task<DiscordSticker> CreateGuildStickerAsync(ulong guildId, string name, string description, string tags, DiscordMessageFile file, string reason)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId}, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ ret.Discord = this.Discord;
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ return ret;
+ }
- var res = await this.DoStickerMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, file, name, tags, description);
+ /// <summary>
+ /// Modifies the guild sticker.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="stickerId">The sticker id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="tags">The tags.</param>
+ /// <param name="reason">The reason.</param>
+ internal async Task<DiscordSticker> ModifyGuildStickerAsync(ulong guildId, ulong stickerId, Optional<string> name, Optional<string> description, Optional<string> tags, string reason)
+ {
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}/:sticker_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, sticker_id = stickerId}, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var ret = JObject.Parse(res.Response).ToDiscordObject<DiscordSticker>();
+ var pld = new RestStickerModifyPayload()
+ {
+ Name = name,
+ Description = description,
+ Tags = tags
+ };
- ret.Discord = this.Discord;
+ var values = new Dictionary<string, string>
+ {
+ ["payload_json"] = DiscordJson.SerializeObject(pld)
+ };
- return ret;
- }
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route);
+ var ret = JObject.Parse(res.Response).ToDiscordObject<DiscordSticker>();
+ ret.Discord = this.Discord;
- /// <summary>
- /// Modifies the guild sticker.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- /// <param name="stickerId">The sticker id.</param>
- /// <param name="name">The name.</param>
- /// <param name="description">The description.</param>
- /// <param name="tags">The tags.</param>
- /// <param name="reason">The reason.</param>
- internal async Task<DiscordSticker> ModifyGuildStickerAsync(ulong guildId, ulong stickerId, Optional<string> name, Optional<string> description, Optional<string> tags, string reason)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}/:sticker_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, sticker_id = stickerId}, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
+ return null;
+ }
- var pld = new RestStickerModifyPayload()
+ /// <summary>
+ /// Deletes the guild sticker async.
+ /// </summary>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="stickerId">The sticker id.</param>
+ /// <param name="reason">The reason.</param>
+ internal async Task DeleteGuildStickerAsync(ulong guildId, ulong stickerId, string reason)
{
- Name = name,
- Description = description,
- Tags = tags
- };
+ var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}/:sticker_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, sticker_id = stickerId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var headers = Utilities.GetBaseHeaders();
+ if (!string.IsNullOrWhiteSpace(reason))
+ headers.Add(REASON_HEADER_NAME, reason);
- var values = new Dictionary<string, string>
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
+ }
+ #endregion
+
+ #region Application Commands
+
+ /// <summary>
+ /// Gets the global application commands.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="withLocalizations">Whether to get the full localization dict.</param>
+ internal async Task<IReadOnlyList<DiscordApplicationCommand>> GetGlobalApplicationCommandsAsync(ulong applicationId, bool withLocalizations = false)
{
- ["payload_json"] = DiscordJson.SerializeObject(pld)
- };
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId }, out var path);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route);
- var ret = JObject.Parse(res.Response).ToDiscordObject<DiscordSticker>();
- ret.Discord = this.Discord;
+ var querydict = new Dictionary<string, string>
+ {
+ ["with_localizations"] = withLocalizations.ToString().ToLower()
+ };
+ var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- return null;
- }
+ var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationCommand>>(res.Response);
+ foreach (var app in ret)
+ app.Discord = this.Discord;
+ return ret.ToList();
+ }
- /// <summary>
- /// Deletes the guild sticker async.
- /// </summary>
- /// <param name="guildId">The guild id.</param>
- /// <param name="stickerId">The sticker id.</param>
- /// <param name="reason">The reason.</param>
- internal async Task DeleteGuildStickerAsync(ulong guildId, ulong stickerId, string reason)
- {
- var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.STICKERS}/:sticker_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, sticker_id = stickerId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var headers = Utilities.GetBaseHeaders();
- if (!string.IsNullOrWhiteSpace(reason))
- headers.Add(REASON_HEADER_NAME, reason);
-
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
- }
- #endregion
+ /// <summary>
+ /// Bulk overwrites the global application commands.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="commands">The commands.</param>
+ internal async Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteGlobalApplicationCommandsAsync(ulong applicationId, IEnumerable<DiscordApplicationCommand> commands)
+ {
+ var pld = new List<RestApplicationCommandCreatePayload>();
+ foreach (var command in commands)
+ {
+ pld.Add(new RestApplicationCommandCreatePayload
+ {
+ Type = command.Type,
+ Name = command.Name,
+ Description = command.Description,
+ Options = command.Options,
+ NameLocalizations = command.NameLocalizations?.GetKeyValuePairs(),
+ DescriptionLocalizations = command.DescriptionLocalizations?.GetKeyValuePairs(),
+ DefaultMemberPermission = command.DefaultMemberPermissions,
+ DmPermission = command.DmPermission
+ });
+ }
- #region Application Commands
+ this.Discord.Logger.LogDebug(DiscordJson.SerializeObject(pld));
- /// <summary>
- /// Gets the global application commands.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="withLocalizations">Whether to get the full localization dict.</param>
- internal async Task<IReadOnlyList<DiscordApplicationCommand>> GetGlobalApplicationCommandsAsync(ulong applicationId, bool withLocalizations = false)
- {
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId }, out var path);
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {application_id = applicationId }, out var path);
- var querydict = new Dictionary<string, string>
- {
- ["with_localizations"] = withLocalizations.ToString().ToLower()
- };
- var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
- var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationCommand>>(res.Response);
- foreach (var app in ret)
- app.Discord = this.Discord;
- return ret.ToList();
- }
+ var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationCommand>>(res.Response);
+ foreach (var app in ret)
+ app.Discord = this.Discord;
+ return ret.ToList();
+ }
- /// <summary>
- /// Bulk overwrites the global application commands.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="commands">The commands.</param>
- internal async Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteGlobalApplicationCommandsAsync(ulong applicationId, IEnumerable<DiscordApplicationCommand> commands)
- {
- var pld = new List<RestApplicationCommandCreatePayload>();
- foreach (var command in commands)
+ /// <summary>
+ /// Creates a global application command.
+ /// </summary>
+ /// <param name="applicationId">The applicationid.</param>
+ /// <param name="command">The command.</param>
+ internal async Task<DiscordApplicationCommand> CreateGlobalApplicationCommandAsync(ulong applicationId, DiscordApplicationCommand command)
{
- pld.Add(new RestApplicationCommandCreatePayload
+ var pld = new RestApplicationCommandCreatePayload
{
Type = command.Type,
Name = command.Name,
Description = command.Description,
Options = command.Options,
- NameLocalizations = command.NameLocalizations?.GetKeyValuePairs(),
- DescriptionLocalizations = command.DescriptionLocalizations?.GetKeyValuePairs(),
+ NameLocalizations = command.NameLocalizations.GetKeyValuePairs(),
+ DescriptionLocalizations = command.DescriptionLocalizations.GetKeyValuePairs(),
DefaultMemberPermission = command.DefaultMemberPermissions,
DmPermission = command.DmPermission
- });
- }
+ };
- this.Discord.Logger.LogDebug(DiscordJson.SerializeObject(pld));
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {application_id = applicationId }, out var path);
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {application_id = applicationId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
+ var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
+ ret.Discord = this.Discord;
- var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationCommand>>(res.Response);
- foreach (var app in ret)
- app.Discord = this.Discord;
- return ret.ToList();
- }
+ return ret;
+ }
- /// <summary>
- /// Creates a global application command.
- /// </summary>
- /// <param name="applicationId">The applicationid.</param>
- /// <param name="command">The command.</param>
- internal async Task<DiscordApplicationCommand> CreateGlobalApplicationCommandAsync(ulong applicationId, DiscordApplicationCommand command)
- {
- var pld = new RestApplicationCommandCreatePayload
+ /// <summary>
+ /// Gets a global application command.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="commandId">The command id.</param>
+ internal async Task<DiscordApplicationCommand> GetGlobalApplicationCommandAsync(ulong applicationId, ulong commandId)
{
- Type = command.Type,
- Name = command.Name,
- Description = command.Description,
- Options = command.Options,
- NameLocalizations = command.NameLocalizations.GetKeyValuePairs(),
- DescriptionLocalizations = command.DescriptionLocalizations.GetKeyValuePairs(),
- DefaultMemberPermission = command.DefaultMemberPermissions,
- DmPermission = command.DmPermission
- };
-
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {application_id = applicationId }, out var path);
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}/:command_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, command_id = commandId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
- ret.Discord = this.Discord;
+ var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
+ ret.Discord = this.Discord;
- return ret;
- }
+ return ret;
+ }
- /// <summary>
- /// Gets a global application command.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="commandId">The command id.</param>
- internal async Task<DiscordApplicationCommand> GetGlobalApplicationCommandAsync(ulong applicationId, ulong commandId)
- {
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}/:command_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, command_id = commandId }, out var path);
+ /// <summary>
+ /// Edits a global application command.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="commandId">The command id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="options">The options.</param>
+ /// <param name="nameLocalization">The localizations of the name.</param>
+ /// <param name="descriptionLocalization">The localizations of the description.</param>
+ /// <param name="defaultMemberPermission">The default member permissions.</param>
+ /// <param name="dmPermission">The dm permission.</param>
+ internal async Task<DiscordApplicationCommand> EditGlobalApplicationCommandAsync(ulong applicationId, ulong commandId, Optional<string> name, Optional<string> description, Optional<IReadOnlyCollection<DiscordApplicationCommandOption>> options,
+ Optional<DiscordApplicationCommandLocalization> nameLocalization, Optional<DiscordApplicationCommandLocalization> descriptionLocalization,
+ Optional<Permissions> defaultMemberPermission, Optional<bool> dmPermission)
+ {
+ var pld = new RestApplicationCommandEditPayload
+ {
+ Name = name,
+ Description = description,
+ Options = options,
+ DefaultMemberPermission = defaultMemberPermission,
+ DmPermission = dmPermission,
+ NameLocalizations = nameLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
+ DescriptionLocalizations = descriptionLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
+ };
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}/:command_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {application_id = applicationId, command_id = commandId }, out var path);
- var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
- ret.Discord = this.Discord;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
- return ret;
- }
+ var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
+ ret.Discord = this.Discord;
- /// <summary>
- /// Edits a global application command.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="commandId">The command id.</param>
- /// <param name="name">The name.</param>
- /// <param name="description">The description.</param>
- /// <param name="options">The options.</param>
- /// <param name="nameLocalization">The localizations of the name.</param>
- /// <param name="descriptionLocalization">The localizations of the description.</param>
- /// <param name="defaultMemberPermission">The default member permissions.</param>
- /// <param name="dmPermission">The dm permission.</param>
- internal async Task<DiscordApplicationCommand> EditGlobalApplicationCommandAsync(ulong applicationId, ulong commandId, Optional<string> name, Optional<string> description, Optional<IReadOnlyCollection<DiscordApplicationCommandOption>> options,
- Optional<DiscordApplicationCommandLocalization> nameLocalization, Optional<DiscordApplicationCommandLocalization> descriptionLocalization,
- Optional<Permissions> defaultMemberPermission, Optional<bool> dmPermission)
- {
- var pld = new RestApplicationCommandEditPayload
- {
- Name = name,
- Description = description,
- Options = options,
- DefaultMemberPermission = defaultMemberPermission,
- DmPermission = dmPermission,
- NameLocalizations = nameLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
- DescriptionLocalizations = descriptionLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
- };
+ return ret;
+ }
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}/:command_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {application_id = applicationId, command_id = commandId }, out var path);
+ /// <summary>
+ /// Deletes a global application command.
+ /// </summary>
+ /// <param name="applicationId">The application_id.</param>
+ /// <param name="commandId">The command_id.</param>
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
+ internal async Task DeleteGlobalApplicationCommandAsync(ulong applicationId, ulong commandId)
+ {
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}/:command_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {application_id = applicationId, command_id = commandId }, out var path);
- var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
- ret.Discord = this.Discord;
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ }
- return ret;
- }
+ /// <summary>
+ /// Gets the guild application commands.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="withLocalizations">Whether to get the full localization dict.</param>
+ internal async Task<IReadOnlyList<DiscordApplicationCommand>> GetGuildApplicationCommandsAsync(ulong applicationId, ulong guildId, bool withLocalizations = false)
+ {
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId }, out var path);
- /// <summary>
- /// Deletes a global application command.
- /// </summary>
- /// <param name="applicationId">The application_id.</param>
- /// <param name="commandId">The command_id.</param>
+ var querydict = new Dictionary<string, string>
+ {
+ ["with_localizations"] = withLocalizations.ToString().ToLower()
+ };
+ var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- internal async Task DeleteGlobalApplicationCommandAsync(ulong applicationId, ulong commandId)
- {
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.COMMANDS}/:command_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {application_id = applicationId, command_id = commandId }, out var path);
+ var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationCommand>>(res.Response);
+ foreach (var app in ret)
+ app.Discord = this.Discord;
+ return ret.ToList();
+ }
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
- }
+ /// <summary>
+ /// Bulk overwrites the guild application commands.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="commands">The commands.</param>
+ internal async Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteGuildApplicationCommandsAsync(ulong applicationId, ulong guildId, IEnumerable<DiscordApplicationCommand> commands)
+ {
+ var pld = new List<RestApplicationCommandCreatePayload>();
+ foreach (var command in commands)
+ {
+ pld.Add(new RestApplicationCommandCreatePayload
+ {
+ Type = command.Type,
+ Name = command.Name,
+ Description = command.Description,
+ Options = command.Options,
+ NameLocalizations = command.NameLocalizations?.GetKeyValuePairs(),
+ DescriptionLocalizations = command.DescriptionLocalizations?.GetKeyValuePairs(),
+ DefaultMemberPermission = command.DefaultMemberPermissions,
+ DmPermission = command.DmPermission
+ });
+ }
+ this.Discord.Logger.LogDebug(DiscordJson.SerializeObject(pld));
- /// <summary>
- /// Gets the guild application commands.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="withLocalizations">Whether to get the full localization dict.</param>
- internal async Task<IReadOnlyList<DiscordApplicationCommand>> GetGuildApplicationCommandsAsync(ulong applicationId, ulong guildId, bool withLocalizations = false)
- {
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId }, out var path);
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {application_id = applicationId, guild_id = guildId }, out var path);
- var querydict = new Dictionary<string, string>
- {
- ["with_localizations"] = withLocalizations.ToString().ToLower()
- };
- var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
- var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationCommand>>(res.Response);
- foreach (var app in ret)
- app.Discord = this.Discord;
- return ret.ToList();
- }
+ var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationCommand>>(res.Response);
+ foreach (var app in ret)
+ app.Discord = this.Discord;
+ return ret.ToList();
+ }
- /// <summary>
- /// Bulk overwrites the guild application commands.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="commands">The commands.</param>
- internal async Task<IReadOnlyList<DiscordApplicationCommand>> BulkOverwriteGuildApplicationCommandsAsync(ulong applicationId, ulong guildId, IEnumerable<DiscordApplicationCommand> commands)
- {
- var pld = new List<RestApplicationCommandCreatePayload>();
- foreach (var command in commands)
+ /// <summary>
+ /// Creates a guild application command.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="command">The command.</param>
+ internal async Task<DiscordApplicationCommand> CreateGuildApplicationCommandAsync(ulong applicationId, ulong guildId, DiscordApplicationCommand command)
{
- pld.Add(new RestApplicationCommandCreatePayload
+ var pld = new RestApplicationCommandCreatePayload
{
Type = command.Type,
Name = command.Name,
Description = command.Description,
Options = command.Options,
- NameLocalizations = command.NameLocalizations?.GetKeyValuePairs(),
- DescriptionLocalizations = command.DescriptionLocalizations?.GetKeyValuePairs(),
+ NameLocalizations = command.NameLocalizations.GetKeyValuePairs(),
+ DescriptionLocalizations = command.DescriptionLocalizations.GetKeyValuePairs(),
DefaultMemberPermission = command.DefaultMemberPermissions,
DmPermission = command.DmPermission
- });
- }
- this.Discord.Logger.LogDebug(DiscordJson.SerializeObject(pld));
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {application_id = applicationId, guild_id = guildId }, out var path);
+ };
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld));
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {application_id = applicationId, guild_id = guildId }, out var path);
- var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationCommand>>(res.Response);
- foreach (var app in ret)
- app.Discord = this.Discord;
- return ret.ToList();
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
- /// <summary>
- /// Creates a guild application command.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="command">The command.</param>
- internal async Task<DiscordApplicationCommand> CreateGuildApplicationCommandAsync(ulong applicationId, ulong guildId, DiscordApplicationCommand command)
- {
- var pld = new RestApplicationCommandCreatePayload
+ var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
+ ret.Discord = this.Discord;
+
+ return ret;
+ }
+
+ /// <summary>
+ /// Gets a guild application command.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="commandId">The command id.</param>
+ internal async Task<DiscordApplicationCommand> GetGuildApplicationCommandAsync(ulong applicationId, ulong guildId, ulong commandId)
{
- Type = command.Type,
- Name = command.Name,
- Description = command.Description,
- Options = command.Options,
- NameLocalizations = command.NameLocalizations.GetKeyValuePairs(),
- DescriptionLocalizations = command.DescriptionLocalizations.GetKeyValuePairs(),
- DefaultMemberPermission = command.DefaultMemberPermissions,
- DmPermission = command.DmPermission
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
- };
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {application_id = applicationId, guild_id = guildId }, out var path);
+ var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
+ ret.Discord = this.Discord;
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
+ return ret;
+ }
- var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
- ret.Discord = this.Discord;
+ /// <summary>
+ /// Edits a guild application command.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="commandId">The command id.</param>
+ /// <param name="name">The name.</param>
+ /// <param name="description">The description.</param>
+ /// <param name="options">The options.</param>
+ /// <param name="nameLocalization">The localizations of the name.</param>
+ /// <param name="descriptionLocalization">The localizations of the description.</param>
+ /// <param name="defaultMemberPermission">The default member permissions.</param>
+ /// <param name="dmPermission">The dm permission.</param>
+ internal async Task<DiscordApplicationCommand> EditGuildApplicationCommandAsync(ulong applicationId, ulong guildId, ulong commandId, Optional<string> name, Optional<string> description, Optional<IReadOnlyCollection<DiscordApplicationCommandOption>> options,
+ Optional<DiscordApplicationCommandLocalization> nameLocalization, Optional<DiscordApplicationCommandLocalization> descriptionLocalization,
+ Optional<Permissions> defaultMemberPermission, Optional<bool> dmPermission)
+ {
+ var pld = new RestApplicationCommandEditPayload
+ {
+ Name = name,
+ Description = description,
+ Options = options,
+ DefaultMemberPermission = defaultMemberPermission,
+ DmPermission = dmPermission,
+ NameLocalizations = nameLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
+ DescriptionLocalizations = descriptionLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
+ };
- return ret;
- }
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
- /// <summary>
- /// Gets a guild application command.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="commandId">The command id.</param>
- internal async Task<DiscordApplicationCommand> GetGuildApplicationCommandAsync(ulong applicationId, ulong guildId, ulong commandId)
- {
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
+ ret.Discord = this.Discord;
- var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
- ret.Discord = this.Discord;
+ return ret;
+ }
- return ret;
- }
+ /// <summary>
+ /// Deletes a guild application command.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="guildId">The guild id.</param>
+ /// <param name="commandId">The command id.</param>
+ internal async Task DeleteGuildApplicationCommandAsync(ulong applicationId, ulong guildId, ulong commandId)
+ {
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
- /// <summary>
- /// Edits a guild application command.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="commandId">The command id.</param>
- /// <param name="name">The name.</param>
- /// <param name="description">The description.</param>
- /// <param name="options">The options.</param>
- /// <param name="nameLocalization">The localizations of the name.</param>
- /// <param name="descriptionLocalization">The localizations of the description.</param>
- /// <param name="defaultMemberPermission">The default member permissions.</param>
- /// <param name="dmPermission">The dm permission.</param>
- internal async Task<DiscordApplicationCommand> EditGuildApplicationCommandAsync(ulong applicationId, ulong guildId, ulong commandId, Optional<string> name, Optional<string> description, Optional<IReadOnlyCollection<DiscordApplicationCommandOption>> options,
- Optional<DiscordApplicationCommandLocalization> nameLocalization, Optional<DiscordApplicationCommandLocalization> descriptionLocalization,
- Optional<Permissions> defaultMemberPermission, Optional<bool> dmPermission)
- {
- var pld = new RestApplicationCommandEditPayload
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
+ }
+
+ #region Permissions 2.1
+
+ /// <summary>
+ /// Gets the guild application command permissions.
+ /// </summary>
+ /// <param name="applicationId">The target application id.</param>
+ /// <param name="guildId">The target guild id.</param>
+ internal async Task<IReadOnlyList<DiscordGuildApplicationCommandPermission>> GetGuildApplicationCommandPermissionsAsync(ulong applicationId, ulong guildId)
{
- Name = name,
- Description = description,
- Options = options,
- DefaultMemberPermission = defaultMemberPermission,
- DmPermission = dmPermission,
- NameLocalizations = nameLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
- DescriptionLocalizations = descriptionLocalization.Map(l => l.GetKeyValuePairs()).ValueOrDefault(),
- };
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}{Endpoints.PERMISSIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId }, out var path);
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
+ var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordGuildApplicationCommandPermission>>(res.Response);
- var ret = JsonConvert.DeserializeObject<DiscordApplicationCommand>(res.Response);
- ret.Discord = this.Discord;
+ foreach (var app in ret)
+ app.Discord = this.Discord;
- return ret;
- }
+ return ret.ToList();
+ }
- /// <summary>
- /// Deletes a guild application command.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="guildId">The guild id.</param>
- /// <param name="commandId">The command id.</param>
- internal async Task DeleteGuildApplicationCommandAsync(ulong applicationId, ulong guildId, ulong commandId)
- {
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
+ /// <summary>
+ /// Gets a guild application command permission.
+ /// </summary>
+ /// <param name="applicationId">The target application id.</param>
+ /// <param name="guildId">The target guild id.</param>
+ /// <param name="commandId">The target command id.</param>
+ internal async Task<DiscordGuildApplicationCommandPermission> GetGuildApplicationCommandPermissionAsync(ulong applicationId, ulong guildId, ulong commandId)
+ {
+ var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id{Endpoints.PERMISSIONS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
- }
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- #region Permissions 2.1
+ var ret = JsonConvert.DeserializeObject<DiscordGuildApplicationCommandPermission>(res.Response);
- /// <summary>
- /// Gets the guild application command permissions.
- /// </summary>
- /// <param name="applicationId">The target application id.</param>
- /// <param name="guildId">The target guild id.</param>
- internal async Task<IReadOnlyList<DiscordGuildApplicationCommandPermission>> GetGuildApplicationCommandPermissionsAsync(ulong applicationId, ulong guildId)
- {
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}{Endpoints.PERMISSIONS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId }, out var path);
+ ret.Discord = this.Discord;
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
+ return ret;
+ }
- var ret = JsonConvert.DeserializeObject<IEnumerable<DiscordGuildApplicationCommandPermission>>(res.Response);
+ #endregion
- foreach (var app in ret)
- app.Discord = this.Discord;
+ /// <summary>
+ /// Creates the interaction response.
+ /// </summary>
+ /// <param name="interactionId">The interaction id.</param>
+ /// <param name="interactionToken">The interaction token.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="builder">The builder.</param>
+ internal async Task CreateInteractionResponseAsync(ulong interactionId, string interactionToken, InteractionResponseType type, DiscordInteractionResponseBuilder builder)
+ {
+ if (builder?.Embeds != null)
+ foreach (var embed in builder.Embeds)
+ if (embed.Timestamp != null)
+ embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
- return ret.ToList();
- }
+ RestInteractionResponsePayload pld;
- /// <summary>
- /// Gets a guild application command permission.
- /// </summary>
- /// <param name="applicationId">The target application id.</param>
- /// <param name="guildId">The target guild id.</param>
- /// <param name="commandId">The target command id.</param>
- internal async Task<DiscordGuildApplicationCommandPermission> GetGuildApplicationCommandPermissionAsync(ulong applicationId, ulong guildId, ulong commandId)
- {
- var route = $"{Endpoints.APPLICATIONS}/:application_id{Endpoints.GUILDS}/:guild_id{Endpoints.COMMANDS}/:command_id{Endpoints.PERMISSIONS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId, guild_id = guildId, command_id = commandId }, out var path);
+ if (type != InteractionResponseType.AutoCompleteResult)
+ {
+ var data = builder != null ? new DiscordInteractionApplicationCommandCallbackData
+ {
+ Content = builder.Content ?? null,
+ Embeds = builder.Embeds ?? null,
+ IsTts = builder.IsTts,
+ Mentions = builder.Mentions ?? null,
+ Flags = builder.IsEphemeral ? MessageFlags.Ephemeral : null,
+ Components = builder.Components ?? null,
+ Choices = null
+ } : null;
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
- var ret = JsonConvert.DeserializeObject<DiscordGuildApplicationCommandPermission>(res.Response);
+ pld = new RestInteractionResponsePayload
+ {
+ Type = type,
+ Data = data
+ };
- ret.Discord = this.Discord;
- return ret;
- }
+ if (builder != null && builder.Files != null && builder.Files.Count > 0)
+ {
+ ulong fileId = 0;
+ List<DiscordAttachment> attachments = new();
+ foreach (var file in builder.Files)
+ {
+ DiscordAttachment att = new()
+ {
+ Id = fileId,
+ Discord = this.Discord,
+ Description = file.Description,
+ FileName = file.FileName,
+ FileSize = null
+ };
+ attachments.Add(att);
+ fileId++;
+ }
+ pld.Attachments = attachments;
+ pld.Data.Attachments = attachments;
+ }
+ }
+ else
+ {
+ pld = new RestInteractionResponsePayload
+ {
+ Type = type,
+ Data = new DiscordInteractionApplicationCommandCallbackData
+ {
+ Content = null,
+ Embeds = null,
+ IsTts = null,
+ Mentions = null,
+ Flags = null,
+ Components = null,
+ Choices = builder.Choices,
+ Attachments = null
+ },
+ Attachments = null
+ };
+ }
- #endregion
+ var values = new Dictionary<string, string>();
- /// <summary>
- /// Creates the interaction response.
- /// </summary>
- /// <param name="interactionId">The interaction id.</param>
- /// <param name="interactionToken">The interaction token.</param>
- /// <param name="type">The type.</param>
- /// <param name="builder">The builder.</param>
- internal async Task CreateInteractionResponseAsync(ulong interactionId, string interactionToken, InteractionResponseType type, DiscordInteractionResponseBuilder builder)
- {
- if (builder?.Embeds != null)
- foreach (var embed in builder.Embeds)
- if (embed.Timestamp != null)
- embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
+ if (builder != null)
+ if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.IsTts == true || builder.Mentions != null || builder.Files?.Count > 0)
+ values["payload_json"] = DiscordJson.SerializeObject(pld);
- RestInteractionResponsePayload pld;
+ var route = $"{Endpoints.INTERACTIONS}/:interaction_id/:interaction_token{Endpoints.CALLBACK}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {interaction_id = interactionId, interaction_token = interactionToken }, out var path);
- if (type != InteractionResponseType.AutoCompleteResult)
- {
- var data = builder != null ? new DiscordInteractionApplicationCommandCallbackData
+ var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "false").Build();
+ if (builder != null)
{
- Content = builder.Content ?? null,
- Embeds = builder.Embeds ?? null,
- IsTts = builder.IsTts,
- Mentions = builder.Mentions ?? null,
- Flags = builder.IsEphemeral ? MessageFlags.Ephemeral : null,
- Components = builder.Components ?? null,
- Choices = null
- } : null;
+ await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files);
+ foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
+ {
+ file.Stream.Position = file.ResetPositionTo.Value;
+ }
+ }
+ else
+ {
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
+ }
+ }
+
+ /// <summary>
+ /// Creates the interaction response.
+ /// </summary>
+ /// <param name="interactionId">The interaction id.</param>
+ /// <param name="interactionToken">The interaction token.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="builder">The builder.</param>
+ internal async Task CreateInteractionModalResponseAsync(ulong interactionId, string interactionToken, InteractionResponseType type, DiscordInteractionModalBuilder builder)
+ {
+ if (builder.ModalComponents.Any(mc => mc.Components.Any(c => c.Type != Enums.ComponentType.InputText)))
+ throw new NotSupportedException("Can't send any other type then Input Text as Modal Component.");
- pld = new RestInteractionResponsePayload
+ var pld = new RestInteractionModalResponsePayload
{
Type = type,
- Data = data
+ Data = new DiscordInteractionApplicationCommandModalCallbackData
+ {
+ Title = builder.Title,
+ CustomId = builder.CustomId,
+ ModalComponents = builder.ModalComponents
+ }
};
+ var values = new Dictionary<string, string>();
+
+ var route = $"{Endpoints.INTERACTIONS}/:interaction_id/:interaction_token{Endpoints.CALLBACK}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {interaction_id = interactionId, interaction_token = interactionToken }, out var path);
+
+ var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true").Build();
+ await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
+ }
+
+ /// <summary>
+ /// Gets the original interaction response.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="interactionToken">The interaction token.</param>
+ internal Task<DiscordMessage> GetOriginalInteractionResponseAsync(ulong applicationId, string interactionToken) =>
+ this.GetWebhookMessageAsync(applicationId, interactionToken, Endpoints.ORIGINAL, null);
+
+ /// <summary>
+ /// Edits the original interaction response.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="interactionToken">The interaction token.</param>
+ /// <param name="builder">The builder.</param>
+ internal Task<DiscordMessage> EditOriginalInteractionResponseAsync(ulong applicationId, string interactionToken, DiscordWebhookBuilder builder) =>
+ this.EditWebhookMessageAsync(applicationId, interactionToken, Endpoints.ORIGINAL, builder, null);
+
+ /// <summary>
+ /// Deletes the original interaction response.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="interactionToken">The interaction token.</param>
+ internal Task DeleteOriginalInteractionResponseAsync(ulong applicationId, string interactionToken) =>
+ this.DeleteWebhookMessageAsync(applicationId, interactionToken, Endpoints.ORIGINAL, null);
+
+ /// <summary>
+ /// Creates the followup message.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="interactionToken">The interaction token.</param>
+ /// <param name="builder">The builder.</param>
+ internal async Task<DiscordMessage> CreateFollowupMessageAsync(ulong applicationId, string interactionToken, DiscordFollowupMessageBuilder builder)
+ {
+ builder.Validate();
- if (builder != null && builder.Files != null && builder.Files.Count > 0)
+ if (builder.Embeds != null)
+ foreach (var embed in builder.Embeds)
+ if (embed.Timestamp != null)
+ embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
+
+ var values = new Dictionary<string, string>();
+ var pld = new RestFollowupMessageCreatePayload
+ {
+ Content = builder.Content,
+ IsTts = builder.IsTts,
+ Embeds = builder.Embeds,
+ Flags = builder.Flags,
+ Components = builder.Components
+ };
+
+
+ if (builder.Files != null && builder.Files.Count > 0)
{
ulong fileId = 0;
List<DiscordAttachment> attachments = new();
foreach (var file in builder.Files)
{
DiscordAttachment att = new()
{
Id = fileId,
Discord = this.Discord,
Description = file.Description,
FileName = file.FileName,
FileSize = null
};
attachments.Add(att);
fileId++;
}
pld.Attachments = attachments;
- pld.Data.Attachments = attachments;
}
- }
- else
- {
- pld = new RestInteractionResponsePayload
- {
- Type = type,
- Data = new DiscordInteractionApplicationCommandCallbackData
- {
- Content = null,
- Embeds = null,
- IsTts = null,
- Mentions = null,
- Flags = null,
- Components = null,
- Choices = builder.Choices,
- Attachments = null
- },
- Attachments = null
- };
- }
- var values = new Dictionary<string, string>();
+ if (builder.Mentions != null)
+ pld.Mentions = new DiscordMentions(builder.Mentions, builder.Mentions.Any());
- if (builder != null)
if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.IsTts == true || builder.Mentions != null || builder.Files?.Count > 0)
values["payload_json"] = DiscordJson.SerializeObject(pld);
- var route = $"{Endpoints.INTERACTIONS}/:interaction_id/:interaction_token{Endpoints.CALLBACK}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {interaction_id = interactionId, interaction_token = interactionToken }, out var path);
+ var route = $"{Endpoints.WEBHOOKS}/:application_id/:interaction_token";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {application_id = applicationId, interaction_token = interactionToken }, out var path);
- var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "false").Build();
- if (builder != null)
- {
- await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files);
+ var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true").Build();
+ var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
+ var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
- foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
+ foreach (var att in ret.AttachmentsInternal)
{
- file.Stream.Position = file.ResetPositionTo.Value;
+ att.Discord = this.Discord;
}
- }
- else
- {
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
- }
- }
-
- /// <summary>
- /// Creates the interaction response.
- /// </summary>
- /// <param name="interactionId">The interaction id.</param>
- /// <param name="interactionToken">The interaction token.</param>
- /// <param name="type">The type.</param>
- /// <param name="builder">The builder.</param>
- internal async Task CreateInteractionModalResponseAsync(ulong interactionId, string interactionToken, InteractionResponseType type, DiscordInteractionModalBuilder builder)
- {
- if (builder.ModalComponents.Any(mc => mc.Components.Any(c => c.Type != Enums.ComponentType.InputText)))
- throw new NotSupportedException("Can't send any other type then Input Text as Modal Component.");
- var pld = new RestInteractionModalResponsePayload
- {
- Type = type,
- Data = new DiscordInteractionApplicationCommandModalCallbackData
+ foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
{
- Title = builder.Title,
- CustomId = builder.CustomId,
- ModalComponents = builder.ModalComponents
+ file.Stream.Position = file.ResetPositionTo.Value;
}
- };
-
- var values = new Dictionary<string, string>();
-
- var route = $"{Endpoints.INTERACTIONS}/:interaction_id/:interaction_token{Endpoints.CALLBACK}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {interaction_id = interactionId, interaction_token = interactionToken }, out var path);
- var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true").Build();
- await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld));
- }
-
- /// <summary>
- /// Gets the original interaction response.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="interactionToken">The interaction token.</param>
- internal Task<DiscordMessage> GetOriginalInteractionResponseAsync(ulong applicationId, string interactionToken) =>
- this.GetWebhookMessageAsync(applicationId, interactionToken, Endpoints.ORIGINAL, null);
+ ret.Discord = this.Discord;
+ return ret;
+ }
- /// <summary>
- /// Edits the original interaction response.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="interactionToken">The interaction token.</param>
- /// <param name="builder">The builder.</param>
- internal Task<DiscordMessage> EditOriginalInteractionResponseAsync(ulong applicationId, string interactionToken, DiscordWebhookBuilder builder) =>
- this.EditWebhookMessageAsync(applicationId, interactionToken, Endpoints.ORIGINAL, builder, null);
+ /// <summary>
+ /// Gets the followup message.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="interactionToken">The interaction token.</param>
+ /// <param name="messageId">The message id.</param>
+ internal Task<DiscordMessage> GetFollowupMessageAsync(ulong applicationId, string interactionToken, ulong messageId) =>
+ this.GetWebhookMessageAsync(applicationId, interactionToken, messageId);
+
+ /// <summary>
+ /// Edits the followup message.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="interactionToken">The interaction token.</param>
+ /// <param name="messageId">The message id.</param>
+ /// <param name="builder">The builder.</param>
+ internal Task<DiscordMessage> EditFollowupMessageAsync(ulong applicationId, string interactionToken, ulong messageId, DiscordWebhookBuilder builder) =>
+ this.EditWebhookMessageAsync(applicationId, interactionToken, messageId.ToString(), builder, null);
+
+ /// <summary>
+ /// Deletes the followup message.
+ /// </summary>
+ /// <param name="applicationId">The application id.</param>
+ /// <param name="interactionToken">The interaction token.</param>
+ /// <param name="messageId">The message id.</param>
+ internal Task DeleteFollowupMessageAsync(ulong applicationId, string interactionToken, ulong messageId) =>
+ this.DeleteWebhookMessageAsync(applicationId, interactionToken, messageId);
+ #endregion
+
+ #region Misc
+ /// <summary>
+ /// Gets the current application info async.
+ /// </summary>
+
+ internal Task<TransportApplication> GetCurrentApplicationInfoAsync()
+ => this.GetApplicationInfoAsync("@me");
+
+ /// <summary>
+ /// Gets the application info async.
+ /// </summary>
+ /// <param name="applicationId">The application_id.</param>
+
+ internal Task<TransportApplication> GetApplicationInfoAsync(ulong applicationId)
+ => this.GetApplicationInfoAsync(applicationId.ToString(CultureInfo.InvariantCulture));
+
+ /// <summary>
+ /// Gets the application info async.
+ /// </summary>
+ /// <param name="applicationId">The application_id.</param>
+
+ private async Task<TransportApplication> GetApplicationInfoAsync(string applicationId)
+ {
+ var route = $"{Endpoints.OAUTH2}{Endpoints.APPLICATIONS}/:application_id";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId }, out var path);
- /// <summary>
- /// Deletes the original interaction response.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="interactionToken">The interaction token.</param>
- internal Task DeleteOriginalInteractionResponseAsync(ulong applicationId, string interactionToken) =>
- this.DeleteWebhookMessageAsync(applicationId, interactionToken, Endpoints.ORIGINAL, null);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- /// <summary>
- /// Creates the followup message.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="interactionToken">The interaction token.</param>
- /// <param name="builder">The builder.</param>
- internal async Task<DiscordMessage> CreateFollowupMessageAsync(ulong applicationId, string interactionToken, DiscordFollowupMessageBuilder builder)
- {
- builder.Validate();
+ return JsonConvert.DeserializeObject<TransportApplication>(res.Response);
+ }
- if (builder.Embeds != null)
- foreach (var embed in builder.Embeds)
- if (embed.Timestamp != null)
- embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
+ /// <summary>
+ /// Gets the application assets async.
+ /// </summary>
+ /// <param name="application">The application.</param>
- var values = new Dictionary<string, string>();
- var pld = new RestFollowupMessageCreatePayload
+ internal async Task<IReadOnlyList<DiscordApplicationAsset>> GetApplicationAssetsAsync(DiscordApplication application)
{
- Content = builder.Content,
- IsTts = builder.IsTts,
- Embeds = builder.Embeds,
- Flags = builder.Flags,
- Components = builder.Components
- };
+ var route = $"{Endpoints.OAUTH2}{Endpoints.APPLICATIONS}/:application_id{Endpoints.ASSETS}";
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { application_id = application.Id }, out var path);
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
- if (builder.Files != null && builder.Files.Count > 0)
- {
- ulong fileId = 0;
- List<DiscordAttachment> attachments = new();
- foreach (var file in builder.Files)
+ var assets = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationAsset>>(res.Response);
+ foreach (var asset in assets)
{
- DiscordAttachment att = new()
- {
- Id = fileId,
- Discord = this.Discord,
- Description = file.Description,
- FileName = file.FileName,
- FileSize = null
- };
- attachments.Add(att);
- fileId++;
+ asset.Discord = application.Discord;
+ asset.Application = application;
}
- pld.Attachments = attachments;
- }
- if (builder.Mentions != null)
- pld.Mentions = new DiscordMentions(builder.Mentions, builder.Mentions.Any());
-
- if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.IsTts == true || builder.Mentions != null || builder.Files?.Count > 0)
- values["payload_json"] = DiscordJson.SerializeObject(pld);
-
- var route = $"{Endpoints.WEBHOOKS}/:application_id/:interaction_token";
- var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {application_id = applicationId, interaction_token = interactionToken }, out var path);
-
- var url = Utilities.GetApiUriBuilderFor(path, this.Discord.Configuration).AddParameter("wait", "true").Build();
- var res = await this.DoMultipartAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, values: values, files: builder.Files).ConfigureAwait(false);
- var ret = JsonConvert.DeserializeObject<DiscordMessage>(res.Response);
-
- foreach (var att in ret.AttachmentsInternal)
- {
- att.Discord = this.Discord;
- }
-
- foreach (var file in builder.Files.Where(x => x.ResetPositionTo.HasValue))
- {
- file.Stream.Position = file.ResetPositionTo.Value;
+ return new ReadOnlyCollection<DiscordApplicationAsset>(new List<DiscordApplicationAsset>(assets));
}
- ret.Discord = this.Discord;
- return ret;
- }
-
- /// <summary>
- /// Gets the followup message.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="interactionToken">The interaction token.</param>
- /// <param name="messageId">The message id.</param>
- internal Task<DiscordMessage> GetFollowupMessageAsync(ulong applicationId, string interactionToken, ulong messageId) =>
- this.GetWebhookMessageAsync(applicationId, interactionToken, messageId);
-
- /// <summary>
- /// Edits the followup message.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="interactionToken">The interaction token.</param>
- /// <param name="messageId">The message id.</param>
- /// <param name="builder">The builder.</param>
- internal Task<DiscordMessage> EditFollowupMessageAsync(ulong applicationId, string interactionToken, ulong messageId, DiscordWebhookBuilder builder) =>
- this.EditWebhookMessageAsync(applicationId, interactionToken, messageId.ToString(), builder, null);
-
- /// <summary>
- /// Deletes the followup message.
- /// </summary>
- /// <param name="applicationId">The application id.</param>
- /// <param name="interactionToken">The interaction token.</param>
- /// <param name="messageId">The message id.</param>
- internal Task DeleteFollowupMessageAsync(ulong applicationId, string interactionToken, ulong messageId) =>
- this.DeleteWebhookMessageAsync(applicationId, interactionToken, messageId);
- #endregion
-
- #region Misc
- /// <summary>
- /// Gets the current application info async.
- /// </summary>
-
- internal Task<TransportApplication> GetCurrentApplicationInfoAsync()
- => this.GetApplicationInfoAsync("@me");
-
- /// <summary>
- /// Gets the application info async.
- /// </summary>
- /// <param name="applicationId">The application_id.</param>
+ /// <summary>
+ /// Gets the gateway info async.
+ /// </summary>
- internal Task<TransportApplication> GetApplicationInfoAsync(ulong applicationId)
- => this.GetApplicationInfoAsync(applicationId.ToString(CultureInfo.InvariantCulture));
-
- /// <summary>
- /// Gets the application info async.
- /// </summary>
- /// <param name="applicationId">The application_id.</param>
-
- private async Task<TransportApplication> GetApplicationInfoAsync(string applicationId)
- {
- var route = $"{Endpoints.OAUTH2}{Endpoints.APPLICATIONS}/:application_id";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {application_id = applicationId }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
-
- return JsonConvert.DeserializeObject<TransportApplication>(res.Response);
- }
-
- /// <summary>
- /// Gets the application assets async.
- /// </summary>
- /// <param name="application">The application.</param>
-
- internal async Task<IReadOnlyList<DiscordApplicationAsset>> GetApplicationAssetsAsync(DiscordApplication application)
- {
- var route = $"{Endpoints.OAUTH2}{Endpoints.APPLICATIONS}/:application_id{Endpoints.ASSETS}";
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { application_id = application.Id }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
-
- var assets = JsonConvert.DeserializeObject<IEnumerable<DiscordApplicationAsset>>(res.Response);
- foreach (var asset in assets)
+ internal async Task<GatewayInfo> GetGatewayInfoAsync()
{
- asset.Discord = application.Discord;
- asset.Application = application;
- }
-
- return new ReadOnlyCollection<DiscordApplicationAsset>(new List<DiscordApplicationAsset>(assets));
- }
+ var headers = Utilities.GetBaseHeaders();
+ var route = Endpoints.GATEWAY;
+ if (this.Discord.Configuration.TokenType == TokenType.Bot)
+ route += Endpoints.BOT;
+ var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
- /// <summary>
- /// Gets the gateway info async.
- /// </summary>
+ var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
+ var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route, headers).ConfigureAwait(false);
- internal async Task<GatewayInfo> GetGatewayInfoAsync()
- {
- var headers = Utilities.GetBaseHeaders();
- var route = Endpoints.GATEWAY;
- if (this.Discord.Configuration.TokenType == TokenType.Bot)
- route += Endpoints.BOT;
- var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { }, out var path);
-
- var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
- var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route, headers).ConfigureAwait(false);
-
- var info = JObject.Parse(res.Response).ToObject<GatewayInfo>();
- info.SessionBucket.ResetAfter = DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(info.SessionBucket.ResetAfterInternal);
- return info;
+ var info = JObject.Parse(res.Response).ToObject<GatewayInfo>();
+ info.SessionBucket.ResetAfter = DateTimeOffset.UtcNow + TimeSpan.FromMilliseconds(info.SessionBucket.ResetAfterInternal);
+ return info;
+ }
+ #endregion
}
- #endregion
}
diff --git a/DisCatSharp/Net/Rest/Endpoints.cs b/DisCatSharp/Net/Rest/Endpoints.cs
index 07fca1a51..b09db4e22 100644
--- a/DisCatSharp/Net/Rest/Endpoints.cs
+++ b/DisCatSharp/Net/Rest/Endpoints.cs
@@ -1,476 +1,478 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// The discord endpoints.
-/// </summary>
-public static class Endpoints
+namespace DisCatSharp.Net
{
- /// <summary>
- /// The base discord api uri.
- /// </summary>
- public const string BASE_URI = "https://discord.com/api/v";
-
- /// <summary>
- /// The base discord canary api uri.
- /// </summary>
- public const string CANARY_URI = "https://canary.discord.com/api/v";
-
- /// <summary>
- /// The base discord ptb api uri.
- /// </summary>
- public const string PTB_URI = "https://ptb.discord.com/api/v";
-
- /// <summary>
- /// The oauth2 endpoint.
- /// </summary>
- public const string OAUTH2 = "/oauth2";
-
- /// <summary>
- /// The oauth2 authorize endpoint.
- /// </summary>
- public const string AUTHORIZE = "/authorize";
-
- /// <summary>
- /// The applications endpoint.
- /// </summary>
- public const string APPLICATIONS = "/applications";
-
- /// <summary>
- /// The message reactions endpoint.
- /// </summary>
- public const string REACTIONS = "/reactions";
-
- /// <summary>
- /// The self (@me) endpoint.
- /// </summary>
- public const string ME = "/@me";
-
- /// <summary>
- /// The @original endpoint.
- /// </summary>
- public const string ORIGINAL = "@original";
-
- /// <summary>
- /// The permissions endpoint.
- /// </summary>
- public const string PERMISSIONS = "/permissions";
-
- /// <summary>
- /// The recipients endpoint.
- /// </summary>
- public const string RECIPIENTS = "/recipients";
-
- /// <summary>
- /// The bulk-delete endpoint.
- /// </summary>
- public const string BULK_DELETE = "/bulk-delete";
-
- /// <summary>
- /// The integrations endpoint.
- /// </summary>
- public const string INTEGRATIONS = "/integrations";
-
- /// <summary>
- /// The sync endpoint.
- /// </summary>
- public const string SYNC = "/sync";
-
- /// <summary>
- /// The prune endpoint.
- /// Used for user removal.
- /// </summary>
- public const string PRUNE = "/prune";
-
- /// <summary>
- /// The regions endpoint.
- /// </summary>
- public const string REGIONS = "/regions";
-
- /// <summary>
- /// The connections endpoint.
- /// </summary>
- public const string CONNECTIONS = "/connections";
-
- /// <summary>
- /// The icons endpoint.
- /// </summary>
- public const string ICONS = "/icons";
-
- /// <summary>
- /// The gateway endpoint.
- /// </summary>
- public const string GATEWAY = "/gateway";
-
- /// <summary>
- /// The oauth2 auth endpoint.
- /// </summary>
- public const string AUTH = "/auth";
-
- /// <summary>
- /// The oauth2 login endpoint.
- /// </summary>
- public const string LOGIN = "/login";
-
- /// <summary>
- /// The channels endpoint.
- /// </summary>
- public const string CHANNELS = "/channels";
-
- /// <summary>
- /// The messages endpoint.
- /// </summary>
- public const string MESSAGES = "/messages";
-
- /// <summary>
- /// The pinned messages endpoint.
- /// </summary>
- public const string PINS = "/pins";
-
- /// <summary>
- /// The users endpoint.
- /// </summary>
- public const string USERS = "/users";
-
- /// <summary>
- /// The guilds endpoint.
- /// </summary>
- public const string GUILDS = "/guilds";
-
- /// <summary>
- /// The guild discovery splash endpoint.
- /// </summary>
- public const string GUILD_DISCOVERY_SPLASHES = "/discovery-splashes";
-
- /// <summary>
- /// The guild splash endpoint.
- /// </summary>
- public const string SPLASHES = "/splashes";
-
- /// <summary>
- /// The search endpoint.
- /// </summary>
- public const string SEARCH = "/search";
-
- /// <summary>
- /// The invites endpoint.
- /// </summary>
- public const string INVITES = "/invites";
-
- /// <summary>
- /// The roles endpoint.
- /// </summary>
- public const string ROLES = "/roles";
-
- /// <summary>
- /// The members endpoint.
- /// </summary>
- public const string MEMBERS = "/members";
-
- /// <summary>
- /// The typing endpoint.
- /// Triggers a typing indicator inside a channel.
- /// </summary>
- public const string TYPING = "/typing";
-
- /// <summary>
- /// The avatars endpoint.
- /// </summary>
- public const string AVATARS = "/avatars";
-
- /// <summary>
- /// The bans endpoint.
- /// </summary>
- public const string BANS = "/bans";
-
- /// <summary>
- /// The webhook endpoint.
- /// </summary>
- public const string WEBHOOKS = "/webhooks";
-
- /// <summary>
- /// The slack endpoint.
- /// Used for <see cref="Entities.DiscordWebhook"/>.
- /// </summary>
- public const string SLACK = "/slack";
-
- /// <summary>
- /// The github endpoint.
- /// Used for <see cref="Entities.DiscordWebhook"/>.
- /// </summary>
- public const string GITHUB = "/github";
-
- /// <summary>
- /// The bot endpoint.
- /// </summary>
- public const string BOT = "/bot";
-
- /// <summary>
- /// The voice endpoint.
- /// </summary>
- public const string VOICE = "/voice";
-
- /// <summary>
- /// The audit logs endpoint.
- /// </summary>
- public const string AUDIT_LOGS = "/audit-logs";
-
- /// <summary>
- /// The acknowledge endpoint.
- /// Indicates that a message is read.
- /// </summary>
- public const string ACK = "/ack";
-
- /// <summary>
- /// The nickname endpoint.
- /// </summary>
- public const string NICK = "/nick";
-
- /// <summary>
- /// The assets endpoint.
- /// </summary>
- public const string ASSETS = "/assets";
-
- /// <summary>
- /// The embed endpoint.
- /// </summary>
- public const string EMBED = "/embed";
-
- /// <summary>
- /// The emojis endpoint.
- /// </summary>
- public const string EMOJIS = "/emojis";
-
- /// <summary>
- /// The vanity url endpoint.
- /// </summary>
- public const string VANITY_URL = "/vanity-url";
-
- /// <summary>
- /// The guild preview endpoint.
- /// </summary>
- public const string PREVIEW = "/preview";
-
- /// <summary>
- /// The followers endpoint.
- /// </summary>
- public const string FOLLOWERS = "/followers";
-
- /// <summary>
- /// The crosspost endpoint.
- /// </summary>
- public const string CROSSPOST = "/crosspost";
-
- /// <summary>
- /// The guild widget endpoint.
- /// </summary>
- public const string WIDGET = "/widget";
-
- /// <summary>
- /// The guild widget json endpoint.
- /// </summary>
- public const string WIDGET_JSON = "/widget.json";
-
- /// <summary>
- /// The guild widget png endpoint.
- /// </summary>
- public const string WIDGET_PNG = "/widget.png";
-
- /// <summary>
- /// The templates endpoint.
- /// </summary>
- public const string TEMPLATES = "/templates";
-
- /// <summary>
- /// The member verification gate endpoint.
- /// </summary>
- public const string MEMBER_VERIFICATION = "/member-verification";
/// <summary>
- /// The slash commands endpoint.
- /// </summary>
- public const string COMMANDS = "/commands";
-
- /// <summary>
- /// The interactions endpoint.
- /// </summary>
- public const string INTERACTIONS = "/interactions";
-
- /// <summary>
- /// The interaction/command callback endpoint.
- /// </summary>
- public const string CALLBACK = "/callback";
-
- /// <summary>
- /// The welcome screen endpoint.
- /// </summary>
- public const string WELCOME_SCREEN = "/welcome-screen";
-
- /// <summary>
- /// The voice states endpoint.
- /// </summary>
- public const string VOICE_STATES = "/voice-states";
-
- /// <summary>
- /// The stage instances endpoint.
- /// </summary>
- public const string STAGE_INSTANCES = "/stage-instances";
-
- /// <summary>
- /// The threads endpoint.
- /// </summary>
- public const string THREADS = "/threads";
-
- /// <summary>
- /// The public threads endpoint.
- /// </summary>
- public const string THREAD_PUBLIC = "/public";
-
- /// <summary>
- /// The private threads endpoint.
- /// </summary>
- public const string THREAD_PRIVATE = "/private";
-
- /// <summary>
- /// The active threads endpoint.
- /// </summary>
- public const string THREAD_ACTIVE = "/active";
-
- /// <summary>
- /// The archived threads endpoint.
- /// </summary>
- public const string THREAD_ARCHIVED = "/archived";
-
- /// <summary>
- /// The thread members endpoint.
- /// </summary>
- public const string THREAD_MEMBERS = "/thread-members";
-
- /// <summary>
- /// The guild scheduled events endpoint.
- /// </summary>
- public const string SCHEDULED_EVENTS = "/scheduled-events";
-
- /// <summary>
- /// The guild scheduled events cover image endpoint.
- /// </summary>
- public const string GUILD_EVENTS = "guild-events";
-
- /// <summary>
- /// The stickers endpoint.
- /// </summary>
- public const string STICKERS = "/stickers";
-
- /// <summary>
- /// The sticker packs endpoint.
- /// Global nitro sticker packs.
- /// </summary>
- public const string STICKERPACKS = "/sticker-packs";
-
- /// <summary>
- /// The store endpoint.
- /// </summary>
- public const string STORE = "/store";
-
- /// <summary>
- /// The app assets endpoint.
- /// </summary>
- public const string APP_ASSETS = "/app-assets";
-
- /// <summary>
- /// The app icons endpoint.
- /// </summary>
- public const string APP_ICONS = "/app-icons";
-
- /// <summary>
- /// The team icons endpoint.
- /// </summary>
- public const string TEAM_ICONS = "/team-icons";
-
- /// <summary>
- /// The channel icons endpoint.
- /// </summary>
- public const string CHANNEL_ICONS = "/channel-icons";
-
- /// <summary>
- /// The user banners endpoint.
- /// </summary>
- public const string BANNERS = "/banners";
-
- /// <summary>
- /// The sticker endpoint.
- /// This endpoint is the static nitro sticker application.
- /// </summary>
- public const string STICKER_APPLICATION = "/710982414301790216";
-
- /// <summary>
- /// The role subscription endpoint.
- /// </summary>
- public const string ROLE_SUBSCRIPTIONS = "/role-subscriptions";
-
- /// <summary>
- /// The group listings endpoint.
- /// </summary>
- public const string GROUP_LISTINGS = "/group-listings";
-
- /// <summary>
- /// The subscription listings endpoint.
- /// </summary>
- public const string SUBSCRIPTION_LISTINGS = "/subscription-listings";
-
- /// <summary>
- /// The directory entries endpoint.
- /// </summary>
- public const string DIRECTORY_ENTRIES = "/directory-entries";
-
- /// <summary>
- /// The counts endpoint.
- /// </summary>
- public const string COUNTS = "/counts";
-
- /// <summary>
- /// The list endpoint.
- /// </summary>
- public const string LIST = "/list";
-
- /// <summary>
- /// The role icons endpoint.
- /// </summary>
- public const string ROLE_ICONS = "/role-icons";
-
- /// <summary>
- /// The activities endpoint.
- /// </summary>
- public const string ACTIVITIES = "/activities";
-
- /// <summary>
- /// The config endpoint.
- /// </summary>
- public const string CONFIG = "/config";
-
- /// <summary>
- /// The ephemeral attachments endpoint.
- /// </summary>
- public const string EPHEMERAL_ATTACHMENTS = "/ephemeral-attachments";
+ /// The discord endpoints.
+ /// </summary>
+ public static class Endpoints
+ {
+ /// <summary>
+ /// The base discord api uri.
+ /// </summary>
+ public const string BASE_URI = "https://discord.com/api/v";
+
+ /// <summary>
+ /// The base discord canary api uri.
+ /// </summary>
+ public const string CANARY_URI = "https://canary.discord.com/api/v";
+
+ /// <summary>
+ /// The base discord ptb api uri.
+ /// </summary>
+ public const string PTB_URI = "https://ptb.discord.com/api/v";
+
+ /// <summary>
+ /// The oauth2 endpoint.
+ /// </summary>
+ public const string OAUTH2 = "/oauth2";
+
+ /// <summary>
+ /// The oauth2 authorize endpoint.
+ /// </summary>
+ public const string AUTHORIZE = "/authorize";
+
+ /// <summary>
+ /// The applications endpoint.
+ /// </summary>
+ public const string APPLICATIONS = "/applications";
+
+ /// <summary>
+ /// The message reactions endpoint.
+ /// </summary>
+ public const string REACTIONS = "/reactions";
+
+ /// <summary>
+ /// The self (@me) endpoint.
+ /// </summary>
+ public const string ME = "/@me";
+
+ /// <summary>
+ /// The @original endpoint.
+ /// </summary>
+ public const string ORIGINAL = "@original";
+
+ /// <summary>
+ /// The permissions endpoint.
+ /// </summary>
+ public const string PERMISSIONS = "/permissions";
+
+ /// <summary>
+ /// The recipients endpoint.
+ /// </summary>
+ public const string RECIPIENTS = "/recipients";
+
+ /// <summary>
+ /// The bulk-delete endpoint.
+ /// </summary>
+ public const string BULK_DELETE = "/bulk-delete";
+
+ /// <summary>
+ /// The integrations endpoint.
+ /// </summary>
+ public const string INTEGRATIONS = "/integrations";
+
+ /// <summary>
+ /// The sync endpoint.
+ /// </summary>
+ public const string SYNC = "/sync";
+
+ /// <summary>
+ /// The prune endpoint.
+ /// Used for user removal.
+ /// </summary>
+ public const string PRUNE = "/prune";
+
+ /// <summary>
+ /// The regions endpoint.
+ /// </summary>
+ public const string REGIONS = "/regions";
+
+ /// <summary>
+ /// The connections endpoint.
+ /// </summary>
+ public const string CONNECTIONS = "/connections";
+
+ /// <summary>
+ /// The icons endpoint.
+ /// </summary>
+ public const string ICONS = "/icons";
+
+ /// <summary>
+ /// The gateway endpoint.
+ /// </summary>
+ public const string GATEWAY = "/gateway";
+
+ /// <summary>
+ /// The oauth2 auth endpoint.
+ /// </summary>
+ public const string AUTH = "/auth";
+
+ /// <summary>
+ /// The oauth2 login endpoint.
+ /// </summary>
+ public const string LOGIN = "/login";
+
+ /// <summary>
+ /// The channels endpoint.
+ /// </summary>
+ public const string CHANNELS = "/channels";
+
+ /// <summary>
+ /// The messages endpoint.
+ /// </summary>
+ public const string MESSAGES = "/messages";
+
+ /// <summary>
+ /// The pinned messages endpoint.
+ /// </summary>
+ public const string PINS = "/pins";
+
+ /// <summary>
+ /// The users endpoint.
+ /// </summary>
+ public const string USERS = "/users";
+
+ /// <summary>
+ /// The guilds endpoint.
+ /// </summary>
+ public const string GUILDS = "/guilds";
+
+ /// <summary>
+ /// The guild discovery splash endpoint.
+ /// </summary>
+ public const string GUILD_DISCOVERY_SPLASHES = "/discovery-splashes";
+
+ /// <summary>
+ /// The guild splash endpoint.
+ /// </summary>
+ public const string SPLASHES = "/splashes";
+
+ /// <summary>
+ /// The search endpoint.
+ /// </summary>
+ public const string SEARCH = "/search";
+
+ /// <summary>
+ /// The invites endpoint.
+ /// </summary>
+ public const string INVITES = "/invites";
+
+ /// <summary>
+ /// The roles endpoint.
+ /// </summary>
+ public const string ROLES = "/roles";
+
+ /// <summary>
+ /// The members endpoint.
+ /// </summary>
+ public const string MEMBERS = "/members";
+
+ /// <summary>
+ /// The typing endpoint.
+ /// Triggers a typing indicator inside a channel.
+ /// </summary>
+ public const string TYPING = "/typing";
+
+ /// <summary>
+ /// The avatars endpoint.
+ /// </summary>
+ public const string AVATARS = "/avatars";
+
+ /// <summary>
+ /// The bans endpoint.
+ /// </summary>
+ public const string BANS = "/bans";
+
+ /// <summary>
+ /// The webhook endpoint.
+ /// </summary>
+ public const string WEBHOOKS = "/webhooks";
+
+ /// <summary>
+ /// The slack endpoint.
+ /// Used for <see cref="Entities.DiscordWebhook"/>.
+ /// </summary>
+ public const string SLACK = "/slack";
+
+ /// <summary>
+ /// The github endpoint.
+ /// Used for <see cref="Entities.DiscordWebhook"/>.
+ /// </summary>
+ public const string GITHUB = "/github";
+
+ /// <summary>
+ /// The bot endpoint.
+ /// </summary>
+ public const string BOT = "/bot";
+
+ /// <summary>
+ /// The voice endpoint.
+ /// </summary>
+ public const string VOICE = "/voice";
+
+ /// <summary>
+ /// The audit logs endpoint.
+ /// </summary>
+ public const string AUDIT_LOGS = "/audit-logs";
+
+ /// <summary>
+ /// The acknowledge endpoint.
+ /// Indicates that a message is read.
+ /// </summary>
+ public const string ACK = "/ack";
+
+ /// <summary>
+ /// The nickname endpoint.
+ /// </summary>
+ public const string NICK = "/nick";
+
+ /// <summary>
+ /// The assets endpoint.
+ /// </summary>
+ public const string ASSETS = "/assets";
+
+ /// <summary>
+ /// The embed endpoint.
+ /// </summary>
+ public const string EMBED = "/embed";
+
+ /// <summary>
+ /// The emojis endpoint.
+ /// </summary>
+ public const string EMOJIS = "/emojis";
+
+ /// <summary>
+ /// The vanity url endpoint.
+ /// </summary>
+ public const string VANITY_URL = "/vanity-url";
+
+ /// <summary>
+ /// The guild preview endpoint.
+ /// </summary>
+ public const string PREVIEW = "/preview";
+
+ /// <summary>
+ /// The followers endpoint.
+ /// </summary>
+ public const string FOLLOWERS = "/followers";
+
+ /// <summary>
+ /// The crosspost endpoint.
+ /// </summary>
+ public const string CROSSPOST = "/crosspost";
+
+ /// <summary>
+ /// The guild widget endpoint.
+ /// </summary>
+ public const string WIDGET = "/widget";
+
+ /// <summary>
+ /// The guild widget json endpoint.
+ /// </summary>
+ public const string WIDGET_JSON = "/widget.json";
+
+ /// <summary>
+ /// The guild widget png endpoint.
+ /// </summary>
+ public const string WIDGET_PNG = "/widget.png";
+
+ /// <summary>
+ /// The templates endpoint.
+ /// </summary>
+ public const string TEMPLATES = "/templates";
+
+ /// <summary>
+ /// The member verification gate endpoint.
+ /// </summary>
+ public const string MEMBER_VERIFICATION = "/member-verification";
+
+ /// <summary>
+ /// The slash commands endpoint.
+ /// </summary>
+ public const string COMMANDS = "/commands";
+
+ /// <summary>
+ /// The interactions endpoint.
+ /// </summary>
+ public const string INTERACTIONS = "/interactions";
+
+ /// <summary>
+ /// The interaction/command callback endpoint.
+ /// </summary>
+ public const string CALLBACK = "/callback";
+
+ /// <summary>
+ /// The welcome screen endpoint.
+ /// </summary>
+ public const string WELCOME_SCREEN = "/welcome-screen";
+
+ /// <summary>
+ /// The voice states endpoint.
+ /// </summary>
+ public const string VOICE_STATES = "/voice-states";
+
+ /// <summary>
+ /// The stage instances endpoint.
+ /// </summary>
+ public const string STAGE_INSTANCES = "/stage-instances";
+
+ /// <summary>
+ /// The threads endpoint.
+ /// </summary>
+ public const string THREADS = "/threads";
+
+ /// <summary>
+ /// The public threads endpoint.
+ /// </summary>
+ public const string THREAD_PUBLIC = "/public";
+
+ /// <summary>
+ /// The private threads endpoint.
+ /// </summary>
+ public const string THREAD_PRIVATE = "/private";
+
+ /// <summary>
+ /// The active threads endpoint.
+ /// </summary>
+ public const string THREAD_ACTIVE = "/active";
+
+ /// <summary>
+ /// The archived threads endpoint.
+ /// </summary>
+ public const string THREAD_ARCHIVED = "/archived";
+
+ /// <summary>
+ /// The thread members endpoint.
+ /// </summary>
+ public const string THREAD_MEMBERS = "/thread-members";
+
+ /// <summary>
+ /// The guild scheduled events endpoint.
+ /// </summary>
+ public const string SCHEDULED_EVENTS = "/scheduled-events";
+
+ /// <summary>
+ /// The guild scheduled events cover image endpoint.
+ /// </summary>
+ public const string GUILD_EVENTS = "guild-events";
+
+ /// <summary>
+ /// The stickers endpoint.
+ /// </summary>
+ public const string STICKERS = "/stickers";
+
+ /// <summary>
+ /// The sticker packs endpoint.
+ /// Global nitro sticker packs.
+ /// </summary>
+ public const string STICKERPACKS = "/sticker-packs";
+
+ /// <summary>
+ /// The store endpoint.
+ /// </summary>
+ public const string STORE = "/store";
+
+ /// <summary>
+ /// The app assets endpoint.
+ /// </summary>
+ public const string APP_ASSETS = "/app-assets";
+
+ /// <summary>
+ /// The app icons endpoint.
+ /// </summary>
+ public const string APP_ICONS = "/app-icons";
+
+ /// <summary>
+ /// The team icons endpoint.
+ /// </summary>
+ public const string TEAM_ICONS = "/team-icons";
+
+ /// <summary>
+ /// The channel icons endpoint.
+ /// </summary>
+ public const string CHANNEL_ICONS = "/channel-icons";
+
+ /// <summary>
+ /// The user banners endpoint.
+ /// </summary>
+ public const string BANNERS = "/banners";
+
+ /// <summary>
+ /// The sticker endpoint.
+ /// This endpoint is the static nitro sticker application.
+ /// </summary>
+ public const string STICKER_APPLICATION = "/710982414301790216";
+
+ /// <summary>
+ /// The role subscription endpoint.
+ /// </summary>
+ public const string ROLE_SUBSCRIPTIONS = "/role-subscriptions";
+
+ /// <summary>
+ /// The group listings endpoint.
+ /// </summary>
+ public const string GROUP_LISTINGS = "/group-listings";
+
+ /// <summary>
+ /// The subscription listings endpoint.
+ /// </summary>
+ public const string SUBSCRIPTION_LISTINGS = "/subscription-listings";
+
+ /// <summary>
+ /// The directory entries endpoint.
+ /// </summary>
+ public const string DIRECTORY_ENTRIES = "/directory-entries";
+
+ /// <summary>
+ /// The counts endpoint.
+ /// </summary>
+ public const string COUNTS = "/counts";
+
+ /// <summary>
+ /// The list endpoint.
+ /// </summary>
+ public const string LIST = "/list";
+
+ /// <summary>
+ /// The role icons endpoint.
+ /// </summary>
+ public const string ROLE_ICONS = "/role-icons";
+
+ /// <summary>
+ /// The activities endpoint.
+ /// </summary>
+ public const string ACTIVITIES = "/activities";
+
+ /// <summary>
+ /// The config endpoint.
+ /// </summary>
+ public const string CONFIG = "/config";
+
+ /// <summary>
+ /// The ephemeral attachments endpoint.
+ /// </summary>
+ public const string EPHEMERAL_ATTACHMENTS = "/ephemeral-attachments";
+ }
}
diff --git a/DisCatSharp/Net/Rest/IpEndpoint.cs b/DisCatSharp/Net/Rest/IpEndpoint.cs
index 558f53fa9..d9c44e8ce 100644
--- a/DisCatSharp/Net/Rest/IpEndpoint.cs
+++ b/DisCatSharp/Net/Rest/IpEndpoint.cs
@@ -1,52 +1,53 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents a network connection IP endpoint.
-/// </summary>
-public struct IpEndpoint
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets or sets the hostname associated with this endpoint.
+ /// Represents a network connection IP endpoint.
/// </summary>
- public IPAddress Address { get; set; }
+ public struct IpEndpoint
+ {
+ /// <summary>
+ /// Gets or sets the hostname associated with this endpoint.
+ /// </summary>
+ public IPAddress Address { get; set; }
- /// <summary>
- /// Gets or sets the port associated with this endpoint.
- /// </summary>
- public int Port { get; set; }
+ /// <summary>
+ /// Gets or sets the port associated with this endpoint.
+ /// </summary>
+ public int Port { get; set; }
- /// <summary>
- /// Creates a new IP endpoint structure.
- /// </summary>
- /// <param name="address">IP address to connect to.</param>
- /// <param name="port">Port to use for connection.</param>
- public IpEndpoint(IPAddress address, int port)
- {
- this.Address = address;
- this.Port = port;
+ /// <summary>
+ /// Creates a new IP endpoint structure.
+ /// </summary>
+ /// <param name="address">IP address to connect to.</param>
+ /// <param name="port">Port to use for connection.</param>
+ public IpEndpoint(IPAddress address, int port)
+ {
+ this.Address = address;
+ this.Port = port;
+ }
}
}
diff --git a/DisCatSharp/Net/Rest/MultipartWebRequest.cs b/DisCatSharp/Net/Rest/MultipartWebRequest.cs
index 4a6aabbef..6dbd25487 100644
--- a/DisCatSharp/Net/Rest/MultipartWebRequest.cs
+++ b/DisCatSharp/Net/Rest/MultipartWebRequest.cs
@@ -1,124 +1,125 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.IO;
using System.Linq;
using DisCatSharp.Entities;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents a multipart HTTP request.
-/// </summary>
-internal sealed class MultipartWebRequest : BaseRestRequest
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets the dictionary of values attached to this request.
+ /// Represents a multipart HTTP request.
/// </summary>
- public IReadOnlyDictionary<string, string> Values { get; }
+ internal sealed class MultipartWebRequest : BaseRestRequest
+ {
+ /// <summary>
+ /// Gets the dictionary of values attached to this request.
+ /// </summary>
+ public IReadOnlyDictionary<string, string> Values { get; }
- /// <summary>
- /// Gets the dictionary of files attached to this request.
- /// </summary>
- public IReadOnlyDictionary<string, Stream> Files { get; }
+ /// <summary>
+ /// Gets the dictionary of files attached to this request.
+ /// </summary>
+ public IReadOnlyDictionary<string, Stream> Files { get; }
- /// <summary>
- /// Overwrites the file id start.
- /// </summary>
- public int? OverwriteFileIdStart { get; }
+ /// <summary>
+ /// Overwrites the file id start.
+ /// </summary>
+ public int? OverwriteFileIdStart { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MultipartWebRequest"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="bucket">The bucket.</param>
- /// <param name="url">The url.</param>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <param name="headers">The headers.</param>
- /// <param name="values">The values.</param>
- /// <param name="files">The files.</param>
- /// <param name="ratelimitWaitOverride">The ratelimit_wait_override.</param>
- /// <param name="overwriteFileIdStart">The file id start.</param>
- internal MultipartWebRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, IReadOnlyDictionary<string, string> values = null,
- IReadOnlyCollection<DiscordMessageFile> files = null, double? ratelimitWaitOverride = null, int? overwriteFileIdStart = null)
- : base(client, bucket, url, method, route, headers, ratelimitWaitOverride)
- {
- this.Values = values;
- this.OverwriteFileIdStart = overwriteFileIdStart;
- this.Files = files.ToDictionary(x => x.FileName, x => x.Stream);
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MultipartWebRequest"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="bucket">The bucket.</param>
+ /// <param name="url">The url.</param>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <param name="headers">The headers.</param>
+ /// <param name="values">The values.</param>
+ /// <param name="files">The files.</param>
+ /// <param name="ratelimitWaitOverride">The ratelimit_wait_override.</param>
+ /// <param name="overwriteFileIdStart">The file id start.</param>
+ internal MultipartWebRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, IReadOnlyDictionary<string, string> values = null,
+ IReadOnlyCollection<DiscordMessageFile> files = null, double? ratelimitWaitOverride = null, int? overwriteFileIdStart = null)
+ : base(client, bucket, url, method, route, headers, ratelimitWaitOverride)
+ {
+ this.Values = values;
+ this.OverwriteFileIdStart = overwriteFileIdStart;
+ this.Files = files.ToDictionary(x => x.FileName, x => x.Stream);
+ }
}
-}
-/// <summary>
-/// Represents a multipart HTTP request for stickers.
-/// </summary>
-internal sealed class MultipartStickerWebRequest : BaseRestRequest
-{
/// <summary>
- /// Gets the file.
+ /// Represents a multipart HTTP request for stickers.
/// </summary>
- public DiscordMessageFile File { get; }
+ internal sealed class MultipartStickerWebRequest : BaseRestRequest
+ {
+ /// <summary>
+ /// Gets the file.
+ /// </summary>
+ public DiscordMessageFile File { get; }
- /// <summary>
- /// Gets the name.
- /// </summary>
- public string Name { get; }
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ public string Name { get; }
- /// <summary>
- /// Gets the description.
- /// </summary>
- public string Description { get; }
+ /// <summary>
+ /// Gets the description.
+ /// </summary>
+ public string Description { get; }
- /// <summary>
- /// Gets the tags.
- /// </summary>
- public string Tags { get; }
+ /// <summary>
+ /// Gets the tags.
+ /// </summary>
+ public string Tags { get; }
- /// <summary>
- /// Initializes a new instance of the <see cref="MultipartStickerWebRequest"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="bucket">The bucket.</param>
- /// <param name="url">The url.</param>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <param name="headers">The headers.</param>
- /// <param name="file">The file.</param>
- /// <param name="name">The sticker name.</param>
- /// <param name="tags">The sticker tag.</param>
- /// <param name="description">The sticker description.</param>
- /// <param name="ratelimitWaitOverride">The ratelimit_wait_override.</param>
- internal MultipartStickerWebRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null,
- DiscordMessageFile file = null, string name = "", string tags = "", string description = "", double? ratelimitWaitOverride = null)
- : base(client, bucket, url, method, route, headers, ratelimitWaitOverride)
- {
- this.File = file;
- this.Name = name;
- this.Description = description;
- this.Tags = tags;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MultipartStickerWebRequest"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="bucket">The bucket.</param>
+ /// <param name="url">The url.</param>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <param name="headers">The headers.</param>
+ /// <param name="file">The file.</param>
+ /// <param name="name">The sticker name.</param>
+ /// <param name="tags">The sticker tag.</param>
+ /// <param name="description">The sticker description.</param>
+ /// <param name="ratelimitWaitOverride">The ratelimit_wait_override.</param>
+ internal MultipartStickerWebRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null,
+ DiscordMessageFile file = null, string name = "", string tags = "", string description = "", double? ratelimitWaitOverride = null)
+ : base(client, bucket, url, method, route, headers, ratelimitWaitOverride)
+ {
+ this.File = file;
+ this.Name = name;
+ this.Description = description;
+ this.Tags = tags;
+ }
}
}
diff --git a/DisCatSharp/Net/Rest/RateLimitBucket.cs b/DisCatSharp/Net/Rest/RateLimitBucket.cs
index cdd0d844e..39bb838a8 100644
--- a/DisCatSharp/Net/Rest/RateLimitBucket.cs
+++ b/DisCatSharp/Net/Rest/RateLimitBucket.cs
@@ -1,290 +1,291 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Threading;
using System.Threading.Tasks;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents a rate limit bucket.
-/// </summary>
-internal class RateLimitBucket : IEquatable<RateLimitBucket>
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets the Id of the guild bucket.
- /// </summary>
- public string GuildId { get; internal set; }
-
- /// <summary>
- /// Gets the Id of the channel bucket.
- /// </summary>
- public string ChannelId { get; internal set; }
-
- /// <summary>
- /// Gets the ID of the webhook bucket.
+ /// Represents a rate limit bucket.
/// </summary>
- public string WebhookId { get; internal set; }
-
- /// <summary>
- /// Gets the Id of the ratelimit bucket.
- /// </summary>
- public volatile string BucketId;
-
- /// <summary>
- /// Gets or sets the ratelimit hash of this bucket.
- /// </summary>
- public string Hash
+ internal class RateLimitBucket : IEquatable<RateLimitBucket>
{
- get => Volatile.Read(ref this.HashInternal);
-
- internal set
+ /// <summary>
+ /// Gets the Id of the guild bucket.
+ /// </summary>
+ public string GuildId { get; internal set; }
+
+ /// <summary>
+ /// Gets the Id of the channel bucket.
+ /// </summary>
+ public string ChannelId { get; internal set; }
+
+ /// <summary>
+ /// Gets the ID of the webhook bucket.
+ /// </summary>
+ public string WebhookId { get; internal set; }
+
+ /// <summary>
+ /// Gets the Id of the ratelimit bucket.
+ /// </summary>
+ public volatile string BucketId;
+
+ /// <summary>
+ /// Gets or sets the ratelimit hash of this bucket.
+ /// </summary>
+ public string Hash
{
- this.IsUnlimited = value.Contains(s_unlimitedHash);
+ get => Volatile.Read(ref this.HashInternal);
- if (this.BucketId != null && !this.BucketId.StartsWith(value))
+ internal set
{
- var id = GenerateBucketId(value, this.GuildId, this.ChannelId, this.WebhookId);
- this.BucketId = id;
- this.RouteHashes.Add(id);
- }
-
- Volatile.Write(ref this.HashInternal, value);
- }
- }
-
- internal string HashInternal;
-
- /// <summary>
- /// Gets the past route hashes associated with this bucket.
- /// </summary>
- public ConcurrentBag<string> RouteHashes { get; }
-
- /// <summary>
- /// Gets when this bucket was last called in a request.
- /// </summary>
- public DateTimeOffset LastAttemptAt { get; internal set; }
-
- /// <summary>
- /// Gets the number of uses left before pre-emptive rate limit is triggered.
- /// </summary>
- public int Remaining
- => this.RemainingInternal;
+ this.IsUnlimited = value.Contains(s_unlimitedHash);
- /// <summary>
- /// Gets the maximum number of uses within a single bucket.
- /// </summary>
- public int Maximum { get; set; }
-
- /// <summary>
- /// Gets the timestamp at which the rate limit resets.
- /// </summary>
- public DateTimeOffset Reset { get; internal set; }
-
- /// <summary>
- /// Gets the time interval to wait before the rate limit resets.
- /// </summary>
- public TimeSpan? ResetAfter { get; internal set; }
-
- /// <summary>
- /// Gets a value indicating whether the ratelimit global.
- /// </summary>
- public bool IsGlobal { get; internal set; } = false;
-
- /// <summary>
- /// Gets the ratelimit scope.
- /// </summary>
- public string Scope { get; internal set; } = "user";
+ if (this.BucketId != null && !this.BucketId.StartsWith(value))
+ {
+ var id = GenerateBucketId(value, this.GuildId, this.ChannelId, this.WebhookId);
+ this.BucketId = id;
+ this.RouteHashes.Add(id);
+ }
- /// <summary>
- /// Gets the time interval to wait before the rate limit resets as offset
- /// </summary>
- internal DateTimeOffset ResetAfterOffset { get; set; }
-
- internal volatile int RemainingInternal;
-
- /// <summary>
- /// Gets whether this bucket has it's ratelimit determined.
- /// <para>This will be <see langword="false"/> if the ratelimit is determined.</para>
- /// </summary>
- internal volatile bool IsUnlimited;
-
- /// <summary>
- /// If the initial request for this bucket that is determining the rate limits is currently executing
- /// This is a int because booleans can't be accessed atomically
- /// 0 => False, all other values => True
- /// </summary>
- internal volatile int LimitTesting;
-
- /// <summary>
- /// Task to wait for the rate limit test to finish
- /// </summary>
- internal volatile Task LimitTestFinished;
-
- /// <summary>
- /// If the rate limits have been determined
- /// </summary>
- internal volatile bool LimitValid;
-
- /// <summary>
- /// Rate limit reset in ticks, UTC on the next response after the rate limit has been reset
- /// </summary>
- internal long NextReset;
-
- /// <summary>
- /// If the rate limit is currently being reset.
- /// This is a int because booleans can't be accessed atomically.
- /// 0 => False, all other values => True
- /// </summary>
- internal volatile int LimitResetting;
-
- private static readonly string s_unlimitedHash = "unlimited";
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RateLimitBucket"/> class.
- /// </summary>
- /// <param name="hash">The hash.</param>
- /// <param name="guildId">The guild_id.</param>
- /// <param name="channelId">The channel_id.</param>
- /// <param name="webhookId">The webhook_id.</param>
- internal RateLimitBucket(string hash, string guildId, string channelId, string webhookId)
- {
- this.Hash = hash;
- this.ChannelId = channelId;
- this.GuildId = guildId;
- this.WebhookId = webhookId;
-
- this.BucketId = GenerateBucketId(hash, guildId, channelId, webhookId);
- this.RouteHashes = new ConcurrentBag<string>();
- }
-
- /// <summary>
- /// Generates an ID for this request bucket.
- /// </summary>
- /// <param name="hash">Hash for this bucket.</param>
- /// <param name="guildId">Guild Id for this bucket.</param>
- /// <param name="channelId">Channel Id for this bucket.</param>
- /// <param name="webhookId">Webhook Id for this bucket.</param>
- /// <returns>Bucket Id.</returns>
- public static string GenerateBucketId(string hash, string guildId, string channelId, string webhookId)
- => $"{hash}:{guildId}:{channelId}:{webhookId}";
-
- /// <summary>
- /// Generates the hash key.
- /// </summary>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <returns>A string.</returns>
- public static string GenerateHashKey(RestRequestMethod method, string route)
- => $"{method}:{route}";
-
- /// <summary>
- /// Generates the unlimited hash.
- /// </summary>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <returns>A string.</returns>
- public static string GenerateUnlimitedHash(RestRequestMethod method, string route)
- => $"{GenerateHashKey(method, route)}:{s_unlimitedHash}";
-
- /// <summary>
- /// Returns a string representation of this bucket.
- /// </summary>
- /// <returns>String representation of this bucket.</returns>
- public override string ToString()
- {
- var guildId = this.GuildId != string.Empty ? this.GuildId : "guild_id";
- var channelId = this.ChannelId != string.Empty ? this.ChannelId : "channel_id";
- var webhookId = this.WebhookId != string.Empty ? this.WebhookId : "webhook_id";
+ Volatile.Write(ref this.HashInternal, value);
+ }
+ }
- return $"{this.Scope} rate limit bucket [{this.Hash}:{guildId}:{channelId}:{webhookId}] [{this.Remaining}/{this.Maximum}] {(this.ResetAfter.HasValue ? this.ResetAfterOffset : this.Reset)}";
- }
+ internal string HashInternal;
+
+ /// <summary>
+ /// Gets the past route hashes associated with this bucket.
+ /// </summary>
+ public ConcurrentBag<string> RouteHashes { get; }
+
+ /// <summary>
+ /// Gets when this bucket was last called in a request.
+ /// </summary>
+ public DateTimeOffset LastAttemptAt { get; internal set; }
+
+ /// <summary>
+ /// Gets the number of uses left before pre-emptive rate limit is triggered.
+ /// </summary>
+ public int Remaining
+ => this.RemainingInternal;
+
+ /// <summary>
+ /// Gets the maximum number of uses within a single bucket.
+ /// </summary>
+ public int Maximum { get; set; }
+
+ /// <summary>
+ /// Gets the timestamp at which the rate limit resets.
+ /// </summary>
+ public DateTimeOffset Reset { get; internal set; }
+
+ /// <summary>
+ /// Gets the time interval to wait before the rate limit resets.
+ /// </summary>
+ public TimeSpan? ResetAfter { get; internal set; }
+
+ /// <summary>
+ /// Gets a value indicating whether the ratelimit global.
+ /// </summary>
+ public bool IsGlobal { get; internal set; } = false;
+
+ /// <summary>
+ /// Gets the ratelimit scope.
+ /// </summary>
+ public string Scope { get; internal set; } = "user";
+
+ /// <summary>
+ /// Gets the time interval to wait before the rate limit resets as offset
+ /// </summary>
+ internal DateTimeOffset ResetAfterOffset { get; set; }
+
+ internal volatile int RemainingInternal;
+
+ /// <summary>
+ /// Gets whether this bucket has it's ratelimit determined.
+ /// <para>This will be <see langword="false"/> if the ratelimit is determined.</para>
+ /// </summary>
+ internal volatile bool IsUnlimited;
+
+ /// <summary>
+ /// If the initial request for this bucket that is determining the rate limits is currently executing
+ /// This is a int because booleans can't be accessed atomically
+ /// 0 => False, all other values => True
+ /// </summary>
+ internal volatile int LimitTesting;
+
+ /// <summary>
+ /// Task to wait for the rate limit test to finish
+ /// </summary>
+ internal volatile Task LimitTestFinished;
+
+ /// <summary>
+ /// If the rate limits have been determined
+ /// </summary>
+ internal volatile bool LimitValid;
+
+ /// <summary>
+ /// Rate limit reset in ticks, UTC on the next response after the rate limit has been reset
+ /// </summary>
+ internal long NextReset;
+
+ /// <summary>
+ /// If the rate limit is currently being reset.
+ /// This is a int because booleans can't be accessed atomically.
+ /// 0 => False, all other values => True
+ /// </summary>
+ internal volatile int LimitResetting;
+
+ private static readonly string s_unlimitedHash = "unlimited";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RateLimitBucket"/> class.
+ /// </summary>
+ /// <param name="hash">The hash.</param>
+ /// <param name="guildId">The guild_id.</param>
+ /// <param name="channelId">The channel_id.</param>
+ /// <param name="webhookId">The webhook_id.</param>
+ internal RateLimitBucket(string hash, string guildId, string channelId, string webhookId)
+ {
+ this.Hash = hash;
+ this.ChannelId = channelId;
+ this.GuildId = guildId;
+ this.WebhookId = webhookId;
- /// <summary>
- /// Checks whether this <see cref="RateLimitBucket"/> is equal to another object.
- /// </summary>
- /// <param name="obj">Object to compare to.</param>
- /// <returns>Whether the object is equal to this <see cref="RateLimitBucket"/>.</returns>
- public override bool Equals(object obj)
- => this.Equals(obj as RateLimitBucket);
+ this.BucketId = GenerateBucketId(hash, guildId, channelId, webhookId);
+ this.RouteHashes = new ConcurrentBag<string>();
+ }
- /// <summary>
- /// Checks whether this <see cref="RateLimitBucket"/> is equal to another <see cref="RateLimitBucket"/>.
- /// </summary>
- /// <param name="e"><see cref="RateLimitBucket"/> to compare to.</param>
- /// <returns>Whether the <see cref="RateLimitBucket"/> is equal to this <see cref="RateLimitBucket"/>.</returns>
- public bool Equals(RateLimitBucket e) => e is not null && (ReferenceEquals(this, e) || this.BucketId == e.BucketId);
+ /// <summary>
+ /// Generates an ID for this request bucket.
+ /// </summary>
+ /// <param name="hash">Hash for this bucket.</param>
+ /// <param name="guildId">Guild Id for this bucket.</param>
+ /// <param name="channelId">Channel Id for this bucket.</param>
+ /// <param name="webhookId">Webhook Id for this bucket.</param>
+ /// <returns>Bucket Id.</returns>
+ public static string GenerateBucketId(string hash, string guildId, string channelId, string webhookId)
+ => $"{hash}:{guildId}:{channelId}:{webhookId}";
+
+ /// <summary>
+ /// Generates the hash key.
+ /// </summary>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <returns>A string.</returns>
+ public static string GenerateHashKey(RestRequestMethod method, string route)
+ => $"{method}:{route}";
+
+ /// <summary>
+ /// Generates the unlimited hash.
+ /// </summary>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <returns>A string.</returns>
+ public static string GenerateUnlimitedHash(RestRequestMethod method, string route)
+ => $"{GenerateHashKey(method, route)}:{s_unlimitedHash}";
+
+ /// <summary>
+ /// Returns a string representation of this bucket.
+ /// </summary>
+ /// <returns>String representation of this bucket.</returns>
+ public override string ToString()
+ {
+ var guildId = this.GuildId != string.Empty ? this.GuildId : "guild_id";
+ var channelId = this.ChannelId != string.Empty ? this.ChannelId : "channel_id";
+ var webhookId = this.WebhookId != string.Empty ? this.WebhookId : "webhook_id";
- /// <summary>
- /// Gets the hash code for this <see cref="RateLimitBucket"/>.
- /// </summary>
- /// <returns>The hash code for this <see cref="RateLimitBucket"/>.</returns>
- public override int GetHashCode()
- => this.BucketId.GetHashCode();
+ return $"{this.Scope} rate limit bucket [{this.Hash}:{guildId}:{channelId}:{webhookId}] [{this.Remaining}/{this.Maximum}] {(this.ResetAfter.HasValue ? this.ResetAfterOffset : this.Reset)}";
+ }
- /// <summary>
- /// Sets remaining number of requests to the maximum when the ratelimit is reset
- /// </summary>
- /// <param name="now">The datetime offset.</param>
- internal async Task TryResetLimitAsync(DateTimeOffset now)
- {
- if (this.ResetAfter.HasValue)
- this.ResetAfter = this.ResetAfterOffset - now;
+ /// <summary>
+ /// Checks whether this <see cref="RateLimitBucket"/> is equal to another object.
+ /// </summary>
+ /// <param name="obj">Object to compare to.</param>
+ /// <returns>Whether the object is equal to this <see cref="RateLimitBucket"/>.</returns>
+ public override bool Equals(object obj)
+ => this.Equals(obj as RateLimitBucket);
+
+ /// <summary>
+ /// Checks whether this <see cref="RateLimitBucket"/> is equal to another <see cref="RateLimitBucket"/>.
+ /// </summary>
+ /// <param name="e"><see cref="RateLimitBucket"/> to compare to.</param>
+ /// <returns>Whether the <see cref="RateLimitBucket"/> is equal to this <see cref="RateLimitBucket"/>.</returns>
+ public bool Equals(RateLimitBucket e) => e is not null && (ReferenceEquals(this, e) || this.BucketId == e.BucketId);
+
+ /// <summary>
+ /// Gets the hash code for this <see cref="RateLimitBucket"/>.
+ /// </summary>
+ /// <returns>The hash code for this <see cref="RateLimitBucket"/>.</returns>
+ public override int GetHashCode()
+ => this.BucketId.GetHashCode();
+
+ /// <summary>
+ /// Sets remaining number of requests to the maximum when the ratelimit is reset
+ /// </summary>
+ /// <param name="now">The datetime offset.</param>
+ internal async Task TryResetLimitAsync(DateTimeOffset now)
+ {
+ if (this.ResetAfter.HasValue)
+ this.ResetAfter = this.ResetAfterOffset - now;
- if (this.NextReset == 0)
- return;
+ if (this.NextReset == 0)
+ return;
- if (this.NextReset > now.UtcTicks)
- return;
+ if (this.NextReset > now.UtcTicks)
+ return;
- while (Interlocked.CompareExchange(ref this.LimitResetting, 1, 0) != 0)
+ while (Interlocked.CompareExchange(ref this.LimitResetting, 1, 0) != 0)
#pragma warning restore 420
- await Task.Yield();
+ await Task.Yield();
- if (this.NextReset != 0)
- {
- this.RemainingInternal = this.Maximum;
- this.NextReset = 0;
- }
+ if (this.NextReset != 0)
+ {
+ this.RemainingInternal = this.Maximum;
+ this.NextReset = 0;
+ }
- this.LimitResetting = 0;
- }
+ this.LimitResetting = 0;
+ }
- /// <summary>
- /// Sets the initial values.
- /// </summary>
- /// <param name="max">The max.</param>
- /// <param name="usesLeft">The uses left.</param>
- /// <param name="newReset">The new reset.</param>
- internal void SetInitialValues(int max, int usesLeft, DateTimeOffset newReset)
- {
- this.Maximum = max;
- this.RemainingInternal = usesLeft;
- this.NextReset = newReset.UtcTicks;
+ /// <summary>
+ /// Sets the initial values.
+ /// </summary>
+ /// <param name="max">The max.</param>
+ /// <param name="usesLeft">The uses left.</param>
+ /// <param name="newReset">The new reset.</param>
+ internal void SetInitialValues(int max, int usesLeft, DateTimeOffset newReset)
+ {
+ this.Maximum = max;
+ this.RemainingInternal = usesLeft;
+ this.NextReset = newReset.UtcTicks;
- this.LimitValid = true;
- this.LimitTestFinished = null;
- this.LimitTesting = 0;
+ this.LimitValid = true;
+ this.LimitTestFinished = null;
+ this.LimitTesting = 0;
+ }
}
}
diff --git a/DisCatSharp/Net/Rest/RestClient.cs b/DisCatSharp/Net/Rest/RestClient.cs
index 7b9af5249..2ae1dcaf7 100644
--- a/DisCatSharp/Net/Rest/RestClient.cs
+++ b/DisCatSharp/Net/Rest/RestClient.cs
@@ -1,872 +1,873 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.Exceptions;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents a client used to make REST requests.
-/// </summary>
-internal sealed class RestClient : IDisposable
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets the route argument regex.
- /// </summary>
- private static Regex s_routeArgumentRegex { get; } = new(@":([a-z_]+)");
-
- /// <summary>
- /// Gets the http client.
- /// </summary>
- internal HttpClient HttpClient { get; }
-
- /// <summary>
- /// Gets the discord client.
- /// </summary>
- private readonly BaseDiscordClient _discord;
-
- /// <summary>
- /// Gets a value indicating whether debug is enabled.
+ /// Represents a client used to make REST requests.
/// </summary>
- internal bool Debug { get; set; }
-
- /// <summary>
- /// Gets the logger.
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// Gets the routes to hashes.
- /// </summary>
- private readonly ConcurrentDictionary<string, string> _routesToHashes;
-
- /// <summary>
- /// Gets the hashes to buckets.
- /// </summary>
- private readonly ConcurrentDictionary<string, RateLimitBucket> _hashesToBuckets;
-
- /// <summary>
- /// Gets the request queue.
- /// </summary>
- private readonly ConcurrentDictionary<string, int> _requestQueue;
-
- /// <summary>
- /// Gets the global rate limit event.
- /// </summary>
- private readonly AsyncManualResetEvent _globalRateLimitEvent;
-
- /// <summary>
- /// Gets a value indicating whether use reset after.
- /// </summary>
- private readonly bool _useResetAfter;
-
- private CancellationTokenSource _bucketCleanerTokenSource;
- private readonly TimeSpan _bucketCleanupDelay = TimeSpan.FromSeconds(60);
- private volatile bool _cleanerRunning;
- private Task _cleanerTask;
- private volatile bool _disposed;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RestClient"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- internal RestClient(BaseDiscordClient client)
- : this(client.Configuration.Proxy, client.Configuration.HttpTimeout, client.Configuration.UseRelativeRatelimit, client.Logger)
+ internal sealed class RestClient : IDisposable
{
- this._discord = client;
- this.HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", Utilities.GetFormattedToken(client));
- if (client.Configuration.Override != null)
+ /// <summary>
+ /// Gets the route argument regex.
+ /// </summary>
+ private static Regex s_routeArgumentRegex { get; } = new(@":([a-z_]+)");
+
+ /// <summary>
+ /// Gets the http client.
+ /// </summary>
+ internal HttpClient HttpClient { get; }
+
+ /// <summary>
+ /// Gets the discord client.
+ /// </summary>
+ private readonly BaseDiscordClient _discord;
+
+ /// <summary>
+ /// Gets a value indicating whether debug is enabled.
+ /// </summary>
+ internal bool Debug { get; set; }
+
+ /// <summary>
+ /// Gets the logger.
+ /// </summary>
+ private readonly ILogger _logger;
+
+ /// <summary>
+ /// Gets the routes to hashes.
+ /// </summary>
+ private readonly ConcurrentDictionary<string, string> _routesToHashes;
+
+ /// <summary>
+ /// Gets the hashes to buckets.
+ /// </summary>
+ private readonly ConcurrentDictionary<string, RateLimitBucket> _hashesToBuckets;
+
+ /// <summary>
+ /// Gets the request queue.
+ /// </summary>
+ private readonly ConcurrentDictionary<string, int> _requestQueue;
+
+ /// <summary>
+ /// Gets the global rate limit event.
+ /// </summary>
+ private readonly AsyncManualResetEvent _globalRateLimitEvent;
+
+ /// <summary>
+ /// Gets a value indicating whether use reset after.
+ /// </summary>
+ private readonly bool _useResetAfter;
+
+ private CancellationTokenSource _bucketCleanerTokenSource;
+ private readonly TimeSpan _bucketCleanupDelay = TimeSpan.FromSeconds(60);
+ private volatile bool _cleanerRunning;
+ private Task _cleanerTask;
+ private volatile bool _disposed;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RestClient"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ internal RestClient(BaseDiscordClient client)
+ : this(client.Configuration.Proxy, client.Configuration.HttpTimeout, client.Configuration.UseRelativeRatelimit, client.Logger)
{
- this.HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("x-super-properties", client.Configuration.Override);
+ this._discord = client;
+ this.HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", Utilities.GetFormattedToken(client));
+ if (client.Configuration.Override != null)
+ {
+ this.HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("x-super-properties", client.Configuration.Override);
+ }
}
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RestClient"/> class.
- /// This is for meta-clients, such as the webhook client.
- /// </summary>
- /// <param name="proxy">The proxy.</param>
- /// <param name="timeout">The timeout.</param>
- /// <param name="useRelativeRatelimit">Whether to use relative ratelimit.</param>
- /// <param name="logger">The logger.</param>
- internal RestClient(IWebProxy proxy, TimeSpan timeout, bool useRelativeRatelimit,
- ILogger logger)
- {
- this._logger = logger;
- var httphandler = new HttpClientHandler
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RestClient"/> class.
+ /// This is for meta-clients, such as the webhook client.
+ /// </summary>
+ /// <param name="proxy">The proxy.</param>
+ /// <param name="timeout">The timeout.</param>
+ /// <param name="useRelativeRatelimit">Whether to use relative ratelimit.</param>
+ /// <param name="logger">The logger.</param>
+ internal RestClient(IWebProxy proxy, TimeSpan timeout, bool useRelativeRatelimit,
+ ILogger logger)
{
- UseCookies = false,
- AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
- UseProxy = proxy != null,
- Proxy = proxy
- };
+ this._logger = logger;
- this.HttpClient = new HttpClient(httphandler)
- {
- BaseAddress = new Uri(Utilities.GetApiBaseUri(this._discord?.Configuration)),
- Timeout = timeout
- };
+ var httphandler = new HttpClientHandler
+ {
+ UseCookies = false,
+ AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
+ UseProxy = proxy != null,
+ Proxy = proxy
+ };
- this.HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Utilities.GetUserAgent());
- if (this._discord != null && this._discord.Configuration != null && this._discord.Configuration.Override != null)
- {
- this.HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("x-super-properties", this._discord.Configuration.Override);
- }
+ this.HttpClient = new HttpClient(httphandler)
+ {
+ BaseAddress = new Uri(Utilities.GetApiBaseUri(this._discord?.Configuration)),
+ Timeout = timeout
+ };
- this._routesToHashes = new ConcurrentDictionary<string, string>();
- this._hashesToBuckets = new ConcurrentDictionary<string, RateLimitBucket>();
- this._requestQueue = new ConcurrentDictionary<string, int>();
+ this.HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", Utilities.GetUserAgent());
+ if (this._discord != null && this._discord.Configuration != null && this._discord.Configuration.Override != null)
+ {
+ this.HttpClient.DefaultRequestHeaders.TryAddWithoutValidation("x-super-properties", this._discord.Configuration.Override);
+ }
- this._globalRateLimitEvent = new AsyncManualResetEvent(true);
- this._useResetAfter = useRelativeRatelimit;
- }
+ this._routesToHashes = new ConcurrentDictionary<string, string>();
+ this._hashesToBuckets = new ConcurrentDictionary<string, RateLimitBucket>();
+ this._requestQueue = new ConcurrentDictionary<string, int>();
- /// <summary>
- /// Gets a ratelimit bucket.
- /// </summary>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <param name="routeParams">The route parameters.</param>
- /// <param name="url">The url.</param>
- /// <returns>A ratelimit bucket.</returns>
- public RateLimitBucket GetBucket(RestRequestMethod method, string route, object routeParams, out string url)
- {
- var rparamsProps = routeParams.GetType()
- .GetTypeInfo()
- .DeclaredProperties;
- var rparams = new Dictionary<string, string>();
- foreach (var xp in rparamsProps)
- {
- var val = xp.GetValue(routeParams);
- rparams[xp.Name] = val is string xs
- ? xs
- : val is DateTime dt
- ? dt.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)
- : val is DateTimeOffset dto
- ? dto.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)
- : val is IFormattable xf ? xf.ToString(null, CultureInfo.InvariantCulture) : val.ToString();
+ this._globalRateLimitEvent = new AsyncManualResetEvent(true);
+ this._useResetAfter = useRelativeRatelimit;
}
- var guildId = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : "";
- var channelId = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : "";
- var webhookId = rparams.ContainsKey("webhook_id") ? rparams["webhook_id"] : "";
-
- // Create a generic route (minus major params) key
- // ex: POST:/channels/channel_id/messages
- var hashKey = RateLimitBucket.GenerateHashKey(method, route);
-
- // We check if the hash is present, using our generic route (without major params)
- // ex: in POST:/channels/channel_id/messages, out 80c17d2f203122d936070c88c8d10f33
- // If it doesn't exist, we create an unlimited hash as our initial key in the form of the hash key + the unlimited constant
- // and assign this to the route to hash cache
- // ex: this.RoutesToHashes[POST:/channels/channel_id/messages] = POST:/channels/channel_id/messages:unlimited
- var hash = this._routesToHashes.GetOrAdd(hashKey, RateLimitBucket.GenerateUnlimitedHash(method, route));
+ /// <summary>
+ /// Gets a ratelimit bucket.
+ /// </summary>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <param name="routeParams">The route parameters.</param>
+ /// <param name="url">The url.</param>
+ /// <returns>A ratelimit bucket.</returns>
+ public RateLimitBucket GetBucket(RestRequestMethod method, string route, object routeParams, out string url)
+ {
+ var rparamsProps = routeParams.GetType()
+ .GetTypeInfo()
+ .DeclaredProperties;
+ var rparams = new Dictionary<string, string>();
+ foreach (var xp in rparamsProps)
+ {
+ var val = xp.GetValue(routeParams);
+ rparams[xp.Name] = val is string xs
+ ? xs
+ : val is DateTime dt
+ ? dt.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)
+ : val is DateTimeOffset dto
+ ? dto.ToString("yyyy-MM-ddTHH:mm:sszzz", CultureInfo.InvariantCulture)
+ : val is IFormattable xf ? xf.ToString(null, CultureInfo.InvariantCulture) : val.ToString();
+ }
- // Next we use the hash to generate the key to obtain the bucket.
- // ex: 80c17d2f203122d936070c88c8d10f33:guild_id:506128773926879242:webhook_id
- // or if unlimited: POST:/channels/channel_id/messages:unlimited:guild_id:506128773926879242:webhook_id
- var bucketId = RateLimitBucket.GenerateBucketId(hash, guildId, channelId, webhookId);
+ var guildId = rparams.ContainsKey("guild_id") ? rparams["guild_id"] : "";
+ var channelId = rparams.ContainsKey("channel_id") ? rparams["channel_id"] : "";
+ var webhookId = rparams.ContainsKey("webhook_id") ? rparams["webhook_id"] : "";
- // If it's not in cache, create a new bucket and index it by its bucket id.
- var bucket = this._hashesToBuckets.GetOrAdd(bucketId, new RateLimitBucket(hash, guildId, channelId, webhookId));
+ // Create a generic route (minus major params) key
+ // ex: POST:/channels/channel_id/messages
+ var hashKey = RateLimitBucket.GenerateHashKey(method, route);
- bucket.LastAttemptAt = DateTimeOffset.UtcNow;
+ // We check if the hash is present, using our generic route (without major params)
+ // ex: in POST:/channels/channel_id/messages, out 80c17d2f203122d936070c88c8d10f33
+ // If it doesn't exist, we create an unlimited hash as our initial key in the form of the hash key + the unlimited constant
+ // and assign this to the route to hash cache
+ // ex: this.RoutesToHashes[POST:/channels/channel_id/messages] = POST:/channels/channel_id/messages:unlimited
+ var hash = this._routesToHashes.GetOrAdd(hashKey, RateLimitBucket.GenerateUnlimitedHash(method, route));
- // Cache the routes for each bucket so it can be used for GC later.
- if (!bucket.RouteHashes.Contains(bucketId))
- bucket.RouteHashes.Add(bucketId);
+ // Next we use the hash to generate the key to obtain the bucket.
+ // ex: 80c17d2f203122d936070c88c8d10f33:guild_id:506128773926879242:webhook_id
+ // or if unlimited: POST:/channels/channel_id/messages:unlimited:guild_id:506128773926879242:webhook_id
+ var bucketId = RateLimitBucket.GenerateBucketId(hash, guildId, channelId, webhookId);
- // Add the current route to the request queue, which indexes the amount
- // of requests occurring to the bucket id.
- _ = this._requestQueue.TryGetValue(bucketId, out var count);
+ // If it's not in cache, create a new bucket and index it by its bucket id.
+ var bucket = this._hashesToBuckets.GetOrAdd(bucketId, new RateLimitBucket(hash, guildId, channelId, webhookId));
- // Increment by one atomically due to concurrency
- this._requestQueue[bucketId] = Interlocked.Increment(ref count);
+ bucket.LastAttemptAt = DateTimeOffset.UtcNow;
- // Start bucket cleaner if not already running.
- if (!this._cleanerRunning)
- {
- this._cleanerRunning = true;
- this._bucketCleanerTokenSource = new CancellationTokenSource();
- this._cleanerTask = Task.Run(this.CleanupBucketsAsync, this._bucketCleanerTokenSource.Token);
- this._logger.LogDebug(LoggerEvents.RestCleaner, "Bucket cleaner task started.");
- }
+ // Cache the routes for each bucket so it can be used for GC later.
+ if (!bucket.RouteHashes.Contains(bucketId))
+ bucket.RouteHashes.Add(bucketId);
- url = s_routeArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]);
- return bucket;
- }
+ // Add the current route to the request queue, which indexes the amount
+ // of requests occurring to the bucket id.
+ _ = this._requestQueue.TryGetValue(bucketId, out var count);
- /// <summary>
- /// Executes the request.
- /// </summary>
- /// <param name="request">The request to be executed.</param>
- public Task ExecuteRequestAsync(BaseRestRequest request)
- => request == null ? throw new ArgumentNullException(nameof(request)) : this.ExecuteRequestAsync(request, null, null);
+ // Increment by one atomically due to concurrency
+ this._requestQueue[bucketId] = Interlocked.Increment(ref count);
- /// <summary>
- /// Executes the request.
- /// This is to allow proper rescheduling of the first request from a bucket.
- /// </summary>
- /// <param name="request">The request to be executed.</param>
- /// <param name="bucket">The bucket.</param>
- /// <param name="ratelimitTcs">The ratelimit task completion source.</param>
- private async Task ExecuteRequestAsync(BaseRestRequest request, RateLimitBucket bucket, TaskCompletionSource<bool> ratelimitTcs)
- {
- if (this._disposed)
- return;
+ // Start bucket cleaner if not already running.
+ if (!this._cleanerRunning)
+ {
+ this._cleanerRunning = true;
+ this._bucketCleanerTokenSource = new CancellationTokenSource();
+ this._cleanerTask = Task.Run(this.CleanupBucketsAsync, this._bucketCleanerTokenSource.Token);
+ this._logger.LogDebug(LoggerEvents.RestCleaner, "Bucket cleaner task started.");
+ }
- HttpResponseMessage res = default;
+ url = s_routeArgumentRegex.Replace(route, xm => rparams[xm.Groups[1].Value]);
+ return bucket;
+ }
- try
+ /// <summary>
+ /// Executes the request.
+ /// </summary>
+ /// <param name="request">The request to be executed.</param>
+ public Task ExecuteRequestAsync(BaseRestRequest request)
+ => request == null ? throw new ArgumentNullException(nameof(request)) : this.ExecuteRequestAsync(request, null, null);
+
+ /// <summary>
+ /// Executes the request.
+ /// This is to allow proper rescheduling of the first request from a bucket.
+ /// </summary>
+ /// <param name="request">The request to be executed.</param>
+ /// <param name="bucket">The bucket.</param>
+ /// <param name="ratelimitTcs">The ratelimit task completion source.</param>
+ private async Task ExecuteRequestAsync(BaseRestRequest request, RateLimitBucket bucket, TaskCompletionSource<bool> ratelimitTcs)
{
- await this._globalRateLimitEvent.WaitAsync().ConfigureAwait(false);
-
- if (bucket == null)
- bucket = request.RateLimitBucket;
+ if (this._disposed)
+ return;
- if (ratelimitTcs == null)
- ratelimitTcs = await this.WaitForInitialRateLimit(bucket).ConfigureAwait(false);
+ HttpResponseMessage res = default;
- if (ratelimitTcs == null) // ckeck rate limit only if we are not the probe request
+ try
{
- var now = DateTimeOffset.UtcNow;
+ await this._globalRateLimitEvent.WaitAsync().ConfigureAwait(false);
+
+ if (bucket == null)
+ bucket = request.RateLimitBucket;
- await bucket.TryResetLimitAsync(now).ConfigureAwait(false);
+ if (ratelimitTcs == null)
+ ratelimitTcs = await this.WaitForInitialRateLimit(bucket).ConfigureAwait(false);
- // Decrement the remaining number of requests as there can be other concurrent requests before this one finishes and has a chance to update the bucket
- if (Interlocked.Decrement(ref bucket.RemainingInternal) < 0)
+ if (ratelimitTcs == null) // ckeck rate limit only if we are not the probe request
{
- this._logger.LogDebug(LoggerEvents.RatelimitDiag, "Request for {0} is blocked", bucket.ToString());
- var delay = bucket.Reset - now;
- var resetDate = bucket.Reset;
+ var now = DateTimeOffset.UtcNow;
- if (this._useResetAfter)
- {
- delay = bucket.ResetAfter.Value;
- resetDate = bucket.ResetAfterOffset;
- }
+ await bucket.TryResetLimitAsync(now).ConfigureAwait(false);
- if (delay < new TimeSpan(-TimeSpan.TicksPerMinute))
+ // Decrement the remaining number of requests as there can be other concurrent requests before this one finishes and has a chance to update the bucket
+ if (Interlocked.Decrement(ref bucket.RemainingInternal) < 0)
{
- this._logger.LogError(LoggerEvents.RatelimitDiag, "Failed to retrieve ratelimits - giving up and allowing next request for bucket");
- bucket.RemainingInternal = 1;
- }
-
- if (delay < TimeSpan.Zero)
- delay = TimeSpan.FromMilliseconds(100);
-
- this._logger.LogWarning(LoggerEvents.RatelimitPreemptive, "Preemptive ratelimit triggered - waiting until {0:yyyy-MM-dd HH:mm:ss zzz} ({1:c}).", resetDate, delay);
- Task.Delay(delay)
- .ContinueWith(_ => this.ExecuteRequestAsync(request, null, null))
- .LogTaskFault(this._logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
-
- return;
- }
- this._logger.LogDebug(LoggerEvents.RatelimitDiag, "Request for {0} is allowed", bucket.ToString());
- }
- else
- this._logger.LogDebug(LoggerEvents.RatelimitDiag, "Initial request for {0} is allowed", bucket.ToString());
+ this._logger.LogDebug(LoggerEvents.RatelimitDiag, "Request for {0} is blocked", bucket.ToString());
+ var delay = bucket.Reset - now;
+ var resetDate = bucket.Reset;
- var req = this.BuildRequest(request);
+ if (this._useResetAfter)
+ {
+ delay = bucket.ResetAfter.Value;
+ resetDate = bucket.ResetAfterOffset;
+ }
- if (this.Debug)
- this._logger.LogTrace(LoggerEvents.Misc, await req.Content.ReadAsStringAsync());
+ if (delay < new TimeSpan(-TimeSpan.TicksPerMinute))
+ {
+ this._logger.LogError(LoggerEvents.RatelimitDiag, "Failed to retrieve ratelimits - giving up and allowing next request for bucket");
+ bucket.RemainingInternal = 1;
+ }
- var response = new RestResponse();
- try
- {
- if (this._disposed)
- return;
+ if (delay < TimeSpan.Zero)
+ delay = TimeSpan.FromMilliseconds(100);
- res = await this.HttpClient.SendAsync(req, HttpCompletionOption.ResponseContentRead, CancellationToken.None).ConfigureAwait(false);
+ this._logger.LogWarning(LoggerEvents.RatelimitPreemptive, "Preemptive ratelimit triggered - waiting until {0:yyyy-MM-dd HH:mm:ss zzz} ({1:c}).", resetDate, delay);
+ Task.Delay(delay)
+ .ContinueWith(_ => this.ExecuteRequestAsync(request, null, null))
+ .LogTaskFault(this._logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
- var bts = await res.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
- var txt = Utilities.UTF8.GetString(bts, 0, bts.Length);
+ return;
+ }
+ this._logger.LogDebug(LoggerEvents.RatelimitDiag, "Request for {0} is allowed", bucket.ToString());
+ }
+ else
+ this._logger.LogDebug(LoggerEvents.RatelimitDiag, "Initial request for {0} is allowed", bucket.ToString());
- this._logger.LogTrace(LoggerEvents.RestRx, txt);
+ var req = this.BuildRequest(request);
- response.Headers = res.Headers.ToDictionary(xh => xh.Key, xh => string.Join("\n", xh.Value), StringComparer.OrdinalIgnoreCase);
- response.Response = txt;
- response.ResponseCode = (int)res.StatusCode;
- }
- catch (HttpRequestException httpex)
- {
- this._logger.LogError(LoggerEvents.RestError, httpex, "Request to {0} triggered an HttpException", request.Url);
- request.SetFaulted(httpex);
- this.FailInitialRateLimitTest(request, ratelimitTcs);
- return;
- }
+ if (this.Debug)
+ this._logger.LogTrace(LoggerEvents.Misc, await req.Content.ReadAsStringAsync());
- this.UpdateBucket(request, response, ratelimitTcs);
+ var response = new RestResponse();
+ try
+ {
+ if (this._disposed)
+ return;
- Exception ex = null;
- switch (response.ResponseCode)
- {
- case 400:
- case 405:
- ex = new BadRequestException(request, response);
- break;
+ res = await this.HttpClient.SendAsync(req, HttpCompletionOption.ResponseContentRead, CancellationToken.None).ConfigureAwait(false);
- case 401:
- case 403:
- ex = new UnauthorizedException(request, response);
- break;
+ var bts = await res.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
+ var txt = Utilities.UTF8.GetString(bts, 0, bts.Length);
- case 404:
- ex = new NotFoundException(request, response);
- break;
+ this._logger.LogTrace(LoggerEvents.RestRx, txt);
- case 413:
- ex = new RequestSizeException(request, response);
- break;
+ response.Headers = res.Headers.ToDictionary(xh => xh.Key, xh => string.Join("\n", xh.Value), StringComparer.OrdinalIgnoreCase);
+ response.Response = txt;
+ response.ResponseCode = (int)res.StatusCode;
+ }
+ catch (HttpRequestException httpex)
+ {
+ this._logger.LogError(LoggerEvents.RestError, httpex, "Request to {0} triggered an HttpException", request.Url);
+ request.SetFaulted(httpex);
+ this.FailInitialRateLimitTest(request, ratelimitTcs);
+ return;
+ }
- case 429:
- ex = new RateLimitException(request, response);
+ this.UpdateBucket(request, response, ratelimitTcs);
- // check the limit info and requeue
- this.Handle429(response, out var wait, out var global);
- if (wait != null)
- {
- if (global)
+ Exception ex = null;
+ switch (response.ResponseCode)
+ {
+ case 400:
+ case 405:
+ ex = new BadRequestException(request, response);
+ break;
+
+ case 401:
+ case 403:
+ ex = new UnauthorizedException(request, response);
+ break;
+
+ case 404:
+ ex = new NotFoundException(request, response);
+ break;
+
+ case 413:
+ ex = new RequestSizeException(request, response);
+ break;
+
+ case 429:
+ ex = new RateLimitException(request, response);
+
+ // check the limit info and requeue
+ this.Handle429(response, out var wait, out var global);
+ if (wait != null)
{
- bucket.IsGlobal = true;
- this._logger.LogError(LoggerEvents.RatelimitHit, "Global ratelimit hit, cooling down");
- try
- {
- this._globalRateLimitEvent.Reset();
- await wait.ConfigureAwait(false);
- }
- finally
+ if (global)
{
- // we don't want to wait here until all the blocked requests have been run, additionally Set can never throw an exception that could be suppressed here
- _ = this._globalRateLimitEvent.SetAsync();
+ bucket.IsGlobal = true;
+ this._logger.LogError(LoggerEvents.RatelimitHit, "Global ratelimit hit, cooling down");
+ try
+ {
+ this._globalRateLimitEvent.Reset();
+ await wait.ConfigureAwait(false);
+ }
+ finally
+ {
+ // we don't want to wait here until all the blocked requests have been run, additionally Set can never throw an exception that could be suppressed here
+ _ = this._globalRateLimitEvent.SetAsync();
+ }
+ this.ExecuteRequestAsync(request, bucket, ratelimitTcs)
+ .LogTaskFault(this._logger, LogLevel.Error, LoggerEvents.RestError, "Error while retrying request");
}
- this.ExecuteRequestAsync(request, bucket, ratelimitTcs)
- .LogTaskFault(this._logger, LogLevel.Error, LoggerEvents.RestError, "Error while retrying request");
- }
- else
- {
- if (this._discord is DiscordClient)
+ else
{
- await (this._discord as DiscordClient)._rateLimitHit.InvokeAsync(this._discord as DiscordClient, new EventArgs.RateLimitExceptionEventArgs(this._discord.ServiceProvider)
+ if (this._discord is DiscordClient)
{
- Exception = ex as RateLimitException,
- ApiEndpoint = request.Url.AbsoluteUri
- });
+ await (this._discord as DiscordClient)._rateLimitHit.InvokeAsync(this._discord as DiscordClient, new EventArgs.RateLimitExceptionEventArgs(this._discord.ServiceProvider)
+ {
+ Exception = ex as RateLimitException,
+ ApiEndpoint = request.Url.AbsoluteUri
+ });
+ }
+ this._logger.LogError(LoggerEvents.RatelimitHit, "Ratelimit hit, requeuing request to {0}", request.Url);
+ await wait.ConfigureAwait(false);
+ this.ExecuteRequestAsync(request, bucket, ratelimitTcs)
+ .LogTaskFault(this._logger, LogLevel.Error, LoggerEvents.RestError, "Error while retrying request");
}
- this._logger.LogError(LoggerEvents.RatelimitHit, "Ratelimit hit, requeuing request to {0}", request.Url);
- await wait.ConfigureAwait(false);
- this.ExecuteRequestAsync(request, bucket, ratelimitTcs)
- .LogTaskFault(this._logger, LogLevel.Error, LoggerEvents.RestError, "Error while retrying request");
- }
- return;
- }
- break;
+ return;
+ }
+ break;
+
+ case 500:
+ case 502:
+ case 503:
+ case 504:
+ ex = new ServerErrorException(request, response);
+ break;
+ }
- case 500:
- case 502:
- case 503:
- case 504:
- ex = new ServerErrorException(request, response);
- break;
+ if (ex != null)
+ request.SetFaulted(ex);
+ else
+ request.SetCompleted(response);
}
+ catch (Exception ex)
+ {
+ this._logger.LogError(LoggerEvents.RestError, ex, "Request to {0} triggered an exception", request.Url);
- if (ex != null)
- request.SetFaulted(ex);
- else
- request.SetCompleted(response);
- }
- catch (Exception ex)
- {
- this._logger.LogError(LoggerEvents.RestError, ex, "Request to {0} triggered an exception", request.Url);
-
- // if something went wrong and we couldn't get rate limits for the first request here, allow the next request to run
- if (bucket != null && ratelimitTcs != null && bucket.LimitTesting != 0)
- this.FailInitialRateLimitTest(request, ratelimitTcs);
+ // if something went wrong and we couldn't get rate limits for the first request here, allow the next request to run
+ if (bucket != null && ratelimitTcs != null && bucket.LimitTesting != 0)
+ this.FailInitialRateLimitTest(request, ratelimitTcs);
- if (!request.TrySetFaulted(ex))
- throw;
- }
- finally
- {
- res?.Dispose();
+ if (!request.TrySetFaulted(ex))
+ throw;
+ }
+ finally
+ {
+ res?.Dispose();
- // Get and decrement active requests in this bucket by 1.
- _ = this._requestQueue.TryGetValue(bucket.BucketId, out var count);
- this._requestQueue[bucket.BucketId] = Interlocked.Decrement(ref count);
+ // Get and decrement active requests in this bucket by 1.
+ _ = this._requestQueue.TryGetValue(bucket.BucketId, out var count);
+ this._requestQueue[bucket.BucketId] = Interlocked.Decrement(ref count);
- // If it's 0 or less, we can remove the bucket from the active request queue,
- // along with any of its past routes.
- if (count <= 0)
- {
- foreach (var r in bucket.RouteHashes)
+ // If it's 0 or less, we can remove the bucket from the active request queue,
+ // along with any of its past routes.
+ if (count <= 0)
{
- if (this._requestQueue.ContainsKey(r))
+ foreach (var r in bucket.RouteHashes)
{
- _ = this._requestQueue.TryRemove(r, out _);
+ if (this._requestQueue.ContainsKey(r))
+ {
+ _ = this._requestQueue.TryRemove(r, out _);
+ }
}
}
}
}
- }
- /// <summary>
- /// Fails the initial rate limit test.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="ratelimitTcs">The ratelimit task completion source.</param>
- /// <param name="resetToInitial">Whether to reset to initial values.</param>
- private void FailInitialRateLimitTest(BaseRestRequest request, TaskCompletionSource<bool> ratelimitTcs, bool resetToInitial = false)
- {
- if (ratelimitTcs == null && !resetToInitial)
- return;
+ /// <summary>
+ /// Fails the initial rate limit test.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="ratelimitTcs">The ratelimit task completion source.</param>
+ /// <param name="resetToInitial">Whether to reset to initial values.</param>
+ private void FailInitialRateLimitTest(BaseRestRequest request, TaskCompletionSource<bool> ratelimitTcs, bool resetToInitial = false)
+ {
+ if (ratelimitTcs == null && !resetToInitial)
+ return;
- var bucket = request.RateLimitBucket;
+ var bucket = request.RateLimitBucket;
- bucket.LimitValid = false;
- bucket.LimitTestFinished = null;
- bucket.LimitTesting = 0;
+ bucket.LimitValid = false;
+ bucket.LimitTestFinished = null;
+ bucket.LimitTesting = 0;
- //Reset to initial values.
- if (resetToInitial)
- {
- this.UpdateHashCaches(request, bucket);
- bucket.Maximum = 0;
- bucket.RemainingInternal = 0;
- return;
- }
+ //Reset to initial values.
+ if (resetToInitial)
+ {
+ this.UpdateHashCaches(request, bucket);
+ bucket.Maximum = 0;
+ bucket.RemainingInternal = 0;
+ return;
+ }
- // no need to wait on all the potentially waiting tasks
- _ = Task.Run(() => ratelimitTcs.TrySetResult(false));
- }
+ // no need to wait on all the potentially waiting tasks
+ _ = Task.Run(() => ratelimitTcs.TrySetResult(false));
+ }
- /// <summary>
- /// Waits for the initial rate limit.
- /// </summary>
- /// <param name="bucket">The bucket.</param>
- private async Task<TaskCompletionSource<bool>> WaitForInitialRateLimit(RateLimitBucket bucket)
- {
- while (!bucket.LimitValid)
+ /// <summary>
+ /// Waits for the initial rate limit.
+ /// </summary>
+ /// <param name="bucket">The bucket.</param>
+ private async Task<TaskCompletionSource<bool>> WaitForInitialRateLimit(RateLimitBucket bucket)
{
- if (bucket.LimitTesting == 0)
+ while (!bucket.LimitValid)
{
- if (Interlocked.CompareExchange(ref bucket.LimitTesting, 1, 0) == 0)
+ if (bucket.LimitTesting == 0)
{
- // if we got here when the first request was just finishing, we must not create the waiter task as it would signal ExecureRequestAsync to bypass rate limiting
- if (bucket.LimitValid)
- return null;
-
- // allow exactly one request to go through without having rate limits available
- var ratelimitsTcs = new TaskCompletionSource<bool>();
- bucket.LimitTestFinished = ratelimitsTcs.Task;
- return ratelimitsTcs;
+ if (Interlocked.CompareExchange(ref bucket.LimitTesting, 1, 0) == 0)
+ {
+ // if we got here when the first request was just finishing, we must not create the waiter task as it would signal ExecureRequestAsync to bypass rate limiting
+ if (bucket.LimitValid)
+ return null;
+
+ // allow exactly one request to go through without having rate limits available
+ var ratelimitsTcs = new TaskCompletionSource<bool>();
+ bucket.LimitTestFinished = ratelimitsTcs.Task;
+ return ratelimitsTcs;
+ }
}
+ // it can take a couple of cycles for the task to be allocated, so wait until it happens or we are no longer probing for the limits
+ Task waitTask = null;
+ while (bucket.LimitTesting != 0 && (waitTask = bucket.LimitTestFinished) == null)
+ await Task.Yield();
+ if (waitTask != null)
+ await waitTask.ConfigureAwait(false);
+
+ // if the request failed and the response did not have rate limit headers we have allow the next request and wait again, thus this is a loop here
}
- // it can take a couple of cycles for the task to be allocated, so wait until it happens or we are no longer probing for the limits
- Task waitTask = null;
- while (bucket.LimitTesting != 0 && (waitTask = bucket.LimitTestFinished) == null)
- await Task.Yield();
- if (waitTask != null)
- await waitTask.ConfigureAwait(false);
-
- // if the request failed and the response did not have rate limit headers we have allow the next request and wait again, thus this is a loop here
+ return null;
}
- return null;
- }
-
- /// <summary>
- /// Builds the request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>A http request message.</returns>
- private HttpRequestMessage BuildRequest(BaseRestRequest request)
- {
- var req = new HttpRequestMessage(new HttpMethod(request.Method.ToString()), request.Url);
- if (request.Headers != null && request.Headers.Any())
- foreach (var kvp in request.Headers)
- req.Headers.Add(kvp.Key, kvp.Value);
- if (request is RestRequest nmprequest && !string.IsNullOrWhiteSpace(nmprequest.Payload))
+ /// <summary>
+ /// Builds the request.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>A http request message.</returns>
+ private HttpRequestMessage BuildRequest(BaseRestRequest request)
{
- this._logger.LogTrace(LoggerEvents.RestTx, nmprequest.Payload);
+ var req = new HttpRequestMessage(new HttpMethod(request.Method.ToString()), request.Url);
+ if (request.Headers != null && request.Headers.Any())
+ foreach (var kvp in request.Headers)
+ req.Headers.Add(kvp.Key, kvp.Value);
- req.Content = new StringContent(nmprequest.Payload);
- req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
- }
+ if (request is RestRequest nmprequest && !string.IsNullOrWhiteSpace(nmprequest.Payload))
+ {
+ this._logger.LogTrace(LoggerEvents.RestTx, nmprequest.Payload);
- if (request is MultipartWebRequest mprequest)
- {
- this._logger.LogTrace(LoggerEvents.RestTx, "<multipart request>");
+ req.Content = new StringContent(nmprequest.Payload);
+ req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
+ }
- var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
+ if (request is MultipartWebRequest mprequest)
+ {
+ this._logger.LogTrace(LoggerEvents.RestTx, "<multipart request>");
- req.Headers.Add("Connection", "keep-alive");
- req.Headers.Add("Keep-Alive", "600");
+ var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
- var content = new MultipartFormDataContent(boundary);
- if (mprequest.Values != null && mprequest.Values.Any())
- foreach (var kvp in mprequest.Values)
- content.Add(new StringContent(kvp.Value), kvp.Key);
+ req.Headers.Add("Connection", "keep-alive");
+ req.Headers.Add("Keep-Alive", "600");
- var fileId = mprequest.OverwriteFileIdStart ?? 0;
+ var content = new MultipartFormDataContent(boundary);
+ if (mprequest.Values != null && mprequest.Values.Any())
+ foreach (var kvp in mprequest.Values)
+ content.Add(new StringContent(kvp.Value), kvp.Key);
- if (mprequest.Files != null && mprequest.Files.Any())
- {
- foreach (var f in mprequest.Files)
+ var fileId = mprequest.OverwriteFileIdStart ?? 0;
+
+ if (mprequest.Files != null && mprequest.Files.Any())
{
- var name = $"files[{fileId.ToString(CultureInfo.InvariantCulture)}]";
- content.Add(new StreamContent(f.Value), name, f.Key);
- fileId++;
+ foreach (var f in mprequest.Files)
+ {
+ var name = $"files[{fileId.ToString(CultureInfo.InvariantCulture)}]";
+ content.Add(new StreamContent(f.Value), name, f.Key);
+ fileId++;
+ }
}
- }
- req.Content = content;
- }
+ req.Content = content;
+ }
- if (request is MultipartStickerWebRequest mpsrequest)
- {
- this._logger.LogTrace(LoggerEvents.RestTx, "<multipart request>");
+ if (request is MultipartStickerWebRequest mpsrequest)
+ {
+ this._logger.LogTrace(LoggerEvents.RestTx, "<multipart request>");
- var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
+ var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
- req.Headers.Add("Connection", "keep-alive");
- req.Headers.Add("Keep-Alive", "600");
+ req.Headers.Add("Connection", "keep-alive");
+ req.Headers.Add("Keep-Alive", "600");
- var sc = new StreamContent(mpsrequest.File.Stream);
+ var sc = new StreamContent(mpsrequest.File.Stream);
- if (mpsrequest.File.ContentType != null)
- sc.Headers.ContentType = new MediaTypeHeaderValue(mpsrequest.File.ContentType);
+ if (mpsrequest.File.ContentType != null)
+ sc.Headers.ContentType = new MediaTypeHeaderValue(mpsrequest.File.ContentType);
- var fileName = mpsrequest.File.FileName;
+ var fileName = mpsrequest.File.FileName;
- if (mpsrequest.File.FileType != null)
- fileName += '.' + mpsrequest.File.FileType;
+ if (mpsrequest.File.FileType != null)
+ fileName += '.' + mpsrequest.File.FileType;
- var content = new MultipartFormDataContent(boundary)
- {
- { new StringContent(mpsrequest.Name), "name" },
- { new StringContent(mpsrequest.Tags), "tags" },
- { new StringContent(mpsrequest.Description), "description" },
- { sc, "file", fileName }
- };
+ var content = new MultipartFormDataContent(boundary)
+ {
+ { new StringContent(mpsrequest.Name), "name" },
+ { new StringContent(mpsrequest.Tags), "tags" },
+ { new StringContent(mpsrequest.Description), "description" },
+ { sc, "file", fileName }
+ };
- req.Content = content;
+ req.Content = content;
+ }
+ return req;
}
- return req;
- }
-
- /// <summary>
- /// Handles the HTTP 429 status.
- /// </summary>
- /// <param name="response">The response.</param>
- /// <param name="waitTask">The wait task.</param>
- /// <param name="global">If true, global.</param>
- private void Handle429(RestResponse response, out Task waitTask, out bool global)
- {
- waitTask = null;
- global = false;
- if (response.Headers == null)
- return;
- var hs = response.Headers;
-
- // handle the wait
- if (hs.TryGetValue("Retry-After", out var retryAfterRaw))
+ /// <summary>
+ /// Handles the HTTP 429 status.
+ /// </summary>
+ /// <param name="response">The response.</param>
+ /// <param name="waitTask">The wait task.</param>
+ /// <param name="global">If true, global.</param>
+ private void Handle429(RestResponse response, out Task waitTask, out bool global)
{
- var retryAfter = TimeSpan.FromSeconds(int.Parse(retryAfterRaw, CultureInfo.InvariantCulture));
- waitTask = Task.Delay(retryAfter);
- }
+ waitTask = null;
+ global = false;
- // check if global b1nzy
- if (hs.TryGetValue("X-RateLimit-Global", out var isGlobal) && isGlobal.ToLowerInvariant() == "true")
- {
- // global
- global = true;
- }
- }
+ if (response.Headers == null)
+ return;
+ var hs = response.Headers;
- /// <summary>
- /// Updates the bucket.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="response">The response.</param>
- /// <param name="ratelimitTcs">The ratelimit task completion source.</param>
- private void UpdateBucket(BaseRestRequest request, RestResponse response, TaskCompletionSource<bool> ratelimitTcs)
- {
- var bucket = request.RateLimitBucket;
+ // handle the wait
+ if (hs.TryGetValue("Retry-After", out var retryAfterRaw))
+ {
+ var retryAfter = TimeSpan.FromSeconds(int.Parse(retryAfterRaw, CultureInfo.InvariantCulture));
+ waitTask = Task.Delay(retryAfter);
+ }
- if (response.Headers == null)
- {
- if (response.ResponseCode != 429) // do not fail when ratelimit was or the next request will be scheduled hitting the rate limit again
- this.FailInitialRateLimitTest(request, ratelimitTcs);
- return;
+ // check if global b1nzy
+ if (hs.TryGetValue("X-RateLimit-Global", out var isGlobal) && isGlobal.ToLowerInvariant() == "true")
+ {
+ // global
+ global = true;
+ }
}
- var hs = response.Headers;
-
- if (hs.TryGetValue("X-RateLimit-Scope", out var scope))
+ /// <summary>
+ /// Updates the bucket.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="response">The response.</param>
+ /// <param name="ratelimitTcs">The ratelimit task completion source.</param>
+ private void UpdateBucket(BaseRestRequest request, RestResponse response, TaskCompletionSource<bool> ratelimitTcs)
{
- bucket.Scope = scope;
- }
-
+ var bucket = request.RateLimitBucket;
- if (hs.TryGetValue("X-RateLimit-Global", out var isGlobal) && isGlobal.ToLowerInvariant() == "true")
- {
- if (response.ResponseCode != 429)
+ if (response.Headers == null)
{
- bucket.IsGlobal = true;
- this.FailInitialRateLimitTest(request, ratelimitTcs);
+ if (response.ResponseCode != 429) // do not fail when ratelimit was or the next request will be scheduled hitting the rate limit again
+ this.FailInitialRateLimitTest(request, ratelimitTcs);
+ return;
}
- return;
- }
-
- var r1 = hs.TryGetValue("X-RateLimit-Limit", out var usesMax);
- var r2 = hs.TryGetValue("X-RateLimit-Remaining", out var usesLeft);
- var r3 = hs.TryGetValue("X-RateLimit-Reset", out var reset);
- var r4 = hs.TryGetValue("X-Ratelimit-Reset-After", out var resetAfter);
- var r5 = hs.TryGetValue("X-Ratelimit-Bucket", out var hash);
-
- if (!r1 || !r2 || !r3 || !r4)
- {
- //If the limits were determined before this request, make the bucket initial again.
- if (response.ResponseCode != 429)
- this.FailInitialRateLimitTest(request, ratelimitTcs, ratelimitTcs == null);
-
- return;
- }
+ var hs = response.Headers;
- var clientTime = DateTimeOffset.UtcNow;
- var resetTime = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).AddSeconds(double.Parse(reset, CultureInfo.InvariantCulture));
- var serverTime = clientTime;
- if (hs.TryGetValue("Date", out var rawDate))
- serverTime = DateTimeOffset.Parse(rawDate, CultureInfo.InvariantCulture).ToUniversalTime();
+ if (hs.TryGetValue("X-RateLimit-Scope", out var scope))
+ {
+ bucket.Scope = scope;
+ }
- var resetDelta = resetTime - serverTime;
- //var difference = clientTime - serverTime;
- //if (Math.Abs(difference.TotalSeconds) >= 1)
- //// this.Logger.LogMessage(LogLevel.DebugBaseDiscordClient.RestEventId, $"Difference between machine and server time: {difference.TotalMilliseconds.ToString("#,##0.00", CultureInfo.InvariantCulture)}ms", DateTime.Now);
- //else
- // difference = TimeSpan.Zero;
- if (request.RateLimitWaitOverride.HasValue)
- resetDelta = TimeSpan.FromSeconds(request.RateLimitWaitOverride.Value);
- var newReset = clientTime + resetDelta;
+ if (hs.TryGetValue("X-RateLimit-Global", out var isGlobal) && isGlobal.ToLowerInvariant() == "true")
+ {
+ if (response.ResponseCode != 429)
+ {
+ bucket.IsGlobal = true;
+ this.FailInitialRateLimitTest(request, ratelimitTcs);
+ }
- if (this._useResetAfter)
- {
- bucket.ResetAfter = TimeSpan.FromSeconds(double.Parse(resetAfter, CultureInfo.InvariantCulture));
- newReset = clientTime + bucket.ResetAfter.Value + (request.RateLimitWaitOverride.HasValue
- ? resetDelta
- : TimeSpan.Zero);
- bucket.ResetAfterOffset = newReset;
- }
- else
- bucket.Reset = newReset;
+ return;
+ }
- var maximum = int.Parse(usesMax, CultureInfo.InvariantCulture);
- var remaining = int.Parse(usesLeft, CultureInfo.InvariantCulture);
+ var r1 = hs.TryGetValue("X-RateLimit-Limit", out var usesMax);
+ var r2 = hs.TryGetValue("X-RateLimit-Remaining", out var usesLeft);
+ var r3 = hs.TryGetValue("X-RateLimit-Reset", out var reset);
+ var r4 = hs.TryGetValue("X-Ratelimit-Reset-After", out var resetAfter);
+ var r5 = hs.TryGetValue("X-Ratelimit-Bucket", out var hash);
- if (ratelimitTcs != null)
- {
- // initial population of the ratelimit data
- bucket.SetInitialValues(maximum, remaining, newReset);
+ if (!r1 || !r2 || !r3 || !r4)
+ {
+ //If the limits were determined before this request, make the bucket initial again.
+ if (response.ResponseCode != 429)
+ this.FailInitialRateLimitTest(request, ratelimitTcs, ratelimitTcs == null);
- _ = Task.Run(() => ratelimitTcs.TrySetResult(true));
- }
- else
- {
- // only update the bucket values if this request was for a newer interval than the one
- // currently in the bucket, to avoid issues with concurrent requests in one bucket
- // remaining is reset by TryResetLimit and not the response, just allow that to happen when it is time
- if (bucket.NextReset == 0)
- bucket.NextReset = newReset.UtcTicks;
- }
+ return;
+ }
- this.UpdateHashCaches(request, bucket, hash);
- }
+ var clientTime = DateTimeOffset.UtcNow;
+ var resetTime = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).AddSeconds(double.Parse(reset, CultureInfo.InvariantCulture));
+ var serverTime = clientTime;
+ if (hs.TryGetValue("Date", out var rawDate))
+ serverTime = DateTimeOffset.Parse(rawDate, CultureInfo.InvariantCulture).ToUniversalTime();
- /// <summary>
- /// Updates the hash caches.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="bucket">The bucket.</param>
- /// <param name="newHash">The new hash.</param>
- private void UpdateHashCaches(BaseRestRequest request, RateLimitBucket bucket, string newHash = null)
- {
- var hashKey = RateLimitBucket.GenerateHashKey(request.Method, request.Route);
+ var resetDelta = resetTime - serverTime;
+ //var difference = clientTime - serverTime;
+ //if (Math.Abs(difference.TotalSeconds) >= 1)
+ //// this.Logger.LogMessage(LogLevel.DebugBaseDiscordClient.RestEventId, $"Difference between machine and server time: {difference.TotalMilliseconds.ToString("#,##0.00", CultureInfo.InvariantCulture)}ms", DateTime.Now);
+ //else
+ // difference = TimeSpan.Zero;
- if (!this._routesToHashes.TryGetValue(hashKey, out var oldHash))
- return;
+ if (request.RateLimitWaitOverride.HasValue)
+ resetDelta = TimeSpan.FromSeconds(request.RateLimitWaitOverride.Value);
+ var newReset = clientTime + resetDelta;
- // This is an unlimited bucket, which we don't need to keep track of.
- if (newHash == null)
- {
- _ = this._routesToHashes.TryRemove(hashKey, out _);
- _ = this._hashesToBuckets.TryRemove(bucket.BucketId, out _);
- return;
- }
+ if (this._useResetAfter)
+ {
+ bucket.ResetAfter = TimeSpan.FromSeconds(double.Parse(resetAfter, CultureInfo.InvariantCulture));
+ newReset = clientTime + bucket.ResetAfter.Value + (request.RateLimitWaitOverride.HasValue
+ ? resetDelta
+ : TimeSpan.Zero);
+ bucket.ResetAfterOffset = newReset;
+ }
+ else
+ bucket.Reset = newReset;
- // Only update the hash once, due to a bug on Discord's end.
- // This will cause issues if the bucket hashes are dynamically changed from the API while running,
- // in which case, Dispose will need to be called to clear the caches.
- if (bucket.IsUnlimited && newHash != oldHash)
- {
- this._logger.LogDebug(LoggerEvents.RestHashMover, "Updating hash in {0}: \"{1}\" -> \"{2}\"", hashKey, oldHash, newHash);
- var bucketId = RateLimitBucket.GenerateBucketId(newHash, bucket.GuildId, bucket.ChannelId, bucket.WebhookId);
+ var maximum = int.Parse(usesMax, CultureInfo.InvariantCulture);
+ var remaining = int.Parse(usesLeft, CultureInfo.InvariantCulture);
- _ = this._routesToHashes.AddOrUpdate(hashKey, newHash, (key, oldHash) =>
+ if (ratelimitTcs != null)
{
- bucket.Hash = newHash;
+ // initial population of the ratelimit data
+ bucket.SetInitialValues(maximum, remaining, newReset);
- var oldBucketId = RateLimitBucket.GenerateBucketId(oldHash, bucket.GuildId, bucket.ChannelId, bucket.WebhookId);
-
- // Remove the old unlimited bucket.
- _ = this._hashesToBuckets.TryRemove(oldBucketId, out _);
- _ = this._hashesToBuckets.AddOrUpdate(bucketId, bucket, (key, oldBucket) => bucket);
+ _ = Task.Run(() => ratelimitTcs.TrySetResult(true));
+ }
+ else
+ {
+ // only update the bucket values if this request was for a newer interval than the one
+ // currently in the bucket, to avoid issues with concurrent requests in one bucket
+ // remaining is reset by TryResetLimit and not the response, just allow that to happen when it is time
+ if (bucket.NextReset == 0)
+ bucket.NextReset = newReset.UtcTicks;
+ }
- return newHash;
- });
+ this.UpdateHashCaches(request, bucket, hash);
}
- return;
- }
-
- /// <summary>
- /// Cleans the buckets.
- /// </summary>
- private async Task CleanupBucketsAsync()
- {
- while (!this._bucketCleanerTokenSource.IsCancellationRequested)
+ /// <summary>
+ /// Updates the hash caches.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <param name="bucket">The bucket.</param>
+ /// <param name="newHash">The new hash.</param>
+ private void UpdateHashCaches(BaseRestRequest request, RateLimitBucket bucket, string newHash = null)
{
- try
- {
- await Task.Delay(this._bucketCleanupDelay, this._bucketCleanerTokenSource.Token).ConfigureAwait(false);
- }
- catch { }
+ var hashKey = RateLimitBucket.GenerateHashKey(request.Method, request.Route);
- if (this._disposed)
+ if (!this._routesToHashes.TryGetValue(hashKey, out var oldHash))
return;
- //Check and clean request queue first in case it wasn't removed properly during requests.
- foreach (var key in this._requestQueue.Keys)
+ // This is an unlimited bucket, which we don't need to keep track of.
+ if (newHash == null)
+ {
+ _ = this._routesToHashes.TryRemove(hashKey, out _);
+ _ = this._hashesToBuckets.TryRemove(bucket.BucketId, out _);
+ return;
+ }
+
+ // Only update the hash once, due to a bug on Discord's end.
+ // This will cause issues if the bucket hashes are dynamically changed from the API while running,
+ // in which case, Dispose will need to be called to clear the caches.
+ if (bucket.IsUnlimited && newHash != oldHash)
{
- var bucket = this._hashesToBuckets.Values.FirstOrDefault(x => x.RouteHashes.Contains(key));
+ this._logger.LogDebug(LoggerEvents.RestHashMover, "Updating hash in {0}: \"{1}\" -> \"{2}\"", hashKey, oldHash, newHash);
+ var bucketId = RateLimitBucket.GenerateBucketId(newHash, bucket.GuildId, bucket.ChannelId, bucket.WebhookId);
+
+ _ = this._routesToHashes.AddOrUpdate(hashKey, newHash, (key, oldHash) =>
+ {
+ bucket.Hash = newHash;
+
+ var oldBucketId = RateLimitBucket.GenerateBucketId(oldHash, bucket.GuildId, bucket.ChannelId, bucket.WebhookId);
+
+ // Remove the old unlimited bucket.
+ _ = this._hashesToBuckets.TryRemove(oldBucketId, out _);
+ _ = this._hashesToBuckets.AddOrUpdate(bucketId, bucket, (key, oldBucket) => bucket);
- if (bucket == null || (bucket != null && bucket.LastAttemptAt.AddSeconds(5) < DateTimeOffset.UtcNow))
- _ = this._requestQueue.TryRemove(key, out _);
+ return newHash;
+ });
}
- var removedBuckets = 0;
- StringBuilder bucketIdStrBuilder = default;
+ return;
+ }
- foreach (var kvp in this._hashesToBuckets)
+ /// <summary>
+ /// Cleans the buckets.
+ /// </summary>
+ private async Task CleanupBucketsAsync()
+ {
+ while (!this._bucketCleanerTokenSource.IsCancellationRequested)
{
- if (bucketIdStrBuilder == null)
- bucketIdStrBuilder = new StringBuilder();
+ try
+ {
+ await Task.Delay(this._bucketCleanupDelay, this._bucketCleanerTokenSource.Token).ConfigureAwait(false);
+ }
+ catch { }
- var key = kvp.Key;
- var value = kvp.Value;
+ if (this._disposed)
+ return;
- // Don't remove the bucket if it's currently being handled by the rest client, unless it's an unlimited bucket.
- if (this._requestQueue.ContainsKey(value.BucketId) && !value.IsUnlimited)
- continue;
+ //Check and clean request queue first in case it wasn't removed properly during requests.
+ foreach (var key in this._requestQueue.Keys)
+ {
+ var bucket = this._hashesToBuckets.Values.FirstOrDefault(x => x.RouteHashes.Contains(key));
- var resetOffset = this._useResetAfter ? value.ResetAfterOffset : value.Reset;
+ if (bucket == null || (bucket != null && bucket.LastAttemptAt.AddSeconds(5) < DateTimeOffset.UtcNow))
+ _ = this._requestQueue.TryRemove(key, out _);
+ }
- // Don't remove the bucket if it's reset date is less than now + the additional wait time, unless it's an unlimited bucket.
- if (!value.IsUnlimited && (resetOffset > DateTimeOffset.UtcNow || DateTimeOffset.UtcNow - resetOffset < this._bucketCleanupDelay))
- continue;
+ var removedBuckets = 0;
+ StringBuilder bucketIdStrBuilder = default;
- _ = this._hashesToBuckets.TryRemove(key, out _);
- removedBuckets++;
- bucketIdStrBuilder.Append(value.BucketId + ", ");
- }
+ foreach (var kvp in this._hashesToBuckets)
+ {
+ if (bucketIdStrBuilder == null)
+ bucketIdStrBuilder = new StringBuilder();
- if (removedBuckets > 0)
- this._logger.LogDebug(LoggerEvents.RestCleaner, "Removed {0} unused bucket{1}: [{2}]", removedBuckets, removedBuckets > 1 ? "s" : string.Empty, bucketIdStrBuilder.ToString().TrimEnd(',', ' '));
+ var key = kvp.Key;
+ var value = kvp.Value;
- if (this._hashesToBuckets.Count == 0)
- break;
- }
+ // Don't remove the bucket if it's currently being handled by the rest client, unless it's an unlimited bucket.
+ if (this._requestQueue.ContainsKey(value.BucketId) && !value.IsUnlimited)
+ continue;
- if (!this._bucketCleanerTokenSource.IsCancellationRequested)
- this._bucketCleanerTokenSource.Cancel();
+ var resetOffset = this._useResetAfter ? value.ResetAfterOffset : value.Reset;
- this._cleanerRunning = false;
- this._logger.LogDebug(LoggerEvents.RestCleaner, "Bucket cleaner task stopped.");
- }
+ // Don't remove the bucket if it's reset date is less than now + the additional wait time, unless it's an unlimited bucket.
+ if (!value.IsUnlimited && (resetOffset > DateTimeOffset.UtcNow || DateTimeOffset.UtcNow - resetOffset < this._bucketCleanupDelay))
+ continue;
- ~RestClient()
- {
- this.Dispose();
- }
+ _ = this._hashesToBuckets.TryRemove(key, out _);
+ removedBuckets++;
+ bucketIdStrBuilder.Append(value.BucketId + ", ");
+ }
- /// <summary>
- /// Disposes the rest client.
- /// </summary>
- public void Dispose()
- {
- if (this._disposed)
- return;
+ if (removedBuckets > 0)
+ this._logger.LogDebug(LoggerEvents.RestCleaner, "Removed {0} unused bucket{1}: [{2}]", removedBuckets, removedBuckets > 1 ? "s" : string.Empty, bucketIdStrBuilder.ToString().TrimEnd(',', ' '));
- this._disposed = true;
+ if (this._hashesToBuckets.Count == 0)
+ break;
+ }
- this._globalRateLimitEvent.Reset();
+ if (!this._bucketCleanerTokenSource.IsCancellationRequested)
+ this._bucketCleanerTokenSource.Cancel();
- if (this._bucketCleanerTokenSource?.IsCancellationRequested == false)
- {
- this._bucketCleanerTokenSource?.Cancel();
+ this._cleanerRunning = false;
this._logger.LogDebug(LoggerEvents.RestCleaner, "Bucket cleaner task stopped.");
}
- try
+ ~RestClient()
{
- this._cleanerTask?.Dispose();
- this._bucketCleanerTokenSource?.Dispose();
- this.HttpClient?.Dispose();
+ this.Dispose();
}
- catch { }
- this._routesToHashes.Clear();
- this._hashesToBuckets.Clear();
- this._requestQueue.Clear();
+ /// <summary>
+ /// Disposes the rest client.
+ /// </summary>
+ public void Dispose()
+ {
+ if (this._disposed)
+ return;
+
+ this._disposed = true;
+
+ this._globalRateLimitEvent.Reset();
+
+ if (this._bucketCleanerTokenSource?.IsCancellationRequested == false)
+ {
+ this._bucketCleanerTokenSource?.Cancel();
+ this._logger.LogDebug(LoggerEvents.RestCleaner, "Bucket cleaner task stopped.");
+ }
+
+ try
+ {
+ this._cleanerTask?.Dispose();
+ this._bucketCleanerTokenSource?.Dispose();
+ this.HttpClient?.Dispose();
+ }
+ catch { }
+
+ this._routesToHashes.Clear();
+ this._hashesToBuckets.Clear();
+ this._requestQueue.Clear();
+ }
}
}
diff --git a/DisCatSharp/Net/Rest/RestRequest.cs b/DisCatSharp/Net/Rest/RestRequest.cs
index d75963779..581d6d667 100644
--- a/DisCatSharp/Net/Rest/RestRequest.cs
+++ b/DisCatSharp/Net/Rest/RestRequest.cs
@@ -1,54 +1,55 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents a non-multipart HTTP request.
-/// </summary>
-internal sealed class RestRequest : BaseRestRequest
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets the payload sent with this request.
+ /// Represents a non-multipart HTTP request.
/// </summary>
- public string Payload { get; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RestRequest"/> class.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <param name="bucket">The bucket.</param>
- /// <param name="url">The url.</param>
- /// <param name="method">The method.</param>
- /// <param name="route">The route.</param>
- /// <param name="headers">The headers.</param>
- /// <param name="payload">The payload.</param>
- /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
- internal RestRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, string payload = null, double? ratelimitWaitOverride = null)
- : base(client, bucket, url, method, route, headers, ratelimitWaitOverride)
+ internal sealed class RestRequest : BaseRestRequest
{
- this.Payload = payload;
+ /// <summary>
+ /// Gets the payload sent with this request.
+ /// </summary>
+ public string Payload { get; }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RestRequest"/> class.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <param name="bucket">The bucket.</param>
+ /// <param name="url">The url.</param>
+ /// <param name="method">The method.</param>
+ /// <param name="route">The route.</param>
+ /// <param name="headers">The headers.</param>
+ /// <param name="payload">The payload.</param>
+ /// <param name="ratelimitWaitOverride">The ratelimit wait override.</param>
+ internal RestRequest(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary<string, string> headers = null, string payload = null, double? ratelimitWaitOverride = null)
+ : base(client, bucket, url, method, route, headers, ratelimitWaitOverride)
+ {
+ this.Payload = payload;
+ }
}
}
diff --git a/DisCatSharp/Net/Rest/RestRequestMethod.cs b/DisCatSharp/Net/Rest/RestRequestMethod.cs
index 4fd2cb679..415d9a315 100644
--- a/DisCatSharp/Net/Rest/RestRequestMethod.cs
+++ b/DisCatSharp/Net/Rest/RestRequestMethod.cs
@@ -1,60 +1,61 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.
// ReSharper disable InconsistentNaming
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Defines the HTTP method to use for an HTTP request.
-/// </summary>
-public enum RestRequestMethod : int
+namespace DisCatSharp.Net
{
/// <summary>
- /// Defines that the request is a GET request.
+ /// Defines the HTTP method to use for an HTTP request.
/// </summary>
- GET = 0,
+ public enum RestRequestMethod : int
+ {
+ /// <summary>
+ /// Defines that the request is a GET request.
+ /// </summary>
+ GET = 0,
- /// <summary>
- /// Defines that the request is a POST request.
- /// </summary>
- POST = 1,
+ /// <summary>
+ /// Defines that the request is a POST request.
+ /// </summary>
+ POST = 1,
- /// <summary>
- /// Defines that the request is a DELETE request.
- /// </summary>
- DELETE = 2,
+ /// <summary>
+ /// Defines that the request is a DELETE request.
+ /// </summary>
+ DELETE = 2,
- /// <summary>
- /// Defines that the request is a PATCH request.
- /// </summary>
- PATCH = 3,
+ /// <summary>
+ /// Defines that the request is a PATCH request.
+ /// </summary>
+ PATCH = 3,
- /// <summary>
- /// Defines that the request is a PUT request.
- /// </summary>
- PUT = 4,
+ /// <summary>
+ /// Defines that the request is a PUT request.
+ /// </summary>
+ PUT = 4,
- /// <summary>
- /// Defines that the request is a HEAD request.
- /// </summary>
- HEAD = 5
+ /// <summary>
+ /// Defines that the request is a HEAD request.
+ /// </summary>
+ HEAD = 5
+ }
}
diff --git a/DisCatSharp/Net/Rest/RestResponse.cs b/DisCatSharp/Net/Rest/RestResponse.cs
index f2f621bd3..063c642b8 100644
--- a/DisCatSharp/Net/Rest/RestResponse.cs
+++ b/DisCatSharp/Net/Rest/RestResponse.cs
@@ -1,51 +1,52 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections.Generic;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents a response sent by the remote HTTP party.
-/// </summary>
-public sealed class RestResponse
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets the response code sent by the remote party.
+ /// Represents a response sent by the remote HTTP party.
/// </summary>
- public int ResponseCode { get; internal set; }
+ public sealed class RestResponse
+ {
+ /// <summary>
+ /// Gets the response code sent by the remote party.
+ /// </summary>
+ public int ResponseCode { get; internal set; }
- /// <summary>
- /// Gets the headers sent by the remote party.
- /// </summary>
- public IReadOnlyDictionary<string, string> Headers { get; internal set; }
+ /// <summary>
+ /// Gets the headers sent by the remote party.
+ /// </summary>
+ public IReadOnlyDictionary<string, string> Headers { get; internal set; }
- /// <summary>
- /// Gets the contents of the response sent by the remote party.
- /// </summary>
- public string Response { get; internal set; }
+ /// <summary>
+ /// Gets the contents of the response sent by the remote party.
+ /// </summary>
+ public string Response { get; internal set; }
- /// <summary>
- /// Initializes a new instance of the <see cref="RestResponse"/> class.
- /// </summary>
- internal RestResponse() { }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RestResponse"/> class.
+ /// </summary>
+ internal RestResponse() { }
+ }
}
diff --git a/DisCatSharp/Net/Rest/SessionBucket.cs b/DisCatSharp/Net/Rest/SessionBucket.cs
index 3da82e727..74b9e92a5 100644
--- a/DisCatSharp/Net/Rest/SessionBucket.cs
+++ b/DisCatSharp/Net/Rest/SessionBucket.cs
@@ -1,71 +1,72 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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 Newtonsoft.Json;
-namespace DisCatSharp.Net;
-
-/// <summary>
-/// Represents the bucket limits for identifying to Discord.
-/// <para>This is only relevant for clients that are manually sharding.</para>
-/// </summary>
-public class SessionBucket
+namespace DisCatSharp.Net
{
/// <summary>
- /// Gets the total amount of sessions per token.
+ /// Represents the bucket limits for identifying to Discord.
+ /// <para>This is only relevant for clients that are manually sharding.</para>
/// </summary>
- [JsonProperty("total")]
- public int Total { get; internal set; }
+ public class SessionBucket
+ {
+ /// <summary>
+ /// Gets the total amount of sessions per token.
+ /// </summary>
+ [JsonProperty("total")]
+ public int Total { get; internal set; }
- /// <summary>
- /// Gets the remaining amount of sessions for this token.
- /// </summary>
- [JsonProperty("remaining")]
- public int Remaining { get; internal set; }
+ /// <summary>
+ /// Gets the remaining amount of sessions for this token.
+ /// </summary>
+ [JsonProperty("remaining")]
+ public int Remaining { get; internal set; }
- /// <summary>
- /// Gets the datetime when the <see cref="Remaining"/> will reset.
- /// </summary>
- [JsonIgnore]
- public DateTimeOffset ResetAfter { get; internal set; }
+ /// <summary>
+ /// Gets the datetime when the <see cref="Remaining"/> will reset.
+ /// </summary>
+ [JsonIgnore]
+ public DateTimeOffset ResetAfter { get; internal set; }
- /// <summary>
- /// Gets the maximum amount of shards that can boot concurrently.
- /// </summary>
- [JsonProperty("max_concurrency")]
- public int MaxConcurrency { get; internal set; }
+ /// <summary>
+ /// Gets the maximum amount of shards that can boot concurrently.
+ /// </summary>
+ [JsonProperty("max_concurrency")]
+ public int MaxConcurrency { get; internal set; }
- /// <summary>
- /// Gets the reset after value.
- /// </summary>
- [JsonProperty("reset_after")]
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
- internal int ResetAfterInternal { get; set; }
+ /// <summary>
+ /// Gets the reset after value.
+ /// </summary>
+ [JsonProperty("reset_after")]
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
+ internal int ResetAfterInternal { get; set; }
- /// <summary>
- /// Returns a readable session bucket string.
- /// </summary>
- public override string ToString()
- => $"[{this.Remaining}/{this.Total}] {this.ResetAfter}. {this.MaxConcurrency}x concurrency";
+ /// <summary>
+ /// Returns a readable session bucket string.
+ /// </summary>
+ public override string ToString()
+ => $"[{this.Remaining}/{this.Total}] {this.ResetAfter}. {this.MaxConcurrency}x concurrency";
+ }
}
diff --git a/DisCatSharp/Net/Serialization/DiscordComponentJsonConverter.cs b/DisCatSharp/Net/Serialization/DiscordComponentJsonConverter.cs
index 4073a234e..0596576bf 100644
--- a/DisCatSharp/Net/Serialization/DiscordComponentJsonConverter.cs
+++ b/DisCatSharp/Net/Serialization/DiscordComponentJsonConverter.cs
@@ -1,92 +1,93 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Net.Serialization;
-
-/// <summary>
-/// Represents a discord component json converter.
-/// </summary>
-internal sealed class DiscordComponentJsonConverter : JsonConverter
+namespace DisCatSharp.Net.Serialization
{
/// <summary>
- /// Whether the converter can write.
+ /// Represents a discord component json converter.
/// </summary>
- public override bool CanWrite => false;
+ internal sealed class DiscordComponentJsonConverter : JsonConverter
+ {
+ /// <summary>
+ /// Whether the converter can write.
+ /// </summary>
+ public override bool CanWrite => false;
- /// <summary>
- /// Writes the json.
- /// </summary>
- /// <param name="writer">The writer.</param>
- /// <param name="value">The value.</param>
- /// <param name="serializer">The serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
+ /// <summary>
+ /// Writes the json.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="value">The value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
- /// <summary>
- /// Reads the json.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="objectType">The object type.</param>
- /// <param name="existingValue">The existing value.</param>
- /// <param name="serializer">The serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- if (reader.TokenType == JsonToken.Null)
- return null;
+ /// <summary>
+ /// Reads the json.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="objectType">The object type.</param>
+ /// <param name="existingValue">The existing value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.Null)
+ return null;
- var job = JObject.Load(reader);
- var type = job["type"]?.ToObject<ComponentType>();
+ var job = JObject.Load(reader);
+ var type = job["type"]?.ToObject<ComponentType>();
- if (type == null)
- throw new ArgumentException($"Value {reader} does not have a component type specifier");
+ if (type == null)
+ throw new ArgumentException($"Value {reader} does not have a component type specifier");
DiscordComponent cmp;
- cmp = type switch
- {
- ComponentType.ActionRow => new DiscordActionRowComponent(),
- ComponentType.Button when (string)job["url"] is not null => new DiscordLinkButtonComponent(),
- ComponentType.Button => new DiscordButtonComponent(),
- ComponentType.Select => new DiscordSelectComponent(),
- ComponentType.InputText => new DiscordTextComponent(),
- _ => new DiscordComponent() { Type = type.Value }
- };
+ cmp = type switch
+ {
+ ComponentType.ActionRow => new DiscordActionRowComponent(),
+ ComponentType.Button when (string)job["url"] is not null => new DiscordLinkButtonComponent(),
+ ComponentType.Button => new DiscordButtonComponent(),
+ ComponentType.Select => new DiscordSelectComponent(),
+ ComponentType.InputText => new DiscordTextComponent(),
+ _ => new DiscordComponent() { Type = type.Value }
+ };
- // Populate the existing component with the values in the JObject. This avoids a recursive JsonConverter loop
- using var jreader = job.CreateReader();
+ // Populate the existing component with the values in the JObject. This avoids a recursive JsonConverter loop
+ using var jreader = job.CreateReader();
serializer.Populate(jreader, cmp);
- return cmp;
- }
+ return cmp;
+ }
- /// <summary>
- /// Whether the json can convert.
- /// </summary>
- /// <param name="objectType">The object type.</param>
- public override bool CanConvert(Type objectType) => typeof(DiscordComponent).IsAssignableFrom(objectType);
+ /// <summary>
+ /// Whether the json can convert.
+ /// </summary>
+ /// <param name="objectType">The object type.</param>
+ public override bool CanConvert(Type objectType) => typeof(DiscordComponent).IsAssignableFrom(objectType);
+ }
}
diff --git a/DisCatSharp/Net/Serialization/DiscordJson.cs b/DisCatSharp/Net/Serialization/DiscordJson.cs
index 6abbc49bb..51c7cb5f1 100644
--- a/DisCatSharp/Net/Serialization/DiscordJson.cs
+++ b/DisCatSharp/Net/Serialization/DiscordJson.cs
@@ -1,83 +1,84 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.IO;
using System.Text;
using DisCatSharp.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Net.Serialization;
-
-/// <summary>
-/// Represents discord json.
-/// </summary>
-public static class DiscordJson
+namespace DisCatSharp.Net.Serialization
{
- private static readonly JsonSerializer s_serializer = JsonSerializer.CreateDefault(new JsonSerializerSettings
+ /// <summary>
+ /// Represents discord json.
+ /// </summary>
+ public static class DiscordJson
{
- ContractResolver = new OptionalJsonContractResolver()
- });
+ private static readonly JsonSerializer s_serializer = JsonSerializer.CreateDefault(new JsonSerializerSettings
+ {
+ ContractResolver = new OptionalJsonContractResolver()
+ });
- /// <summary>Serializes the specified object to a JSON string.</summary>
- /// <param name="value">The object to serialize.</param>
- /// <returns>A JSON string representation of the object.</returns>
- public static string SerializeObject(object value) => SerializeObjectInternal(value, null, s_serializer);
+ /// <summary>Serializes the specified object to a JSON string.</summary>
+ /// <param name="value">The object to serialize.</param>
+ /// <returns>A JSON string representation of the object.</returns>
+ public static string SerializeObject(object value) => SerializeObjectInternal(value, null, s_serializer);
- /// <summary>Populates an object with the values from a JSON node.</summary>
- /// <param name="value">The token to populate the object with.</param>
- /// <param name="target">The object to populate.</param>
- public static void PopulateObject(JToken value, object target)
- {
- using var reader = value.CreateReader();
- s_serializer.Populate(reader, target);
- }
+ /// <summary>Populates an object with the values from a JSON node.</summary>
+ /// <param name="value">The token to populate the object with.</param>
+ /// <param name="target">The object to populate.</param>
+ public static void PopulateObject(JToken value, object target)
+ {
+ using var reader = value.CreateReader();
+ s_serializer.Populate(reader, target);
+ }
- /// <summary>
- /// Converts this token into an object, passing any properties through extra <see cref="Newtonsoft.Json.JsonConverter"/>s if needed.
- /// </summary>
- /// <param name="token">The token to convert</param>
- /// <typeparam name="T">Type to convert to</typeparam>
- /// <returns>The converted token</returns>
- public static T ToDiscordObject<T>(this JToken token) => token.ToObject<T>(s_serializer);
+ /// <summary>
+ /// Converts this token into an object, passing any properties through extra <see cref="Newtonsoft.Json.JsonConverter"/>s if needed.
+ /// </summary>
+ /// <param name="token">The token to convert</param>
+ /// <typeparam name="T">Type to convert to</typeparam>
+ /// <returns>The converted token</returns>
+ public static T ToDiscordObject<T>(this JToken token) => token.ToObject<T>(s_serializer);
- /// <summary>
- /// Serializes the object.
- /// </summary>
- /// <param name="value">The value.</param>
- /// <param name="type">The type.</param>
- /// <param name="jsonSerializer">The json serializer.</param>
- private static string SerializeObjectInternal(object value, Type type, JsonSerializer jsonSerializer)
- {
- var stringWriter = new StringWriter(new StringBuilder(256), CultureInfo.InvariantCulture);
- using (var jsonTextWriter = new JsonTextWriter(stringWriter))
+ /// <summary>
+ /// Serializes the object.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="jsonSerializer">The json serializer.</param>
+ private static string SerializeObjectInternal(object value, Type type, JsonSerializer jsonSerializer)
{
- jsonTextWriter.Formatting = jsonSerializer.Formatting;
- jsonSerializer.Serialize(jsonTextWriter, value, type);
+ var stringWriter = new StringWriter(new StringBuilder(256), CultureInfo.InvariantCulture);
+ using (var jsonTextWriter = new JsonTextWriter(stringWriter))
+ {
+ jsonTextWriter.Formatting = jsonSerializer.Formatting;
+ jsonSerializer.Serialize(jsonTextWriter, value, type);
+ }
+ return stringWriter.ToString();
}
- return stringWriter.ToString();
}
}
diff --git a/DisCatSharp/Net/Serialization/SnowflakeArrayAsDictionaryJsonConverter.cs b/DisCatSharp/Net/Serialization/SnowflakeArrayAsDictionaryJsonConverter.cs
index cbc2e8abc..603062668 100644
--- a/DisCatSharp/Net/Serialization/SnowflakeArrayAsDictionaryJsonConverter.cs
+++ b/DisCatSharp/Net/Serialization/SnowflakeArrayAsDictionaryJsonConverter.cs
@@ -1,110 +1,111 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using DisCatSharp.Entities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
-namespace DisCatSharp.Net.Serialization;
-
-/// <summary>
-/// Used for a <see cref="Dictionary{TKey,TValue}"/> or <see cref="ConcurrentDictionary{TKey,TValue}"/> mapping
-/// <see cref="ulong"/> to any class extending <see cref="SnowflakeObject"/> (or, as a special case,
-/// <see cref="DiscordVoiceState"/>). When serializing, discards the ulong
-/// keys and writes only the values. When deserializing, pulls the keys from <see cref="SnowflakeObject.Id"/> (or,
-/// in the case of <see cref="DiscordVoiceState"/>, <see cref="DiscordVoiceState.UserId"/>.
-/// </summary>
-internal class SnowflakeArrayAsDictionaryJsonConverter : JsonConverter
+namespace DisCatSharp.Net.Serialization
{
/// <summary>
- /// Writes the json.
+ /// Used for a <see cref="Dictionary{TKey,TValue}"/> or <see cref="ConcurrentDictionary{TKey,TValue}"/> mapping
+ /// <see cref="ulong"/> to any class extending <see cref="SnowflakeObject"/> (or, as a special case,
+ /// <see cref="DiscordVoiceState"/>). When serializing, discards the ulong
+ /// keys and writes only the values. When deserializing, pulls the keys from <see cref="SnowflakeObject.Id"/> (or,
+ /// in the case of <see cref="DiscordVoiceState"/>, <see cref="DiscordVoiceState.UserId"/>.
/// </summary>
- /// <param name="writer">The writer.</param>
- /// <param name="value">The value.</param>
- /// <param name="serializer">The serializer.</param>
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ internal class SnowflakeArrayAsDictionaryJsonConverter : JsonConverter
{
- if (value == null)
- {
- writer.WriteNull();
- }
- else
+ /// <summary>
+ /// Writes the json.
+ /// </summary>
+ /// <param name="writer">The writer.</param>
+ /// <param name="value">The value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
- var type = value.GetType().GetTypeInfo();
- JToken.FromObject(type.GetDeclaredProperty("Values").GetValue(value)).WriteTo(writer);
+ if (value == null)
+ {
+ writer.WriteNull();
+ }
+ else
+ {
+ var type = value.GetType().GetTypeInfo();
+ JToken.FromObject(type.GetDeclaredProperty("Values").GetValue(value)).WriteTo(writer);
+ }
}
- }
- /// <summary>
- /// Reads the json.
- /// </summary>
- /// <param name="reader">The reader.</param>
- /// <param name="objectType">The object type.</param>
- /// <param name="existingValue">The existing value.</param>
- /// <param name="serializer">The serializer.</param>
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- var constructor = objectType.GetTypeInfo().DeclaredConstructors
- .FirstOrDefault(e => !e.IsStatic && e.GetParameters().Length == 0);
+ /// <summary>
+ /// Reads the json.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="objectType">The object type.</param>
+ /// <param name="existingValue">The existing value.</param>
+ /// <param name="serializer">The serializer.</param>
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ var constructor = objectType.GetTypeInfo().DeclaredConstructors
+ .FirstOrDefault(e => !e.IsStatic && e.GetParameters().Length == 0);
- var dict = constructor.Invoke(new object[] {});
+ var dict = constructor.Invoke(new object[] {});
- // the default name of an indexer is "Item"
- var properties = objectType.GetTypeInfo().GetDeclaredProperty("Item");
+ // the default name of an indexer is "Item"
+ var properties = objectType.GetTypeInfo().GetDeclaredProperty("Item");
- var entries = (IEnumerable) serializer.Deserialize(reader, objectType.GenericTypeArguments[1].MakeArrayType());
- foreach (var entry in entries)
- {
- properties.SetValue(dict, entry, new object[]
+ var entries = (IEnumerable) serializer.Deserialize(reader, objectType.GenericTypeArguments[1].MakeArrayType());
+ foreach (var entry in entries)
{
- (entry as SnowflakeObject)?.Id
- ?? (entry as DiscordVoiceState)?.UserId
- ?? throw new InvalidOperationException($"Type {entry?.GetType()} is not deserializable")
- });
- }
+ properties.SetValue(dict, entry, new object[]
+ {
+ (entry as SnowflakeObject)?.Id
+ ?? (entry as DiscordVoiceState)?.UserId
+ ?? throw new InvalidOperationException($"Type {entry?.GetType()} is not deserializable")
+ });
+ }
- return dict;
- }
+ return dict;
+ }
- /// <summary>
- /// Whether the snowflake can be converted.
- /// </summary>
- /// <param name="objectType">The object type.</param>
- public override bool CanConvert(Type objectType)
- {
- var genericTypedef = objectType.GetGenericTypeDefinition();
- if (genericTypedef != typeof(Dictionary<,>) && genericTypedef != typeof(ConcurrentDictionary<,>)) return false;
- if (objectType.GenericTypeArguments[0] != typeof(ulong)) return false;
+ /// <summary>
+ /// Whether the snowflake can be converted.
+ /// </summary>
+ /// <param name="objectType">The object type.</param>
+ public override bool CanConvert(Type objectType)
+ {
+ var genericTypedef = objectType.GetGenericTypeDefinition();
+ if (genericTypedef != typeof(Dictionary<,>) && genericTypedef != typeof(ConcurrentDictionary<,>)) return false;
+ if (objectType.GenericTypeArguments[0] != typeof(ulong)) return false;
- var valueParam = objectType.GenericTypeArguments[1];
- return typeof(SnowflakeObject).GetTypeInfo().IsAssignableFrom(valueParam.GetTypeInfo()) ||
- valueParam == typeof(DiscordVoiceState);
+ var valueParam = objectType.GenericTypeArguments[1];
+ return typeof(SnowflakeObject).GetTypeInfo().IsAssignableFrom(valueParam.GetTypeInfo()) ||
+ valueParam == typeof(DiscordVoiceState);
+ }
}
}
diff --git a/DisCatSharp/Net/Udp/BaseUdpClient.cs b/DisCatSharp/Net/Udp/BaseUdpClient.cs
index 1d397143c..e6469bfc4 100644
--- a/DisCatSharp/Net/Udp/BaseUdpClient.cs
+++ b/DisCatSharp/Net/Udp/BaseUdpClient.cs
@@ -1,62 +1,63 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading.Tasks;
-namespace DisCatSharp.Net.Udp;
-
-/// <summary>
-/// Creates an instance of a UDP client implementation.
-/// </summary>
-/// <returns>Constructed UDP client implementation.</returns>
-public delegate BaseUdpClient UdpClientFactoryDelegate();
-
-/// <summary>
-/// Represents a base abstraction for all UDP client implementations.
-/// </summary>
-public abstract class BaseUdpClient
+namespace DisCatSharp.Net.Udp
{
/// <summary>
- /// Configures the UDP client.
+ /// Creates an instance of a UDP client implementation.
/// </summary>
- /// <param name="endpoint">Endpoint that the client will be communicating with.</param>
- public abstract void Setup(ConnectionEndpoint endpoint);
+ /// <returns>Constructed UDP client implementation.</returns>
+ public delegate BaseUdpClient UdpClientFactoryDelegate();
/// <summary>
- /// Sends a datagram.
+ /// Represents a base abstraction for all UDP client implementations.
/// </summary>
- /// <param name="data">Datagram.</param>
- /// <param name="dataLength">Length of the datagram.</param>
- /// <returns></returns>
- public abstract Task SendAsync(byte[] data, int dataLength);
+ public abstract class BaseUdpClient
+ {
+ /// <summary>
+ /// Configures the UDP client.
+ /// </summary>
+ /// <param name="endpoint">Endpoint that the client will be communicating with.</param>
+ public abstract void Setup(ConnectionEndpoint endpoint);
- /// <summary>
- /// Receives a datagram.
- /// </summary>
- /// <returns>The received bytes.</returns>
- public abstract Task<byte[]> ReceiveAsync();
+ /// <summary>
+ /// Sends a datagram.
+ /// </summary>
+ /// <param name="data">Datagram.</param>
+ /// <param name="dataLength">Length of the datagram.</param>
+ /// <returns></returns>
+ public abstract Task SendAsync(byte[] data, int dataLength);
- /// <summary>
- /// Closes and disposes the client.
- /// </summary>
- public abstract void Close();
+ /// <summary>
+ /// Receives a datagram.
+ /// </summary>
+ /// <returns>The received bytes.</returns>
+ public abstract Task<byte[]> ReceiveAsync();
+
+ /// <summary>
+ /// Closes and disposes the client.
+ /// </summary>
+ public abstract void Close();
+ }
}
diff --git a/DisCatSharp/Net/Udp/DCSUdpClient.cs b/DisCatSharp/Net/Udp/DCSUdpClient.cs
index da18677db..b8b30b64f 100644
--- a/DisCatSharp/Net/Udp/DCSUdpClient.cs
+++ b/DisCatSharp/Net/Udp/DCSUdpClient.cs
@@ -1,144 +1,145 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Concurrent;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
-namespace DisCatSharp.Net.Udp;
-
-/// <summary>
-/// The default, native-based UDP client implementation.
-/// </summary>
-internal class DcsUdpClient : BaseUdpClient
+namespace DisCatSharp.Net.Udp
{
/// <summary>
- /// Gets the client.
- /// </summary>
- private UdpClient _client;
-
- /// <summary>
- /// Gets the end point.
- /// </summary>
- private ConnectionEndpoint _endPoint;
-
- /// <summary>
- /// Gets the packet queue.
- /// </summary>
- private readonly BlockingCollection<byte[]> _packetQueue;
-
-
- /// <summary>
- /// Gets the receiver task.
- /// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members",
- Justification = "<Pending>")]
- private Task _receiverTask;
-
- /// <summary>
- /// Gets the cancellation token source.
- /// </summary>
- private readonly CancellationTokenSource _tokenSource;
-
- /// <summary>
- /// Gets the cancellation token.
- /// </summary>
- private CancellationToken TOKEN => this._tokenSource.Token;
-
- /// <summary>
- /// Creates a new UDP client instance.
- /// </summary>
- public DcsUdpClient()
- {
- this._packetQueue = new BlockingCollection<byte[]>();
- this._tokenSource = new CancellationTokenSource();
- }
-
- /// <summary>
- /// Configures the UDP client.
- /// </summary>
- /// <param name="endpoint">Endpoint that the client will be communicating with.</param>
- public override void Setup(ConnectionEndpoint endpoint)
- {
- this._endPoint = endpoint;
- this._client = new UdpClient();
- this._receiverTask = Task.Run(this.ReceiverLoopAsync, this.TOKEN);
- }
-
- /// <summary>
- /// Sends a datagram.
- /// </summary>
- /// <param name="data">Datagram.</param>
- /// <param name="dataLength">Length of the datagram.</param>
- /// <returns></returns>
- public override Task SendAsync(byte[] data, int dataLength)
- => this._client.SendAsync(data, dataLength, this._endPoint.Hostname, this._endPoint.Port);
-
- /// <summary>
- /// Receives a datagram.
- /// </summary>
- /// <returns>The received bytes.</returns>
- public override Task<byte[]> ReceiveAsync() => Task.FromResult(this._packetQueue.Take(this.TOKEN));
-
- /// <summary>
- /// Closes and disposes the client.
+ /// The default, native-based UDP client implementation.
/// </summary>
- public override void Close()
+ internal class DcsUdpClient : BaseUdpClient
{
- this._tokenSource.Cancel();
- try
+ /// <summary>
+ /// Gets the client.
+ /// </summary>
+ private UdpClient _client;
+
+ /// <summary>
+ /// Gets the end point.
+ /// </summary>
+ private ConnectionEndpoint _endPoint;
+
+ /// <summary>
+ /// Gets the packet queue.
+ /// </summary>
+ private readonly BlockingCollection<byte[]> _packetQueue;
+
+
+ /// <summary>
+ /// Gets the receiver task.
+ /// </summary>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members",
+ Justification = "<Pending>")]
+ private Task _receiverTask;
+
+ /// <summary>
+ /// Gets the cancellation token source.
+ /// </summary>
+ private readonly CancellationTokenSource _tokenSource;
+
+ /// <summary>
+ /// Gets the cancellation token.
+ /// </summary>
+ private CancellationToken TOKEN => this._tokenSource.Token;
+
+ /// <summary>
+ /// Creates a new UDP client instance.
+ /// </summary>
+ public DcsUdpClient()
{
- this._client.Close();
+ this._packetQueue = new BlockingCollection<byte[]>();
+ this._tokenSource = new CancellationTokenSource();
}
- catch (Exception)
+
+ /// <summary>
+ /// Configures the UDP client.
+ /// </summary>
+ /// <param name="endpoint">Endpoint that the client will be communicating with.</param>
+ public override void Setup(ConnectionEndpoint endpoint)
{
- // Nothing
+ this._endPoint = endpoint;
+ this._client = new UdpClient();
+ this._receiverTask = Task.Run(this.ReceiverLoopAsync, this.TOKEN);
}
- // dequeue all the packets
- this._packetQueue.Dispose();
- }
-
- /// <summary>
- /// Receivers the loop.
- /// </summary>
- private async Task ReceiverLoopAsync()
- {
- while (!this.TOKEN.IsCancellationRequested)
+ /// <summary>
+ /// Sends a datagram.
+ /// </summary>
+ /// <param name="data">Datagram.</param>
+ /// <param name="dataLength">Length of the datagram.</param>
+ /// <returns></returns>
+ public override Task SendAsync(byte[] data, int dataLength)
+ => this._client.SendAsync(data, dataLength, this._endPoint.Hostname, this._endPoint.Port);
+
+ /// <summary>
+ /// Receives a datagram.
+ /// </summary>
+ /// <returns>The received bytes.</returns>
+ public override Task<byte[]> ReceiveAsync() => Task.FromResult(this._packetQueue.Take(this.TOKEN));
+
+ /// <summary>
+ /// Closes and disposes the client.
+ /// </summary>
+ public override void Close()
{
+ this._tokenSource.Cancel();
try
{
- var packet = await this._client.ReceiveAsync().ConfigureAwait(false);
- this._packetQueue.Add(packet.Buffer);
+ this._client.Close();
+ }
+ catch (Exception)
+ {
+ // Nothing
}
- catch (Exception) { }
+
+ // dequeue all the packets
+ this._packetQueue.Dispose();
}
- }
- /// <summary>
- /// Creates a new instance of <see cref="BaseUdpClient"/>.
- /// </summary>
- public static BaseUdpClient CreateNew()
- => new DcsUdpClient();
+ /// <summary>
+ /// Receivers the loop.
+ /// </summary>
+ private async Task ReceiverLoopAsync()
+ {
+ while (!this.TOKEN.IsCancellationRequested)
+ {
+ try
+ {
+ var packet = await this._client.ReceiveAsync().ConfigureAwait(false);
+ this._packetQueue.Add(packet.Buffer);
+ }
+ catch (Exception) { }
+ }
+ }
+
+ /// <summary>
+ /// Creates a new instance of <see cref="BaseUdpClient"/>.
+ /// </summary>
+ public static BaseUdpClient CreateNew()
+ => new DcsUdpClient();
+ }
}
diff --git a/DisCatSharp/Net/WebSocket/IWebSocketClient.cs b/DisCatSharp/Net/WebSocket/IWebSocketClient.cs
index 350e11b6b..c31cf8c35 100644
--- a/DisCatSharp/Net/WebSocket/IWebSocketClient.cs
+++ b/DisCatSharp/Net/WebSocket/IWebSocketClient.cs
@@ -1,114 +1,115 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Net;
using System.Threading.Tasks;
using DisCatSharp.Common.Utilities;
using DisCatSharp.EventArgs;
-namespace DisCatSharp.Net.WebSocket;
-
-/// <summary>
-/// Creates an instance of a WebSocket client implementation.
-/// </summary>
-/// <param name="proxy">Proxy settings to use for the new WebSocket client instance.</param>
-/// <param name="provider">Service provider.</param>
-/// <returns>Constructed WebSocket client implementation.</returns>
-public delegate IWebSocketClient WebSocketClientFactoryDelegate(IWebProxy proxy, IServiceProvider provider);
-
-/// <summary>
-/// Represents a base abstraction for all WebSocket client implementations.
-/// </summary>
-public interface IWebSocketClient : IDisposable
+namespace DisCatSharp.Net.WebSocket
{
/// <summary>
- /// Gets the proxy settings for this client.
- /// </summary>
- IWebProxy Proxy { get; }
-
- /// <summary>
- /// Gets the collection of default headers to send when connecting to the remote endpoint.
- /// </summary>
- IReadOnlyDictionary<string, string> DefaultHeaders { get; }
-
- /// <summary>
- /// <para>Gets the service provider.</para>
- /// </summary>
- internal IServiceProvider ServiceProvider { get; set; }
-
- /// <summary>
- /// Connects to a specified remote WebSocket endpoint.
- /// </summary>
- /// <param name="uri">The URI of the WebSocket endpoint.</param>
- Task ConnectAsync(Uri uri);
-
- /// <summary>
- /// Disconnects the WebSocket connection.
- /// </summary>
- /// <param name="code">The code.</param>
- /// <param name="message">The message.</param>
- Task DisconnectAsync(int code = 1000, string message = "");
-
- /// <summary>
- /// Send a message to the WebSocket server.
- /// </summary>
- /// <param name="message">The message to send.</param>
- Task SendMessageAsync(string message);
-
- /// <summary>
- /// Adds a header to the default header collection.
- /// </summary>
- /// <param name="name">Name of the header to add.</param>
- /// <param name="value">Value of the header to add.</param>
- /// <returns>Whether the operation succeeded.</returns>
- bool AddDefaultHeader(string name, string value);
-
- /// <summary>
- /// Removes a header from the default header collection.
- /// </summary>
- /// <param name="name">Name of the header to remove.</param>
- /// <returns>Whether the operation succeeded.</returns>
- bool RemoveDefaultHeader(string name);
-
- /// <summary>
- /// Triggered when the client connects successfully.
- /// </summary>
- event AsyncEventHandler<IWebSocketClient, SocketEventArgs> Connected;
-
- /// <summary>
- /// Triggered when the client is disconnected.
- /// </summary>
- event AsyncEventHandler<IWebSocketClient, SocketCloseEventArgs> Disconnected;
-
- /// <summary>
- /// Triggered when the client receives a message from the remote party.
+ /// Creates an instance of a WebSocket client implementation.
/// </summary>
- event AsyncEventHandler<IWebSocketClient, SocketMessageEventArgs> MessageReceived;
+ /// <param name="proxy">Proxy settings to use for the new WebSocket client instance.</param>
+ /// <param name="provider">Service provider.</param>
+ /// <returns>Constructed WebSocket client implementation.</returns>
+ public delegate IWebSocketClient WebSocketClientFactoryDelegate(IWebProxy proxy, IServiceProvider provider);
/// <summary>
- /// Triggered when an error occurs in the client.
+ /// Represents a base abstraction for all WebSocket client implementations.
/// </summary>
- event AsyncEventHandler<IWebSocketClient, SocketErrorEventArgs> ExceptionThrown;
+ public interface IWebSocketClient : IDisposable
+ {
+ /// <summary>
+ /// Gets the proxy settings for this client.
+ /// </summary>
+ IWebProxy Proxy { get; }
+
+ /// <summary>
+ /// Gets the collection of default headers to send when connecting to the remote endpoint.
+ /// </summary>
+ IReadOnlyDictionary<string, string> DefaultHeaders { get; }
+
+ /// <summary>
+ /// <para>Gets the service provider.</para>
+ /// </summary>
+ internal IServiceProvider ServiceProvider { get; set; }
+
+ /// <summary>
+ /// Connects to a specified remote WebSocket endpoint.
+ /// </summary>
+ /// <param name="uri">The URI of the WebSocket endpoint.</param>
+ Task ConnectAsync(Uri uri);
+
+ /// <summary>
+ /// Disconnects the WebSocket connection.
+ /// </summary>
+ /// <param name="code">The code.</param>
+ /// <param name="message">The message.</param>
+ Task DisconnectAsync(int code = 1000, string message = "");
+
+ /// <summary>
+ /// Send a message to the WebSocket server.
+ /// </summary>
+ /// <param name="message">The message to send.</param>
+ Task SendMessageAsync(string message);
+
+ /// <summary>
+ /// Adds a header to the default header collection.
+ /// </summary>
+ /// <param name="name">Name of the header to add.</param>
+ /// <param name="value">Value of the header to add.</param>
+ /// <returns>Whether the operation succeeded.</returns>
+ bool AddDefaultHeader(string name, string value);
+
+ /// <summary>
+ /// Removes a header from the default header collection.
+ /// </summary>
+ /// <param name="name">Name of the header to remove.</param>
+ /// <returns>Whether the operation succeeded.</returns>
+ bool RemoveDefaultHeader(string name);
+
+ /// <summary>
+ /// Triggered when the client connects successfully.
+ /// </summary>
+ event AsyncEventHandler<IWebSocketClient, SocketEventArgs> Connected;
+
+ /// <summary>
+ /// Triggered when the client is disconnected.
+ /// </summary>
+ event AsyncEventHandler<IWebSocketClient, SocketCloseEventArgs> Disconnected;
+
+ /// <summary>
+ /// Triggered when the client receives a message from the remote party.
+ /// </summary>
+ event AsyncEventHandler<IWebSocketClient, SocketMessageEventArgs> MessageReceived;
+
+ /// <summary>
+ /// Triggered when an error occurs in the client.
+ /// </summary>
+ event AsyncEventHandler<IWebSocketClient, SocketErrorEventArgs> ExceptionThrown;
+ }
}
diff --git a/DisCatSharp/Net/WebSocket/PayloadDecompressor.cs b/DisCatSharp/Net/WebSocket/PayloadDecompressor.cs
index ad269bcd4..5cbd08022 100644
--- a/DisCatSharp/Net/WebSocket/PayloadDecompressor.cs
+++ b/DisCatSharp/Net/WebSocket/PayloadDecompressor.cs
@@ -1,128 +1,129 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Buffers.Binary;
using System.IO;
using System.IO.Compression;
-namespace DisCatSharp.Net.WebSocket;
-
-/// <summary>
-/// Represents a payload decompressor.
-/// </summary>
-internal sealed class PayloadDecompressor : IDisposable
+namespace DisCatSharp.Net.WebSocket
{
/// <summary>
- /// The zlib flush.
- /// </summary>
- private const uint ZLIB_FLUSH = 0x0000FFFF;
-
- /// <summary>
- /// The zlib prefix.
- /// </summary>
- private const byte ZLIB_PREFIX = 0x78;
-
- /// <summary>
- /// Gets the compression level.
- /// </summary>
- public GatewayCompressionLevel CompressionLevel { get; }
-
- /// <summary>
- /// Gets the compressed stream.
+ /// Represents a payload decompressor.
/// </summary>
- private readonly MemoryStream _compressedStream;
-
- /// <summary>
- /// Gets the decompressor stream.
- /// </summary>
- private readonly DeflateStream _decompressorStream;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="PayloadDecompressor"/> class.
- /// </summary>
- /// <param name="compressionLevel">The compression level.</param>
- public PayloadDecompressor(GatewayCompressionLevel compressionLevel)
+ internal sealed class PayloadDecompressor : IDisposable
{
- if (compressionLevel == GatewayCompressionLevel.None)
- throw new InvalidOperationException("Decompressor requires a valid compression mode.");
-
- this.CompressionLevel = compressionLevel;
- this._compressedStream = new MemoryStream();
- if (this.CompressionLevel == GatewayCompressionLevel.Stream)
- this._decompressorStream = new DeflateStream(this._compressedStream, CompressionMode.Decompress);
- }
-
- /// <summary>
- /// Tries the decompress.
- /// </summary>
- /// <param name="compressed">The compressed bytes.</param>
- /// <param name="decompressed">The decompressed memory stream.</param>
- public bool TryDecompress(ArraySegment<byte> compressed, MemoryStream decompressed)
- {
- var zlib = this.CompressionLevel == GatewayCompressionLevel.Stream
- ? this._decompressorStream
- : new DeflateStream(this._compressedStream, CompressionMode.Decompress, true);
-
- if (compressed.Array[0] == ZLIB_PREFIX)
- this._compressedStream.Write(compressed.Array, compressed.Offset + 2, compressed.Count - 2);
- else
- this._compressedStream.Write(compressed.Array, compressed.Offset, compressed.Count);
-
- this._compressedStream.Flush();
- this._compressedStream.Position = 0;
-
- var cspan = compressed.AsSpan();
- var suffix = BinaryPrimitives.ReadUInt32BigEndian(cspan[^4..]);
- if (this.CompressionLevel == GatewayCompressionLevel.Stream && suffix != ZLIB_FLUSH)
+ /// <summary>
+ /// The zlib flush.
+ /// </summary>
+ private const uint ZLIB_FLUSH = 0x0000FFFF;
+
+ /// <summary>
+ /// The zlib prefix.
+ /// </summary>
+ private const byte ZLIB_PREFIX = 0x78;
+
+ /// <summary>
+ /// Gets the compression level.
+ /// </summary>
+ public GatewayCompressionLevel CompressionLevel { get; }
+
+ /// <summary>
+ /// Gets the compressed stream.
+ /// </summary>
+ private readonly MemoryStream _compressedStream;
+
+ /// <summary>
+ /// Gets the decompressor stream.
+ /// </summary>
+ private readonly DeflateStream _decompressorStream;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PayloadDecompressor"/> class.
+ /// </summary>
+ /// <param name="compressionLevel">The compression level.</param>
+ public PayloadDecompressor(GatewayCompressionLevel compressionLevel)
{
- if (this.CompressionLevel == GatewayCompressionLevel.Payload)
- zlib.Dispose();
+ if (compressionLevel == GatewayCompressionLevel.None)
+ throw new InvalidOperationException("Decompressor requires a valid compression mode.");
- return false;
+ this.CompressionLevel = compressionLevel;
+ this._compressedStream = new MemoryStream();
+ if (this.CompressionLevel == GatewayCompressionLevel.Stream)
+ this._decompressorStream = new DeflateStream(this._compressedStream, CompressionMode.Decompress);
}
- try
- {
- zlib.CopyTo(decompressed);
- return true;
- }
- catch { return false; }
- finally
+ /// <summary>
+ /// Tries the decompress.
+ /// </summary>
+ /// <param name="compressed">The compressed bytes.</param>
+ /// <param name="decompressed">The decompressed memory stream.</param>
+ public bool TryDecompress(ArraySegment<byte> compressed, MemoryStream decompressed)
{
+ var zlib = this.CompressionLevel == GatewayCompressionLevel.Stream
+ ? this._decompressorStream
+ : new DeflateStream(this._compressedStream, CompressionMode.Decompress, true);
+
+ if (compressed.Array[0] == ZLIB_PREFIX)
+ this._compressedStream.Write(compressed.Array, compressed.Offset + 2, compressed.Count - 2);
+ else
+ this._compressedStream.Write(compressed.Array, compressed.Offset, compressed.Count);
+
+ this._compressedStream.Flush();
this._compressedStream.Position = 0;
- this._compressedStream.SetLength(0);
- if (this.CompressionLevel == GatewayCompressionLevel.Payload)
- zlib.Dispose();
+ var cspan = compressed.AsSpan();
+ var suffix = BinaryPrimitives.ReadUInt32BigEndian(cspan[^4..]);
+ if (this.CompressionLevel == GatewayCompressionLevel.Stream && suffix != ZLIB_FLUSH)
+ {
+ if (this.CompressionLevel == GatewayCompressionLevel.Payload)
+ zlib.Dispose();
+
+ return false;
+ }
+
+ try
+ {
+ zlib.CopyTo(decompressed);
+ return true;
+ }
+ catch { return false; }
+ finally
+ {
+ this._compressedStream.Position = 0;
+ this._compressedStream.SetLength(0);
+
+ if (this.CompressionLevel == GatewayCompressionLevel.Payload)
+ zlib.Dispose();
+ }
}
- }
- /// <summary>
- /// Disposes the decompressor.
- /// </summary>
- public void Dispose()
- {
- this._decompressorStream?.Dispose();
- this._compressedStream.Dispose();
+ /// <summary>
+ /// Disposes the decompressor.
+ /// </summary>
+ public void Dispose()
+ {
+ this._decompressorStream?.Dispose();
+ this._compressedStream.Dispose();
+ }
}
}
diff --git a/DisCatSharp/Net/WebSocket/SocketLock.cs b/DisCatSharp/Net/WebSocket/SocketLock.cs
index 2f7c2d492..7971fa2fe 100644
--- a/DisCatSharp/Net/WebSocket/SocketLock.cs
+++ b/DisCatSharp/Net/WebSocket/SocketLock.cs
@@ -1,138 +1,139 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Threading;
using System.Threading.Tasks;
-namespace DisCatSharp.Net.WebSocket;
-
-// Licensed from Clyde.NET
-
-/// <summary>
-/// Represents a socket lock.
-/// </summary>
-internal sealed class SocketLock : IDisposable
+namespace DisCatSharp.Net.WebSocket
{
- /// <summary>
- /// Gets the application id.
- /// </summary>
- public ulong ApplicationId { get; }
-
- /// <summary>
- /// Gets the lock semaphore.
- /// </summary>
- private readonly SemaphoreSlim _lockSemaphore;
-
- /// <summary>
- /// Gets or sets the timeout cancel source.
- /// </summary>
- private CancellationTokenSource _timeoutCancelSource;
-
- /// <summary>
- /// Gets the cancel token.
- /// </summary>
- private CancellationToken TIMEOUT_CANCEL => this._timeoutCancelSource.Token;
-
- /// <summary>
- /// Gets or sets the unlock task.
- /// </summary>
- private Task _unlockTask;
-
- /// <summary>
- /// Gets or sets the max concurrency.
- /// </summary>
- private readonly int _maxConcurrency;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SocketLock"/> class.
- /// </summary>
- /// <param name="appId">The app id.</param>
- /// <param name="maxConcurrency">The max concurrency.</param>
- public SocketLock(ulong appId, int maxConcurrency)
- {
- this.ApplicationId = appId;
- this._timeoutCancelSource = null;
- this._maxConcurrency = maxConcurrency;
- this._lockSemaphore = new SemaphoreSlim(maxConcurrency);
- }
+ // Licensed from Clyde.NET
/// <summary>
- /// Locks the socket.
+ /// Represents a socket lock.
/// </summary>
- public async Task LockAsync()
+ internal sealed class SocketLock : IDisposable
{
- await this._lockSemaphore.WaitAsync().ConfigureAwait(false);
+ /// <summary>
+ /// Gets the application id.
+ /// </summary>
+ public ulong ApplicationId { get; }
+
+ /// <summary>
+ /// Gets the lock semaphore.
+ /// </summary>
+ private readonly SemaphoreSlim _lockSemaphore;
+
+ /// <summary>
+ /// Gets or sets the timeout cancel source.
+ /// </summary>
+ private CancellationTokenSource _timeoutCancelSource;
+
+ /// <summary>
+ /// Gets the cancel token.
+ /// </summary>
+ private CancellationToken TIMEOUT_CANCEL => this._timeoutCancelSource.Token;
+
+ /// <summary>
+ /// Gets or sets the unlock task.
+ /// </summary>
+ private Task _unlockTask;
+
+ /// <summary>
+ /// Gets or sets the max concurrency.
+ /// </summary>
+ private readonly int _maxConcurrency;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SocketLock"/> class.
+ /// </summary>
+ /// <param name="appId">The app id.</param>
+ /// <param name="maxConcurrency">The max concurrency.</param>
+ public SocketLock(ulong appId, int maxConcurrency)
+ {
+ this.ApplicationId = appId;
+ this._timeoutCancelSource = null;
+ this._maxConcurrency = maxConcurrency;
+ this._lockSemaphore = new SemaphoreSlim(maxConcurrency);
+ }
- this._timeoutCancelSource = new CancellationTokenSource();
- this._unlockTask = Task.Delay(TimeSpan.FromSeconds(30), this.TIMEOUT_CANCEL);
- _ = this._unlockTask.ContinueWith(this.InternalUnlock, TaskContinuationOptions.NotOnCanceled);
- }
+ /// <summary>
+ /// Locks the socket.
+ /// </summary>
+ public async Task LockAsync()
+ {
+ await this._lockSemaphore.WaitAsync().ConfigureAwait(false);
- /// <summary>
- /// Unlocks the socket after a given timespan.
- /// </summary>
- /// <param name="unlockDelay">The unlock delay.</param>
- public void UnlockAfter(TimeSpan unlockDelay)
- {
- if (this._timeoutCancelSource == null || this._lockSemaphore.CurrentCount > 0)
- return; // it's not unlockable because it's post-IDENTIFY or not locked
+ this._timeoutCancelSource = new CancellationTokenSource();
+ this._unlockTask = Task.Delay(TimeSpan.FromSeconds(30), this.TIMEOUT_CANCEL);
+ _ = this._unlockTask.ContinueWith(this.InternalUnlock, TaskContinuationOptions.NotOnCanceled);
+ }
- try
+ /// <summary>
+ /// Unlocks the socket after a given timespan.
+ /// </summary>
+ /// <param name="unlockDelay">The unlock delay.</param>
+ public void UnlockAfter(TimeSpan unlockDelay)
{
- this._timeoutCancelSource.Cancel();
- this._timeoutCancelSource.Dispose();
+ if (this._timeoutCancelSource == null || this._lockSemaphore.CurrentCount > 0)
+ return; // it's not unlockable because it's post-IDENTIFY or not locked
+
+ try
+ {
+ this._timeoutCancelSource.Cancel();
+ this._timeoutCancelSource.Dispose();
+ }
+ catch { }
+ this._timeoutCancelSource = null;
+
+ this._unlockTask = Task.Delay(unlockDelay, CancellationToken.None);
+ _ = this._unlockTask.ContinueWith(this.InternalUnlock);
}
- catch { }
- this._timeoutCancelSource = null;
-
- this._unlockTask = Task.Delay(unlockDelay, CancellationToken.None);
- _ = this._unlockTask.ContinueWith(this.InternalUnlock);
- }
-
- /// <summary>
- /// Waits for the socket lock.
- /// </summary>
- /// <returns>A Task.</returns>
- public Task WaitAsync()
- => this._lockSemaphore.WaitAsync();
- /// <summary>
- /// Disposes the socket lock.
- /// </summary>
- public void Dispose()
- {
- try
+ /// <summary>
+ /// Waits for the socket lock.
+ /// </summary>
+ /// <returns>A Task.</returns>
+ public Task WaitAsync()
+ => this._lockSemaphore.WaitAsync();
+
+ /// <summary>
+ /// Disposes the socket lock.
+ /// </summary>
+ public void Dispose()
{
- this._timeoutCancelSource?.Cancel();
- this._timeoutCancelSource?.Dispose();
+ try
+ {
+ this._timeoutCancelSource?.Cancel();
+ this._timeoutCancelSource?.Dispose();
+ }
+ catch { }
}
- catch { }
- }
- /// <summary>
- /// Unlocks the socket.
- /// </summary>
- /// <param name="t">The task.</param>
- private void InternalUnlock(Task t)
- => this._lockSemaphore.Release(this._maxConcurrency);
+ /// <summary>
+ /// Unlocks the socket.
+ /// </summary>
+ /// <param name="t">The task.</param>
+ private void InternalUnlock(Task t)
+ => this._lockSemaphore.Release(this._maxConcurrency);
+ }
}
diff --git a/DisCatSharp/Net/WebSocket/WebSocketClient.cs b/DisCatSharp/Net/WebSocket/WebSocketClient.cs
index e0d0028d8..d65e2931f 100644
--- a/DisCatSharp/Net/WebSocket/WebSocketClient.cs
+++ b/DisCatSharp/Net/WebSocket/WebSocketClient.cs
@@ -1,415 +1,416 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Common.Utilities;
using DisCatSharp.EventArgs;
-namespace DisCatSharp.Net.WebSocket;
-
-// weebsocket
-// not even sure whether emzi or I posted this. much love, naam.
-/// <summary>
-/// The default, native-based WebSocket client implementation.
-/// </summary>
-public class WebSocketClient : IWebSocketClient
+namespace DisCatSharp.Net.WebSocket
{
+ // weebsocket
+ // not even sure whether emzi or I posted this. much love, naam.
/// <summary>
- /// The outgoing chunk size.
+ /// The default, native-based WebSocket client implementation.
/// </summary>
- private const int OUTGOING_CHUNK_SIZE = 8192; // 8 KiB
-
- /// <summary>
- /// The incoming chunk size.
- /// </summary>
- private const int INCOMING_CHUNK_SIZE = 32768; // 32 KiB
-
- /// <summary>
- /// Gets the proxy settings for this client.
- /// </summary>
- public IWebProxy Proxy { get; }
-
- /// <summary>
- /// Gets the collection of default headers to send when connecting to the remote endpoint.
- /// </summary>
- public IReadOnlyDictionary<string, string> DefaultHeaders { get; }
-
- /// <summary>
- /// Gets or sets the service provider.
- /// </summary>
- IServiceProvider IWebSocketClient.ServiceProvider
+ public class WebSocketClient : IWebSocketClient
{
- get => this._serviceProvider;
- set => this._serviceProvider = value;
- }
+ /// <summary>
+ /// The outgoing chunk size.
+ /// </summary>
+ private const int OUTGOING_CHUNK_SIZE = 8192; // 8 KiB
+
+ /// <summary>
+ /// The incoming chunk size.
+ /// </summary>
+ private const int INCOMING_CHUNK_SIZE = 32768; // 32 KiB
+
+ /// <summary>
+ /// Gets the proxy settings for this client.
+ /// </summary>
+ public IWebProxy Proxy { get; }
+
+ /// <summary>
+ /// Gets the collection of default headers to send when connecting to the remote endpoint.
+ /// </summary>
+ public IReadOnlyDictionary<string, string> DefaultHeaders { get; }
+
+ /// <summary>
+ /// Gets or sets the service provider.
+ /// </summary>
+ IServiceProvider IWebSocketClient.ServiceProvider
+ {
+ get => this._serviceProvider;
+ set => this._serviceProvider = value;
+ }
- private readonly Dictionary<string, string> _defaultHeaders;
+ private readonly Dictionary<string, string> _defaultHeaders;
- private Task _receiverTask;
- private CancellationTokenSource _receiverTokenSource;
- private CancellationToken _receiverToken;
- private readonly SemaphoreSlim _senderLock;
+ private Task _receiverTask;
+ private CancellationTokenSource _receiverTokenSource;
+ private CancellationToken _receiverToken;
+ private readonly SemaphoreSlim _senderLock;
- private CancellationTokenSource _socketTokenSource;
- private CancellationToken _socketToken;
- private ClientWebSocket _ws;
+ private CancellationTokenSource _socketTokenSource;
+ private CancellationToken _socketToken;
+ private ClientWebSocket _ws;
- private volatile bool _isClientClose;
- private volatile bool _isConnected;
- private bool _isDisposed;
+ private volatile bool _isClientClose;
+ private volatile bool _isConnected;
+ private bool _isDisposed;
- /// <summary>
- /// Instantiates a new WebSocket client with specified proxy settings.
- /// </summary>
- /// <param name="proxy">Proxy settings for the client.</param>
- /// <param name="provider">Service provider.</param>
- private WebSocketClient(IWebProxy proxy, IServiceProvider provider)
- {
- this._connected = new AsyncEvent<WebSocketClient, SocketEventArgs>("WS_CONNECT", TimeSpan.Zero, this.EventErrorHandler);
- this._disconnected = new AsyncEvent<WebSocketClient, SocketCloseEventArgs>("WS_DISCONNECT", TimeSpan.Zero, this.EventErrorHandler);
- this._messageReceived = new AsyncEvent<WebSocketClient, SocketMessageEventArgs>("WS_MESSAGE", TimeSpan.Zero, this.EventErrorHandler);
- this._exceptionThrown = new AsyncEvent<WebSocketClient, SocketErrorEventArgs>("WS_ERROR", TimeSpan.Zero, null);
+ /// <summary>
+ /// Instantiates a new WebSocket client with specified proxy settings.
+ /// </summary>
+ /// <param name="proxy">Proxy settings for the client.</param>
+ /// <param name="provider">Service provider.</param>
+ private WebSocketClient(IWebProxy proxy, IServiceProvider provider)
+ {
+ this._connected = new AsyncEvent<WebSocketClient, SocketEventArgs>("WS_CONNECT", TimeSpan.Zero, this.EventErrorHandler);
+ this._disconnected = new AsyncEvent<WebSocketClient, SocketCloseEventArgs>("WS_DISCONNECT", TimeSpan.Zero, this.EventErrorHandler);
+ this._messageReceived = new AsyncEvent<WebSocketClient, SocketMessageEventArgs>("WS_MESSAGE", TimeSpan.Zero, this.EventErrorHandler);
+ this._exceptionThrown = new AsyncEvent<WebSocketClient, SocketErrorEventArgs>("WS_ERROR", TimeSpan.Zero, null);
- this.Proxy = proxy;
- this._defaultHeaders = new Dictionary<string, string>();
- this.DefaultHeaders = new ReadOnlyDictionary<string, string>(this._defaultHeaders);
+ this.Proxy = proxy;
+ this._defaultHeaders = new Dictionary<string, string>();
+ this.DefaultHeaders = new ReadOnlyDictionary<string, string>(this._defaultHeaders);
- this._receiverTokenSource = null;
- this._receiverToken = CancellationToken.None;
- this._senderLock = new SemaphoreSlim(1);
+ this._receiverTokenSource = null;
+ this._receiverToken = CancellationToken.None;
+ this._senderLock = new SemaphoreSlim(1);
- this._socketTokenSource = null;
- this._socketToken = CancellationToken.None;
+ this._socketTokenSource = null;
+ this._socketToken = CancellationToken.None;
- this._serviceProvider = provider;
- }
+ this._serviceProvider = provider;
+ }
- /// <summary>
- /// Connects to a specified remote WebSocket endpoint.
- /// </summary>
- /// <param name="uri">The URI of the WebSocket endpoint.</param>
- public async Task ConnectAsync(Uri uri)
- {
- // Disconnect first
- try { await this.DisconnectAsync().ConfigureAwait(false); } catch { }
+ /// <summary>
+ /// Connects to a specified remote WebSocket endpoint.
+ /// </summary>
+ /// <param name="uri">The URI of the WebSocket endpoint.</param>
+ public async Task ConnectAsync(Uri uri)
+ {
+ // Disconnect first
+ try { await this.DisconnectAsync().ConfigureAwait(false); } catch { }
- // Disallow sending messages
- await this._senderLock.WaitAsync().ConfigureAwait(false);
+ // Disallow sending messages
+ await this._senderLock.WaitAsync().ConfigureAwait(false);
- try
- {
- // This can be null at this point
- this._receiverTokenSource?.Dispose();
- this._socketTokenSource?.Dispose();
+ 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._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._receiverTokenSource = new CancellationTokenSource();
+ this._receiverToken = this._receiverTokenSource.Token;
- this._socketTokenSource = new CancellationTokenSource();
- this._socketToken = this._socketTokenSource.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();
+ 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();
+ }
}
- }
- /// <summary>
- /// Disconnects the WebSocket connection.
- /// </summary>
- /// <param name="code">The code</param>
- /// <param name="message">The message</param>
- /// <created>Lala Sabathil,06.07.2021</created>
- /// <changed>Lala Sabathil,06.07.2021</changed>
- public async Task DisconnectAsync(int code = 1000, string message = "")
- {
- // Ensure that messages cannot be sent
- await this._senderLock.WaitAsync().ConfigureAwait(false);
-
- try
+ /// <summary>
+ /// Disconnects the WebSocket connection.
+ /// </summary>
+ /// <param name="code">The code</param>
+ /// <param name="message">The message</param>
+ /// <created>Lala Sabathil,06.07.2021</created>
+ /// <changed>Lala Sabathil,06.07.2021</changed>
+ public async Task DisconnectAsync(int code = 1000, string message = "")
{
- 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);
+ // Ensure that messages cannot be sent
+ await this._senderLock.WaitAsync().ConfigureAwait(false);
- if (this._receiverTask != null)
- await this._receiverTask.ConfigureAwait(false); // Ensure that receiving completed
+ 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._isConnected)
- this._isConnected = false;
+ if (this._receiverTask != null)
+ await this._receiverTask.ConfigureAwait(false); // Ensure that receiving completed
- if (!this._isDisposed)
- {
- // Cancel all running tasks
- if (this._socketToken.CanBeCanceled)
- this._socketTokenSource?.Cancel();
- this._socketTokenSource?.Dispose();
+ if (this._isConnected)
+ this._isConnected = false;
- if (this._receiverToken.CanBeCanceled)
- this._receiverTokenSource?.Cancel();
- this._receiverTokenSource?.Dispose();
+ 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;
+ this._isDisposed = true;
+ }
+ }
+ catch { }
+ finally
+ {
+ this._senderLock.Release();
}
}
- catch { }
- finally
+
+ /// <summary>
+ /// Send a message to the WebSocket server.
+ /// </summary>
+ /// <param name="message">The message to send.</param>
+ public async Task SendMessageAsync(string message)
{
- this._senderLock.Release();
- }
- }
+ if (this._ws == null)
+ return;
- /// <summary>
- /// Send a message to the WebSocket server.
- /// </summary>
- /// <param name="message">The message to send.</param>
- public async Task SendMessageAsync(string message)
- {
- if (this._ws == null)
- return;
+ if (this._ws.State != WebSocketState.Open && this._ws.State != WebSocketState.CloseReceived)
+ 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 / OUTGOING_CHUNK_SIZE;
+ if (len % OUTGOING_CHUNK_SIZE != 0)
+ segCount++;
- var bytes = Utilities.UTF8.GetBytes(message);
- await this._senderLock.WaitAsync().ConfigureAwait(false);
- try
- {
- var len = bytes.Length;
- var segCount = len / OUTGOING_CHUNK_SIZE;
- if (len % OUTGOING_CHUNK_SIZE != 0)
- segCount++;
+ for (var i = 0; i < segCount; i++)
+ {
+ var segStart = OUTGOING_CHUNK_SIZE * i;
+ var segLen = Math.Min(OUTGOING_CHUNK_SIZE, len - segStart);
- for (var i = 0; i < segCount; i++)
+ await this._ws.SendAsync(new ArraySegment<byte>(bytes, segStart, segLen), WebSocketMessageType.Text, i == segCount - 1, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+ finally
{
- var segStart = OUTGOING_CHUNK_SIZE * i;
- var segLen = Math.Min(OUTGOING_CHUNK_SIZE, len - segStart);
-
- await this._ws.SendAsync(new ArraySegment<byte>(bytes, segStart, segLen), WebSocketMessageType.Text, i == segCount - 1, CancellationToken.None).ConfigureAwait(false);
+ this._senderLock.Release();
}
}
- finally
+
+ /// <summary>
+ /// Adds a header to the default header collection.
+ /// </summary>
+ /// <param name="name">Name of the header to add.</param>
+ /// <param name="value">Value of the header to add.</param>
+ /// <returns>Whether the operation succeeded.</returns>
+ public bool AddDefaultHeader(string name, string value)
{
- this._senderLock.Release();
+ this._defaultHeaders[name] = value;
+ return true;
}
- }
-
- /// <summary>
- /// Adds a header to the default header collection.
- /// </summary>
- /// <param name="name">Name of the header to add.</param>
- /// <param name="value">Value of the header to add.</param>
- /// <returns>Whether the operation succeeded.</returns>
- public bool AddDefaultHeader(string name, string value)
- {
- this._defaultHeaders[name] = value;
- return true;
- }
- /// <summary>
- /// Removes a header from the default header collection.
- /// </summary>
- /// <param name="name">Name of the header to remove.</param>
- /// <returns>Whether the operation succeeded.</returns>
- public bool RemoveDefaultHeader(string name)
- => this._defaultHeaders.Remove(name);
-
- /// <summary>
- /// Disposes of resources used by this WebSocket client instance.
- /// </summary>
- public void Dispose()
- {
- if (this._isDisposed)
- return;
+ /// <summary>
+ /// Removes a header from the default header collection.
+ /// </summary>
+ /// <param name="name">Name of the header to remove.</param>
+ /// <returns>Whether the operation succeeded.</returns>
+ public bool RemoveDefaultHeader(string name)
+ => this._defaultHeaders.Remove(name);
+
+ /// <summary>
+ /// Disposes of resources used by this WebSocket client instance.
+ /// </summary>
+ public void Dispose()
+ {
+ if (this._isDisposed)
+ return;
- this._isDisposed = true;
+ this._isDisposed = true;
- this.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult();
+ this.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult();
- this._receiverTokenSource?.Dispose();
- this._socketTokenSource?.Dispose();
- }
+ this._receiverTokenSource?.Dispose();
+ this._socketTokenSource?.Dispose();
+ }
- /// <summary>
- /// Receivers the loop.
- /// </summary>
- internal async Task ReceiverLoopAsync()
- {
- await Task.Yield();
+ /// <summary>
+ /// Receivers the loop.
+ /// </summary>
+ internal async Task ReceiverLoopAsync()
+ {
+ await Task.Yield();
- var token = this._receiverToken;
- var buffer = new ArraySegment<byte>(new byte[INCOMING_CHUNK_SIZE]);
+ var token = this._receiverToken;
+ var buffer = new ArraySegment<byte>(new byte[INCOMING_CHUNK_SIZE]);
- try
- {
- using var bs = new MemoryStream();
- while (!token.IsCancellationRequested)
+ try
{
- // See https://github.com/RogueException/Discord.Net/commit/ac389f5f6823e3a720aedd81b7805adbdd78b66d
- // for explanation on the cancellation token
-
- WebSocketReceiveResult result;
- byte[] resultBytes;
- do
+ using var bs = new MemoryStream();
+ while (!token.IsCancellationRequested)
{
- result = await this._ws.ReceiveAsync(buffer, CancellationToken.None).ConfigureAwait(false);
+ // See https://github.com/RogueException/Discord.Net/commit/ac389f5f6823e3a720aedd81b7805adbdd78b66d
+ // for explanation on the cancellation token
- if (result.MessageType == WebSocketMessageType.Close)
- break;
+ WebSocketReceiveResult result;
+ byte[] resultBytes;
+ do
+ {
+ result = await this._ws.ReceiveAsync(buffer, CancellationToken.None).ConfigureAwait(false);
- bs.Write(buffer.Array, 0, result.Count);
- }
- while (!result.EndOfMessage);
+ if (result.MessageType == WebSocketMessageType.Close)
+ break;
- resultBytes = new byte[bs.Length];
- bs.Position = 0;
- bs.Read(resultBytes, 0, resultBytes.Length);
- bs.Position = 0;
- bs.SetLength(0);
+ bs.Write(buffer.Array, 0, result.Count);
+ }
+ while (!result.EndOfMessage);
- if (!this._isConnected && result.MessageType != WebSocketMessageType.Close)
- {
- this._isConnected = true;
- await this._connected.InvokeAsync(this, new SocketEventArgs(this._serviceProvider)).ConfigureAwait(false);
- }
+ resultBytes = new byte[bs.Length];
+ bs.Position = 0;
+ bs.Read(resultBytes, 0, resultBytes.Length);
+ bs.Position = 0;
+ bs.SetLength(0);
- 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)
+ if (!this._isConnected && result.MessageType != WebSocketMessageType.Close)
{
- var code = result.CloseStatus.Value;
- code = code == WebSocketCloseStatus.NormalClosure || code == WebSocketCloseStatus.EndpointUnavailable
- ? (WebSocketCloseStatus)4000
- : code;
+ this._isConnected = true;
+ await this._connected.InvokeAsync(this, new SocketEventArgs(this._serviceProvider)).ConfigureAwait(false);
+ }
- await this._ws.CloseOutputAsync(code, result.CloseStatusDescription, CancellationToken.None).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;
+ 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);
}
- catch (Exception ex)
+
+ /// <summary>
+ /// Creates a new instance of <see cref="WebSocketClient"/>.
+ /// </summary>
+ /// <param name="proxy">Proxy to use for this client instance.</param>
+ /// <param name="provider">Service provider.</param>
+ /// <returns>An instance of <see cref="WebSocketClient"/>.</returns>
+ public static IWebSocketClient CreateNew(IWebProxy proxy, IServiceProvider provider)
+ => new WebSocketClient(proxy, provider);
+
+ #region Events
+ /// <summary>
+ /// Triggered when the client connects successfully.
+ /// </summary>
+ public event AsyncEventHandler<IWebSocketClient, SocketEventArgs> Connected
{
- 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);
+ add => this._connected.Register(value);
+ remove => this._connected.Unregister(value);
}
+ private readonly AsyncEvent<WebSocketClient, SocketEventArgs> _connected;
- // Don't await or you deadlock
- // DisconnectAsync waits for this method
- _ = this.DisconnectAsync().ConfigureAwait(false);
- }
-
- /// <summary>
- /// Creates a new instance of <see cref="WebSocketClient"/>.
- /// </summary>
- /// <param name="proxy">Proxy to use for this client instance.</param>
- /// <param name="provider">Service provider.</param>
- /// <returns>An instance of <see cref="WebSocketClient"/>.</returns>
- public static IWebSocketClient CreateNew(IWebProxy proxy, IServiceProvider provider)
- => new WebSocketClient(proxy, provider);
-
- #region Events
- /// <summary>
- /// Triggered when the client connects successfully.
- /// </summary>
- public event AsyncEventHandler<IWebSocketClient, SocketEventArgs> Connected
- {
- add => this._connected.Register(value);
- remove => this._connected.Unregister(value);
- }
- private readonly AsyncEvent<WebSocketClient, SocketEventArgs> _connected;
-
- /// <summary>
- /// Triggered when the client is disconnected.
- /// </summary>
- public event AsyncEventHandler<IWebSocketClient, SocketCloseEventArgs> Disconnected
- {
- add => this._disconnected.Register(value);
- remove => this._disconnected.Unregister(value);
- }
- private readonly AsyncEvent<WebSocketClient, SocketCloseEventArgs> _disconnected;
+ /// <summary>
+ /// Triggered when the client is disconnected.
+ /// </summary>
+ public event AsyncEventHandler<IWebSocketClient, SocketCloseEventArgs> Disconnected
+ {
+ add => this._disconnected.Register(value);
+ remove => this._disconnected.Unregister(value);
+ }
+ private readonly AsyncEvent<WebSocketClient, SocketCloseEventArgs> _disconnected;
- /// <summary>
- /// Triggered when the client receives a message from the remote party.
- /// </summary>
- public event AsyncEventHandler<IWebSocketClient, SocketMessageEventArgs> MessageReceived
- {
- add => this._messageReceived.Register(value);
- remove => this._messageReceived.Unregister(value);
- }
- private readonly AsyncEvent<WebSocketClient, SocketMessageEventArgs> _messageReceived;
+ /// <summary>
+ /// Triggered when the client receives a message from the remote party.
+ /// </summary>
+ public event AsyncEventHandler<IWebSocketClient, SocketMessageEventArgs> MessageReceived
+ {
+ add => this._messageReceived.Register(value);
+ remove => this._messageReceived.Unregister(value);
+ }
+ private readonly AsyncEvent<WebSocketClient, SocketMessageEventArgs> _messageReceived;
- /// <summary>
- /// Triggered when an error occurs in the client.
- /// </summary>
- public event AsyncEventHandler<IWebSocketClient, SocketErrorEventArgs> ExceptionThrown
- {
- add => this._exceptionThrown.Register(value);
- remove => this._exceptionThrown.Unregister(value);
+ /// <summary>
+ /// Triggered when an error occurs in the client.
+ /// </summary>
+ public event AsyncEventHandler<IWebSocketClient, SocketErrorEventArgs> ExceptionThrown
+ {
+ add => this._exceptionThrown.Register(value);
+ remove => this._exceptionThrown.Unregister(value);
+ }
+ private readonly AsyncEvent<WebSocketClient, SocketErrorEventArgs> _exceptionThrown;
+ private IServiceProvider _serviceProvider;
+
+ /// <summary>
+ /// Events the error handler.
+ /// </summary>
+ /// <param name="asyncEvent">The event.</param>
+ /// <param name="ex">The exception.</param>
+ /// <param name="handler">The handler.</param>
+ /// <param name="sender">The sender.</param>
+ /// <param name="eventArgs">The event args.</param>
+ private void EventErrorHandler<TArgs>(AsyncEvent<WebSocketClient, TArgs> asyncEvent, Exception ex, AsyncEventHandler<WebSocketClient, TArgs> handler, WebSocketClient sender, TArgs eventArgs)
+ where TArgs : AsyncEventArgs
+ => this._exceptionThrown.InvokeAsync(this, new SocketErrorEventArgs(this._serviceProvider) { Exception = ex }).ConfigureAwait(false).GetAwaiter().GetResult();
+ #endregion
}
- private readonly AsyncEvent<WebSocketClient, SocketErrorEventArgs> _exceptionThrown;
- private IServiceProvider _serviceProvider;
-
- /// <summary>
- /// Events the error handler.
- /// </summary>
- /// <param name="asyncEvent">The event.</param>
- /// <param name="ex">The exception.</param>
- /// <param name="handler">The handler.</param>
- /// <param name="sender">The sender.</param>
- /// <param name="eventArgs">The event args.</param>
- private void EventErrorHandler<TArgs>(AsyncEvent<WebSocketClient, TArgs> asyncEvent, Exception ex, AsyncEventHandler<WebSocketClient, TArgs> handler, WebSocketClient sender, TArgs eventArgs)
- where TArgs : AsyncEventArgs
- => this._exceptionThrown.InvokeAsync(this, new SocketErrorEventArgs(this._serviceProvider) { Exception = ex }).ConfigureAwait(false).GetAwaiter().GetResult();
- #endregion
}
diff --git a/DisCatSharp/QueryUriBuilder.cs b/DisCatSharp/QueryUriBuilder.cs
index a36a189ba..d1eec8ff0 100644
--- a/DisCatSharp/QueryUriBuilder.cs
+++ b/DisCatSharp/QueryUriBuilder.cs
@@ -1,93 +1,94 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Linq;
-namespace DisCatSharp;
-
-/// <summary>
-/// Represents a query uri builder.
-/// </summary>
-internal class QueryUriBuilder
+namespace DisCatSharp
{
/// <summary>
- /// Gets the source uri.
- /// </summary>
- public Uri SourceUri { get; }
-
- /// <summary>
- /// Gets the query parameters.
- /// </summary>
- public IReadOnlyList<KeyValuePair<string, string>> QueryParameters => this._queryParams;
- private readonly List<KeyValuePair<string, string>> _queryParams = new();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="QueryUriBuilder"/> class.
+ /// Represents a query uri builder.
/// </summary>
- /// <param name="uri">The uri.</param>
- public QueryUriBuilder(string uri)
+ internal class QueryUriBuilder
{
- if (uri == null)
- throw new ArgumentNullException(nameof(uri));
+ /// <summary>
+ /// Gets the source uri.
+ /// </summary>
+ public Uri SourceUri { get; }
- this.SourceUri = new Uri(uri);
- }
+ /// <summary>
+ /// Gets the query parameters.
+ /// </summary>
+ public IReadOnlyList<KeyValuePair<string, string>> QueryParameters => this._queryParams;
+ private readonly List<KeyValuePair<string, string>> _queryParams = new();
- /// <summary>
- /// Initializes a new instance of the <see cref="QueryUriBuilder"/> class.
- /// </summary>
- /// <param name="uri">The uri.</param>
- public QueryUriBuilder(Uri uri)
- {
- if (uri == null)
- throw new ArgumentNullException(nameof(uri));
+ /// <summary>
+ /// Initializes a new instance of the <see cref="QueryUriBuilder"/> class.
+ /// </summary>
+ /// <param name="uri">The uri.</param>
+ public QueryUriBuilder(string uri)
+ {
+ if (uri == null)
+ throw new ArgumentNullException(nameof(uri));
- this.SourceUri = uri;
- }
+ this.SourceUri = new Uri(uri);
+ }
- /// <summary>
- /// Adds a parameter.
- /// </summary>
- /// <param name="key">The key to be added.</param>
- /// <param name="value">The value to be added.</param>
- public QueryUriBuilder AddParameter(string key, string value)
- {
- this._queryParams.Add(new KeyValuePair<string, string>(key, value));
- return this;
- }
+ /// <summary>
+ /// Initializes a new instance of the <see cref="QueryUriBuilder"/> class.
+ /// </summary>
+ /// <param name="uri">The uri.</param>
+ public QueryUriBuilder(Uri uri)
+ {
+ if (uri == null)
+ throw new ArgumentNullException(nameof(uri));
- /// <summary>
- /// Builds the uri.
- /// </summary>
- public Uri Build() =>
- new UriBuilder(this.SourceUri)
+ this.SourceUri = uri;
+ }
+
+ /// <summary>
+ /// Adds a parameter.
+ /// </summary>
+ /// <param name="key">The key to be added.</param>
+ /// <param name="value">The value to be added.</param>
+ public QueryUriBuilder AddParameter(string key, string value)
{
- Query = string.Join("&", this._queryParams.Select(e => Uri.EscapeDataString(e.Key) + '=' + Uri.EscapeDataString(e.Value)))
- }.Uri;
+ this._queryParams.Add(new KeyValuePair<string, string>(key, value));
+ return this;
+ }
- /// <summary>
- /// Returns a readable string.
- /// </summary>
- public override string ToString() => this.Build().ToString();
+ /// <summary>
+ /// Builds the uri.
+ /// </summary>
+ public Uri Build() =>
+ new UriBuilder(this.SourceUri)
+ {
+ Query = string.Join("&", this._queryParams.Select(e => Uri.EscapeDataString(e.Key) + '=' + Uri.EscapeDataString(e.Value)))
+ }.Uri;
+
+ /// <summary>
+ /// Returns a readable string.
+ /// </summary>
+ public override string ToString() => this.Build().ToString();
+ }
}
diff --git a/DisCatSharp/ReadOnlyConcurrentDictionary.cs b/DisCatSharp/ReadOnlyConcurrentDictionary.cs
index 98b9c449a..245f04bf5 100644
--- a/DisCatSharp/ReadOnlyConcurrentDictionary.cs
+++ b/DisCatSharp/ReadOnlyConcurrentDictionary.cs
@@ -1,95 +1,96 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
-namespace DisCatSharp;
-
-/// <summary>
-/// Read-only view of a given <see cref="ConcurrentDictionary{TKey,TValue}"/>.
-/// </summary>
-/// <remarks>
-/// This type exists because <see cref="ConcurrentDictionary{TKey,TValue}"/> is not an
-/// <see cref="IReadOnlyDictionary{TKey,TValue}"/> in .NET Standard 1.1.
-/// </remarks>
-/// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
-/// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
-internal readonly struct ReadOnlyConcurrentDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
+namespace DisCatSharp
{
- private readonly ConcurrentDictionary<TKey, TValue> _underlyingDict;
-
/// <summary>
- /// Creates a new read-only view of the given dictionary.
+ /// Read-only view of a given <see cref="ConcurrentDictionary{TKey,TValue}"/>.
/// </summary>
- /// <param name="underlyingDict">Dictionary to create a view over.</param>
- public ReadOnlyConcurrentDictionary(ConcurrentDictionary<TKey, TValue> underlyingDict)
+ /// <remarks>
+ /// This type exists because <see cref="ConcurrentDictionary{TKey,TValue}"/> is not an
+ /// <see cref="IReadOnlyDictionary{TKey,TValue}"/> in .NET Standard 1.1.
+ /// </remarks>
+ /// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
+ /// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
+ internal readonly struct ReadOnlyConcurrentDictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>
{
- this._underlyingDict = underlyingDict;
- }
+ private readonly ConcurrentDictionary<TKey, TValue> _underlyingDict;
- /// <summary>
- /// Gets the enumerator.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => this._underlyingDict.GetEnumerator();
+ /// <summary>
+ /// Creates a new read-only view of the given dictionary.
+ /// </summary>
+ /// <param name="underlyingDict">Dictionary to create a view over.</param>
+ public ReadOnlyConcurrentDictionary(ConcurrentDictionary<TKey, TValue> underlyingDict)
+ {
+ this._underlyingDict = underlyingDict;
+ }
- /// <summary>
- /// Gets the enumerator.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this._underlyingDict).GetEnumerator();
+ /// <summary>
+ /// Gets the enumerator.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => this._underlyingDict.GetEnumerator();
- /// <summary>
- /// Gets the count.
- /// </summary>
- public int Count => this._underlyingDict.Count;
+ /// <summary>
+ /// Gets the enumerator.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this._underlyingDict).GetEnumerator();
- /// <summary>
- /// Contains the key.
- /// </summary>
- /// <param name="key">The key.</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool ContainsKey(TKey key) => this._underlyingDict.ContainsKey(key);
+ /// <summary>
+ /// Gets the count.
+ /// </summary>
+ public int Count => this._underlyingDict.Count;
- /// <summary>
- /// Tries the get value.
- /// </summary>
- /// <param name="key">The key.</param>
- /// <param name="value">The value.</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool TryGetValue(TKey key, out TValue value) => this._underlyingDict.TryGetValue(key, out value);
+ /// <summary>
+ /// Contains the key.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool ContainsKey(TKey key) => this._underlyingDict.ContainsKey(key);
- public TValue this[TKey key] => this._underlyingDict[key];
+ /// <summary>
+ /// Tries the get value.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ /// <param name="value">The value.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool TryGetValue(TKey key, out TValue value) => this._underlyingDict.TryGetValue(key, out value);
- /// <summary>
- /// Gets the keys.
- /// </summary>
- public IEnumerable<TKey> Keys => this._underlyingDict.Keys;
+ public TValue this[TKey key] => this._underlyingDict[key];
- /// <summary>
- /// Gets the values.
- /// </summary>
- public IEnumerable<TValue> Values => this._underlyingDict.Values;
+ /// <summary>
+ /// Gets the keys.
+ /// </summary>
+ public IEnumerable<TKey> Keys => this._underlyingDict.Keys;
+
+ /// <summary>
+ /// Gets the values.
+ /// </summary>
+ public IEnumerable<TValue> Values => this._underlyingDict.Values;
+ }
}
diff --git a/DisCatSharp/ReadOnlySet.cs b/DisCatSharp/ReadOnlySet.cs
index bdbd167f7..1828093f9 100644
--- a/DisCatSharp/ReadOnlySet.cs
+++ b/DisCatSharp/ReadOnlySet.cs
@@ -1,66 +1,67 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
-namespace DisCatSharp;
-
-/// <summary>
-/// Read-only view of a given <see cref="ISet{T}"/>.
-/// </summary>
-/// <typeparam name="T">Type of the items in the set.</typeparam>
-internal readonly struct ReadOnlySet<T> : IReadOnlyCollection<T>
+namespace DisCatSharp
{
- private readonly ISet<T> _underlyingSet;
-
/// <summary>
- /// Creates a new read-only view of the given set.
+ /// Read-only view of a given <see cref="ISet{T}"/>.
/// </summary>
- /// <param name="sourceSet">Set to create a view over.</param>
- public ReadOnlySet(ISet<T> sourceSet)
+ /// <typeparam name="T">Type of the items in the set.</typeparam>
+ internal readonly struct ReadOnlySet<T> : IReadOnlyCollection<T>
{
- this._underlyingSet = sourceSet;
- }
+ private readonly ISet<T> _underlyingSet;
- /// <summary>
- /// Gets the number of items in the underlying set.
- /// </summary>
- public int Count => this._underlyingSet.Count;
+ /// <summary>
+ /// Creates a new read-only view of the given set.
+ /// </summary>
+ /// <param name="sourceSet">Set to create a view over.</param>
+ public ReadOnlySet(ISet<T> sourceSet)
+ {
+ this._underlyingSet = sourceSet;
+ }
- /// <summary>
- /// Returns an enumerator that iterates through this set view.
- /// </summary>
- /// <returns>Enumerator for the underlying set.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public IEnumerator<T> GetEnumerator()
- => this._underlyingSet.GetEnumerator();
+ /// <summary>
+ /// Gets the number of items in the underlying set.
+ /// </summary>
+ public int Count => this._underlyingSet.Count;
- /// <summary>
- /// Returns an enumerator that iterates through this set view.
- /// </summary>
- /// <returns>Enumerator for the underlying set.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- IEnumerator IEnumerable.GetEnumerator()
- => (this._underlyingSet as IEnumerable).GetEnumerator();
+ /// <summary>
+ /// Returns an enumerator that iterates through this set view.
+ /// </summary>
+ /// <returns>Enumerator for the underlying set.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public IEnumerator<T> GetEnumerator()
+ => this._underlyingSet.GetEnumerator();
+
+ /// <summary>
+ /// Returns an enumerator that iterates through this set view.
+ /// </summary>
+ /// <returns>Enumerator for the underlying set.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ IEnumerator IEnumerable.GetEnumerator()
+ => (this._underlyingSet as IEnumerable).GetEnumerator();
+ }
}
diff --git a/DisCatSharp/RingBuffer.cs b/DisCatSharp/RingBuffer.cs
index f38033df3..73a8fa528 100644
--- a/DisCatSharp/RingBuffer.cs
+++ b/DisCatSharp/RingBuffer.cs
@@ -1,238 +1,239 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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;
using System.Collections.Generic;
using System.Linq;
-namespace DisCatSharp;
-
-/// <summary>
-/// A circular buffer collection.
-/// </summary>
-/// <typeparam name="T">Type of elements within this ring buffer.</typeparam>
-public class RingBuffer<T> : ICollection<T>
+namespace DisCatSharp
{
/// <summary>
- /// Gets the current index of the buffer items.
- /// </summary>
- public int CurrentIndex { get; protected set; }
-
- /// <summary>
- /// Gets the capacity of this ring buffer.
- /// </summary>
- public int Capacity { get; protected set; }
-
- /// <summary>
- /// Gets the number of items in this ring buffer.
- /// </summary>
- public int Count
- => this._reachedEnd ? this.Capacity : this.CurrentIndex;
-
- /// <summary>
- /// Gets whether this ring buffer is read-only.
- /// </summary>
- public bool IsReadOnly
- => false;
-
- /// <summary>
- /// Gets or sets the internal collection of items.
- /// </summary>
- protected T[] InternalBuffer { get; set; }
- private bool _reachedEnd;
-
- /// <summary>
- /// Creates a new ring buffer with specified size.
- /// </summary>
- /// <param name="size">Size of the buffer to create.</param>
- /// <exception cref="System.ArgumentOutOfRangeException" />
- public RingBuffer(int size)
- {
- if (size <= 0)
- throw new ArgumentOutOfRangeException(nameof(size), "Size must be positive.");
-
- this.CurrentIndex = 0;
- this.Capacity = size;
- this.InternalBuffer = new T[this.Capacity];
- }
-
- /// <summary>
- /// Creates a new ring buffer, filled with specified elements.
- /// </summary>
- /// <param name="elements">Elements to fill the buffer with.</param>
- /// <exception cref="System.ArgumentException" />
- /// <exception cref="System.ArgumentOutOfRangeException" />
- public RingBuffer(IEnumerable<T> elements)
- : this(elements, 0)
- { }
-
- /// <summary>
- /// Creates a new ring buffer, filled with specified elements, and starting at specified index.
+ /// A circular buffer collection.
/// </summary>
- /// <param name="elements">Elements to fill the buffer with.</param>
- /// <param name="index">Starting element index.</param>
- /// <exception cref="System.ArgumentException" />
- /// <exception cref="System.ArgumentOutOfRangeException" />
- public RingBuffer(IEnumerable<T> elements, int index)
+ /// <typeparam name="T">Type of elements within this ring buffer.</typeparam>
+ public class RingBuffer<T> : ICollection<T>
{
- if (elements == null || !elements.Any())
- throw new ArgumentException(nameof(elements), "The collection cannot be null or empty.");
+ /// <summary>
+ /// Gets the current index of the buffer items.
+ /// </summary>
+ public int CurrentIndex { get; protected set; }
+
+ /// <summary>
+ /// Gets the capacity of this ring buffer.
+ /// </summary>
+ public int Capacity { get; protected set; }
+
+ /// <summary>
+ /// Gets the number of items in this ring buffer.
+ /// </summary>
+ public int Count
+ => this._reachedEnd ? this.Capacity : this.CurrentIndex;
+
+ /// <summary>
+ /// Gets whether this ring buffer is read-only.
+ /// </summary>
+ public bool IsReadOnly
+ => false;
+
+ /// <summary>
+ /// Gets or sets the internal collection of items.
+ /// </summary>
+ protected T[] InternalBuffer { get; set; }
+ private bool _reachedEnd;
+
+ /// <summary>
+ /// Creates a new ring buffer with specified size.
+ /// </summary>
+ /// <param name="size">Size of the buffer to create.</param>
+ /// <exception cref="System.ArgumentOutOfRangeException" />
+ public RingBuffer(int size)
+ {
+ if (size <= 0)
+ throw new ArgumentOutOfRangeException(nameof(size), "Size must be positive.");
- this.CurrentIndex = index;
- this.InternalBuffer = elements.ToArray();
- this.Capacity = this.InternalBuffer.Length;
+ this.CurrentIndex = 0;
+ this.Capacity = size;
+ this.InternalBuffer = new T[this.Capacity];
+ }
- if (this.CurrentIndex >= this.InternalBuffer.Length || this.CurrentIndex < 0)
- throw new ArgumentOutOfRangeException(nameof(index), "Index must be less than buffer capacity, and greater than zero.");
- }
+ /// <summary>
+ /// Creates a new ring buffer, filled with specified elements.
+ /// </summary>
+ /// <param name="elements">Elements to fill the buffer with.</param>
+ /// <exception cref="System.ArgumentException" />
+ /// <exception cref="System.ArgumentOutOfRangeException" />
+ public RingBuffer(IEnumerable<T> elements)
+ : this(elements, 0)
+ { }
+
+ /// <summary>
+ /// Creates a new ring buffer, filled with specified elements, and starting at specified index.
+ /// </summary>
+ /// <param name="elements">Elements to fill the buffer with.</param>
+ /// <param name="index">Starting element index.</param>
+ /// <exception cref="System.ArgumentException" />
+ /// <exception cref="System.ArgumentOutOfRangeException" />
+ public RingBuffer(IEnumerable<T> elements, int index)
+ {
+ if (elements == null || !elements.Any())
+ throw new ArgumentException(nameof(elements), "The collection cannot be null or empty.");
- /// <summary>
- /// Inserts an item into this ring buffer.
- /// </summary>
- /// <param name="item">Item to insert.</param>
- public void Add(T item)
- {
- this.InternalBuffer[this.CurrentIndex++] = item;
+ this.CurrentIndex = index;
+ this.InternalBuffer = elements.ToArray();
+ this.Capacity = this.InternalBuffer.Length;
- if (this.CurrentIndex == this.Capacity)
- {
- this.CurrentIndex = 0;
- this._reachedEnd = true;
+ if (this.CurrentIndex >= this.InternalBuffer.Length || this.CurrentIndex < 0)
+ throw new ArgumentOutOfRangeException(nameof(index), "Index must be less than buffer capacity, and greater than zero.");
}
- }
- /// <summary>
- /// Gets first item from the buffer that matches the predicate.
- /// </summary>
- /// <param name="predicate">Predicate used to find the item.</param>
- /// <param name="item">Item that matches the predicate, or default value for the type of the items in this ring buffer, if one is not found.</param>
- /// <returns>Whether an item that matches the predicate was found or not.</returns>
- public bool TryGet(Func<T, bool> predicate, out T item)
- {
- for (var i = this.CurrentIndex; i < this.InternalBuffer.Length; i++)
+ /// <summary>
+ /// Inserts an item into this ring buffer.
+ /// </summary>
+ /// <param name="item">Item to insert.</param>
+ public void Add(T item)
{
- if (this.InternalBuffer[i] != null && predicate(this.InternalBuffer[i]))
+ this.InternalBuffer[this.CurrentIndex++] = item;
+
+ if (this.CurrentIndex == this.Capacity)
{
- item = this.InternalBuffer[i];
- return true;
+ this.CurrentIndex = 0;
+ this._reachedEnd = true;
}
}
- for (var i = 0; i < this.CurrentIndex; i++)
+
+ /// <summary>
+ /// Gets first item from the buffer that matches the predicate.
+ /// </summary>
+ /// <param name="predicate">Predicate used to find the item.</param>
+ /// <param name="item">Item that matches the predicate, or default value for the type of the items in this ring buffer, if one is not found.</param>
+ /// <returns>Whether an item that matches the predicate was found or not.</returns>
+ public bool TryGet(Func<T, bool> predicate, out T item)
{
- if (this.InternalBuffer[i] != null && predicate(this.InternalBuffer[i]))
+ for (var i = this.CurrentIndex; i < this.InternalBuffer.Length; i++)
{
- item = this.InternalBuffer[i];
- return true;
+ if (this.InternalBuffer[i] != null && predicate(this.InternalBuffer[i]))
+ {
+ item = this.InternalBuffer[i];
+ return true;
+ }
+ }
+ for (var i = 0; i < this.CurrentIndex; i++)
+ {
+ if (this.InternalBuffer[i] != null && predicate(this.InternalBuffer[i]))
+ {
+ item = this.InternalBuffer[i];
+ return true;
+ }
}
- }
-
- item = default;
- return false;
- }
-
- /// <summary>
- /// Clears this ring buffer and resets the current item index.
- /// </summary>
- public void Clear()
- {
- for (var i = 0; i < this.InternalBuffer.Length; i++)
- this.InternalBuffer[i] = default;
-
- this.CurrentIndex = 0;
- }
- /// <summary>
- /// Checks whether given item is present in the buffer. This method is not implemented. Use <see cref="Contains(Func{T, bool})"/> instead.
- /// </summary>
- /// <param name="item">Item to check for.</param>
- /// <returns>Whether the buffer contains the item.</returns>
- /// <exception cref="System.NotImplementedException" />
- public bool Contains(T item) => throw new NotImplementedException("This method is not implemented. Use .Contains(predicate) instead.");
+ item = default;
+ return false;
+ }
- /// <summary>
- /// Checks whether given item is present in the buffer using given predicate to find it.
- /// </summary>
- /// <param name="predicate">Predicate used to check for the item.</param>
- /// <returns>Whether the buffer contains the item.</returns>
- public bool Contains(Func<T, bool> predicate) => this.InternalBuffer.Any(predicate);
+ /// <summary>
+ /// Clears this ring buffer and resets the current item index.
+ /// </summary>
+ public void Clear()
+ {
+ for (var i = 0; i < this.InternalBuffer.Length; i++)
+ this.InternalBuffer[i] = default;
- /// <summary>
- /// Copies this ring buffer to target array, attempting to maintain the order of items within.
- /// </summary>
- /// <param name="array">Target array.</param>
- /// <param name="index">Index starting at which to copy the items to.</param>
- public void CopyTo(T[] array, int index)
- {
- if (array.Length - index < 1)
- throw new ArgumentException("Target array is too small to contain the elements from this buffer.", nameof(array));
-
- var ci = 0;
- for (var i = this.CurrentIndex; i < this.InternalBuffer.Length; i++)
- array[ci++] = this.InternalBuffer[i];
- for (var i = 0; i < this.CurrentIndex; i++)
- array[ci++] = this.InternalBuffer[i];
- }
+ this.CurrentIndex = 0;
+ }
- /// <summary>
- /// Removes an item from the buffer. This method is not implemented. Use <see cref="Remove(Func{T, bool})"/> instead.
- /// </summary>
- /// <param name="item">Item to remove.</param>
- /// <returns>Whether an item was removed or not.</returns>
- public bool Remove(T item) => throw new NotImplementedException("This method is not implemented. Use .Remove(predicate) instead.");
+ /// <summary>
+ /// Checks whether given item is present in the buffer. This method is not implemented. Use <see cref="Contains(Func{T, bool})"/> instead.
+ /// </summary>
+ /// <param name="item">Item to check for.</param>
+ /// <returns>Whether the buffer contains the item.</returns>
+ /// <exception cref="System.NotImplementedException" />
+ public bool Contains(T item) => throw new NotImplementedException("This method is not implemented. Use .Contains(predicate) instead.");
+
+ /// <summary>
+ /// Checks whether given item is present in the buffer using given predicate to find it.
+ /// </summary>
+ /// <param name="predicate">Predicate used to check for the item.</param>
+ /// <returns>Whether the buffer contains the item.</returns>
+ public bool Contains(Func<T, bool> predicate) => this.InternalBuffer.Any(predicate);
+
+ /// <summary>
+ /// Copies this ring buffer to target array, attempting to maintain the order of items within.
+ /// </summary>
+ /// <param name="array">Target array.</param>
+ /// <param name="index">Index starting at which to copy the items to.</param>
+ public void CopyTo(T[] array, int index)
+ {
+ if (array.Length - index < 1)
+ throw new ArgumentException("Target array is too small to contain the elements from this buffer.", nameof(array));
+
+ var ci = 0;
+ for (var i = this.CurrentIndex; i < this.InternalBuffer.Length; i++)
+ array[ci++] = this.InternalBuffer[i];
+ for (var i = 0; i < this.CurrentIndex; i++)
+ array[ci++] = this.InternalBuffer[i];
+ }
- /// <summary>
- /// Removes an item from the buffer using given predicate to find it.
- /// </summary>
- /// <param name="predicate">Predicate used to find the item.</param>
- /// <returns>Whether an item was removed or not.</returns>
- public bool Remove(Func<T, bool> predicate)
- {
- for (var i = 0; i < this.InternalBuffer.Length; i++)
+ /// <summary>
+ /// Removes an item from the buffer. This method is not implemented. Use <see cref="Remove(Func{T, bool})"/> instead.
+ /// </summary>
+ /// <param name="item">Item to remove.</param>
+ /// <returns>Whether an item was removed or not.</returns>
+ public bool Remove(T item) => throw new NotImplementedException("This method is not implemented. Use .Remove(predicate) instead.");
+
+ /// <summary>
+ /// Removes an item from the buffer using given predicate to find it.
+ /// </summary>
+ /// <param name="predicate">Predicate used to find the item.</param>
+ /// <returns>Whether an item was removed or not.</returns>
+ public bool Remove(Func<T, bool> predicate)
{
- if (this.InternalBuffer[i] != null && predicate(this.InternalBuffer[i]))
+ for (var i = 0; i < this.InternalBuffer.Length; i++)
{
- this.InternalBuffer[i] = default;
- return true;
+ if (this.InternalBuffer[i] != null && predicate(this.InternalBuffer[i]))
+ {
+ this.InternalBuffer[i] = default;
+ return true;
+ }
}
+
+ return false;
}
- return false;
+ /// <summary>
+ /// Returns an enumerator for this ring buffer.
+ /// </summary>
+ /// <returns>Enumerator for this ring buffer.</returns>
+ public IEnumerator<T> GetEnumerator() =>
+ !this._reachedEnd
+ ? this.InternalBuffer.AsEnumerable().GetEnumerator()
+ : this.InternalBuffer.Skip(this.CurrentIndex)
+ .Concat(this.InternalBuffer.Take(this.CurrentIndex))
+ .GetEnumerator();
+
+ /// <summary>
+ /// Returns an enumerator for this ring buffer.
+ /// </summary>
+ /// <returns>Enumerator for this ring buffer.</returns>
+ IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
-
- /// <summary>
- /// Returns an enumerator for this ring buffer.
- /// </summary>
- /// <returns>Enumerator for this ring buffer.</returns>
- public IEnumerator<T> GetEnumerator() =>
- !this._reachedEnd
- ? this.InternalBuffer.AsEnumerable().GetEnumerator()
- : this.InternalBuffer.Skip(this.CurrentIndex)
- .Concat(this.InternalBuffer.Take(this.CurrentIndex))
- .GetEnumerator();
-
- /// <summary>
- /// Returns an enumerator for this ring buffer.
- /// </summary>
- /// <returns>Enumerator for this ring buffer.</returns>
- IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
diff --git a/DisCatSharp/Utilities.cs b/DisCatSharp/Utilities.cs
index 22774a69d..6caf3f4a7 100644
--- a/DisCatSharp/Utilities.cs
+++ b/DisCatSharp/Utilities.cs
@@ -1,461 +1,462 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 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.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Net;
using Microsoft.Extensions.Logging;
-namespace DisCatSharp;
-
-/// <summary>
-/// Various Discord-related utilities.
-/// </summary>
-public static class Utilities
+namespace DisCatSharp
{
/// <summary>
- /// Gets the version of the library
- /// </summary>
- internal static string VersionHeader { get; set; }
-
- /// <summary>
- /// Gets or sets the permission strings.
- /// </summary>
- internal static Dictionary<Permissions, string> PermissionStrings { get; set; }
-
- /// <summary>
- /// Gets the utf8 encoding
- /// </summary>
- // ReSharper disable once InconsistentNaming
- internal static UTF8Encoding UTF8 { get; } = new(false);
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Utilities"/> class.
+ /// Various Discord-related utilities.
/// </summary>
- static Utilities()
+ public static class Utilities
{
- PermissionStrings = new Dictionary<Permissions, string>();
- var t = typeof(Permissions);
- var ti = t.GetTypeInfo();
- var vals = Enum.GetValues(t).Cast<Permissions>();
-
- foreach (var xv in vals)
+ /// <summary>
+ /// Gets the version of the library
+ /// </summary>
+ internal static string VersionHeader { get; set; }
+
+ /// <summary>
+ /// Gets or sets the permission strings.
+ /// </summary>
+ internal static Dictionary<Permissions, string> PermissionStrings { get; set; }
+
+ /// <summary>
+ /// Gets the utf8 encoding
+ /// </summary>
+ // ReSharper disable once InconsistentNaming
+ internal static UTF8Encoding UTF8 { get; } = new(false);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Utilities"/> class.
+ /// </summary>
+ static Utilities()
{
- var xsv = xv.ToString();
- var xmv = ti.DeclaredMembers.FirstOrDefault(xm => xm.Name == xsv);
- var xav = xmv.GetCustomAttribute<PermissionStringAttribute>();
-
- PermissionStrings[xv] = xav.String;
+ PermissionStrings = new Dictionary<Permissions, string>();
+ var t = typeof(Permissions);
+ var ti = t.GetTypeInfo();
+ var vals = Enum.GetValues(t).Cast<Permissions>();
+
+ foreach (var xv in vals)
+ {
+ var xsv = xv.ToString();
+ var xmv = ti.DeclaredMembers.FirstOrDefault(xm => xm.Name == xsv);
+ var xav = xmv.GetCustomAttribute<PermissionStringAttribute>();
+
+ PermissionStrings[xv] = xav.String;
+ }
+
+ var a = typeof(DiscordClient).GetTypeInfo().Assembly;
+
+ var vs = "";
+ var iv = a.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
+ if (iv != null)
+ vs = iv.InformationalVersion;
+ else
+ {
+ var v = a.GetName().Version;
+ vs = v.ToString(3);
+ }
+
+ VersionHeader = $"DiscordBot (https://github.com/Aiko-IT-Systems/DisCatSharp, v{vs})";
}
- var a = typeof(DiscordClient).GetTypeInfo().Assembly;
-
- var vs = "";
- var iv = a.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
- if (iv != null)
- vs = iv.InformationalVersion;
- else
+ /// <summary>
+ /// Gets the api base uri.
+ /// </summary>
+ /// <param name="config">The config</param>
+ /// <returns>A string.</returns>
+ internal static string GetApiBaseUri(DiscordConfiguration config = null)
+ => config == null ? Endpoints.BASE_URI + "9" : config.UseCanary ? Endpoints.CANARY_URI + config.ApiVersion : Endpoints.BASE_URI + config.ApiVersion;
+
+ /// <summary>
+ /// Gets the api uri for.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <param name="config">The config</param>
+ /// <returns>An Uri.</returns>
+ internal static Uri GetApiUriFor(string path, DiscordConfiguration config)
+ => new($"{GetApiBaseUri(config)}{path}");
+
+ /// <summary>
+ /// Gets the api uri for.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <param name="queryString">The query string.</param>
+ /// <param name="config">The config</param>
+ /// <returns>An Uri.</returns>
+ internal static Uri GetApiUriFor(string path, string queryString, DiscordConfiguration config)
+ => new($"{GetApiBaseUri(config)}{path}{queryString}");
+
+ /// <summary>
+ /// Gets the api uri builder for.
+ /// </summary>
+ /// <param name="path">The path.</param>
+ /// <param name="config">The config</param>
+ /// <returns>A QueryUriBuilder.</returns>
+ internal static QueryUriBuilder GetApiUriBuilderFor(string path, DiscordConfiguration config)
+ => new($"{GetApiBaseUri(config)}{path}");
+
+ /// <summary>
+ /// Gets the formatted token.
+ /// </summary>
+ /// <param name="client">The client.</param>
+ /// <returns>A string.</returns>
+ internal static string GetFormattedToken(BaseDiscordClient client) => GetFormattedToken(client.Configuration);
+
+ /// <summary>
+ /// Gets the formatted token.
+ /// </summary>
+ /// <param name="config">The config.</param>
+ /// <returns>A string.</returns>
+ internal static string GetFormattedToken(DiscordConfiguration config) =>
+ config.TokenType switch
+ {
+ TokenType.Bearer => $"Bearer {config.Token}",
+ TokenType.Bot => $"Bot {config.Token}",
+ _ => throw new ArgumentException("Invalid token type specified.", nameof(config.Token)),
+ };
+
+ /// <summary>
+ /// Gets the base headers.
+ /// </summary>
+ /// <returns>A Dictionary.</returns>
+ internal static Dictionary<string, string> GetBaseHeaders()
+ => new();
+
+ /// <summary>
+ /// Gets the user agent.
+ /// </summary>
+ /// <returns>A string.</returns>
+ internal static string GetUserAgent()
+ => VersionHeader;
+
+ /// <summary>
+ /// Contains the user mentions.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>A bool.</returns>
+ internal static bool ContainsUserMentions(string message)
{
- var v = a.GetName().Version;
- vs = v.ToString(3);
+ var pattern = @"<@(\d+)>";
+ var regex = new Regex(pattern, RegexOptions.ECMAScript);
+ return regex.IsMatch(message);
}
- VersionHeader = $"DiscordBot (https://github.com/Aiko-IT-Systems/DisCatSharp, v{vs})";
- }
-
- /// <summary>
- /// Gets the api base uri.
- /// </summary>
- /// <param name="config">The config</param>
- /// <returns>A string.</returns>
- internal static string GetApiBaseUri(DiscordConfiguration config = null)
- => config == null ? Endpoints.BASE_URI + "9" : config.UseCanary ? Endpoints.CANARY_URI + config.ApiVersion : Endpoints.BASE_URI + config.ApiVersion;
-
- /// <summary>
- /// Gets the api uri for.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="config">The config</param>
- /// <returns>An Uri.</returns>
- internal static Uri GetApiUriFor(string path, DiscordConfiguration config)
- => new($"{GetApiBaseUri(config)}{path}");
-
- /// <summary>
- /// Gets the api uri for.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="queryString">The query string.</param>
- /// <param name="config">The config</param>
- /// <returns>An Uri.</returns>
- internal static Uri GetApiUriFor(string path, string queryString, DiscordConfiguration config)
- => new($"{GetApiBaseUri(config)}{path}{queryString}");
-
- /// <summary>
- /// Gets the api uri builder for.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <param name="config">The config</param>
- /// <returns>A QueryUriBuilder.</returns>
- internal static QueryUriBuilder GetApiUriBuilderFor(string path, DiscordConfiguration config)
- => new($"{GetApiBaseUri(config)}{path}");
-
- /// <summary>
- /// Gets the formatted token.
- /// </summary>
- /// <param name="client">The client.</param>
- /// <returns>A string.</returns>
- internal static string GetFormattedToken(BaseDiscordClient client) => GetFormattedToken(client.Configuration);
-
- /// <summary>
- /// Gets the formatted token.
- /// </summary>
- /// <param name="config">The config.</param>
- /// <returns>A string.</returns>
- internal static string GetFormattedToken(DiscordConfiguration config) =>
- config.TokenType switch
+ /// <summary>
+ /// Contains the nickname mentions.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>A bool.</returns>
+ internal static bool ContainsNicknameMentions(string message)
{
- TokenType.Bearer => $"Bearer {config.Token}",
- TokenType.Bot => $"Bot {config.Token}",
- _ => throw new ArgumentException("Invalid token type specified.", nameof(config.Token)),
- };
-
- /// <summary>
- /// Gets the base headers.
- /// </summary>
- /// <returns>A Dictionary.</returns>
- internal static Dictionary<string, string> GetBaseHeaders()
- => new();
-
- /// <summary>
- /// Gets the user agent.
- /// </summary>
- /// <returns>A string.</returns>
- internal static string GetUserAgent()
- => VersionHeader;
-
- /// <summary>
- /// Contains the user mentions.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>A bool.</returns>
- internal static bool ContainsUserMentions(string message)
- {
- var pattern = @"<@(\d+)>";
- var regex = new Regex(pattern, RegexOptions.ECMAScript);
- return regex.IsMatch(message);
- }
-
- /// <summary>
- /// Contains the nickname mentions.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>A bool.</returns>
- internal static bool ContainsNicknameMentions(string message)
- {
- var pattern = @"<@!(\d+)>";
- var regex = new Regex(pattern, RegexOptions.ECMAScript);
- return regex.IsMatch(message);
- }
-
- /// <summary>
- /// Contains the channel mentions.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>A bool.</returns>
- internal static bool ContainsChannelMentions(string message)
- {
- var pattern = @"<#(\d+)>";
- var regex = new Regex(pattern, RegexOptions.ECMAScript);
- return regex.IsMatch(message);
- }
-
- /// <summary>
- /// Contains the role mentions.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>A bool.</returns>
- internal static bool ContainsRoleMentions(string message)
- {
- var pattern = @"<@&(\d+)>";
- var regex = new Regex(pattern, RegexOptions.ECMAScript);
- return regex.IsMatch(message);
- }
-
- /// <summary>
- /// Contains the emojis.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>A bool.</returns>
- internal static bool ContainsEmojis(string message)
- {
- var pattern = @"<a?:(.*):(\d+)>";
- var regex = new Regex(pattern, RegexOptions.ECMAScript);
- return regex.IsMatch(message);
- }
-
- /// <summary>
- /// Gets the user mentions.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>A list of ulong.</returns>
- internal static IEnumerable<ulong> GetUserMentions(DiscordMessage message)
- {
- var regex = new Regex(@"<@!?(\d+)>", RegexOptions.ECMAScript);
- var matches = regex.Matches(message.Content);
- foreach (Match match in matches)
- yield return ulong.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
- }
-
- /// <summary>
- /// Gets the role mentions.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>A list of ulong.</returns>
- internal static IEnumerable<ulong> GetRoleMentions(DiscordMessage message)
- {
- var regex = new Regex(@"<@&(\d+)>", RegexOptions.ECMAScript);
- var matches = regex.Matches(message.Content);
- foreach (Match match in matches)
- yield return ulong.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
- }
-
- /// <summary>
- /// Gets the channel mentions.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>A list of ulong.</returns>
- internal static IEnumerable<ulong> GetChannelMentions(DiscordMessage message)
- {
- var regex = new Regex(@"<#(\d+)>", RegexOptions.ECMAScript);
- var matches = regex.Matches(message.Content);
- foreach (Match match in matches)
- yield return ulong.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
- }
-
- /// <summary>
- /// Gets the emojis.
- /// </summary>
- /// <param name="message">The message.</param>
- /// <returns>A list of ulong.</returns>
- internal static IEnumerable<ulong> GetEmojis(DiscordMessage message)
- {
- var regex = new Regex(@"<a?:([a-zA-Z0-9_]+):(\d+)>", RegexOptions.ECMAScript);
- var matches = regex.Matches(message.Content);
- foreach (Match match in matches)
- yield return ulong.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture);
- }
-
- /// <summary>
- /// Are the valid slash command name.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <returns>A bool.</returns>
- internal static bool IsValidSlashCommandName(string name)
- {
- var regex = new Regex(@"^[\w-]{1,32}$");
- return regex.IsMatch(name);
- }
-
- /// <summary>
- /// Checks the thread auto archive duration feature.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <param name="taad">The taad.</param>
- /// <returns>A bool.</returns>
- internal static bool CheckThreadAutoArchiveDurationFeature(DiscordGuild guild, ThreadAutoArchiveDuration taad)
- => true;
-
- /// <summary>
- /// Checks the thread private feature.
- /// </summary>
- /// <param name="guild">The guild.</param>
- /// <returns>A bool.</returns>
- internal static bool CheckThreadPrivateFeature(DiscordGuild guild) => guild.PremiumTier.HasFlag(PremiumTier.TierTwo) || guild.Features.CanCreatePrivateThreads;
-
- /// <summary>
- /// Have the message intents.
- /// </summary>
- /// <param name="intents">The intents.</param>
- /// <returns>A bool.</returns>
- internal static bool HasMessageIntents(DiscordIntents intents)
- => intents.HasIntent(DiscordIntents.GuildMessages) || intents.HasIntent(DiscordIntents.DirectMessages);
-
- /// <summary>
- /// Have the reaction intents.
- /// </summary>
- /// <param name="intents">The intents.</param>
- /// <returns>A bool.</returns>
- internal static bool HasReactionIntents(DiscordIntents intents)
- => intents.HasIntent(DiscordIntents.GuildMessageReactions) || intents.HasIntent(DiscordIntents.DirectMessageReactions);
+ var pattern = @"<@!(\d+)>";
+ var regex = new Regex(pattern, RegexOptions.ECMAScript);
+ return regex.IsMatch(message);
+ }
- /// <summary>
- /// Have the typing intents.
- /// </summary>
- /// <param name="intents">The intents.</param>
- /// <returns>A bool.</returns>
- internal static bool HasTypingIntents(DiscordIntents intents)
- => intents.HasIntent(DiscordIntents.GuildMessageTyping) || intents.HasIntent(DiscordIntents.DirectMessageTyping);
+ /// <summary>
+ /// Contains the channel mentions.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>A bool.</returns>
+ internal static bool ContainsChannelMentions(string message)
+ {
+ var pattern = @"<#(\d+)>";
+ var regex = new Regex(pattern, RegexOptions.ECMAScript);
+ return regex.IsMatch(message);
+ }
- // https://discord.com/developers/docs/topics/gateway#sharding-sharding-formula
- /// <summary>
- /// Gets a shard id from a guild id and total shard count.
- /// </summary>
- /// <param name="guildId">The guild id the shard is on.</param>
- /// <param name="shardCount">The total amount of shards.</param>
- /// <returns>The shard id.</returns>
- public static int GetShardId(ulong guildId, int shardCount)
- => (int)(guildId >> 22) % shardCount;
+ /// <summary>
+ /// Contains the role mentions.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>A bool.</returns>
+ internal static bool ContainsRoleMentions(string message)
+ {
+ var pattern = @"<@&(\d+)>";
+ var regex = new Regex(pattern, RegexOptions.ECMAScript);
+ return regex.IsMatch(message);
+ }
- /// <summary>
- /// Helper method to create a <see cref="System.DateTimeOffset"/> from Unix time seconds for targets that do not support this natively.
- /// </summary>
- /// <param name="unixTime">Unix time seconds to convert.</param>
- /// <param name="shouldThrow">Whether the method should throw on failure. Defaults to true.</param>
- /// <returns>Calculated <see cref="System.DateTimeOffset"/>.</returns>
- public static DateTimeOffset GetDateTimeOffset(long unixTime, bool shouldThrow = true)
- {
- try
+ /// <summary>
+ /// Contains the emojis.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>A bool.</returns>
+ internal static bool ContainsEmojis(string message)
{
- return DateTimeOffset.FromUnixTimeSeconds(unixTime);
+ var pattern = @"<a?:(.*):(\d+)>";
+ var regex = new Regex(pattern, RegexOptions.ECMAScript);
+ return regex.IsMatch(message);
}
- catch (Exception)
+
+ /// <summary>
+ /// Gets the user mentions.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>A list of ulong.</returns>
+ internal static IEnumerable<ulong> GetUserMentions(DiscordMessage message)
{
- if (shouldThrow)
- throw;
+ var regex = new Regex(@"<@!?(\d+)>", RegexOptions.ECMAScript);
+ var matches = regex.Matches(message.Content);
+ foreach (Match match in matches)
+ yield return ulong.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
+ }
- return DateTimeOffset.MinValue;
+ /// <summary>
+ /// Gets the role mentions.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>A list of ulong.</returns>
+ internal static IEnumerable<ulong> GetRoleMentions(DiscordMessage message)
+ {
+ var regex = new Regex(@"<@&(\d+)>", RegexOptions.ECMAScript);
+ var matches = regex.Matches(message.Content);
+ foreach (Match match in matches)
+ yield return ulong.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
}
- }
- /// <summary>
- /// Helper method to create a <see cref="System.DateTimeOffset"/> from Unix time milliseconds for targets that do not support this natively.
- /// </summary>
- /// <param name="unixTime">Unix time milliseconds to convert.</param>
- /// <param name="shouldThrow">Whether the method should throw on failure. Defaults to true.</param>
- /// <returns>Calculated <see cref="System.DateTimeOffset"/>.</returns>
- public static DateTimeOffset GetDateTimeOffsetFromMilliseconds(long unixTime, bool shouldThrow = true)
- {
- try
+ /// <summary>
+ /// Gets the channel mentions.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>A list of ulong.</returns>
+ internal static IEnumerable<ulong> GetChannelMentions(DiscordMessage message)
{
- return DateTimeOffset.FromUnixTimeMilliseconds(unixTime);
+ var regex = new Regex(@"<#(\d+)>", RegexOptions.ECMAScript);
+ var matches = regex.Matches(message.Content);
+ foreach (Match match in matches)
+ yield return ulong.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
}
- catch (Exception)
+
+ /// <summary>
+ /// Gets the emojis.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>A list of ulong.</returns>
+ internal static IEnumerable<ulong> GetEmojis(DiscordMessage message)
{
- if (shouldThrow)
- throw;
+ var regex = new Regex(@"<a?:([a-zA-Z0-9_]+):(\d+)>", RegexOptions.ECMAScript);
+ var matches = regex.Matches(message.Content);
+ foreach (Match match in matches)
+ yield return ulong.Parse(match.Groups[2].Value, CultureInfo.InvariantCulture);
+ }
- return DateTimeOffset.MinValue;
+ /// <summary>
+ /// Are the valid slash command name.
+ /// </summary>
+ /// <param name="name">The name.</param>
+ /// <returns>A bool.</returns>
+ internal static bool IsValidSlashCommandName(string name)
+ {
+ var regex = new Regex(@"^[\w-]{1,32}$");
+ return regex.IsMatch(name);
}
- }
- /// <summary>
- /// Helper method to calculate Unix time seconds from a <see cref="System.DateTimeOffset"/> for targets that do not support this natively.
- /// </summary>
- /// <param name="dto"><see cref="System.DateTimeOffset"/> to calculate Unix time for.</param>
- /// <returns>Calculated Unix time.</returns>
- public static long GetUnixTime(DateTimeOffset dto)
- => dto.ToUnixTimeMilliseconds();
+ /// <summary>
+ /// Checks the thread auto archive duration feature.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <param name="taad">The taad.</param>
+ /// <returns>A bool.</returns>
+ internal static bool CheckThreadAutoArchiveDurationFeature(DiscordGuild guild, ThreadAutoArchiveDuration taad)
+ => true;
+
+ /// <summary>
+ /// Checks the thread private feature.
+ /// </summary>
+ /// <param name="guild">The guild.</param>
+ /// <returns>A bool.</returns>
+ internal static bool CheckThreadPrivateFeature(DiscordGuild guild) => guild.PremiumTier.HasFlag(PremiumTier.TierTwo) || guild.Features.CanCreatePrivateThreads;
+
+ /// <summary>
+ /// Have the message intents.
+ /// </summary>
+ /// <param name="intents">The intents.</param>
+ /// <returns>A bool.</returns>
+ internal static bool HasMessageIntents(DiscordIntents intents)
+ => intents.HasIntent(DiscordIntents.GuildMessages) || intents.HasIntent(DiscordIntents.DirectMessages);
+
+ /// <summary>
+ /// Have the reaction intents.
+ /// </summary>
+ /// <param name="intents">The intents.</param>
+ /// <returns>A bool.</returns>
+ internal static bool HasReactionIntents(DiscordIntents intents)
+ => intents.HasIntent(DiscordIntents.GuildMessageReactions) || intents.HasIntent(DiscordIntents.DirectMessageReactions);
+
+ /// <summary>
+ /// Have the typing intents.
+ /// </summary>
+ /// <param name="intents">The intents.</param>
+ /// <returns>A bool.</returns>
+ internal static bool HasTypingIntents(DiscordIntents intents)
+ => intents.HasIntent(DiscordIntents.GuildMessageTyping) || intents.HasIntent(DiscordIntents.DirectMessageTyping);
+
+ // https://discord.com/developers/docs/topics/gateway#sharding-sharding-formula
+ /// <summary>
+ /// Gets a shard id from a guild id and total shard count.
+ /// </summary>
+ /// <param name="guildId">The guild id the shard is on.</param>
+ /// <param name="shardCount">The total amount of shards.</param>
+ /// <returns>The shard id.</returns>
+ public static int GetShardId(ulong guildId, int shardCount)
+ => (int)(guildId >> 22) % shardCount;
+
+ /// <summary>
+ /// Helper method to create a <see cref="System.DateTimeOffset"/> from Unix time seconds for targets that do not support this natively.
+ /// </summary>
+ /// <param name="unixTime">Unix time seconds to convert.</param>
+ /// <param name="shouldThrow">Whether the method should throw on failure. Defaults to true.</param>
+ /// <returns>Calculated <see cref="System.DateTimeOffset"/>.</returns>
+ public static DateTimeOffset GetDateTimeOffset(long unixTime, bool shouldThrow = true)
+ {
+ try
+ {
+ return DateTimeOffset.FromUnixTimeSeconds(unixTime);
+ }
+ catch (Exception)
+ {
+ if (shouldThrow)
+ throw;
+
+ return DateTimeOffset.MinValue;
+ }
+ }
- /// <summary>
- /// Computes a timestamp from a given snowflake.
- /// </summary>
- /// <param name="snowflake">Snowflake to compute a timestamp from.</param>
- /// <returns>Computed timestamp.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static DateTimeOffset GetSnowflakeTime(this ulong snowflake)
- => DiscordClient.DiscordEpoch.AddMilliseconds(snowflake >> 22);
+ /// <summary>
+ /// Helper method to create a <see cref="System.DateTimeOffset"/> from Unix time milliseconds for targets that do not support this natively.
+ /// </summary>
+ /// <param name="unixTime">Unix time milliseconds to convert.</param>
+ /// <param name="shouldThrow">Whether the method should throw on failure. Defaults to true.</param>
+ /// <returns>Calculated <see cref="System.DateTimeOffset"/>.</returns>
+ public static DateTimeOffset GetDateTimeOffsetFromMilliseconds(long unixTime, bool shouldThrow = true)
+ {
+ try
+ {
+ return DateTimeOffset.FromUnixTimeMilliseconds(unixTime);
+ }
+ catch (Exception)
+ {
+ if (shouldThrow)
+ throw;
+
+ return DateTimeOffset.MinValue;
+ }
+ }
- /// <summary>
- /// Converts this <see cref="Permissions"/> into human-readable format.
- /// </summary>
- /// <param name="perm">Permissions enumeration to convert.</param>
- /// <returns>Human-readable permissions.</returns>
- public static string ToPermissionString(this Permissions perm)
- {
- if (perm == Permissions.None)
- return PermissionStrings[perm];
+ /// <summary>
+ /// Helper method to calculate Unix time seconds from a <see cref="System.DateTimeOffset"/> for targets that do not support this natively.
+ /// </summary>
+ /// <param name="dto"><see cref="System.DateTimeOffset"/> to calculate Unix time for.</param>
+ /// <returns>Calculated Unix time.</returns>
+ public static long GetUnixTime(DateTimeOffset dto)
+ => dto.ToUnixTimeMilliseconds();
+
+ /// <summary>
+ /// Computes a timestamp from a given snowflake.
+ /// </summary>
+ /// <param name="snowflake">Snowflake to compute a timestamp from.</param>
+ /// <returns>Computed timestamp.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static DateTimeOffset GetSnowflakeTime(this ulong snowflake)
+ => DiscordClient.DiscordEpoch.AddMilliseconds(snowflake >> 22);
+
+ /// <summary>
+ /// Converts this <see cref="Permissions"/> into human-readable format.
+ /// </summary>
+ /// <param name="perm">Permissions enumeration to convert.</param>
+ /// <returns>Human-readable permissions.</returns>
+ public static string ToPermissionString(this Permissions perm)
+ {
+ if (perm == Permissions.None)
+ return PermissionStrings[perm];
- perm &= PermissionMethods.FullPerms;
+ perm &= PermissionMethods.FullPerms;
- var strs = PermissionStrings
- .Where(xkvp => xkvp.Key != Permissions.None && (perm & xkvp.Key) == xkvp.Key)
- .Select(xkvp => xkvp.Value);
+ var strs = PermissionStrings
+ .Where(xkvp => xkvp.Key != Permissions.None && (perm & xkvp.Key) == xkvp.Key)
+ .Select(xkvp => xkvp.Value);
- return string.Join(", ", strs.OrderBy(xs => xs));
- }
+ return string.Join(", ", strs.OrderBy(xs => xs));
+ }
- /// <summary>
- /// Checks whether this string contains given characters.
- /// </summary>
- /// <param name="str">String to check.</param>
- /// <param name="characters">Characters to check for.</param>
- /// <returns>Whether the string contained these characters.</returns>
- public static bool Contains(this string str, params char[] characters)
- {
- foreach (var xc in str)
- if (characters.Contains(xc))
- return true;
+ /// <summary>
+ /// Checks whether this string contains given characters.
+ /// </summary>
+ /// <param name="str">String to check.</param>
+ /// <param name="characters">Characters to check for.</param>
+ /// <returns>Whether the string contained these characters.</returns>
+ public static bool Contains(this string str, params char[] characters)
+ {
+ foreach (var xc in str)
+ if (characters.Contains(xc))
+ return true;
- return false;
- }
+ return false;
+ }
- /// <summary>
- /// Logs the task fault.
- /// </summary>
- /// <param name="task">The task.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="level">The level.</param>
- /// <param name="eventId">The event id.</param>
- /// <param name="message">The message.</param>
- internal static void LogTaskFault(this Task task, ILogger logger, LogLevel level, EventId eventId, string message)
- {
- if (task == null)
- throw new ArgumentNullException(nameof(task));
+ /// <summary>
+ /// Logs the task fault.
+ /// </summary>
+ /// <param name="task">The task.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="level">The level.</param>
+ /// <param name="eventId">The event id.</param>
+ /// <param name="message">The message.</param>
+ internal static void LogTaskFault(this Task task, ILogger logger, LogLevel level, EventId eventId, string message)
+ {
+ if (task == null)
+ throw new ArgumentNullException(nameof(task));
- if (logger == null)
- return;
+ if (logger == null)
+ return;
- task.ContinueWith(t => logger.Log(level, eventId, t.Exception, message), TaskContinuationOptions.OnlyOnFaulted);
- }
+ task.ContinueWith(t => logger.Log(level, eventId, t.Exception, message), TaskContinuationOptions.OnlyOnFaulted);
+ }
- /// <summary>
- /// Deconstructs the.
- /// </summary>
- /// <param name="kvp">The kvp.</param>
- /// <param name="key">The key.</param>
- /// <param name="value">The value.</param>
- internal static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
- {
- key = kvp.Key;
- value = kvp.Value;
+ /// <summary>
+ /// Deconstructs the.
+ /// </summary>
+ /// <param name="kvp">The kvp.</param>
+ /// <param name="key">The key.</param>
+ /// <param name="value">The value.</param>
+ internal static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
+ {
+ key = kvp.Key;
+ value = kvp.Value;
+ }
}
}

File Metadata

Mime Type
application/octet-stream
Expires
Sat, May 11, 02:05 (1 d, 23 h)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
Dd6.ioHqsXqY
Default Alt Text
(7 MB)

Event Timeline