Documentation Overhaul (#1161)
* Add XML docs
* Clean up style switcher
* Squash commits on branch docs/faq-n-patches
* Fix broken theme selector
* Add local image embed instruction
* Add a bunch of XML docs
* Add a bunch of XML docs
* Fix broken search
+ DocFX by default ships with an older version of jQuery, switching to a newer version confuses parts of the DocFX Javascript.
* Minor fixes for CONTRIBUTING.md and README.md
* Clean up filterConfig.yml
+ New config exposes Discord.Net namespace since it has several common public exceptions that may be helpful to users
* Add XML docs
* Read token from Environment Variable instead of hardcode
* Add XMLDocs
* Compress some assets & add OAuth2 URL generator
* Fix sample link & add missing pictures
* Add tag examples
* Fix embed docs consistency
* Add details regarding userbot support
* Add XML Docs
* Add XML Docs
* Add XML Docs
* Minor fixes in documentations
+ Fix unescaped '<'
+ Fix typo
* Fix seealso for preconditions and add missing descriptions
* Add missing exceptions
* Document exposed TypeReaders
* Fix letter-casing for files
* Add 'last modified' plugin
Source: https://github.com/Still34/DocFx.Plugin.LastModified
Licensed under MIT License
* XML Docs
* Fix minor consistencies & redundant impl
* Add properties examples to overwrite
* Fix missing Username prop
* Add warning for bulk-delete endpoint
* Replace note block
* Add BaseSocketClient docs
* Add XML docs
* Replace langword null to code block null instead
- Because DocFX sucks at rendering langword
* Replace all langword placements with code block
* Add more IGuild docs
* Add details to SpotifyGame
* Initial proofread of the articles
* Add explanation for RunMode
* Add event docs
- MessageReceived
- ChannelUpdated/Destroyed/Created
* Fix light theme link color
* Fix xml docs error
* Add partial documentation for audit log impl
* Add documentation for some REST-based objects
* Add partial documentation for audit log objects
* Add more XML comments to quotation mark alias map stuff, including an example
* Add reference to CommandServiceConfig from the util docs'
* Add explanation that if " is removed then it wont work
* Fix missing service provider in example
* Add documentation for new INestedChannel
* Add documentation
* Add documentation for new API version & few events
* Revise guide paragraphs/samples
+ Fix various formatting.
+ Provide a more detailed walkthrough for dependency injection.
+ Add C# note at intro.
* Fix typos & formatting
* Improve group module example
* Small amount to see if I'm doing it right
* Remove/cleanup redundant variables
* Fix EnterTypingState impl for doc inheritance
* Fix Test to resolve changes made in 15b58e
* Improve precondition documentation
+ Add precondition usage sample
+ Add precondition group usage sample
+ Move precondition samples to its own sample folder
* Move samples to individual folders
* Clarify token source
* Cleanup styling of README.md for docs
* Replace InvalidPathChars for NS1.3
* InvalidPathChars does not exist in NS1.3; replaced with GetInvalidPathChars instead.
* Add a missing change for 2c7cc738
* Update LastModified to v1.1.0 & add license
* Rewrite installation page for Core 2.1
* Fix anchor link
* Bump post-processor to v1.1.1
* Add fixes to partial file & add license
* Moved theme-switcher code to scripts partial file
+ Add author's MIT license to featherlight javascript
* Remove unused bootstrap plugin
* Bump LastModified plugin
* Changed the path from 'lastmodified' to 'last-modified' for consistency
* Cleanup README & Contribution guide
* Changes to last pr
* Fix GetCategoryAsync docs
* Proofread and cleanup articles
* Change passive voice in "Get Started" to active
* Fix improper preposition in Commands Introduction page
* Fix minor grammar mistakes in "Your First Bot" (future tense -> present tense/subjunctive mood -> indicative mood/proper noun casing/incorrect noun/add missing article)
* Fix minor grammar mistakes in "Installation" (missing article)
* no hablo ingles
* Try try try again
* I'm sure you're having as much fun as I am
* Cleanup TOC & fix titles
* Improve styling
+ Change title font to Noto Sans
+ Add materialized design for commit message box
* Add DescriptionGenerator plugin
* Add nightly section for clarification
* Fix typos in Nightlies & Post-execution
* Bump DescriptionGenerator to v1.1.0
+ This build adds the functionality of generating managed references' summary into the description tag.
* Initial emoji article draft
* Add 'additional information' section for emoji article
* Add cosmetic changes to the master css
* Alter info box color
+ Add transition to article content
* Add clarification in the emoji article
* Emphasize that normal emoji string will not translate to its Unicode representation.
* Clean up or add some of the samples featured in the article.
+ Add emoji/emote declaration section for clarification.
+ Add WebSocket emote sample.
- Remove inconsistent styling ('wacky memes' proves to be too out of place).
* Improve readability for nightlies article
* Move 'Bundled Preconditions' section
* Bump LastModified to fix UTC DateTime parsing
* Add langwordMapping.yml
* Add XML docs
* Add VSC workspace rule
* The root workspace limits the ruler to 120 characters for member documentations and excludes folders such as 'samples' and 'docs'.
* The docs workspace limits the ruler to 70 characters for standard conceptual article to comply with documentation's CONTRIBUTING.md rule, and excludes temprorary folders created by DocFX.
* Update CONTRIBUTING.md
* Add documentation style rule
* Fix styling of several member documentation
* Fix ' />' caused by Agent Smith oddities
* Fix styling to be more specific about the mention of IDs
* Fix exception summary to comply with official Microsoft Docs style
* References
https://docs.microsoft.com/en-us/dotnet/api/system.argumentnullexception?view=netframework-4.7.2
https://docs.microsoft.com/en-us/dotnet/api/system.platformnotsupportedexception?view=netframework-4.7.2
https://docs.microsoft.com/en-us/dotnet/api/system.badimageformatexception?view=netframework-4.7.2
* Add XML documentations
* Shift color return docs
* Fix minor docs
* Added documentation for SocketDMChannel, SocketGuildChannel, and SocketTextChannel
* Add XML docs
* Corrections to SocketGuildChannel
* Corrections to SocketTextChannel
* Corrections to SocketDMChannel
* Swapped out 'id' for 'snowflake identifier
* Swapped out 'id' for 'snowflake identifier'
* SocketDMChannel amendments
* SocketGuildChannel amendments
* SocketTextChannel amendments
* Add XML docs & patch return types
+ Starting from this commit, all return types for tasks will use style similar to most documentations featured on docs.microsoft.com
References:
https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontext.-ctor?view=efcore-2.1
https://docs.microsoft.com/en-us/dotnet/api/system.io.filestream.readasync?view=netcore-2.1
https://docs.microsoft.com/en-us/dotnet/api/system.io.textwriter.writelineasync?view=netcore-2.1#System_IO_TextWriter_WriteLineAsync_System_Char___
And many more other asynchronous method documentations featured in the latest BCL.
* Added documentation for many audit log data types, fixed vowel indefinite articles
* Change audit log data types to start with 'Contains' (verb) instead of an article
* Fix some documentation issues and document some more audit log data types
* Fix English posession
* Add XML doc
* Documented two more types
* Documented RoleCreateAuditLogData
* Document remaining audit log data types
* Added RestDMChannel documentation
* Added RestGuildChannel documentation
* Added RestTextChannel documentation
* Added RestVoiceChannel documentation
* Added RestUser documentation
* Added RestRole documentation
* Added RestMessage documentation
* Slightly better wording
* Contains -> Contains a piece of (describe article)
* [EN] Present perf. -> past perf.
* Add XML docs
* Fix arrow alignment
* Clarify supported nullable type
* Fixed a typo in ISnowflakeEntity
* Added RestUser Documentation
* Added RestInvite documentation
* Add XML docs & minor optimizations
* Minor optimization for doc rendering
* Rollback font optimization changes
* Amendments to RestUser
* Added SocketDMChannel documentation
* Added RestDMChannel documentation
* Added RestGuild documentation
* Adjustment to SocketDMChannel
* Added minimal descriptions from the API documentation for Integration types
* Added obsolete mention to the ReadMessages flag.
* Added remarks about 2FA requirement for guild permissions
* Added xmldoc for GuildPermission methods
* Added xml doc for ToAllowList and ToDenyList
* Added specification of how the bits of the color raw value are packed
* Added discord API documentation to IConnection interface
* I can spell :^)
* Fix whitespace in ChannelPermission
* fix spacing of values in guildpermission
* Made changes to get field descriptions from feedback, added returns tag to IConnection
* Added property get standard for IntegrationAccount
* Added property get pattern to xml docs and identical returns tag.
* Change all color class references to struct
...because it isn't a class.
* Add XML docs
* Rewrote the returns tags in IGuildIntegration, removed the ones I was unsure about.
* Rewrote the rest of the returns tags
* Amendments
* Cleanup doc for c1d78189
* Added types to <returns> tags where missing
* Added second sample for adding reactions
* Added some class summaries
* Missed a period
* Amendments
* restored the removed line break
* Removed unnecessary see tag
* Use consistent quotation marks around subscribers, the name for these users are dependant on the source of where they are integrated from (youtube or twitch), so we should not use a name that is specific to one platform
* Add <remarks> tag to the IGuildIntegration xmldocs
* Fix grammar issue
* Update DescriptionGenerator
* Cleanup of https://github.com/Still34/Discord.Net/pull/8
* Cleanup previous PR
* Fix for misleading behaviour in the emoji guide
+ Original lines stated that sending a emoji wrapped in colon will not be parsed, but that was incorrect; replaced with reactions instead of sending messages as the example
* Add strings for dictionary in DotSettings
* Add XML docs
* Fix lots of typos in comments
+ Geez, I didn't know there were so many.
* Add XML docs & rewrite GetMessagesAsync docs
This commit rewrites the remarks section of GetMessagesAsync, as well as adding examples to several methods.
* Update 'Your First Bot'
+ This commit reflects the new changes made to the Discord Application Developer Portal after its major update
* Initial optimization for DocFX render & add missing files
* Add examples in message methods
* Cleanup https://github.com/RogueException/Discord.Net/pull/1128
* Fix first bot note
* Cleanup FAQ structure
* Add XML docs
* Update docfx plugins
* Fix navbar collapsing issue
* Fix broken xref
* Cleanup FAQ section
+ Add introductory paragraphs to each FAQ section.
+ Add 'missing dependency' entry to commands FAQ.
* Split commands FAQ to 'General' and 'DI' sections.
* Cleanup https://github.com/RogueException/Discord.Net/pull/1139
* Fix missing namespace
* Add missing highlighting css for the light theme
* Add additional clarification for installing packages
* Add indentation to example for clarity
* Cleanup several articles to be more human-friendly and easier to read
* Remove RPC-related notes
* Cleanup slow-mode-related documentation strings
* Add an additional note about cross-guild emote usage
* Add CreateTextChannel sample
* Add XMLDocs
This commit is contained in:
@@ -2,14 +2,37 @@ using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary> Provides aliases for a command. </summary>
|
||||
/// <summary>
|
||||
/// Marks the aliases for a command.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This attribute allows a command to have one or multiple aliases. In other words, the base command can have
|
||||
/// multiple aliases when triggering the command itself, giving the end-user more freedom of choices when giving
|
||||
/// hot-words to trigger the desired command. See the example for a better illustration.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// In the following example, the command can be triggered with the base name, "stats", or either "stat" or
|
||||
/// "info".
|
||||
/// <code language="cs">
|
||||
/// [Command("stats")]
|
||||
/// [Alias("stat", "info")]
|
||||
/// public async Task GetStatsAsync(IUser user)
|
||||
/// {
|
||||
/// // ...pull stats
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||
public class AliasAttribute : Attribute
|
||||
{
|
||||
/// <summary> The aliases which have been defined for the command. </summary>
|
||||
/// <summary>
|
||||
/// Gets the aliases which have been defined for the command.
|
||||
/// </summary>
|
||||
public string[] Aliases { get; }
|
||||
|
||||
/// <summary> Creates a new <see cref="AliasAttribute"/> with the given aliases. </summary>
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="AliasAttribute" /> with the given aliases.
|
||||
/// </summary>
|
||||
public AliasAttribute(params string[] aliases)
|
||||
{
|
||||
Aliases = aliases;
|
||||
|
||||
@@ -2,17 +2,32 @@ using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks the execution information for a command.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||
public class CommandAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the text that has been set to be recognized as a command.
|
||||
/// </summary>
|
||||
public string Text { get; }
|
||||
/// <summary>
|
||||
/// Specifies the <see cref="RunMode" /> of the command. This affects how the command is executed.
|
||||
/// </summary>
|
||||
public RunMode RunMode { get; set; } = RunMode.Default;
|
||||
public bool? IgnoreExtraArgs { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public CommandAttribute()
|
||||
{
|
||||
Text = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="CommandAttribute" /> attribute with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="text">The name of the command.</param>
|
||||
public CommandAttribute(string text)
|
||||
{
|
||||
Text = text;
|
||||
|
||||
@@ -2,6 +2,14 @@ using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Prevents the marked module from being loaded automatically.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This attribute tells <see cref="CommandService" /> to ignore the marked module from being loaded
|
||||
/// automatically (e.g. the <see cref="CommandService.AddModulesAsync" /> method). If a non-public module marked
|
||||
/// with this attribute is attempted to be loaded manually, the loading process will also fail.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public class DontAutoLoadAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Discord.Commands {
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class DontInjectAttribute : Attribute {
|
||||
}
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Prevents the marked property from being injected into a module.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This attribute prevents the marked member from being injected into its parent module. Useful when you have a
|
||||
/// public property that you do not wish to invoke the library's dependency injection service.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// In the following example, <c>DatabaseService</c> will not be automatically injected into the module and will
|
||||
/// not throw an error message if the dependency fails to be resolved.
|
||||
/// <code language="cs">
|
||||
/// public class MyModule : ModuleBase
|
||||
/// {
|
||||
/// [DontInject]
|
||||
/// public DatabaseService DatabaseService;
|
||||
/// public MyModule()
|
||||
/// {
|
||||
/// DatabaseService = DatabaseFactory.Generate();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class DontInjectAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,26 @@ using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks the module as a command group.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public class GroupAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the prefix set for the module.
|
||||
/// </summary>
|
||||
public string Prefix { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public GroupAttribute()
|
||||
{
|
||||
Prefix = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="GroupAttribute" /> with the provided prefix.
|
||||
/// </summary>
|
||||
/// <param name="prefix">The prefix of the module group.</param>
|
||||
public GroupAttribute(string prefix)
|
||||
{
|
||||
Prefix = prefix;
|
||||
|
||||
@@ -3,11 +3,21 @@ using System;
|
||||
namespace Discord.Commands
|
||||
{
|
||||
// Override public name of command/module
|
||||
/// <summary>
|
||||
/// Marks the public name of a command, module, or parameter.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
public class NameAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the command.
|
||||
/// </summary>
|
||||
public string Text { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Marks the public name of a command, module, or parameter with the provided name.
|
||||
/// </summary>
|
||||
/// <param name="text">The public name of the object.</param>
|
||||
public NameAttribute(string text)
|
||||
{
|
||||
Text = text;
|
||||
|
||||
@@ -4,17 +4,46 @@ using System.Reflection;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks the <see cref="Type"/> to be read by the specified <see cref="Discord.Commands.TypeReader"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This attribute will override the <see cref="Discord.Commands.TypeReader"/> to be used when parsing for the
|
||||
/// desired type in the command. This is useful when one wishes to use a particular
|
||||
/// <see cref="Discord.Commands.TypeReader"/> without affecting other commands that are using the same target
|
||||
/// type.
|
||||
/// <note type="warning">
|
||||
/// If the given type reader does not inherit from <see cref="Discord.Commands.TypeReader"/>, an
|
||||
/// <see cref="ArgumentException"/> will be thrown.
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// In this example, the <see cref="TimeSpan"/> will be read by a custom
|
||||
/// <see cref="Discord.Commands.TypeReader"/>, <c>FriendlyTimeSpanTypeReader</c>, instead of the
|
||||
/// <see cref="TimeSpanTypeReader"/> shipped by Discord.Net.
|
||||
/// <code language="cs">
|
||||
/// [Command("time")]
|
||||
/// public Task GetTimeAsync([OverrideTypeReader(typeof(FriendlyTimeSpanTypeReader))]TimeSpan time)
|
||||
/// => ReplyAsync(time);
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
public class OverrideTypeReaderAttribute : Attribute
|
||||
{
|
||||
private static readonly TypeInfo TypeReaderTypeInfo = typeof(TypeReader).GetTypeInfo();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="TypeReader"/> of the parameter.
|
||||
/// </summary>
|
||||
public Type TypeReader { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <param name="overridenTypeReader">The <see cref="TypeReader"/> to be used with the parameter. </param>
|
||||
/// <exception cref="ArgumentException">The given <paramref name="overridenTypeReader"/> does not inherit from <see cref="TypeReader"/>.</exception>
|
||||
public OverrideTypeReaderAttribute(Type overridenTypeReader)
|
||||
{
|
||||
if (!TypeReaderTypeInfo.IsAssignableFrom(overridenTypeReader.GetTypeInfo()))
|
||||
throw new ArgumentException($"{nameof(overridenTypeReader)} must inherit from {nameof(TypeReader)}");
|
||||
throw new ArgumentException($"{nameof(overridenTypeReader)} must inherit from {nameof(TypeReader)}.");
|
||||
|
||||
TypeReader = overridenTypeReader;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,20 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Requires the parameter to pass the specified precondition before execution can begin.
|
||||
/// </summary>
|
||||
/// <seealso cref="PreconditionAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)]
|
||||
public abstract class ParameterPreconditionAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks whether the condition is met before execution of the command.
|
||||
/// </summary>
|
||||
/// <param name="context">The context of the command.</param>
|
||||
/// <param name="parameter">The parameter of the command being checked against.</param>
|
||||
/// <param name="value">The raw value of the parameter.</param>
|
||||
/// <param name="services">The service collection used for dependency injection.</param>
|
||||
public abstract Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, ParameterInfo parameter, object value, IServiceProvider services);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Requires the module or class to pass the specified precondition before execution can begin.
|
||||
/// </summary>
|
||||
/// <seealso cref="ParameterPreconditionAttribute"/>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
|
||||
public abstract class PreconditionAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Specify a group that this precondition belongs to. Preconditions of the same group require only one
|
||||
/// of the preconditions to pass in order to be successful (A || B). Specifying <see cref="Group"/> = <see langword="null"/>
|
||||
/// or not at all will require *all* preconditions to pass, just like normal (A && B).
|
||||
/// Specifies a group that this precondition belongs to.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="Preconditions" /> of the same group require only one of the preconditions to pass in order to
|
||||
/// be successful (A || B). Specifying <see cref="Group" /> = <c>null</c> or not at all will
|
||||
/// require *all* preconditions to pass, just like normal (A && B).
|
||||
/// </remarks>
|
||||
public string Group { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the <paramref name="command"/> has the sufficient permission to be executed.
|
||||
/// </summary>
|
||||
/// <param name="context">The context of the command.</param>
|
||||
/// <param name="command">The command being executed.</param>
|
||||
/// <param name="services">The service collection used for dependency injection.</param>
|
||||
public abstract Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,52 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute requires that the bot has a specified permission in the channel a command is invoked in.
|
||||
/// Requires the bot to have a specific permission in the channel a command is invoked in.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireBotPermissionAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="Discord.GuildPermission" /> of the precondition.
|
||||
/// </summary>
|
||||
public GuildPermission? GuildPermission { get; }
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="Discord.ChannelPermission" /> of the precondition.
|
||||
/// </summary>
|
||||
public ChannelPermission? ChannelPermission { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Require that the bot account has a specified GuildPermission
|
||||
/// Requires the bot account to have a specific <see cref="Discord.GuildPermission"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This precondition will always fail if the command is being invoked in a private channel.</remarks>
|
||||
/// <param name="permission">The GuildPermission that the bot must have. Multiple permissions can be specified by ORing the permissions together.</param>
|
||||
/// <remarks>
|
||||
/// This precondition will always fail if the command is being invoked in a <see cref="IPrivateChannel"/>.
|
||||
/// </remarks>
|
||||
/// <param name="permission">
|
||||
/// The <see cref="Discord.GuildPermission"/> that the bot must have. Multiple permissions can be specified
|
||||
/// by ORing the permissions together.
|
||||
/// </param>
|
||||
public RequireBotPermissionAttribute(GuildPermission permission)
|
||||
{
|
||||
GuildPermission = permission;
|
||||
ChannelPermission = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Require that the bot account has a specified ChannelPermission.
|
||||
/// Requires that the bot account to have a specific <see cref="Discord.ChannelPermission"/>.
|
||||
/// </summary>
|
||||
/// <param name="permission">The ChannelPermission that the bot must have. Multiple permissions can be specified by ORing the permissions together.</param>
|
||||
/// <example>
|
||||
/// <code language="c#">
|
||||
/// [Command("permission")]
|
||||
/// [RequireBotPermission(ChannelPermission.ManageMessages)]
|
||||
/// public async Task Purge()
|
||||
/// {
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="permission">
|
||||
/// The <see cref="Discord.ChannelPermission"/> that the bot must have. Multiple permissions can be
|
||||
/// specified by ORing the permissions together.
|
||||
/// </param>
|
||||
public RequireBotPermissionAttribute(ChannelPermission permission)
|
||||
{
|
||||
ChannelPermission = permission;
|
||||
GuildPermission = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||
{
|
||||
IGuildUser guildUser = null;
|
||||
@@ -50,9 +56,9 @@ namespace Discord.Commands
|
||||
if (GuildPermission.HasValue)
|
||||
{
|
||||
if (guildUser == null)
|
||||
return PreconditionResult.FromError("Command must be used in a guild channel");
|
||||
return PreconditionResult.FromError("Command must be used in a guild channel.");
|
||||
if (!guildUser.GuildPermissions.Has(GuildPermission.Value))
|
||||
return PreconditionResult.FromError($"Bot requires guild permission {GuildPermission.Value}");
|
||||
return PreconditionResult.FromError($"Bot requires guild permission {GuildPermission.Value}.");
|
||||
}
|
||||
|
||||
if (ChannelPermission.HasValue)
|
||||
@@ -64,7 +70,7 @@ namespace Discord.Commands
|
||||
perms = ChannelPermissions.All(context.Channel);
|
||||
|
||||
if (!perms.Has(ChannelPermission.Value))
|
||||
return PreconditionResult.FromError($"Bot requires channel permission {ChannelPermission.Value}");
|
||||
return PreconditionResult.FromError($"Bot requires channel permission {ChannelPermission.Value}.");
|
||||
}
|
||||
|
||||
return PreconditionResult.FromSuccess();
|
||||
|
||||
@@ -1,35 +1,48 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the type of command context (i.e. where the command is being executed).
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ContextType
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the command to be executed within a guild.
|
||||
/// </summary>
|
||||
Guild = 0x01,
|
||||
/// <summary>
|
||||
/// Specifies the command to be executed within a DM.
|
||||
/// </summary>
|
||||
DM = 0x02,
|
||||
/// <summary>
|
||||
/// Specifies the command to be executed within a group.
|
||||
/// </summary>
|
||||
Group = 0x04
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Require that the command be invoked in a specified context.
|
||||
/// Requires the command to be invoked in a specified context (e.g. in guild, DM).
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireContextAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the context required to execute the command.
|
||||
/// </summary>
|
||||
public ContextType Contexts { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Require that the command be invoked in a specified context.
|
||||
/// </summary>
|
||||
/// <summary> Requires the command to be invoked in the specified context. </summary>
|
||||
/// <param name="contexts">The type of context the command can be invoked in. Multiple contexts can be specified by ORing the contexts together.</param>
|
||||
/// <example>
|
||||
/// <code language="c#">
|
||||
/// [Command("private_only")]
|
||||
/// <code language="cs">
|
||||
/// [Command("secret")]
|
||||
/// [RequireContext(ContextType.DM | ContextType.Group)]
|
||||
/// public async Task PrivateOnly()
|
||||
/// public Task PrivateOnlyAsync()
|
||||
/// {
|
||||
/// return ReplyAsync("shh, this command is a secret");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
@@ -38,12 +51,13 @@ namespace Discord.Commands
|
||||
Contexts = contexts;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||
{
|
||||
bool isValid = false;
|
||||
|
||||
if ((Contexts & ContextType.Guild) != 0)
|
||||
isValid = isValid || context.Channel is IGuildChannel;
|
||||
isValid = context.Channel is IGuildChannel;
|
||||
if ((Contexts & ContextType.DM) != 0)
|
||||
isValid = isValid || context.Channel is IDMChannel;
|
||||
if ((Contexts & ContextType.Group) != 0)
|
||||
@@ -52,7 +66,7 @@ namespace Discord.Commands
|
||||
if (isValid)
|
||||
return Task.FromResult(PreconditionResult.FromSuccess());
|
||||
else
|
||||
return Task.FromResult(PreconditionResult.FromError($"Invalid context for command; accepted contexts: {Contexts}"));
|
||||
return Task.FromResult(PreconditionResult.FromError($"Invalid context for command; accepted contexts: {Contexts}."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,33 @@ using System.Threading.Tasks;
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Require that the command is invoked in a channel marked NSFW
|
||||
/// Requires the command to be invoked in a channel marked NSFW.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The precondition will restrict the access of the command or module to be accessed within a guild channel
|
||||
/// that has been marked as mature or NSFW. If the channel is not of type <see cref="ITextChannel"/> or the
|
||||
/// channel is not marked as NSFW, the precondition will fail with an erroneous <see cref="PreconditionResult"/>.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example restricts the command <c>too-cool</c> to an NSFW-enabled channel only.
|
||||
/// <code language="cs">
|
||||
/// public class DankModule : ModuleBase
|
||||
/// {
|
||||
/// [Command("cool")]
|
||||
/// public Task CoolAsync()
|
||||
/// => ReplyAsync("I'm cool for everyone.");
|
||||
///
|
||||
/// [RequireNsfw]
|
||||
/// [Command("too-cool")]
|
||||
/// public Task TooCoolAsync()
|
||||
/// => ReplyAsync("You can only see this if you're cool enough.");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireNsfwAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||
{
|
||||
if (context.Channel is ITextChannel text && text.IsNsfw)
|
||||
|
||||
@@ -4,20 +4,45 @@ using System.Threading.Tasks;
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Require that the command is invoked by the owner of the bot.
|
||||
/// Requires the command to be invoked by the owner of the bot.
|
||||
/// </summary>
|
||||
/// <remarks>This precondition will only work if the bot is a bot account.</remarks>
|
||||
/// <remarks>
|
||||
/// This precondition will restrict the access of the command or module to the owner of the Discord application.
|
||||
/// If the precondition fails to be met, an erroneous <see cref="PreconditionResult"/> will be returned with the
|
||||
/// message "Command can only be run by the owner of the bot."
|
||||
/// <note>
|
||||
/// This precondition will only work if the account has a <see cref="TokenType"/> of <see cref="TokenType.Bot"/>
|
||||
/// ;otherwise, this precondition will always fail.
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example restricts the command to a set of sensitive commands that only the owner of the bot
|
||||
/// application should be able to access.
|
||||
/// <code language="cs">
|
||||
/// [RequireOwner]
|
||||
/// [Group("admin")]
|
||||
/// public class AdminModule : ModuleBase
|
||||
/// {
|
||||
/// [Command("exit")]
|
||||
/// public async Task ExitAsync()
|
||||
/// {
|
||||
/// Environment.Exit(0);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireOwnerAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||
{
|
||||
switch (context.Client.TokenType)
|
||||
{
|
||||
case TokenType.Bot:
|
||||
var application = await context.Client.GetApplicationInfoAsync();
|
||||
var application = await context.Client.GetApplicationInfoAsync().ConfigureAwait(false);
|
||||
if (context.User.Id != application.Owner.Id)
|
||||
return PreconditionResult.FromError("Command can only be run by the owner of the bot");
|
||||
return PreconditionResult.FromError("Command can only be run by the owner of the bot.");
|
||||
return PreconditionResult.FromSuccess();
|
||||
default:
|
||||
return PreconditionResult.FromError($"{nameof(RequireOwnerAttribute)} is not supported by this {nameof(TokenType)}.");
|
||||
|
||||
@@ -1,47 +1,52 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute requires that the user invoking the command has a specified permission.
|
||||
/// Requires the user invoking the command to have a specified permission.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class RequireUserPermissionAttribute : PreconditionAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="Discord.GuildPermission" /> of the precondition.
|
||||
/// </summary>
|
||||
public GuildPermission? GuildPermission { get; }
|
||||
/// <summary>
|
||||
/// Gets the specified <see cref="Discord.ChannelPermission" /> of the precondition.
|
||||
/// </summary>
|
||||
public ChannelPermission? ChannelPermission { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Require that the user invoking the command has a specified GuildPermission
|
||||
/// Requires that the user invoking the command to have a specific <see cref="Discord.GuildPermission"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This precondition will always fail if the command is being invoked in a private channel.</remarks>
|
||||
/// <param name="permission">The GuildPermission that the user must have. Multiple permissions can be specified by ORing the permissions together.</param>
|
||||
/// <remarks>
|
||||
/// This precondition will always fail if the command is being invoked in a <see cref="IPrivateChannel"/>.
|
||||
/// </remarks>
|
||||
/// <param name="permission">
|
||||
/// The <see cref="Discord.GuildPermission" /> that the user must have. Multiple permissions can be
|
||||
/// specified by ORing the permissions together.
|
||||
/// </param>
|
||||
public RequireUserPermissionAttribute(GuildPermission permission)
|
||||
{
|
||||
GuildPermission = permission;
|
||||
ChannelPermission = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Require that the user invoking the command has a specified ChannelPermission.
|
||||
/// Requires that the user invoking the command to have a specific <see cref="Discord.ChannelPermission"/>.
|
||||
/// </summary>
|
||||
/// <param name="permission">The ChannelPermission that the user must have. Multiple permissions can be specified by ORing the permissions together.</param>
|
||||
/// <example>
|
||||
/// <code language="c#">
|
||||
/// [Command("permission")]
|
||||
/// [RequireUserPermission(ChannelPermission.ReadMessageHistory | ChannelPermission.ReadMessages)]
|
||||
/// public async Task HasPermission()
|
||||
/// {
|
||||
/// await ReplyAsync("You can read messages and the message history!");
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="permission">
|
||||
/// The <see cref="Discord.ChannelPermission"/> that the user must have. Multiple permissions can be
|
||||
/// specified by ORing the permissions together.
|
||||
/// </param>
|
||||
public RequireUserPermissionAttribute(ChannelPermission permission)
|
||||
{
|
||||
ChannelPermission = permission;
|
||||
GuildPermission = null;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
|
||||
{
|
||||
var guildUser = context.User as IGuildUser;
|
||||
@@ -49,9 +54,9 @@ namespace Discord.Commands
|
||||
if (GuildPermission.HasValue)
|
||||
{
|
||||
if (guildUser == null)
|
||||
return Task.FromResult(PreconditionResult.FromError("Command must be used in a guild channel"));
|
||||
return Task.FromResult(PreconditionResult.FromError("Command must be used in a guild channel."));
|
||||
if (!guildUser.GuildPermissions.Has(GuildPermission.Value))
|
||||
return Task.FromResult(PreconditionResult.FromError($"User requires guild permission {GuildPermission.Value}"));
|
||||
return Task.FromResult(PreconditionResult.FromError($"User requires guild permission {GuildPermission.Value}."));
|
||||
}
|
||||
|
||||
if (ChannelPermission.HasValue)
|
||||
@@ -63,7 +68,7 @@ namespace Discord.Commands
|
||||
perms = ChannelPermissions.All(context.Channel);
|
||||
|
||||
if (!perms.Has(ChannelPermission.Value))
|
||||
return Task.FromResult(PreconditionResult.FromError($"User requires channel permission {ChannelPermission.Value}"));
|
||||
return Task.FromResult(PreconditionResult.FromError($"User requires channel permission {ChannelPermission.Value}."));
|
||||
}
|
||||
|
||||
return Task.FromResult(PreconditionResult.FromSuccess());
|
||||
|
||||
@@ -2,14 +2,20 @@ using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary> Sets priority of commands </summary>
|
||||
/// <summary>
|
||||
/// Sets priority of commands.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
|
||||
public class PriorityAttribute : Attribute
|
||||
{
|
||||
/// <summary> The priority which has been set for the command </summary>
|
||||
/// <summary>
|
||||
/// Gets the priority which has been set for the command.
|
||||
/// </summary>
|
||||
public int Priority { get; }
|
||||
|
||||
/// <summary> Creates a new <see cref="PriorityAttribute"/> with the given priority. </summary>
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="PriorityAttribute" /> attribute with the given priority.
|
||||
/// </summary>
|
||||
public PriorityAttribute(int priority)
|
||||
{
|
||||
Priority = priority;
|
||||
|
||||
@@ -2,6 +2,9 @@ using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks the input to not be parsed by the parser.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
public class RemainderAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -3,6 +3,9 @@ using System;
|
||||
namespace Discord.Commands
|
||||
{
|
||||
// Extension of the Cosmetic Summary, for Groups, Commands, and Parameters
|
||||
/// <summary>
|
||||
/// Attaches remarks to your commands.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public class RemarksAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -3,6 +3,9 @@ using System;
|
||||
namespace Discord.Commands
|
||||
{
|
||||
// Cosmetic Summary, for Groups and Commands
|
||||
/// <summary>
|
||||
/// Attaches a summary to your command.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||
public class SummaryAttribute : Attribute
|
||||
{
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace Discord.Commands.Builders
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Only the last parameter in a command may have the Remainder or Multiple flag.</exception>
|
||||
internal CommandInfo Build(ModuleInfo info, CommandService service)
|
||||
{
|
||||
//Default name to primary alias
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Discord.Commands
|
||||
}
|
||||
else if (IsLoadableModule(typeInfo))
|
||||
{
|
||||
await service._cmdLogger.WarningAsync($"Class {typeInfo.FullName} is not public and cannot be loaded. To suppress this message, mark the class with {nameof(DontAutoLoadAttribute)}.");
|
||||
await service._cmdLogger.WarningAsync($"Class {typeInfo.FullName} is not public and cannot be loaded. To suppress this message, mark the class with {nameof(DontAutoLoadAttribute)}.").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
namespace Discord.Commands
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary> The context of a command which may contain the client, user, guild, channel, and message. </summary>
|
||||
public class CommandContext : ICommandContext
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public IDiscordClient Client { get; }
|
||||
/// <inheritdoc/>
|
||||
public IGuild Guild { get; }
|
||||
/// <inheritdoc/>
|
||||
public IMessageChannel Channel { get; }
|
||||
/// <inheritdoc/>
|
||||
public IUser User { get; }
|
||||
/// <inheritdoc/>
|
||||
public IUserMessage Message { get; }
|
||||
|
||||
/// <summary> Indicates whether the channel that the command is executed in is a private channel. </summary>
|
||||
public bool IsPrivate => Channel is IPrivateChannel;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="CommandContext" /> class with the provided client and message.
|
||||
/// </summary>
|
||||
/// <param name="client">The underlying client.</param>
|
||||
/// <param name="msg">The underlying message.</param>
|
||||
public CommandContext(IDiscordClient client, IUserMessage msg)
|
||||
{
|
||||
Client = client;
|
||||
|
||||
@@ -1,26 +1,51 @@
|
||||
namespace Discord.Commands
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary> Defines the type of error a command can throw. </summary>
|
||||
public enum CommandError
|
||||
{
|
||||
//Search
|
||||
/// <summary>
|
||||
/// Thrown when the command is unknown.
|
||||
/// </summary>
|
||||
UnknownCommand = 1,
|
||||
|
||||
//Parse
|
||||
/// <summary>
|
||||
/// Thrown when the command fails to be parsed.
|
||||
/// </summary>
|
||||
ParseFailed,
|
||||
/// <summary>
|
||||
/// Thrown when the input text has too few or too many arguments.
|
||||
/// </summary>
|
||||
BadArgCount,
|
||||
|
||||
//Parse (Type Reader)
|
||||
//CastFailed,
|
||||
/// <summary>
|
||||
/// Thrown when the object cannot be found by the <see cref="TypeReader"/>.
|
||||
/// </summary>
|
||||
ObjectNotFound,
|
||||
/// <summary>
|
||||
/// Thrown when more than one object is matched by <see cref="TypeReader"/>.
|
||||
/// </summary>
|
||||
MultipleMatches,
|
||||
|
||||
//Preconditions
|
||||
/// <summary>
|
||||
/// Thrown when the command fails to meet a <see cref="PreconditionAttribute"/>'s conditions.
|
||||
/// </summary>
|
||||
UnmetPrecondition,
|
||||
|
||||
//Execute
|
||||
/// <summary>
|
||||
/// Thrown when an exception occurs mid-command execution.
|
||||
/// </summary>
|
||||
Exception,
|
||||
|
||||
//Runtime
|
||||
/// <summary>
|
||||
/// Thrown when the command is not successfully executed on runtime.
|
||||
/// </summary>
|
||||
Unsuccessful
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,24 @@ using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown if another exception occurs during a command execution.
|
||||
/// </summary>
|
||||
public class CommandException : Exception
|
||||
{
|
||||
/// <summary> Gets the command that caused the exception. </summary>
|
||||
public CommandInfo Command { get; }
|
||||
/// <summary> Gets the command context of the exception. </summary>
|
||||
public ICommandContext Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CommandException" /> class using a
|
||||
/// <paramref name="command"/> information, a <paramref name="command"/> context, and the exception that
|
||||
/// interrupted the execution.
|
||||
/// </summary>
|
||||
/// <param name="command">The command information.</param>
|
||||
/// <param name="context">The context of the command.</param>
|
||||
/// <param name="ex">The exception that interrupted the command execution.</param>
|
||||
public CommandException(CommandInfo command, ICommandContext context, Exception ex)
|
||||
: base($"Error occurred executing {command.GetLogText(context)}.", ex)
|
||||
{
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
public struct CommandMatch
|
||||
{
|
||||
/// <summary> The command that matches the search result. </summary>
|
||||
public CommandInfo Command { get; }
|
||||
/// <summary> The alias of the command. </summary>
|
||||
public string Alias { get; }
|
||||
|
||||
public CommandMatch(CommandInfo command, string alias)
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace Discord.Commands
|
||||
if (isEscaping)
|
||||
return ParseResult.FromError(CommandError.ParseFailed, "Input text may not end on an incomplete escape.");
|
||||
if (curPart == ParserPart.QuotedParameter)
|
||||
return ParseResult.FromError(CommandError.ParseFailed, "A quoted parameter is incomplete");
|
||||
return ParseResult.FromError(CommandError.ParseFailed, "A quoted parameter is incomplete.");
|
||||
|
||||
//Add missing optionals
|
||||
for (int i = argList.Count; i < command.Parameters.Count; i++)
|
||||
|
||||
@@ -11,11 +11,44 @@ using Discord.Logging;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a framework for building Discord commands.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The service provides a framework for building Discord commands both dynamically via runtime builders or
|
||||
/// statically via compile-time modules. To create a command module at compile-time, see
|
||||
/// <see cref="ModuleBase" /> (most common); otherwise, see <see cref="ModuleBuilder" />.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This service also provides several events for monitoring command usages; such as
|
||||
/// <see cref="Discord.Commands.CommandService.Log" /> for any command-related log events, and
|
||||
/// <see cref="Discord.Commands.CommandService.CommandExecuted" /> for information about commands that have
|
||||
/// been successfully executed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class CommandService
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when a command-related information is received.
|
||||
/// </summary>
|
||||
public event Func<LogMessage, Task> Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } }
|
||||
internal readonly AsyncEvent<Func<LogMessage, Task>> _logEvent = new AsyncEvent<Func<LogMessage, Task>>();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a command is successfully executed without any error.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This event is fired when a command has been successfully executed without any of the following errors:
|
||||
/// </para>
|
||||
/// <para>* Parsing error</para>
|
||||
/// <para>* Precondition error</para>
|
||||
/// <para>* Runtime exception</para>
|
||||
/// <para>
|
||||
/// Should the command encounter any of the aforementioned error, this event will not be raised.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public event Func<CommandInfo, ICommandContext, IResult, Task> CommandExecuted { add { _commandExecutedEvent.Add(value); } remove { _commandExecutedEvent.Remove(value); } }
|
||||
internal readonly AsyncEvent<Func<CommandInfo, ICommandContext, IResult, Task>> _commandExecutedEvent = new AsyncEvent<Func<CommandInfo, ICommandContext, IResult, Task>>();
|
||||
|
||||
@@ -34,11 +67,33 @@ namespace Discord.Commands
|
||||
internal readonly LogManager _logManager;
|
||||
internal readonly IReadOnlyDictionary<char, char> _quotationMarkAliasMap;
|
||||
|
||||
/// <summary>
|
||||
/// Represents all modules loaded within <see cref="CommandService"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<ModuleInfo> Modules => _moduleDefs.Select(x => x);
|
||||
|
||||
/// <summary>
|
||||
/// Represents all commands loaded within <see cref="CommandService"/>.
|
||||
/// </summary>
|
||||
public IEnumerable<CommandInfo> Commands => _moduleDefs.SelectMany(x => x.Commands);
|
||||
|
||||
/// <summary>
|
||||
/// Represents all <see cref="TypeReader" /> loaded within <see cref="CommandService"/>.
|
||||
/// </summary>
|
||||
public ILookup<Type, TypeReader> TypeReaders => _typeReaders.SelectMany(x => x.Value.Select(y => new { y.Key, y.Value })).ToLookup(x => x.Key, x => x.Value);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="CommandService"/> class.
|
||||
/// </summary>
|
||||
public CommandService() : this(new CommandServiceConfig()) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="CommandService"/> class with the provided configuration.
|
||||
/// </summary>
|
||||
/// <param name="config">The configuration class.</param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The <see cref="RunMode"/> cannot be set to <see cref="RunMode.Default"/>.
|
||||
/// </exception>
|
||||
public CommandService(CommandServiceConfig config)
|
||||
{
|
||||
_caseSensitive = config.CaseSensitiveCommands;
|
||||
@@ -102,12 +157,39 @@ namespace Discord.Commands
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a command module from a type
|
||||
/// Add a command module from a <see cref="Type" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of module</typeparam>
|
||||
/// <param name="services">An IServiceProvider for your dependency injection solution, if using one - otherwise, pass null</param>
|
||||
/// <returns>A built module</returns>
|
||||
/// <example>
|
||||
/// <para>The following example registers the module <c>MyModule</c> to <c>commandService</c>.</para>
|
||||
/// <code language="cs">
|
||||
/// await commandService.AddModuleAsync<MyModule>(serviceProvider);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <typeparam name="T">The type of module.</typeparam>
|
||||
/// <param name="services">The <see cref="IServiceProvider"/> for your dependency injection solution if using one; otherwise, pass <c>null</c>.</param>
|
||||
/// <exception cref="ArgumentException">This module has already been added.</exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The <see cref="ModuleInfo"/> fails to be built; an invalid type may have been provided.
|
||||
/// </exception>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation for adding the module. The task result contains the
|
||||
/// built module.
|
||||
/// </returns>
|
||||
public Task<ModuleInfo> AddModuleAsync<T>(IServiceProvider services) => AddModuleAsync(typeof(T), services);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a command module from a <see cref="Type" />.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of module.</param>
|
||||
/// <param name="services">The <see cref="IServiceProvider" /> for your dependency injection solution if using one; otherwise, pass <c>null</c> .</param>
|
||||
/// <exception cref="ArgumentException">This module has already been added.</exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// The <see cref="ModuleInfo"/> fails to be built; an invalid type may have been provided.
|
||||
/// </exception>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation for adding the module. The task result contains the
|
||||
/// built module.
|
||||
/// </returns>
|
||||
public async Task<ModuleInfo> AddModuleAsync(Type type, IServiceProvider services)
|
||||
{
|
||||
services = services ?? EmptyServiceProvider.Instance;
|
||||
@@ -135,11 +217,14 @@ namespace Discord.Commands
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Add command modules from an assembly
|
||||
/// Add command modules from an <see cref="Assembly"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly containing command modules</param>
|
||||
/// <param name="services">An IServiceProvider for your dependency injection solution, if using one - otherwise, pass null</param>
|
||||
/// <returns>A collection of built modules</returns>
|
||||
/// <param name="assembly">The <see cref="Assembly"/> containing command modules.</param>
|
||||
/// <param name="services">The <see cref="IServiceProvider"/> for your dependency injection solution if using one; otherwise, pass <c>null</c>.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation for adding the command modules. The task result
|
||||
/// contains an enumerable collection of modules added.
|
||||
/// </returns>
|
||||
public async Task<IEnumerable<ModuleInfo>> AddModulesAsync(Assembly assembly, IServiceProvider services)
|
||||
{
|
||||
services = services ?? EmptyServiceProvider.Instance;
|
||||
@@ -175,7 +260,14 @@ namespace Discord.Commands
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the command module.
|
||||
/// </summary>
|
||||
/// <param name="module">The <see cref="ModuleInfo" /> to be removed from the service.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous removal operation. The task result contains a value that
|
||||
/// indicates whether the <paramref name="module"/> is successfully removed.
|
||||
/// </returns>
|
||||
public async Task<bool> RemoveModuleAsync(ModuleInfo module)
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
@@ -188,7 +280,23 @@ namespace Discord.Commands
|
||||
_moduleLock.Release();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Removes the command module.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The <see cref="Type"/> of the module.</typeparam>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous removal operation. The task result contains a value that
|
||||
/// indicates whether the module is successfully removed.
|
||||
/// </returns>
|
||||
public Task<bool> RemoveModuleAsync<T>() => RemoveModuleAsync(typeof(T));
|
||||
/// <summary>
|
||||
/// Removes the command module.
|
||||
/// </summary>
|
||||
/// <param name="type">The <see cref="Type"/> of the module.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous removal operation. The task result contains a value that
|
||||
/// indicates whether the module is successfully removed.
|
||||
/// </returns>
|
||||
public async Task<bool> RemoveModuleAsync(Type type)
|
||||
{
|
||||
await _moduleLock.WaitAsync().ConfigureAwait(false);
|
||||
@@ -222,21 +330,27 @@ namespace Discord.Commands
|
||||
|
||||
//Type Readers
|
||||
/// <summary>
|
||||
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
|
||||
/// If <typeparamref name="T"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> will also be added.
|
||||
/// If a default <see cref="TypeReader"/> exists for <typeparamref name="T"/>, a warning will be logged and the default <see cref="TypeReader"/> will be replaced.
|
||||
/// Adds a custom <see cref="TypeReader" /> to this <see cref="CommandService" /> for the supplied object
|
||||
/// type.
|
||||
/// If <typeparamref name="T" /> is a <see cref="ValueType" />, a nullable <see cref="TypeReader" /> will
|
||||
/// also be added.
|
||||
/// If a default <see cref="TypeReader" /> exists for <typeparamref name="T" />, a warning will be logged
|
||||
/// and the default <see cref="TypeReader" /> will be replaced.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The object type to be read by the <see cref="TypeReader"/>.</typeparam>
|
||||
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
|
||||
/// <param name="reader">An instance of the <see cref="TypeReader" /> to be added.</param>
|
||||
public void AddTypeReader<T>(TypeReader reader)
|
||||
=> AddTypeReader(typeof(T), reader);
|
||||
/// <summary>
|
||||
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
|
||||
/// If <paramref name="type"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> for the value type will also be added.
|
||||
/// If a default <see cref="TypeReader"/> exists for <paramref name="type"/>, a warning will be logged and the default <see cref="TypeReader"/> will be replaced.
|
||||
/// Adds a custom <see cref="TypeReader" /> to this <see cref="CommandService" /> for the supplied object
|
||||
/// type.
|
||||
/// If <paramref name="type" /> is a <see cref="ValueType" />, a nullable <see cref="TypeReader" /> for the
|
||||
/// value type will also be added.
|
||||
/// If a default <see cref="TypeReader" /> exists for <paramref name="type" />, a warning will be logged and
|
||||
/// the default <see cref="TypeReader" /> will be replaced.
|
||||
/// </summary>
|
||||
/// <param name="type">A <see cref="Type"/> instance for the type to be read.</param>
|
||||
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
|
||||
/// <param name="type">A <see cref="Type" /> instance for the type to be read.</param>
|
||||
/// <param name="reader">An instance of the <see cref="TypeReader" /> to be added.</param>
|
||||
public void AddTypeReader(Type type, TypeReader reader)
|
||||
{
|
||||
if (_defaultTypeReaders.ContainsKey(type))
|
||||
@@ -245,21 +359,31 @@ namespace Discord.Commands
|
||||
AddTypeReader(type, reader, true);
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
|
||||
/// If <typeparamref name="T"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> will also be added.
|
||||
/// Adds a custom <see cref="TypeReader" /> to this <see cref="CommandService" /> for the supplied object
|
||||
/// type.
|
||||
/// If <typeparamref name="T" /> is a <see cref="ValueType" />, a nullable <see cref="TypeReader" /> will
|
||||
/// also be added.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The object type to be read by the <see cref="TypeReader"/>.</typeparam>
|
||||
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
|
||||
/// <param name="replaceDefault">If <paramref name="reader"/> should replace the default <see cref="TypeReader"/> for <typeparamref name="T"/> if one exists.</param>
|
||||
/// <param name="reader">An instance of the <see cref="TypeReader" /> to be added.</param>
|
||||
/// <param name="replaceDefault">
|
||||
/// Defines whether the <see cref="TypeReader"/> should replace the default one for
|
||||
/// <see cref="Type" /> if it exists.
|
||||
/// </param>
|
||||
public void AddTypeReader<T>(TypeReader reader, bool replaceDefault)
|
||||
=> AddTypeReader(typeof(T), reader, replaceDefault);
|
||||
/// <summary>
|
||||
/// Adds a custom <see cref="TypeReader"/> to this <see cref="CommandService"/> for the supplied object type.
|
||||
/// If <paramref name="type"/> is a <see cref="ValueType"/>, a <see cref="NullableTypeReader{T}"/> for the value type will also be added.
|
||||
/// Adds a custom <see cref="TypeReader" /> to this <see cref="CommandService" /> for the supplied object
|
||||
/// type.
|
||||
/// If <paramref name="type" /> is a <see cref="ValueType" />, a nullable <see cref="TypeReader" /> for the
|
||||
/// value type will also be added.
|
||||
/// </summary>
|
||||
/// <param name="type">A <see cref="Type"/> instance for the type to be read.</param>
|
||||
/// <param name="reader">An instance of the <see cref="TypeReader"/> to be added.</param>
|
||||
/// <param name="replaceDefault">If <paramref name="reader"/> should replace the default <see cref="TypeReader"/> for <paramref name="type"/> if one exists.</param>
|
||||
/// <param name="type">A <see cref="Type" /> instance for the type to be read.</param>
|
||||
/// <param name="reader">An instance of the <see cref="TypeReader" /> to be added.</param>
|
||||
/// <param name="replaceDefault">
|
||||
/// Defines whether the <see cref="TypeReader"/> should replace the default one for <see cref="Type" /> if
|
||||
/// it exists.
|
||||
/// </param>
|
||||
public void AddTypeReader(Type type, TypeReader reader, bool replaceDefault)
|
||||
{
|
||||
if (replaceDefault && HasDefaultTypeReader(type))
|
||||
@@ -331,8 +455,20 @@ namespace Discord.Commands
|
||||
}
|
||||
|
||||
//Execution
|
||||
/// <summary>
|
||||
/// Searches for the command.
|
||||
/// </summary>
|
||||
/// <param name="context">The context of the command.</param>
|
||||
/// <param name="argPos">The position of which the command starts at.</param>
|
||||
/// <returns>The result containing the matching commands.</returns>
|
||||
public SearchResult Search(ICommandContext context, int argPos)
|
||||
=> Search(context.Message.Content.Substring(argPos));
|
||||
/// <summary>
|
||||
/// Searches for the command.
|
||||
/// </summary>
|
||||
/// <param name="context">The context of the command.</param>
|
||||
/// <param name="input">The command string.</param>
|
||||
/// <returns>The result containing the matching commands.</returns>
|
||||
public SearchResult Search(ICommandContext context, string input)
|
||||
=> Search(input);
|
||||
public SearchResult Search(string input)
|
||||
@@ -346,8 +482,30 @@ namespace Discord.Commands
|
||||
return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the command.
|
||||
/// </summary>
|
||||
/// <param name="context">The context of the command.</param>
|
||||
/// <param name="argPos">The position of which the command starts at.</param>
|
||||
/// <param name="services">The service to be used in the command's dependency injection.</param>
|
||||
/// <param name="multiMatchHandling">The handling mode when multiple command matches are found.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous execution operation. The task result contains the result of the
|
||||
/// command execution.
|
||||
/// </returns>
|
||||
public Task<IResult> ExecuteAsync(ICommandContext context, int argPos, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
||||
=> ExecuteAsync(context, context.Message.Content.Substring(argPos), services, multiMatchHandling);
|
||||
/// <summary>
|
||||
/// Executes the command.
|
||||
/// </summary>
|
||||
/// <param name="context">The context of the command.</param>
|
||||
/// <param name="input">The command string.</param>
|
||||
/// <param name="services">The service to be used in the command's dependency injection.</param>
|
||||
/// <param name="multiMatchHandling">The handling mode when multiple command matches are found.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous execution operation. The task result contains the result of the
|
||||
/// command execution.
|
||||
/// </returns>
|
||||
public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
|
||||
{
|
||||
services = services ?? EmptyServiceProvider.Instance;
|
||||
|
||||
@@ -1,29 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a configuration class for <see cref="CommandService"/>.
|
||||
/// </summary>
|
||||
public class CommandServiceConfig
|
||||
{
|
||||
/// <summary> Gets or sets the default RunMode commands should have, if one is not specified on the Command attribute or builder. </summary>
|
||||
/// <summary>
|
||||
/// Gets or sets the default <see cref="RunMode" /> commands should have, if one is not specified on the
|
||||
/// Command attribute or builder.
|
||||
/// </summary>
|
||||
public RunMode DefaultRunMode { get; set; } = RunMode.Sync;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="char"/> that separates an argument with another.
|
||||
/// </summary>
|
||||
public char SeparatorChar { get; set; } = ' ';
|
||||
|
||||
/// <summary> Determines whether commands should be case-sensitive. </summary>
|
||||
/// <summary>
|
||||
/// Gets or sets whether commands should be case-sensitive.
|
||||
/// </summary>
|
||||
public bool CaseSensitiveCommands { get; set; } = false;
|
||||
|
||||
/// <summary> Gets or sets the minimum log level severity that will be sent to the Log event. </summary>
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum log level severity that will be sent to the <see cref="CommandService.Log"/> event.
|
||||
/// </summary>
|
||||
public LogSeverity LogLevel { get; set; } = LogSeverity.Info;
|
||||
|
||||
/// <summary> Determines whether RunMode.Sync commands should push exceptions up to the caller. </summary>
|
||||
/// <summary>
|
||||
/// Gets or sets whether <see cref="RunMode.Sync"/> commands should push exceptions up to the caller.
|
||||
/// </summary>
|
||||
public bool ThrowOnError { get; set; } = true;
|
||||
|
||||
/// <summary> Collection of aliases that can wrap strings for command parsing.
|
||||
/// represents the opening quotation mark and the value is the corresponding closing mark.</summary>
|
||||
/// <summary>
|
||||
/// Collection of aliases for matching pairs of string delimiters.
|
||||
/// The dictionary stores the opening delimiter as a key, and the matching closing delimiter as the value.
|
||||
/// If no value is supplied <see cref="QuotationAliasUtils.GetDefaultAliasMap"/> will be used, which contains
|
||||
/// many regional equivalents.
|
||||
/// Only values that are specified in this map will be used as string delimiters, so if " is removed then
|
||||
/// it won't be used.
|
||||
/// If this map is set to null or empty, the default delimiter of " will be used.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code language="cs">
|
||||
/// QuotationMarkAliasMap = new Dictionary<char, char%gt;()
|
||||
/// {
|
||||
/// {'\"', '\"' },
|
||||
/// {'“', '”' },
|
||||
/// {'「', '」' },
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public Dictionary<char, char> QuotationMarkAliasMap { get; set; } = QuotationAliasUtils.GetDefaultAliasMap;
|
||||
|
||||
/// <summary> Determines whether extra parameters should be ignored. </summary>
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether extra parameters should be ignored.
|
||||
/// </summary>
|
||||
public bool IgnoreExtraArgs { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,20 @@ using System;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="IUserMessage" /> that relates to commands.
|
||||
/// </summary>
|
||||
public static class MessageExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets whether the message starts with the provided character.
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to check against.</param>
|
||||
/// <param name="c">The char prefix.</param>
|
||||
/// <param name="argPos">References where the command starts.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the message begins with the char <paramref name="c"/>; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool HasCharPrefix(this IUserMessage msg, char c, ref int argPos)
|
||||
{
|
||||
var text = msg.Content;
|
||||
@@ -14,6 +26,9 @@ namespace Discord.Commands
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets whether the message starts with the provided string.
|
||||
/// </summary>
|
||||
public static bool HasStringPrefix(this IUserMessage msg, string str, ref int argPos, StringComparison comparisonType = StringComparison.Ordinal)
|
||||
{
|
||||
var text = msg.Content;
|
||||
@@ -24,6 +39,9 @@ namespace Discord.Commands
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets whether the message starts with the user's mention string.
|
||||
/// </summary>
|
||||
public static bool HasMentionPrefix(this IUserMessage msg, IUser user, ref int argPos)
|
||||
{
|
||||
var text = msg.Content;
|
||||
|
||||
@@ -8,10 +8,16 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the information of a command.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This object contains the information of a command. This can include the module of the command, various
|
||||
/// descriptions regarding the command, and its <see cref="RunMode"/>.
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("{Name,nq}")]
|
||||
public class CommandInfo
|
||||
{
|
||||
@@ -21,18 +27,63 @@ namespace Discord.Commands
|
||||
private readonly CommandService _commandService;
|
||||
private readonly Func<ICommandContext, object[], IServiceProvider, CommandInfo, Task> _action;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the module that the command belongs in.
|
||||
/// </summary>
|
||||
public ModuleInfo Module { get; }
|
||||
/// <summary>
|
||||
/// Gets the name of the command. If none is set, the first alias is used.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// Gets the summary of the command.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This field returns the summary of the command. <see cref="Summary"/> and <see cref="Remarks"/> can be
|
||||
/// useful in help commands and various implementation that fetches details of the command for the user.
|
||||
/// </remarks>
|
||||
public string Summary { get; }
|
||||
/// <summary>
|
||||
/// Gets the remarks of the command.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This field returns the summary of the command. <see cref="Summary"/> and <see cref="Remarks"/> can be
|
||||
/// useful in help commands and various implementation that fetches details of the command for the user.
|
||||
/// </remarks>
|
||||
public string Remarks { get; }
|
||||
/// <summary>
|
||||
/// Gets the priority of the command. This is used when there are multiple overloads of the command.
|
||||
/// </summary>
|
||||
public int Priority { get; }
|
||||
/// <summary>
|
||||
/// Indicates whether the command accepts a <see langword="params"/> <see cref="Type"/>[] for its
|
||||
/// parameter.
|
||||
/// </summary>
|
||||
public bool HasVarArgs { get; }
|
||||
/// <summary>
|
||||
/// Indicates whether extra arguments should be ignored for this command.
|
||||
/// </summary>
|
||||
public bool IgnoreExtraArgs { get; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="RunMode" /> that is being used for the command.
|
||||
/// </summary>
|
||||
public RunMode RunMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of aliases defined by the <see cref="AliasAttribute" /> of the command.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Aliases { get; }
|
||||
/// <summary>
|
||||
/// Gets a list of information about the parameters of the command.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ParameterInfo> Parameters { get; }
|
||||
/// <summary>
|
||||
/// Gets a list of preconditions defined by the <see cref="PreconditionAttribute" /> of the command.
|
||||
/// </summary>
|
||||
public IReadOnlyList<PreconditionAttribute> Preconditions { get; }
|
||||
/// <summary>
|
||||
/// Gets a list of attributes of the command.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Attribute> Attributes { get; }
|
||||
|
||||
internal CommandInfo(CommandBuilder builder, ModuleInfo module, CommandService service)
|
||||
@@ -100,11 +151,11 @@ namespace Discord.Commands
|
||||
return PreconditionGroupResult.FromSuccess();
|
||||
}
|
||||
|
||||
var moduleResult = await CheckGroups(Module.Preconditions, "Module");
|
||||
var moduleResult = await CheckGroups(Module.Preconditions, "Module").ConfigureAwait(false);
|
||||
if (!moduleResult.IsSuccess)
|
||||
return moduleResult;
|
||||
|
||||
var commandResult = await CheckGroups(Preconditions, "Command");
|
||||
var commandResult = await CheckGroups(Preconditions, "Command").ConfigureAwait(false);
|
||||
if (!commandResult.IsSuccess)
|
||||
return commandResult;
|
||||
|
||||
@@ -124,7 +175,7 @@ namespace Discord.Commands
|
||||
|
||||
return await CommandParser.ParseArgsAsync(this, context, _commandService._ignoreExtraArgs, services, input, 0, _commandService._quotationMarkAliasMap).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
public Task<IResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services)
|
||||
{
|
||||
if (!parseResult.IsSuccess)
|
||||
@@ -248,11 +299,11 @@ namespace Discord.Commands
|
||||
foreach (object arg in argList)
|
||||
{
|
||||
if (i == argCount)
|
||||
throw new InvalidOperationException("Command was invoked with too many parameters");
|
||||
throw new InvalidOperationException("Command was invoked with too many parameters.");
|
||||
array[i++] = arg;
|
||||
}
|
||||
if (i < argCount)
|
||||
throw new InvalidOperationException("Command was invoked with too few parameters");
|
||||
throw new InvalidOperationException("Command was invoked with too few parameters.");
|
||||
|
||||
if (HasVarArgs)
|
||||
{
|
||||
|
||||
@@ -6,20 +6,59 @@ using Discord.Commands.Builders;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the information of a module.
|
||||
/// </summary>
|
||||
public class ModuleInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the command service associated with this module.
|
||||
/// </summary>
|
||||
public CommandService Service { get; }
|
||||
/// <summary>
|
||||
/// Gets the name of this module.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// Gets the summary of this module.
|
||||
/// </summary>
|
||||
public string Summary { get; }
|
||||
/// <summary>
|
||||
/// Gets the remarks of this module.
|
||||
/// </summary>
|
||||
public string Remarks { get; }
|
||||
/// <summary>
|
||||
/// Gets the group name (main prefix) of this module.
|
||||
/// </summary>
|
||||
public string Group { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only list of aliases associated with this module.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Aliases { get; }
|
||||
/// <summary>
|
||||
/// Gets a read-only list of commands associated with this module.
|
||||
/// </summary>
|
||||
public IReadOnlyList<CommandInfo> Commands { get; }
|
||||
/// <summary>
|
||||
/// Gets a read-only list of preconditions that apply to this module.
|
||||
/// </summary>
|
||||
public IReadOnlyList<PreconditionAttribute> Preconditions { get; }
|
||||
/// <summary>
|
||||
/// Gets a read-only list of attributes that apply to this module.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Attribute> Attributes { get; }
|
||||
/// <summary>
|
||||
/// Gets a read-only list of submodules associated with this module.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ModuleInfo> Submodules { get; }
|
||||
/// <summary>
|
||||
/// Gets the parent module of this submodule if applicable.
|
||||
/// </summary>
|
||||
public ModuleInfo Parent { get; }
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether this module is a submodule or not.
|
||||
/// </summary>
|
||||
public bool IsSubmodule => Parent != null;
|
||||
|
||||
internal ModuleInfo(ModuleBuilder builder, CommandService service, IServiceProvider services, ModuleInfo parent = null)
|
||||
|
||||
@@ -2,25 +2,56 @@ using Discord.Commands.Builders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the information of a parameter.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class ParameterInfo
|
||||
{
|
||||
private readonly TypeReader _reader;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command that associates with this parameter.
|
||||
/// </summary>
|
||||
public CommandInfo Command { get; }
|
||||
/// <summary>
|
||||
/// Gets the name of this parameter.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// Gets the summary of this parameter.
|
||||
/// </summary>
|
||||
public string Summary { get; }
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether this parameter is optional or not.
|
||||
/// </summary>
|
||||
public bool IsOptional { get; }
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether this parameter is a remainder parameter or not.
|
||||
/// </summary>
|
||||
public bool IsRemainder { get; }
|
||||
public bool IsMultiple { get; }
|
||||
/// <summary>
|
||||
/// Gets the type of the parameter.
|
||||
/// </summary>
|
||||
public Type Type { get; }
|
||||
/// <summary>
|
||||
/// Gets the default value for this optional parameter if applicable.
|
||||
/// </summary>
|
||||
public object DefaultValue { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only list of precondition that apply to this parameter.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ParameterPreconditionAttribute> Preconditions { get; }
|
||||
/// <summary>
|
||||
/// Gets a read-only list of attributes that apply to this parameter.
|
||||
/// </summary>
|
||||
public IReadOnlyList<Attribute> Attributes { get; }
|
||||
|
||||
internal ParameterInfo(ParameterBuilder builder, CommandInfo command, CommandService service)
|
||||
@@ -65,4 +96,4 @@ namespace Discord.Commands
|
||||
public override string ToString() => Name;
|
||||
private string DebuggerDisplay => $"{Name}{(IsOptional ? " (Optional)" : "")}{(IsRemainder ? " (Remainder)" : "")}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Discord.Commands
|
||||
_commands = ImmutableArray.Create<CommandInfo>();
|
||||
}
|
||||
|
||||
/// <exception cref="InvalidOperationException">Cannot add commands to the root node.</exception>
|
||||
public void AddCommand(CommandService service, string text, int index, CommandInfo command)
|
||||
{
|
||||
int nextSegment = NextSegment(text, index, service._separatorChar);
|
||||
|
||||
@@ -4,32 +4,57 @@ using Discord.Commands.Builders;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for a command module to inherit from.
|
||||
/// </summary>
|
||||
public abstract class ModuleBase : ModuleBase<ICommandContext> { }
|
||||
|
||||
/// <summary>
|
||||
/// Provides a base class for a command module to inherit from.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">A class that implements <see cref="ICommandContext"/>.</typeparam>
|
||||
public abstract class ModuleBase<T> : IModuleBase
|
||||
where T : class, ICommandContext
|
||||
{
|
||||
/// <summary>
|
||||
/// The underlying context of the command.
|
||||
/// </summary>
|
||||
/// <seealso cref="T:Discord.Commands.ICommandContext" />
|
||||
/// <seealso cref="T:Discord.Commands.CommandContext" />
|
||||
public T Context { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message to the source channel
|
||||
/// Sends a message to the source channel.
|
||||
/// </summary>
|
||||
/// <param name="message">Contents of the message; optional only if <paramref name="embed"/> is specified</param>
|
||||
/// <param name="isTTS">Specifies if Discord should read this message aloud using TTS</param>
|
||||
/// <param name="embed">An embed to be displayed alongside the message</param>
|
||||
/// <param name="message">
|
||||
/// Contents of the message; optional only if <paramref name="embed" /> is specified.
|
||||
/// </param>
|
||||
/// <param name="isTTS">Specifies if Discord should read this <paramref name="message"/> aloud using text-to-speech.</param>
|
||||
/// <param name="embed">An embed to be displayed alongside the <paramref name="message"/>.</param>
|
||||
protected virtual async Task<IUserMessage> ReplyAsync(string message = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
|
||||
{
|
||||
return await Context.Channel.SendMessageAsync(message, isTTS, embed, options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method to execute before executing the command.
|
||||
/// </summary>
|
||||
/// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param>
|
||||
protected virtual void BeforeExecute(CommandInfo command)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method to execute after executing the command.
|
||||
/// </summary>
|
||||
/// <param name="command">The <see cref="CommandInfo"/> of the command to be executed.</param>
|
||||
protected virtual void AfterExecute(CommandInfo command)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method to execute when building the module.
|
||||
/// </summary>
|
||||
/// <param name="commandService">The <see cref="CommandService"/> used to create the module.</param>
|
||||
/// <param name="builder">The builder used to build the module.</param>
|
||||
protected virtual void OnModuleBuilding(CommandService commandService, ModuleBuilder builder)
|
||||
{
|
||||
}
|
||||
@@ -38,7 +63,7 @@ namespace Discord.Commands
|
||||
void IModuleBase.SetContext(ICommandContext context)
|
||||
{
|
||||
var newValue = context as T;
|
||||
Context = newValue ?? throw new InvalidOperationException($"Invalid context type. Expected {typeof(T).Name}, got {context.GetType().Name}");
|
||||
Context = newValue ?? throw new InvalidOperationException($"Invalid context type. Expected {typeof(T).Name}, got {context.GetType().Name}.");
|
||||
}
|
||||
void IModuleBase.BeforeExecute(CommandInfo command) => BeforeExecute(command);
|
||||
void IModuleBase.AfterExecute(CommandInfo command) => AfterExecute(command);
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
namespace Discord.Commands
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the behavior when multiple matches are found during the command parsing stage.
|
||||
/// </summary>
|
||||
public enum MultiMatchHandling
|
||||
{
|
||||
/// <summary> Indicates that when multiple results are found, an exception should be thrown. </summary>
|
||||
Exception,
|
||||
/// <summary> Indicates that when multiple results are found, the best result should be chosen. </summary>
|
||||
Best
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,29 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="TypeReader"/> for parsing objects implementing <see cref="IChannel"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This <see cref="TypeReader"/> is shipped with Discord.Net and is used by default to parse any
|
||||
/// <see cref="IChannel"/> implemented object within a command. The TypeReader will attempt to first parse the
|
||||
/// input by mention, then the snowflake identifier, then by name; the highest candidate will be chosen as the
|
||||
/// final output; otherwise, an erroneous <see cref="TypeReaderResult"/> is returned.
|
||||
/// </remarks>
|
||||
/// <typeparam name="T">The type to be checked; must implement <see cref="IChannel"/>.</typeparam>
|
||||
public class ChannelTypeReader<T> : TypeReader
|
||||
where T : class, IChannel
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
if (context.Guild != null)
|
||||
{
|
||||
var results = new Dictionary<ulong, TypeReaderValue>();
|
||||
var channels = await context.Guild.GetChannelsAsync(CacheMode.CacheOnly).ConfigureAwait(false);
|
||||
ulong id;
|
||||
|
||||
//By Mention (1.0)
|
||||
if (MentionUtils.TryParseChannel(input, out id))
|
||||
if (MentionUtils.TryParseChannel(input, out ulong id))
|
||||
AddResult(results, await context.Guild.GetChannelAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 1.00f);
|
||||
|
||||
//By Id (0.9)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
@@ -44,6 +44,7 @@ namespace Discord.Commands
|
||||
_enumsByValue = byValueBuilder.ToImmutable();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
object enumValue;
|
||||
@@ -53,14 +54,14 @@ namespace Discord.Commands
|
||||
if (_enumsByValue.TryGetValue(baseValue, out enumValue))
|
||||
return Task.FromResult(TypeReaderResult.FromSuccess(enumValue));
|
||||
else
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, $"Value is not a {_enumType.Name}"));
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, $"Value is not a {_enumType.Name}."));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_enumsByName.TryGetValue(input.ToLower(), out enumValue))
|
||||
return Task.FromResult(TypeReaderResult.FromSuccess(enumValue));
|
||||
else
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, $"Value is not a {_enumType.Name}"));
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, $"Value is not a {_enumType.Name}."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,18 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="TypeReader"/> for parsing objects implementing <see cref="IMessage"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to be checked; must implement <see cref="IMessage"/>.</typeparam>
|
||||
public class MessageTypeReader<T> : TypeReader
|
||||
where T : class, IMessage
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
ulong id;
|
||||
|
||||
//By Id (1.0)
|
||||
if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out id))
|
||||
if (ulong.TryParse(input, NumberStyles.None, CultureInfo.InvariantCulture, out ulong id))
|
||||
{
|
||||
if (await context.Channel.GetMessageAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) is T msg)
|
||||
return TypeReaderResult.FromSuccess(msg);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
@@ -24,11 +24,12 @@ namespace Discord.Commands
|
||||
_baseTypeReader = baseTypeReader;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
if (string.Equals(input, "null", StringComparison.OrdinalIgnoreCase) || string.Equals(input, "nothing", StringComparison.OrdinalIgnoreCase))
|
||||
return TypeReaderResult.FromSuccess(new T?());
|
||||
return await _baseTypeReader.ReadAsync(context, input, services);
|
||||
return await _baseTypeReader.ReadAsync(context, input, services).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
@@ -17,14 +17,16 @@ namespace Discord.Commands
|
||||
private readonly TryParseDelegate<T> _tryParse;
|
||||
private readonly float _score;
|
||||
|
||||
/// <exception cref="ArgumentOutOfRangeException"><typeparamref name="T"/> must be within the range [0, 1].</exception>
|
||||
public PrimitiveTypeReader()
|
||||
: this(PrimitiveParsers.Get<T>(), 1)
|
||||
{ }
|
||||
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="score"/> must be within the range [0, 1].</exception>
|
||||
public PrimitiveTypeReader(TryParseDelegate<T> tryParse, float score)
|
||||
{
|
||||
if (score < 0 || score > 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(score), score, "Scores must be within the range [0, 1]");
|
||||
throw new ArgumentOutOfRangeException(nameof(score), score, "Scores must be within the range [0, 1].");
|
||||
|
||||
_tryParse = tryParse;
|
||||
_score = score;
|
||||
@@ -34,7 +36,7 @@ namespace Discord.Commands
|
||||
{
|
||||
if (_tryParse(input, out T value))
|
||||
return Task.FromResult(TypeReaderResult.FromSuccess(new TypeReaderValue(value, _score)));
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, $"Failed to parse {typeof(T).Name}"));
|
||||
return Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, $"Failed to parse {typeof(T).Name}."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,20 +6,23 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="TypeReader"/> for parsing objects implementing <see cref="IRole"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to be checked; must implement <see cref="IRole"/>.</typeparam>
|
||||
public class RoleTypeReader<T> : TypeReader
|
||||
where T : class, IRole
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
ulong id;
|
||||
|
||||
if (context.Guild != null)
|
||||
{
|
||||
var results = new Dictionary<ulong, TypeReaderValue>();
|
||||
var roles = context.Guild.Roles;
|
||||
|
||||
//By Mention (1.0)
|
||||
if (MentionUtils.TryParseRole(input, out id))
|
||||
if (MentionUtils.TryParseRole(input, out var id))
|
||||
AddResult(results, context.Guild.GetRole(id) as T, 1.00f);
|
||||
|
||||
//By Id (0.9)
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Discord.Commands
|
||||
"%s's'", // 1s
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
return (TimeSpan.TryParseExact(input.ToLowerInvariant(), Formats, CultureInfo.InvariantCulture, out var timeSpan))
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a reader class that parses user input into a specified type.
|
||||
/// </summary>
|
||||
public abstract class TypeReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to parse the <paramref name="input"/> into the desired type.
|
||||
/// </summary>
|
||||
/// <param name="context">The context of the command.</param>
|
||||
/// <param name="input">The raw input of the command.</param>
|
||||
/// <param name="services">The service collection used for dependency injection.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous parsing operation. The task result contains the parsing result.
|
||||
/// </returns>
|
||||
public abstract Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,21 +7,25 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="TypeReader"/> for parsing objects implementing <see cref="IUser"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to be checked; must implement <see cref="IUser"/>.</typeparam>
|
||||
public class UserTypeReader<T> : TypeReader
|
||||
where T : class, IUser
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override async Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
|
||||
{
|
||||
var results = new Dictionary<ulong, TypeReaderValue>();
|
||||
IAsyncEnumerable<IUser> channelUsers = context.Channel.GetUsersAsync(CacheMode.CacheOnly).Flatten(); // it's better
|
||||
IReadOnlyCollection<IGuildUser> guildUsers = ImmutableArray.Create<IGuildUser>();
|
||||
ulong id;
|
||||
|
||||
if (context.Guild != null)
|
||||
guildUsers = await context.Guild.GetUsersAsync(CacheMode.CacheOnly).ConfigureAwait(false);
|
||||
|
||||
//By Mention (1.0)
|
||||
if (MentionUtils.TryParseUser(input, out id))
|
||||
if (MentionUtils.TryParseUser(input, out var id))
|
||||
{
|
||||
if (context.Guild != null)
|
||||
AddResult(results, await context.Guild.GetUserAsync(id, CacheMode.CacheOnly).ConfigureAwait(false) as T, 1.00f);
|
||||
@@ -46,7 +50,7 @@ namespace Discord.Commands
|
||||
if (ushort.TryParse(input.Substring(index + 1), out ushort discriminator))
|
||||
{
|
||||
var channelUser = await channelUsers.FirstOrDefault(x => x.DiscriminatorValue == discriminator &&
|
||||
string.Equals(username, x.Username, StringComparison.OrdinalIgnoreCase));
|
||||
string.Equals(username, x.Username, StringComparison.OrdinalIgnoreCase)).ConfigureAwait(false);
|
||||
AddResult(results, channelUser as T, channelUser?.Username == username ? 0.85f : 0.75f);
|
||||
|
||||
var guildUser = guildUsers.FirstOrDefault(x => x.DiscriminatorValue == discriminator &&
|
||||
@@ -59,7 +63,8 @@ namespace Discord.Commands
|
||||
{
|
||||
await channelUsers
|
||||
.Where(x => string.Equals(input, x.Username, StringComparison.OrdinalIgnoreCase))
|
||||
.ForEachAsync(channelUser => AddResult(results, channelUser as T, channelUser.Username == input ? 0.65f : 0.55f));
|
||||
.ForEachAsync(channelUser => AddResult(results, channelUser as T, channelUser.Username == input ? 0.65f : 0.55f))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (var guildUser in guildUsers.Where(x => string.Equals(input, x.Username, StringComparison.OrdinalIgnoreCase)))
|
||||
AddResult(results, guildUser as T, guildUser.Username == input ? 0.60f : 0.50f);
|
||||
@@ -69,7 +74,8 @@ namespace Discord.Commands
|
||||
{
|
||||
await channelUsers
|
||||
.Where(x => string.Equals(input, (x as IGuildUser)?.Nickname, StringComparison.OrdinalIgnoreCase))
|
||||
.ForEachAsync(channelUser => AddResult(results, channelUser as T, (channelUser as IGuildUser).Nickname == input ? 0.65f : 0.55f));
|
||||
.ForEachAsync(channelUser => AddResult(results, channelUser as T, (channelUser as IGuildUser).Nickname == input ? 0.65f : 0.55f))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (var guildUser in guildUsers.Where(x => string.Equals(input, x.Nickname, StringComparison.OrdinalIgnoreCase)))
|
||||
AddResult(results, guildUser as T, guildUser.Nickname == input ? 0.60f : 0.50f);
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information of the command's overall execution result.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public struct ExecuteResult : IResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the exception that may have occurred during the command execution.
|
||||
/// </summary>
|
||||
public Exception Exception { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public CommandError? Error { get; }
|
||||
/// <inheritdoc />
|
||||
public string ErrorReason { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsSuccess => !Error.HasValue;
|
||||
|
||||
private ExecuteResult(Exception exception, CommandError? error, string errorReason)
|
||||
@@ -20,15 +29,56 @@ namespace Discord.Commands
|
||||
ErrorReason = errorReason;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ExecuteResult" /> with no error, indicating a successful execution.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="ExecuteResult" /> that does not contain any errors.
|
||||
/// </returns>
|
||||
public static ExecuteResult FromSuccess()
|
||||
=> new ExecuteResult(null, null, null);
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ExecuteResult" /> with a specified <see cref="CommandError" /> and its
|
||||
/// reason, indicating an unsuccessful execution.
|
||||
/// </summary>
|
||||
/// <param name="error">The type of error.</param>
|
||||
/// <param name="reason">The reason behind the error.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="ExecuteResult" /> that contains a <see cref="CommandError" /> and reason.
|
||||
/// </returns>
|
||||
public static ExecuteResult FromError(CommandError error, string reason)
|
||||
=> new ExecuteResult(null, error, reason);
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ExecuteResult" /> with a specified exception, indicating an unsuccessful
|
||||
/// execution.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception that caused the command execution to fail.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="ExecuteResult" /> that contains the exception that caused the unsuccessful execution, along
|
||||
/// with a <see cref="CommandError" /> of type <c>Exception</c> as well as the exception message as the
|
||||
/// reason.
|
||||
/// </returns>
|
||||
public static ExecuteResult FromError(Exception ex)
|
||||
=> new ExecuteResult(ex, CommandError.Exception, ex.Message);
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ExecuteResult" /> with a specified result; this may or may not be an
|
||||
/// successful execution depending on the <see cref="Discord.Commands.IResult.Error" /> and
|
||||
/// <see cref="Discord.Commands.IResult.ErrorReason" /> specified.
|
||||
/// </summary>
|
||||
/// <param name="result">The result to inherit from.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="ExecuteResult"/> that inherits the <see cref="IResult"/> error type and reason.
|
||||
/// </returns>
|
||||
public static ExecuteResult FromError(IResult result)
|
||||
=> new ExecuteResult(null, result.Error, result.ErrorReason);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string that indicates the execution result.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>Success</c> if <see cref="IsSuccess"/> is <c>true</c>; otherwise "<see cref="Error"/>:
|
||||
/// <see cref="ErrorReason"/>".
|
||||
/// </returns>
|
||||
public override string ToString() => IsSuccess ? "Success" : $"{Error}: {ErrorReason}";
|
||||
private string DebuggerDisplay => IsSuccess ? "Success" : $"{Error}: {ErrorReason}";
|
||||
}
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
namespace Discord.Commands
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information of the result related to a command.
|
||||
/// </summary>
|
||||
public interface IResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the error type that may have occurred during the operation.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="CommandError" /> indicating the type of error that may have occurred during the operation;
|
||||
/// <c>null</c> if the operation was successful.
|
||||
/// </returns>
|
||||
CommandError? Error { get; }
|
||||
/// <summary>
|
||||
/// Describes the reason for the error.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the error reason.
|
||||
/// </returns>
|
||||
string ErrorReason { get; }
|
||||
/// <summary>
|
||||
/// Indicates whether the operation was successful or not.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the result is positive; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
bool IsSuccess { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,21 @@ using System.Diagnostics;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information for the parsing result from the command service's parser.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public struct ParseResult : IResult
|
||||
{
|
||||
public IReadOnlyList<TypeReaderResult> ArgValues { get; }
|
||||
public IReadOnlyList<TypeReaderResult> ParamValues { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public CommandError? Error { get; }
|
||||
/// <inheritdoc/>
|
||||
public string ErrorReason { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSuccess => !Error.HasValue;
|
||||
|
||||
private ParseResult(IReadOnlyList<TypeReaderResult> argValues, IReadOnlyList<TypeReaderResult> paramValues, CommandError? error, string errorReason)
|
||||
@@ -22,7 +28,7 @@ namespace Discord.Commands
|
||||
Error = error;
|
||||
ErrorReason = errorReason;
|
||||
}
|
||||
|
||||
|
||||
public static ParseResult FromSuccess(IReadOnlyList<TypeReaderResult> argValues, IReadOnlyList<TypeReaderResult> paramValues)
|
||||
{
|
||||
for (int i = 0; i < argValues.Count; i++)
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Discord.Commands
|
||||
PreconditionResults = (preconditions ?? new List<PreconditionResult>(0)).ToReadOnlyCollection();
|
||||
}
|
||||
|
||||
public static new PreconditionGroupResult FromSuccess()
|
||||
public new static PreconditionGroupResult FromSuccess()
|
||||
=> new PreconditionGroupResult(null, null, null);
|
||||
public static PreconditionGroupResult FromError(string reason, ICollection<PreconditionResult> preconditions)
|
||||
=> new PreconditionGroupResult(CommandError.UnmetPrecondition, reason, preconditions);
|
||||
|
||||
@@ -3,29 +3,56 @@ using System.Diagnostics;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a result type for command preconditions.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class PreconditionResult : IResult
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public CommandError? Error { get; }
|
||||
/// <inheritdoc/>
|
||||
public string ErrorReason { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSuccess => !Error.HasValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="PreconditionResult" /> class with the command <paramref name="error"/> type
|
||||
/// and reason.
|
||||
/// </summary>
|
||||
/// <param name="error">The type of failure.</param>
|
||||
/// <param name="errorReason">The reason of failure.</param>
|
||||
protected PreconditionResult(CommandError? error, string errorReason)
|
||||
{
|
||||
Error = error;
|
||||
ErrorReason = errorReason;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="PreconditionResult" /> with no errors.
|
||||
/// </summary>
|
||||
public static PreconditionResult FromSuccess()
|
||||
=> new PreconditionResult(null, null);
|
||||
/// <summary>
|
||||
/// Returns a <see cref="PreconditionResult" /> with <see cref="CommandError.UnmetPrecondition" /> and the
|
||||
/// specified reason.
|
||||
/// </summary>
|
||||
/// <param name="reason">The reason of failure.</param>
|
||||
public static PreconditionResult FromError(string reason)
|
||||
=> new PreconditionResult(CommandError.UnmetPrecondition, reason);
|
||||
public static PreconditionResult FromError(Exception ex)
|
||||
=> new PreconditionResult(CommandError.Exception, ex.Message);
|
||||
/// <summary>
|
||||
/// Returns a <see cref="PreconditionResult" /> with the specified <paramref name="result"/> type.
|
||||
/// </summary>
|
||||
/// <param name="result">The result of failure.</param>
|
||||
public static PreconditionResult FromError(IResult result)
|
||||
=> new PreconditionResult(result.Error, result.ErrorReason);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string indicating whether the <see cref="PreconditionResult"/> is successful.
|
||||
/// </summary>
|
||||
public override string ToString() => IsSuccess ? "Success" : $"{Error}: {ErrorReason}";
|
||||
private string DebuggerDisplay => IsSuccess ? "Success" : $"{Error}: {ErrorReason}";
|
||||
}
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public abstract class RuntimeResult : IResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="RuntimeResult" /> class with the type of error and reason.
|
||||
/// </summary>
|
||||
/// <param name="error">The type of failure, or <c>null</c> if none.</param>
|
||||
/// <param name="reason">The reason of failure.</param>
|
||||
protected RuntimeResult(CommandError? error, string reason)
|
||||
{
|
||||
Error = error;
|
||||
Reason = reason;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public CommandError? Error { get; }
|
||||
/// <summary> Describes the execution reason or result. </summary>
|
||||
public string Reason { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSuccess => !Error.HasValue;
|
||||
|
||||
/// <inheritdoc/>
|
||||
string IResult.ErrorReason => Reason;
|
||||
|
||||
public override string ToString() => Reason ?? (IsSuccess ? "Successful" : "Unsuccessful");
|
||||
|
||||
@@ -10,9 +10,12 @@ namespace Discord.Commands
|
||||
public string Text { get; }
|
||||
public IReadOnlyList<CommandMatch> Commands { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public CommandError? Error { get; }
|
||||
/// <inheritdoc/>
|
||||
public string ErrorReason { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSuccess => !Error.HasValue;
|
||||
|
||||
private SearchResult(string text, IReadOnlyList<CommandMatch> commands, CommandError? error, string errorReason)
|
||||
|
||||
@@ -27,10 +27,15 @@ namespace Discord.Commands
|
||||
{
|
||||
public IReadOnlyCollection<TypeReaderValue> Values { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public CommandError? Error { get; }
|
||||
/// <inheritdoc/>
|
||||
public string ErrorReason { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSuccess => !Error.HasValue;
|
||||
|
||||
/// <exception cref="InvalidOperationException">TypeReaderResult was not successful.</exception>
|
||||
public object BestMatch => IsSuccess
|
||||
? (Values.Count == 1 ? Values.Single().Value : Values.OrderByDescending(v => v.Score).First().Value)
|
||||
: throw new InvalidOperationException("TypeReaderResult was not successful.");
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
namespace Discord.Commands
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the behavior of the command execution workflow.
|
||||
/// </summary>
|
||||
/// <seealso cref="CommandServiceConfig"/>
|
||||
/// <seealso cref="CommandAttribute"/>
|
||||
public enum RunMode
|
||||
{
|
||||
/// <summary>
|
||||
/// The default behaviour set in <see cref="CommandServiceConfig"/>.
|
||||
/// </summary>
|
||||
Default,
|
||||
/// <summary>
|
||||
/// Executes the command on the same thread as gateway one.
|
||||
/// </summary>
|
||||
Sync,
|
||||
/// <summary>
|
||||
/// Executes the command on a different thread from the gateway one.
|
||||
/// </summary>
|
||||
Async
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility methods for generating matching pairs of unicode quotation marks for CommandServiceConfig
|
||||
/// Utility class which contains the default matching pairs of quotation marks for CommandServiceConfig
|
||||
/// </summary>
|
||||
internal static class QuotationAliasUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates an IEnumerable of characters representing open-close pairs of
|
||||
/// quotation punctuation.
|
||||
/// A default map of open-close pairs of quotation marks.
|
||||
/// Contains many regional and Unicode equivalents.
|
||||
/// Used in the <see cref="CommandServiceConfig"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="CommandServiceConfig.QuotationMarkAliasMap"/>
|
||||
internal static Dictionary<char, char> GetDefaultAliasMap
|
||||
{
|
||||
get
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Discord.Commands
|
||||
{
|
||||
@@ -38,7 +37,7 @@ namespace Discord.Commands
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to create \"{ownerType.FullName}\"", ex);
|
||||
throw new Exception($"Failed to create \"{ownerType.FullName}\".", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +45,12 @@ namespace Discord.Commands
|
||||
{
|
||||
var constructors = ownerType.DeclaredConstructors.Where(x => !x.IsStatic).ToArray();
|
||||
if (constructors.Length == 0)
|
||||
throw new InvalidOperationException($"No constructor found for \"{ownerType.FullName}\"");
|
||||
throw new InvalidOperationException($"No constructor found for \"{ownerType.FullName}\".");
|
||||
else if (constructors.Length > 1)
|
||||
throw new InvalidOperationException($"Multiple constructors found for \"{ownerType.FullName}\"");
|
||||
throw new InvalidOperationException($"Multiple constructors found for \"{ownerType.FullName}\".");
|
||||
return constructors[0];
|
||||
}
|
||||
private static System.Reflection.PropertyInfo[] GetProperties(TypeInfo ownerType)
|
||||
private static PropertyInfo[] GetProperties(TypeInfo ownerType)
|
||||
{
|
||||
var result = new List<System.Reflection.PropertyInfo>();
|
||||
while (ownerType != ObjectTypeInfo)
|
||||
@@ -71,7 +70,7 @@ namespace Discord.Commands
|
||||
return commands;
|
||||
if (memberType == typeof(IServiceProvider) || memberType == services.GetType())
|
||||
return services;
|
||||
var service = services?.GetService(memberType);
|
||||
var service = services.GetService(memberType);
|
||||
if (service != null)
|
||||
return service;
|
||||
throw new InvalidOperationException($"Failed to create \"{ownerType.FullName}\", dependency \"{memberType.Name}\" was not found.");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Discord.Audio
|
||||
@@ -7,8 +7,17 @@ namespace Discord.Audio
|
||||
{
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
|
||||
public override void SetLength(long value) { throw new NotSupportedException(); }
|
||||
public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="NotSupportedException">Reading this stream is not supported.</exception>
|
||||
public override int Read(byte[] buffer, int offset, int count) =>
|
||||
throw new NotSupportedException();
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="NotSupportedException">Setting the length to this stream is not supported.</exception>
|
||||
public override void SetLength(long value) =>
|
||||
throw new NotSupportedException();
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="NotSupportedException">Seeking this stream is not supported..</exception>
|
||||
public override long Seek(long offset, SeekOrigin origin) =>
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,9 @@ namespace Discord.Audio
|
||||
public override bool CanSeek => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public virtual void WriteHeader(ushort seq, uint timestamp, bool missed)
|
||||
{
|
||||
throw new InvalidOperationException("This stream does not accept headers");
|
||||
}
|
||||
/// <exception cref="InvalidOperationException">This stream does not accept headers.</exception>
|
||||
public virtual void WriteHeader(ushort seq, uint timestamp, bool missed) =>
|
||||
throw new InvalidOperationException("This stream does not accept headers.");
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();
|
||||
@@ -30,15 +29,30 @@ namespace Discord.Audio
|
||||
|
||||
public virtual Task ClearAsync(CancellationToken cancellationToken) { return Task.Delay(0); }
|
||||
|
||||
public override long Length { get { throw new NotSupportedException(); } }
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="NotSupportedException">Reading stream length is not supported.</exception>
|
||||
public override long Length =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="NotSupportedException">Getting or setting this stream position is not supported.</exception>
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
|
||||
public override void SetLength(long value) { throw new NotSupportedException(); }
|
||||
public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="NotSupportedException">Reading this stream is not supported.</exception>
|
||||
public override int Read(byte[] buffer, int offset, int count) =>
|
||||
throw new NotSupportedException();
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="NotSupportedException">Setting the length to this stream is not supported.</exception>
|
||||
public override void SetLength(long value) =>
|
||||
throw new NotSupportedException();
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="NotSupportedException">Seeking this stream is not supported..</exception>
|
||||
public override long Seek(long offset, SeekOrigin origin) =>
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Discord.Audio
|
||||
|
||||
/// <summary> Gets the current connection state of this client. </summary>
|
||||
ConnectionState ConnectionState { get; }
|
||||
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the voice websocket server. </summary>
|
||||
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the voice WebSocket server. </summary>
|
||||
int Latency { get; }
|
||||
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the voice UDP server. </summary>
|
||||
int UdpLatency { get; }
|
||||
|
||||
@@ -2,10 +2,32 @@ using System;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a class containing the strings related to various Content Delivery Networks (CDNs).
|
||||
/// </summary>
|
||||
public static class CDN
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns an application icon URL.
|
||||
/// </summary>
|
||||
/// <param name="appId">The application identifier.</param>
|
||||
/// <param name="iconId">The icon identifier.</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the application's icon.
|
||||
/// </returns>
|
||||
public static string GetApplicationIconUrl(ulong appId, string iconId)
|
||||
=> iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a user avatar URL.
|
||||
/// </summary>
|
||||
/// <param name="userId">The user snowflake identifier.</param>
|
||||
/// <param name="avatarId">The avatar identifier.</param>
|
||||
/// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048.</param>
|
||||
/// <param name="format">The format to return.</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the user's avatar in the specified size.
|
||||
/// </returns>
|
||||
public static string GetUserAvatarUrl(ulong userId, string avatarId, ushort size, ImageFormat format)
|
||||
{
|
||||
if (avatarId == null)
|
||||
@@ -13,27 +35,90 @@ namespace Discord
|
||||
string extension = FormatToExtension(format, avatarId);
|
||||
return $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.{extension}?size={size}";
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the default user avatar URL.
|
||||
/// </summary>
|
||||
/// <param name="discriminator">The discriminator value of a user.</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the user's default avatar when one isn't set.
|
||||
/// </returns>
|
||||
public static string GetDefaultUserAvatarUrl(ushort discriminator)
|
||||
{
|
||||
return $"{DiscordConfig.CDNUrl}embed/avatars/{discriminator % 5}.png";
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns an icon URL.
|
||||
/// </summary>
|
||||
/// <param name="guildId">The guild snowflake identifier.</param>
|
||||
/// <param name="iconId">The icon identifier.</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the guild's icon.
|
||||
/// </returns>
|
||||
public static string GetGuildIconUrl(ulong guildId, string iconId)
|
||||
=> iconId != null ? $"{DiscordConfig.CDNUrl}icons/{guildId}/{iconId}.jpg" : null;
|
||||
/// <summary>
|
||||
/// Returns a guild splash URL.
|
||||
/// </summary>
|
||||
/// <param name="guildId">The guild snowflake identifier.</param>
|
||||
/// <param name="splashId">The splash icon identifier.</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the guild's icon.
|
||||
/// </returns>
|
||||
public static string GetGuildSplashUrl(ulong guildId, string splashId)
|
||||
=> splashId != null ? $"{DiscordConfig.CDNUrl}splashes/{guildId}/{splashId}.jpg" : null;
|
||||
/// <summary>
|
||||
/// Returns a channel icon URL.
|
||||
/// </summary>
|
||||
/// <param name="channelId">The channel snowflake identifier.</param>
|
||||
/// <param name="iconId">The icon identifier.</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the channel's icon.
|
||||
/// </returns>
|
||||
public static string GetChannelIconUrl(ulong channelId, string iconId)
|
||||
=> iconId != null ? $"{DiscordConfig.CDNUrl}channel-icons/{channelId}/{iconId}.jpg" : null;
|
||||
/// <summary>
|
||||
/// Returns an emoji URL.
|
||||
/// </summary>
|
||||
/// <param name="emojiId">The emoji snowflake identifier.</param>
|
||||
/// <param name="animated">Whether this emoji is animated.</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the custom emote.
|
||||
/// </returns>
|
||||
public static string GetEmojiUrl(ulong emojiId, bool animated)
|
||||
=> $"{DiscordConfig.CDNUrl}emojis/{emojiId}.{(animated ? "gif" : "png")}";
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Rich Presence asset URL.
|
||||
/// </summary>
|
||||
/// <param name="appId">The application identifier.</param>
|
||||
/// <param name="assetId">The asset identifier.</param>
|
||||
/// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048.</param>
|
||||
/// <param name="format">The format to return.</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the asset image in the specified size.
|
||||
/// </returns>
|
||||
public static string GetRichAssetUrl(ulong appId, string assetId, ushort size, ImageFormat format)
|
||||
{
|
||||
string extension = FormatToExtension(format, "");
|
||||
return $"{DiscordConfig.CDNUrl}app-assets/{appId}/{assetId}.{extension}?size={size}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a Spotify album URL.
|
||||
/// </summary>
|
||||
/// <param name="albumArtId">The identifier for the album art (e.g. 6be8f4c8614ecf4f1dd3ebba8d8692d8ce4951ac).</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the Spotify album art.
|
||||
/// </returns>
|
||||
public static string GetSpotifyAlbumArtUrl(string albumArtId)
|
||||
=> $"https://i.scdn.co/image/{albumArtId}";
|
||||
/// <summary>
|
||||
/// Returns a Spotify direct URL for a track.
|
||||
/// </summary>
|
||||
/// <param name="trackId">The identifier for the track (e.g. 4uLU6hMCjMI75M1A2tKUQC).</param>
|
||||
/// <returns>
|
||||
/// A URL pointing to the Spotify track.
|
||||
/// </returns>
|
||||
public static string GetSpotifyDirectUrl(string trackId)
|
||||
=> $"https://open.spotify.com/track/{trackId}";
|
||||
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
namespace Discord.Commands
|
||||
namespace Discord.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a context of a command. This may include the client, guild, channel, user, and message.
|
||||
/// </summary>
|
||||
public interface ICommandContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IDiscordClient" /> that the command is executed with.
|
||||
/// </summary>
|
||||
IDiscordClient Client { get; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IGuild" /> that the command is executed in.
|
||||
/// </summary>
|
||||
IGuild Guild { get; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IMessageChannel" /> that the command is executed in.
|
||||
/// </summary>
|
||||
IMessageChannel Channel { get; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IUser" /> who executed the command.
|
||||
/// </summary>
|
||||
IUser User { get; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IUserMessage" /> that the command is interpreted from.
|
||||
/// </summary>
|
||||
IUserMessage Message { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary> Specifies the connection state of a client. </summary>
|
||||
public enum ConnectionState : byte
|
||||
{
|
||||
/// <summary> The client has disconnected from Discord. </summary>
|
||||
Disconnected,
|
||||
/// <summary> The client is connecting to Discord. </summary>
|
||||
Connecting,
|
||||
/// <summary> The client has established a connection to Discord. </summary>
|
||||
Connected,
|
||||
/// <summary> The client is disconnecting from Discord. </summary>
|
||||
Disconnecting
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,35 +2,143 @@ using System.Reflection;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines various behaviors of Discord.Net.
|
||||
/// </summary>
|
||||
public class DiscordConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the API version Discord.Net uses.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="int"/> representing the API version that Discord.Net uses to communicate with Discord.
|
||||
/// <para>A list of available API version can be seen on the official
|
||||
/// <see href="https://discordapp.com/developers/docs/reference#api-versioning">Discord API documentation</see>
|
||||
/// .</para>
|
||||
/// </returns>
|
||||
public const int APIVersion = 6;
|
||||
/// <summary>
|
||||
/// Returns the Voice API version Discord.Net uses.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="int"/> representing the API version that Discord.Net uses to communicate with Discord's
|
||||
/// voice server.
|
||||
/// </returns>
|
||||
public const int VoiceAPIVersion = 3;
|
||||
/// <summary>
|
||||
/// Gets the Discord.Net version, including the build number.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the detailed version information, including its build number; <c>Unknown</c> when
|
||||
/// the version fails to be fetched.
|
||||
/// </returns>
|
||||
public static string Version { get; } =
|
||||
typeof(DiscordConfig).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ??
|
||||
typeof(DiscordConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3) ??
|
||||
"Unknown";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the user agent that Discord.Net uses in its clients.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The user agent used in each Discord.Net request.
|
||||
/// </returns>
|
||||
public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})";
|
||||
/// <summary>
|
||||
/// Returns the base Discord API URL.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The Discord API URL using <see cref="APIVersion"/>.
|
||||
/// </returns>
|
||||
public static readonly string APIUrl = $"https://discordapp.com/api/v{APIVersion}/";
|
||||
/// <summary>
|
||||
/// Returns the base Discord CDN URL.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The base Discord Content Delivery Network (CDN) URL.
|
||||
/// </returns>
|
||||
public const string CDNUrl = "https://cdn.discordapp.com/";
|
||||
/// <summary>
|
||||
/// Returns the base Discord invite URL.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The base Discord invite URL.
|
||||
/// </returns>
|
||||
public const string InviteUrl = "https://discord.gg/";
|
||||
|
||||
/// <summary>
|
||||
/// Returns the default timeout for requests.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The amount of time it takes in milliseconds before a request is timed out.
|
||||
/// </returns>
|
||||
public const int DefaultRequestTimeout = 15000;
|
||||
/// <summary>
|
||||
/// Returns the max length for a Discord message.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The maximum length of a message allowed by Discord.
|
||||
/// </returns>
|
||||
public const int MaxMessageSize = 2000;
|
||||
/// <summary>
|
||||
/// Returns the max messages allowed to be in a request.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The maximum number of messages that can be gotten per-batch.
|
||||
/// </returns>
|
||||
public const int MaxMessagesPerBatch = 100;
|
||||
/// <summary>
|
||||
/// Returns the max users allowed to be in a request.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The maximum number of users that can be gotten per-batch.
|
||||
/// </returns>
|
||||
public const int MaxUsersPerBatch = 1000;
|
||||
/// <summary>
|
||||
/// Returns the max guilds allowed to be in a request.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The maximum number of guilds that can be gotten per-batch.
|
||||
/// </returns>
|
||||
public const int MaxGuildsPerBatch = 100;
|
||||
/// <summary>
|
||||
/// Returns the max user reactions allowed to be in a request.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The maximum number of user reactions that can be gotten per-batch.
|
||||
/// </returns>
|
||||
public const int MaxUserReactionsPerBatch = 100;
|
||||
/// <summary>
|
||||
/// Returns the max audit log entries allowed to be in a request.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The maximum number of audit log entries that can be gotten per-batch.
|
||||
/// </returns>
|
||||
public const int MaxAuditLogEntriesPerBatch = 100;
|
||||
|
||||
/// <summary> Gets or sets how a request should act in the case of an error, by default. </summary>
|
||||
/// <summary>
|
||||
/// Gets or sets how a request should act in the case of an error, by default.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The currently set <see cref="RetryMode"/>.
|
||||
/// </returns>
|
||||
public RetryMode DefaultRetryMode { get; set; } = RetryMode.AlwaysRetry;
|
||||
|
||||
/// <summary> Gets or sets the minimum log level severity that will be sent to the Log event. </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum log level severity that will be sent to the Log event.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The currently set <see cref="LogSeverity"/> for logging level.
|
||||
/// </returns>
|
||||
public LogSeverity LogLevel { get; set; } = LogSeverity.Info;
|
||||
|
||||
/// <summary> Gets or sets whether the initial log entry should be printed. </summary>
|
||||
/// <summary>
|
||||
/// Gets or sets whether the initial log entry should be printed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If set to <c>true</c>, the library will attempt to print the current version of the library, as well as
|
||||
/// the API version it uses on startup.
|
||||
/// </remarks>
|
||||
internal bool DisplayInitialLog { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies a Discord user's activity type.
|
||||
/// </summary>
|
||||
public enum ActivityType
|
||||
{
|
||||
/// <summary>
|
||||
/// The user is playing a game.
|
||||
/// </summary>
|
||||
Playing = 0,
|
||||
/// <summary>
|
||||
/// The user is streaming online.
|
||||
/// </summary>
|
||||
Streaming = 1,
|
||||
/// <summary>
|
||||
/// The user is listening to a song.
|
||||
/// </summary>
|
||||
Listening = 2,
|
||||
/// <summary>
|
||||
/// The user is watching a media.
|
||||
/// </summary>
|
||||
Watching = 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,30 @@ using System.Diagnostics;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// A user's game status.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class Game : IActivity
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Name { get; internal set; }
|
||||
/// <inheritdoc/>
|
||||
public ActivityType Type { get; internal set; }
|
||||
|
||||
internal Game() { }
|
||||
/// <summary>
|
||||
/// Creates a <see cref="Game"/> with the provided <paramref name="name"/> and <see cref="ActivityType"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the game.</param>
|
||||
/// <param name="type">The type of activity. Default is <see cref="Discord.ActivityType.Playing"/>.</param>
|
||||
public Game(string name, ActivityType type = ActivityType.Playing)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary> Returns the name of the <see cref="Game"/>. </summary>
|
||||
public override string ToString() => Name;
|
||||
private string DebuggerDisplay => Name;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// An asset for a <see cref="RichGame" /> object containing the text and image.
|
||||
/// </summary>
|
||||
public class GameAsset
|
||||
{
|
||||
internal GameAsset() { }
|
||||
|
||||
internal ulong? ApplicationId { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of the asset.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the description of the asset.
|
||||
/// </returns>
|
||||
public string Text { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the image ID of the asset.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the unique image identifier of the asset.
|
||||
/// </returns>
|
||||
public string ImageId { get; internal set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the image URL of the asset.
|
||||
/// </summary>
|
||||
/// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048.</param>
|
||||
/// <param name="format">The format to return.</param>
|
||||
/// <returns>
|
||||
/// A string pointing to the image URL of the asset; <c>null</c> when the application ID does not exist.
|
||||
/// </returns>
|
||||
public string GetImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)
|
||||
=> ApplicationId.HasValue ? CDN.GetRichAssetUrl(ApplicationId.Value, ImageId, size, format) : null;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Party information for a <see cref="RichGame" /> object.
|
||||
/// </summary>
|
||||
public class GameParty
|
||||
{
|
||||
internal GameParty() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID of the party.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the unique identifier of the party.
|
||||
/// </returns>
|
||||
public string Id { get; internal set; }
|
||||
public long Members { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the party's current and maximum size.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="long"/> representing the capacity of the party.
|
||||
/// </returns>
|
||||
public long Capacity { get; internal set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Party secret for a <see cref="RichGame" /> object.
|
||||
/// </summary>
|
||||
public class GameSecrets
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the secret for a specific instanced match.
|
||||
/// </summary>
|
||||
public string Match { get; }
|
||||
/// <summary>
|
||||
/// Gets the secret for joining a party.
|
||||
/// </summary>
|
||||
public string Join { get; }
|
||||
/// <summary>
|
||||
/// Gets the secret for spectating a game.
|
||||
/// </summary>
|
||||
public string Spectate { get; }
|
||||
|
||||
internal GameSecrets(string match, string join, string spectate)
|
||||
@@ -13,4 +25,4 @@
|
||||
Spectate = spectate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Timestamps for a <see cref="RichGame" /> object.
|
||||
/// </summary>
|
||||
public class GameTimestamps
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets when the activity started.
|
||||
/// </summary>
|
||||
public DateTimeOffset? Start { get; }
|
||||
/// <summary>
|
||||
/// Gets when the activity ends.
|
||||
/// </summary>
|
||||
public DateTimeOffset? End { get; }
|
||||
|
||||
internal GameTimestamps(DateTimeOffset? start, DateTimeOffset? end)
|
||||
@@ -13,4 +22,4 @@ namespace Discord
|
||||
End = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// A user's activity status, typically a <see cref="Game"/>.
|
||||
/// </summary>
|
||||
public interface IActivity
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name of the activity.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the name of the activity that the user is doing.
|
||||
/// </returns>
|
||||
string Name { get; }
|
||||
/// <summary>
|
||||
/// Gets the type of the activity.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The type of activity.
|
||||
/// </returns>
|
||||
ActivityType Type { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,50 @@ using System.Diagnostics;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// A user's Rich Presence status.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class RichGame : Game
|
||||
{
|
||||
internal RichGame() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets what the player is currently doing.
|
||||
/// </summary>
|
||||
public string Details { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the user's current party status.
|
||||
/// </summary>
|
||||
public string State { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the application ID for the game.
|
||||
/// </summary>
|
||||
public ulong ApplicationId { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the small image for the presence and their hover texts.
|
||||
/// </summary>
|
||||
public GameAsset SmallAsset { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the large image for the presence and their hover texts.
|
||||
/// </summary>
|
||||
public GameAsset LargeAsset { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the information for the current party of the player.
|
||||
/// </summary>
|
||||
public GameParty Party { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the secrets for Rich Presence joining and spectating.
|
||||
/// </summary>
|
||||
public GameSecrets Secrets { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the timestamps for start and/or end of the game.
|
||||
/// </summary>
|
||||
public GameTimestamps Timestamps { get; internal set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the Rich Presence.
|
||||
/// </summary>
|
||||
public override string ToString() => Name;
|
||||
private string DebuggerDisplay => $"{Name} (Rich)";
|
||||
}
|
||||
|
||||
@@ -4,22 +4,85 @@ using System.Diagnostics;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// A user's activity for listening to a song on Spotify.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class SpotifyGame : Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the song's artist(s).
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A collection of string containing all artists featured in the track (e.g. <c>Avicii</c>; <c>Rita Ora</c>).
|
||||
/// </returns>
|
||||
public IReadOnlyCollection<string> Artists { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the Spotify album title of the song.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the name of the album (e.g. <c>AVĪCI (01)</c>).
|
||||
/// </returns>
|
||||
public string AlbumTitle { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the track title of the song.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the name of the song (e.g. <c>Lonely Together (feat. Rita Ora)</c>).
|
||||
/// </returns>
|
||||
public string TrackTitle { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the duration of the song.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="TimeSpan"/> containing the duration of the song.
|
||||
/// </returns>
|
||||
public TimeSpan? Duration { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the track ID of the song.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the Spotify ID of the track (e.g. <c>7DoN0sCGIT9IcLrtBDm4f0</c>).
|
||||
/// </returns>
|
||||
public string TrackId { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the session ID of the song.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The purpose of this property is currently unknown.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// A string containing the session ID.
|
||||
/// </returns>
|
||||
public string SessionId { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URL of the album art.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A URL pointing to the album art of the track (e.g.
|
||||
/// <c>https://i.scdn.co/image/ba2fd8823d42802c2f8738db0b33a4597f2f39e7</c>).
|
||||
/// </returns>
|
||||
public string AlbumArtUrl { get; internal set; }
|
||||
/// <summary>
|
||||
/// Gets the direct Spotify URL of the track.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A URL pointing directly to the track on Spotify. (e.g.
|
||||
/// <c>https://open.spotify.com/track/7DoN0sCGIT9IcLrtBDm4f0</c>).
|
||||
/// </returns>
|
||||
public string TrackUrl { get; internal set; }
|
||||
|
||||
internal SpotifyGame() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full information of the song.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the full information of the song (e.g.
|
||||
/// <c>Avicii, Rita Ora - Lonely Together (feat. Rita Ora) (3:08)</c>
|
||||
/// </returns>
|
||||
public override string ToString() => $"{string.Join(", ", Artists)} - {TrackTitle} ({Duration})";
|
||||
private string DebuggerDisplay => $"{Name} (Spotify)";
|
||||
}
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// A user's activity for streaming on services such as Twitch.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class StreamingGame : Game
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the URL of the stream.
|
||||
/// </summary>
|
||||
public string Url { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="StreamingGame" /> based on the <paramref name="name"/> on the stream URL.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the stream.</param>
|
||||
/// <param name="url">The URL of the stream.</param>
|
||||
public StreamingGame(string name, string url)
|
||||
{
|
||||
Name = name;
|
||||
@@ -14,7 +25,10 @@ namespace Discord
|
||||
Type = ActivityType.Streaming;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the stream.
|
||||
/// </summary>
|
||||
public override string ToString() => Name;
|
||||
private string DebuggerDisplay => $"{Name} ({Url})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// The action type within a <see cref="IAuditLogEntry"/>
|
||||
/// Representing a type of action within an <see cref="IAuditLogEntry"/>.
|
||||
/// </summary>
|
||||
public enum ActionType
|
||||
{
|
||||
/// <summary>
|
||||
/// this guild was updated.
|
||||
/// </summary>
|
||||
GuildUpdated = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A channel was created.
|
||||
/// </summary>
|
||||
ChannelCreated = 10,
|
||||
/// <summary>
|
||||
/// A channel was updated.
|
||||
/// </summary>
|
||||
ChannelUpdated = 11,
|
||||
/// <summary>
|
||||
/// A channel was deleted.
|
||||
/// </summary>
|
||||
ChannelDeleted = 12,
|
||||
|
||||
/// <summary>
|
||||
/// A permission overwrite was created for a channel.
|
||||
/// </summary>
|
||||
OverwriteCreated = 13,
|
||||
/// <summary>
|
||||
/// A permission overwrite was updated for a channel.
|
||||
/// </summary>
|
||||
OverwriteUpdated = 14,
|
||||
/// <summary>
|
||||
/// A permission overwrite was deleted for a channel.
|
||||
/// </summary>
|
||||
OverwriteDeleted = 15,
|
||||
|
||||
/// <summary>
|
||||
/// A user was kicked from this guild.
|
||||
/// </summary>
|
||||
Kick = 20,
|
||||
/// <summary>
|
||||
/// A prune took place in this guild.
|
||||
/// </summary>
|
||||
Prune = 21,
|
||||
/// <summary>
|
||||
/// A user banned another user from this guild.
|
||||
/// </summary>
|
||||
Ban = 22,
|
||||
/// <summary>
|
||||
/// A user unbanned another user from this guild.
|
||||
/// </summary>
|
||||
Unban = 23,
|
||||
|
||||
/// <summary>
|
||||
/// A guild member whose information was updated.
|
||||
/// </summary>
|
||||
MemberUpdated = 24,
|
||||
/// <summary>
|
||||
/// A guild member's role collection was updated.
|
||||
/// </summary>
|
||||
MemberRoleUpdated = 25,
|
||||
|
||||
/// <summary>
|
||||
/// A role was created in this guild.
|
||||
/// </summary>
|
||||
RoleCreated = 30,
|
||||
/// <summary>
|
||||
/// A role was updated in this guild.
|
||||
/// </summary>
|
||||
RoleUpdated = 31,
|
||||
/// <summary>
|
||||
/// A role was deleted from this guild.
|
||||
/// </summary>
|
||||
RoleDeleted = 32,
|
||||
|
||||
/// <summary>
|
||||
/// An invite was created in this guild.
|
||||
/// </summary>
|
||||
InviteCreated = 40,
|
||||
/// <summary>
|
||||
/// An invite was updated in this guild.
|
||||
/// </summary>
|
||||
InviteUpdated = 41,
|
||||
/// <summary>
|
||||
/// An invite was deleted from this guild.
|
||||
/// </summary>
|
||||
InviteDeleted = 42,
|
||||
|
||||
/// <summary>
|
||||
/// A Webhook was created in this guild.
|
||||
/// </summary>
|
||||
WebhookCreated = 50,
|
||||
/// <summary>
|
||||
/// A Webhook was updated in this guild.
|
||||
/// </summary>
|
||||
WebhookUpdated = 51,
|
||||
/// <summary>
|
||||
/// A Webhook was deleted from this guild.
|
||||
/// </summary>
|
||||
WebhookDeleted = 52,
|
||||
|
||||
/// <summary>
|
||||
/// An emoji was created in this guild.
|
||||
/// </summary>
|
||||
EmojiCreated = 60,
|
||||
/// <summary>
|
||||
/// An emoji was updated in this guild.
|
||||
/// </summary>
|
||||
EmojiUpdated = 61,
|
||||
/// <summary>
|
||||
/// An emoji was deleted from this guild.
|
||||
/// </summary>
|
||||
EmojiDeleted = 62,
|
||||
|
||||
/// <summary>
|
||||
/// A message was deleted from this guild.
|
||||
/// </summary>
|
||||
MessageDeleted = 72
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents data applied to an <see cref="IAuditLogEntry"/>
|
||||
/// Represents data applied to an <see cref="IAuditLogEntry"/>.
|
||||
/// </summary>
|
||||
public interface IAuditLogData
|
||||
{ }
|
||||
|
||||
@@ -7,28 +7,40 @@ using System.Threading.Tasks;
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entry in an audit log
|
||||
/// Represents a generic audit log entry.
|
||||
/// </summary>
|
||||
public interface IAuditLogEntry : ISnowflakeEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// The action which occured to create this entry
|
||||
/// Gets the action which occurred to create this entry.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The type of action for this audit log entry.
|
||||
/// </returns>
|
||||
ActionType Action { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The data for this entry. May be <see cref="null"/> if no data was available.
|
||||
/// Gets the data for this entry.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="IAuditLogData" /> for this audit log entry; <c>null</c> if no data is available.
|
||||
/// </returns>
|
||||
IAuditLogData Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The user responsible for causing the changes
|
||||
/// Gets the user responsible for causing the changes.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A user object.
|
||||
/// </returns>
|
||||
IUser User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The reason behind the change. May be <see cref="null"/> if no reason was provided.
|
||||
/// Gets the reason behind the change.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the reason for the change; <c>null</c> if none is provided.
|
||||
/// </returns>
|
||||
string Reason { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the cache mode that should be used.
|
||||
/// </summary>
|
||||
public enum CacheMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows the object to be downloaded if it does not exist in the current cache.
|
||||
/// </summary>
|
||||
AllowDownload,
|
||||
/// <summary>
|
||||
/// Only allows the object to be pulled from the existing cache.
|
||||
/// </summary>
|
||||
CacheOnly
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary> Defines the types of channels. </summary>
|
||||
public enum ChannelType
|
||||
{
|
||||
/// <summary> The channel is a text channel. </summary>
|
||||
Text = 0,
|
||||
/// <summary> The channel is a Direct Message channel. </summary>
|
||||
DM = 1,
|
||||
/// <summary> The channel is a voice channel. </summary>
|
||||
Voice = 2,
|
||||
/// <summary> The channel is a group channel. </summary>
|
||||
Group = 3,
|
||||
/// <summary> The channel is a category channel. </summary>
|
||||
Category = 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the direction of where message(s) should be gotten from.
|
||||
/// </summary>
|
||||
public enum Direction
|
||||
{
|
||||
/// <summary>
|
||||
/// The message(s) should be retrieved before a message.
|
||||
/// </summary>
|
||||
Before,
|
||||
/// <summary>
|
||||
/// The message(s) should be retrieved after a message.
|
||||
/// </summary>
|
||||
After,
|
||||
/// <summary>
|
||||
/// The message(s) should be retrieved around a message.
|
||||
/// </summary>
|
||||
Around
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,28 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Modify an IGuildChannel with the specified changes.
|
||||
/// Properties that are used to modify an <see cref="IGuildChannel" /> with the specified changes.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code language="c#">
|
||||
/// await (Context.Channel as ITextChannel)?.ModifyAsync(x =>
|
||||
/// {
|
||||
/// x.Name = "do-not-enter";
|
||||
/// });
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <seealso cref="IGuildChannel.ModifyAsync"/>
|
||||
public class GuildChannelProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Set the channel to this name
|
||||
/// Gets or sets the channel to this name.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// When modifying an ITextChannel, the Name MUST be alphanumeric with dashes.
|
||||
/// It must match the following RegEx: [a-z0-9-_]{2,100}
|
||||
/// This property defines the new name for this channel.
|
||||
/// <note type="warning">
|
||||
/// When modifying an <see cref="ITextChannel"/>, the <see cref="Name"/> must be alphanumeric with
|
||||
/// dashes. It must match the RegEx <c>[a-z0-9-_]{2,100}</c>.
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <exception cref="Net.HttpException">A BadRequest will be thrown if the name does not match the above RegEx.</exception>
|
||||
public Optional<string> Name { get; set; }
|
||||
/// <summary>
|
||||
/// Move the channel to the following position. This is 0-based!
|
||||
/// Moves the channel to the following position. This property is zero-based.
|
||||
/// </summary>
|
||||
public Optional<int> Position { get; set; }
|
||||
/// <summary>
|
||||
/// Sets the category for this channel
|
||||
/// Gets or sets the category ID for this channel.
|
||||
/// </summary>
|
||||
public Optional<ulong?> CategoryId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
using Discord.Audio;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic audio channel.
|
||||
/// </summary>
|
||||
public interface IAudioChannel : IChannel
|
||||
{
|
||||
/// <summary> Connects to this audio channel. </summary>
|
||||
/// <summary>
|
||||
/// Connects to this audio channel.
|
||||
/// </summary>
|
||||
/// <param name="selfDeaf">Determines whether the client should deaf itself upon connection.</param>
|
||||
/// <param name="selfMute">Determines whether the client should mute itself upon connection.</param>
|
||||
/// <param name="external">Determines whether the audio client is an external one or not.</param>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous connection operation. The task result contains the
|
||||
/// <see cref="IAudioClient"/> responsible for the connection.
|
||||
/// </returns>
|
||||
Task<IAudioClient> ConnectAsync(bool selfDeaf = false, bool selfMute = false, bool external = false);
|
||||
|
||||
/// <summary> Disconnects from this audio channel. </summary>
|
||||
/// <summary>
|
||||
/// Disconnects from this audio channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous operation for disconnecting from the audio channel.
|
||||
/// </returns>
|
||||
Task DisconnectAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic category channel.
|
||||
/// </summary>
|
||||
public interface ICategoryChannel : IGuildChannel
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,17 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic channel.
|
||||
/// </summary>
|
||||
public interface IChannel : ISnowflakeEntity
|
||||
{
|
||||
/// <summary> Gets the name of this channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets the name of this channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string containing the name of this channel.
|
||||
/// </returns>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary> Gets a collection of all users in this channel. </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of all users in this channel.
|
||||
/// </summary>
|
||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A paged collection containing a collection of users that can access this channel. Flattening the
|
||||
/// paginated response into a collection of users with
|
||||
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> is required if you wish to access the users.
|
||||
/// </returns>
|
||||
IAsyncEnumerable<IReadOnlyCollection<IUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
|
||||
|
||||
/// <summary> Gets a user in this channel with the provided id. </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Gets a user in this channel.
|
||||
/// </summary>
|
||||
/// <param name="id">The snowflake identifier of the user (e.g. <c>168693960628371456</c>).</param>
|
||||
/// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.</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 user object that
|
||||
/// represents the found user; <c>null</c> if none is found.
|
||||
/// </returns>
|
||||
Task<IUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic direct-message channel.
|
||||
/// </summary>
|
||||
public interface IDMChannel : IMessageChannel, IPrivateChannel
|
||||
{
|
||||
/// <summary> Gets the recipient of all messages in this channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets the recipient of all messages in this channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A user object that represents the other user in this channel.
|
||||
/// </returns>
|
||||
IUser Recipient { get; }
|
||||
|
||||
/// <summary> Closes this private channel, removing it from your channel list. </summary>
|
||||
/// <summary>
|
||||
/// Closes this private channel, removing it from your channel list.
|
||||
/// </summary>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous close operation.
|
||||
/// </returns>
|
||||
Task CloseAsync(RequestOptions options = null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic private group channel.
|
||||
/// </summary>
|
||||
public interface IGroupChannel : IMessageChannel, IPrivateChannel, IAudioChannel
|
||||
{
|
||||
/// <summary> Leaves this group. </summary>
|
||||
/// <summary>
|
||||
/// Leaves this group.
|
||||
/// </summary>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous leave operation.
|
||||
/// </returns>
|
||||
Task LeaveAsync(RequestOptions options = null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,45 +4,172 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic guild channel.
|
||||
/// </summary>
|
||||
/// <seealso cref="ITextChannel"/>
|
||||
/// <seealso cref="IVoiceChannel"/>
|
||||
/// <seealso cref="ICategoryChannel"/>
|
||||
public interface IGuildChannel : IChannel, IDeletable
|
||||
{
|
||||
/// <summary> Gets the position of this channel in the guild's channel list, relative to others of the same type. </summary>
|
||||
/// <summary>
|
||||
/// Gets the position of this channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="int"/> representing the position of this channel in the guild's channel list relative to
|
||||
/// others of the same type.
|
||||
/// </returns>
|
||||
int Position { get; }
|
||||
|
||||
/// <summary> Gets the guild this channel is a member of. </summary>
|
||||
/// <summary>
|
||||
/// Gets the guild associated with this channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A guild object that this channel belongs to.
|
||||
/// </returns>
|
||||
IGuild Guild { get; }
|
||||
/// <summary> Gets the id of the guild this channel is a member of. </summary>
|
||||
/// <summary>
|
||||
/// Gets the guild ID associated with this channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="ulong"/> representing the guild snowflake identifier for the guild that this channel
|
||||
/// belongs to.
|
||||
/// </returns>
|
||||
ulong GuildId { get; }
|
||||
/// <summary> Gets a collection of permission overwrites for this channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets a collection of permission overwrites for this channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A collection of overwrites associated with this channel.
|
||||
/// </returns>
|
||||
IReadOnlyCollection<Overwrite> PermissionOverwrites { get; }
|
||||
|
||||
/// <summary> Creates a new invite to this channel. </summary>
|
||||
/// <param name="maxAge"> The time (in seconds) until the invite expires. Set to null to never expire. </param>
|
||||
/// <param name="maxUses"> The max amount of times this invite may be used. Set to null to have unlimited uses. </param>
|
||||
/// <param name="isTemporary"> If true, a user accepting this invite will be kicked from the guild after closing their client. </param>
|
||||
/// <summary>
|
||||
/// Creates a new invite to this channel.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example creates a new invite to this channel; the invite lasts for 12 hours and can only
|
||||
/// be used 3 times throughout its lifespan.
|
||||
/// <code language="cs">
|
||||
/// await guildChannel.CreateInviteAsync(maxAge: 43200, maxUses: 3);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="maxAge">The time (in seconds) until the invite expires. Set to <c>null</c> to never expire.</param>
|
||||
/// <param name="maxUses">The max amount of times this invite may be used. Set to <c>null</c> to have unlimited uses.</param>
|
||||
/// <param name="isTemporary">If <c>true</c>, the user accepting this invite will be kicked from the guild after closing their client.</param>
|
||||
/// <param name="isUnique">If <c>true</c>, don't try to reuse a similar invite (useful for creating many unique one time use invites).</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous invite creation operation. The task result contains an invite
|
||||
/// metadata object containing information for the created invite.
|
||||
/// </returns>
|
||||
Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null);
|
||||
/// <summary> Returns a collection of all invites to this channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets a collection of all invites to this channel.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example gets all of the invites that have been created in this channel and selects the
|
||||
/// most used invite.
|
||||
/// <code language="cs">
|
||||
/// var invites = await channel.GetInvitesAsync();
|
||||
/// if (invites.Count == 0) return;
|
||||
/// var invite = invites.OrderByDescending(x => x.Uses).FirstOrDefault();
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <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 invite metadata that are created for this channel.
|
||||
/// </returns>
|
||||
Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null);
|
||||
|
||||
/// <summary> Modifies this guild channel. </summary>
|
||||
/// <summary>
|
||||
/// Modifies this guild channel.
|
||||
/// </summary>
|
||||
/// <param name="func">The delegate containing the properties to modify the channel with.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous modification operation.
|
||||
/// </returns>
|
||||
Task ModifyAsync(Action<GuildChannelProperties> func, RequestOptions options = null);
|
||||
|
||||
/// <summary> Gets the permission overwrite for a specific role, or null if one does not exist. </summary>
|
||||
/// <summary>
|
||||
/// Gets the permission overwrite for a specific role.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to get the overwrite from.</param>
|
||||
/// <returns>
|
||||
/// An overwrite object for the targeted role; <c>null</c> if none is set.
|
||||
/// </returns>
|
||||
OverwritePermissions? GetPermissionOverwrite(IRole role);
|
||||
/// <summary> Gets the permission overwrite for a specific user, or null if one does not exist. </summary>
|
||||
/// <summary>
|
||||
/// Gets the permission overwrite for a specific user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to get the overwrite from.</param>
|
||||
/// <returns>
|
||||
/// An overwrite object for the targeted user; <c>null</c> if none is set.
|
||||
/// </returns>
|
||||
OverwritePermissions? GetPermissionOverwrite(IUser user);
|
||||
/// <summary> Removes the permission overwrite for the given role, if one exists. </summary>
|
||||
/// <summary>
|
||||
/// Removes the permission overwrite for the given role, if one exists.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to remove the overwrite from.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous operation for removing the specified permissions from the channel.
|
||||
/// </returns>
|
||||
Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null);
|
||||
/// <summary> Removes the permission overwrite for the given user, if one exists. </summary>
|
||||
/// <summary>
|
||||
/// Removes the permission overwrite for the given user, if one exists.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to remove the overwrite from.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous operation for removing the specified permissions from the channel.
|
||||
/// </returns>
|
||||
Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null);
|
||||
/// <summary> Adds or updates the permission overwrite for the given role. </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates the permission overwrite for the given role.
|
||||
/// </summary>
|
||||
/// <param name="role">The role to add the overwrite to.</param>
|
||||
/// <param name="permissions">The overwrite to add to the role.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous permission operation for adding the specified permissions to the channel.
|
||||
/// </returns>
|
||||
Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null);
|
||||
/// <summary> Adds or updates the permission overwrite for the given user. </summary>
|
||||
/// <summary>
|
||||
/// Adds or updates the permission overwrite for the given user.
|
||||
/// </summary>
|
||||
/// <param name="user">The user to add the overwrite to.</param>
|
||||
/// <param name="permissions">The overwrite to add to the user.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous permission operation for adding the specified permissions to the channel.
|
||||
/// </returns>
|
||||
Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null);
|
||||
|
||||
/// <summary> Gets a collection of all users in this channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets a collection of users that are able to view the channel.
|
||||
/// </summary>
|
||||
/// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A paged collection containing a collection of guild users that can access this channel. Flattening the
|
||||
/// paginated response into a collection of users with
|
||||
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> is required if you wish to access the users.
|
||||
/// </returns>
|
||||
new IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
|
||||
/// <summary> Gets a user in this channel with the provided id.</summary>
|
||||
/// <summary>
|
||||
/// Gets a user in this channel.
|
||||
/// </summary>
|
||||
/// <param name="id">The snowflake identifier of the user.</param>
|
||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task representing the asynchronous get operation. The task result contains a guild user object that
|
||||
/// represents the user; <c>null</c> if none is found.
|
||||
/// </returns>
|
||||
new Task<IGuildUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,38 +5,276 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic channel that can send and receive messages.
|
||||
/// </summary>
|
||||
public interface IMessageChannel : IChannel
|
||||
{
|
||||
/// <summary> Sends a message to this message channel. </summary>
|
||||
/// <summary>
|
||||
/// Sends a message to this message channel.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example sends a message with the current system time in RFC 1123 format to the channel and
|
||||
/// deletes itself after 5 seconds.
|
||||
/// <code language="cs">
|
||||
/// var message = await channel.SendMessageAsync(DateTimeOffset.UtcNow.ToString("R"));
|
||||
/// await Task.Delay(TimeSpan.FromSeconds(5))
|
||||
/// .ContinueWith(x => message.DeleteAsync());
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="text">The message to be sent.</param>
|
||||
/// <param name="isTTS">Determines whether the message should be read aloud by Discord or not.</param>
|
||||
/// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
Task<IUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
|
||||
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
|
||||
/// <summary>
|
||||
/// Sends a file to this message channel with an optional caption.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example uploads a local file called <c>wumpus.txt</c> along with the text
|
||||
/// <c>good discord boi</c> to the channel.
|
||||
/// <code language="cs">
|
||||
/// await channel.SendFileAsync("wumpus.txt", "good discord boi");
|
||||
/// </code>
|
||||
///
|
||||
/// The following example uploads a local image called <c>b1nzy.jpg</c> embedded inside a rich embed to the
|
||||
/// channel.
|
||||
/// <code language="cs">
|
||||
/// await channel.SendFileAsync("b1nzy.jpg",
|
||||
/// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// This method sends a file as if you are uploading an attachment directly from your Discord client.
|
||||
/// <note>
|
||||
/// If you wish to upload an image and have it embedded in a <see cref="Discord.EmbedType.Rich"/> embed,
|
||||
/// you may upload the file and refer to the file with "attachment://filename.ext" in the
|
||||
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>. See the example section for its usage.
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <param name="filePath">The file path of the file.</param>
|
||||
/// <param name="text">The message to be sent.</param>
|
||||
/// <param name="isTTS">Whether the message should be read aloud by Discord or not.</param>
|
||||
/// <param name="embed">The <see cref="Discord.EmbedType.Rich" /> <see cref="Embed" /> to be sent.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
/// <summary> Sends a file to this text channel, with an optional caption. </summary>
|
||||
/// <summary>
|
||||
/// Sends a file to this message channel with an optional caption.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example uploads a streamed image that will be called <c>b1nzy.jpg</c> embedded inside a
|
||||
/// rich embed to the channel.
|
||||
/// <code language="cs">
|
||||
/// await channel.SendFileAsync(b1nzyStream, "b1nzy.jpg",
|
||||
/// embed: new EmbedBuilder {ImageUrl = "attachment://b1nzy.jpg"}.Build());
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// This method sends a file as if you are uploading an attachment directly from your Discord client.
|
||||
/// <note>
|
||||
/// If you wish to upload an image and have it embedded in a <see cref="Discord.EmbedType.Rich"/> embed,
|
||||
/// you may upload the file and refer to the file with "attachment://filename.ext" in the
|
||||
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>. See the example section for its usage.
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <param name="stream">The <see cref="Stream" /> of the file to be sent.</param>
|
||||
/// <param name="filename">The name of the attachment.</param>
|
||||
/// <param name="text">The message to be sent.</param>
|
||||
/// <param name="isTTS">Whether the message should be read aloud by Discord or not.</param>
|
||||
/// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous send operation for delivering the message. The task result
|
||||
/// contains the sent message.
|
||||
/// </returns>
|
||||
Task<IUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
|
||||
|
||||
/// <summary> Gets a message from this message channel with the given id, or null if not found. </summary>
|
||||
/// <summary>
|
||||
/// Gets a message from this message channel.
|
||||
/// </summary>
|
||||
/// <param name="id">The snowflake identifier of the message.</param>
|
||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents an asynchronous get operation for retrieving the message. The task result contains
|
||||
/// the retrieved message; <c>null</c> if no message is found with the specified identifier.
|
||||
/// </returns>
|
||||
Task<IMessage> GetMessageAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
|
||||
/// <summary> Gets the last N messages from this message channel. </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last N messages from this message channel.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <note type="important">
|
||||
/// The returned collection is an asynchronous enumerable object; one must call
|
||||
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
|
||||
/// collection.
|
||||
/// </note>
|
||||
/// <note type="warning">
|
||||
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
|
||||
/// rate limit, causing your bot to freeze!
|
||||
/// </note>
|
||||
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/>. The
|
||||
/// library will attempt to split up the requests according to your <paramref name="limit"/> and
|
||||
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
|
||||
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
|
||||
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
|
||||
/// of flattening.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example downloads 300 messages and gets messages that belong to the user
|
||||
/// <c>53905483156684800</c>.
|
||||
/// <code lang="cs">
|
||||
/// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync();
|
||||
/// var userMessages = messages.Where(x => x.Author.Id == 53905483156684800);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="limit">The numbers of message to be gotten from.</param>
|
||||
/// <param name="mode">The <see cref="CacheMode" /> that determines whether the object should be fetched from
|
||||
/// cache.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// Paged collection of messages.
|
||||
/// </returns>
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch,
|
||||
CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
|
||||
/// <summary> Gets a collection of messages in this channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets a collection of messages in this channel.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <note type="important">
|
||||
/// The returned collection is an asynchronous enumerable object; one must call
|
||||
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
|
||||
/// collection.
|
||||
/// </note>
|
||||
/// <note type="warning">
|
||||
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
|
||||
/// rate limit, causing your bot to freeze!
|
||||
/// </note>
|
||||
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/> around
|
||||
/// the message <paramref name="fromMessageId"/> depending on the <paramref name="dir"/>. The library will
|
||||
/// attempt to split up the requests according to your <paramref name="limit"/> and
|
||||
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
|
||||
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
|
||||
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
|
||||
/// of flattening.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.
|
||||
/// <code lang="cs">
|
||||
/// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync();
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="fromMessageId">The ID of the starting message to get the messages from.</param>
|
||||
/// <param name="dir">The direction of the messages to be gotten from.</param>
|
||||
/// <param name="limit">The numbers of message to be gotten from.</param>
|
||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from
|
||||
/// cache.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// Paged collection of messages.
|
||||
/// </returns>
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch,
|
||||
CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
|
||||
/// <summary> Gets a collection of messages in this channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets a collection of messages in this channel.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <note type="important">
|
||||
/// The returned collection is an asynchronous enumerable object; one must call
|
||||
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
|
||||
/// collection.
|
||||
/// </note>
|
||||
/// <note type="warning">
|
||||
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
|
||||
/// rate limit, causing your bot to freeze!
|
||||
/// </note>
|
||||
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/> around
|
||||
/// the message <paramref name="fromMessage"/> depending on the <paramref name="dir"/>. The library will
|
||||
/// attempt to split up the requests according to your <paramref name="limit"/> and
|
||||
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
|
||||
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
|
||||
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
|
||||
/// of flattening.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// The following example gets 5 message prior to a specific message, <c>oldMessage</c>.
|
||||
/// <code lang="cs">
|
||||
/// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync();
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="fromMessage">The starting message to get the messages from.</param>
|
||||
/// <param name="dir">The direction of the messages to be gotten from.</param>
|
||||
/// <param name="limit">The numbers of message to be gotten from.</param>
|
||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from
|
||||
/// cache.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// Paged collection of messages.
|
||||
/// </returns>
|
||||
IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch,
|
||||
CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
|
||||
/// <summary> Gets a collection of pinned messages in this channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets a collection of pinned messages in this channel.
|
||||
/// </summary>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous get operation for retrieving pinned messages in this channel.
|
||||
/// The task result contains a collection of messages found in the pinned messages.
|
||||
/// </returns>
|
||||
Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync(RequestOptions options = null);
|
||||
|
||||
/// <summary> Deletes a message based on the message ID in this channel. </summary>
|
||||
/// <summary>
|
||||
/// Deletes a message.
|
||||
/// </summary>
|
||||
/// <param name="messageId">The snowflake identifier of the message that would be removed.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous removal operation.
|
||||
/// </returns>
|
||||
Task DeleteMessageAsync(ulong messageId, RequestOptions options = null);
|
||||
/// <summary> Deletes a message based on the provided message in this channel. </summary>
|
||||
/// <param name="message">The message that would be removed.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous removal operation.
|
||||
/// </returns>
|
||||
Task DeleteMessageAsync(IMessage message, RequestOptions options = null);
|
||||
|
||||
/// <summary> Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. </summary>
|
||||
/// <summary>
|
||||
/// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds.
|
||||
/// </summary>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous operation that triggers the broadcast.
|
||||
/// </returns>
|
||||
Task TriggerTypingAsync(RequestOptions options = null);
|
||||
/// <summary> Continuously broadcasts the "user is typing" message to all users in this channel until the returned object is disposed. </summary>
|
||||
/// <summary>
|
||||
/// Continuously broadcasts the "user is typing" message to all users in this channel until the returned
|
||||
/// object is disposed.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example keeps the client in the typing state until <c>LongRunningAsync</c> has finished.
|
||||
/// <code lang="cs">
|
||||
/// using (messageChannel.EnterTypingState())
|
||||
/// {
|
||||
/// await LongRunningAsync();
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A disposable object that, upon its disposal, will stop the client from broadcasting its typing state in
|
||||
/// this channel.
|
||||
/// </returns>
|
||||
IDisposable EnterTypingState(RequestOptions options = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,27 @@ using System.Threading.Tasks;
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of guild channel that can be nested within a category.
|
||||
/// Contains a CategoryId that is set to the parent category, if it is set.
|
||||
/// Represents a type of guild channel that can be nested within a category.
|
||||
/// </summary>
|
||||
public interface INestedChannel : IGuildChannel
|
||||
{
|
||||
/// <summary> Gets the parentid (category) of this channel in the guild's channel list. </summary>
|
||||
/// <summary>
|
||||
/// Gets the parent (category) ID of this channel in the guild's channel list.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="ulong"/> representing the snowflake identifier of the parent of this channel;
|
||||
/// <c>null</c> if none is set.
|
||||
/// </returns>
|
||||
ulong? CategoryId { get; }
|
||||
/// <summary> Gets the parent channel (category) of this channel, if it is set. If unset, returns null.</summary>
|
||||
/// <summary>
|
||||
/// Gets the parent (category) channel of this channel.
|
||||
/// </summary>
|
||||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</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 the category channel
|
||||
/// representing the parent of this channel; <c>null</c> if none is set.
|
||||
/// </returns>
|
||||
Task<ICategoryChannel> GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic channel that is private to select recipients.
|
||||
/// </summary>
|
||||
public interface IPrivateChannel : IChannel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the users that can access this channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A read-only collection of users that can access this channel.
|
||||
/// </returns>
|
||||
IReadOnlyCollection<IUser> Recipients { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,30 +5,114 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic channel in a guild that can send and receive messages.
|
||||
/// </summary>
|
||||
public interface ITextChannel : IMessageChannel, IMentionable, INestedChannel
|
||||
{
|
||||
/// <summary> Checks if the channel is NSFW. </summary>
|
||||
/// <summary>
|
||||
/// Gets a value that indicates whether the channel is NSFW.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the channel has the NSFW flag enabled; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
bool IsNsfw { get; }
|
||||
|
||||
/// <summary> Gets the current topic for this text channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets the current topic for this text channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representing the topic set in the channel; <c>null</c> if none is set.
|
||||
/// </returns>
|
||||
string Topic { get; }
|
||||
|
||||
///<summary> Gets the current slow-mode delay for this channel. 0 if disabled. </summary>
|
||||
/// <summary>
|
||||
/// Gets the current slow-mode delay for this channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="Int32"/> representing the time in seconds required before the user can send another
|
||||
/// message; <c>0</c> if disabled.
|
||||
/// </returns>
|
||||
int SlowModeInterval { get; }
|
||||
|
||||
/// <summary> Bulk deletes multiple messages. </summary>
|
||||
/// <summary>
|
||||
/// Bulk-deletes multiple messages.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example gets 250 messages from the channel and deletes them.
|
||||
/// <code lang="cs">
|
||||
/// var messages = await textChannel.GetMessagesAsync(250).FlattenAsync();
|
||||
/// await textChannel.DeleteMessagesAsync(messages);
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <remarks>
|
||||
/// This method attempts to remove the messages specified in bulk.
|
||||
/// <note type="important">
|
||||
/// Due to the limitation set by Discord, this method can only remove messages that are posted within 14 days!
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <param name="messages">The messages to be bulk-deleted.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous bulk-removal operation.
|
||||
/// </returns>
|
||||
Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null);
|
||||
/// <summary> Bulk deletes multiple messages. </summary>
|
||||
/// <summary>
|
||||
/// Bulk-deletes multiple messages.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method attempts to remove the messages specified in bulk.
|
||||
/// <note type="important">
|
||||
/// Due to the limitation set by Discord, this method can only remove messages that are posted within 14 days!
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <param name="messageIds">The snowflake identifier of the messages to be bulk-deleted.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous bulk-removal operation.
|
||||
/// </returns>
|
||||
Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null);
|
||||
|
||||
/// <summary> Modifies this text channel. </summary>
|
||||
/// <summary>
|
||||
/// Modifies this text channel.
|
||||
/// </summary>
|
||||
/// <param name="func">The delegate containing the properties to modify the channel with.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous modification operation.
|
||||
/// </returns>
|
||||
/// <seealso cref="TextChannelProperties"/>
|
||||
Task ModifyAsync(Action<TextChannelProperties> func, RequestOptions options = null);
|
||||
|
||||
/// <summary> Creates a webhook in this text channel. </summary>
|
||||
|
||||
/// <summary>
|
||||
/// Creates a webhook in this text channel.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the webhook.</param>
|
||||
/// <param name="avatar">The avatar of the webhook.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous creation operation. The task result contains the newly created
|
||||
/// webhook.
|
||||
/// </returns>
|
||||
Task<IWebhook> CreateWebhookAsync(string name, Stream avatar = null, RequestOptions options = null);
|
||||
/// <summary> Gets the webhook in this text channel with the provided id, or null if not found. </summary>
|
||||
/// <summary>
|
||||
/// Gets a webhook available in this text channel.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier of the webhook.</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 webhook associated
|
||||
/// with the identifier; <c>null</c> if the webhook is not found.
|
||||
/// </returns>
|
||||
Task<IWebhook> GetWebhookAsync(ulong id, RequestOptions options = null);
|
||||
/// <summary> Gets the webhooks for this text channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets the webhooks available in this text channel.
|
||||
/// </summary>
|
||||
/// <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 webhooks that is available in this channel.
|
||||
/// </returns>
|
||||
Task<IReadOnlyCollection<IWebhook>> GetWebhooksAsync(RequestOptions options = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,37 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic voice channel in a guild.
|
||||
/// </summary>
|
||||
public interface IVoiceChannel : INestedChannel, IAudioChannel
|
||||
{
|
||||
/// <summary> Gets the bitrate, in bits per second, clients in this voice channel are requested to use. </summary>
|
||||
/// <summary>
|
||||
/// Gets the bit-rate that the clients in this voice channel are requested to use.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="int"/> representing the bit-rate (bps) that this voice channel defines and requests the
|
||||
/// client(s) to use.
|
||||
/// </returns>
|
||||
int Bitrate { get; }
|
||||
/// <summary> Gets the max amount of users allowed to be connected to this channel at one time. </summary>
|
||||
/// <summary>
|
||||
/// Gets the max number of users allowed to be connected to this channel at once.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="int"/> representing the maximum number of users that are allowed to be connected to this
|
||||
/// channel at once; <c>null</c> if a limit is not set.
|
||||
/// </returns>
|
||||
int? UserLimit { get; }
|
||||
|
||||
/// <summary> Modifies this voice channel. </summary>
|
||||
/// <summary>
|
||||
/// Modifies this voice channel.
|
||||
/// </summary>
|
||||
/// <param name="func">The properties to modify the channel with.</param>
|
||||
/// <param name="options">The options to be used when sending the request.</param>
|
||||
/// <returns>
|
||||
/// A task that represents the asynchronous modification operation.
|
||||
/// </returns>
|
||||
/// <seealso cref="VoiceChannelProperties"/>
|
||||
Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOptions options = null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties that are used to reorder an <see cref="IGuildChannel"/>.
|
||||
/// </summary>
|
||||
public class ReorderChannelProperties
|
||||
{
|
||||
/// <summary>The id of the channel to apply this position to.</summary>
|
||||
/// <summary>
|
||||
/// Gets the ID of the channel to apply this position to.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="ulong"/> representing the snowflake identifier of this channel.
|
||||
/// </returns>
|
||||
public ulong Id { get; }
|
||||
/// <summary>The new zero-based position of this channel. </summary>
|
||||
/// <summary>
|
||||
/// Gets the new zero-based position of this channel.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// An <see cref="int"/> representing the new position of this channel.
|
||||
/// </returns>
|
||||
public int Position { get; }
|
||||
|
||||
/// <summary> Initializes a new instance of the <see cref="ReorderChannelProperties"/> class used to reorder a channel. </summary>
|
||||
/// <param name="id"> Sets the ID of the channel to apply this position to. </param>
|
||||
/// <param name="position"> Sets the new zero-based position of this channel. </param>
|
||||
public ReorderChannelProperties(ulong id, int position)
|
||||
{
|
||||
Id = id;
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Provides properties that are used to modify an <see cref="ITextChannel"/> with the specified changes.
|
||||
/// </summary>
|
||||
/// <seealso cref="ITextChannel.ModifyAsync(System.Action{TextChannelProperties}, RequestOptions)"/>
|
||||
public class TextChannelProperties : GuildChannelProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// What the topic of the channel should be set to.
|
||||
/// Gets or sets the topic of the channel.
|
||||
/// </summary>
|
||||
public Optional<string> Topic { get; set; }
|
||||
/// <summary>
|
||||
/// Should this channel be flagged as NSFW?
|
||||
/// Gets or sets whether this channel should be flagged as NSFW.
|
||||
/// </summary>
|
||||
public Optional<bool> IsNsfw { get; set; }
|
||||
/// <summary>
|
||||
/// What the slow-mode ratelimit for this channel should be set to; 0 will disable slow-mode.
|
||||
/// Gets or sets the slow-mode ratelimit in seconds for this channel.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This value must fall within [0, 120]
|
||||
///
|
||||
/// Users with <see cref="ChannelPermission.ManageMessages"/> will be exempt from slow-mode.
|
||||
/// Setting this value to <c>0</c> will disable slow-mode for this channel.
|
||||
/// <note>
|
||||
/// Users with <see cref="Discord.ChannelPermission.ManageMessages" /> will be exempt from slow-mode.
|
||||
/// </note>
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Throws ArgummentOutOfRange if the value does not fall within [0, 120]</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown if the value does not fall within [0, 120].</exception>
|
||||
public Optional<int> SlowModeInterval { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Provides properties that are used to modify an <see cref="IVoiceChannel" /> with the specified changes.
|
||||
/// </summary>
|
||||
public class VoiceChannelProperties : GuildChannelProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// The bitrate of the voice connections in this channel. Must be greater than 8000
|
||||
/// Gets or sets the bitrate of the voice connections in this channel. Must be greater than 8000.
|
||||
/// </summary>
|
||||
public Optional<int> Bitrate { get; set; }
|
||||
/// <summary>
|
||||
/// The maximum number of users that can be present in a channel.
|
||||
/// Gets or sets the maximum number of users that can be present in a channel, or <c>null</c> if none.
|
||||
/// </summary>
|
||||
public Optional<int?> UserLimit { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,28 +1,35 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// A unicode emoji
|
||||
/// A Unicode emoji.
|
||||
/// </summary>
|
||||
public class Emoji : IEmote
|
||||
{
|
||||
// TODO: need to constrain this to unicode-only emojis somehow
|
||||
// TODO: need to constrain this to Unicode-only emojis somehow
|
||||
|
||||
/// <summary>
|
||||
/// The unicode representation of this emote.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Unicode representation of this emote.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string that resolves to <see cref="Emoji.Name"/>.
|
||||
/// </returns>
|
||||
public override string ToString() => Name;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a unicode emoji.
|
||||
/// Initializes a new <see cref="Emoji"/> class with the provided Unicode.
|
||||
/// </summary>
|
||||
/// <param name="unicode">The pure UTF-8 encoding of an emoji</param>
|
||||
/// <param name="unicode">The pure UTF-8 encoding of an emoji.</param>
|
||||
public Emoji(string unicode)
|
||||
{
|
||||
Name = unicode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified emoji is equal to the current one.
|
||||
/// </summary>
|
||||
/// <param name="other">The object to compare with the current object.</param>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (other == null) return false;
|
||||
@@ -34,6 +41,7 @@
|
||||
return string.Equals(Name, otherEmoji.Name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode() => Name.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,34 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom image-based emote
|
||||
/// A custom image-based emote.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class Emote : IEmote, ISnowflakeEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// The display name (tooltip) of this emote
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public string Name { get; }
|
||||
/// <summary>
|
||||
/// The ID of this emote
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public ulong Id { get; }
|
||||
/// <summary>
|
||||
/// Is this emote animated?
|
||||
/// Gets whether this emote is animated.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A boolean that determines whether or not this emote is an animated one.
|
||||
/// </returns>
|
||||
public bool Animated { get; }
|
||||
/// <inheritdoc />
|
||||
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
|
||||
/// <summary>
|
||||
/// Gets the image URL of this emote.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string that points to the URL of this emote.
|
||||
/// </returns>
|
||||
public string Url => CDN.GetEmojiUrl(Id, Animated);
|
||||
|
||||
internal Emote(ulong id, string name, bool animated)
|
||||
@@ -30,6 +38,10 @@ namespace Discord
|
||||
Animated = animated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified emote is equal to the current emote.
|
||||
/// </summary>
|
||||
/// <param name="other">The object to compare with the current object.</param>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (other == null) return false;
|
||||
@@ -41,6 +53,7 @@ namespace Discord
|
||||
return string.Equals(Name, otherEmote.Name) && Id == otherEmote.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
@@ -49,18 +62,20 @@ namespace Discord
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an Emote from its raw format
|
||||
/// </summary>
|
||||
/// <param name="text">The raw encoding of an emote; for example, <:dab:277855270321782784></param>
|
||||
/// <returns>An emote</returns>
|
||||
/// <summary> Parses an <see cref="Emote"/> from its raw format. </summary>
|
||||
/// <param name="text">The raw encoding of an emote (e.g. <c><:dab:277855270321782784></c>).</param>
|
||||
/// <returns>An emote.</returns>
|
||||
/// <exception cref="ArgumentException">Invalid emote format.</exception>
|
||||
public static Emote Parse(string text)
|
||||
{
|
||||
if (TryParse(text, out Emote result))
|
||||
return result;
|
||||
throw new ArgumentException(message: "Invalid emote format", paramName: nameof(text));
|
||||
throw new ArgumentException(message: "Invalid emote format.", paramName: nameof(text));
|
||||
}
|
||||
|
||||
/// <summary> Tries to parse an <see cref="Emote"/> from its raw format. </summary>
|
||||
/// <param name="text">The raw encoding of an emote; for example, <:dab:277855270321782784>.</param>
|
||||
/// <param name="result">An emote.</param>
|
||||
public static bool TryParse(string text, out Emote result)
|
||||
{
|
||||
result = null;
|
||||
@@ -85,6 +100,12 @@ namespace Discord
|
||||
}
|
||||
|
||||
private string DebuggerDisplay => $"{Name} ({Id})";
|
||||
/// <summary>
|
||||
/// Returns the raw representation of the emote.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representing the raw presentation of the emote (e.g. <c><:thonkang:282745590985523200></c>).
|
||||
/// </returns>
|
||||
public override string ToString() => $"<{(Animated ? "a" : "")}:{Name}:{Id}>";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties that are used to modify an <see cref="Emote" /> with the specified changes.
|
||||
/// </summary>
|
||||
/// <seealso cref="IGuild.ModifyEmoteAsync"/>
|
||||
public class EmoteProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the <see cref="Emote"/>.
|
||||
/// </summary>
|
||||
public Optional<string> Name { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the roles that can access this <see cref="Emote"/>.
|
||||
/// </summary>
|
||||
public Optional<IEnumerable<IRole>> Roles { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// An image-based emote that is attached to a guild
|
||||
/// An image-based emote that is attached to a guild.
|
||||
/// </summary>
|
||||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
|
||||
public class GuildEmote : Emote
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets whether this emoji is managed by an integration.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A boolean that determines whether or not this emote is managed by a Twitch integration.
|
||||
/// </returns>
|
||||
public bool IsManaged { get; }
|
||||
/// <summary>
|
||||
/// Gets whether this emoji must be wrapped in colons.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A boolean that determines whether or not this emote requires the use of colons in chat to be used.
|
||||
/// </returns>
|
||||
public bool RequireColons { get; }
|
||||
/// <summary>
|
||||
/// Gets the roles that are allowed to use this emoji.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A read-only list containing snowflake identifiers for roles that are allowed to use this emoji.
|
||||
/// </returns>
|
||||
public IReadOnlyList<ulong> RoleIds { get; }
|
||||
|
||||
internal GuildEmote(ulong id, string name, bool animated, bool isManaged, bool requireColons, IReadOnlyList<ulong> roleIds) : base(id, name, animated)
|
||||
@@ -21,6 +39,12 @@ namespace Discord
|
||||
}
|
||||
|
||||
private string DebuggerDisplay => $"{Name} ({Id})";
|
||||
/// <summary>
|
||||
/// Gets the raw representation of the emote.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representing the raw presentation of the emote (e.g. <c><:thonkang:282745590985523200></c>).
|
||||
/// </returns>
|
||||
public override string ToString() => $"<{(Animated ? "a" : "")}:{Name}:{Id}>";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// A general container for any type of emote in a message.
|
||||
/// Represents a general container for any type of emote in a message.
|
||||
/// </summary>
|
||||
public interface IEmote
|
||||
{
|
||||
/// <summary>
|
||||
/// The display name or unicode representation of this emote
|
||||
/// Gets the display name or Unicode representation of this emote.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string representing the display name or the Unicode representation (e.g. <c>🤔</c>) of this emote.
|
||||
/// </returns>
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the default message notification behavior the guild uses.
|
||||
/// </summary>
|
||||
public enum DefaultMessageNotifications
|
||||
{
|
||||
/// <summary> By default, all messages will trigger notifications. </summary>
|
||||
/// <summary>
|
||||
/// By default, all messages will trigger notifications.
|
||||
/// </summary>
|
||||
AllMessages = 0,
|
||||
/// <summary> By default, only mentions will trigger notifications. </summary>
|
||||
/// <summary>
|
||||
/// By default, only mentions will trigger notifications.
|
||||
/// </summary>
|
||||
MentionsOnly = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Modify the widget of an IGuild with the specified parameters
|
||||
/// Provides properties that are used to modify the widget of an <see cref="IGuild" /> with the specified changes.
|
||||
/// </summary>
|
||||
public class GuildEmbedProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Should the widget be enabled?
|
||||
/// Sets whether the widget should be enabled.
|
||||
/// </summary>
|
||||
public Optional<bool> Enabled { get; set; }
|
||||
/// <summary>
|
||||
/// What channel should the invite place users in, if not null.
|
||||
/// Sets the channel that the invite should place its users in, if not <c>null</c>.
|
||||
/// </summary>
|
||||
public Optional<IChannel> Channel { get; set; }
|
||||
/// <summary>
|
||||
/// What channel should the invite place users in, if not null.
|
||||
/// Sets the channel the invite should place its users in, if not <c>null</c>.
|
||||
/// </summary>
|
||||
public Optional<ulong?> ChannelId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
namespace Discord
|
||||
namespace Discord
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides properties used to modify an <see cref="IGuildIntegration" /> with the specified changes.
|
||||
/// </summary>
|
||||
public class GuildIntegrationProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the behavior when an integration subscription lapses.
|
||||
/// </summary>
|
||||
public Optional<int> ExpireBehavior { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the period (in seconds) where the integration will ignore lapsed subscriptions.
|
||||
/// </summary>
|
||||
public Optional<int> ExpireGracePeriod { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets whether emoticons should be synced for this integration.
|
||||
/// </summary>
|
||||
public Optional<bool> EnableEmoticons { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user