Labs builds are available on nuget (`https://www.nuget.org/packages/Discord.Net.Labs/`) and myget (`https://www.myget.org/F/discord-net-labs/api/v3/index.json`).
### Unstable
## Compiling
Nightly builds are available through our MyGet feed (`https://www.myget.org/F/discord-net/api/v3/index.json`).
These builds target the dev branch.
In order to compile Discord.Net, you require the following:
## 🛑 Known Issues
### Using Visual Studio
### WebSockets (Win7 and earlier)
- [Visual Studio 2017](https://www.microsoft.com/net/core#windowsvs2017)
This library generally abides by [Semantic Versioning](https://semver.org). Packages are published in `MAJOR.MINOR.PATCH` version format.
### WebSockets (Win7 and earlier)
### Patch component
.NET Core 1.1 does not support WebSockets on Win7 and earlier. This issue has been fixed since the release of .NET Core 2.1. It is recommended to target .NET Core 2.1 or above for your project if you wish to run your bot on legacy platforms; alternatively, you may choose to install the [Discord.Net.Providers.WS4Net](https://www.nuget.org/packages/Discord.Net.Providers.WS4Net/) package.
An increment of the **PATCH** component always indicates that an internal-only change was made, generally a bugfix. These changes will not affect the public-facing API in any way, and are always guaranteed to be forward- and backwards-compatible with your codebase, any pre-compiled dependencies of your codebase.
## Versioning Guarantees
### Minor component
This library generally abides by [Semantic Versioning](https://semver.org). Packages are published in MAJOR.MINOR.PATCH version format.
An increment of the **MINOR** component indicates that some addition was made to the library,
and this addition is not backwards-compatible with prior versions.
However, Discord.Net **does not guarantee forward-compatibility** on minor additions.
In other words, we permit a limited set of breaking changes on a minor version bump.
An increment of the PATCH component always indicates that an internal-only change was made, generally a bugfix. These changes will not affect the public-facing API in any way, and are always guaranteed to be forward- and backwards-compatible with your codebase, any pre-compiled dependencies of your codebase.
Due to the nature of the Discord API, we will oftentimes need to add a property to an entity to support the latest API changes.
Discord.Net provides interfaces as a method of consuming entities; and as such, introducing a new field to an entity is technically a breaking change.
Major version bumps generally indicate some major change to the library,
and as such we are hesitant to bump the major version for every minor addition to the library.
To compromise, we have decided that interfaces should be treated as **consumable only**,
and your applications should typically not be implementing interfaces.
An increment of the MINOR component indicates that some addition was made to the library, and this addition is not backwards-compatible with prior versions. However, Discord.Net **does not guarantee forward-compatibility** on minor additions. In other words, we permit a limited set of breaking changes on a minor version bump.
> For applications where interfaces are implemented, such as in test mocks, we apologize for this inconsistency with SemVer.
Due to the nature of the Discord API, we will oftentimes need to add a property to an entity to support the latest API changes. Discord.Net provides interfaces as a method of consuming entities; and as such, introducing a new field to an entity is technically a breaking change. Major version bumps generally indicate some major change to the library, and as such we are hesitant to bump the major version for every minor addition to the library. To compromise, we have decided that interfaces should be treated as **consumable only**, and your applications should typically not be implementing interfaces. (For applications where interfaces are implemented, such as in test mocks, we apologize for this inconsistency with SemVer).
While we will never break the API (outside of interface changes) on minor builds,
we will occasionally need to break the ABI, by introducing parameters to a method to match changes upstream with Discord.
As such, a minor version increment may require you to recompile your code, and dependencies,
such as addons, may also need to be recompiled and republished on the newer version.
When a binary breaking change is made, the change will be noted in the release notes.
Furthermore, while we will never break the API (outside of interface changes) on minor builds, we will occasionally need to break the ABI, by introducing parameters to a method to match changes upstream with Discord. As such, a minor version increment may require you to recompile your code, and dependencies, such as addons, may also need to be recompiled and republished on the newer version. When a binary breaking change is made, the change will be noted in the release notes.
### Major component
An increment of the MAJOR component indicates that breaking changes have been made to the library; consumers should check the release notes to determine what changes need to be made.
An increment of the **MAJOR** component indicates that breaking changes have been made to the library;
consumers should check the release notes to determine what changes need to be made.
You can inject registered services into any class that is registered to the `IServiceProvider`.
This can be done through property or constructor.
> [!NOTE]
> As mentioned above, the dependency *and* the target class have to be registered in order for the serviceprovider to resolve it.
## Injecting through a constructor
Services can be injected from the constructor of the class.
This is the preferred approach, because it automatically locks the readonly field in place with the provided service and isn't accessible outside of the class.
If you accept `Command/InteractionService` or `IServiceProvider` as a parameter in your constructor or as an injectable property,
these entries will be filled by the `Command/InteractionService` that the module is loaded from and the `IServiceProvider` that is passed into it respectively.
> [!NOTE]
> Annotating a property with a [DontInjectAttribute] attribute will
> prevent the property from being injected.
## Services
Because modules are transient of nature and will reinstantiate on every request,
it is suggested to create a singleton service behind it to hold values across multiple command executions.
@@ -279,8 +279,8 @@ Meaning, the constructor parameters and public settable properties of a module w
For more information on dependency injection, read the [DependencyInjection] guides.
> [!NOTE]
> On every command execution, module dependencies are resolved using a new service scope which allows you to utilize scoped service instances, just like in Asp.Net.
> Including the precondition checks, every module method is executed using the same service scope and service scopes are disposed right after the `AfterExecute` method returns.
> On every command execution, if the 'AutoServiceScopes' option is enabled in the config , module dependencies are resolved using a new service scope which allows you to utilize scoped service instances, just like in Asp.Net.
> Including the precondition checks, every module method is executed using the same service scope and service scopes are disposed right after the `AfterExecute` method returns. This doesn't apply to methods other than `ExecuteAsync()`.
## Module Groups
@@ -291,6 +291,11 @@ By nesting commands inside a module that is tagged with [GroupAttribute] you can
> Although creating nested module stuctures are allowed,
> you are not permitted to use more than 2 [GroupAttribute]'s in module hierarchy.
> [!NOTE]
> To not use the command group's name as a prefix for component or modal interaction's custom id set `ignoreGroupNames` parameter to `true` in classes with [GroupAttribute]
>
> However, you have to be careful to prevent overlapping ids of buttons and modals.
[!code-csharp[Command Group Example](samples/intro/groupmodule.cs)]
## Executing Commands
@@ -303,8 +308,19 @@ Any of the following socket events can be used to execute commands:
- [AutocompleteExecuted]
- [UserCommandExecuted]
- [MessageCommandExecuted]
- [ModalExecuted]
These events will trigger for the specific type of interaction they inherit their name from. The [InteractionCreated] event will trigger for all.
An example of executing a command from an event can be seen here:
Commands can be either executed on the gateway thread or on a seperate thread from the thread pool.
This behaviour can be configured by changing the `RunMode` property of `InteractionServiceConfig` or by setting the *runMode* parameter of a command attribute.
Commands can be either executed on the gateway thread or on a seperate thread from the thread pool. This behaviour can be configured by changing the *RunMode* property of `InteractionServiceConfig` or by setting the *runMode* parameter of a command attribute.
> [!WARNING]
> In the example above, no form of post-execution is presented.
> Please carefully read the [Post Execution Documentation] for the best approach in resolving the result based on your `RunMode`.
You can also configure the way [InteractionService] executes the commands.
By default, commands are executed using `ConstructorInfo.Invoke()` to create module instances and
@@ -330,10 +346,13 @@ Command registration methods can only be used after the gateway client is ready
Methods like `AddModulesToGuildAsync()`, `AddCommandsToGuildAsync()`, `AddModulesGloballyAsync()` and `AddCommandsGloballyAsync()`
can be used to register cherry picked modules or commands to global/guild scopes.
> [!NOTE]
> [DontAutoRegisterAttribute] can be used on module classes to prevent `RegisterCommandsGloballyAsync()` and `RegisterCommandsToGuildAsync()` from registering them to the Discord.
> [!NOTE]
> In debug environment, since Global commands can take up to 1 hour to register/update,
> it is adviced to register your commands to a test guild for your changes to take effect immediately.
> You can use preprocessor directives to create a simple logic for registering commands as seen above
> You can use preprocessor directives to create a simple logic for registering commands as seen above.
## Interaction Utility
@@ -357,10 +376,52 @@ respond to the Interactions within your command modules you need to perform the
delegate can be used to create HTTP responses from a deserialized json object string.
- Use the interaction endpoints of the module base instead of the interaction object (ie. `RespondAsync()`, `FollowupAsync()`...).
## Localization
Discord Slash Commands support name/description localization. Localization is available for names and descriptions of Slash Command Groups ([GroupAttribute]), Slash Commands ([SlashCommandAttribute]), Slash Command parameters and Slash Command Parameter Choices. Interaction Service can be initialized with an `ILocalizationManager` instance in its config which is used to create the necessary localization dictionaries on command registration. Interaction Service has two built-in `ILocalizationManager` implementations: `ResxLocalizationManager` and `JsonLocalizationManager`.
### ResXLocalizationManager
`ResxLocalizationManager` uses `.` delimited key names to traverse the resource files and get the localized strings (`group1.group2.command.parameter.name`). A `ResxLocalizationManager` instance must be initialized with a base resource name, a target assembly and a collection of `CultureInfo`s. Every key path must end with either `.name` or `.description`, including parameter choice strings. [Discord.Tools.LocalizationTemplate.Resx](https://www.nuget.org/packages/Discord.Tools.LocalizationTemplate.Resx) dotnet tool can be used to create localization file templates.
### JsonLocalizationManager
`JsonLocaliationManager` uses a nested data structure similar to Discord's Application Commands schema. You can get the Json schema [here](https://gist.github.com/Cenngo/d46a881de24823302f66c3c7e2f7b254). `JsonLocalizationManager` accepts a base path and a base file name and automatically discovers every resource file ( \basePath\fileName.locale.json ). A Json resource file should have a structure similar to:
The Text Command Service is bundled with a very barebone Dependency
Injection service for your convenience. It is recommended that you use
DI when writing your modules.
> [!WARNING]
> If you were brought here from the Interaction Service guides,
> make sure to replace all namespaces that imply `Discord.Commands` with `Discord.Interactions`
## Setup
1. Create a @Microsoft.Extensions.DependencyInjection.ServiceCollection.
2. Add the dependencies to the service collection that you wish
to use in the modules.
3. Build the service collection into a service provider.
4. Pass the service collection into @Discord.Commands.CommandService.AddModulesAsync* / @Discord.Commands.CommandService.AddModuleAsync* , @Discord.Commands.CommandService.ExecuteAsync* .
**For Windows users, precompiled binaries are available for your convienence [here](https://github.com/discord-net/Discord.Net/tree/dev/voice-natives).**
For Linux Users, you will need to compile [Sodium] and [Opus] from
source, or install them from your package manager.
**For Linux users, you will need to compile [Sodium] and [Opus] from source, or install them from your package manager.**
/// The guild has access to set an animated guild icon.
/// </summary>
AnimatedIcon = 1 << 1,
AnimatedIcon = 1L << 1,
/// <summary>
/// The guild has access to set a guild banner image.
/// </summary>
Banner = 1 << 2,
Banner = 1L << 2,
/// <summary>
/// The guild has access to channel banners.
/// </summary>
ChannelBanner = 1 << 3,
ChannelBanner = 1L << 3,
/// <summary>
/// The guild has access to use commerce features (i.e. create store channels).
/// </summary>
Commerce = 1 << 4,
Commerce = 1L << 4,
/// <summary>
/// The guild can enable welcome screen, Membership Screening, stage channels and discovery, and receives community updates.
/// </summary>
Community = 1 << 5,
Community = 1L << 5,
/// <summary>
/// The guild is able to be discovered in the directory.
/// </summary>
Discoverable = 1 << 6,
Discoverable = 1L << 6,
/// <summary>
/// The guild has discoverable disabled.
/// </summary>
DiscoverableDisabled = 1 << 7,
DiscoverableDisabled = 1L << 7,
/// <summary>
/// The guild has enabled discoverable before.
/// </summary>
EnabledDiscoverableBefore = 1 << 8,
EnabledDiscoverableBefore = 1L << 8,
/// <summary>
/// The guild is able to be featured in the directory.
/// </summary>
Featureable = 1 << 9,
Featureable = 1L << 9,
/// <summary>
/// The guild has a force relay.
/// </summary>
ForceRelay = 1 << 10,
ForceRelay = 1L << 10,
/// <summary>
/// The guild has a directory entry.
/// </summary>
HasDirectoryEntry = 1 << 11,
HasDirectoryEntry = 1L << 11,
/// <summary>
/// The guild is a hub.
/// </summary>
Hub = 1 << 12,
Hub = 1L << 12,
/// <summary>
/// You shouldn't be here...
/// </summary>
InternalEmployeeOnly = 1 << 13,
InternalEmployeeOnly = 1L << 13,
/// <summary>
/// The guild has access to set an invite splash background.
/// </summary>
InviteSplash = 1 << 14,
InviteSplash = 1L << 14,
/// <summary>
/// The guild is linked to a hub.
/// </summary>
LinkedToHub = 1 << 15,
LinkedToHub = 1L << 15,
/// <summary>
/// The guild has member profiles.
/// </summary>
MemberProfiles = 1 << 16,
MemberProfiles = 1L << 16,
/// <summary>
/// The guild has enabled <seealso href="https://discord.com/developers/docs/resources/guild#membership-screening-object">Membership Screening</seealso>.
/// </summary>
MemberVerificationGateEnabled = 1 << 17,
MemberVerificationGateEnabled = 1L << 17,
/// <summary>
/// The guild has enabled monetization.
/// </summary>
MonetizationEnabled = 1 << 18,
MonetizationEnabled = 1L << 18,
/// <summary>
/// The guild has more emojis.
/// </summary>
MoreEmoji = 1 << 19,
MoreEmoji = 1L << 19,
/// <summary>
/// The guild has increased custom sticker slots.
/// </summary>
MoreStickers = 1 << 20,
MoreStickers = 1L << 20,
/// <summary>
/// The guild has access to create news channels.
/// </summary>
News = 1 << 21,
News = 1L << 21,
/// <summary>
/// The guild has new thread permissions.
/// </summary>
NewThreadPermissions = 1 << 22,
NewThreadPermissions = 1L << 22,
/// <summary>
/// The guild is partnered.
/// </summary>
Partnered = 1 << 23,
Partnered = 1L << 23,
/// <summary>
/// The guild has a premium tier three override; guilds made by Discord usually have this.
/// </summary>
PremiumTier3Override = 1 << 24,
PremiumTier3Override = 1L << 24,
/// <summary>
/// The guild can be previewed before joining via Membership Screening or the directory.
/// </summary>
PreviewEnabled = 1 << 25,
PreviewEnabled = 1L << 25,
/// <summary>
/// The guild has access to create private threads.
/// </summary>
PrivateThreads = 1 << 26,
PrivateThreads = 1L << 26,
/// <summary>
/// The guild has relay enabled.
/// </summary>
RelayEnabled = 1 << 27,
RelayEnabled = 1L << 27,
/// <summary>
/// The guild is able to set role icons.
/// </summary>
RoleIcons = 1 << 28,
RoleIcons = 1L << 28,
/// <summary>
/// The guild has role subscriptions available for purchase.
/// </summary>
RoleSubscriptionsAvailableForPurchase = 1 << 29,
RoleSubscriptionsAvailableForPurchase = 1L << 29,
/// <summary>
/// The guild has role subscriptions enabled.
/// </summary>
RoleSubscriptionsEnabled = 1 << 30,
RoleSubscriptionsEnabled = 1L << 30,
/// <summary>
/// The guild has access to the seven day archive time for threads.
/// </summary>
SevenDayThreadArchive = 1 << 31,
SevenDayThreadArchive = 1L << 31,
/// <summary>
/// The guild has text in voice enabled.
/// </summary>
TextInVoiceEnabled = 1 << 32,
TextInVoiceEnabled = 1L << 32,
/// <summary>
/// The guild has threads enabled.
/// </summary>
ThreadsEnabled = 1 << 33,
ThreadsEnabled = 1L << 33,
/// <summary>
/// The guild has testing threads enabled.
/// </summary>
ThreadsEnabledTesting = 1 << 34,
ThreadsEnabledTesting = 1L << 34,
/// <summary>
/// The guild has the default thread auto archive.
/// </summary>
ThreadsDefaultAutoArchiveDuration = 1 << 35,
ThreadsDefaultAutoArchiveDuration = 1L << 35,
/// <summary>
/// The guild has access to the three day archive time for threads.
/// </summary>
ThreeDayThreadArchive = 1 << 36,
ThreeDayThreadArchive = 1L << 36,
/// <summary>
/// The guild has enabled ticketed events.
/// </summary>
TicketedEventsEnabled = 1 << 37,
TicketedEventsEnabled = 1L << 37,
/// <summary>
/// The guild has access to set a vanity URL.
/// </summary>
VanityUrl = 1 << 38,
VanityUrl = 1L << 38,
/// <summary>
/// The guild is verified.
/// </summary>
Verified = 1 << 39,
Verified = 1L << 39,
/// <summary>
/// The guild has access to set 384kbps bitrate in voice (previously VIP voice servers).
/// Whether to include full localization dictionaries in the returned objects,
/// instead of the localized name and description fields.
/// </param>
/// <param name="locale">The target locale of the localized name and description fields. Sets the <c>X-Discord-Locale</c> header, which takes precedence over <c>Accept-Language</c>.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection
/// of application commands found within the guild.
/// This will be empty if the privileged <see cref="GatewayIntents.MessageContent"/> is disabled.
/// </remarks>
/// <returns>
/// A string that contains the body of the message; note that this field may be empty if there is an embed.
/// </returns>
@@ -55,6 +58,9 @@ namespace Discord
/// <summary>
/// Gets the clean content for this message.
/// </summary>
/// <remarks>
/// This will be empty if the privileged <see cref="GatewayIntents.MessageContent"/> is disabled.
/// </remarks>
/// <returns>
/// A string that contains the body of the message stripped of mentions, markdown, emojis and pings; note that this field may be empty if there is an embed.
/// <summary> This intent includes TYPING_START </summary>
DirectMessageTyping = 1 << 14,
/// <summary> This intent includes GUILD_SCHEDULED_EVENT_CREATE, GUILD_SCHEDULED_EVENT_UPDATE, GUILD_SCHEDULED_EVENT_DELETE, GUILD_SCHEDULED_EVENT_USER_ADD, GUILD_SCHEDULED_EVENT_USER_REMOVE </summary>
/// <summary>
/// This intent defines if the content within messages received by MESSAGE_CREATE is available or not.
/// This is a privileged intent and needs to be enabled in the developer portal.
/// </summary>
MessageContent = 1 << 15,
/// <summary>
/// This intent includes GUILD_SCHEDULED_EVENT_CREATE, GUILD_SCHEDULED_EVENT_UPDATE, GUILD_SCHEDULED_EVENT_DELETE, GUILD_SCHEDULED_EVENT_USER_ADD, GUILD_SCHEDULED_EVENT_USER_REMOVE
/// </summary>
GuildScheduledEvents = 1 << 16,
/// <summary>
/// This intent includes all but <see cref="GuildMembers"/> and <see cref="GuildPresences"/>
@@ -51,6 +58,6 @@ namespace Discord
/// <summary>
/// This intent includes all of them, including privileged ones.
/// </summary>
All = AllUnprivileged | GuildMembers | GuildPresences
All = AllUnprivileged | GuildMembers | GuildPresences | MessageContent
/// <param name="withLocalizations">Whether to include full localization dictionaries in the returned objects, instead of the name localized and description localized fields.</param>
/// <param name="locale">The target locale of the localized name and description fields. Sets <c>X-Discord-Locale</c> header, which takes precedence over <c>Accept-Language</c>.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of global
throw new ArgumentException($"{typeof(T).FullName} isn't referenced by any registered Modal Interaction Command and doesn't have a cached {typeof(ModalInfo)}");
/// Gets the minimum length allowed for a string type parameter.
/// </summary>
public int? MinLength { get; }
/// <summary>
/// Gets the maximum length allowed for a string type parameter.
/// </summary>
public int? MaxLength { get; }
/// <summary>
/// Gets the <see cref="TypeConverter{T}"/> that will be used to convert the incoming <see cref="Discord.WebSocket.SocketSlashCommandDataOption"/> into
/// use <see cref="AddModulesToGuildAsync(IGuild, bool, ModuleInfo[])"/>. Registering a commands without group names might cause the command traversal to fail.
/// </remarks>
/// <param name="guild">The target guild.</param>
/// <param name="deleteMissing">If <see langword="false"/>, this operation will not delete the commands that are missing from <see cref="InteractionService"/>.</param>
/// <param name="commands">Commands to be registered to Discord.</param>
/// <returns>
/// A task representing the command registration process. The task result contains the active application commands of the target guild.
/// Register Application Commands from <paramref name="commands"/> to a guild.
/// </summary>
/// <remarks>
/// Commands will be registered as standalone commands, if you want the <see cref="GroupAttribute"/> to take effect,
/// use <see cref="AddModulesToGuildAsync(ulong, bool, ModuleInfo[])"/>. Registering a commands without group names might cause the command traversal to fail.
/// <param name="deleteMissing">If <see langword="false"/>, this operation will not delete the commands that are missing from <see cref="InteractionService"/>.</param>
/// <param name="commands">Commands to be registered to Discord.</param>
/// <returns>
/// A task representing the command registration process. The task result contains the active application commands of the target guild.
/// Register Application Commands from modules provided in <paramref name="modules"/> to a guild.
/// </summary>
/// <param name="guild">The target guild.</param>
/// <param name="deleteMissing">If <see langword="false"/>, this operation will not delete the commands that are missing from <see cref="InteractionService"/>.</param>
/// <param name="modules">Modules to be registered to Discord.</param>
/// <returns>
/// A task representing the command registration process. The task result contains the active application commands of the target guild.
/// <param name="deleteMissing">If <see langword="false"/>, this operation will not delete the commands that are missing from <see cref="InteractionService"/>.</param>
/// <param name="modules">Modules to be registered to Discord.</param>
/// <returns>
/// A task representing the command registration process. The task result contains the active application commands of the target guild.
/// Register Application Commands from modules provided in <paramref name="modules"/> as global commands.
/// </summary>
/// <param name="deleteMissing">If <see langword="false"/>, this operation will not delete the commands that are missing from <see cref="InteractionService"/>.</param>
/// <param name="modules">Modules to be registered to Discord.</param>
/// <returns>
/// A task representing the command registration process. The task result contains the active application commands of the target guild.
/// Commands will be registered as standalone commands, if you want the <see cref="GroupAttribute"/> to take effect,
/// use <see cref="AddModulesToGuildAsync(IGuild, bool, ModuleInfo[])"/>. Registering a commands without group names might cause the command traversal to fail.
/// </remarks>
/// <param name="deleteMissing">If <see langword="false"/>, this operation will not delete the commands that are missing from <see cref="InteractionService"/>.</param>
/// <param name="commands">Commands to be registered to Discord.</param>
/// <returns>
/// A task representing the command registration process. The task result contains the active application commands of the target guild.
Thank you for your continuous support to the Openl Qizhi Community AI Collaboration Platform. In order to protect your usage rights and ensure network security, we updated the Openl Qizhi Community AI Collaboration Platform Usage Agreement in January 2024. The updated agreement specifies that users are prohibited from using intranet penetration tools. After you click "Agree and continue", you can continue to use our services. Thank you for your cooperation and understanding.