commitpull/988/headdece19d895
Merge:fdaa689a
e764dafe
Author: Still Hsu <341464@gmail.com> Date: Mon May 21 11:47:47 2018 +0800 Merge branch 'dev' into docs/faq-n-patches-offline commite764dafe08
Author: Quahu <quahuu@gmail.com> Date: Sun May 13 15:34:40 2018 +0200 Add ViewChannel to Voice channel permissions (#1059) Previously, Voice channels did not have ViewChannel in their "all" permissions commit32fc2df21b
Author: Alex Gravely <tcbskater@hotmail.com> Date: Sat May 12 20:47:44 2018 -0400 Remove unused field in EmbedFieldBuilder. (#1018) commit39dffe8585
Author: Finite Reality <FiniteReality@users.noreply.github.com> Date: Sun May 13 01:46:07 2018 +0100 Audit Logs implementation (#1055) * Copy audit logs impl from old branch and clean up I suck at using git, so I'm gonna use brute force. * Remove unnecessary TODOs Category channels do not provide any new information, and the other I forgot to remove beforehand * Add invite update data, clean up after feedback * Remove TODOs, add WebhookType enum for future use WebhookType is a future-use type, as currently audit logs are the only thing which may return it. commitfdaa689ae8
Author: Still Hsu <341464@gmail.com> Date: Wed May 9 06:04:59 2018 +0800 Add explanation for RunMode commitea82c2537e
Author: Still Hsu <341464@gmail.com> Date: Tue May 8 16:30:48 2018 +0800 Initial proofread of the articles commit124f1a267c
Merge:25557218
97c89310
Author: Still Hsu <341464@gmail.com> Date: Tue May 8 05:02:01 2018 +0800 Merge branch 'dev' into docs/faq-n-patches-offline commit97c893107b
Author: Still Hsu <341464@gmail.com> Date: Mon May 7 06:22:49 2018 +0800 Implement GetBanAsync (#1056) commit25557218db
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 16:11:19 2018 +0800 Add details to SpotifyGame commitc7b236ddf5
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 15:58:23 2018 +0800 Add more IGuild docs commit1bb06cc37b
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 15:40:31 2018 +0800 Replace all langword placements with code block commitac47d84ea7
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 15:22:17 2018 +0800 Replace langword null to code block null instead - Because DocFX sucks at rendering langword commit0b15bbc54d
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 15:20:34 2018 +0800 Add XML docs commit65d4e4360e
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 06:57:53 2018 +0800 Add BaseSocketClient docs commit8f64c04599
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 06:37:55 2018 +0800 Replace note block commitd8bb9e7aaa
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 06:31:50 2018 +0800 Add warning for bulk-delete endpoint commitadae5ffc9e
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 06:07:28 2018 +0800 Fix missing Username prop commit3e591972ca
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 06:01:34 2018 +0800 Add properties examples to overwrite commit0ad66f6765
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 04:55:15 2018 +0800 Fix minor consistencies & redundant impl commit124efdf7e6
Author: Still Hsu <341464@gmail.com> Date: Sun May 6 04:40:14 2018 +0800 XML Docs commit3aa5d363de
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 18:22:46 2018 +0800 Add 'last modified' plugin Source: https://github.com/Still34/DocFx.Plugin.LastModified Licensed under MIT License commit2014870dc0
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 15:57:40 2018 +0800 Fix letter-casing for files commitf27d659ebe
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 15:50:00 2018 +0800 Document exposed TypeReaders commit5a824a5695
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 15:44:15 2018 +0800 Add missing exceptions commitc2de0c055f
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 15:40:16 2018 +0800 Fix seealso for preconditions and add missing descriptions commit3a7d7ee955
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 15:36:22 2018 +0800 Minor fixes in documentations + Fix unescaped '<' + Fix typo commit45839bd982
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 15:29:47 2018 +0800 Add XML Docs commit9e6254600c
Merge:aea06788
bb4bb138
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 14:48:13 2018 +0800 Merge branch 'dev' into docs/faq-n-patches-offline commitaea067884c
Merge:27dc4831
9ddd7090
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 13:51:07 2018 +0800 Merge branch 'docs/faq-n-patches-offline' of https://github.com/Still34/Discord.Net into docs/faq-n-patches-offline commit27dc4831e8
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 13:50:27 2018 +0800 Add XML Docs commitbaa8beb382
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 13:49:42 2018 +0800 Add XML Docs commit089f97a010
Author: Still Hsu <341464@gmail.com> Date: Sat May 5 13:43:04 2018 +0800 Add details regarding userbot support commitbb4bb13846
Author: Finite Reality <FiniteReality@users.noreply.github.com> Date: Fri May 4 11:42:54 2018 +0100 Fix issues with #984, remove extraneous whitespace (#1051) - Removed unnecessary parameter in SocketVoiceServer - Moved SocketVoiceServer into Entities/Voice - Fixed a bug where trying to download the cached guild would throw - Fixed a potential bug where Discord might not give us a port when connecting to voice commit9ddd70906a
Merge:f197174f
7cfed7ff
Author: Still Hsu <341464@gmail.com> Date: Fri May 4 09:37:04 2018 +0800 Merge branch 'dev' into docs/faq-n-patches-offline commitf197174fcc
Author: Still Hsu <341464@gmail.com> Date: Fri May 4 09:36:53 2018 +0800 Fix embed docs consistency commit7cfed7ff67
Author: Alex Gravely <tcbskater@hotmail.com> Date: Thu May 3 21:30:13 2018 -0400 Fix nullref when passing null to GetShardIdFor. (#1049) commite775853b1b
Author: Luke <dev@crimsonxv.pro> Date: Fri May 4 02:29:51 2018 +0100 Expose VoiceServerUpdate events (#984) * Expose VoiceServerUpdate events * Amend based on feedback * Move this out of guild entity * Fix namespace issue * Adjust based on feedback #2 * Use cacheable instead * Change based on feedback commit157acc4695
Author: Still Hsu <341464@gmail.com> Date: Thu May 3 23:03:35 2018 +0800 Add tag examples commit57ea571a81
Author: Still Hsu <341464@gmail.com> Date: Thu May 3 22:48:33 2018 +0800 Fix sample link & add missing pictures commit6769c37ad7
Author: Still Hsu <341464@gmail.com> Date: Thu May 3 22:39:26 2018 +0800 Compress some assets & add OAuth2 URL generator
@@ -1,7 +1,5 @@ | |||
--- | |||
uid: Discord.Commands.PreconditionAttribute | |||
seealso: | |||
- linkId: Discord.Commands.ParameterPreconditionAttribute | |||
remarks: *content | |||
--- | |||
@@ -12,8 +10,6 @@ method-level for a command. | |||
--- | |||
uid: Discord.Commands.ParameterPreconditionAttribute | |||
seealso: | |||
- linkId: Discord.Commands.PreconditionAttribute | |||
remarks: *content | |||
--- | |||
@@ -31,11 +27,11 @@ The following example creates a precondition to see if the user has | |||
sufficient role required to access the command. | |||
```cs | |||
public class RequireRoleAtribute : PreconditionAttribute | |||
public class RequireRoleAttribute : PreconditionAttribute | |||
{ | |||
private readonly ulong _roleId; | |||
public RequireRoleAtribute(ulong roleId) | |||
public RequireRoleAttribute(ulong roleId) | |||
{ | |||
_roleId = roleId; | |||
} | |||
@@ -0,0 +1,174 @@ | |||
--- | |||
uid: Discord.GuildChannelProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.IGuildChannel.ModifyAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
var channel = _client.GetChannel(id) as IGuildChannel; | |||
if (channel == null) return; | |||
await channel.ModifyAsync(x => | |||
{ | |||
x.Name = "new-name"; | |||
x.Position = channel.Position - 1; | |||
}); | |||
``` | |||
--- | |||
uid: Discord.TextChannelProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.ITextChannel.ModifyAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
var channel = _client.GetChannel(id) as ITextChannel; | |||
if (channel == null) return; | |||
await channel.ModifyAsync(x => | |||
{ | |||
x.Name = "cool-guys-only"; | |||
x.Topic = "This channel is only for cool guys and adults!!!"; | |||
x.Position = channel.Position - 1; | |||
x.IsNsfw = true; | |||
}); | |||
``` | |||
--- | |||
uid: Discord.VoiceChannelProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.IVoiceChannel.ModifyAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
var channel = _client.GetChannel(id) as IVoiceChannel; | |||
if (channel == null) return; | |||
await channel.ModifyAsync(x => | |||
{ | |||
x.UserLimit = 5; | |||
}); | |||
``` | |||
--- | |||
uid: Discord.EmoteProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.IGuild.ModifyEmoteAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
await guild.ModifyEmoteAsync(x => | |||
{ | |||
x.Name = "blobo"; | |||
}); | |||
``` | |||
--- | |||
uid: Discord.MessageProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.IUserMessage.ModifyAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
var message = await channel.SendMessageAsync("boo"); | |||
await Task.Delay(TimeSpan.FromSeconds(1)); | |||
await message.ModifyAsync(x => x.Content = "boi"); | |||
``` | |||
--- | |||
uid: Discord.GuildProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.IGuild.ModifyAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
var guild = _client.GetGuild(id); | |||
if (guild == null) return; | |||
await guild.ModifyAsync(x => | |||
{ | |||
x.Name = "VERY Fast Discord Running at Incredible Hihg Speed"; | |||
}); | |||
``` | |||
--- | |||
uid: Discord.RoleProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.IRole.ModifyAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
var role = guild.GetRole(id); | |||
if (role == null) return; | |||
await role.ModifyAsync(x => | |||
{ | |||
x.Name = "cool boi"; | |||
x.Color = Color.Gold; | |||
x.Hoist = true; | |||
x.Mentionable = true; | |||
}); | |||
``` | |||
--- | |||
uid: Discord.GuildUserProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.IGuildUser.ModifyAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
var user = guild.GetUser(id); | |||
if (user == null) return; | |||
await user.ModifyAsync(x => | |||
{ | |||
x.Nickname = "I need healing"; | |||
}); | |||
``` | |||
--- | |||
uid: Discord.SelfUserProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.ISelfUser.ModifyAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
await selfUser.ModifyAsync(x => | |||
{ | |||
x.Username = "Mercy"; | |||
}); | |||
``` | |||
--- | |||
uid: Discord.WebhookProperties | |||
example: [*content] | |||
--- | |||
The following example uses @Discord.IWebhook.ModifyAsync* to | |||
apply changes specified in the properties, | |||
```cs | |||
await webhook.ModifyAsync(x => | |||
{ | |||
x.Name = "very fast fox"; | |||
x.ChannelId = newChannelId; | |||
}); | |||
``` |
@@ -35,9 +35,10 @@ | |||
"dest": "_site", | |||
"template": [ | |||
"default", | |||
"_template/light-dark-theme" | |||
"_template/light-dark-theme", | |||
"_template/lastmodified" | |||
], | |||
"postProcessors": [ "ExtractSearchIndex" ], | |||
"postProcessors": [ "ExtractSearchIndex", "LastModifiedPostProcessor" ], | |||
"overwrite": "_overwrites/**/**.md", | |||
"globalMetadata": { | |||
"_appTitle": "Discord.Net Documentation", | |||
@@ -8,7 +8,7 @@ title: Questions about Basic Operations | |||
## How should I safely check a type? | |||
> [!WARNING] | |||
> Direct casting (e.g. `(Type)type`) is **the least recommended** | |||
> Direct casting (e.g., `(Type)type`) is **the least recommended** | |||
> way of casting, as it *can* throw an [InvalidCastException] | |||
> when the object isn't the desired type. | |||
> | |||
@@ -28,9 +28,9 @@ A good and safe casting example: | |||
## How do I send a message? | |||
> [!TIP] | |||
> The [GetChannel] method by default returns an [IChannel]. | |||
> This means channels such as [IVoiceChannel], [ICategoryChannel] | |||
> can be returned. This is why that you cannot send message | |||
> The [GetChannel] method by default returns an [IChannel], allowing | |||
> channel types such as [IVoiceChannel], [ICategoryChannel] | |||
> to be returned; consequently, you cannot send a message | |||
> to channels like those. | |||
Any implementation of [IMessageChannel] has a [SendMessageAsync] | |||
@@ -8,11 +8,12 @@ title: Basic Questions about Client | |||
## My client keeps returning 401 upon logging in! | |||
> [!WARNING] | |||
> Userbot/selfbot (logging in with a user token) is not | |||
> officially supported with this library. | |||
> Userbot/selfbot (logging in with a user token) is no | |||
> longer supported with this library starting from 2.0, as | |||
> logging in under a user account may result in account termination. | |||
> | |||
> Logging in under a user account may result in account | |||
> termination! | |||
> For more information, see issue [827] & [958], as well as the official | |||
> [Discord API Terms of Service]. | |||
There are few possible reasons why this may occur. | |||
@@ -20,20 +21,23 @@ There are few possible reasons why this may occur. | |||
bot account created from the Discord Developer portal, you should | |||
be using `TokenType.Bot`. | |||
2. You are not using the correct login credentials. Please keep in | |||
mind that tokens is different from a *client secret*. | |||
mind that a token is **different** from a *client secret*. | |||
[TokenType]: xref:Discord.TokenType | |||
[827]: https://github.com/RogueException/Discord.Net/issues/827 | |||
[958]: https://github.com/RogueException/Discord.Net/issues/958 | |||
[Discord API Terms of Service]: https://discordapp.com/developers/docs/legal | |||
## How do I do X, Y, Z when my bot connects/logs on? Why do I get a `NullReferenceException` upon calling any client methods after connect? | |||
Your bot should **not** attempt to interact in any way with | |||
guilds/servers until the [Ready] event fires. When the bot | |||
connects, it first has to download guild information from | |||
Discord in order for you to get access to any server | |||
Discord for you to get access to any server | |||
information; the client is not ready at this point. | |||
Technically, the [GuildAvailable] event fires once the data for a | |||
particular guild has downloaded; however, it's best to wait for all | |||
particular guild has downloaded; however, it is best to wait for all | |||
guilds to be downloaded. Once all downloads are complete, the [Ready] | |||
event is triggered, then you can proceed to do whatever you like. | |||
@@ -42,7 +46,7 @@ event is triggered, then you can proceed to do whatever you like. | |||
## How do I get a message's previous content when that message is edited? | |||
If you need to do anything with messages (e.g. checking Reactions, | |||
If you need to do anything with messages (e.g., checking Reactions, | |||
checking the content of edited/deleted messages), you must set the | |||
[MessageCacheSize] in your [DiscordSocketConfig] settings in order to | |||
use the cached message entity. Read more about it [here](xref:Guides.Concepts.Events#cacheable). | |||
@@ -8,25 +8,27 @@ title: Beginner Questions / How to Get Started | |||
## How do I add my bot to my server/guild? | |||
You can do so by using the [permission calculator] provided | |||
by FiniteReality. | |||
This tool allows you to set the permissions that the bot will be | |||
added with, and invite the bot into your guild. With this method, | |||
bots will also be assigned their own special roles that normal users | |||
cannot use; this is what we call a `Managed` role, and this is a much | |||
safer method of permission management than to create a role that any | |||
users can be assigned to. | |||
by [FiniteReality]. | |||
This tool allows you to set permissions that the bot will be assigned | |||
with, and invite the bot into your guild. With this method, bots will | |||
also be assigned a unique role that a regular user cannot use; this | |||
is what we call a `Managed` role. Because you cannot assign this | |||
role to any other users, it is much safer than creating a single | |||
role which, intentionally or not, can be applied to other users | |||
to escalate their privilege. | |||
[FiniteReality]: https://github.com/FiniteReality/permissions-calculator | |||
[permission calculator]: https://finitereality.github.io/permissions-calculator | |||
## What is a token? | |||
A token is a credential used to log into an account. This information | |||
should be kept **private** and for your eyes only. Anyone with your | |||
token can log into your account. This applies to both user and bot | |||
accounts. That also means that you should never ever hardcode your | |||
token or add it into source control, as your identity may be stolen | |||
by scrape bots on the internet that scours through constantly to | |||
obtain a token. | |||
token can log into your account. This risk applies to both user | |||
and bot accounts. That also means that you should **never** hardcode | |||
your token or add it into source control, as your identity may be | |||
stolen by scrape bots on the internet that scours through | |||
constantly to obtain a token. | |||
## What is a client/user/object ID? | |||
@@ -5,37 +5,35 @@ title: Questions about Commands | |||
# Command-related Questions | |||
## How can I restrict some of my commands so only certain users can execute them? | |||
## How can I restrict some of my commands so only specific users can execute them? | |||
Based on how you want to implement the restrictions, you can use the | |||
built-in [RequireUserPermission] precondition, which allows you to | |||
restrict the command based on the user's current permissions in the | |||
guild or channel (*e.g. `GuildPermission.Administrator`, | |||
`ChannelPermission.ManageMessages` etc.*). | |||
guild or channel (*e.g., `GuildPermission.Administrator`, | |||
`ChannelPermission.ManageMessages`*). | |||
If, however, you wish to restrict the commands based on the user's | |||
role, you can either create your own custom precondition or use | |||
role, you can either create your custom precondition or use | |||
Joe4evr's [Preconditions Addons] that provides a few custom | |||
preconditions that aren't provided in the stock library. | |||
Its source can also be used as an example for creating your own | |||
Its source can also be used as an example for creating your | |||
custom preconditions. | |||
[RequireUserPermission]: xref:Discord.Commands.RequireUserPermissionAttribute | |||
[Preconditions Addons]: https://github.com/Joe4evr/Discord.Addons/tree/master/src/Discord.Addons.Preconditions | |||
## I'm getting an error about `Assembly#GetEntryAssembly`. | |||
## Why am I getting an error about `Assembly.GetEntryAssembly`? | |||
You may be confusing [CommandService#AddModulesAsync] with | |||
[CommandService#AddModuleAsync]. The former is used to add modules | |||
via the assembly, while the latter is used to add a single module. | |||
[CommandService#AddModulesAsync]: xref:Discord.Commands.CommandService.AddModulesAsync* | |||
[CommandService#AddModuleAsync]: xref:Discord.Commands.CommandService.AddModuleAsync* | |||
You may be confusing @Discord.Commands.CommandService.AddModulesAsync* | |||
with @Discord.Commands.CommandService.AddModuleAsync*. The former | |||
is used to add modules via the assembly, while the latter is used to | |||
add a single module. | |||
## What does [Remainder] do in the command signature? | |||
The [RemainderAttribute] leaves the string unparsed, meaning you | |||
don't have to add quotes around the text for the text to be | |||
do not have to add quotes around the text for the text to be | |||
recognized as a single object. Please note that if your method has | |||
multiple parameters, the remainder attribute can only be applied to | |||
the last parameter. | |||
@@ -47,13 +45,14 @@ the last parameter. | |||
## What is a service? Why does my module not hold any data after execution? | |||
In Discord.Net, modules are created similarly to ASP.NET, meaning | |||
that they have a transient nature. This means that they are spawned | |||
every time when a request is received, and are killed from memory | |||
when the execution finishes. This is why you cannot store persistent | |||
data inside a module. To workaround this, consider using a service. | |||
Service is often used to hold data externally, so that they will | |||
persist throughout execution. Think of it like a chest that holds | |||
that they have a transient nature; modules are spawned whenever a | |||
request is received, and are killed from memory when the execution | |||
finishes. In other words, you cannot store persistent | |||
data inside a module. Consider using a service if you wish to | |||
workaround this. | |||
Service is often used to hold data externally so that they persist | |||
throughout execution. Think of it like a chest that holds | |||
whatever you throw at it that won't be affected by anything unless | |||
you want it to. Note that you should also learn Microsoft's | |||
implementation of [Dependency Injection] \([video]) before proceeding, | |||
@@ -66,25 +65,47 @@ A brief example of service and dependency injection can be seen below. | |||
[Dependency Injection]: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection | |||
[video]: https://www.youtube.com/watch?v=QtDTfn8YxXg | |||
## I have a long-running Task in my command, and Discord.Net keeps saying that a `MessageReceived` handler is blocking the gateway. What gives? | |||
## Discord.Net keeps saying that a `MessageReceived` handler is blocking the gateway, what should I do? | |||
By default, the library warns the user about any long-running event | |||
handler that persists for **more than 3 seconds**. Any event | |||
handlers that are run on the same thread as the gateway task, the task | |||
in charge of keeping the connection alive, may block the processing of | |||
heartbeat, and thus terminating the connection. | |||
In this case, the library detects that a `MessageReceived` | |||
event handler is blocking the gateway thread. This warning is | |||
typically associated with the command handler as it listens for that | |||
particular event. If the command handler is blocking the thread, then | |||
this **might** mean that you have a long-running command. | |||
By default, all commands are executed on the same thread as the | |||
gateway task, which is responsible for keeping the connection from | |||
your client to Discord alive. When you execute a command, | |||
this blocks the gateway from communicating for as long as the command | |||
task is being executed. The library will warn you about any long | |||
running event handler (in this case, the command handler) that | |||
persists for **more than 3 seconds**. | |||
> [!NOTE] | |||
> In rare cases, runtime errors can also cause blockage, usually | |||
> associated with Mono, which is not supported by this library. | |||
To resolve this, the library has designed a flag called [RunMode]. | |||
To prevent a long-running command from blocking the gateway | |||
thread, a flag called [RunMode] is explicitly designed to resolve | |||
this issue. | |||
There are 2 main `RunMode`s. | |||
1. `RunMode.Sync` (default) | |||
1. `RunMode.Sync` | |||
2. `RunMode.Async` | |||
`Sync` is the default behavior and makes the command to be run on the | |||
same thread as the gateway one. `Async` will spin the task off to a | |||
different thread from the gateway one. | |||
> [!IMPORTANT] | |||
> While specifying `RunMode.Async` allows the command to be spun off | |||
> to a different thread, keep in mind that by doing so, there will be | |||
> **potentially unwanted consequences**. Before applying this flag, | |||
> please consider whether it is necessary to do so. | |||
> | |||
> Further details regarding `RunMode.Async` can be found below. | |||
You can set the `RunMode` either by specifying it individually via | |||
the `CommandAttribute`, or by setting the global default with | |||
the `CommandAttribute` or by setting the global default with | |||
the [DefaultRunMode] flag under `CommandServiceConfig`. | |||
# [CommandAttribute](#tab/cmdattrib) | |||
@@ -99,15 +120,6 @@ the [DefaultRunMode] flag under `CommandServiceConfig`. | |||
*** | |||
> [!IMPORTANT] | |||
> While specifying `RunMode.Async` allows the command to be spun off | |||
> to a different thread instead of the gateway thread, | |||
> keep in mind that there will be **potential consequences** | |||
> by doing so. Before applying this flag, please | |||
> consider whether it is necessary to do so. | |||
> | |||
> Further details regarding `RunMode.Async` can be found below. | |||
[RunMode]: xref:Discord.Commands.RunMode | |||
[CommandAttribute]: xref:Discord.Commands.CommandAttribute | |||
[DefaultRunMode]: xref:Discord.Commands.CommandServiceConfig.DefaultRunMode | |||
@@ -115,16 +127,15 @@ the [DefaultRunMode] flag under `CommandServiceConfig`. | |||
## How does `RunMode.Async` work, and why is Discord.Net *not* using it by default? | |||
`RunMode.Async` works by spawning a new `Task` with an unawaited | |||
[Task.Run], essentially making `ExecuteAsyncInternalAsync`, the task | |||
that is used to invoke the command task, to be finished on a | |||
different thread. This means that [ExecuteAsync] will be forced to | |||
return a successful [ExecuteResult] regardless of the actual | |||
execution result. | |||
[Task.Run], essentially making the task that is used to invoke the | |||
command task to be finished on a different thread. This design means | |||
that [ExecuteAsync] will be forced to return a successful | |||
[ExecuteResult] regardless of the actual execution result. | |||
The following are the known caveats with `RunMode.Async`, | |||
1. You can potentially introduce race condition. | |||
2. Unnecessary overhead caused by [async state machine]. | |||
1. You can potentially introduce a race condition. | |||
2. Unnecessary overhead caused by the [async state machine]. | |||
3. [ExecuteAsync] will immediately return [ExecuteResult] instead of | |||
other result types (this is particularly important for those who wish | |||
to utilize [RuntimeResult] in 2.0). | |||
@@ -137,7 +148,7 @@ For #3, in Discord.Net 2.0, the library introduces a new event called | |||
**successfully executed**. This event will be raised regardless of | |||
the `RunMode` type and will return the appropriate execution result. | |||
For #4, exceptions are caught in [CommandService#Log] event under | |||
For #4, exceptions are caught in [CommandService.Log] event under | |||
[LogMessage.Exception] as [CommandException]. | |||
[Task.Run]: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run | |||
@@ -146,6 +157,6 @@ For #4, exceptions are caught in [CommandService#Log] event under | |||
[ExecuteResult]: xref:Discord.Commands.ExecuteResult | |||
[RuntimeResult]: xref:Discord.Commands.RuntimeResult | |||
[CommandExecuted]: xref:Discord.Commands.CommandService.CommandExecuted | |||
[CommandService#Log]: xref:Discord.Commands.CommandService.Log | |||
[CommandService.Log]: xref:Discord.Commands.CommandService.Log | |||
[LogMessage.Exception]: xref:Discord.LogMessage.Exception* | |||
[CommandException]: xref:Discord.Commands.CommandException |
@@ -16,5 +16,5 @@ public Task EchoAsync(string text) => ReplyAsync(text); | |||
// Wrapping the message in quotes solves this. | |||
// This way, the system knows the entire message is to be parsed as a | |||
// single String. | |||
// e.g. | |||
// e.g., | |||
// !echo "Coffee Cake" |
@@ -7,8 +7,8 @@ title: Questions about Legacy Versions | |||
## X, Y, Z does not work! It doesn't return a valid value anymore | |||
If you're currently using an older version in stable branch, please | |||
upgrade to the latest pre-release version to ensure maximum | |||
If you are currently using an older version of the stable branch, | |||
please upgrade to the latest pre-release version to ensure maximum | |||
compatibility. Several features may be broken in older | |||
versions and will likely not be fixed in the version branch due to | |||
their breaking nature. |
@@ -107,7 +107,7 @@ be found in @Guides.Commands.TypeReaders. | |||
#### Optional Parameters | |||
Parameters, by default, are always required. To make a parameter | |||
optional, give it a default value (i.e. `int num = 0`). | |||
optional, give it a default value (i.e., `int num = 0`). | |||
#### Parameters with Spaces | |||
@@ -6,18 +6,18 @@ title: Post-command Execution Handling | |||
# Preface | |||
When developing commands, you may want to consider building a | |||
post-execution handling system so you can have a finer control | |||
post-execution handling system so you can have finer control | |||
over commands. Discord.Net offers several post-execution workflows | |||
for you to work with. | |||
If you recall, in the [Command Guide], we've shown the following | |||
If you recall, in the [Command Guide], we have shown the following | |||
example for executing and handling commands, | |||
[!code[Command Handler](samples/command_handler.cs)] | |||
You may notice that after we perform [ExecuteAsync], we store the | |||
result and print it to the chat. This is essentially the most | |||
basic post-execution handling. | |||
result and print it to the chat, essentially creating the most | |||
fundamental form of a post-execution handler. | |||
With this in mind, we could start doing things like the following, | |||
@@ -25,8 +25,8 @@ With this in mind, we could start doing things like the following, | |||
However, this may not always be preferred, because you are | |||
creating your post-execution logic *with* the essential command | |||
handler. This could lead to messy code and could potentially be a | |||
violation of the SRP (Single Responsibility Principle). | |||
handler. This design could lead to messy code and could potentially | |||
be a violation of the SRP (Single Responsibility Principle). | |||
Another major issue is if your command is marked with | |||
`RunMode.Async`, [ExecuteAsync] will **always** return a successful | |||
@@ -37,8 +37,8 @@ about the impact in the [FAQ](xref:FAQ.Commands). | |||
Enter [CommandExecuted], an event that was introduced in | |||
Discord.Net 2.0. This event is raised whenever a command is | |||
successfully executed **without any run-time exceptions** or **without | |||
any parsing or precondition failure**. This means this event can be | |||
successfully executed **without any run-time exceptions** or **any | |||
parsing or precondition failure**. This means this event can be | |||
used to streamline your post-execution design, and the best thing | |||
about this event is that it is not prone to `RunMode.Async`'s | |||
[ExecuteAsync] drawbacks. | |||
@@ -52,7 +52,7 @@ next? We can take this further by using [RuntimeResult]. | |||
### RuntimeResult | |||
`RuntimeResult` was originally introduced in 1.0 to allow | |||
`RuntimeResult` was initially introduced in 1.0 to allow | |||
developers to centralize their command result logic. | |||
In other words, it is a result type that is designed to be | |||
returned when the command has finished its execution. | |||
@@ -62,7 +62,7 @@ However, it wasn't widely adopted due to the aforementioned | |||
result-handler via the [CommandExecuted] event, we can start | |||
making use of this class. | |||
The best way to make use of it is to create your own version of | |||
The best way to make use of it is to create your version of | |||
`RuntimeResult`. You can achieve this by inheriting the `RuntimeResult` | |||
class. | |||
@@ -71,16 +71,16 @@ of `RuntimeResult`, | |||
[!code[Base Use](samples/customresult_base.cs)] | |||
The sky's the limit from here. You can add any additional information | |||
you'd like regarding the execution result. | |||
The sky is the limit from here. You can add any additional information | |||
you would like regarding the execution result. | |||
For example, you may want to add your own result type or other | |||
For example, you may want to add your result type or other | |||
helpful information regarding the execution, or something | |||
simple like static methods to help you create return types easily. | |||
[!code[Extended Use](samples/customresult_extended.cs)] | |||
After you're done creating your own [RuntimeResult], you can | |||
After you're done creating your [RuntimeResult], you can | |||
implement it in your command by marking the command return type to | |||
`Task<RuntimeResult>`. | |||
@@ -100,12 +100,12 @@ And now we can check for it in our [CommandExecuted] handler: | |||
## CommandService.Log Event | |||
We have so far covered the handling of various result types, but we | |||
haven't talked about what to do if the command enters a catastrophic | |||
failure (i.e. exceptions). To resolve this, we can make use of the | |||
have not talked about what to do if the command enters a catastrophic | |||
failure (i.e., exceptions). To resolve this, we can make use of the | |||
[CommandService.Log] event. | |||
All exceptions thrown during a command execution will be caught and | |||
be sent to the Log event under the [LogMessage.Exception] property | |||
All exceptions thrown during a command execution are caught and sent | |||
to the Log event under the [LogMessage.Exception] property | |||
as a [CommandException] type. The [CommandException] class allows | |||
us to access the exception thrown, as well as the context | |||
of the command. | |||
@@ -11,14 +11,14 @@ stopped. | |||
To start a connection, invoke the `StartAsync` method on a client that | |||
supports a WebSocket connection; to end a connection, invoke the | |||
`StopAsync` method. This will gracefully close any open WebSocket or | |||
`StopAsync` method, which gracefully closes any open WebSocket or | |||
UdpSocket connections. | |||
Since the Start/Stop methods only signal to an underlying connection | |||
manager that a connection needs to be started, **they return before a | |||
connection is actually made.** | |||
connection is made.** | |||
As a result, you will need to hook into one of the connection-state | |||
As a result, you need to hook into one of the connection-state | |||
based events to have an accurate representation of when a client is | |||
ready for use. | |||
@@ -29,7 +29,7 @@ ready to be used. | |||
A separate event, `Ready`, is provided on [DiscordSocketClient], which | |||
is raised only when the client has finished guild stream or guild | |||
sync, and has a complete guild cache. | |||
sync and has a completed guild cache. | |||
[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient | |||
@@ -41,8 +41,8 @@ sync, and has a complete guild cache. | |||
> [!TIP] | |||
> Avoid running long-running code on the gateway! If you deadlock the | |||
> gateway (as explained in [events]), the connection manager will be | |||
> unable to recover and reconnect. | |||
> gateway (as explained in [events]), the connection manager will | |||
> **NOT** be able to recover and reconnect. | |||
Assuming the client disconnected because of a fault on Discord's end, | |||
and not a deadlock on your end, we will always attempt to reconnect | |||
@@ -50,6 +50,6 @@ and resume a connection. | |||
Don't worry about trying to maintain your own connections, the | |||
connection manager is designed to be bulletproof and never fail - if | |||
your client doesn't manage to reconnect, you've found a bug! | |||
your client does not manage to reconnect, you have found a bug! | |||
[events]: xref:Guides.Concepts.Events |
@@ -68,7 +68,7 @@ for use on another machine without installing the dependencies first. | |||
This can be achieved by using the dotnet CLI too on the development | |||
machine: | |||
`dotnet publish -c Release` | |||
* `dotnet publish -c Release` | |||
Additionally, you may want to target a specific platform when | |||
publishing the application so you may use the application without | |||
@@ -80,7 +80,7 @@ For example, when targeting a Windows 10 machine, you may want to use | |||
the following to create the application in Windows executable | |||
format (.exe): | |||
`dotnet publish -c Release -r win10-x64` | |||
* `dotnet publish -c Release -r win10-x64` | |||
[.NET Core application deployment]: https://docs.microsoft.com/en-us/dotnet/core/deploying/ | |||
[Runtime ID]: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog |
@@ -56,7 +56,7 @@ DiscordSocketClient. | |||
> [FAQ](xref:FAQ.Basics.GetStarted) page. | |||
More detailed versions of entities can be pulled from the basic | |||
entities, e.g. `SocketGuild.GetUser`, which returns a | |||
entities, e.g., `SocketGuild.GetUser`, which returns a | |||
`SocketGuildUser`, or `SocketGuild.GetChannel`, which returns a | |||
`SocketGuildChannel`. Again, you may need to cast these objects to get | |||
a variant of the type that you need. | |||
@@ -74,7 +74,7 @@ object. | |||
[Cacheable]: xref:Discord.Cacheable`2 | |||
> [!NOTE] | |||
> Many events relating to a Message entity (i.e. `MessageUpdated` and | |||
> Many events relating to a Message entity (i.e., `MessageUpdated` and | |||
> `ReactionAdded`) rely on the client's message cache, which is | |||
> **not** enabled by default. Set the `MessageCacheSize` flag in | |||
> @Discord.WebSocket.DiscordSocketConfig to enable it. | |||
@@ -16,7 +16,7 @@ section. | |||
> [!WARNING] | |||
> Due to the nature of Discord.Net's event system, all log event | |||
> handlers will be executed synchronously on the gateway thread. If your | |||
> log output will be dumped to a Web API (e.g. Sentry), you are advised | |||
> log output will be dumped to a Web API (e.g., Sentry), you are advised | |||
> to wrap your output in a `Task.Run` so the gateway thread does not | |||
> become blocked while waiting for logging data to be written. | |||
@@ -27,8 +27,8 @@ Discord Applications Portal first. | |||
 | |||
6. Confirm the popup. | |||
7. If this bot will be public, check "Public Bot." **Do not tick any | |||
other options!** | |||
7. (Optional) If this bot will be public, check "Public Bot." | |||
* **Do not tick any other options!** | |||
[Discord Applications Portal]: https://discordapp.com/developers/applications/me | |||
@@ -38,15 +38,18 @@ Bots **cannot** use invite links; they must be explicitly invited | |||
through the OAuth2 flow. | |||
1. Open your bot's application on the [Discord Applications Portal]. | |||
2. Retrieve the application's **Client ID**. | |||
2. Navigate to `OAuth2 URL Generator` and click on `Generate OAuth2 URL`. | |||
 | |||
 | |||
3. Create an OAuth2 authorization URL | |||
3. Select the permissions that you wish to assign your bot with. | |||
- `https://discordapp.com/oauth2/authorize?client_id=<CLIENT ID>&scope=bot` | |||
> [!NOTE] | |||
> This will assign the bot with a special "managed" role that no | |||
> one else can use. The permissions can be changed later in the | |||
> roles settings if you ever change your mind! | |||
4. Open the authorization URL in your browser. | |||
4. Open the generated authorization URL in your browser. | |||
5. Select a server. | |||
6. Click on authorize. | |||
@@ -40,7 +40,7 @@ Release builds of Discord.Net will be published to the | |||
Development builds of Discord.Net, as well as add-ons, will be | |||
published to our [MyGet feed]. | |||
Direct feed link: `https://www.myget.org/F/discord-net/api/v3/index.json` | |||
* Direct feed link: `https://www.myget.org/F/discord-net/api/v3/index.json` | |||
Not sure how to add a direct feed? See how [with Visual Studio] or | |||
[without Visual Studio]. | |||
@@ -28,13 +28,13 @@ addon will run on all platforms. | |||
`Discord.Net.Rest` provides a set of concrete classes to be used | |||
**strictly** with the REST portion of Discord's API. Entities in this | |||
implementation are prefixed with `Rest` (e.g. `RestChannel`). | |||
implementation are prefixed with `Rest` (e.g., `RestChannel`). | |||
`Discord.Net.Rpc` provides a set of concrete classes that are used | |||
with Discord's RPC API. Entities in this implementation are prefixed | |||
with `Rpc` (e.g. `RpcChannel`). | |||
with `Rpc` (e.g., `RpcChannel`). | |||
`Discord.Net.WebSocket` provides a set of concrete classes that are | |||
used primarily with Discord's WebSocket API or entities that are kept | |||
in cache. When developing bots, you will be using this implementation. | |||
All entities are prefixed with `Socket` (e.g. `SocketChannel`). | |||
All entities are prefixed with `Socket` (e.g., `SocketChannel`). |
@@ -17,7 +17,7 @@ understand these topics to some extent before proceeding. | |||
Here are some examples: | |||
1. [Official quick start guide] | |||
1. [Official samples] | |||
2. [Official template] | |||
> [!NOTE] | |||
@@ -26,7 +26,7 @@ Here are some examples: | |||
> It is not meant to be something that will work out of the box. | |||
[Official template]: https://github.com/foxbot/DiscordBotBase/tree/csharp/src/DiscordBot | |||
[Official quick start guide]: https://github.com/RogueException/Discord.Net/blob/dev/docs/guides/getting_started/samples/first-bot/structure.cs | |||
[Official samples]: https://github.com/RogueException/Discord.Net/tree/dev/samples | |||
[Task-based Asynchronous Pattern]: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap | |||
[polymorphism]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/polymorphism | |||
[interface]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/ | |||
@@ -6,6 +6,7 @@ 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 | |||
{ | |||
@@ -3,20 +3,29 @@ using System.Threading.Tasks; | |||
namespace Discord.Commands | |||
{ | |||
/// <summary> Requires the module or class to pass the specified precondition before execution can begin. </summary> | |||
/// <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. | |||
/// 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" /> = <see langword="null" /> or not at all will | |||
/// 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); | |||
} | |||
} |
@@ -54,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) | |||
@@ -68,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()); | |||
@@ -129,7 +129,7 @@ namespace Discord.Commands | |||
/// <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 | |||
/// <see langword="null" />. | |||
/// <c>null</c>. | |||
/// </param> | |||
/// <returns> | |||
/// A built module. | |||
@@ -144,7 +144,7 @@ namespace Discord.Commands | |||
/// <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 | |||
/// <see langword="null" />. | |||
/// <c>null</c>. | |||
/// </param> | |||
/// <returns> | |||
/// A built module. | |||
@@ -183,7 +183,7 @@ namespace Discord.Commands | |||
/// <param name="assembly">The <see cref="Assembly" /> containing command modules.</param> | |||
/// <param name="services"> | |||
/// An <see cref="IServiceProvider" /> for your dependency injection solution, if using one - otherwise, pass | |||
/// <see langword="null" />. | |||
/// <c>null</c>. | |||
/// </param> | |||
/// <returns> | |||
/// A collection of built modules. | |||
@@ -6,19 +6,23 @@ using System.Threading.Tasks; | |||
namespace Discord.Commands | |||
{ | |||
/// <summary> | |||
/// A <see cref="TypeReader"/> for parsing objects implementing <see cref="IChannel"/>. | |||
/// </summary> | |||
/// <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) | |||
@@ -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); | |||
@@ -6,9 +6,14 @@ 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) | |||
{ | |||
if (context.Guild != null) | |||
@@ -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> | |||
/// An awaitable Task containing the result of the type reading process. | |||
/// </returns> | |||
public abstract Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services); | |||
} | |||
} |
@@ -7,9 +7,14 @@ 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>(); | |||
@@ -72,8 +77,8 @@ namespace Discord.Commands | |||
.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 as IGuildUser).Nickname, StringComparison.OrdinalIgnoreCase))) | |||
AddResult(results, guildUser as T, (guildUser as IGuildUser).Nickname == input ? 0.60f : 0.50f); | |||
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); | |||
} | |||
if (results.Count > 0) | |||
@@ -8,7 +8,7 @@ namespace Discord.Commands | |||
/// <summary> | |||
/// Initializes a new <see cref="RuntimeResult" /> class with the type of error and reason. | |||
/// </summary> | |||
/// <param name="error">The type of failure, or <see langword="null" /> if none.</param> | |||
/// <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) | |||
{ | |||
@@ -8,13 +8,26 @@ namespace Discord | |||
public static class CDN | |||
{ | |||
/// <summary> | |||
/// Returns the Discord developer application icon. | |||
/// 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 the user avatar URL based on the <paramref name="size"/> and <see cref="ImageFormat" />. | |||
/// 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) | |||
@@ -26,34 +39,64 @@ namespace Discord | |||
/// 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 the icon URL associated with the given guild ID. | |||
/// 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 the guild splash URL associated with the given guild and splash ID. | |||
/// 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 the channel icon URL associated with the given guild and icon ID. | |||
/// 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 the emoji URL based on the emoji ID. | |||
/// 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 the rich presence asset URL based on the asset ID and <see cref="ImageFormat" />. | |||
/// 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, ""); | |||
@@ -61,10 +104,21 @@ namespace Discord | |||
} | |||
/// <summary> | |||
/// Returns the Spotify album URL based on the album art ID. | |||
/// 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}"; | |||
@@ -8,68 +8,116 @@ namespace Discord | |||
public class DiscordConfig | |||
{ | |||
/// <summary> | |||
/// Returns the gateway version Discord.Net uses. | |||
/// Returns the API version Discord.Net uses. | |||
/// </summary> | |||
/// <returns> | |||
/// A 32-bit integer 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> | |||
/// 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) ?? | |||
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. | |||
/// 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; | |||
public const int MaxAuditLogEntriesPerBatch = 100; | |||
/// <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> | |||
/// <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> | |||
/// <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; | |||
} | |||
} |
@@ -19,7 +19,7 @@ namespace Discord | |||
public string ImageId { get; internal set; } | |||
/// <summary> | |||
/// Returns the image URL of the asset, or <see langword="null"/> when the application ID does not exist. | |||
/// Returns the image URL of the asset, or <c>null</c> when the application ID does not exist. | |||
/// </summary> | |||
public string GetImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) | |||
=> ApplicationId.HasValue ? CDN.GetRichAssetUrl(ApplicationId.Value, ImageId, size, format) : null; | |||
@@ -13,36 +13,65 @@ namespace Discord | |||
/// <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() { } | |||
@@ -50,6 +79,10 @@ namespace Discord | |||
/// <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)"; | |||
} | |||
@@ -0,0 +1,50 @@ | |||
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"/> | |||
/// </summary> | |||
public enum ActionType | |||
{ | |||
GuildUpdated = 1, | |||
ChannelCreated = 10, | |||
ChannelUpdated = 11, | |||
ChannelDeleted = 12, | |||
OverwriteCreated = 13, | |||
OverwriteUpdated = 14, | |||
OverwriteDeleted = 15, | |||
Kick = 20, | |||
Prune = 21, | |||
Ban = 22, | |||
Unban = 23, | |||
MemberUpdated = 24, | |||
MemberRoleUpdated = 25, | |||
RoleCreated = 30, | |||
RoleUpdated = 31, | |||
RoleDeleted = 32, | |||
InviteCreated = 40, | |||
InviteUpdated = 41, | |||
InviteDeleted = 42, | |||
WebhookCreated = 50, | |||
WebhookUpdated = 51, | |||
WebhookDeleted = 52, | |||
EmojiCreated = 60, | |||
EmojiUpdated = 61, | |||
EmojiDeleted = 62, | |||
MessageDeleted = 72 | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
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"/> | |||
/// </summary> | |||
public interface IAuditLogData | |||
{ } | |||
} |
@@ -0,0 +1,34 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Represents an entry in an audit log | |||
/// </summary> | |||
public interface IAuditLogEntry : IEntity<ulong> | |||
{ | |||
/// <summary> | |||
/// The action which occured to create this entry | |||
/// </summary> | |||
ActionType Action { get; } | |||
/// <summary> | |||
/// The data for this entry. May be <see cref="null"/> if no data was available. | |||
/// </summary> | |||
IAuditLogData Data { get; } | |||
/// <summary> | |||
/// The user responsible for causing the changes | |||
/// </summary> | |||
IUser User { get; } | |||
/// <summary> | |||
/// The reason behind the change. May be <see cref="null"/> if no reason was provided. | |||
/// </summary> | |||
string Reason { get; } | |||
} | |||
} |
@@ -3,14 +3,7 @@ namespace Discord | |||
/// <summary> | |||
/// Properties that are used to modify an <see cref="IGuildChannel" /> with the specified changes. | |||
/// </summary> | |||
/// <example> | |||
/// <code lang="c#"> | |||
/// await (Context.Channel as ITextChannel)?.ModifyAsync(x => | |||
/// { | |||
/// x.Name = "do-not-enter"; | |||
/// }); | |||
/// </code> | |||
/// </example> | |||
/// <seealso cref="IGuildChannel.ModifyAsync"/> | |||
public class GuildChannelProperties | |||
{ | |||
/// <summary> | |||
@@ -24,7 +24,7 @@ namespace Discord | |||
/// Gets the parent ID (category) of this channel in the guild's channel list. | |||
/// </summary> | |||
/// <returns> | |||
/// The parent category ID associated with this channel, or <see langword="null"/> if none is set. | |||
/// The parent category ID associated with this channel, or <c>null</c> if none is set. | |||
/// </returns> | |||
ulong? CategoryId { get; } | |||
/// <summary> | |||
@@ -57,16 +57,16 @@ namespace Discord | |||
/// Creates a new invite to this channel. | |||
/// </summary> | |||
/// <param name="maxAge"> | |||
/// The time (in seconds) until the invite expires. Set to <see langword="null"/> to never expire. | |||
/// 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 <see langword="null"/> to have unlimited uses. | |||
/// The max amount of times this invite may be used. Set to <c>null</c> to have unlimited uses. | |||
/// </param> | |||
/// <param name="isTemporary"> | |||
/// If <see langword="true"/>, a user accepting this invite will be kicked from the guild after closing their client. | |||
/// If <c>true</c>, a user accepting this invite will be kicked from the guild after closing their client. | |||
/// </param> | |||
/// <param name="isUnique"> | |||
/// If <see langword="true"/>, don't try to reuse a similar invite (useful for creating many unique one time use invites). | |||
/// 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. | |||
@@ -86,12 +86,12 @@ namespace Discord | |||
Task ModifyAsync(Action<GuildChannelProperties> func, RequestOptions options = null); | |||
/// <summary> | |||
/// Gets the permission overwrite for a specific role, or <see langword="null"/> if one does not exist. | |||
/// Gets the permission overwrite for a specific role, or <c>null</c> if one does not exist. | |||
/// </summary> | |||
/// <param name="role">The role to get the overwrite from.</param> | |||
OverwritePermissions? GetPermissionOverwrite(IRole role); | |||
/// <summary> | |||
/// Gets the permission overwrite for a specific user, or <see langword="null"/> if one does not exist. | |||
/// Gets the permission overwrite for a specific user, or <c>null</c> if one does not exist. | |||
/// </summary> | |||
/// <param name="user">The user to get the overwrite from.</param> | |||
OverwritePermissions? GetPermissionOverwrite(IUser user); | |||
@@ -17,10 +17,13 @@ namespace Discord | |||
/// <param name="isTTS">Whether the message should be read aloud by Discord or not.</param> | |||
/// <param name="embed">The <see cref="EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable Task containing the message sent to the channel. | |||
/// </returns> | |||
Task<IUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); | |||
#if FILESYSTEM | |||
/// <summary> | |||
/// Sends a file to this message channel, with an optional caption. | |||
/// Sends a file to this message channel with an optional caption. | |||
/// </summary> | |||
/// <param name="filePath">The file path of the file.</param> | |||
/// <param name="text">The message to be sent.</param> | |||
@@ -32,10 +35,13 @@ namespace Discord | |||
/// upload the file and refer to the file with "attachment://filename.ext" in the | |||
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>. | |||
/// </remarks> | |||
/// <returns> | |||
/// An awaitable Task containing the message sent to the channel. | |||
/// </returns> | |||
Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); | |||
#endif | |||
/// <summary> | |||
/// Sends a file to this message channel, with an optional caption. | |||
/// Sends a file to this message channel with an optional caption. | |||
/// </summary> | |||
/// <param name="stream">The <see cref="Stream"/> of the file to be sent.</param> | |||
/// <param name="filename">The name of the attachment.</param> | |||
@@ -48,16 +54,19 @@ namespace Discord | |||
/// upload the file and refer to the file with "attachment://filename.ext" in the | |||
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>. | |||
/// </remarks> | |||
/// <returns> | |||
/// An awaitable Task containing the message sent to the channel. | |||
/// </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 <see langword="null"/> if not found. | |||
/// Gets a message from this message channel with the given id, or <c>null</c> if not found. | |||
/// </summary> | |||
/// <param name="id">The ID 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> | |||
/// The message gotten from either the cache or the download, or <see langword="null"/> if none is found. | |||
/// The message gotten from either the cache or the download, or <c>null</c> if none is found. | |||
/// </returns> | |||
Task<IMessage> GetMessageAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
@@ -108,7 +117,7 @@ namespace Discord | |||
/// </summary> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// A collection of messages. | |||
/// An awaitable Task containing a collection of messages. | |||
/// </returns> | |||
Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync(RequestOptions options = null); | |||
@@ -14,7 +14,7 @@ namespace Discord | |||
/// Determines whether the channel is NSFW. | |||
/// </summary> | |||
/// <returns> | |||
/// <see langword="true"/> if the channel has the NSFW flag enabled; otherwise, <see langword="false"/>. | |||
/// <c>true</c> if the channel has the NSFW flag enabled; otherwise, <c>false</c>. | |||
/// </returns> | |||
bool IsNsfw { get; } | |||
@@ -22,19 +22,29 @@ namespace Discord | |||
/// Gets the current topic for this text channel. | |||
/// </summary> | |||
/// <returns> | |||
/// The topic set in the channel, or <see langword="null"/> if none is set. | |||
/// The topic set in the channel, or <c>null</c> if none is set. | |||
/// </returns> | |||
string Topic { get; } | |||
/// <summary> | |||
/// Bulk-deletes multiple messages. | |||
/// </summary> | |||
/// <remarks> | |||
/// <note type="important"> | |||
/// 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> | |||
Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null); | |||
/// <summary> | |||
/// Bulk-deletes multiple messages. | |||
/// </summary> | |||
/// <remarks> | |||
/// <note type="important"> | |||
/// This method can only remove messages that are posted within 14 days! | |||
/// </note> | |||
/// </remarks> | |||
/// <param name="messageIds">The IDs of the messages to be bulk-deleted.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null); | |||
@@ -62,7 +72,7 @@ namespace Discord | |||
/// <param name="id">The ID of the webhook.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// A webhook associated with the <paramref name="id"/>, or <see langword="null"/> if not found. | |||
/// A webhook associated with the <paramref name="id"/>, or <c>null</c> if not found. | |||
/// </returns> | |||
Task<IWebhook> GetWebhookAsync(ulong id, RequestOptions options = null); | |||
/// <summary> | |||
@@ -14,7 +14,7 @@ namespace Discord | |||
int Bitrate { get; } | |||
/// <summary> | |||
/// Gets the max amount of users allowed to be connected to this channel at one time, or | |||
/// <see langword="null"/> if none is set. | |||
/// <c>null</c> if none is set. | |||
/// </summary> | |||
int? UserLimit { get; } | |||
@@ -1,8 +1,11 @@ | |||
using System; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Properties that are used to modify an <see cref="ITextChannel"/> with the specified changes. | |||
/// </summary> | |||
/// <seealso cref="ITextChannel.ModifyAsync(Action{TextChannelProperties}, RequestOptions)"/> | |||
public class TextChannelProperties : GuildChannelProperties | |||
{ | |||
/// <summary> | |||
@@ -10,7 +10,7 @@ namespace Discord | |||
/// </summary> | |||
public Optional<int> Bitrate { get; set; } | |||
/// <summary> | |||
/// Gets or sets the maximum number of users that can be present in a channel, or <see langword="null"/> if none. | |||
/// 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; } | |||
} | |||
@@ -5,6 +5,7 @@ namespace Discord | |||
/// <summary> | |||
/// Properties that are used to modify an <see cref="Emote" /> with the specified changes. | |||
/// </summary> | |||
/// <seealso cref="IGuild.ModifyEmoteAsync"/> | |||
public class EmoteProperties | |||
{ | |||
/// <summary> | |||
@@ -10,11 +10,11 @@ namespace Discord | |||
/// </summary> | |||
public Optional<bool> Enabled { get; set; } | |||
/// <summary> | |||
/// Sets the channel that the invite should place its users in, if not <see langword="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> | |||
/// Sets the channel the invite should place its users in, if not <see langword="null"/>. | |||
/// Sets the channel the invite should place its users in, if not <c>null</c>. | |||
/// </summary> | |||
public Optional<ulong?> ChannelId { get; set; } | |||
} | |||
@@ -3,28 +3,19 @@ namespace Discord | |||
/// <summary> | |||
/// Properties that are used to modify an <see cref="IGuild" /> with the specified changes. | |||
/// </summary> | |||
/// <example> | |||
/// <code lang="c#"> | |||
/// await Context.Guild.ModifyAsync(async x => | |||
/// { | |||
/// x.Name = "aaaaaah"; | |||
/// }); | |||
/// </code> | |||
/// </example> | |||
/// <see cref="T:Discord.IGuild" /> | |||
/// <see cref="IGuild.ModifyAsync"/> | |||
public class GuildProperties | |||
{ | |||
public Optional<string> Username { get; set; } | |||
/// <summary> | |||
/// Gets or sets the name of the Guild. | |||
/// Gets or sets the name of the guild. Must be within 100 characters. | |||
/// </summary> | |||
public Optional<string> Name { get; set; } | |||
/// <summary> | |||
/// Gets or sets the region for the Guild's voice connections. | |||
/// Gets or sets the region for the guild's voice connections. | |||
/// </summary> | |||
public Optional<IVoiceRegion> Region { get; set; } | |||
/// <summary> | |||
/// Gets or sets the ID of the region for the Guild's voice connections. | |||
/// Gets or sets the ID of the region for the guild's voice connections. | |||
/// </summary> | |||
public Optional<string> RegionId { get; set; } | |||
/// <summary> | |||
@@ -13,17 +13,23 @@ namespace Discord | |||
/// <summary> | |||
/// Gets the name of this guild. | |||
/// </summary> | |||
/// <returns> | |||
/// A string containing the name of this guild. | |||
/// </returns> | |||
string Name { get; } | |||
/// <summary> | |||
/// Gets the amount of time (in seconds) a user must be inactive in a voice channel for until they are | |||
/// automatically moved to the AFK voice channel, if one is set. | |||
/// automatically moved to the AFK voice channel. | |||
/// </summary> | |||
/// <returns> | |||
/// The amount of time in seconds for a user to be marked as inactive and moved into the AFK voice channel. | |||
/// </returns> | |||
int AFKTimeout { get; } | |||
/// <summary> | |||
/// Determines if this guild is embeddable (i.e. can use widget). | |||
/// </summary> | |||
/// <returns> | |||
/// <see langword="true"/> if this guild can be embedded via widgets; otherwise <see langword="false"/>. | |||
/// <c>true</c> if this guild can be embedded via widgets; otherwise <c>false</c>. | |||
/// </returns> | |||
bool IsEmbeddable { get; } | |||
/// <summary> | |||
@@ -34,38 +40,61 @@ namespace Discord | |||
/// Gets the level of Multi-Factor Authentication requirements a user must fulfill before being allowed to | |||
/// perform administrative actions in this guild. | |||
/// </summary> | |||
/// <returns> | |||
/// The level of MFA requirement. | |||
/// </returns> | |||
MfaLevel MfaLevel { get; } | |||
/// <summary> | |||
/// Gets the level of requirements a user must fulfill before being allowed to post messages in this guild. | |||
/// </summary> | |||
/// <returns> | |||
/// The level of requirements. | |||
/// </returns> | |||
VerificationLevel VerificationLevel { get; } | |||
/// <summary> | |||
/// Returns the ID of this guild's icon, or <see langword="null"/> if none is set. | |||
/// Gets the ID of this guild's icon. | |||
/// </summary> | |||
/// <returns> | |||
/// An identifier for the splash image; <c>null</c> if none is set. | |||
/// </returns> | |||
string IconId { get; } | |||
/// <summary> | |||
/// Returns the URL of this guild's icon, or <see langword="null"/> if none is set. | |||
/// Gets the URL of this guild's icon. | |||
/// </summary> | |||
/// <returns> | |||
/// A URL pointing to the guild's icon; <c>null</c> if none is set. | |||
/// </returns> | |||
string IconUrl { get; } | |||
/// <summary> | |||
/// Returns the ID of this guild's splash image, or <see langword="null"/> if none is set. | |||
/// Gets the ID of this guild's splash image. | |||
/// </summary> | |||
/// <returns> | |||
/// An identifier for the splash image; <c>null</c> if none is set. | |||
/// </returns> | |||
string SplashId { get; } | |||
/// <summary> | |||
/// Returns the URL of this guild's splash image, or <see langword="null"/> if none is set. | |||
/// Gets the URL of this guild's splash image. | |||
/// </summary> | |||
/// <returns> | |||
/// A URL pointing to the guild's splash image; <c>null</c> if none is set. | |||
/// </returns> | |||
string SplashUrl { get; } | |||
/// <summary> | |||
/// Determines if this guild is currently connected and ready to be used. | |||
/// </summary> | |||
/// <remarks> | |||
/// <note> | |||
/// This property only applies to a WebSocket-based client. | |||
/// </note> | |||
/// This boolean is used to determine if the guild is currently connected to the WebSocket and is ready to be used/accessed. | |||
/// </remarks> | |||
/// <returns> | |||
/// Returns <see langword="true"/> if this guild is currently connected and ready to be used. Only applies | |||
/// to the WebSocket client. | |||
/// <c>true</c> if this guild is currently connected and ready to be used; otherwise <c>false</c>. | |||
/// </returns> | |||
bool Available { get; } | |||
/// <summary> | |||
/// Gets the ID of the AFK voice channel for this guild, or <see langword="null"/> if none is set. | |||
/// Gets the ID of the AFK voice channel for this guild, or <c>null</c> if none is set. | |||
/// </summary> | |||
ulong? AFKChannelId { get; } | |||
/// <summary> | |||
@@ -73,11 +102,11 @@ namespace Discord | |||
/// </summary> | |||
ulong DefaultChannelId { get; } | |||
/// <summary> | |||
/// Gets the ID of the embed channel set in the widget settings of this guild, or <see langword="null"/> if none is set. | |||
/// Gets the ID of the embed channel set in the widget settings of this guild, or <c>null</c> if none is set. | |||
/// </summary> | |||
ulong? EmbedChannelId { get; } | |||
/// <summary> | |||
/// Gets the ID of the channel where randomized welcome messages are sent, or <see langword="null"/> if none is set. | |||
/// Gets the ID of the channel where randomized welcome messages are sent, or <c>null</c> if none is set. | |||
/// </summary> | |||
ulong? SystemChannelId { get; } | |||
/// <summary> | |||
@@ -143,7 +172,7 @@ namespace Discord | |||
/// </returns> | |||
Task ModifyEmbedAsync(Action<GuildEmbedProperties> func, RequestOptions options = null); | |||
/// <summary> | |||
/// Bulk modifies the order of channels in this guild. | |||
/// Bulk-modifies the order of channels in this guild. | |||
/// </summary> | |||
/// <param name="args">The properties used to modify the channel positions with.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
@@ -152,17 +181,21 @@ namespace Discord | |||
/// </returns> | |||
Task ReorderChannelsAsync(IEnumerable<ReorderChannelProperties> args, RequestOptions options = null); | |||
/// <summary> | |||
/// Bulk modifies the order of roles in this guild. | |||
/// Bulk-modifies the order of roles in this guild. | |||
/// </summary> | |||
/// <param name="args">The properties used to modify the role positions with.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
Task ReorderRolesAsync(IEnumerable<ReorderRoleProperties> args, RequestOptions options = null); | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/>. | |||
/// </returns> | |||
Task ReorderRolesAsync(IEnumerable<ReorderRoleProperties> args, RequestOptions options = null); | |||
/// <summary> | |||
/// Leaves this guild. If you are the owner, use <see cref="IDeletable.DeleteAsync" /> instead. | |||
/// Leaves this guild. | |||
/// </summary> | |||
/// <remarks> | |||
/// This method will make the currently logged-in user leave the guild. If the user is the owner, use | |||
/// <see cref="IDeletable.DeleteAsync" /> instead. | |||
/// </remarks> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/>. | |||
@@ -178,7 +211,25 @@ namespace Discord | |||
/// </returns> | |||
Task<IReadOnlyCollection<IBan>> GetBansAsync(RequestOptions options = null); | |||
/// <summary> | |||
/// Bans the provided user from this guild and optionally prunes their recent messages. | |||
/// Gets a ban object for a banned user. | |||
/// </summary> | |||
/// <param name="user">The banned user.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the ban object, which contains the user information and the | |||
/// reason for the ban; <c>null<c/> if the ban entry cannot be found. | |||
/// </returns> | |||
Task<IBan> GetBanAsync(IUser user, RequestOptions options = null); | |||
/// <summary> | |||
/// Gets a ban object for a banned user. | |||
/// </summary> | |||
/// <param name="userId">The snowflake identifier for the banned user.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the ban object, which contains the user information and the | |||
/// reason for the ban; <c>null<c/> if the ban entry cannot be found. | |||
/// </returns> | |||
Task<IBan> GetBanAsync(ulong userId, RequestOptions options = null); | |||
/// <summary> | |||
/// Bans the user from this guild and optionally prunes their recent messages. | |||
/// </summary> | |||
/// <param name="user">The user to ban.</param> | |||
/// <param name="pruneDays"> | |||
@@ -192,9 +243,9 @@ namespace Discord | |||
/// </returns> | |||
Task AddBanAsync(IUser user, int pruneDays = 0, string reason = null, RequestOptions options = null); | |||
/// <summary> | |||
/// Bans the provided user ID from this guild and optionally prunes their recent messages. | |||
/// Bans the user from this guild and optionally prunes their recent messages. | |||
/// </summary> | |||
/// <param name="userId">The ID of the user to ban.</param> | |||
/// <param name="userId">The snowflake ID of the user to ban.</param> | |||
/// <param name="pruneDays"> | |||
/// The number of days to remove messages from this user for - must be between [0, 7]. | |||
/// </param> | |||
@@ -245,7 +296,7 @@ namespace Discord | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the generic channel with the specified ID, or | |||
/// <see langword="null"/> if none is found. | |||
/// <c>null</c> if none is found. | |||
/// </returns> | |||
Task<IGuildChannel> GetChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
@@ -260,7 +311,7 @@ namespace Discord | |||
/// </returns> | |||
Task<IReadOnlyCollection<ITextChannel>> GetTextChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
/// Gets a text channel in this guild with the provided ID, or <see langword="null" /> if not found. | |||
/// Gets a text channel in this guild with the provided ID, or <c>null</c> if not found. | |||
/// </summary> | |||
/// <param name="id">The text channel ID.</param> | |||
/// <param name="mode"> | |||
@@ -269,7 +320,7 @@ namespace Discord | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the text channel with the specified ID, or | |||
/// <see langword="null"/> if none is found. | |||
/// <c>null</c> if none is found. | |||
/// </returns> | |||
Task<ITextChannel> GetTextChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
@@ -304,7 +355,7 @@ namespace Discord | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the voice channel with the specified ID, or | |||
/// <see langword="null"/> if none is found. | |||
/// <c>null</c> if none is found. | |||
/// </returns> | |||
Task<IVoiceChannel> GetVoiceChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
@@ -316,7 +367,7 @@ namespace Discord | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the AFK voice channel set within this guild, or | |||
/// <see langword="null"/> if none is set. | |||
/// <c>null</c> if none is set. | |||
/// </returns> | |||
Task<IVoiceChannel> GetAFKChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
@@ -328,7 +379,7 @@ namespace Discord | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the system channel within this guild, or | |||
/// <see langword="null" /> if none is set. | |||
/// <c>null</c> if none is set. | |||
/// </returns> | |||
Task<ITextChannel> GetSystemChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
@@ -340,7 +391,7 @@ namespace Discord | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the first viewable text channel in this guild, or | |||
/// <see langword="null"/> if none is found. | |||
/// <c>null</c> if none is found. | |||
/// </returns> | |||
Task<ITextChannel> GetDefaultChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
@@ -352,7 +403,7 @@ namespace Discord | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the embed channel set within the server's widget settings, or | |||
/// <see langword="null"/> if none is set. | |||
/// <c>null</c> if none is set. | |||
/// </returns> | |||
Task<IGuildChannel> GetEmbedChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
@@ -389,12 +440,19 @@ namespace Discord | |||
/// <summary> | |||
/// Gets a collection of all invites to this guild. | |||
/// </summary> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing a collection of invites found within this guild. | |||
/// </returns> | |||
Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null); | |||
/// <summary> | |||
/// Gets the role in this guild with the provided ID, or <see langword="null"/> if not found. | |||
/// Gets a role in this guild. | |||
/// </summary> | |||
/// <param name="id">The role ID.</param> | |||
/// <returns> | |||
/// A role that matches the provided snowflake identifier; <c>null</c> if none is found. | |||
/// </returns> | |||
IRole GetRole(ulong id); | |||
/// <summary> | |||
/// Creates a new role with the provided name. | |||
@@ -412,25 +470,30 @@ namespace Discord | |||
/// <summary> | |||
/// Gets a collection of all users in this guild. | |||
/// </summary> | |||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from | |||
/// cache.</param> | |||
/// <remarks> | |||
/// This method retrieves all users found within this guild. | |||
/// <note> | |||
/// This may return an incomplete list on the WebSocket implementation. | |||
/// </note> | |||
/// </remarks> | |||
/// <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> | |||
/// An awaitable <see cref="Task"/> containing a collection of users found within this guild. | |||
/// </returns> | |||
/// <remarks> | |||
/// This may return an incomplete list on the WebSocket implementation because Discord only sends offline | |||
/// users on large guilds. | |||
/// </remarks> | |||
Task<IReadOnlyCollection<IGuildUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
/// Gets the user in this guild with the provided ID, or <see langword="null"/> if not found. | |||
/// Gets a user found in this guild. | |||
/// </summary> | |||
/// <param name="id">The user ID.</param> | |||
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param> | |||
/// <param name="id">The user ID to search for.</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> | |||
/// An awaitable <see cref="Task"/> containing the guild user with the specified ID, otherwise <see langword="null"/>. | |||
/// An awaitable <see cref="Task"/> containing the guild user with the specified ID; <c>null</c> if none is | |||
/// found. | |||
/// </returns> | |||
Task<IGuildUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
@@ -459,25 +522,33 @@ namespace Discord | |||
/// </returns> | |||
Task DownloadUsersAsync(); | |||
/// <summary> | |||
/// Removes all users from this guild if they have not logged on in a provided number of | |||
/// <paramref name="days" /> or, if <paramref name="simulate" /> is <see langword="true"/>, returns the | |||
/// number of users that would be removed. | |||
/// Prunes inactive users. | |||
/// </summary> | |||
/// <remarks> | |||
/// This method removes all users that have not logged on in the provided number of days or, if | |||
/// <paramref name="simulate"/> is <c>true</c>, returns the number of users that would be removed. | |||
/// </remarks> | |||
/// <param name="days">The number of days required for the users to be kicked.</param> | |||
/// <param name="simulate">Whether this prune action is a simulation.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the number of users to be or has been removed from this guild. | |||
/// An awaitable <see cref="Task" /> containing the number of users to be or has been removed from this | |||
/// guild. | |||
/// </returns> | |||
Task<int> PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null); | |||
/// <summary> Gets the specified number of audit log entries for this guild. </summary> | |||
Task<IReadOnlyCollection<IAuditLogEntry>> GetAuditLogAsync(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch, | |||
CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
/// Gets the webhook in this guild with the provided ID, or <see langword="null"/> if not found. | |||
/// Gets a webhook found within this guild. | |||
/// </summary> | |||
/// <param name="id">The webhook ID.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the webhook with the specified ID, otherwise <see langword="null"/>. | |||
/// An awaitable <see cref="Task"/> containing the webhook with the specified ID; <c>null</c> if none is | |||
/// found. | |||
/// </returns> | |||
Task<IWebhook> GetWebhookAsync(ulong id, RequestOptions options = null); | |||
/// <summary> | |||
@@ -495,7 +566,8 @@ namespace Discord | |||
/// <param name="id">The guild emote ID.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the emote found with the specified ID, or <see langword="null"/> if not found. | |||
/// An awaitable <see cref="Task"/> containing the emote found with the specified ID; <c>null</c> if none is | |||
/// found. | |||
/// </returns> | |||
Task<GuildEmote> GetEmoteAsync(ulong id, RequestOptions options = null); | |||
/// <summary> | |||
@@ -7,11 +7,11 @@ namespace Discord | |||
/// </summary> | |||
string Name { get; } | |||
/// <summary> | |||
/// Gets the icon URL associated with this guild, or <see langword="null"/> if one is not set. | |||
/// Gets the icon URL associated with this guild, or <c>null</c> if one is not set. | |||
/// </summary> | |||
string IconUrl { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if the current user owns this guild. | |||
/// Returns <c>true</c> if the current user owns this guild. | |||
/// </summary> | |||
bool IsOwner { get; } | |||
/// <summary> | |||
@@ -14,19 +14,19 @@ namespace Discord | |||
/// </summary> | |||
string Name { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this voice region is exclusive to VIP accounts. | |||
/// Returns <c>true</c> if this voice region is exclusive to VIP accounts. | |||
/// </summary> | |||
bool IsVip { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this voice region is the closest to your machine. | |||
/// Returns <c>true</c> if this voice region is the closest to your machine. | |||
/// </summary> | |||
bool IsOptimal { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this is a deprecated voice region (avoid switching to these). | |||
/// Returns <c>true</c> if this is a deprecated voice region (avoid switching to these). | |||
/// </summary> | |||
bool IsDeprecated { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this is a custom voice region (used for events/etc). | |||
/// Returns <c>true</c> if this is a custom voice region (used for events/etc). | |||
/// </summary> | |||
bool IsCustom { get; } | |||
} | |||
@@ -35,7 +35,7 @@ namespace Discord | |||
/// <paramref name="path" /> is a zero-length string, contains only white space, or contains one or more invalid | |||
/// characters as defined by <see cref="Path.GetInvalidPathChars" />. | |||
/// </exception> | |||
/// <exception cref="ArgumentNullException"><paramref name="path" /> is <see langword="null" />.</exception> | |||
/// <exception cref="ArgumentNullException"><paramref name="path" /> is <c>null</c>.</exception> | |||
/// <exception cref="PathTooLongException"> | |||
/// The specified path, file name, or both exceed the system-defined maximum length. For example, on | |||
/// Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 | |||
@@ -10,20 +10,20 @@ namespace Discord | |||
/// </summary> | |||
IUser Inviter { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this invite was revoked. | |||
/// Returns <c>true</c> if this invite was revoked. | |||
/// </summary> | |||
bool IsRevoked { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if users accepting this invite will be removed from the guild when they | |||
/// Returns <c>true</c> if users accepting this invite will be removed from the guild when they | |||
/// log off. | |||
/// </summary> | |||
bool IsTemporary { get; } | |||
/// <summary> | |||
/// Gets the time (in seconds) until the invite expires, or <see langword="null"/> if it never expires. | |||
/// Gets the time (in seconds) until the invite expires, or <c>null</c> if it never expires. | |||
/// </summary> | |||
int? MaxAge { get; } | |||
/// <summary> | |||
/// Gets the max amount of times this invite may be used, or <see langword="null"/> if there is no limit. | |||
/// Gets the max amount of times this invite may be used, or <c>null</c> if there is no limit. | |||
/// </summary> | |||
int? MaxUses { get; } | |||
/// <summary> | |||
@@ -3,7 +3,7 @@ using System.Diagnostics; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Represents a author field of an <see cref="Embed" />. | |||
/// A author field of an <see cref="Embed" />. | |||
/// </summary> | |||
[DebuggerDisplay("{DebuggerDisplay,nq}")] | |||
public struct EmbedAuthor | |||
@@ -106,7 +106,7 @@ namespace Discord | |||
/// <summary> Gets or sets the list of <see cref="EmbedFieldBuilder"/> of an <see cref="Embed"/>. </summary> | |||
/// <exception cref="ArgumentNullException" accessor="set">An embed builder's fields collection is set to | |||
/// <see langword="null"/>.</exception> | |||
/// <c>null</c>.</exception> | |||
/// <exception cref="ArgumentException" accessor="set">Description length exceeds <see cref="MaxFieldCount"/>. | |||
/// </exception> | |||
/// <returns> The list of existing <see cref="EmbedFieldBuilder"/>.</returns> | |||
@@ -125,28 +125,28 @@ namespace Discord | |||
/// Gets or sets the timestamp of an <see cref="Embed" />. | |||
/// </summary> | |||
/// <returns> | |||
/// The timestamp of the embed, or <see langword="null" /> if none is set. | |||
/// The timestamp of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
public DateTimeOffset? Timestamp { get; set; } | |||
/// <summary> | |||
/// Gets or sets the sidebar color of an <see cref="Embed" />. | |||
/// </summary> | |||
/// <returns> | |||
/// The color of the embed, or <see langword="null" /> if none is set. | |||
/// The color of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
public Color? Color { get; set; } | |||
/// <summary> | |||
/// Gets or sets the <see cref="EmbedAuthorBuilder" /> of an <see cref="Embed" />. | |||
/// </summary> | |||
/// <returns> | |||
/// The author field builder of the embed, or <see langword="null" /> if none is set. | |||
/// The author field builder of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
public EmbedAuthorBuilder Author { get; set; } | |||
/// <summary> | |||
/// Gets or sets the <see cref="EmbedFooterBuilder" /> of an <see cref="Embed" />. | |||
/// </summary> | |||
/// <returns> | |||
/// The footer field builder of the embed, or <see langword="null" /> if none is set. | |||
/// The footer field builder of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
public EmbedFooterBuilder Footer { get; set; } | |||
@@ -438,7 +438,6 @@ namespace Discord | |||
{ | |||
private string _name; | |||
private string _value; | |||
private EmbedField _field; | |||
/// <summary> | |||
/// Gets the maximum field length for name allowed by Discord. | |||
/// </summary> | |||
@@ -452,7 +451,7 @@ namespace Discord | |||
/// Gets or sets the field name. | |||
/// </summary> | |||
/// <exception cref="ArgumentException"> | |||
/// <para>Field name is <see langword="null" />, empty or entirely whitespace.</para> | |||
/// <para>Field name is <c>null</c>, empty or entirely whitespace.</para> | |||
/// <para><c>- or -</c></para> | |||
/// <para>Field name length exceeds <see cref="MaxFieldNameLength"/>.</para> | |||
/// </exception> | |||
@@ -474,7 +473,7 @@ namespace Discord | |||
/// Gets or sets the field value. | |||
/// </summary> | |||
/// <exception cref="ArgumentException" accessor="set"> | |||
/// <para>Field value is <see langword="null" />, empty or entirely whitespace.</para> | |||
/// <para>Field value is <c>null</c>, empty or entirely whitespace.</para> | |||
/// <para><c>- or -</c></para> | |||
/// <para>Field value length exceeds <see cref="MaxFieldValueLength"/>.</para> | |||
/// </exception> | |||
@@ -540,7 +539,7 @@ namespace Discord | |||
/// The current builder. | |||
/// </returns> | |||
/// <exception cref="ArgumentException"> | |||
/// <para><see cref="Name"/> or <see cref="Value"/> is <see langword="null" />, empty or entirely whitespace.</para> | |||
/// <para><see cref="Name"/> or <see cref="Value"/> is <c>null</c>, empty or entirely whitespace.</para> | |||
/// <para><c>- or -</c></para> | |||
/// <para><see cref="Name"/> or <see cref="Value"/> exceeds the maximum length allowed by Discord.</para> | |||
/// </exception> | |||
@@ -3,7 +3,7 @@ using System.Diagnostics; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Represents a field for an <see cref="Embed" />. | |||
/// A field for an <see cref="Embed" />. | |||
/// </summary> | |||
[DebuggerDisplay("{DebuggerDisplay,nq}")] | |||
public struct EmbedField | |||
@@ -13,11 +13,11 @@ namespace Discord | |||
/// </summary> | |||
public string Url { get; } | |||
/// <summary> | |||
/// Gets the height of the video, or <see langword="null"/> if none. | |||
/// Gets the height of the video, or <c>null</c> if none. | |||
/// </summary> | |||
public int? Height { get; } | |||
/// <summary> | |||
/// Gets the weight of the video, or <see langword="null"/> if none. | |||
/// Gets the weight of the video, or <c>null</c> if none. | |||
/// </summary> | |||
public int? Width { get; } | |||
@@ -6,33 +6,54 @@ namespace Discord | |||
public interface IAttachment | |||
{ | |||
/// <summary> | |||
/// Gets the snowflake ID of the attachment. | |||
/// Gets the ID of this attachment. | |||
/// </summary> | |||
/// <returns> | |||
/// A snowflake ID associated with this attachment. | |||
/// </returns> | |||
ulong Id { get; } | |||
/// <summary> | |||
/// Gets the filename of the attachment. | |||
/// Gets the filename of this attachment. | |||
/// </summary> | |||
/// <returns> | |||
/// A string containing the full filename of this attachment (e.g. textFile.txt). | |||
/// </returns> | |||
string Filename { get; } | |||
/// <summary> | |||
/// Gets the URL of the attachment. | |||
/// Gets the URL of this attachment. | |||
/// </summary> | |||
/// <returns> | |||
/// A string containing the URL of this attachment. | |||
/// </returns> | |||
string Url { get; } | |||
/// <summary> | |||
/// Gets the proxied URL of the attachment. | |||
/// Gets a proxied URL of this attachment. | |||
/// </summary> | |||
/// <returns> | |||
/// A string containing the proxied URL of this attachment. | |||
/// </returns> | |||
string ProxyUrl { get; } | |||
/// <summary> | |||
/// Gets the file size of the attachment. | |||
/// Gets the file size of this attachment. | |||
/// </summary> | |||
/// <returns> | |||
/// The size of this attachment in bytes. | |||
/// </returns> | |||
int Size { get; } | |||
/// <summary> | |||
/// Gets the height of the attachment if it is an image, or return <see langword="null" /> when it is not. | |||
/// Gets the height of this attachment. | |||
/// </summary> | |||
/// <returns> | |||
/// The height of this attachment if it is a picture; otherwise <c>null</c>. | |||
/// </returns> | |||
int? Height { get; } | |||
/// <summary> | |||
/// Gets the width of the attachment if it is an image, or return <see langword="null" /> when it is not. | |||
/// Gets the width of this attachment. | |||
/// </summary> | |||
/// <returns> | |||
/// The width of this attachment if it is a picture; otherwise <c>null</c>. | |||
/// </returns> | |||
int? Width { get; } | |||
} | |||
} |
@@ -9,56 +9,96 @@ namespace Discord | |||
public interface IEmbed | |||
{ | |||
/// <summary> | |||
/// Gets the title URL of the embed. | |||
/// Gets the title URL of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// A string containing the URL set in a title of the embed. | |||
/// </returns> | |||
string Url { get; } | |||
/// <summary> | |||
/// Gets the title of the embed. | |||
/// Gets the title of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The title of the embed. | |||
/// </returns> | |||
string Title { get; } | |||
/// <summary> | |||
/// Gets the description of the embed. | |||
/// Gets the description of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The description field of the embed. | |||
/// </returns> | |||
string Description { get; } | |||
/// <summary> | |||
/// Gets the type of the embed. | |||
/// Gets the type of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The type of the embed. | |||
/// </returns> | |||
EmbedType Type { get; } | |||
/// <summary> | |||
/// Gets the timestamp of the embed, or <see langword="null" /> if none is set. | |||
/// Gets the timestamp of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// A <see cref="DateTimeOffset"/> based on the timestamp present at the bottom left of the embed, or | |||
/// <c>null</c> if none is set. | |||
/// </returns> | |||
DateTimeOffset? Timestamp { get; } | |||
/// <summary> | |||
/// Gets the sidebar color of the embed, or <see langword="null" /> if none is set. | |||
/// Gets the color of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The color of the embed present on the side of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
Color? Color { get; } | |||
/// <summary> | |||
/// Gets the image of the embed, or <see langword="null" /> if none is set. | |||
/// Gets the image of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The image of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
EmbedImage? Image { get; } | |||
/// <summary> | |||
/// Gets the video of the embed, or <see langword="null" /> if none is set. | |||
/// Gets the video of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The video of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
EmbedVideo? Video { get; } | |||
/// <summary> | |||
/// Gets the author field of the embed, or <see langword="null" /> if none is set. | |||
/// Gets the author field of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The author field of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
EmbedAuthor? Author { get; } | |||
/// <summary> | |||
/// Gets the footer field of the embed, or <see langword="null" /> if none is set. | |||
/// Gets the footer field of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The author field of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
EmbedFooter? Footer { get; } | |||
/// <summary> | |||
/// Gets the provider of the embed, or <see langword="null" /> if none is set. | |||
/// Gets the provider of this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The source of the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
EmbedProvider? Provider { get; } | |||
/// <summary> | |||
/// Gets the thumbnail featured in the embed, or <see langword="null" /> if none is set. | |||
/// Gets the thumbnail featured in this embed. | |||
/// </summary> | |||
/// <returns> | |||
/// The thumbnail featured in the embed, or <c>null</c> if none is set. | |||
/// </returns> | |||
EmbedThumbnail? Thumbnail { get; } | |||
/// <summary> | |||
/// Gets the fields of the embed. | |||
/// </summary> | |||
/// <returns> | |||
/// An array of the fields of the embed. | |||
/// </returns> | |||
ImmutableArray<EmbedField> Fields { get; } | |||
} | |||
} |
@@ -17,11 +17,11 @@ namespace Discord | |||
/// </summary> | |||
MessageSource Source { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this message was sent as a text-to-speech message. | |||
/// Returns <c>true</c> if this message was sent as a text-to-speech message. | |||
/// </summary> | |||
bool IsTTS { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this message was added to its channel's pinned messages. | |||
/// Returns <c>true</c> if this message was added to its channel's pinned messages. | |||
/// </summary> | |||
bool IsPinned { get; } | |||
/// <summary> | |||
@@ -31,14 +31,20 @@ namespace Discord | |||
/// <summary> | |||
/// Gets the time this message was sent. | |||
/// </summary> | |||
/// <returns> | |||
/// Time of when the message was sent. | |||
/// </returns> | |||
DateTimeOffset Timestamp { get; } | |||
/// <summary> | |||
/// Gets the time of this message's last edit, or <see langword="null" /> if none is set. | |||
/// Gets the time of this message's last edit. | |||
/// </summary> | |||
/// <returns> | |||
/// Time of when the message was last edited; <c>null</c> when the message is never edited. | |||
/// </returns> | |||
DateTimeOffset? EditedTimestamp { get; } | |||
/// <summary> | |||
/// Gets the channel this message was sent to. | |||
/// Gets the source channel of the message. | |||
/// </summary> | |||
IMessageChannel Channel { get; } | |||
/// <summary> | |||
@@ -49,10 +55,16 @@ namespace Discord | |||
/// <summary> | |||
/// Returns all attachments included in this message. | |||
/// </summary> | |||
/// <returns> | |||
/// Collection of attachments. | |||
/// </returns> | |||
IReadOnlyCollection<IAttachment> Attachments { get; } | |||
/// <summary> | |||
/// Returns all embeds included in this message. | |||
/// </summary> | |||
/// <returns> | |||
/// Collection of embed objects. | |||
/// </returns> | |||
IReadOnlyCollection<IEmbed> Embeds { get; } | |||
/// <summary> | |||
/// Returns all tags included in this message's content. | |||
@@ -61,14 +73,23 @@ namespace Discord | |||
/// <summary> | |||
/// Returns the IDs of channels mentioned in this message. | |||
/// </summary> | |||
/// <returns> | |||
/// Collection of channel IDs. | |||
/// </returns> | |||
IReadOnlyCollection<ulong> MentionedChannelIds { get; } | |||
/// <summary> | |||
/// Returns the IDs of roles mentioned in this message. | |||
/// </summary> | |||
/// <returns> | |||
/// Collection of role IDs. | |||
/// </returns> | |||
IReadOnlyCollection<ulong> MentionedRoleIds { get; } | |||
/// <summary> | |||
/// Returns the IDs of users mentioned in this message. | |||
/// </summary> | |||
/// <returns> | |||
/// Collection of user IDs. | |||
/// </returns> | |||
IReadOnlyCollection<ulong> MentionedUserIds { get; } | |||
} | |||
} |
@@ -1,7 +1,7 @@ | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Represents a message sent by the system. | |||
/// Represents a generic message sent by the system. | |||
/// </summary> | |||
public interface ISystemMessage : IMessage | |||
{ | |||
@@ -5,7 +5,7 @@ using System.Threading.Tasks; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Represents a Discord message object. | |||
/// Represents a generic message sent by a user. | |||
/// </summary> | |||
public interface IUserMessage : IMessage | |||
{ | |||
@@ -4,23 +4,9 @@ namespace Discord | |||
/// Properties that are used to modify an <see cref="IUserMessage" /> with the specified changes. | |||
/// </summary> | |||
/// <remarks> | |||
/// The content of a message can be cleared with <see cref="string.Empty"/> if and only if an <see cref="Discord.Embed"/> is present. | |||
/// The content of a message can be cleared with <see cref="System.String.Empty"/> if and only if an <see cref="Discord.Embed"/> is present. | |||
/// </remarks> | |||
/// <example> | |||
/// <code lang="c#"> | |||
/// var message = await ReplyAsync("abc"); | |||
/// await message.ModifyAsync(x => | |||
/// { | |||
/// x.Content = ""; | |||
/// x.Embed = new EmbedBuilder() | |||
/// .WithColor(new Color(40, 40, 120)) | |||
/// .WithAuthor(a => a.Name = "foxbot") | |||
/// .WithTitle("Embed!") | |||
/// .WithDescription("This is an embed.") | |||
/// .Build(); | |||
/// }); | |||
/// </code> | |||
/// </example> | |||
/// <seealso cref="IUserMessage.ModifyAsync"/> | |||
public class MessageProperties | |||
{ | |||
/// <summary> | |||
@@ -11,7 +11,7 @@ namespace Discord | |||
public int ReactionCount { get; internal set; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if the current user has used this reaction. | |||
/// Returns <c>true</c> if the current user has used this reaction. | |||
/// </summary> | |||
public bool IsMe { get; internal set; } | |||
} | |||
@@ -1,21 +1,39 @@ | |||
namespace Discord | |||
{ | |||
/// <summary> Specifies the handling type the tag should use. </summary> | |||
/// <summary> | |||
/// Specifies the handling type the tag should use. | |||
/// </summary> | |||
/// <seealso cref="MentionUtils"/> | |||
/// <seealso cref="IUserMessage.Resolve"/> | |||
public enum TagHandling | |||
{ | |||
/// <summary> Tag handling is ignored. </summary> | |||
Ignore = 0, //<@53905483156684800> -> <@53905483156684800> | |||
/// <summary> Removes the tag entirely. </summary> | |||
Remove, //<@53905483156684800> -> | |||
/// <summary> Resolves to username (e.g. @User). </summary> | |||
Name, //<@53905483156684800> -> @Voltana | |||
/// <summary> Resolves to username without mention prefix (e.g. User). </summary> | |||
NameNoPrefix, //<@53905483156684800> -> Voltana | |||
/// <summary> Resolves to username with discriminator value. (e.g. @User#0001). </summary> | |||
FullName, //<@53905483156684800> -> @Voltana#8252 | |||
/// <summary> Resolves to username with discriminator value without mention prefix. (e.g. User#0001). </summary> | |||
FullNameNoPrefix, //<@53905483156684800> -> Voltana#8252 | |||
/// <summary> Sanitizes the tag. </summary> | |||
Sanitize //<@53905483156684800> -> <@53905483156684800> (w/ nbsp) | |||
/// <summary> | |||
/// Tag handling is ignored (e.g. <@53905483156684800> -> <@53905483156684800>). | |||
/// </summary> | |||
Ignore = 0, | |||
/// <summary> | |||
/// Removes the tag entirely. | |||
/// </summary> | |||
Remove, | |||
/// <summary> | |||
/// Resolves to username (e.g. <@53905483156684800> -> @Voltana). | |||
/// </summary> | |||
Name, | |||
/// <summary> | |||
/// Resolves to username without mention prefix (e.g. <@53905483156684800> -> Voltana). | |||
/// </summary> | |||
NameNoPrefix, | |||
/// <summary> | |||
/// Resolves to username with discriminator value. (e.g. <@53905483156684800> -> @Voltana#8252). | |||
/// </summary> | |||
FullName, | |||
/// <summary> | |||
/// Resolves to username with discriminator value without mention prefix. (e.g. <@53905483156684800> -> Voltana#8252). | |||
/// </summary> | |||
FullNameNoPrefix, | |||
/// <summary> | |||
/// Sanitizes the tag (e.g. <@53905483156684800> -> <@53905483156684800> (w/ nbsp)). | |||
/// </summary> | |||
Sanitize | |||
} | |||
} |
@@ -12,7 +12,7 @@ namespace Discord | |||
/// <summary> Gets a <see cref="ChannelPermissions"/> that grants all permissions for text channels. </summary> | |||
public static readonly ChannelPermissions Text = new ChannelPermissions(0b01100_0000000_1111111110001_010001); | |||
/// <summary> Gets a <see cref="ChannelPermissions"/> that grants all permissions for voice channels. </summary> | |||
public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000000000_010001); | |||
public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000010000_010001); | |||
/// <summary> Gets a <see cref="ChannelPermissions"/> that grants all permissions for category channels. </summary> | |||
public static readonly ChannelPermissions Category = new ChannelPermissions(0b01100_1111110_1111111110001_010001); | |||
/// <summary> Gets a <see cref="ChannelPermissions"/> that grants all permissions for direct message channels. </summary> | |||
@@ -37,52 +37,52 @@ namespace Discord | |||
/// <summary> Gets a packed value representing all the permissions in this <see cref="ChannelPermissions"/>. </summary> | |||
public ulong RawValue { get; } | |||
/// <summary> If <see langword="true"/>, a user may create invites. </summary> | |||
/// <summary> If <c>true</c>, a user may create invites. </summary> | |||
public bool CreateInstantInvite => Permissions.GetValue(RawValue, ChannelPermission.CreateInstantInvite); | |||
/// <summary> If <see langword="true"/>, a user may create, delete and modify this channel. </summary> | |||
/// <summary> If <c>true</c>, a user may create, delete and modify this channel. </summary> | |||
public bool ManageChannel => Permissions.GetValue(RawValue, ChannelPermission.ManageChannels); | |||
/// <summary> If <see langword="true"/>, a user may add reactions. </summary> | |||
/// <summary> If <c>true</c>, a user may add reactions. </summary> | |||
public bool AddReactions => Permissions.GetValue(RawValue, ChannelPermission.AddReactions); | |||
/// <summary> If <see langword="true"/>, a user may join channels. </summary> | |||
/// <summary> If <c>true</c>, a user may join channels. </summary> | |||
[Obsolete("Use ViewChannel instead.")] | |||
public bool ReadMessages => ViewChannel; | |||
/// <summary> If <see langword="true"/>, a user may view channels. </summary> | |||
/// <summary> If <c>true</c>, a user may view channels. </summary> | |||
public bool ViewChannel => Permissions.GetValue(RawValue, ChannelPermission.ViewChannel); | |||
/// <summary> If <see langword="true"/>, a user may send messages. </summary> | |||
/// <summary> If <c>true</c>, a user may send messages. </summary> | |||
public bool SendMessages => Permissions.GetValue(RawValue, ChannelPermission.SendMessages); | |||
/// <summary> If <see langword="true"/>, a user may send text-to-speech messages. </summary> | |||
/// <summary> If <c>true</c>, a user may send text-to-speech messages. </summary> | |||
public bool SendTTSMessages => Permissions.GetValue(RawValue, ChannelPermission.SendTTSMessages); | |||
/// <summary> If <see langword="true"/>, a user may delete messages. </summary> | |||
/// <summary> If <c>true</c>, a user may delete messages. </summary> | |||
public bool ManageMessages => Permissions.GetValue(RawValue, ChannelPermission.ManageMessages); | |||
/// <summary> If <see langword="true"/>, Discord will auto-embed links sent by this user. </summary> | |||
/// <summary> If <c>true</c>, Discord will auto-embed links sent by this user. </summary> | |||
public bool EmbedLinks => Permissions.GetValue(RawValue, ChannelPermission.EmbedLinks); | |||
/// <summary> If <see langword="true"/>, a user may send files. </summary> | |||
/// <summary> If <c>true</c>, a user may send files. </summary> | |||
public bool AttachFiles => Permissions.GetValue(RawValue, ChannelPermission.AttachFiles); | |||
/// <summary> If <see langword="true"/>, a user may read previous messages. </summary> | |||
/// <summary> If <c>true</c>, a user may read previous messages. </summary> | |||
public bool ReadMessageHistory => Permissions.GetValue(RawValue, ChannelPermission.ReadMessageHistory); | |||
/// <summary> If <see langword="true"/>, a user may mention @everyone. </summary> | |||
/// <summary> If <c>true</c>, a user may mention @everyone. </summary> | |||
public bool MentionEveryone => Permissions.GetValue(RawValue, ChannelPermission.MentionEveryone); | |||
/// <summary> If <see langword="true"/>, a user may use custom emoji from other guilds. </summary> | |||
/// <summary> If <c>true</c>, a user may use custom emoji from other guilds. </summary> | |||
public bool UseExternalEmojis => Permissions.GetValue(RawValue, ChannelPermission.UseExternalEmojis); | |||
/// <summary> If <see langword="true"/>, a user may connect to a voice channel. </summary> | |||
/// <summary> If <c>true</c>, a user may connect to a voice channel. </summary> | |||
public bool Connect => Permissions.GetValue(RawValue, ChannelPermission.Connect); | |||
/// <summary> If <see langword="true"/>, a user may speak in a voice channel. </summary> | |||
/// <summary> If <c>true</c>, a user may speak in a voice channel. </summary> | |||
public bool Speak => Permissions.GetValue(RawValue, ChannelPermission.Speak); | |||
/// <summary> If <see langword="true"/>, a user may mute users. </summary> | |||
/// <summary> If <c>true</c>, a user may mute users. </summary> | |||
public bool MuteMembers => Permissions.GetValue(RawValue, ChannelPermission.MuteMembers); | |||
/// <summary> If <see langword="true"/>, a user may deafen users. </summary> | |||
/// <summary> If <c>true</c>, a user may deafen users. </summary> | |||
public bool DeafenMembers => Permissions.GetValue(RawValue, ChannelPermission.DeafenMembers); | |||
/// <summary> If <see langword="true"/>, a user may move other users between voice channels. </summary> | |||
/// <summary> If <c>true</c>, a user may move other users between voice channels. </summary> | |||
public bool MoveMembers => Permissions.GetValue(RawValue, ChannelPermission.MoveMembers); | |||
/// <summary> If <see langword="true"/>, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
/// <summary> If <c>true</c>, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
public bool UseVAD => Permissions.GetValue(RawValue, ChannelPermission.UseVAD); | |||
/// <summary> If <see langword="true"/>, a user may adjust role permissions. This also implictly grants all other permissions. </summary> | |||
/// <summary> If <c>true</c>, a user may adjust role permissions. This also implictly grants all other permissions. </summary> | |||
public bool ManageRoles => Permissions.GetValue(RawValue, ChannelPermission.ManageRoles); | |||
/// <summary> If <see langword="true"/>, a user may edit the webhooks for this channel. </summary> | |||
/// <summary> If <c>true</c>, a user may edit the webhooks for this channel. </summary> | |||
public bool ManageWebhooks => Permissions.GetValue(RawValue, ChannelPermission.ManageWebhooks); | |||
/// <summary> Creates a new <see cref="ChannelPermissions"/> with the provided packed value. </summary> | |||
@@ -16,65 +16,65 @@ namespace Discord | |||
/// <summary> Gets a packed value representing all the permissions in this <see cref="GuildPermissions"/>. </summary> | |||
public ulong RawValue { get; } | |||
/// <summary> If <see langword="true"/>, a user may create invites. </summary> | |||
/// <summary> If <c>true</c>, a user may create invites. </summary> | |||
public bool CreateInstantInvite => Permissions.GetValue(RawValue, GuildPermission.CreateInstantInvite); | |||
/// <summary> If <see langword="true"/>, a user may ban users from the guild. </summary> | |||
/// <summary> If <c>true</c>, a user may ban users from the guild. </summary> | |||
public bool BanMembers => Permissions.GetValue(RawValue, GuildPermission.BanMembers); | |||
/// <summary> If <see langword="true"/>, a user may kick users from the guild. </summary> | |||
/// <summary> If <c>true</c>, a user may kick users from the guild. </summary> | |||
public bool KickMembers => Permissions.GetValue(RawValue, GuildPermission.KickMembers); | |||
/// <summary> If <see langword="true"/>, a user is granted all permissions, and cannot have them revoked via channel permissions. </summary> | |||
/// <summary> If <c>true</c>, a user is granted all permissions, and cannot have them revoked via channel permissions. </summary> | |||
public bool Administrator => Permissions.GetValue(RawValue, GuildPermission.Administrator); | |||
/// <summary> If <see langword="true"/>, a user may create, delete and modify channels. </summary> | |||
/// <summary> If <c>true</c>, a user may create, delete and modify channels. </summary> | |||
public bool ManageChannels => Permissions.GetValue(RawValue, GuildPermission.ManageChannels); | |||
/// <summary> If <see langword="true"/>, a user may adjust guild properties. </summary> | |||
/// <summary> If <c>true</c>, a user may adjust guild properties. </summary> | |||
public bool ManageGuild => Permissions.GetValue(RawValue, GuildPermission.ManageGuild); | |||
/// <summary> If <see langword="true"/>, a user may add reactions. </summary> | |||
/// <summary> If <c>true</c>, a user may add reactions. </summary> | |||
public bool AddReactions => Permissions.GetValue(RawValue, GuildPermission.AddReactions); | |||
/// <summary> If <see langword="true"/>, a user may view the audit log. </summary> | |||
/// <summary> If <c>true</c>, a user may view the audit log. </summary> | |||
public bool ViewAuditLog => Permissions.GetValue(RawValue, GuildPermission.ViewAuditLog); | |||
/// <summary> If <see langword="true"/>, a user may join channels. </summary> | |||
/// <summary> If <c>true</c>, a user may join channels. </summary> | |||
public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages); | |||
/// <summary> If <see langword="true"/>, a user may send messages. </summary> | |||
/// <summary> If <c>true</c>, a user may send messages. </summary> | |||
public bool SendMessages => Permissions.GetValue(RawValue, GuildPermission.SendMessages); | |||
/// <summary> If <see langword="true"/>, a user may send text-to-speech messages. </summary> | |||
/// <summary> If <c>true</c>, a user may send text-to-speech messages. </summary> | |||
public bool SendTTSMessages => Permissions.GetValue(RawValue, GuildPermission.SendTTSMessages); | |||
/// <summary> If <see langword="true"/>, a user may delete messages. </summary> | |||
/// <summary> If <c>true</c>, a user may delete messages. </summary> | |||
public bool ManageMessages => Permissions.GetValue(RawValue, GuildPermission.ManageMessages); | |||
/// <summary> If <see langword="true"/>, Discord will auto-embed links sent by this user. </summary> | |||
/// <summary> If <c>true</c>, Discord will auto-embed links sent by this user. </summary> | |||
public bool EmbedLinks => Permissions.GetValue(RawValue, GuildPermission.EmbedLinks); | |||
/// <summary> If <see langword="true"/>, a user may send files. </summary> | |||
/// <summary> If <c>true</c>, a user may send files. </summary> | |||
public bool AttachFiles => Permissions.GetValue(RawValue, GuildPermission.AttachFiles); | |||
/// <summary> If <see langword="true"/>, a user may read previous messages. </summary> | |||
/// <summary> If <c>true</c>, a user may read previous messages. </summary> | |||
public bool ReadMessageHistory => Permissions.GetValue(RawValue, GuildPermission.ReadMessageHistory); | |||
/// <summary> If <see langword="true"/>, a user may mention @everyone. </summary> | |||
/// <summary> If <c>true</c>, a user may mention @everyone. </summary> | |||
public bool MentionEveryone => Permissions.GetValue(RawValue, GuildPermission.MentionEveryone); | |||
/// <summary> If <see langword="true"/>, a user may use custom emoji from other guilds. </summary> | |||
/// <summary> If <c>true</c>, a user may use custom emoji from other guilds. </summary> | |||
public bool UseExternalEmojis => Permissions.GetValue(RawValue, GuildPermission.UseExternalEmojis); | |||
/// <summary> If <see langword="true"/>, a user may connect to a voice channel. </summary> | |||
/// <summary> If <c>true</c>, a user may connect to a voice channel. </summary> | |||
public bool Connect => Permissions.GetValue(RawValue, GuildPermission.Connect); | |||
/// <summary> If <see langword="true"/>, a user may speak in a voice channel. </summary> | |||
/// <summary> If <c>true</c>, a user may speak in a voice channel. </summary> | |||
public bool Speak => Permissions.GetValue(RawValue, GuildPermission.Speak); | |||
/// <summary> If <see langword="true"/>, a user may mute users. </summary> | |||
/// <summary> If <c>true</c>, a user may mute users. </summary> | |||
public bool MuteMembers => Permissions.GetValue(RawValue, GuildPermission.MuteMembers); | |||
/// <summary> If <see langword="true"/>, a user may deafen users. </summary> | |||
/// <summary> If <c>true</c>, a user may deafen users. </summary> | |||
public bool DeafenMembers => Permissions.GetValue(RawValue, GuildPermission.DeafenMembers); | |||
/// <summary> If <see langword="true"/>, a user may move other users between voice channels. </summary> | |||
/// <summary> If <c>true</c>, a user may move other users between voice channels. </summary> | |||
public bool MoveMembers => Permissions.GetValue(RawValue, GuildPermission.MoveMembers); | |||
/// <summary> If <see langword="true"/>, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
/// <summary> If <c>true</c>, a user may use voice-activity-detection rather than push-to-talk. </summary> | |||
public bool UseVAD => Permissions.GetValue(RawValue, GuildPermission.UseVAD); | |||
/// <summary> If <see langword="true"/>, a user may change their own nickname. </summary> | |||
/// <summary> If <c>true</c>, a user may change their own nickname. </summary> | |||
public bool ChangeNickname => Permissions.GetValue(RawValue, GuildPermission.ChangeNickname); | |||
/// <summary> If <see langword="true"/>, a user may change the nickname of other users. </summary> | |||
/// <summary> If <c>true</c>, a user may change the nickname of other users. </summary> | |||
public bool ManageNicknames => Permissions.GetValue(RawValue, GuildPermission.ManageNicknames); | |||
/// <summary> If <see langword="true"/>, a user may adjust roles. </summary> | |||
/// <summary> If <c>true</c>, a user may adjust roles. </summary> | |||
public bool ManageRoles => Permissions.GetValue(RawValue, GuildPermission.ManageRoles); | |||
/// <summary> If <see langword="true"/>, a user may edit the webhooks for this guild. </summary> | |||
/// <summary> If <c>true</c>, a user may edit the webhooks for this guild. </summary> | |||
public bool ManageWebhooks => Permissions.GetValue(RawValue, GuildPermission.ManageWebhooks); | |||
/// <summary> If <see langword="true"/>, a user may edit the emojis for this guild. </summary> | |||
/// <summary> If <c>true</c>, a user may edit the emojis for this guild. </summary> | |||
public bool ManageEmojis => Permissions.GetValue(RawValue, GuildPermission.ManageEmojis); | |||
/// <summary> Creates a new <see cref="GuildPermissions"/> with the provided packed value. </summary> | |||
@@ -4,7 +4,7 @@ using System.Diagnostics; | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Represents a Discord color. | |||
/// Represents a color used in Discord. | |||
/// </summary> | |||
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] | |||
public struct Color | |||
@@ -65,7 +65,7 @@ namespace Discord | |||
/// <summary> | |||
/// Initializes a <see cref="Color" /> struct with the given raw value. | |||
/// </summary> | |||
/// <param name="rawValue">A raw value for the color (e.g. <c>0x607D8B</c>).</param> | |||
/// <param name="rawValue">The raw value of the color (e.g. <c>0x607D8B</c>).</param> | |||
public Color(uint rawValue) | |||
{ | |||
RawValue = rawValue; | |||
@@ -73,9 +73,9 @@ namespace Discord | |||
/// <summary> | |||
/// Initializes a <see cref="Color" /> struct with the given RGB bytes. | |||
/// </summary> | |||
/// <param name="r">The <see langword="byte"/> that represents the red color.</param> | |||
/// <param name="g">The <see langword="byte"/> that represents the green color.</param> | |||
/// <param name="b">The <see langword="byte"/> that represents the blue color.</param> | |||
/// <param name="r">The byte that represents the red color.</param> | |||
/// <param name="g">The byte that represents the green color.</param> | |||
/// <param name="b">The byte that represents the blue color.</param> | |||
public Color(byte r, byte g, byte b) | |||
{ | |||
RawValue = | |||
@@ -126,8 +126,11 @@ namespace Discord | |||
} | |||
/// <summary> | |||
/// Gets the hexadecimal representation of the color (e.g. #000ccc). | |||
/// Gets the hexadecimal representation of the color (e.g. <c>#000ccc</c>). | |||
/// </summary> | |||
/// <returns> | |||
/// A hexadecimal string of the color. | |||
/// </returns> | |||
public override string ToString() => | |||
$"#{Convert.ToString(RawValue, 16)}"; | |||
private string DebuggerDisplay => | |||
@@ -21,24 +21,24 @@ namespace Discord | |||
/// Determines whether the role can be separated in the user list. | |||
/// </summary> | |||
/// <returns> | |||
/// Returns <see langword="true"/> if users of this role are separated in the user list; otherwise, returns | |||
/// <see langword="false"/>. | |||
/// Returns <c>true</c> if users of this role are separated in the user list; otherwise, returns | |||
/// <c>false</c>. | |||
/// </returns> | |||
bool IsHoisted { get; } | |||
/// <summary> | |||
/// Determines whether the role is managed by Discord. | |||
/// </summary> | |||
/// <returns> | |||
/// Returns <see langword="true"/> if this role is automatically managed by Discord; otherwise, returns | |||
/// <see langword="false"/>. | |||
/// Returns <c>true</c> if this role is automatically managed by Discord; otherwise, returns | |||
/// <c>false</c>. | |||
/// </returns> | |||
bool IsManaged { get; } | |||
/// <summary> | |||
/// Determines whether the role is mentionable. | |||
/// </summary> | |||
/// <returns> | |||
/// Returns <see langword="true"/> if this role may be mentioned in messages; otherwise, returns | |||
/// <see langword="false"/>. | |||
/// Returns <c>true</c> if this role may be mentioned in messages; otherwise, returns | |||
/// <c>false</c>. | |||
/// </returns> | |||
bool IsMentionable { get; } | |||
/// <summary> | |||
@@ -3,16 +3,7 @@ namespace Discord | |||
/// <summary> | |||
/// Properties that are used to modify an <see cref="IRole" /> with the specified changes. | |||
/// </summary> | |||
/// <example> | |||
/// <code lang="c#"> | |||
/// await role.ModifyAsync(x => | |||
/// { | |||
/// x.Color = new Color(180, 15, 40); | |||
/// x.Hoist = true; | |||
/// }); | |||
/// </code> | |||
/// </example> | |||
/// <seealso cref="T:Discord.IRole" /> | |||
/// <seealso cref="IRole.ModifyAsync" /> | |||
public class RoleProperties | |||
{ | |||
/// <summary> | |||
@@ -5,36 +5,28 @@ namespace Discord | |||
/// <summary> | |||
/// Properties that are used to modify an <see cref="IGuildUser" /> with the following parameters. | |||
/// </summary> | |||
/// <example> | |||
/// <code lang="c#"> | |||
/// await guildUser.ModifyAsync(x => | |||
/// { | |||
/// x.Nickname = $"festive {guildUser.Username}"; | |||
/// }); | |||
/// </code> | |||
/// </example> | |||
/// <seealso cref="T:Discord.IGuildUser" /> | |||
/// <seealso cref="IGuildUser.ModifyAsync" /> | |||
public class GuildUserProperties | |||
{ | |||
/// <summary> | |||
/// Gets or sets whether the user should be muted in a voice channel. | |||
/// </summary> | |||
/// <remarks> | |||
/// If this value is set to <see langword="true"/>, no user will be able to hear this user speak in the guild. | |||
/// If this value is set to <c>true</c>, no user will be able to hear this user speak in the guild. | |||
/// </remarks> | |||
public Optional<bool> Mute { get; set; } | |||
/// <summary> | |||
/// Gets or sets whether the user should be deafened in a voice channel. | |||
/// </summary> | |||
/// <remarks> | |||
/// If this value is set to <see langword="true"/>, this user will not be able to hear anyone speak in the guild. | |||
/// If this value is set to <c>true</c>, this user will not be able to hear anyone speak in the guild. | |||
/// </remarks> | |||
public Optional<bool> Deaf { get; set; } | |||
/// <summary> | |||
/// Gets or sets the user's nickname. | |||
/// </summary> | |||
/// <remarks> | |||
/// To clear the user's nickname, this value can be set to <see langword="null" /> or | |||
/// To clear the user's nickname, this value can be set to <c>null</c> or | |||
/// <see cref="string.Empty" />. | |||
/// </remarks> | |||
public Optional<string> Nickname { get; set; } | |||
@@ -13,11 +13,11 @@ namespace Discord | |||
/// </summary> | |||
string Email { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this user's email has been verified. | |||
/// Returns <c>true</c> if this user's email has been verified. | |||
/// </summary> | |||
bool IsVerified { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this user has enabled MFA on their account. | |||
/// Returns <c>true</c> if this user has enabled MFA on their account. | |||
/// </summary> | |||
bool IsMfaEnabled { get; } | |||
@@ -12,11 +12,18 @@ namespace Discord | |||
/// </summary> | |||
string AvatarId { get; } | |||
/// <summary> | |||
/// Gets the URL to this user's avatar. | |||
/// </summary> | |||
/// Returns a URL to this user's avatar. | |||
/// </summary> | |||
/// <param name="format">The format to return.</param> | |||
/// <param name="size"> | |||
/// The size of the image to return in. This can be any power of two between 16 and 2048. | |||
/// </param> | |||
/// <returns> | |||
/// User's avatar URL. | |||
/// </returns> | |||
string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128); | |||
/// <summary> | |||
/// Gets the URL to this user's default avatar. | |||
/// Returns the URL to this user's default avatar. | |||
/// </summary> | |||
string GetDefaultAvatarUrl(); | |||
/// <summary> | |||
@@ -28,11 +35,11 @@ namespace Discord | |||
/// </summary> | |||
ushort DiscriminatorValue { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this user is a bot user. | |||
/// Gets <c>true</c> if this user is a bot user. | |||
/// </summary> | |||
bool IsBot { get; } | |||
/// <summary> | |||
/// Returns <see langword="true"/> if this user is a webhook user. | |||
/// Gets <c>true</c> if this user is a webhook user. | |||
/// </summary> | |||
bool IsWebhook { get; } | |||
/// <summary> | |||
@@ -43,6 +50,10 @@ namespace Discord | |||
/// <summary> | |||
/// Returns a direct message channel to this user, or create one if it does not already exist. | |||
/// </summary> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable Task containing the DM channel. | |||
/// </returns> | |||
Task<IDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null); | |||
} | |||
} |
@@ -6,27 +6,27 @@ namespace Discord | |||
public interface IVoiceState | |||
{ | |||
/// <summary> | |||
/// Returns <see langword="true" /> if the guild has deafened this user. | |||
/// Returns <c>true</c> if the guild has deafened this user. | |||
/// </summary> | |||
bool IsDeafened { get; } | |||
/// <summary> | |||
/// Returns <see langword="true" /> if the guild has muted this user. | |||
/// Returns <c>true</c> if the guild has muted this user. | |||
/// </summary> | |||
bool IsMuted { get; } | |||
/// <summary> | |||
/// Returns <see langword="true" /> if this user has marked themselves as deafened. | |||
/// Returns <c>true</c> if this user has marked themselves as deafened. | |||
/// </summary> | |||
bool IsSelfDeafened { get; } | |||
/// <summary> | |||
/// Returns <see langword="true" /> if this user has marked themselves as muted. | |||
/// Returns <c>true</c> if this user has marked themselves as muted. | |||
/// </summary> | |||
bool IsSelfMuted { get; } | |||
/// <summary> | |||
/// Returns <see langword="true" /> if the guild is temporarily blocking audio to/from this user. | |||
/// Returns <c>true</c> if the guild is temporarily blocking audio to/from this user. | |||
/// </summary> | |||
bool IsSuppressed { get; } | |||
/// <summary> | |||
/// Gets the voice channel this user is currently in, or <see langword="null"/> if none. | |||
/// Gets the voice channel this user is currently in, or <c>null</c> if none. | |||
/// </summary> | |||
IVoiceChannel VoiceChannel { get; } | |||
/// <summary> | |||
@@ -3,15 +3,7 @@ namespace Discord | |||
/// <summary> | |||
/// Properties that are used to modify the <see cref="ISelfUser" /> with the specified changes. | |||
/// </summary> | |||
/// <example> | |||
/// <code lang="c#"> | |||
/// await Context.Client.CurrentUser.ModifyAsync(x => | |||
/// { | |||
/// x.Avatar = new Image(File.OpenRead("avatar.jpg")); | |||
/// }); | |||
/// </code> | |||
/// </example> | |||
/// <seealso cref="T:Discord.ISelfUser" /> | |||
/// <seealso cref="ISelfUser.ModifyAsync" /> | |||
public class SelfUserProperties | |||
{ | |||
/// <summary> | |||
@@ -3,16 +3,7 @@ namespace Discord | |||
/// <summary> | |||
/// Properties used to modify an <see cref="IWebhook" /> with the specified changes. | |||
/// </summary> | |||
/// <example> | |||
/// <code lang="c#"> | |||
/// await webhook.ModifyAsync(x => | |||
/// { | |||
/// x.Name = "Bob"; | |||
/// x.Avatar = new Image("avatar.jpg"); | |||
/// }); | |||
/// </code> | |||
/// </example> | |||
/// <seealso cref="T:Discord.IWebhook" /> | |||
/// <seealso cref="IWebhook.ModifyAsync"/> | |||
public class WebhookProperties | |||
{ | |||
/// <summary> | |||
@@ -0,0 +1,14 @@ | |||
namespace Discord | |||
{ | |||
/// <summary> | |||
/// Represents the type of a webhook. | |||
/// </summary> | |||
/// <remarks> | |||
/// This type is currently unused, and is only returned in audit log responses. | |||
/// </remarks> | |||
public enum WebhookType | |||
{ | |||
/// <summary> An incoming webhook </summary> | |||
Incoming = 1 | |||
} | |||
} |
@@ -1,3 +1,4 @@ | |||
using System; | |||
using System.Threading.Tasks; | |||
using System.IO; | |||
@@ -6,7 +7,16 @@ namespace Discord | |||
/// <summary> An extension class for various Discord user objects. </summary> | |||
public static class UserExtensions | |||
{ | |||
/// <summary> Sends a message to the user via DM. </summary> | |||
/// <summary> | |||
/// Sends a message via DM. | |||
/// </summary> | |||
/// <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="EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable Task containing the message sent to the channel. | |||
/// </returns> | |||
public static async Task<IUserMessage> SendMessageAsync(this IUser user, | |||
string text, | |||
bool isTTS = false, | |||
@@ -16,7 +26,23 @@ namespace Discord | |||
return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); | |||
} | |||
/// <summary> Sends a file to the user via DM. </summary> | |||
/// <summary> | |||
/// Sends a file to this message channel with an optional caption. | |||
/// </summary> | |||
/// <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="EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <remarks> | |||
/// If you wish to upload an image and have it embedded in a <see cref="EmbedType.Rich"/> embed, you may | |||
/// upload the file and refer to the file with "attachment://filename.ext" in the | |||
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>. | |||
/// </remarks> | |||
/// <returns> | |||
/// An awaitable Task containing the message sent to the channel. | |||
/// </returns> | |||
public static async Task<IUserMessage> SendFileAsync(this IUser user, | |||
Stream stream, | |||
string filename, | |||
@@ -30,7 +56,22 @@ namespace Discord | |||
} | |||
#if FILESYSTEM | |||
/// <summary> Sends a file to the user via DM. </summary> | |||
/// <summary> | |||
/// Sends a file via DM with an optional caption. | |||
/// </summary> | |||
/// <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="EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <remarks> | |||
/// If you wish to upload an image and have it embedded in a <see cref="EmbedType.Rich"/> embed, you may | |||
/// upload the file and refer to the file with "attachment://filename.ext" in the | |||
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>. | |||
/// </remarks> | |||
/// <returns> | |||
/// An awaitable Task containing the message sent to the channel. | |||
/// </returns> | |||
public static async Task<IUserMessage> SendFileAsync(this IUser user, | |||
string filePath, | |||
string text = null, | |||
@@ -41,10 +82,15 @@ namespace Discord | |||
return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); | |||
} | |||
#endif | |||
/// <summary> Bans the provided user from the guild and optionally prunes their recent messages. </summary> | |||
/// <param name="user"> The user to ban. </param> | |||
/// <param name="pruneDays"> The number of days to remove messages from this user for - must be between [0, 7]</param> | |||
/// <param name="reason"> The reason of the ban to be written in the audit log. </param> | |||
/// <summary> | |||
/// Bans the provided user from the guild and optionally prunes their recent messages. | |||
/// </summary> | |||
/// <param name="user">The user to ban.</param> | |||
/// <param name="pruneDays"> | |||
/// The number of days to remove messages from this user for - must be between [0, 7] | |||
/// </param> | |||
/// <param name="reason">The reason of the ban to be written in the audit log.</param> | |||
/// <exception cref="ArgumentException"><paramref name="pruneDays" /> is not between 0 to 7.</exception> | |||
public static Task BanAsync(this IGuildUser user, int pruneDays = 0, string reason = null, RequestOptions options = null) | |||
=> user.Guild.AddBanAsync(user, pruneDays, reason, options); | |||
} | |||
@@ -10,16 +10,33 @@ namespace Discord | |||
/// </summary> | |||
public interface IDiscordClient : IDisposable | |||
{ | |||
/// <summary> | |||
/// Gets the current state of connection. | |||
/// </summary> | |||
ConnectionState ConnectionState { get; } | |||
/// <summary> | |||
/// Gets the currently logged-in user. | |||
/// </summary> | |||
ISelfUser CurrentUser { get; } | |||
/// <summary> | |||
/// Gets the token type of the logged-in user. | |||
/// </summary> | |||
TokenType TokenType { get; } | |||
Task StartAsync(); | |||
Task StopAsync(); | |||
/// <summary> | |||
/// Gets the application information associated with this account. | |||
/// Gets a Discord application information for the logged-in user. | |||
/// </summary> | |||
/// <remarks> | |||
/// This method reflects your application information you submitted when creating a Discord application via | |||
/// the Developer Portal. | |||
/// </remarks> | |||
/// <param name="options">The options to be used when sending the request.</param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task"/> containing the application information. | |||
/// </returns> | |||
Task<IApplication> GetApplicationInfoAsync(RequestOptions options = null); | |||
/// <summary> | |||
@@ -36,11 +53,21 @@ namespace Discord | |||
/// </param> | |||
Task<IReadOnlyCollection<IPrivateChannel>> GetPrivateChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
/// Gets a list of direct message channels. | |||
/// Returns a collection of direct message channels. | |||
/// </summary> | |||
/// <remarks> | |||
/// This method returns a collection of currently opened direct message channels. | |||
/// <note type="note"> | |||
/// This method will not return previously opened DM channels outside of the current session! If you | |||
/// have just started the client, this may return an empty collection. | |||
/// </note> | |||
/// </remarks> | |||
/// <param name="mode"> | |||
/// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. | |||
/// </param> | |||
/// <returns> | |||
/// An awaitable <see cref="Task" /> containing a collection of DM channels. | |||
/// </returns> | |||
Task<IReadOnlyCollection<IDMChannel>> GetDMChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
/// <summary> | |||
/// Gets a list of group channels. | |||
@@ -23,7 +23,7 @@ namespace Discord.Net | |||
/// <returns> | |||
/// A | |||
/// <see href="https://discordapp.com/developers/docs/topics/opcodes-and-status-codes#json">JSON error code</see> | |||
/// from Discord, or <see langword="null"/> if none. | |||
/// from Discord, or <c>null</c> if none. | |||
/// </returns> | |||
public int? DiscordCode { get; } | |||
/// <summary> | |||
@@ -14,7 +14,7 @@ namespace Discord | |||
/// <summary> | |||
/// Gets or set the max time, in milliseconds, to wait for this request to complete. If | |||
/// <see langword="null"/>, a request will not time out. If a rate limit has been triggered for this | |||
/// <c>null</c>, a request will not time out. If a rate limit has been triggered for this | |||
/// request's bucket and will not be unpaused in time, this request will fail immediately. | |||
/// </summary> | |||
public int? Timeout { get; set; } | |||
@@ -25,7 +25,7 @@ namespace Discord | |||
/// </summary> | |||
/// <remarks> | |||
/// This value is not guaranteed to be set; in cases where the entity cannot be pulled from cache, it is | |||
/// <see langword="null"/>. | |||
/// <c>null</c>. | |||
/// </remarks> | |||
public TEntity Value { get; } | |||
private Func<Task<TEntity>> DownloadFunc { get; } | |||
@@ -157,7 +157,7 @@ namespace Discord | |||
: this(collection, EqualityComparer<T>.Default) { } | |||
public ConcurrentHashSet(IEqualityComparer<T> comparer) | |||
: this(DefaultConcurrencyLevel, DefaultCapacity, true, comparer) { } | |||
/// <exception cref="ArgumentNullException"><paramref name="collection"/> is <see langword="null"/></exception> | |||
/// <exception cref="ArgumentNullException"><paramref name="collection"/> is <c>null</c></exception> | |||
public ConcurrentHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer) | |||
: this(comparer) | |||
{ | |||
@@ -165,7 +165,7 @@ namespace Discord | |||
InitializeFromCollection(collection); | |||
} | |||
/// <exception cref="ArgumentNullException"> | |||
/// <paramref name="collection" /> or <paramref name="comparer" /> is <see langword="null" /> | |||
/// <paramref name="collection" /> or <paramref name="comparer" /> is <c>null</c> | |||
/// </exception> | |||
public ConcurrentHashSet(int concurrencyLevel, IEnumerable<T> collection, IEqualityComparer<T> comparer) | |||
: this(concurrencyLevel, DefaultCapacity, false, comparer) | |||
@@ -210,7 +210,7 @@ namespace Discord | |||
if (_budget == 0) | |||
_budget = _tables._buckets.Length / _tables._locks.Length; | |||
} | |||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/></exception> | |||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <c>null</c></exception> | |||
public bool ContainsKey(T value) | |||
{ | |||
if (value == null) throw new ArgumentNullException(nameof(value)); | |||
@@ -234,7 +234,7 @@ namespace Discord | |||
return false; | |||
} | |||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/></exception> | |||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <c>null</c></exception> | |||
public bool TryAdd(T value) | |||
{ | |||
if (value == null) throw new ArgumentNullException(nameof(value)); | |||
@@ -284,7 +284,7 @@ namespace Discord | |||
} | |||
} | |||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <see langword="null"/></exception> | |||
/// <exception cref="ArgumentNullException"><paramref name="value"/> is <c>null</c></exception> | |||
public bool TryRemove(T value) | |||
{ | |||
if (value == null) throw new ArgumentNullException(nameof(value)); | |||
@@ -16,16 +16,25 @@ namespace Discord | |||
/// <summary> | |||
/// Returns a mention string based on the user ID. | |||
/// </summary> | |||
/// <returns> | |||
/// A user mention string (e.g. <@80351110224678912>). | |||
/// </returns> | |||
public static string MentionUser(ulong id) => MentionUser(id.ToString(), true); | |||
internal static string MentionChannel(string id) => $"<#{id}>"; | |||
/// <summary> | |||
/// Returns a mention string based on the channel ID. | |||
/// </summary> | |||
/// <returns> | |||
/// A channel mention string (e.g. <#103735883630395392>). | |||
/// </returns> | |||
public static string MentionChannel(ulong id) => MentionChannel(id.ToString()); | |||
internal static string MentionRole(string id) => $"<@&{id}>"; | |||
/// <summary> | |||
/// Returns a mention string based on the role ID. | |||
/// </summary> | |||
/// <returns> | |||
/// A role mention string (e.g. <@&165511591545143296>). | |||
/// </returns> | |||
public static string MentionRole(ulong id) => MentionRole(id.ToString()); | |||
/// <summary> | |||
@@ -0,0 +1,16 @@ | |||
using Newtonsoft.Json; | |||
namespace Discord.API | |||
{ | |||
internal class AuditLog | |||
{ | |||
[JsonProperty("webhooks")] | |||
public Webhook[] Webhooks { get; set; } | |||
[JsonProperty("users")] | |||
public User[] Users { get; set; } | |||
[JsonProperty("audit_log_entries")] | |||
public AuditLogEntry[] Entries { get; set; } | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
using Newtonsoft.Json; | |||
using Newtonsoft.Json.Linq; | |||
namespace Discord.API | |||
{ | |||
internal class AuditLogChange | |||
{ | |||
[JsonProperty("key")] | |||
public string ChangedProperty { get; set; } | |||
[JsonProperty("new_value")] | |||
public JToken NewValue { get; set; } | |||
[JsonProperty("old_value")] | |||
public JToken OldValue { get; set; } | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
using Newtonsoft.Json; | |||
namespace Discord.API | |||
{ | |||
internal class AuditLogEntry | |||
{ | |||
[JsonProperty("target_id")] | |||
public ulong? TargetId { get; set; } | |||
[JsonProperty("user_id")] | |||
public ulong UserId { get; set; } | |||
[JsonProperty("changes")] | |||
public AuditLogChange[] Changes { get; set; } | |||
[JsonProperty("options")] | |||
public AuditLogOptions Options { get; set; } | |||
[JsonProperty("id")] | |||
public ulong Id { get; set; } | |||
[JsonProperty("action_type")] | |||
public ActionType Action { get; set; } | |||
[JsonProperty("reason")] | |||
public string Reason { get; set; } | |||
} | |||
} |
@@ -0,0 +1,27 @@ | |||
using Newtonsoft.Json; | |||
namespace Discord.API | |||
{ | |||
internal class AuditLogOptions | |||
{ | |||
//Message delete | |||
[JsonProperty("count")] | |||
public int? MessageDeleteCount { get; set; } | |||
[JsonProperty("channel_id")] | |||
public ulong? MessageDeleteChannelId { get; set; } | |||
//Prune | |||
[JsonProperty("delete_member_days")] | |||
public int? PruneDeleteMemberDays { get; set; } | |||
[JsonProperty("members_removed")] | |||
public int? PruneMembersRemoved { get; set; } | |||
//Overwrite Update | |||
[JsonProperty("role_name")] | |||
public string OverwriteRoleName { get; set; } | |||
[JsonProperty("type")] | |||
public string OverwriteType { get; set; } | |||
[JsonProperty("id")] | |||
public ulong? OverwriteTargetId { get; set; } | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
namespace Discord.API.Rest | |||
{ | |||
class GetAuditLogsParams | |||
{ | |||
public Optional<int> Limit { get; set; } | |||
public Optional<ulong> BeforeEntryId { get; set; } | |||
} | |||
} |
@@ -812,6 +812,15 @@ namespace Discord.API | |||
var ids = new BucketIds(guildId: guildId); | |||
return await SendAsync<IReadOnlyCollection<Ban>>("GET", () => $"guilds/{guildId}/bans", ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task<Ban> GetGuildBanAsync(ulong guildId, ulong userId, RequestOptions options) | |||
{ | |||
Preconditions.NotEqual(userId, 0, nameof(userId)); | |||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
options = RequestOptions.CreateOrClone(options); | |||
var ids = new BucketIds(guildId: guildId); | |||
return await SendAsync<Ban>("GET", () => $"guilds/{guildId}/bans/{userId}", ids, options: options).ConfigureAwait(false); | |||
} | |||
public async Task CreateGuildBanAsync(ulong guildId, ulong userId, CreateGuildBanParams args, RequestOptions options = null) | |||
{ | |||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
@@ -1209,6 +1218,26 @@ namespace Discord.API | |||
return await SendAsync<IReadOnlyCollection<VoiceRegion>>("GET", () => $"guilds/{guildId}/regions", ids, options: options).ConfigureAwait(false); | |||
} | |||
//Audit logs | |||
public async Task<AuditLog> GetAuditLogsAsync(ulong guildId, GetAuditLogsParams args, RequestOptions options = null) | |||
{ | |||
Preconditions.NotEqual(guildId, 0, nameof(guildId)); | |||
Preconditions.NotNull(args, nameof(args)); | |||
options = RequestOptions.CreateOrClone(options); | |||
int limit = args.Limit.GetValueOrDefault(int.MaxValue); | |||
var ids = new BucketIds(guildId: guildId); | |||
Expression<Func<string>> endpoint; | |||
if (args.BeforeEntryId.IsSpecified) | |||
endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}&before={args.BeforeEntryId.Value}"; | |||
else | |||
endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}"; | |||
return await SendAsync<AuditLog>("GET", endpoint, ids, options: options).ConfigureAwait(false); | |||
} | |||
//Webhooks | |||
public async Task<Webhook> CreateWebhookAsync(ulong channelId, CreateWebhookParams args, RequestOptions options = null) | |||
{ | |||
@@ -0,0 +1,58 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using Model = Discord.API.AuditLog; | |||
using EntryModel = Discord.API.AuditLogEntry; | |||
namespace Discord.Rest | |||
{ | |||
internal static class AuditLogHelper | |||
{ | |||
private static readonly Dictionary<ActionType, Func<BaseDiscordClient, Model, EntryModel, IAuditLogData>> CreateMapping | |||
= new Dictionary<ActionType, Func<BaseDiscordClient, Model, EntryModel, IAuditLogData>>() | |||
{ | |||
[ActionType.GuildUpdated] = GuildUpdateAuditLogData.Create, | |||
[ActionType.ChannelCreated] = ChannelCreateAuditLogData.Create, | |||
[ActionType.ChannelUpdated] = ChannelUpdateAuditLogData.Create, | |||
[ActionType.ChannelDeleted] = ChannelDeleteAuditLogData.Create, | |||
[ActionType.OverwriteCreated] = OverwriteCreateAuditLogData.Create, | |||
[ActionType.OverwriteUpdated] = OverwriteUpdateAuditLogData.Create, | |||
[ActionType.OverwriteDeleted] = OverwriteDeleteAuditLogData.Create, | |||
[ActionType.Kick] = KickAuditLogData.Create, | |||
[ActionType.Prune] = PruneAuditLogData.Create, | |||
[ActionType.Ban] = BanAuditLogData.Create, | |||
[ActionType.Unban] = UnbanAuditLogData.Create, | |||
[ActionType.MemberUpdated] = MemberUpdateAuditLogData.Create, | |||
[ActionType.MemberRoleUpdated] = MemberRoleAuditLogData.Create, | |||
[ActionType.RoleCreated] = RoleCreateAuditLogData.Create, | |||
[ActionType.RoleUpdated] = RoleUpdateAuditLogData.Create, | |||
[ActionType.RoleDeleted] = RoleDeleteAuditLogData.Create, | |||
[ActionType.InviteCreated] = InviteCreateAuditLogData.Create, | |||
[ActionType.InviteUpdated] = InviteUpdateAuditLogData.Create, | |||
[ActionType.InviteDeleted] = InviteDeleteAuditLogData.Create, | |||
[ActionType.WebhookCreated] = WebhookCreateAuditLogData.Create, | |||
[ActionType.WebhookUpdated] = WebhookUpdateAuditLogData.Create, | |||
[ActionType.WebhookDeleted] = WebhookDeleteAuditLogData.Create, | |||
[ActionType.EmojiCreated] = EmoteCreateAuditLogData.Create, | |||
[ActionType.EmojiUpdated] = EmoteUpdateAuditLogData.Create, | |||
[ActionType.EmojiDeleted] = EmoteDeleteAuditLogData.Create, | |||
[ActionType.MessageDeleted] = MessageDeleteAuditLogData.Create, | |||
}; | |||
public static IAuditLogData CreateData(BaseDiscordClient discord, Model log, EntryModel entry) | |||
{ | |||
if (CreateMapping.TryGetValue(entry.Action, out var func)) | |||
return func(discord, log, entry); | |||
return null; | |||
} | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
using System.Linq; | |||
using Model = Discord.API.AuditLog; | |||
using EntryModel = Discord.API.AuditLogEntry; | |||
namespace Discord.Rest | |||
{ | |||
public class BanAuditLogData : IAuditLogData | |||
{ | |||
private BanAuditLogData(IUser user) | |||
{ | |||
Target = user; | |||
} | |||
internal static BanAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) | |||
{ | |||
var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); | |||
return new BanAuditLogData(RestUser.Create(discord, userInfo)); | |||
} | |||
public IUser Target { get; } | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
using Newtonsoft.Json.Linq; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using Model = Discord.API.AuditLog; | |||
using EntryModel = Discord.API.AuditLogEntry; | |||
namespace Discord.Rest | |||
{ | |||
public class ChannelCreateAuditLogData : IAuditLogData | |||
{ | |||
private ChannelCreateAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection<Overwrite> overwrites) | |||
{ | |||
ChannelId = id; | |||
ChannelName = name; | |||
ChannelType = type; | |||
Overwrites = overwrites; | |||
} | |||
internal static ChannelCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) | |||
{ | |||
var changes = entry.Changes; | |||
var overwrites = new List<Overwrite>(); | |||
var overwritesModel = changes.FirstOrDefault(x => x.ChangedProperty == "permission_overwrites"); | |||
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); | |||
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); | |||
var type = typeModel.NewValue.ToObject<ChannelType>(); | |||
var name = nameModel.NewValue.ToObject<string>(); | |||
foreach (var overwrite in overwritesModel.NewValue) | |||
{ | |||
var deny = overwrite.Value<ulong>("deny"); | |||
var _type = overwrite.Value<string>("type"); | |||
var id = overwrite.Value<ulong>("id"); | |||
var allow = overwrite.Value<ulong>("allow"); | |||
PermissionTarget permType = _type == "member" ? PermissionTarget.User : PermissionTarget.Role; | |||
overwrites.Add(new Overwrite(id, permType, new OverwritePermissions(allow, deny))); | |||
} | |||
return new ChannelCreateAuditLogData(entry.TargetId.Value, name, type, overwrites.ToReadOnlyCollection()); | |||
} | |||
public ulong ChannelId { get; } | |||
public string ChannelName { get; } | |||
public ChannelType ChannelType { get; } | |||
public IReadOnlyCollection<Overwrite> Overwrites { get; } | |||
} | |||
} |