Browse Source

Squashed commit of the following:

commit dece19d895
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

commit e764dafe08
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

commit 32fc2df21b
Author: Alex Gravely <tcbskater@hotmail.com>
Date:   Sat May 12 20:47:44 2018 -0400

    Remove unused field in EmbedFieldBuilder. (#1018)

commit 39dffe8585
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.

commit fdaa689ae8
Author: Still Hsu <341464@gmail.com>
Date:   Wed May 9 06:04:59 2018 +0800

    Add explanation for RunMode

commit ea82c2537e
Author: Still Hsu <341464@gmail.com>
Date:   Tue May 8 16:30:48 2018 +0800

    Initial proofread of the articles

commit 124f1a267c
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

commit 97c893107b
Author: Still Hsu <341464@gmail.com>
Date:   Mon May 7 06:22:49 2018 +0800

    Implement GetBanAsync (#1056)

commit 25557218db
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 16:11:19 2018 +0800

    Add details to SpotifyGame

commit c7b236ddf5
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 15:58:23 2018 +0800

    Add more IGuild docs

commit 1bb06cc37b
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 15:40:31 2018 +0800

    Replace all langword placements with code block

commit ac47d84ea7
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

commit 0b15bbc54d
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 15:20:34 2018 +0800

    Add XML docs

commit 65d4e4360e
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 06:57:53 2018 +0800

    Add BaseSocketClient docs

commit 8f64c04599
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 06:37:55 2018 +0800

    Replace note block

commit d8bb9e7aaa
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 06:31:50 2018 +0800

    Add warning for bulk-delete endpoint

commit adae5ffc9e
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 06:07:28 2018 +0800

    Fix missing Username prop

commit 3e591972ca
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 06:01:34 2018 +0800

    Add properties examples to overwrite

commit 0ad66f6765
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 04:55:15 2018 +0800

    Fix minor consistencies & redundant impl

commit 124efdf7e6
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 04:40:14 2018 +0800

    XML Docs

commit 3aa5d363de
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

commit 2014870dc0
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 15:57:40 2018 +0800

    Fix letter-casing for files

commit f27d659ebe
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 15:50:00 2018 +0800

    Document exposed TypeReaders

commit 5a824a5695
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 15:44:15 2018 +0800

    Add missing exceptions

commit c2de0c055f
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 15:40:16 2018 +0800

    Fix seealso for preconditions and add missing descriptions

commit 3a7d7ee955
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 15:36:22 2018 +0800

    Minor fixes in documentations
    + Fix unescaped '<'
    + Fix typo

commit 45839bd982
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 15:29:47 2018 +0800

    Add XML Docs

commit 9e6254600c
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

commit aea067884c
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

commit 27dc4831e8
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 13:50:27 2018 +0800

    Add XML Docs

commit baa8beb382
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 13:49:42 2018 +0800

    Add XML Docs

commit 089f97a010
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 13:43:04 2018 +0800

    Add details regarding userbot support

commit bb4bb13846
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

commit 9ddd70906a
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

commit f197174fcc
Author: Still Hsu <341464@gmail.com>
Date:   Fri May 4 09:36:53 2018 +0800

    Fix embed docs consistency

commit 7cfed7ff67
Author: Alex Gravely <tcbskater@hotmail.com>
Date:   Thu May 3 21:30:13 2018 -0400

    Fix nullref when passing null to GetShardIdFor. (#1049)

commit e775853b1b
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

commit 157acc4695
Author: Still Hsu <341464@gmail.com>
Date:   Thu May 3 23:03:35 2018 +0800

    Add tag examples

commit 57ea571a81
Author: Still Hsu <341464@gmail.com>
Date:   Thu May 3 22:48:33 2018 +0800

    Fix sample link & add missing pictures

commit 6769c37ad7
Author: Still Hsu <341464@gmail.com>
Date:   Thu May 3 22:39:26 2018 +0800

    Compress some assets & add OAuth2 URL generator
pull/988/head
Still Hsu 7 years ago
parent
commit
954b913616
No known key found for this signature in database GPG Key ID: 8601A145FDA95209
100 changed files with 1416 additions and 457 deletions
  1. +2
    -6
      docs/_overwrites/Commands/PreconditionAttribute.Overwrites.md
  2. +174
    -0
      docs/_overwrites/Common/ObjectProperties.Overwrites.md
  3. BIN
      docs/_template/lastmodified/plugins/LastModifiedPostProcessor.dll
  4. +3
    -2
      docs/docfx.json
  5. +4
    -4
      docs/faq/basics/basic-operations.md
  6. +12
    -8
      docs/faq/basics/client-basics.md
  7. +15
    -13
      docs/faq/basics/getting-started.md
  8. +60
    -49
      docs/faq/commands/commands.md
  9. +1
    -1
      docs/faq/commands/samples/Remainder.cs
  10. +0
    -0
      docs/faq/misc/glossary.md
  11. +2
    -2
      docs/faq/misc/legacy.md
  12. +1
    -1
      docs/guides/commands/intro.md
  13. +18
    -18
      docs/guides/commands/post-execution.md
  14. +7
    -7
      docs/guides/concepts/connections.md
  15. +2
    -2
      docs/guides/concepts/deployment.md
  16. +1
    -1
      docs/guides/concepts/entities.md
  17. +1
    -1
      docs/guides/concepts/events.md
  18. +1
    -1
      docs/guides/concepts/logging.md
  19. +10
    -7
      docs/guides/getting_started/first-bot.md
  20. BIN
      docs/guides/getting_started/images/install-vs-nuget.png
  21. BIN
      docs/guides/getting_started/images/intro-client-id.png
  22. BIN
      docs/guides/getting_started/images/intro-copy-oauth.png
  23. BIN
      docs/guides/getting_started/images/intro-create-app.png
  24. BIN
      docs/guides/getting_started/images/intro-create-bot.png
  25. BIN
      docs/guides/getting_started/images/intro-generate-oauth.png
  26. +1
    -1
      docs/guides/getting_started/installing.md
  27. +3
    -3
      docs/guides/getting_started/terminology.md
  28. +2
    -2
      docs/guides/introduction/intro.md
  29. +1
    -0
      src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs
  30. +12
    -3
      src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs
  31. +3
    -3
      src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs
  32. +3
    -3
      src/Discord.Net.Commands/CommandService.cs
  33. +6
    -2
      src/Discord.Net.Commands/Readers/ChannelTypeReader.cs
  34. +6
    -3
      src/Discord.Net.Commands/Readers/MessageTypeReader.cs
  35. +5
    -0
      src/Discord.Net.Commands/Readers/RoleTypeReader.cs
  36. +13
    -1
      src/Discord.Net.Commands/Readers/TypeReader.cs
  37. +7
    -2
      src/Discord.Net.Commands/Readers/UserTypeReader.cs
  38. +1
    -1
      src/Discord.Net.Commands/Results/RuntimeResult.cs
  39. +62
    -8
      src/Discord.Net.Core/CDN.cs
  40. +51
    -3
      src/Discord.Net.Core/DiscordConfig.cs
  41. +1
    -1
      src/Discord.Net.Core/Entities/Activities/GameAsset.cs
  42. +33
    -0
      src/Discord.Net.Core/Entities/Activities/SpotifyGame.cs
  43. +50
    -0
      src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs
  44. +14
    -0
      src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs
  45. +34
    -0
      src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs
  46. +1
    -8
      src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs
  47. +7
    -7
      src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
  48. +14
    -5
      src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
  49. +13
    -3
      src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
  50. +1
    -1
      src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs
  51. +3
    -0
      src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs
  52. +1
    -1
      src/Discord.Net.Core/Entities/Channels/VoiceChannelProperties.cs
  53. +1
    -0
      src/Discord.Net.Core/Entities/Emotes/EmoteProperties.cs
  54. +2
    -2
      src/Discord.Net.Core/Entities/Guilds/GuildEmbedProperties.cs
  55. +4
    -13
      src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs
  56. +116
    -44
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  57. +2
    -2
      src/Discord.Net.Core/Entities/Guilds/IUserGuild.cs
  58. +4
    -4
      src/Discord.Net.Core/Entities/Guilds/IVoiceRegion.cs
  59. +1
    -1
      src/Discord.Net.Core/Entities/Image.cs
  60. +4
    -4
      src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs
  61. +1
    -1
      src/Discord.Net.Core/Entities/Messages/EmbedAuthor.cs
  62. +8
    -9
      src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs
  63. +1
    -1
      src/Discord.Net.Core/Entities/Messages/EmbedField.cs
  64. +2
    -2
      src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs
  65. +28
    -7
      src/Discord.Net.Core/Entities/Messages/IAttachment.cs
  66. +52
    -12
      src/Discord.Net.Core/Entities/Messages/IEmbed.cs
  67. +25
    -4
      src/Discord.Net.Core/Entities/Messages/IMessage.cs
  68. +1
    -1
      src/Discord.Net.Core/Entities/Messages/ISystemMessage.cs
  69. +1
    -1
      src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
  70. +2
    -16
      src/Discord.Net.Core/Entities/Messages/MessageProperties.cs
  71. +1
    -1
      src/Discord.Net.Core/Entities/Messages/ReactionMetadata.cs
  72. +33
    -15
      src/Discord.Net.Core/Entities/Messages/TagHandling.cs
  73. +22
    -22
      src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
  74. +28
    -28
      src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
  75. +9
    -6
      src/Discord.Net.Core/Entities/Roles/Color.cs
  76. +6
    -6
      src/Discord.Net.Core/Entities/Roles/IRole.cs
  77. +1
    -10
      src/Discord.Net.Core/Entities/Roles/RoleProperties.cs
  78. +4
    -12
      src/Discord.Net.Core/Entities/Users/GuildUserProperties.cs
  79. +2
    -2
      src/Discord.Net.Core/Entities/Users/ISelfUser.cs
  80. +16
    -5
      src/Discord.Net.Core/Entities/Users/IUser.cs
  81. +6
    -6
      src/Discord.Net.Core/Entities/Users/IVoiceState.cs
  82. +1
    -9
      src/Discord.Net.Core/Entities/Users/SelfUserProperties.cs
  83. +1
    -10
      src/Discord.Net.Core/Entities/Webhooks/WebhookProperties.cs
  84. +14
    -0
      src/Discord.Net.Core/Entities/Webhooks/WebhookType.cs
  85. +53
    -7
      src/Discord.Net.Core/Extensions/UserExtensions.cs
  86. +29
    -2
      src/Discord.Net.Core/IDiscordClient.cs
  87. +1
    -1
      src/Discord.Net.Core/Net/HttpException.cs
  88. +1
    -1
      src/Discord.Net.Core/RequestOptions.cs
  89. +1
    -1
      src/Discord.Net.Core/Utils/Cacheable.cs
  90. +5
    -5
      src/Discord.Net.Core/Utils/ConcurrentHashSet.cs
  91. +9
    -0
      src/Discord.Net.Core/Utils/MentionUtils.cs
  92. +16
    -0
      src/Discord.Net.Rest/API/Common/AuditLog.cs
  93. +17
    -0
      src/Discord.Net.Rest/API/Common/AuditLogChange.cs
  94. +26
    -0
      src/Discord.Net.Rest/API/Common/AuditLogEntry.cs
  95. +27
    -0
      src/Discord.Net.Rest/API/Common/AuditLogOptions.cs
  96. +8
    -0
      src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs
  97. +29
    -0
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  98. +58
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs
  99. +23
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs
  100. +52
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs

+ 2
- 6
docs/_overwrites/Commands/PreconditionAttribute.Overwrites.md View File

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


+ 174
- 0
docs/_overwrites/Common/ObjectProperties.Overwrites.md View File

@@ -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;
});
```

BIN
docs/_template/lastmodified/plugins/LastModifiedPostProcessor.dll View File


+ 3
- 2
docs/docfx.json View File

@@ -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",


+ 4
- 4
docs/faq/basics/basic-operations.md View File

@@ -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]


+ 12
- 8
docs/faq/basics/client-basics.md View File

@@ -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).


+ 15
- 13
docs/faq/basics/getting-started.md View File

@@ -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?



docs/faq/commands/Commands.md → docs/faq/commands/commands.md View File

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

+ 1
- 1
docs/faq/commands/samples/Remainder.cs View File

@@ -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"

docs/faq/misc/Glossary.md → docs/faq/misc/glossary.md View File


docs/faq/misc/Legacy.md → docs/faq/misc/legacy.md View File

@@ -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.

+ 1
- 1
docs/guides/commands/intro.md View File

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



+ 18
- 18
docs/guides/commands/post-execution.md View File

@@ -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.


+ 7
- 7
docs/guides/concepts/connections.md View File

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

+ 2
- 2
docs/guides/concepts/deployment.md View File

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

+ 1
- 1
docs/guides/concepts/entities.md View File

@@ -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.


+ 1
- 1
docs/guides/concepts/events.md View File

@@ -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.


+ 1
- 1
docs/guides/concepts/logging.md View File

@@ -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.



+ 10
- 7
docs/guides/getting_started/first-bot.md View File

@@ -27,8 +27,8 @@ Discord Applications Portal first.
![Step 5](images/intro-create-bot.png)

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`.

![Step 2](images/intro-client-id.png)
![Step 2](images/intro-generate-oauth.png)

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.



BIN
docs/guides/getting_started/images/install-vs-nuget.png View File

Before After
Width: 1563  |  Height: 936  |  Size: 120 kB Width: 1508  |  Height: 935  |  Size: 50 kB

BIN
docs/guides/getting_started/images/intro-client-id.png View File

Before After
Width: 340  |  Height: 130  |  Size: 5.1 kB

BIN
docs/guides/getting_started/images/intro-copy-oauth.png View File

Before After
Width: 652  |  Height: 447  |  Size: 20 kB

BIN
docs/guides/getting_started/images/intro-create-app.png View File

Before After
Width: 962  |  Height: 716  |  Size: 52 kB Width: 962  |  Height: 716  |  Size: 24 kB

BIN
docs/guides/getting_started/images/intro-create-bot.png View File

Before After
Width: 763  |  Height: 769  |  Size: 46 kB Width: 763  |  Height: 769  |  Size: 20 kB

BIN
docs/guides/getting_started/images/intro-generate-oauth.png View File

Before After
Width: 686  |  Height: 138  |  Size: 9.0 kB

+ 1
- 1
docs/guides/getting_started/installing.md View File

@@ -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].


+ 3
- 3
docs/guides/getting_started/terminology.md View File

@@ -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`).

+ 2
- 2
docs/guides/introduction/intro.md View File

@@ -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/


+ 1
- 0
src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs View File

@@ -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
{


+ 12
- 3
src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs View File

@@ -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 &amp;&amp; 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);
}
}

+ 3
- 3
src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs View File

@@ -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());


+ 3
- 3
src/Discord.Net.Commands/CommandService.cs View File

@@ -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
- 2
src/Discord.Net.Commands/Readers/ChannelTypeReader.cs View File

@@ -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)


+ 6
- 3
src/Discord.Net.Commands/Readers/MessageTypeReader.cs View File

@@ -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);


+ 5
- 0
src/Discord.Net.Commands/Readers/RoleTypeReader.cs View File

@@ -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)


+ 13
- 1
src/Discord.Net.Commands/Readers/TypeReader.cs View File

@@ -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
- 2
src/Discord.Net.Commands/Readers/UserTypeReader.cs View File

@@ -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)


+ 1
- 1
src/Discord.Net.Commands/Results/RuntimeResult.cs View File

@@ -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)
{


+ 62
- 8
src/Discord.Net.Core/CDN.cs View File

@@ -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}";



+ 51
- 3
src/Discord.Net.Core/DiscordConfig.cs View File

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

+ 1
- 1
src/Discord.Net.Core/Entities/Activities/GameAsset.cs View File

@@ -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;


+ 33
- 0
src/Discord.Net.Core/Entities/Activities/SpotifyGame.cs View File

@@ -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)";
}


+ 50
- 0
src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs View File

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

+ 14
- 0
src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs View File

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

+ 34
- 0
src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs View File

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

+ 1
- 8
src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs View File

@@ -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 =&gt;
/// {
/// x.Name = "do-not-enter";
/// });
/// </code>
/// </example>
/// <seealso cref="IGuildChannel.ModifyAsync"/>
public class GuildChannelProperties
{
/// <summary>


+ 7
- 7
src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs View File

@@ -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);


+ 14
- 5
src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs View File

@@ -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);



+ 13
- 3
src/Discord.Net.Core/Entities/Channels/ITextChannel.cs View File

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


+ 1
- 1
src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs View File

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



+ 3
- 0
src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs View File

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


+ 1
- 1
src/Discord.Net.Core/Entities/Channels/VoiceChannelProperties.cs View File

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


+ 1
- 0
src/Discord.Net.Core/Entities/Emotes/EmoteProperties.cs View File

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


+ 2
- 2
src/Discord.Net.Core/Entities/Guilds/GuildEmbedProperties.cs View File

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


+ 4
- 13
src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs View File

@@ -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 =&gt;
/// {
/// 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>


+ 116
- 44
src/Discord.Net.Core/Entities/Guilds/IGuild.cs View File

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


+ 2
- 2
src/Discord.Net.Core/Entities/Guilds/IUserGuild.cs View File

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


+ 4
- 4
src/Discord.Net.Core/Entities/Guilds/IVoiceRegion.cs View File

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


+ 1
- 1
src/Discord.Net.Core/Entities/Image.cs View File

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


+ 4
- 4
src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs View File

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


+ 1
- 1
src/Discord.Net.Core/Entities/Messages/EmbedAuthor.cs View File

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


+ 8
- 9
src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs View File

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


+ 1
- 1
src/Discord.Net.Core/Entities/Messages/EmbedField.cs View File

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


+ 2
- 2
src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs View File

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



+ 28
- 7
src/Discord.Net.Core/Entities/Messages/IAttachment.cs View File

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

+ 52
- 12
src/Discord.Net.Core/Entities/Messages/IEmbed.cs View File

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

+ 25
- 4
src/Discord.Net.Core/Entities/Messages/IMessage.cs View File

@@ -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
- 1
src/Discord.Net.Core/Entities/Messages/ISystemMessage.cs View File

@@ -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
{


+ 1
- 1
src/Discord.Net.Core/Entities/Messages/IUserMessage.cs View File

@@ -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
{


+ 2
- 16
src/Discord.Net.Core/Entities/Messages/MessageProperties.cs View File

@@ -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 =&gt;
/// {
/// x.Content = "";
/// x.Embed = new EmbedBuilder()
/// .WithColor(new Color(40, 40, 120))
/// .WithAuthor(a =&gt; a.Name = "foxbot")
/// .WithTitle("Embed!")
/// .WithDescription("This is an embed.")
/// .Build();
/// });
/// </code>
/// </example>
/// <seealso cref="IUserMessage.ModifyAsync"/>
public class MessageProperties
{
/// <summary>


+ 1
- 1
src/Discord.Net.Core/Entities/Messages/ReactionMetadata.cs View File

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


+ 33
- 15
src/Discord.Net.Core/Entities/Messages/TagHandling.cs View File

@@ -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. &lt;@53905483156684800&gt; -&gt; &lt;@53905483156684800&gt;).
/// </summary>
Ignore = 0,
/// <summary>
/// Removes the tag entirely.
/// </summary>
Remove,
/// <summary>
/// Resolves to username (e.g. &lt;@53905483156684800&gt; -&gt; @Voltana).
/// </summary>
Name,
/// <summary>
/// Resolves to username without mention prefix (e.g. &lt;@53905483156684800&gt; -&gt; Voltana).
/// </summary>
NameNoPrefix,
/// <summary>
/// Resolves to username with discriminator value. (e.g. &lt;@53905483156684800&gt; -&gt; @Voltana#8252).
/// </summary>
FullName,
/// <summary>
/// Resolves to username with discriminator value without mention prefix. (e.g. &lt;@53905483156684800&gt; -&gt; Voltana#8252).
/// </summary>
FullNameNoPrefix,
/// <summary>
/// Sanitizes the tag (e.g. &lt;@53905483156684800&gt; -&gt; &lt;@53905483156684800&gt; (w/ nbsp)).
/// </summary>
Sanitize
}
}

+ 22
- 22
src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs View File

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


+ 28
- 28
src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs View File

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


+ 9
- 6
src/Discord.Net.Core/Entities/Roles/Color.cs View File

@@ -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 =>


+ 6
- 6
src/Discord.Net.Core/Entities/Roles/IRole.cs View File

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


+ 1
- 10
src/Discord.Net.Core/Entities/Roles/RoleProperties.cs View File

@@ -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 =&gt;
/// {
/// 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>


+ 4
- 12
src/Discord.Net.Core/Entities/Users/GuildUserProperties.cs View File

@@ -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 =&gt;
/// {
/// 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; }


+ 2
- 2
src/Discord.Net.Core/Entities/Users/ISelfUser.cs View File

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



+ 16
- 5
src/Discord.Net.Core/Entities/Users/IUser.cs View File

@@ -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
- 6
src/Discord.Net.Core/Entities/Users/IVoiceState.cs View File

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


+ 1
- 9
src/Discord.Net.Core/Entities/Users/SelfUserProperties.cs View File

@@ -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 =&gt;
/// {
/// x.Avatar = new Image(File.OpenRead("avatar.jpg"));
/// });
/// </code>
/// </example>
/// <seealso cref="T:Discord.ISelfUser" />
/// <seealso cref="ISelfUser.ModifyAsync" />
public class SelfUserProperties
{
/// <summary>


+ 1
- 10
src/Discord.Net.Core/Entities/Webhooks/WebhookProperties.cs View File

@@ -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 =&gt;
/// {
/// x.Name = "Bob";
/// x.Avatar = new Image("avatar.jpg");
/// });
/// </code>
/// </example>
/// <seealso cref="T:Discord.IWebhook" />
/// <seealso cref="IWebhook.ModifyAsync"/>
public class WebhookProperties
{
/// <summary>


+ 14
- 0
src/Discord.Net.Core/Entities/Webhooks/WebhookType.cs View File

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

+ 53
- 7
src/Discord.Net.Core/Extensions/UserExtensions.cs View File

@@ -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);
}


+ 29
- 2
src/Discord.Net.Core/IDiscordClient.cs View File

@@ -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.


+ 1
- 1
src/Discord.Net.Core/Net/HttpException.cs View File

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


+ 1
- 1
src/Discord.Net.Core/RequestOptions.cs View File

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


+ 1
- 1
src/Discord.Net.Core/Utils/Cacheable.cs View File

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


+ 5
- 5
src/Discord.Net.Core/Utils/ConcurrentHashSet.cs View File

@@ -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));


+ 9
- 0
src/Discord.Net.Core/Utils/MentionUtils.cs View File

@@ -16,16 +16,25 @@ namespace Discord
/// <summary>
/// Returns a mention string based on the user ID.
/// </summary>
/// <returns>
/// A user mention string (e.g. &lt;@80351110224678912&gt;).
/// </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. &lt;#103735883630395392&gt;).
/// </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. &lt;@&amp;165511591545143296&gt;).
/// </returns>
public static string MentionRole(ulong id) => MentionRole(id.ToString());

/// <summary>


+ 16
- 0
src/Discord.Net.Rest/API/Common/AuditLog.cs View File

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

+ 17
- 0
src/Discord.Net.Rest/API/Common/AuditLogChange.cs View File

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

+ 26
- 0
src/Discord.Net.Rest/API/Common/AuditLogEntry.cs View File

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

+ 27
- 0
src/Discord.Net.Rest/API/Common/AuditLogOptions.cs View File

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

+ 8
- 0
src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs View File

@@ -0,0 +1,8 @@
namespace Discord.API.Rest
{
class GetAuditLogsParams
{
public Optional<int> Limit { get; set; }
public Optional<ulong> BeforeEntryId { get; set; }
}
}

+ 29
- 0
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -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)
{


+ 58
- 0
src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs View File

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

+ 23
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs View File

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

+ 52
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs View File

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

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save