Browse Source

docs: Documentation December Update (#1218)

* Fix broken link (#11)

* Fix typos and improve wording

* Add information for IGuildUser

+ Add GetPermission sample
+ Add ModifyAsync remarks

* Add information for IGuildChannel

+ Add ModifyAsync remarks
+ Add GetOverwritePermissionAsync examples

* Add warning for Direction.Around

* Fix indentations and references

* Move IRole.ModifyAsync sample

* Add information for IUser

+ Add example, remarks for Get(Default)AvatarUrl
+ Add example, remarks for GetOrCreateDMChannelAsync
+ Add missing remarks/summary/returns for other properties of the class

* Change verbs used in IVoiceState summary/remarks

* Add additional explanation for IGuildUser.RoleIds

* Change verbs used in IMessage summary/remarks

* Clarify IUserMessage Add/RemoveReactionAsync samples

* Fix command handler sample typo

* Add information for DiscordSocketConfig

+ Add remarks/example to the class
+ Add remarks to AlwaysDownloadUsers

* Fix documentation for SlowMode

* Add additional remarks for Guild/TextChannelProperties

* Update DocFx.Plugins.LastModified to v1.2.0
This should drastically improve docfx build time.

* Add missing dependencies

* Update DocFx.Plugins.LastModified to v1.2.1

Improve performance

* Update DocFx.Plugins.LastModified to v1.2.2

* Clarify deployment.md

+ Rewritten .NET Core deployment strategies for better clarification
    * Split deployment types into framework-dependent and self-contained
    * Clarify the benefits of using different types of publishing
    * Include a sample of how to execute dotnet application with the dotnet command in a TIP dialog for visibility

* Update post-execution article and samples

+ This change is to reflect changes made in https://github.com/RogueException/Discord.Net/pull/1164, where CommandInfo is now passed into the CommandExecuted event as an Optional<T>

* Update DocFX.Plugin.DescriptionGenerator to v1.1.1

* Adjust according to recent CommandExecuted changes

See:
+ f549da50e0
+ 6260749095

* Add further documentation for https://github.com/RogueException/Discord.Net/pull/1037

* Add partial documentation for the precondition helper class

* Include CHANGELOG.md in docs

* Revise post-execution docs
* Fix incorrect Optional<T> usage
* Indent some sample code and add a comment reminding the user that the post-execution basic sample code is not ideal.

* Streamline docs for Attachment
+ This commit also adds further explanation for why Embeds and Attachments are read-only collections

* Add further documentation for MessageActivity and MessageApplication

* Add caching-related docs to ISocketMessageChannel

* Add missing documentation inheritance for SyncPermissionsAsync

* Streamline documentation process

This is done by changing the documentation of the implementations required by interfaces to redirect to the interface method instead (e.g., SocketDMChannel#GetMessagesAsync refer to IMessageChannel.GetMessagesAsync within the remarks of the method).

* Cleanup 92bf8363ca

* Update src/Discord.Net.Core/Entities/Channels/Direction.cs

Co-Authored-By: Still34 <341464@gmail.com>

* Update src/Discord.Net.Core/Entities/Channels/Direction.cs

Co-Authored-By: Still34 <341464@gmail.com>

* Update src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs

Co-Authored-By: Still34 <341464@gmail.com>

* Update src/Discord.Net.WebSocket/DiscordSocketConfig.cs

Co-Authored-By: Still34 <341464@gmail.com>

* Update according to PR suggestions

* Reword sentences of deployment article for clarification & remove mention of portability
* Fix typos/grammar errors within TextChannelProperties

* Add the logo SVG to the page navbar

* Implement changing logo image based on theme color using CSS background image

* Add a favicon

* use the purple logomark instead of white

* hack? set the title to navbar svg to read "Discord.Net Docs"
tags/2.0
Still Hsu Christopher F 6 years ago
parent
commit
0fecdf69bb
65 changed files with 813 additions and 380 deletions
  1. +0
    -2
      .gitignore
  2. BIN
      docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll
  3. +8
    -0
      docs/_template/last-modified/plugins/LICENSE
  4. BIN
      docs/_template/last-modified/plugins/LastModifiedPostProcessor.dll
  5. BIN
      docs/_template/last-modified/plugins/LibGit2Sharp.dll
  6. +4
    -0
      docs/_template/last-modified/plugins/LibGit2Sharp.dll.config
  7. BIN
      docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-8e0b172.so
  8. BIN
      docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-8e0b172.so
  9. BIN
      docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-8e0b172.so
  10. BIN
      docs/_template/last-modified/plugins/lib/linux-x64/libgit2-8e0b172.so
  11. BIN
      docs/_template/last-modified/plugins/lib/osx/libgit2-8e0b172.dylib
  12. BIN
      docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-8e0b172.so
  13. BIN
      docs/_template/last-modified/plugins/lib/win32/x64/git2-8e0b172.dll
  14. BIN
      docs/_template/last-modified/plugins/lib/win32/x86/git2-8e0b172.dll
  15. +5
    -1
      docs/_template/light-dark-theme/styles/dark.css
  16. +5
    -1
      docs/_template/light-dark-theme/styles/gray.css
  17. +5
    -1
      docs/_template/light-dark-theme/styles/light.css
  18. +19
    -1
      docs/_template/light-dark-theme/styles/master.css
  19. +10
    -2
      docs/docfx.json
  20. +1
    -1
      docs/faq/basics/basic-operations.md
  21. +12
    -7
      docs/faq/commands/general.md
  22. BIN
      docs/favicon.ico
  23. +4
    -6
      docs/guides/commands/post-execution.md
  24. +1
    -1
      docs/guides/commands/samples/intro/command_handler.cs
  25. +1
    -1
      docs/guides/commands/samples/post-execution/command_executed_adv_demo.cs
  26. +9
    -9
      docs/guides/commands/samples/post-execution/command_executed_demo.cs
  27. +5
    -2
      docs/guides/commands/samples/post-execution/post-execution_basic.cs
  28. +38
    -21
      docs/guides/deployment/deployment.md
  29. +3
    -1
      docs/index.md
  30. +1
    -1
      docs/marketing/logo/SVG/Logomark Purple.svg
  31. +3
    -1
      docs/toc.yml
  32. +30
    -0
      src/Discord.Net.Commands/Extensions/CommandServiceExtensions.cs
  33. +2
    -2
      src/Discord.Net.Core/Entities/Activities/Game.cs
  34. +9
    -1
      src/Discord.Net.Core/Entities/Channels/Direction.cs
  35. +5
    -0
      src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs
  36. +50
    -1
      src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
  37. +15
    -2
      src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs
  38. +3
    -2
      src/Discord.Net.Core/Entities/Emotes/GuildEmote.cs
  39. +3
    -1
      src/Discord.Net.Core/Entities/Invites/IInvite.cs
  40. +1
    -1
      src/Discord.Net.Core/Entities/Messages/IAttachment.cs
  41. +21
    -10
      src/Discord.Net.Core/Entities/Messages/IMessage.cs
  42. +17
    -9
      src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
  43. +10
    -5
      src/Discord.Net.Core/Entities/Messages/MessageActivity.cs
  44. +0
    -5
      src/Discord.Net.Core/Entities/Messages/MessageApplication.cs
  45. +2
    -1
      src/Discord.Net.Core/Entities/Messages/MessageProperties.cs
  46. +4
    -10
      src/Discord.Net.Core/Entities/Roles/IRole.cs
  47. +12
    -0
      src/Discord.Net.Core/Entities/Roles/RoleProperties.cs
  48. +22
    -6
      src/Discord.Net.Core/Entities/Users/IGuildUser.cs
  49. +62
    -9
      src/Discord.Net.Core/Entities/Users/IUser.cs
  50. +28
    -6
      src/Discord.Net.Core/Entities/Users/IVoiceState.cs
  51. +7
    -0
      src/Discord.Net.Core/Extensions/EmbedBuilderExtensions.cs
  52. +117
    -9
      src/Discord.Net.Core/Utils/Preconditions.cs
  53. +36
    -0
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  54. +8
    -0
      src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
  55. +23
    -83
      src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs
  56. +2
    -0
      src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
  57. +1
    -1
      src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
  58. +1
    -3
      src/Discord.Net.Rest/Entities/Messages/Attachment.cs
  59. +40
    -0
      src/Discord.Net.WebSocket/DiscordSocketConfig.cs
  60. +71
    -20
      src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs
  61. +6
    -67
      src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs
  62. +54
    -0
      src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
  63. +13
    -67
      src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs
  64. +1
    -0
      src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
  65. +3
    -0
      src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs

+ 0
- 2
.gitignore View File

@@ -15,8 +15,6 @@
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/


BIN
docs/_template/description-generator/plugins/DocFX.Plugin.DescriptionGenerator.dll View File


+ 8
- 0
docs/_template/last-modified/plugins/LICENSE View File

@@ -19,3 +19,11 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

==============================================================================

Humanizer (https://github.com/Humanizr/Humanizer)
The MIT License (MIT)
Copyright (c) .NET Foundation and Contributors

==============================================================================

BIN
docs/_template/last-modified/plugins/LastModifiedPostProcessor.dll View File


BIN
docs/_template/last-modified/plugins/LibGit2Sharp.dll View File


+ 4
- 0
docs/_template/last-modified/plugins/LibGit2Sharp.dll.config View File

@@ -0,0 +1,4 @@
<configuration>
<dllmap os="linux" cpu="x86-64" wordsize="64" dll="git2-8e0b172" target="lib/linux-x64/libgit2-8e0b172.so" />
<dllmap os="osx" cpu="x86,x86-64" dll="git2-8e0b172" target="lib/osx/libgit2-8e0b172.dylib" />
</configuration>

BIN
docs/_template/last-modified/plugins/lib/alpine-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/debian.9-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/fedora-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/linux-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/osx/libgit2-8e0b172.dylib View File


BIN
docs/_template/last-modified/plugins/lib/rhel-x64/libgit2-8e0b172.so View File


BIN
docs/_template/last-modified/plugins/lib/win32/x64/git2-8e0b172.dll View File


BIN
docs/_template/last-modified/plugins/lib/win32/x86/git2-8e0b172.dll View File


+ 5
- 1
docs/_template/light-dark-theme/styles/dark.css View File

@@ -301,4 +301,8 @@ span.arrow-d{

span.arrow-r{
border-left: 5px solid white
}
}

.logo-switcher {
background: url("/marketing/logo/SVG/Combinationmark White.svg") no-repeat;
}

+ 5
- 1
docs/_template/light-dark-theme/styles/gray.css View File

@@ -308,4 +308,8 @@ span.arrow-d{

span.arrow-r{
border-left: 5px solid white
}
}

.logo-switcher {
background: url("/marketing/logo/SVG/Combinationmark White.svg") no-repeat;
}

+ 5
- 1
docs/_template/light-dark-theme/styles/light.css View File

@@ -110,4 +110,8 @@ span.arrow-d{

span.arrow-r{
border-left: 5px solid black
}
}

.logo-switcher {
background: url("/marketing/logo/SVG/Combinationmark.svg") no-repeat;
}

+ 19
- 1
docs/_template/light-dark-theme/styles/master.css View File

@@ -9,6 +9,15 @@ body {
scroll-behavior: smooth;
}

#logo
{
max-width: 100px;
max-height: 100px;
width: 38pt;
height: 38pt;
padding: 8pt;
}

p,
li,
.toc {
@@ -23,6 +32,15 @@ img {
margin-bottom: 15px;
}

.big-logo {
display: block;
box-shadow: none !important;
/* Width value was taken from the original size of the combomark svg */
width: 951pt;
/* Height was arbitrarily determined */
min-height: 100pt;
}

article.content p{
-webkit-transition: all .75s ease-in-out;
transition: all .75s ease-in-out;
@@ -153,4 +171,4 @@ span.arrow-d{

span.arrow-r{
top: 6px; position: relative;
}
}

+ 10
- 2
docs/docfx.json View File

@@ -24,13 +24,19 @@
},
{
"files": ["guides/**.md", "guides/**/toc.yml"]
},
{
"src": "../",
"files": [ "CHANGELOG.md" ]
}
],
"resource": [{
"files": [
"**/images/**",
"**/samples/**",
"langwordMapping.yml"
"langwordMapping.yml",
"marketing/logo/SVG/**.svg",
"favicon.ico"
]
}],
"dest": "_site",
@@ -45,7 +51,9 @@
"globalMetadata": {
"_appTitle": "Discord.Net Documentation",
"_appFooter": "Discord.Net (c) 2015-2018 2.0.0-beta",
"_enableSearch": true
"_enableSearch": true,
"_appLogoPath": "marketing/logo/SVG/Logomark Purple.svg",
"_appFaviconPath": "favicon.ico"
},
"xrefService": [
"https://xref.docs.microsoft.com/query?uid={uid}"


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

@@ -27,7 +27,7 @@ A good and safe casting example:
[!code-csharp[Casting](samples/cast.cs)]

[InvalidCastException]: https://docs.microsoft.com/en-us/dotnet/api/system.invalidcastexception
[this post]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/how-to-safely-cast-by-using-as-and-is-operators
[this post]: https://docs.microsoft.com/en-us/dotnet/csharp/how-to/safely-cast-using-pattern-matching-is-and-as-operators

## How do I send a message?



+ 12
- 7
docs/faq/commands/general.md View File

@@ -119,24 +119,29 @@ The following are the known caveats with `RunMode.Async`,
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).
4. Exceptions are swallowed.
4. Exceptions are swallowed in the `ExecuteAsync` result.

However, there are ways to remedy some of these.

For #3, in Discord.Net 2.0, the library introduces a new event called
[CommandExecuted], which is raised whenever the command is
**successfully executed**. This event will be raised regardless of
the `RunMode` type and will return the appropriate execution result.
[CommandService.CommandExecuted], which is raised whenever the command is executed.
This event will be raised regardless of
the `RunMode` type and will return the appropriate execution result
and the associated @Discord.Commands.CommandInfo if applicable.

For #4, exceptions are caught in [CommandService.Log] event under
[LogMessage.Exception] as [CommandException].
[LogMessage.Exception] as [CommandException] and in the
[CommandService.CommandExecuted] event under the [IResult] as
[ExecuteResult.Exception].

[Task.Run]: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run
[async state machine]: https://www.red-gate.com/simple-talk/dotnet/net-tools/c-async-what-is-it-and-how-does-it-work/
[ExecuteAsync]: xref:Discord.Commands.CommandService.ExecuteAsync*
[ExecuteResult]: xref:Discord.Commands.ExecuteResult
[RuntimeResult]: xref:Discord.Commands.RuntimeResult
[CommandExecuted]: xref:Discord.Commands.CommandService.CommandExecuted
[CommandService.CommandExecuted]: xref:Discord.Commands.CommandService.CommandExecuted
[CommandService.Log]: xref:Discord.Commands.CommandService.Log
[LogMessage.Exception]: xref:Discord.LogMessage.Exception*
[CommandException]: xref:Discord.Commands.CommandException
[ExecuteResult.Exception]: xref:Discord.Commands.ExecuteResult.Exception*
[CommandException]: xref:Discord.Commands.CommandException
[IResult]: xref:Discord.Commands.IResult

BIN
docs/favicon.ico View File

Before After

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

@@ -31,17 +31,15 @@ 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
[ExecuteResult] instead of the actual result. You can learn more
about the impact in the [FAQ](xref:FAQ.Commands.General).
about the impact in @FAQ.Commands.General.

## CommandExecuted Event

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 **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.
executed regardless of its execution status. This means this
event can be used to streamline your post-execution design,
is not prone to `RunMode.Async`'s [ExecuteAsync] drawbacks.

Thus, we can begin working on code such as:



+ 1
- 1
docs/guides/commands/samples/intro/command_handler.cs View File

@@ -49,7 +49,7 @@ public class CommandHandler
// Keep in mind that result does not indicate a return value
// rather an object stating if the command executed successfully.
var result = await _command.ExecuteAsync(
var result = await _commands.ExecuteAsync(
context: context,
argPos: argPos,
services: null);


+ 1
- 1
docs/guides/commands/samples/post-execution/command_executed_adv_demo.cs View File

@@ -1,4 +1,4 @@
public async Task OnCommandExecutedAsync(CommandInfo command, ICommandContext context, IResult result)
public async Task OnCommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
{
switch(result)
{


+ 9
- 9
docs/guides/commands/samples/post-execution/command_executed_demo.cs View File

@@ -6,7 +6,7 @@ public async Task SetupAsync()
// Hook the command handler
_client.MessageReceived += HandleCommandAsync;
}
public async Task OnCommandExecutedAsync(CommandInfo command, ICommandContext context, IResult result)
public async Task OnCommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
{
// We have access to the information of the command executed,
// the context of the command, and the result returned from the
@@ -20,19 +20,19 @@ public async Task OnCommandExecutedAsync(CommandInfo command, ICommandContext co

// ...or even log the result (the method used should fit into
// your existing log handler)
await _log.LogAsync(new LogMessage(LogSeverity.Info, "CommandExecution", $"{command?.Name} was executed at {DateTime.UtcNow}."));
var commandName = command.IsSpecified ? command.Value.Name : "A command";
await _log.LogAsync(new LogMessage(LogSeverity.Info,
"CommandExecution",
$"{commandName} was executed at {DateTime.UtcNow}."));
}
public async Task HandleCommandAsync(SocketMessage msg)
{
var message = messageParam as SocketUserMessage;
if (message == null) return;
int argPos = 0;
if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(_client.CurrentUser, ref argPos)) || message.Author.IsBot) return;
if (!(message.HasCharPrefix('!', ref argPos) ||
message.HasMentionPrefix(_client.CurrentUser, ref argPos)) ||
message.Author.IsBot) return;
var context = new SocketCommandContext(_client, message);
var result = await _commands.ExecuteAsync(context, argPos, _services);
// Optionally, you may pass the result manually into your
// CommandExecuted event handler if you wish to handle parsing or
// precondition failures in the same method.

// await OnCommandExecutedAsync(null, context, result);
await _commands.ExecuteAsync(context, argPos, _services);
}

+ 5
- 2
docs/guides/commands/samples/post-execution/post-execution_basic.cs View File

@@ -1,11 +1,14 @@
// Bad code!!!
var result = await _commands.ExecuteAsync(context, argPos, _services);
if (result.CommandError != null)
switch(result.CommandError)
{
case CommandError.BadArgCount:
await context.Channel.SendMessageAsync("Parameter count does not match any command's.");
await context.Channel.SendMessageAsync(
"Parameter count does not match any command's.");
break;
default:
await context.Channel.SendMessageAsync($"An error has occurred {result.ErrorReason}");
await context.Channel.SendMessageAsync(
$"An error has occurred {result.ErrorReason}");
break;
}

+ 38
- 21
docs/guides/deployment/deployment.md View File

@@ -52,33 +52,50 @@ enough. Here is a list of recommended VPS provider.

> [!NOTE]
> This section only covers the very basics of .NET Core deployment.
> To learn more about deployment, visit [.NET Core application deployment]
> by Microsoft.
> To learn more about .NET Core deployment,
> visit [.NET Core application deployment] by Microsoft.

By default, .NET Core compiles all projects as a DLL file, so that any
.NET Core runtime can execute the application.
When redistributing the application - whether for deployment on a
remote machine or for sharing with another user - you may want to
publish the application; in other words, to create a
self-contained package without installing the dependencies
and the runtime on the target platform.

You may execute the application via `dotnet myprogram.dll` assuming you
have the dotnet CLI installed.
### Framework-dependent Deployment

When redistributing the application, you may want to publish the
application, or in other words, create a self-contained package
for use on another machine without installing the dependencies first.

This can be achieved by using the dotnet CLI too on the development
machine:
To deploy a framework-dependent package (i.e. files to be used on a
remote machine with the `dotnet` command), simply publish
the package with:

* `dotnet publish -c Release`

Additionally, you may want to target a specific platform when
publishing the application so you may use the application without
having to install the Core runtime on the target machine. To do this,
you may specify an [Runtime ID] upon build/publish with the `-r`
option.

For example, when targeting a Windows 10 machine, you may want to use
the following to create the application in Windows executable
format (.exe):
This will create a package with the **least dependencies**
included with the application; however, the remote machine
must have `dotnet` runtime installed before the remote could run the
program.

> [!TIP]
> Do not know how to run a .NET Core application with
> the `dotnet` runtime? Navigate to the folder of the program
> (typically under `$projFolder/bin/Release`) and
> enter `dotnet program.dll` where `program.dll` is your compiled
> binaries.

### Self-contained Deployment

To deploy a self-contained package (i.e. files to be used on a remote
machine without the `dotnet` runtime), publish with a specific
[Runtime ID] with the `-r` switch.

This will create a package with dependencies compiled for the target
platform, meaning that all the required dependencies will be included
with the program. This will result in **larger package size**;
however, that means the copy of the runtime that can be run
natively on the target platform.

For example, the following command will create a Windows
executable (`.exe`) that is ready to be executed on any
Windows 10 x64 based machine:

* `dotnet publish -c Release -r win10-x64`



+ 3
- 1
docs/index.md View File

@@ -5,6 +5,8 @@ title: Home

# Discord.Net Documentation

<img class="big-logo logo-switcher" />

## What is Discord.Net?

Discord.Net is an asynchronous, multi-platform .NET Library used to
@@ -25,4 +27,4 @@ objects in the library.
- [GitHub](https://github.com/RogueException/Discord.Net/)
- [NuGet](https://www.nuget.org/packages/Discord.Net/)
- [MyGet Feed](https://www.myget.org/feed/Packages/discord-net) - Add-ons and nightly builds
- [AppVeyor CI](https://ci.appveyor.com/project/RogueException/discord-net) - Nightly builds via Continuous Integration
- [AppVeyor CI](https://ci.appveyor.com/project/RogueException/discord-net) - Nightly builds via Continuous Integration

+ 1
- 1
docs/marketing/logo/SVG/Logomark Purple.svg View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 265.74 274.24"><defs><style>.cls-1{fill:#68217a;}.cls-2{fill:#939;}</style></defs><title>Logomark Purple</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M54.48.13,239.93,17.62A28.57,28.57,0,0,1,265.61,48.8L248,235.91a28.57,28.57,0,0,1-31.06,25.84L60,246.94,69.71,222l-19.27,14.8L32.23,250.75,0,274.24,23.42,26A28.56,28.56,0,0,1,54.48.13Z"/><rect class="cls-2" x="113.88" y="-22.58" width="16.56" height="138.01" rx="6.32" transform="translate(63.14 163.03) rotate(-83.96)"/><rect class="cls-2" x="157.3" y="20.84" width="16.56" height="138.01" rx="6.32" transform="translate(58.81 245.05) rotate(-83.96)"/><rect class="cls-2" x="100.92" y="99.96" width="16.56" height="138.01" rx="6.32" transform="translate(-70.32 259.78) rotate(-83.96)"/><rect class="cls-2" x="153.11" y="60.4" width="16.56" height="138.01" rx="6.32" transform="translate(15.73 276.29) rotate(-83.96)"/></g></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 265.74 274.24"><defs><style>.cls-1{fill:#68217a;}.cls-2{fill:#939;}</style></defs><title>Discord.Net Docs</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M54.48.13,239.93,17.62A28.57,28.57,0,0,1,265.61,48.8L248,235.91a28.57,28.57,0,0,1-31.06,25.84L60,246.94,69.71,222l-19.27,14.8L32.23,250.75,0,274.24,23.42,26A28.56,28.56,0,0,1,54.48.13Z"/><rect class="cls-2" x="113.88" y="-22.58" width="16.56" height="138.01" rx="6.32" transform="translate(63.14 163.03) rotate(-83.96)"/><rect class="cls-2" x="157.3" y="20.84" width="16.56" height="138.01" rx="6.32" transform="translate(58.81 245.05) rotate(-83.96)"/><rect class="cls-2" x="100.92" y="99.96" width="16.56" height="138.01" rx="6.32" transform="translate(-70.32 259.78) rotate(-83.96)"/><rect class="cls-2" x="153.11" y="60.4" width="16.56" height="138.01" rx="6.32" transform="translate(15.73 276.29) rotate(-83.96)"/></g></g></svg>

+ 3
- 1
docs/toc.yml View File

@@ -6,4 +6,6 @@
topicUid: FAQ.Basics.GetStarted
- name: API Documentation
href: api/
topicUid: API.Docs
topicUid: API.Docs
- name: Changelog
topicHref: ../CHANGELOG.md

+ 30
- 0
src/Discord.Net.Commands/Extensions/CommandServiceExtensions.cs View File

@@ -5,8 +5,20 @@ using System.Threading.Tasks;

namespace Discord.Commands
{
/// <summary>
/// Provides extension methods for the <see cref="CommandService"/> class.
/// </summary>
public static class CommandServiceExtensions
{
/// <summary>
/// Returns commands that can be executed under the current context.
/// </summary>
/// <param name="commands">The set of commands to be checked against.</param>
/// <param name="context">The current command context.</param>
/// <param name="provider">The service provider used for dependency injection upon precondition check.</param>
/// <returns>
/// A read-only collection of commands that can be executed under the current context.
/// </returns>
public static async Task<IReadOnlyCollection<CommandInfo>> GetExecutableCommandsAsync(this ICollection<CommandInfo> commands, ICommandContext context, IServiceProvider provider)
{
var executableCommands = new List<CommandInfo>();
@@ -27,8 +39,26 @@ namespace Discord.Commands

return executableCommands;
}
/// <summary>
/// Returns commands that can be executed under the current context.
/// </summary>
/// <param name="commandService">The desired command service class to check against.</param>
/// <param name="context">The current command context.</param>
/// <param name="provider">The service provider used for dependency injection upon precondition check.</param>
/// <returns>
/// A read-only collection of commands that can be executed under the current context.
/// </returns>
public static Task<IReadOnlyCollection<CommandInfo>> GetExecutableCommandsAsync(this CommandService commandService, ICommandContext context, IServiceProvider provider)
=> GetExecutableCommandsAsync(commandService.Commands.ToArray(), context, provider);
/// <summary>
/// Returns commands that can be executed under the current context.
/// </summary>
/// <param name="module">The module to be checked against.</param>
/// <param name="context">The current command context.</param>
/// <param name="provider">The service provider used for dependency injection upon precondition check.</param>
/// <returns>
/// A read-only collection of commands that can be executed under the current context.
/// </returns>
public static async Task<IReadOnlyCollection<CommandInfo>> GetExecutableCommandsAsync(this ModuleInfo module, ICommandContext context, IServiceProvider provider)
{
var executableCommands = new List<CommandInfo>();


+ 2
- 2
src/Discord.Net.Core/Entities/Activities/Game.cs View File

@@ -15,10 +15,10 @@ namespace Discord

internal Game() { }
/// <summary>
/// Creates a <see cref="Game"/> with the provided <paramref name="name"/> and <see cref="ActivityType"/>.
/// Creates a <see cref="Game"/> with the provided name and <see cref="ActivityType"/>.
/// </summary>
/// <param name="name">The name of the game.</param>
/// <param name="type">The type of activity. Default is <see cref="Discord.ActivityType.Playing"/>.</param>
/// <param name="type">The type of activity.</param>
public Game(string name, ActivityType type = ActivityType.Playing)
{
Name = name;


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

@@ -1,8 +1,16 @@
namespace Discord
{
/// <summary>
/// Specifies the direction of where message(s) should be gotten from.
/// Specifies the direction of where message(s) should be retrieved from.
/// </summary>
/// <remarks>
/// This enum is used to specify the direction for retrieving messages.
/// <note type="important">
/// At the time of writing, <see cref="Around"/> is not yet implemented into
/// <see cref="IMessageChannel.GetMessagesAsync"/>. Attempting to use the method with <see cref="Around"/>
/// as its direction will throw a <see cref="System.NotImplementedException"/>.
/// </note>
/// </remarks>
public enum Direction
{
/// <summary>


+ 5
- 0
src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs View File

@@ -24,6 +24,11 @@ namespace Discord
/// <summary>
/// Gets or sets the category ID for this channel.
/// </summary>
/// <remarks>
/// Setting this value to a category's snowflake identifier will change or set this channel's parent to the
/// specified channel; setting this value to <c>0</c> will detach this channel from its parent if one
/// is set.
/// </remarks>
public Optional<ulong?> CategoryId { get; set; }
}
}

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

@@ -47,6 +47,10 @@ namespace Discord
/// <summary>
/// Modifies this guild channel.
/// </summary>
/// <remarks>
/// This method modifies the current guild channel with the specified properties. To see an example of this
/// method and what properties are available, please refer to <see cref="GuildChannelProperties"/>.
/// </remarks>
/// <param name="func">The delegate containing the properties to modify the channel with.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
@@ -92,16 +96,61 @@ namespace Discord
/// <summary>
/// Adds or updates the permission overwrite for the given role.
/// </summary>
/// <example>
/// The following example fetches a role via <see cref="IGuild.GetRole"/> and a channel via
/// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via
/// <see cref="GetPermissionOverwrite(Discord.IRole)"/>; if not, it denies the role from sending any
/// messages to the channel.
/// <code lang="cs">
/// // Fetches the role and channels
/// var role = guild.GetRole(339805618376540160);
/// var channel = await guild.GetChannelAsync(233937283911385098);
///
/// // If either the of the object does not exist, bail
/// if (role == null || channel == null) return;
///
/// // Fetches the previous overwrite and bail if one is found
/// var previousOverwrite = channel.GetPermissionOverwrite(role);
/// if (previousOverwrite.HasValue) return;
///
/// // Creates a new OverwritePermissions with send message set to deny and pass it into the method
/// await channel.AddPermissionOverwriteAsync(role,
/// new OverwritePermissions(sendMessage: PermValue.Deny));
/// </code>
/// </example>
/// <param name="role">The role to add the overwrite to.</param>
/// <param name="permissions">The overwrite to add to the role.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task representing the asynchronous permission operation for adding the specified permissions to the channel.
/// A task representing the asynchronous permission operation for adding the specified permissions to the
/// channel.
/// </returns>
Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null);
/// <summary>
/// Adds or updates the permission overwrite for the given user.
/// </summary>
/// <example>
/// The following example fetches a user via <see cref="IGuild.GetUserAsync"/> and a channel via
/// <see cref="IGuild.GetChannelAsync"/>. Next, it checks if an overwrite had already been set via
/// <see cref="GetPermissionOverwrite(Discord.IUser)"/>; if not, it denies the user from sending any
/// messages to the channel.
/// <code lang="cs">
/// // Fetches the role and channels
/// var user = await guild.GetUserAsync(168693960628371456);
/// var channel = await guild.GetChannelAsync(233937283911385098);
///
/// // If either the of the object does not exist, bail
/// if (user == null || channel == null) return;
///
/// // Fetches the previous overwrite and bail if one is found
/// var previousOverwrite = channel.GetPermissionOverwrite(user);
/// if (previousOverwrite.HasValue) return;
///
/// // Creates a new OverwritePermissions with send message set to deny and pass it into the method
/// await channel.AddPermissionOverwriteAsync(role,
/// new OverwritePermissions(sendMessage: PermValue.Deny));
/// </code>
/// </example>
/// <param name="user">The user to add the overwrite to.</param>
/// <param name="permissions">The overwrite to add to the user.</param>
/// <param name="options">The options to be used when sending the request.</param>


+ 15
- 2
src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs View File

@@ -1,3 +1,5 @@
using System;

namespace Discord
{
/// <summary>
@@ -9,18 +11,29 @@ namespace Discord
/// <summary>
/// Gets or sets the topic of the channel.
/// </summary>
/// <remarks>
/// Setting this value to any string other than <c>null</c> or <see cref="string.Empty"/> will set the
/// channel topic or description to the desired value.
/// </remarks>
public Optional<string> Topic { get; set; }
/// <summary>
/// Gets or sets whether this channel should be flagged as NSFW.
/// </summary>
/// <remarks>
/// Setting this value to <c>true</c> will mark the channel as NSFW (Not Safe For Work) and will prompt the
/// user about its possibly mature nature before they may view the channel; setting this value to <c>false</c> will
/// remove the NSFW indicator.
/// </remarks>
public Optional<bool> IsNsfw { get; set; }
/// <summary>
/// Gets or sets the slow-mode ratelimit in seconds for this channel.
/// </summary>
/// <remarks>
/// Setting this value to <c>0</c> will disable slow-mode for this channel.
/// Setting this value to anything above zero will require each user to wait X seconds before
/// sending another message; setting this value to <c>0</c> will disable slow-mode for this channel.
/// <note>
/// Users with <see cref="Discord.ChannelPermission.ManageMessages" /> will be exempt from slow-mode.
/// Users with <see cref="Discord.ChannelPermission.ManageMessages"/> or
/// <see cref="ChannelPermission.ManageChannels"/> will be exempt from slow-mode.
/// </note>
/// </remarks>
/// <exception cref="ArgumentOutOfRangeException">Thrown if the value does not fall within [0, 120].</exception>


+ 3
- 2
src/Discord.Net.Core/Entities/Emotes/GuildEmote.cs View File

@@ -31,10 +31,11 @@ namespace Discord
/// </returns>
public IReadOnlyList<ulong> RoleIds { get; }
/// <summary>
/// Gets the user Id that created this emoji.
/// Gets the user ID associated with the creation of this emoji.
/// </summary>
/// <returns>
/// A user Id of the user who created this emoji, which may be null. A null value only indicates that the creator was not supplied as part of the API response.
/// An <see cref="ulong"/> snowflake identifier representing the user who created this emoji;
/// <c>null</c> if unknown.
/// </returns>
public ulong? CreatorId { get; }



+ 3
- 1
src/Discord.Net.Core/Entities/Invites/IInvite.cs View File

@@ -27,7 +27,9 @@ namespace Discord
/// A generic channel that the invite points to.
/// </returns>
IChannel Channel { get; }
/// <summary> Gets the type of the channel this invite is linked to. </summary>
/// <summary>
/// Gets the type of the channel this invite is linked to.
/// </summary>
ChannelType ChannelType { get; }
/// <summary>
/// Gets the ID of the channel this invite is linked to.


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

@@ -1,7 +1,7 @@
namespace Discord
{
/// <summary>
/// Represents a Discord attachment.
/// Represents a message attachment found in a <see cref="IUserMessage"/>.
/// </summary>
public interface IAttachment
{


+ 21
- 10
src/Discord.Net.Core/Entities/Messages/IMessage.cs View File

@@ -48,7 +48,7 @@ namespace Discord
/// 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.
/// Time of when the message was last edited; <c>null</c> if the message is never edited.
/// </returns>
DateTimeOffset? EditedTimestamp { get; }
@@ -62,56 +62,67 @@ namespace Discord
IUser Author { get; }

/// <summary>
/// Returns all attachments included in this message.
/// Gets all attachments included in this message.
/// </summary>
/// <remarks>
/// This property gets a read-only collection of attachments associated with this message. Depending on the
/// user's end-client, a sent message may contain one or more attachments. For example, mobile users may
/// attach more than one file in their message, while the desktop client only allows for one.
/// </remarks>
/// <returns>
/// A read-only collection of attachments.
/// </returns>
IReadOnlyCollection<IAttachment> Attachments { get; }
/// <summary>
/// Returns all embeds included in this message.
/// Gets all embeds included in this message.
/// </summary>
/// <remarks>
/// </remarks>
/// This property gets a read-only collection of embeds associated with this message. Depending on the
/// message, a sent message may contain one or more embeds. This is usually true when multiple link previews
/// are generated; however, only one <see cref="EmbedType.Rich"/> <see cref="Embed"/> can be featured.
/// <returns>
/// A read-only collection of embed objects.
/// </returns>
IReadOnlyCollection<IEmbed> Embeds { get; }
/// <summary>
/// Returns all tags included in this message's content.
/// Gets all tags included in this message's content.
/// </summary>
IReadOnlyCollection<ITag> Tags { get; }
/// <summary>
/// Returns the IDs of channels mentioned in this message.
/// Gets the IDs of channels mentioned in this message.
/// </summary>
/// <returns>
/// A read-only collection of channel IDs.
/// </returns>
IReadOnlyCollection<ulong> MentionedChannelIds { get; }
/// <summary>
/// Returns the IDs of roles mentioned in this message.
/// Gets the IDs of roles mentioned in this message.
/// </summary>
/// <returns>
/// A read-only collection of role IDs.
/// </returns>
IReadOnlyCollection<ulong> MentionedRoleIds { get; }
/// <summary>
/// Returns the IDs of users mentioned in this message.
/// Gets the IDs of users mentioned in this message.
/// </summary>
/// <returns>
/// A read-only collection of user IDs.
/// </returns>
IReadOnlyCollection<ulong> MentionedUserIds { get; }
/// <summary>
/// Returns the Activity associated with a message.
/// Gets the activity associated with a message.
/// </summary>
/// <remarks>
/// Sent with Rich Presence-related chat embeds.
/// Sent with Rich Presence-related chat embeds. This often refers to activity that requires end-user's
/// interaction, such as a Spotify Invite activity.
/// </remarks>
/// <returns>
/// A message's activity, if any is associated.
/// </returns>
MessageActivity Activity { get; }
/// <summary>
/// Returns the Application associated with a messsage.
/// Gets the application associated with a message.
/// </summary>
/// <remarks>
/// Sent with Rich-Presence-related chat embeds.


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

@@ -12,10 +12,15 @@ namespace Discord
/// <summary>
/// Modifies this message.
/// </summary>
/// <remarks>
/// This method modifies this message with the specified properties. To see an example of this
/// method and what properties are available, please refer to <see cref="MessageProperties"/>.
/// </remarks>
/// <example>
/// <code language="cs">
/// await msg.ModifyAsync(x =&gt; x.Content = "Hello World!");
/// </code>
/// The following example replaces the content of the message with <c>Hello World!</c>.
/// <code language="cs">
/// await msg.ModifyAsync(x =&gt; x.Content = "Hello World!");
/// </code>
/// </example>
/// <param name="func">A delegate containing the properties to modify the message with.</param>
/// <param name="options">The options to be used when sending the request.</param>
@@ -49,9 +54,10 @@ namespace Discord
/// Adds a reaction to this message.
/// </summary>
/// <example>
/// <code language="cs">
/// await msg.AddReactionAsync(new Emoji("\U0001f495"));
/// </code>
/// The following example adds the reaction, <c>💕</c>, to the message.
/// <code language="cs">
/// await msg.AddReactionAsync(new Emoji("\U0001f495"));
/// </code>
/// </example>
/// <param name="emote">The emoji used to react to this message.</param>
/// <param name="options">The options to be used when sending the request.</param>
@@ -64,9 +70,10 @@ namespace Discord
/// Removes a reaction from message.
/// </summary>
/// <example>
/// <code language="cs">
/// await msg.RemoveReactionAsync(new Emoji("\U0001f495"), msg.Author);
/// </code>
/// The following example removes the reaction, <c>💕</c>, added by the message author from the message.
/// <code language="cs">
/// await msg.RemoveReactionAsync(new Emoji("\U0001f495"), msg.Author);
/// </code>
/// </example>
/// <param name="emote">The emoji used to react to this message.</param>
/// <param name="user">The user that added the emoji.</param>
@@ -89,6 +96,7 @@ namespace Discord
/// Gets all users that reacted to a message with a given emote.
/// </summary>
/// <example>
/// The following example gets the users that have reacted with the emoji <c>💕</c> to the message.
/// <code language="cs">
/// var emoji = new Emoji("\U0001f495");
/// var reactedUsers = await message.GetReactionUsersAsync(emoji, 100).FlattenAsync();


+ 10
- 5
src/Discord.Net.Core/Entities/Messages/MessageActivity.cs View File

@@ -1,12 +1,17 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord
{
/// <summary>
/// An activity object found in a sent message.
/// </summary>
/// <remarks>
/// <para>
/// This class refers to an activity object, visually similar to an embed within a message. However, a message
/// activity is interactive as opposed to a standard static embed.
/// </para>
/// <para>For example, a Spotify party invitation counts as a message activity.</para>
/// </remarks>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class MessageActivity
{


+ 0
- 5
src/Discord.Net.Core/Entities/Messages/MessageApplication.cs View File

@@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord
{


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

@@ -4,7 +4,8 @@ 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="System.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>
/// <seealso cref="IUserMessage.ModifyAsync"/>
public class MessageProperties


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

@@ -69,16 +69,10 @@ namespace Discord
/// <summary>
/// Modifies this role.
/// </summary>
/// <example>
/// <code language="cs">
/// await role.ModifyAsync(x =&gt;
/// {
/// x.Name = "Sonic";
/// x.Color = new Color(0x1A50BC);
/// x.Mentionable = true;
/// });
/// </code>
/// </example>
/// <remarks>
/// This method modifies this role with the specified properties. To see an example of this
/// method and what properties are available, please refer to <see cref="RoleProperties"/>.
/// </remarks>
/// <param name="func">A delegate containing the properties to modify the role with.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>


+ 12
- 0
src/Discord.Net.Core/Entities/Roles/RoleProperties.cs View File

@@ -3,6 +3,18 @@ namespace Discord
/// <summary>
/// Properties that are used to modify an <see cref="IRole" /> with the specified changes.
/// </summary>
/// <example>
/// The following example modifies the role to a mentionable one, renames the role into <c>Sonic</c>, and
/// changes the color to a light-blue.
/// <code language="cs">
/// await role.ModifyAsync(x =&gt;
/// {
/// x.Name = "Sonic";
/// x.Color = new Color(0x1A50BC);
/// x.Mentionable = true;
/// });
/// </code>
/// </example>
/// <seealso cref="IRole.ModifyAsync" />
public class RoleProperties
{


+ 22
- 6
src/Discord.Net.Core/Entities/Users/IGuildUser.cs View File

@@ -50,15 +50,28 @@ namespace Discord
/// <summary>
/// Gets a collection of IDs for the roles that this user currently possesses in the guild.
/// </summary>
/// <remarks>
/// This property returns a read-only collection of the identifiers of the roles that this user possesses.
/// For WebSocket users, a Roles property can be found in place of this property. Due to the REST
/// implementation, only a collection of identifiers can be retrieved instead of the full role objects.
/// </remarks>
/// <returns>
/// A read-only collection of <see cref="ulong"/>, each representing a snowflake identifier for a role that
/// this user posesses.
/// this user possesses.
/// </returns>
IReadOnlyCollection<ulong> RoleIds { get; }

/// <summary>
/// Gets the level permissions granted to this user to a given channel.
/// </summary>
/// <example>
/// The following example checks if the current user has the ability to send a message with attachment in
/// this channel; if so, uploads a file via <see cref="IMessageChannel.SendFileAsync"/>.
/// <code language="cs">
/// if (currentUser?.GetPermissions(targetChannel)?.AttachFiles)
/// await targetChannel.SendFileAsync("fortnite.png");
/// </code>
/// </example>
/// <param name="channel">The channel to get the permission from.</param>
/// <returns>
/// A <see cref="Discord.ChannelPermissions"/> structure representing the permissions that a user has in the
@@ -78,16 +91,19 @@ namespace Discord
/// <summary>
/// Modifies this user's properties in this guild.
/// </summary>
/// <remarks>
/// This method modifies the current guild user with the specified properties. To see an example of this
/// method and what properties are available, please refer to <see cref="GuildUserProperties"/>.
/// </remarks>
/// <param name="func">The delegate containing the properties to modify the user with.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous modification operation.
/// </returns>
/// <seealso cref="Discord.GuildUserProperties"/>
Task ModifyAsync(Action<GuildUserProperties> func, RequestOptions options = null);

/// <summary>
/// Adds the specified <paramref name="role"/> to this user in this guild.
/// Adds the specified role to this user in the guild.
/// </summary>
/// <param name="role">The role to be added to the user.</param>
/// <param name="options">The options to be used when sending the request.</param>
@@ -96,7 +112,7 @@ namespace Discord
/// </returns>
Task AddRoleAsync(IRole role, RequestOptions options = null);
/// <summary>
/// Adds the specified <paramref name="roles"/> to this user in this guild.
/// Adds the specified <paramref name="roles"/> to this user in the guild.
/// </summary>
/// <param name="roles">The roles to be added to the user.</param>
/// <param name="options">The options to be used when sending the request.</param>
@@ -105,7 +121,7 @@ namespace Discord
/// </returns>
Task AddRolesAsync(IEnumerable<IRole> roles, RequestOptions options = null);
/// <summary>
/// Removes the specified <paramref name="role"/> from this user in this guild.
/// Removes the specified <paramref name="role"/> from this user in the guild.
/// </summary>
/// <param name="role">The role to be removed from the user.</param>
/// <param name="options">The options to be used when sending the request.</param>
@@ -114,7 +130,7 @@ namespace Discord
/// </returns>
Task RemoveRoleAsync(IRole role, RequestOptions options = null);
/// <summary>
/// Removes the specified <paramref name="roles"/> from this user in this guild.
/// Removes the specified <paramref name="roles"/> from this user in the guild.
/// </summary>
/// <param name="roles">The roles to be removed from the user.</param>
/// <param name="options">The options to be used when sending the request.</param>


+ 62
- 9
src/Discord.Net.Core/Entities/Users/IUser.cs View File

@@ -8,21 +8,44 @@ namespace Discord
public interface IUser : ISnowflakeEntity, IMentionable, IPresence
{
/// <summary>
/// Gets the ID of this user's avatar.
/// Gets the identifier of this user's avatar.
/// </summary>
string AvatarId { get; }
/// <summary>
/// Returns a URL to this user's avatar.
/// Gets the avatar URL for this user.
/// </summary>
/// <remarks>
/// This property retrieves a URL for this user's avatar. In event that the user does not have a valid avatar
/// (i.e. their avatar identifier is not set), this property will return <c>null</c>. If you wish to
/// retrieve the default avatar for this user, consider using <see cref="IUser.GetDefaultAvatarUrl"/> (see
/// example).
/// </remarks>
/// <example>
/// The following example attempts to retrieve the user's current avatar and send it to a channel; if one is
/// not set, a default avatar for this user will be returned instead.
/// <code language="cs">
/// var userAvatarUrl = user.GetAvatarUrl() ?? user.GetDefaultAvatarUrl();
/// await textChannel.SendMessageAsync(userAvatarUrl);
/// </code>
/// </example>
/// <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>
/// <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.
/// A string representing the user's avatar URL; <c>null</c> if the user does not have an avatar in place.
/// </returns>
string GetAvatarUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128);
/// <summary>
/// Returns the URL to this user's default avatar.
/// Gets the default avatar URL for this user.
/// </summary>
/// <remarks>
/// This property retrieves a URL for this user's default avatar generated by Discord (Discord logo followed
/// by a random color as its background). This property will always return a value as it is calculated based
/// on the user's <see cref="IUser.DiscriminatorValue"/> (<c>discriminator % 5</c>).
/// </remarks>
/// <returns>
/// A string representing the user's avatar URL.
/// </returns>
string GetDefaultAvatarUrl();
/// <summary>
/// Gets the per-username unique ID for this user.
@@ -33,10 +56,14 @@ namespace Discord
/// </summary>
ushort DiscriminatorValue { get; }
/// <summary>
/// Gets a value that indicates whether this user is a bot user.
/// Gets a value that indicates whether this user is identified as a bot.
/// </summary>
/// <remarks>
/// This property retrieves a value that indicates whether this user is a registered bot application
/// (indicated by the blue BOT tag within the official chat client).
/// </remarks>
/// <returns>
/// <c>true</c> if the user is a bot; otherwise <c>false</c>.
/// <c>true</c> if the user is a bot application; otherwise <c>false</c>.
/// </returns>
bool IsBot { get; }
/// <summary>
@@ -52,11 +79,37 @@ namespace Discord
string Username { get; }

/// <summary>
/// Returns a direct message channel to this user, or create one if it does not already exist.
/// Gets the direct message channel of this user, or create one if it does not already exist.
/// </summary>
/// <remarks>
/// This method is used to obtain or create a channel used to send a direct message.
/// <note type="warning">
/// In event that the current user cannot send a message to the target user, a channel can and will still be
/// created by Discord. However, attempting to send a message will yield a
/// <see cref="Discord.Net.HttpException"/> with a 403 as its
/// <see cref="Discord.Net.HttpException.HttpCode"/>. There are currently no official workarounds by
/// Discord.
/// </note>
/// </remarks>
/// <example>
/// The following example attempts to send a direct message to the target user and logs the incident should
/// it fail.
/// <code language="cs">
/// var channel = await user.GetOrCreateDMChannelAsync();
/// try
/// {
/// await channel.SendMessageAsync("Awesome stuff!");
/// }
/// catch (Discord.Net.HttpException ex) when (ex.HttpCode == 403)
/// {
/// Console.WriteLine($"Boo, I cannot message {user}");
/// }
/// </code>
/// </example>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous operation for getting or creating a DM channel.
/// A task that represents the asynchronous operation for getting or creating a DM channel. The task result
/// contains the DM channel associated with this user.
/// </returns>
Task<IDMChannel> GetOrCreateDMChannelAsync(RequestOptions options = null);
}


+ 28
- 6
src/Discord.Net.Core/Entities/Users/IVoiceState.cs View File

@@ -6,28 +6,50 @@ namespace Discord
public interface IVoiceState
{
/// <summary>
/// Returns <c>true</c> if the guild has deafened this user.
/// Gets a value that indicates whether this user is deafened by the guild.
/// </summary>
/// <returns>
/// <c>true</c> if the user is deafened (i.e. not permitted to listen to or speak to others) by the guild;
/// otherwise <c>false</c>.
/// </returns>
bool IsDeafened { get; }
/// <summary>
/// Returns <c>true</c> if the guild has muted this user.
/// Gets a value that indicates whether this user is muted (i.e. not permitted to speak via voice) by the
/// guild.
/// </summary>
/// <returns>
/// <c>true</c> if this user is muted by the guild; otherwise <c>false</c>.
/// </returns>
bool IsMuted { get; }
/// <summary>
/// Returns <c>true</c> if this user has marked themselves as deafened.
/// Gets a value that indicates whether this user has marked themselves as deafened.
/// </summary>
/// <returns>
/// <c>true</c> if this user has deafened themselves (i.e. not permitted to listen to or speak to others); otherwise <c>false</c>.
/// </returns>
bool IsSelfDeafened { get; }
/// <summary>
/// Returns <c>true</c> if this user has marked themselves as muted.
/// Gets a value that indicates whether this user has marked themselves as muted (i.e. not permitted to
/// speak via voice).
/// </summary>
/// <returns>
/// <c>true</c> if this user has muted themselves; otherwise <c>false</c>.
/// </returns>
bool IsSelfMuted { get; }
/// <summary>
/// Returns <c>true</c> if the guild is temporarily blocking audio to/from this user.
/// Gets a value that indicates whether the user is muted by the current user.
/// </summary>
/// <returns>
/// <c>true</c> if the guild is temporarily blocking audio to/from this user; otherwise <c>false</c>.
/// </returns>
bool IsSuppressed { get; }
/// <summary>
/// Gets the voice channel this user is currently in, or <c>null</c> if none.
/// Gets the voice channel this user is currently in.
/// </summary>
/// <returns>
/// A generic voice channel object representing the voice channel that the user is currently in; <c>null</c>
/// if none.
/// </returns>
IVoiceChannel VoiceChannel { get; }
/// <summary>
/// Gets the unique identifier for this user's voice session.


+ 7
- 0
src/Discord.Net.Core/Extensions/EmbedBuilderExtensions.cs View File

@@ -68,6 +68,10 @@ namespace Discord
return builder;
}

/// <summary>
/// Adds the specified fields into this <see cref="EmbedBuilder"/>.
/// </summary>
/// <exception cref="ArgumentException">Field count exceeds <see cref="EmbedBuilder.MaxFieldCount"/>.</exception>
public static EmbedBuilder WithFields(this EmbedBuilder builder, IEnumerable<EmbedFieldBuilder> fields)
{
foreach (var field in fields)
@@ -75,6 +79,9 @@ namespace Discord

return builder;
}
/// <summary>
/// Adds the specified fields into this <see cref="EmbedBuilder"/>.
/// </summary>
public static EmbedBuilder WithFields(this EmbedBuilder builder, params EmbedFieldBuilder[] fields)
=> WithFields(builder, fields.AsEnumerable());
}


+ 117
- 9
src/Discord.Net.Core/Utils/Preconditions.cs View File

@@ -5,7 +5,9 @@ namespace Discord
internal static class Preconditions
{
//Objects
/// <exception cref="ArgumentNullException"><paramref name="obj"/> must not be <see langword="null"/>.</exception>
public static void NotNull<T>(T obj, string name, string msg = null) where T : class { if (obj == null) throw CreateNotNullException(name, msg); }
/// <exception cref="ArgumentNullException"><paramref name="obj"/> must not be <see langword="null"/>.</exception>
public static void NotNull<T>(Optional<T> obj, string name, string msg = null) where T : class { if (obj.IsSpecified && obj.Value == null) throw CreateNotNullException(name, msg); }

private static ArgumentNullException CreateNotNullException(string name, string msg)
@@ -15,13 +17,19 @@ namespace Discord
}

//Strings
/// <exception cref="ArgumentException"><paramref name="obj"/> cannot be blank.</exception>
public static void NotEmpty(string obj, string name, string msg = null) { if (obj.Length == 0) throw CreateNotEmptyException(name, msg); }
/// <exception cref="ArgumentException"><paramref name="obj"/> cannot be blank.</exception>
public static void NotEmpty(Optional<string> obj, string name, string msg = null) { if (obj.IsSpecified && obj.Value.Length == 0) throw CreateNotEmptyException(name, msg); }
/// <exception cref="ArgumentException"><paramref name="obj"/> cannot be blank.</exception>
/// <exception cref="ArgumentNullException"><paramref name="obj"/> must not be <see langword="null"/>.</exception>
public static void NotNullOrEmpty(string obj, string name, string msg = null)
{
if (obj == null) throw CreateNotNullException(name, msg);
if (obj.Length == 0) throw CreateNotEmptyException(name, msg);
}
/// <exception cref="ArgumentException"><paramref name="obj"/> cannot be blank.</exception>
/// <exception cref="ArgumentNullException"><paramref name="obj"/> must not be <see langword="null"/>.</exception>
public static void NotNullOrEmpty(Optional<string> obj, string name, string msg = null)
{
if (obj.IsSpecified)
@@ -30,11 +38,15 @@ namespace Discord
if (obj.Value.Length == 0) throw CreateNotEmptyException(name, msg);
}
}
/// <exception cref="ArgumentException"><paramref name="obj"/> cannot be blank.</exception>
/// <exception cref="ArgumentNullException"><paramref name="obj"/> must not be <see langword="null"/>.</exception>
public static void NotNullOrWhitespace(string obj, string name, string msg = null)
{
if (obj == null) throw CreateNotNullException(name, msg);
if (obj.Trim().Length == 0) throw CreateNotEmptyException(name, msg);
}
/// <exception cref="ArgumentException"><paramref name="obj"/> cannot be blank.</exception>
/// <exception cref="ArgumentNullException"><paramref name="obj"/> must not be <see langword="null"/>.</exception>
public static void NotNullOrWhitespace(Optional<string> obj, string name, string msg = null)
{
if (obj.IsSpecified)
@@ -48,121 +60,217 @@ namespace Discord
=> new ArgumentException(message: msg ?? "Argument cannot be blank.", paramName: name);

//Numerics
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(sbyte obj, sbyte value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(byte obj, byte value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(short obj, short value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(ushort obj, ushort value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(int obj, int value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(uint obj, uint value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(long obj, long value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(ulong obj, ulong value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<sbyte> obj, sbyte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<byte> obj, byte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<short> obj, short value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<ushort> obj, ushort value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<int> obj, int value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<uint> obj, uint value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<long> obj, long value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<ulong> obj, ulong value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(sbyte? obj, sbyte value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(byte? obj, byte value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(short? obj, short value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(ushort? obj, ushort value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(int? obj, int value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(uint? obj, uint value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(long? obj, long value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(ulong? obj, ulong value, string name, string msg = null) { if (obj == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<sbyte?> obj, sbyte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<byte?> obj, byte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<short?> obj, short value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<ushort?> obj, ushort value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<int?> obj, int value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<uint?> obj, uint value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<long?> obj, long value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }
/// <exception cref="ArgumentException">Value may not be equal to <paramref name="value"/>.</exception>
public static void NotEqual(Optional<ulong?> obj, ulong value, string name, string msg = null) { if (obj.IsSpecified && obj.Value == value) throw CreateNotEqualException(name, msg, value); }

private static ArgumentException CreateNotEqualException<T>(string name, string msg, T value)
=> new ArgumentException(message: msg ?? $"Value may not be equal to {value}", paramName: name);
=> new ArgumentException(message: msg ?? $"Value may not be equal to {value}.", paramName: name);
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(sbyte obj, sbyte value, string name, string msg = null) { if (obj < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(byte obj, byte value, string name, string msg = null) { if (obj < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(short obj, short value, string name, string msg = null) { if (obj < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(ushort obj, ushort value, string name, string msg = null) { if (obj < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(int obj, int value, string name, string msg = null) { if (obj < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(uint obj, uint value, string name, string msg = null) { if (obj < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(long obj, long value, string name, string msg = null) { if (obj < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(ulong obj, ulong value, string name, string msg = null) { if (obj < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(Optional<sbyte> obj, sbyte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(Optional<byte> obj, byte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(Optional<short> obj, short value, string name, string msg = null) { if (obj.IsSpecified && obj.Value < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(Optional<ushort> obj, ushort value, string name, string msg = null) { if (obj.IsSpecified && obj.Value < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(Optional<int> obj, int value, string name, string msg = null) { if (obj.IsSpecified && obj.Value < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(Optional<uint> obj, uint value, string name, string msg = null) { if (obj.IsSpecified && obj.Value < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(Optional<long> obj, long value, string name, string msg = null) { if (obj.IsSpecified && obj.Value < value) throw CreateAtLeastException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at least <paramref name="value"/>.</exception>
public static void AtLeast(Optional<ulong> obj, ulong value, string name, string msg = null) { if (obj.IsSpecified && obj.Value < value) throw CreateAtLeastException(name, msg, value); }

private static ArgumentException CreateAtLeastException<T>(string name, string msg, T value)
=> new ArgumentException(message: msg ?? $"Value must be at least {value}", paramName: name);

=> new ArgumentException(message: msg ?? $"Value must be at least {value}.", paramName: name);
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(sbyte obj, sbyte value, string name, string msg = null) { if (obj <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(byte obj, byte value, string name, string msg = null) { if (obj <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(short obj, short value, string name, string msg = null) { if (obj <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(ushort obj, ushort value, string name, string msg = null) { if (obj <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(int obj, int value, string name, string msg = null) { if (obj <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(uint obj, uint value, string name, string msg = null) { if (obj <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(long obj, long value, string name, string msg = null) { if (obj <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(ulong obj, ulong value, string name, string msg = null) { if (obj <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(Optional<sbyte> obj, sbyte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(Optional<byte> obj, byte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(Optional<short> obj, short value, string name, string msg = null) { if (obj.IsSpecified && obj.Value <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(Optional<ushort> obj, ushort value, string name, string msg = null) { if (obj.IsSpecified && obj.Value <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(Optional<int> obj, int value, string name, string msg = null) { if (obj.IsSpecified && obj.Value <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(Optional<uint> obj, uint value, string name, string msg = null) { if (obj.IsSpecified && obj.Value <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(Optional<long> obj, long value, string name, string msg = null) { if (obj.IsSpecified && obj.Value <= value) throw CreateGreaterThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be greater than <paramref name="value"/>.</exception>
public static void GreaterThan(Optional<ulong> obj, ulong value, string name, string msg = null) { if (obj.IsSpecified && obj.Value <= value) throw CreateGreaterThanException(name, msg, value); }

private static ArgumentException CreateGreaterThanException<T>(string name, string msg, T value)
=> new ArgumentException(message: msg ?? $"Value must be greater than {value}", paramName: name);

=> new ArgumentException(message: msg ?? $"Value must be greater than {value}.", paramName: name);
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(sbyte obj, sbyte value, string name, string msg = null) { if (obj > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(byte obj, byte value, string name, string msg = null) { if (obj > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(short obj, short value, string name, string msg = null) { if (obj > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(ushort obj, ushort value, string name, string msg = null) { if (obj > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(int obj, int value, string name, string msg = null) { if (obj > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(uint obj, uint value, string name, string msg = null) { if (obj > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(long obj, long value, string name, string msg = null) { if (obj > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(ulong obj, ulong value, string name, string msg = null) { if (obj > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(Optional<sbyte> obj, sbyte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(Optional<byte> obj, byte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(Optional<short> obj, short value, string name, string msg = null) { if (obj.IsSpecified && obj.Value > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(Optional<ushort> obj, ushort value, string name, string msg = null) { if (obj.IsSpecified && obj.Value > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(Optional<int> obj, int value, string name, string msg = null) { if (obj.IsSpecified && obj.Value > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(Optional<uint> obj, uint value, string name, string msg = null) { if (obj.IsSpecified && obj.Value > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(Optional<long> obj, long value, string name, string msg = null) { if (obj.IsSpecified && obj.Value > value) throw CreateAtMostException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be at most <paramref name="value"/>.</exception>
public static void AtMost(Optional<ulong> obj, ulong value, string name, string msg = null) { if (obj.IsSpecified && obj.Value > value) throw CreateAtMostException(name, msg, value); }

private static ArgumentException CreateAtMostException<T>(string name, string msg, T value)
=> new ArgumentException(message: msg ?? $"Value must be at most {value}", paramName: name);

=> new ArgumentException(message: msg ?? $"Value must be at most {value}.", paramName: name);
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(sbyte obj, sbyte value, string name, string msg = null) { if (obj >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(byte obj, byte value, string name, string msg = null) { if (obj >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(short obj, short value, string name, string msg = null) { if (obj >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(ushort obj, ushort value, string name, string msg = null) { if (obj >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(int obj, int value, string name, string msg = null) { if (obj >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(uint obj, uint value, string name, string msg = null) { if (obj >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(long obj, long value, string name, string msg = null) { if (obj >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(ulong obj, ulong value, string name, string msg = null) { if (obj >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(Optional<sbyte> obj, sbyte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(Optional<byte> obj, byte value, string name, string msg = null) { if (obj.IsSpecified && obj.Value >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(Optional<short> obj, short value, string name, string msg = null) { if (obj.IsSpecified && obj.Value >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(Optional<ushort> obj, ushort value, string name, string msg = null) { if (obj.IsSpecified && obj.Value >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(Optional<int> obj, int value, string name, string msg = null) { if (obj.IsSpecified && obj.Value >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(Optional<uint> obj, uint value, string name, string msg = null) { if (obj.IsSpecified && obj.Value >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(Optional<long> obj, long value, string name, string msg = null) { if (obj.IsSpecified && obj.Value >= value) throw CreateLessThanException(name, msg, value); }
/// <exception cref="ArgumentException">Value must be less than <paramref name="value"/>.</exception>
public static void LessThan(Optional<ulong> obj, ulong value, string name, string msg = null) { if (obj.IsSpecified && obj.Value >= value) throw CreateLessThanException(name, msg, value); }

private static ArgumentException CreateLessThanException<T>(string name, string msg, T value)
=> new ArgumentException(message: msg ?? $"Value must be less than {value}", paramName: name);
=> new ArgumentException(message: msg ?? $"Value must be less than {value}.", paramName: name);

// Bulk Delete
/// <exception cref="ArgumentOutOfRangeException">Messages are younger than 2 weeks.</exception>
@@ -182,7 +290,7 @@ namespace Discord
for (var i = 0; i < roles.Length; i++)
{
if (roles[i] == guildId)
throw new ArgumentException(message: "The everyone role cannot be assigned to a user", paramName: name);
throw new ArgumentException(message: "The everyone role cannot be assigned to a user.", paramName: name);
}
}
}


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

@@ -348,6 +348,16 @@ namespace Discord.API
var ids = new BucketIds(channelId: channelId);
return await SendAsync<Channel>("DELETE", () => $"channels/{channelId}", ids, options: options).ConfigureAwait(false);
}
/// <exception cref="ArgumentException">
/// <paramref name="channelId"/> must not be equal to zero.
/// -and-
/// <paramref name="args.Position"/> must be greater than zero.
/// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="args"/> must not be <see langword="null"/>.
/// -and-
/// <paramref name="args.Name"/> must not be <see langword="null"/> or empty.
/// </exception>
public async Task<Channel> ModifyGuildChannelAsync(ulong channelId, Rest.ModifyGuildChannelParams args, RequestOptions options = null)
{
Preconditions.NotEqual(channelId, 0, nameof(channelId));
@@ -836,6 +846,12 @@ namespace Discord.API
var ids = new BucketIds(guildId: guildId);
return await SendAsync<Ban>("GET", () => $"guilds/{guildId}/bans/{userId}", ids, options: options).ConfigureAwait(false);
}
/// <exception cref="ArgumentException">
/// <paramref name="guildId"/> and <paramref name="userId"/> must not be equal to zero.
/// -and-
/// <paramref name="args.DeleteMessageDays"/> must be between 0 to 7.
/// </exception>
/// <exception cref="ArgumentNullException"><paramref name="args"/> must not be <see langword="null"/>.</exception>
public async Task CreateGuildBanAsync(ulong guildId, ulong userId, CreateGuildBanParams args, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
@@ -849,6 +865,7 @@ namespace Discord.API
string reason = string.IsNullOrWhiteSpace(args.Reason) ? "" : $"&reason={Uri.EscapeDataString(args.Reason)}";
await SendAsync("PUT", () => $"guilds/{guildId}/bans/{userId}?delete-message-days={args.DeleteMessageDays}{reason}", ids, options: options).ConfigureAwait(false);
}
/// <exception cref="ArgumentException"><paramref name="guildId"/> and <paramref name="userId"/> must not be equal to zero.</exception>
public async Task RemoveGuildBanAsync(ulong guildId, ulong userId, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
@@ -860,6 +877,7 @@ namespace Discord.API
}

//Guild Embeds
/// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception>
public async Task<GuildEmbed> GetGuildEmbedAsync(ulong guildId, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
@@ -872,6 +890,8 @@ namespace Discord.API
}
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; }
}
/// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception>
/// <exception cref="ArgumentNullException"><paramref name="args"/> must not be <see langword="null"/>.</exception>
public async Task<GuildEmbed> ModifyGuildEmbedAsync(ulong guildId, Rest.ModifyGuildEmbedParams args, RequestOptions options = null)
{
Preconditions.NotNull(args, nameof(args));
@@ -883,6 +903,7 @@ namespace Discord.API
}

//Guild Integrations
/// <exception cref="ArgumentException"><paramref name="guildId"/> must not be equal to zero.</exception>
public async Task<IReadOnlyCollection<Integration>> GetGuildIntegrationsAsync(ulong guildId, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
@@ -891,6 +912,8 @@ namespace Discord.API
var ids = new BucketIds(guildId: guildId);
return await SendAsync<IReadOnlyCollection<Integration>>("GET", () => $"guilds/{guildId}/integrations", ids, options: options).ConfigureAwait(false);
}
/// <exception cref="ArgumentException"><paramref name="guildId"/> and <paramref name="args.Id"/> must not be equal to zero.</exception>
/// <exception cref="ArgumentNullException"><paramref name="args"/> must not be <see langword="null"/>.</exception>
public async Task<Integration> CreateGuildIntegrationAsync(ulong guildId, CreateGuildIntegrationParams args, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
@@ -933,6 +956,8 @@ namespace Discord.API
}

//Guild Invites
/// <exception cref="ArgumentException"><paramref name="inviteId"/> cannot be blank.</exception>
/// <exception cref="ArgumentNullException"><paramref name="inviteId"/> must not be <see langword="null"/>.</exception>
public async Task<InviteMetadata> GetInviteAsync(string inviteId, RequestOptions options = null)
{
Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId));
@@ -952,6 +977,7 @@ namespace Discord.API
}
catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; }
}
/// <exception cref="ArgumentException"><paramref name="guildId"/> may not be equal to zero.</exception>
public async Task<InviteVanity> GetVanityInviteAsync(ulong guildId, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
@@ -960,6 +986,7 @@ namespace Discord.API
var ids = new BucketIds(guildId: guildId);
return await SendAsync<InviteVanity>("GET", () => $"guilds/{guildId}/vanity-url", ids, options: options).ConfigureAwait(false);
}
/// <exception cref="ArgumentException"><paramref name="guildId"/> may not be equal to zero.</exception>
public async Task<IReadOnlyCollection<InviteMetadata>> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null)
{
Preconditions.NotEqual(guildId, 0, nameof(guildId));
@@ -968,6 +995,7 @@ namespace Discord.API
var ids = new BucketIds(guildId: guildId);
return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", () => $"guilds/{guildId}/invites", ids, options: options).ConfigureAwait(false);
}
/// <exception cref="ArgumentException"><paramref name="channelId"/> may not be equal to zero.</exception>
public async Task<IReadOnlyCollection<InviteMetadata>> GetChannelInvitesAsync(ulong channelId, RequestOptions options = null)
{
Preconditions.NotEqual(channelId, 0, nameof(channelId));
@@ -976,6 +1004,14 @@ namespace Discord.API
var ids = new BucketIds(channelId: channelId);
return await SendAsync<IReadOnlyCollection<InviteMetadata>>("GET", () => $"channels/{channelId}/invites", ids, options: options).ConfigureAwait(false);
}
/// <exception cref="ArgumentException">
/// <paramref name="channelId"/> may not be equal to zero.
/// -and-
/// <paramref name="args.MaxAge"/> and <paramref name="args.MaxUses"/> must be greater than zero.
/// -and-
/// <paramref name="args.MaxAge"/> must be lesser than 86400.
/// </exception>
/// <exception cref="ArgumentNullException"><paramref name="args"/> must not be <see langword="null"/>.</exception>
public async Task<InviteMetadata> CreateChannelInviteAsync(ulong channelId, CreateChannelInviteParams args, RequestOptions options = null)
{
Preconditions.NotEqual(channelId, 0, nameof(channelId));


+ 8
- 0
src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs View File

@@ -73,6 +73,13 @@ namespace Discord.Rest
var models = await client.ApiClient.GetChannelInvitesAsync(channel.Id, options).ConfigureAwait(false);
return models.Select(x => RestInviteMetadata.Create(client, null, channel, x)).ToImmutableArray();
}
/// <exception cref="ArgumentException">
/// <paramref name="channel.Id"/> may not be equal to zero.
/// -and-
/// <paramref name="maxAge"/> and <paramref name="maxUses"/> must be greater than zero.
/// -and-
/// <paramref name="maxAge"/> must be lesser than 86400.
/// </exception>
public static async Task<RestInviteMetadata> CreateInviteAsync(IGuildChannel channel, BaseDiscordClient client,
int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options)
{
@@ -352,6 +359,7 @@ namespace Discord.Rest
var model = await client.ApiClient.GetChannelAsync(channel.CategoryId.Value, options).ConfigureAwait(false);
return RestCategoryChannel.Create(client, model) as ICategoryChannel;
}
/// <exception cref="InvalidOperationException">This channel does not have a parent channel.</exception>
public static async Task SyncPermissionsAsync(INestedChannel channel, BaseDiscordClient client, RequestOptions options)
{
var category = await GetCategoryAsync(channel, client, options).ConfigureAwait(false);


+ 23
- 83
src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs View File

@@ -12,6 +12,10 @@ namespace Discord.Rest
/// <summary>
/// Sends a message to this message channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.SendMessageAsync"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="text">The message to be sent.</param>
/// <param name="isTTS">Determines whether the message should be read aloud by Discord or not.</param>
/// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param>
@@ -25,12 +29,9 @@ namespace Discord.Rest
/// Sends a file to this message channel with an optional caption.
/// </summary>
/// <remarks>
/// This method sends a file as if you are uploading an attachment directly from your Discord client.
/// <note>
/// If you wish to upload an image and have it embedded in a <see cref="Discord.EmbedType.Rich"/>embed,
/// you may upload the file and refer to the file with "attachment://filename.ext" in the
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>.
/// </note>
/// This method follows the same behavior as described in
/// <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions)"/>. Please visit
/// its documentation for more details on this method.
/// </remarks>
/// <param name="filePath">The file path of the file.</param>
/// <param name="text">The message to be sent.</param>
@@ -46,12 +47,8 @@ namespace Discord.Rest
/// Sends a file to this message channel with an optional caption.
/// </summary>
/// <remarks>
/// This method sends a file as if you are uploading an attachment directly from your Discord client.
/// <note>
/// If you wish to upload an image and have it embedded in a <see cref="Discord.EmbedType.Rich"/>embed,
/// you may upload the file and refer to the file with "attachment://filename.ext" in the
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>.
/// </note>
/// This method follows the same behavior as described in <see cref="IMessageChannel.SendFileAsync(Stream, string, string, bool, Embed, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="stream">The <see cref="Stream" /> of the file to be sent.</param>
/// <param name="filename">The name of the attachment.</param>
@@ -68,6 +65,10 @@ namespace Discord.Rest
/// <summary>
/// Gets a message from this message channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessageAsync(ulong, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="id">The snowflake identifier of the message.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
@@ -79,30 +80,9 @@ namespace Discord.Rest
/// Gets the last N messages from this message channel.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
/// rate limit, causing your bot to freeze!
/// </note>
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/>. The
/// library will attempt to split up the requests according to your <paramref name="limit"/> and
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
/// of flattening.
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <example>
/// The following example downloads 300 messages and gets messages that belong to the user
/// <c>53905483156684800</c>.
/// <code lang="cs">
/// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync();
/// var userMessages = messages.Where(x =&gt; x.Author.Id == 53905483156684800);
/// </code>
/// </example>
/// <param name="limit">The numbers of message to be gotten from.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
@@ -113,34 +93,12 @@ namespace Discord.Rest
/// Gets a collection of messages in this channel.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
/// rate limit, causing your bot to freeze!
/// </note>
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/> around
/// the message <paramref name="fromMessageId"/> depending on the <paramref name="dir"/>. The library will
/// attempt to split up the requests according to your <paramref name="limit"/> and
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
/// of flattening.
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(ulong, Direction, int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <example>
/// The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.
/// <code lang="cs">
/// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync();
/// </code>
/// </example>
/// <param name="fromMessageId">The ID of the starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param>
/// <param name="limit">The numbers of message to be gotten from.</param>
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from
/// cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// Paged collection of messages.
@@ -150,34 +108,12 @@ namespace Discord.Rest
/// Gets a collection of messages in this channel.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
/// rate limit, causing your bot to freeze!
/// </note>
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/> around
/// the message <paramref name="fromMessage"/> depending on the <paramref name="dir"/>. The library will
/// attempt to split up the requests according to your <paramref name="limit"/> and
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
/// of flattening.
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(IMessage, Direction, int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <example>
/// The following example gets 5 message prior to a specific message, <c>oldMessage</c>.
/// <code lang="cs">
/// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync();
/// </code>
/// </example>
/// <param name="fromMessage">The starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param>
/// <param name="limit">The numbers of message to be gotten from.</param>
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from
/// cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// Paged collection of messages.
@@ -186,6 +122,10 @@ namespace Discord.Rest
/// <summary>
/// Gets a collection of pinned messages in this channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetPinnedMessagesAsync"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation for retrieving pinned messages in this channel.


+ 2
- 0
src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs View File

@@ -16,6 +16,7 @@ namespace Discord.Rest
{
/// <inheritdoc />
public string Topic { get; private set; }
/// <inheritdoc />
public int SlowModeInterval { get; private set; }
/// <inheritdoc />
public ulong? CategoryId { get; private set; }
@@ -201,6 +202,7 @@ namespace Discord.Rest
/// </returns>
public Task<ICategoryChannel> GetCategoryAsync(RequestOptions options = null)
=> ChannelHelper.GetCategoryAsync(this, Discord, options);
/// <inheritdoc />
public Task SyncPermissionsAsync(RequestOptions options = null)
=> ChannelHelper.SyncPermissionsAsync(this, Discord, options);



+ 1
- 1
src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs View File

@@ -57,7 +57,7 @@ namespace Discord.Rest
/// </returns>
public Task<ICategoryChannel> GetCategoryAsync(RequestOptions options = null)
=> ChannelHelper.GetCategoryAsync(this, Discord, options);
/// <inheritdoc />
public Task SyncPermissionsAsync(RequestOptions options = null)
=> ChannelHelper.SyncPermissionsAsync(this, Discord, options);


+ 1
- 3
src/Discord.Net.Rest/Entities/Messages/Attachment.cs View File

@@ -3,9 +3,7 @@ using Model = Discord.API.Attachment;

namespace Discord
{
/// <summary>
/// An attachment file seen in a <see cref="IUserMessage"/>.
/// </summary>
/// <inheritdoc/>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class Attachment : IAttachment
{


+ 40
- 0
src/Discord.Net.WebSocket/DiscordSocketConfig.cs View File

@@ -7,6 +7,22 @@ namespace Discord.WebSocket
/// <summary>
/// Represents a configuration class for <see cref="DiscordSocketClient"/>.
/// </summary>
/// <remarks>
/// This configuration, based on <see cref="DiscordRestConfig"/>, helps determine several key configurations the
/// socket client depend on. For instance, shards and connection timeout.
/// </remarks>
/// <example>
/// The following config enables the message cache and configures the client to always download user upon guild
/// availability.
/// <code language="cs">
/// var config = new DiscordSocketConfig
/// {
/// AlwaysDownloadUsers = true,
/// MessageCacheSize = 100
/// };
/// var client = new DiscordSocketClient(config);
/// </code>
/// </example>
public class DiscordSocketConfig : DiscordRestConfig
{
/// <summary>
@@ -57,6 +73,30 @@ namespace Discord.WebSocket
/// <summary>
/// Gets or sets whether or not all users should be downloaded as guilds come available.
/// </summary>
/// <remarks>
/// <para>
/// By default, the Discord gateway will only send offline members if a guild has less than a certain number
/// of members (determined by <see cref="LargeThreshold"/> in this library). This behaviour is why
/// sometimes a user may be missing from the WebSocket cache for collections such as
/// <see cref="Discord.WebSocket.SocketGuild.Users"/>.
/// </para>
/// <para>
/// This property ensures that whenever a guild becomes available (determined by
/// <see cref="Discord.WebSocket.BaseSocketClient.GuildAvailable"/>), incomplete user chunks will be
/// downloaded to the WebSocket cache.
/// </para>
/// <para>
/// For more information, please see
/// <see href="https://discordapp.com/developers/docs/topics/gateway#request-guild-members">Request Guild Members</see>
/// on the official Discord API documentation.
/// </para>
/// <note>
/// Please note that it can be difficult to fill the cache completely on large guilds depending on the
/// traffic. If you are using the command system, the default user TypeReader may fail to find the user
/// due to this issue. This may be resolved at v3 of the library. Until then, you may want to consider
/// overriding the TypeReader and use <see cref="DiscordRestClient.GetGuildUserAsync"/> as a backup.
/// </note>
/// </remarks>
public bool AlwaysDownloadUsers { get; set; } = false;
/// <summary>
/// Gets or sets the timeout for event handlers, in milliseconds, after which a warning will be logged. Null


+ 71
- 20
src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs View File

@@ -21,6 +21,10 @@ namespace Discord.WebSocket
/// <summary>
/// Sends a message to this message channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.SendMessageAsync"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="text">The message to be sent.</param>
/// <param name="isTTS">Determines whether the message should be read aloud by Discord or not.</param>
/// <param name="embed">The <see cref="Discord.EmbedType.Rich"/> <see cref="Embed"/> to be sent.</param>
@@ -34,12 +38,8 @@ namespace Discord.WebSocket
/// Sends a file to this message channel with an optional caption.
/// </summary>
/// <remarks>
/// This method sends a file as if you are uploading an attachment directly from your Discord client.
/// <note>
/// If you wish to upload an image and have it embedded in a <see cref="Discord.EmbedType.Rich"/>embed,
/// you may upload the file and refer to the file with "attachment://filename.ext" in the
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>.
/// </note>
/// This method follows the same behavior as described in <see cref="IMessageChannel.SendFileAsync(string, string, bool, Embed, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="filePath">The file path of the file.</param>
/// <param name="text">The message to be sent.</param>
@@ -55,12 +55,8 @@ namespace Discord.WebSocket
/// Sends a file to this message channel with an optional caption.
/// </summary>
/// <remarks>
/// This method sends a file as if you are uploading an attachment directly from your Discord client.
/// <note>
/// If you wish to upload an image and have it embedded in a <see cref="Discord.EmbedType.Rich"/>embed,
/// you may upload the file and refer to the file with "attachment://filename.ext" in the
/// <see cref="Discord.EmbedBuilder.ImageUrl"/>.
/// </note>
/// This method follows the same behavior as described in <see cref="IMessageChannel.SendFileAsync(Stream, string, string, bool, Embed, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="stream">The <see cref="Stream" /> of the file to be sent.</param>
/// <param name="filename">The name of the attachment.</param>
@@ -75,16 +71,41 @@ namespace Discord.WebSocket
new Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);

/// <summary>
/// Gets the cached message if one exists.
/// Gets a cached message from this channel.
/// </summary>
/// <param name="id">The ID of the message.</param>
/// <remarks>
/// <note type="warning">
/// This method requires the use of cache, which is not enabled by default; if caching is not enabled,
/// this method will always return <c>null</c>. Please refer to
/// <see cref="Discord.WebSocket.DiscordSocketConfig.MessageCacheSize" /> for more details.
/// </note>
/// <para>
/// This method retrieves the message from the local WebSocket cache and does not send any additional
/// request to Discord. This message may be a message that has been deleted.
/// </para>
/// </remarks>
/// <param name="id">The snowflake identifier of the message.</param>
/// <returns>
/// Cached message object; <c>null</c> if it doesn't exist in the cache.
/// A WebSocket-based message object; <c>null</c> if it does not exist in the cache or if caching is not
/// enabled.
/// </returns>
SocketMessage GetCachedMessage(ulong id);
/// <summary>
/// Gets the last N messages from this message channel.
/// Gets the last N cached messages from this message channel.
/// </summary>
/// <remarks>
/// <note type="warning">
/// This method requires the use of cache, which is not enabled by default; if caching is not enabled,
/// this method will always return an empty collection. Please refer to
/// <see cref="Discord.WebSocket.DiscordSocketConfig.MessageCacheSize" /> for more details.
/// </note>
/// <para>
/// This method retrieves the message(s) from the local WebSocket cache and does not send any additional
/// request to Discord. This read-only collection may include messages that have been deleted. The
/// maximum number of messages that can be retrieved from this method depends on the
/// <see cref="Discord.WebSocket.DiscordSocketConfig.MessageCacheSize" /> set.
/// </para>
/// </remarks>
/// <param name="limit">The number of messages to get.</param>
/// <returns>
/// A read-only collection of WebSocket-based messages.
@@ -92,8 +113,21 @@ namespace Discord.WebSocket
IReadOnlyCollection<SocketMessage> GetCachedMessages(int limit = DiscordConfig.MaxMessagesPerBatch);

/// <summary>
/// Gets a collection of messages in this channel.
/// Gets the last N cached messages starting from a certain message in this message channel.
/// </summary>
/// <remarks>
/// <note type="warning">
/// This method requires the use of cache, which is not enabled by default; if caching is not enabled,
/// this method will always return an empty collection. Please refer to
/// <see cref="Discord.WebSocket.DiscordSocketConfig.MessageCacheSize" /> for more details.
/// </note>
/// <para>
/// This method retrieves the message(s) from the local WebSocket cache and does not send any additional
/// request to Discord. This read-only collection may include messages that have been deleted. The
/// maximum number of messages that can be retrieved from this method depends on the
/// <see cref="Discord.WebSocket.DiscordSocketConfig.MessageCacheSize" /> set.
/// </para>
/// </remarks>
/// <param name="fromMessageId">The message ID to start the fetching from.</param>
/// <param name="dir">The direction of which the message should be gotten from.</param>
/// <param name="limit">The number of messages to get.</param>
@@ -102,8 +136,21 @@ namespace Discord.WebSocket
/// </returns>
IReadOnlyCollection<SocketMessage> GetCachedMessages(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch);
/// <summary>
/// Gets a collection of messages in this channel.
/// Gets the last N cached messages starting from a certain message in this message channel.
/// </summary>
/// <remarks>
/// <note type="warning">
/// This method requires the use of cache, which is not enabled by default; if caching is not enabled,
/// this method will always return an empty collection. Please refer to
/// <see cref="Discord.WebSocket.DiscordSocketConfig.MessageCacheSize" /> for more details.
/// </note>
/// <para>
/// This method retrieves the message(s) from the local WebSocket cache and does not send any additional
/// request to Discord. This read-only collection may include messages that have been deleted. The
/// maximum number of messages that can be retrieved from this method depends on the
/// <see cref="Discord.WebSocket.DiscordSocketConfig.MessageCacheSize" /> set.
/// </para>
/// </remarks>
/// <param name="fromMessage">The message to start the fetching from.</param>
/// <param name="dir">The direction of which the message should be gotten from.</param>
/// <param name="limit">The number of messages to get.</param>
@@ -112,12 +159,16 @@ namespace Discord.WebSocket
/// </returns>
IReadOnlyCollection<SocketMessage> GetCachedMessages(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch);
/// <summary>
/// Gets a collection of pinned messages in this channel.
/// Gets a read-only collection of pinned messages in this channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetPinnedMessagesAsync"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation for retrieving pinned messages in this channel.
/// The task result contains a collection of messages found in the pinned messages.
/// The task result contains a read-only collection of messages found in the pinned messages.
/// </returns>
new Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null);
}


+ 6
- 67
src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs View File

@@ -78,30 +78,9 @@ namespace Discord.WebSocket
/// Gets the last N messages from this message channel.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
/// rate limit, causing your bot to freeze!
/// </note>
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/>. The
/// library will attempt to split up the requests according to your <paramref name="limit"/> and
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
/// of flattening.
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <example>
/// The following example downloads 300 messages and gets messages that belong to the user
/// <c>53905483156684800</c>.
/// <code lang="cs">
/// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync();
/// var userMessages = messages.Where(x =&gt; x.Author.Id == 53905483156684800);
/// </code>
/// </example>
/// <param name="limit">The numbers of message to be gotten from.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
@@ -113,29 +92,9 @@ namespace Discord.WebSocket
/// Gets a collection of messages in this channel.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
/// rate limit, causing your bot to freeze!
/// </note>
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/> around
/// the message <paramref name="fromMessageId"/> depending on the <paramref name="dir"/>. The library will
/// attempt to split up the requests according to your <paramref name="limit"/> and
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
/// of flattening.
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(ulong, Direction, int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <example>
/// The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.
/// <code lang="cs">
/// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync();
/// </code>
/// </example>
/// <param name="fromMessageId">The ID of the starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param>
/// <param name="limit">The numbers of message to be gotten from.</param>
@@ -149,29 +108,9 @@ namespace Discord.WebSocket
/// Gets a collection of messages in this channel.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
/// rate limit, causing your bot to freeze!
/// </note>
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/> around
/// the message <paramref name="fromMessage"/> depending on the <paramref name="dir"/>. The library will
/// attempt to split up the requests according to your <paramref name="limit"/> and
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
/// of flattening.
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(IMessage, Direction, int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <example>
/// The following example gets 5 message prior to a specific message, <c>oldMessage</c>.
/// <code lang="cs">
/// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync();
/// </code>
/// </example>
/// <param name="fromMessage">The starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param>
/// <param name="limit">The numbers of message to be gotten from.</param>


+ 54
- 0
src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs View File

@@ -81,6 +81,19 @@ namespace Discord.WebSocket
/// <inheritdoc />
public SocketMessage GetCachedMessage(ulong id)
=> _messages?.Get(id);
/// <summary>
/// Gets a message from this message channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessageAsync"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="id">The snowflake identifier of the message.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents an asynchronous get operation for retrieving the message. The task result contains
/// the retrieved message; <c>null</c> if no message is found with the specified identifier.
/// </returns>
public async Task<IMessage> GetMessageAsync(ulong id, RequestOptions options = null)
{
IMessage msg = _messages?.Get(id);
@@ -88,10 +101,51 @@ namespace Discord.WebSocket
msg = await ChannelHelper.GetMessageAsync(this, Discord, id, options).ConfigureAwait(false);
return msg;
}

/// <summary>
/// Gets the last N messages from this message channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="limit">The numbers of message to be gotten from.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// Paged collection of messages.
/// </returns>
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, null, Direction.Before, limit, CacheMode.AllowDownload, options);
/// <summary>
/// Gets a collection of messages in this channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(ulong, Direction, int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="fromMessageId">The ID of the starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param>
/// <param name="limit">The numbers of message to be gotten from.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// Paged collection of messages.
/// </returns>
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessageId, dir, limit, CacheMode.AllowDownload, options);
/// <summary>
/// Gets a collection of messages in this channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(IMessage, Direction, int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="fromMessage">The starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param>
/// <param name="limit">The numbers of message to be gotten from.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// Paged collection of messages.
/// </returns>
public IAsyncEnumerable<IReadOnlyCollection<IMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, CacheMode.AllowDownload, options);
/// <inheritdoc />


+ 13
- 67
src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs View File

@@ -20,6 +20,7 @@ namespace Discord.WebSocket

/// <inheritdoc />
public string Topic { get; private set; }
/// <inheritdoc />
public int SlowModeInterval { get; private set; }
/// <inheritdoc />
public ulong? CategoryId { get; private set; }
@@ -31,6 +32,7 @@ namespace Discord.WebSocket
/// </returns>
public ICategoryChannel Category
=> CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null;
/// <inheritdoc />
public Task SyncPermissionsAsync(RequestOptions options = null)
=> ChannelHelper.SyncPermissionsAsync(this, Discord, options);

@@ -80,6 +82,10 @@ namespace Discord.WebSocket
/// <summary>
/// Gets a message from this message channel.
/// </summary>
/// <remarks>
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessageAsync"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <param name="id">The snowflake identifier of the message.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
@@ -93,34 +99,14 @@ namespace Discord.WebSocket
msg = await ChannelHelper.GetMessageAsync(this, Discord, id, options).ConfigureAwait(false);
return msg;
}

/// <summary>
/// Gets the last N messages from this message channel.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
/// rate limit, causing your bot to freeze!
/// </note>
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/>. The
/// library will attempt to split up the requests according to your <paramref name="limit"/> and
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
/// of flattening.
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <example>
/// The following example downloads 300 messages and gets messages that belong to the user
/// <c>53905483156684800</c>.
/// <code lang="cs">
/// var messages = await messageChannel.GetMessagesAsync(300).FlattenAsync();
/// var userMessages = messages.Where(x =&gt; x.Author.Id == 53905483156684800);
/// </code>
/// </example>
/// <param name="limit">The numbers of message to be gotten from.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
@@ -132,29 +118,9 @@ namespace Discord.WebSocket
/// Gets a collection of messages in this channel.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
/// rate limit, causing your bot to freeze!
/// </note>
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/> around
/// the message <paramref name="fromMessageId"/> depending on the <paramref name="dir"/>. The library will
/// attempt to split up the requests according to your <paramref name="limit"/> and
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
/// of flattening.
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(ulong, Direction, int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <example>
/// The following example gets 5 message prior to the message identifier <c>442012544660537354</c>.
/// <code lang="cs">
/// var messages = await channel.GetMessagesAsync(442012544660537354, Direction.Before, 5).FlattenAsync();
/// </code>
/// </example>
/// <param name="fromMessageId">The ID of the starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param>
/// <param name="limit">The numbers of message to be gotten from.</param>
@@ -168,29 +134,9 @@ namespace Discord.WebSocket
/// Gets a collection of messages in this channel.
/// </summary>
/// <remarks>
/// <note type="important">
/// The returned collection is an asynchronous enumerable object; one must call
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> to access the individual messages as a
/// collection.
/// </note>
/// <note type="warning">
/// Do not fetch too many messages at once! This may cause unwanted preemptive rate limit or even actual
/// rate limit, causing your bot to freeze!
/// </note>
/// This method will attempt to fetch the number of messages specified under <paramref name="limit"/> around
/// the message <paramref name="fromMessage"/> depending on the <paramref name="dir"/>. The library will
/// attempt to split up the requests according to your <paramref name="limit"/> and
/// <see cref="DiscordConfig.MaxMessagesPerBatch"/>. In other words, should the user request 500 messages,
/// and the <see cref="Discord.DiscordConfig.MaxMessagesPerBatch"/> constant is <c>100</c>, the request will
/// be split into 5 individual requests; thus returning 5 individual asynchronous responses, hence the need
/// of flattening.
/// This method follows the same behavior as described in <see cref="IMessageChannel.GetMessagesAsync(IMessage, Direction, int, CacheMode, RequestOptions)"/>.
/// Please visit its documentation for more details on this method.
/// </remarks>
/// <example>
/// The following example gets 5 message prior to a specific message, <c>oldMessage</c>.
/// <code lang="cs">
/// var messages = await channel.GetMessagesAsync(oldMessage, Direction.Before, 5).FlattenAsync();
/// </code>
/// </example>
/// <param name="fromMessage">The starting message to get the messages from.</param>
/// <param name="dir">The direction of the messages to be gotten from.</param>
/// <param name="limit">The numbers of message to be gotten from.</param>


+ 1
- 0
src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs View File

@@ -30,6 +30,7 @@ namespace Discord.WebSocket
/// </returns>
public ICategoryChannel Category
=> CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null;
/// <inheritdoc />
public Task SyncPermissionsAsync(RequestOptions options = null)
=> ChannelHelper.SyncPermissionsAsync(this, Discord, options);



+ 3
- 0
src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs View File

@@ -38,6 +38,9 @@ namespace Discord.WebSocket
public IActivity Activity => Presence.Activity;
/// <inheritdoc />
public UserStatus Status => Presence.Status;
/// <summary>
/// Gets mutual guilds shared with this user.
/// </summary>
public IReadOnlyCollection<SocketGuild> MutualGuilds
=> Discord.Guilds.Where(g => g.Users.Any(u => u.Id == Id)).ToImmutableArray();



Loading…
Cancel
Save