Browse Source

Squashed commit of the following:

commit 17a4af71ee
Author: Still Hsu <341464@gmail.com>
Date:   Tue May 29 17:15:10 2018 +0800

    Add documentation for new API version & few events

commit f4fb027ea9
Merge: d74c5287 3acf2a9a
Author: Still Hsu <341464@gmail.com>
Date:   Tue May 29 08:25:41 2018 +0800

    Merge remote-tracking branch 'upstream/dev' into docs/faq-n-patches-offline

commit 3acf2a9a6b
Author: HelpfulStranger999 <helpfulstranger999@gmail.com>
Date:   Mon May 28 14:59:30 2018 -0500

    Refactors readMessages, fixing from #1033 (#1075)

commit 513a489bf8
Author: Christopher F <foxbot@protonmail.com>
Date:   Mon May 28 15:45:42 2018 -0400

    codefix: add VoiceAPIVersion constant to DiscordConfig

commit 9ba38d7796
Author: Christopher F <foxbot@protonmail.com>
Date:   Mon May 28 15:35:59 2018 -0400

    api: upgrade audio client to VoiceWS v3

commit 64d8938ed5
Author: Christopher F <foxbot@protonmail.com>
Date:   Mon May 28 15:25:46 2018 -0400

    fix: Discord does not always send the member on VOICE_STATE_UPDATE

    some guilds aren't on g250k yet? not sure

commit aeb3095593
Author: FiniteReality <digpoealternate@gmail.com>
Date:   Mon May 28 17:48:38 2018 +0100

    Remove Debug.Print call causing tests to fail

    Forgot to hit save properly... Whoops.

commit a718a7d3c2
Author: Christopher F <foxbot@protonmail.com>
Date:   Mon May 28 12:45:49 2018 -0400

    cleanup: Remove ChannelHelper#IsNsfw

    Closes #1074

    This code was not referenced anywhere in the active codebase.

commit 0c7cb73b06
Author: FiniteReality <digpoealternate@gmail.com>
Date:   Mon May 28 17:41:05 2018 +0100

    Fix permission-related ViewChannel tests

commit 415e2f7739
Author: FiniteReality <digpoealternate@gmail.com>
Date:   Mon May 28 17:14:18 2018 +0100

    Fix analyzer warnings with tests

    Use (actual, expected) format for Assert.Equal, fixes analyzer warnings

commit b0042606b6
Author: FiniteReality <digpoealternate@gmail.com>
Date:   Mon May 28 17:13:59 2018 +0100

    Move RpcCategoryChannel into the correct project

commit 683b3f2701
Author: FiniteReality <digpoealternate@gmail.com>
Date:   Mon May 28 17:12:28 2018 +0100

    Mark ChannelHelper.IsNsfw Obsolete

    Discord no longer treats channels prefixed with 'nsfw' as NSFW,
    so we no longer need to have this check.

commit d74c52873f
Merge: 341b9588 237ad0f8
Author: Still Hsu <341464@gmail.com>
Date:   Mon May 28 11:48:47 2018 +0800

    Merge branch 'dev' into docs/faq-n-patches-offline

commit 237ad0f867
Author: Christopher F <foxbot@protonmail.com>
Date:   Sun May 27 18:00:51 2018 -0400

    Revert "fix: Properly rethrow exceptions in SocketGuild audio client"

    This reverts commit adf4da19fc.

    Someone hasn't written c# in a while

commit adf4da19fc
Author: Christopher F <foxbot@protonmail.com>
Date:   Sun May 27 17:56:14 2018 -0400

    fix: Properly rethrow exceptions in SocketGuild audio client

commit c0c565fd7e
Author: Christopher F <foxbot@protonmail.com>
Date:   Sun May 27 16:45:54 2018 -0400

    retain fallback case for if user is still null

commit 8fb2c71814
Author: Christopher F <foxbot@protonmail.com>
Date:   Sun May 27 16:37:17 2018 -0400

    Add new member objects to events

commit 341b9588d6
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 27 19:01:04 2018 +0800

    Add documentation

commit 2df635e025
Merge: 180b20dd 9995cab4
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 27 18:25:51 2018 +0800

    Merge pull request #1 from Chris-Johnston/better-documentation-for-quotation-mark-aliases

    Improve XML docs for commands quotation mark aliases feature

commit 180b20dd02
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 27 18:19:20 2018 +0800

    Add documentation for new INestedChannel

commit e9f72b6ece
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 27 18:18:52 2018 +0800

    Fix missing service provider in example

commit 9995cab40c
Merge: a8d3031b 1a146afb
Author: Chris Johnston <chris@thejohnstons.net>
Date:   Sun May 27 01:11:17 2018 -0700

    Merge branch 'docs/faq-n-patches-offline' into better-documentation-for-quotation-mark-aliases

commit a8d3031b2d
Author: Chris Johnston <chris@thejohnstons.net>
Date:   Sat May 26 23:32:40 2018 -0700

    Add explanation that if " is removed then it wont work

commit cb7c6d3546
Author: Chris Johnston <chris@thejohnstons.net>
Date:   Sat May 26 23:29:32 2018 -0700

    Add reference to CommandServiceConfig from the util docs'

commit 110b97513f
Author: Chris Johnston <chris@thejohnstons.net>
Date:   Sat May 26 23:15:01 2018 -0700

    Add more XML comments to quotation mark alias map stuff, including an example

commit 1a146afbe1
Merge: cb57ada8 a06e2126
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 27 11:25:10 2018 +0800

    Merge branch 'dev' into docs/faq-n-patches-offline

commit a06e21261c
Author: Chris Johnston <chris@thejohnstons.net>
Date:   Sat May 26 11:15:09 2018 -0700

    Fix ChannelPermissions Modify parameter to be correct default value (#1003)

    * fix channel permissions modify parameter to use nullable boolean, correct default value

    * Add general tests for the ChannelPermissions.Modify method to test default values

    * remove unused cast in tests

    * add guildpermission modify no param tests

    * Add no-param modify tests for OverwritePermissions

    * fix inconsistent parameters in GuildPermissions cstr

    * Adjust formatting of methods and cstrs with many parameters

    * remove temp file that was included. no idea what that is

    * Fix System dependency

    I should really stop fixing merge conflicts in the github website.

commit f9cbff5e42
Author: Chris Johnston <chris@thejohnstons.net>
Date:   Sat May 26 11:06:35 2018 -0700

    Fix #995, Move Category Implementation from IGuildChannel to INestedChannel (#1004)

    * Fix #995 ICategoryChannel.CategoryID throws NotSupportedException

    * Add tests

    * change run mode of TestChannelCategories

    * Add throw for GetCategoryAsync

    * Add xml doc explaining why exception is thrown

    * Add test coverage for text and voice channel categories

    * initial implementation of INestedChannel

    * more implementation of INestedChannel design

    * Add case in RestChannel Create for Category type

    * set the CategoryID for RestVoiceChannel

    * rewrite channel category tests to work with existing pattern

    * remove outdated todo

    * Make IVoiceChannel implement INestedChannel

    * remove redundant interface implementation

    * Add c#7 feature from feedback

    * Remove redundant GetCategoryAsync methods from socket entities

    * Added configureawait to async methods

    * change signature of interface GetCategoryAsync

    * Add check for cachemode in rest channel GetCategory

    * remove redundant IGuildChannel interface from ITextChannel and IVoiceChannel

commit 4d8764e124
Author: FiniteReality <digpoealternate@gmail.com>
Date:   Sat May 26 14:11:49 2018 +0100

    Refactor RoleInfo types into separate classes

    - Fixes indentation with a few files (looks like a bad copy+paste job)
    - Renames RoleInfo to RoleEditInfo
    - Moves RoleInfo from MemberRoleAuditLogData into its own class

    All of this should improve docs.

commit cb57ada8d9
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 26 21:03:07 2018 +0800

    Add partial documentation for audit log objects

commit afda0cd172
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 26 20:47:21 2018 +0800

    Add documentation for some REST-based objects

commit 098ead93d9
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 26 16:33:38 2018 +0800

    Add partial documentation for audit log impl

commit 5d1621a9c5
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 26 16:06:20 2018 +0800

    Fix xml docs error

commit 596b47e1b8
Merge: 3f572ccc fa759a22
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 26 15:54:08 2018 +0800

    Merge branch 'dev' into docs/faq-n-patches-offline

commit 3f572ccc0d
Merge: 73ebc025 a9983026
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 26 15:32:38 2018 +0800

    Merge branch 'docs/faq-n-patches-offline' of https://github.com/Still34/Discord.Net into docs/faq-n-patches-offline

commit 73ebc02580
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 26 15:32:23 2018 +0800

    Fix light theme link color

commit fa759a22ea
Author: Chris Johnston <chris@thejohnstons.net>
Date:   Fri May 25 06:38:59 2018 -0700

    Remove Build Warnings for Obsolete Guild Permission Read Messages (#1067)

    - Replaces the usages of `ReadMessages` with `ViewChannel`
    - Renames the read message parameters of `GuildPermissions#Modify` to be view channel as well

commit bbbac85c46
Author: Hawx <Hawxy@users.noreply.github.com>
Date:   Fri May 25 20:14:28 2018 +0800

    Update Dependencies and support NS2.0 builds (#1046)

    * Update deps, fix test warnings.

    * Support ns2.0

    * Fix typo

    * Remove ns1.1 support

    * Net.Http and Net.Websockets.Client are not needed in ns2.0

    * Move to net46 per volt

    * Remove ns1.3 constants

commit 5f084adf94
Author: HelpfulStranger999 <helpfulstranger999@gmail.com>
Date:   Thu May 24 19:17:19 2018 -0500

    Deprecates ReadMessages, introduces ViewChannel (#1033)

    * Deprecates ReadMessages, introduces ViewChannel

    * Adds period and comma somehow missed

commit bc6009ec72
Author: Still Hsu <341464@gmail.com>
Date:   Fri May 25 08:08:51 2018 +0800

    Implement IMessageChannel#DeleteMessageAsync (#996)

    * Implement DeleteMessageAsync

    * Refer to MessageHelper instead of duplicating call

    * Fix refactor error

commit cee71ef35a
Author: Chris Johnston <chris@thejohnstons.net>
Date:   Thu May 24 17:07:37 2018 -0700

    Add support for parsing multiple types of quotation marks in commands, Fix #942 (#943)

    * Add ability to support different types of quotation marks

    * Added normal quotation mark to list of aliases, removed single quote mark

    * clean up leftover changes from testing

    * change quotation mark parsing to use a map of matching pairs

    * remove commented out code

    * Fix conventions of the command parser utility functions

    * change storage type of alias dictionary to be IReadOnlyDictionary

    * revert type of CommandServiceConfig QuotationMarkAliasMap to Dictionary

    * minor formatting changes to CommandParser

    * remove unnecessary whitespace

    * Move aliases outside of CommandInfo class

    * copy IReadOnlyDictionary to ImmutableDictionary

    * minor syntax changes in CommandServiceConfig

    * add newline before namespace for consistency

    * newline formatting tweak

    * simplification of GetMatch method for CommandParser

    * add more quote unicode punctuation pairs

    * add check for null value when building ImmutableDictionary

    * Move default alias map into a separate source file

    * Ensure that the collection passed into command service is not null

commit b52af7ae7c
Author: Joe4evr <jii.geugten@gmail.com>
Date:   Fri May 25 01:59:32 2018 +0200

    Add a dedicated TimeSpan reader so it doesn't suck (#1005)

    * Add a dedicated TimeSpan reader so it doesn't suck

    * Pass input as lower case

commit c275e57528
Author: o Acid Chicken (硫硫▒~E▒▒▒~O) <root@acid-chicken.com>
Date:   Thu May 24 19:52:25 2018 -0400

    Add support casting System.Drawing.Color to Discord.Color

    commit fa33034267
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Sun Apr 29 13:50:56 2018 +0900

        Use built-in symbol

        refs: https://github.com/RogueException/Discord.Net/pull/1043#issuecomment-385223999

    commit 27ea826685
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Sun Apr 29 11:29:31 2018 +0900

        Add support casting System.Drawing.Color to Discord.Color

    commit 1ab9de2497
    Merge: f5bb99c7 a4d1e2bc
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Sun Apr 29 10:16:46 2018 +0900

        Merge remote-tracking branch 'upstream/dev' into dev

    commit f5bb99c77d
    Merge: 3be8e40d b8b59d97
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Tue Apr 10 11:07:36 2018 +0900

        Merge remote-tracking branch 'upstream/dev' into dev

    commit 3be8e40d38
    Merge: c692306f 9d77a3cd
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Sun Jan 7 15:05:31 2018 +0900

        Merge remote-tracking branch 'upstream/dev' into dev

    commit c692306fcc
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Sat Nov 11 10:49:00 2017 +0900

        Add target of the internal fields

    commit 2d08f9a655
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Sat Nov 11 10:17:42 2017 +0900

        Add some more extension

    commit 4f19b835ff
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Sat Nov 11 01:19:11 2017 +0900

        Add naming rules

    commit af756cd9fe
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Sat Nov 11 00:35:30 2017 +0900

        Add basic .NET style rules

    commit 503ece558b
    Author: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
    Date:   Fri Nov 10 22:36:52 2017 +0900

        Add EditorConfig

commit 5023357a60
Author: Christopher F <foxbot@protonmail.com>
Date:   Thu May 24 19:36:33 2018 -0400

    codefix #1020: RequestOptions should always be sorted last

commit bf5275e071
Author: Joe4evr <jii.geugten@gmail.com>
Date:   Thu May 24 19:36:00 2018 -0400

    Add ability to specify parameters on channel creation (#1020)

    commit 07bca5b31a
    Author: Joe4evr <jii.geugten@gmail.com>
    Date:   Fri Apr 6 09:44:50 2018 +0200

        Add ability to specify parameters on channel creation

commit 0ba8b063ad
Author: HelpfulStranger999 <helpfulstranger999@gmail.com>
Date:   Thu May 24 19:23:44 2018 -0400

    Makes text parameter of sending messages optional (#1042)

    commit 114e5b431b
    Author: HelpfulStranger999 <helpfulstranger999@gmail.com>
    Date:   Sat Apr 28 19:08:35 2018 -0500

        Fixes lack of default value for tts

    commit 1fd8c70c53
    Author: HelpfulStranger999 <helpfulstranger999@gmail.com>
    Date:   Sat Apr 28 15:21:11 2018 -0500

        Makes text parameter of sending messages optional

commit 79811d0e61
Author: HelpfulStranger999 <helpfulstranger999@gmail.com>
Date:   Thu May 24 17:35:38 2018 -0500

    Paginate reactions - solved #1007 (#1022)

    * Cleaned up and refactored slightly

    * Resolves #971

    * Adds support for default avatars and resolves #971

    * Amendment

    * Final amendment

    * Paginating reactions

    * Amendments based on feedback

    * Further amendment based on review

    * Final(?) amendment

    * Removes default limit and after user id

    * Removes fromUserId; cleans up model creation; replaces function with individual parameters

commit a9983026f0
Author: Still Hsu <341464@gmail.com>
Date:   Tue May 22 10:15:49 2018 +0800

    Add event docs

    - MessageReceived
    - ChannelUpdated/Destroyed/Created

commit dece19d895
Merge: fdaa689a e764dafe
Author: Still Hsu <341464@gmail.com>
Date:   Mon May 21 11:47:47 2018 +0800

    Merge branch 'dev' into docs/faq-n-patches-offline

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

    Add explanation for RunMode

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

    Initial proofread of the articles

commit 124f1a267c
Merge: 25557218 97c89310
Author: Still Hsu <341464@gmail.com>
Date:   Tue May 8 05:02:01 2018 +0800

    Merge branch 'dev' into docs/faq-n-patches-offline

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

    Add details to SpotifyGame

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

    Add more IGuild docs

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

    Replace all langword placements with code block

commit ac47d84ea7
Author: Still Hsu <341464@gmail.com>
Date:   Sun May 6 15:22:17 2018 +0800

    Replace langword null to code block null instead

    - Because DocFX sucks at rendering langword

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

    Add XML docs

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

    Add BaseSocketClient docs

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

    Replace note block

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

    Add warning for bulk-delete endpoint

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

    Fix missing Username prop

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

    Add properties examples to overwrite

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

    Fix minor consistencies & redundant impl

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

    XML Docs

commit 3aa5d363de
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 18:22:46 2018 +0800

    Add 'last modified' plugin

    Source: https://github.com/Still34/DocFx.Plugin.LastModified
    Licensed under MIT License

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

    Fix letter-casing for files

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

    Document exposed TypeReaders

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

    Add missing exceptions

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

    Fix seealso for preconditions and add missing descriptions

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

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

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

    Add XML Docs

commit 9e6254600c
Merge: aea06788 bb4bb138
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 14:48:13 2018 +0800

    Merge branch 'dev' into docs/faq-n-patches-offline

commit aea067884c
Merge: 27dc4831 9ddd7090
Author: Still Hsu <341464@gmail.com>
Date:   Sat May 5 13:51:07 2018 +0800

    Merge branch 'docs/faq-n-patches-offline' of https://github.com/Still34/Discord.Net into docs/faq-n-patches-offline

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

    Add XML Docs

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

    Add XML Docs

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

    Add details regarding userbot support

commit 9ddd70906a
Merge: f197174f 7cfed7ff
Author: Still Hsu <341464@gmail.com>
Date:   Fri May 4 09:37:04 2018 +0800

    Merge branch 'dev' into docs/faq-n-patches-offline

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

    Fix embed docs consistency

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

    Add tag examples

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

    Fix sample link & add missing pictures

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

    Compress some assets & add OAuth2 URL generator
pull/988/head
Still Hsu 7 years ago
parent
commit
15f09dbbb4
No known key found for this signature in database GPG Key ID: 8601A145FDA95209
100 changed files with 1652 additions and 459 deletions
  1. +1
    -7
      Discord.Net.targets
  2. +15
    -2
      docs/_template/light-dark-theme/styles/light.css
  3. +1
    -1
      docs/docfx.json
  4. +6
    -4
      docs/guides/commands/samples/command_handler.cs
  5. +0
    -0
      experiment/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs
  6. +1
    -1
      samples/02_commands_framework/02_commands_framework.csproj
  7. +1
    -1
      src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj
  8. +28
    -6
      src/Discord.Net.Commands/CommandParser.cs
  9. +7
    -2
      src/Discord.Net.Commands/CommandService.cs
  10. +21
    -1
      src/Discord.Net.Commands/CommandServiceConfig.cs
  11. +7
    -3
      src/Discord.Net.Commands/Discord.Net.Commands.csproj
  12. +3
    -2
      src/Discord.Net.Commands/Info/CommandInfo.cs
  13. +2
    -2
      src/Discord.Net.Commands/PrimitiveParsers.cs
  14. +35
    -0
      src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs
  15. +97
    -0
      src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs
  16. +5
    -5
      src/Discord.Net.Core/Discord.Net.Core.csproj
  17. +22
    -1
      src/Discord.Net.Core/DiscordConfig.cs
  18. +12
    -1
      src/Discord.Net.Core/Entities/Activities/GameAsset.cs
  19. +6
    -0
      src/Discord.Net.Core/Entities/Activities/GameParty.cs
  20. +6
    -0
      src/Discord.Net.Core/Entities/Activities/IActivity.cs
  21. +79
    -7
      src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs
  22. +1
    -7
      src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs
  23. +17
    -11
      src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs
  24. +19
    -3
      src/Discord.Net.Core/Entities/Channels/IChannel.cs
  25. +41
    -14
      src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
  26. +6
    -3
      src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs
  27. +28
    -0
      src/Discord.Net.Core/Entities/Channels/INestedChannel.cs
  28. +2
    -2
      src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
  29. +2
    -2
      src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs
  30. +6
    -0
      src/Discord.Net.Core/Entities/Channels/ReorderChannelProperties.cs
  31. +7
    -1
      src/Discord.Net.Core/Entities/Guilds/IBan.cs
  32. +27
    -7
      src/Discord.Net.Core/Entities/Guilds/IGuild.cs
  33. +6
    -7
      src/Discord.Net.Core/Entities/Image.cs
  34. +33
    -3
      src/Discord.Net.Core/Entities/Invites/IInvite.cs
  35. +33
    -7
      src/Discord.Net.Core/Entities/Invites/IInviteMetadata.cs
  36. +1
    -1
      src/Discord.Net.Core/Entities/Messages/IUserMessage.cs
  37. +81
    -18
      src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs
  38. +3
    -7
      src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs
  39. +125
    -36
      src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs
  40. +61
    -17
      src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs
  41. +10
    -0
      src/Discord.Net.Core/Entities/Roles/Color.cs
  42. +2
    -3
      src/Discord.Net.Core/Extensions/UserExtensions.cs
  43. +9
    -12
      src/Discord.Net.Core/Logging/LogManager.cs
  44. +12
    -13
      src/Discord.Net.Core/Logging/Logger.cs
  45. +5
    -2
      src/Discord.Net.Core/RequestOptions.cs
  46. +2
    -45
      src/Discord.Net.Core/Utils/DateTimeUtils.cs
  47. +2
    -2
      src/Discord.Net.Core/Utils/SnowflakeUtils.cs
  48. +7
    -1
      src/Discord.Net.Rest/API/Common/Message.cs
  49. +4
    -1
      src/Discord.Net.Rest/API/Common/VoiceState.cs
  50. +12
    -1
      src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs
  51. +1
    -1
      src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs
  52. +6
    -7
      src/Discord.Net.Rest/Discord.Net.Rest.csproj
  53. +2
    -2
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  54. +10
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs
  55. +27
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs
  56. +27
    -3
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs
  57. +29
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs
  58. +12
    -3
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs
  59. +15
    -4
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs
  60. +16
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs
  61. +22
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs
  62. +55
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs
  63. +16
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs
  64. +49
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs
  65. +49
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs
  66. +38
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs
  67. +3
    -18
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs
  68. +16
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleEditInfo.cs
  69. +1
    -2
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs
  70. +0
    -6
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs
  71. +3
    -3
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs
  72. +3
    -3
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs
  73. +21
    -0
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleEditInfo.cs
  74. +0
    -21
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs
  75. +5
    -5
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs
  76. +0
    -4
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs
  77. +0
    -4
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs
  78. +4
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs
  79. +15
    -7
      src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
  80. +1
    -3
      src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs
  81. +0
    -8
      src/Discord.Net.Rest/Entities/Channels/RestCategoryChannel.cs
  82. +5
    -0
      src/Discord.Net.Rest/Entities/Channels/RestChannel.cs
  83. +37
    -8
      src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
  84. +10
    -5
      src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
  85. +1
    -12
      src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
  86. +87
    -8
      src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
  87. +22
    -1
      src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
  88. +11
    -8
      src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs
  89. +20
    -4
      src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs
  90. +15
    -0
      src/Discord.Net.Rest/Entities/Guilds/RestBan.cs
  91. +8
    -8
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  92. +35
    -8
      src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs
  93. +3
    -3
      src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs
  94. +1
    -2
      src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs
  95. +1
    -1
      src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs
  96. +1
    -1
      src/Discord.Net.Rest/Net/RateLimitInfo.cs
  97. +5
    -1
      src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs
  98. +10
    -0
      src/Discord.Net.WebSocket/API/Voice/HelloEvent.cs
  99. +3
    -1
      src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs
  100. +12
    -4
      src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs

+ 1
- 7
Discord.Net.targets View File

@@ -16,13 +16,7 @@
<PropertyGroup Condition=" '$(BuildNumber)' != '' And $(IsTagBuild) != 'true' "> <PropertyGroup Condition=" '$(BuildNumber)' != '' And $(IsTagBuild) != 'true' ">
<VersionSuffix Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)-$(BuildNumber)</VersionSuffix> <VersionSuffix Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)-$(BuildNumber)</VersionSuffix>
<VersionSuffix Condition=" '$(VersionSuffix)' == '' ">build-$(BuildNumber)</VersionSuffix> <VersionSuffix Condition=" '$(VersionSuffix)' == '' ">build-$(BuildNumber)</VersionSuffix>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' Or '$(TargetFramework)' == 'net45' ">
<DefineConstants>$(DefineConstants);FILESYSTEM;DEFAULTUDPCLIENT;DEFAULTWEBSOCKET</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<DefineConstants>$(DefineConstants);FORMATSTR;UNIXTIME;MSTRYBUFFER;UDPDISPOSE</DefineConstants>
</PropertyGroup>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<NoWarn>$(NoWarn);CS1573;CS1591</NoWarn> <NoWarn>$(NoWarn);CS1573;CS1591</NoWarn>
<WarningsAsErrors>true</WarningsAsErrors> <WarningsAsErrors>true</WarningsAsErrors>


+ 15
- 2
docs/_template/light-dark-theme/styles/light.css View File

@@ -10,11 +10,24 @@ body {
overflow: visible; overflow: visible;
} }


/* links */

a:active, a:hover, a:visited {
color: #0078d7;
}

a {
color: #0050c5;
cursor: pointer;
text-decoration: none;
word-wrap: break-word;
}

/* code */ /* code */


code { code {
color: #222f3d;
background-color: #f9f9f9;
color: #3b5269;
background-color: #ececec;
border-radius: 4px; border-radius: 4px;
padding: 3px 7px; padding: 3px 7px;
} }


+ 1
- 1
docs/docfx.json View File

@@ -43,7 +43,7 @@
"globalMetadata": { "globalMetadata": {
"_appTitle": "Discord.Net Documentation", "_appTitle": "Discord.Net Documentation",
"_appFooter": "Discord.Net (c) 2015-2018 2.0.0-beta", "_appFooter": "Discord.Net (c) 2015-2018 2.0.0-beta",
"_enableSearch": true,
"_enableSearch": true
}, },
"noLangKeyword": false, "noLangKeyword": false,
"xrefService": [ "xrefService": [


+ 6
- 4
docs/guides/commands/samples/command_handler.cs View File

@@ -1,12 +1,14 @@
public class CommandHandle
public class CommandHandler
{ {
private readonly DiscordSocketClient _client; private readonly DiscordSocketClient _client;
private readonly CommandService _commands; private readonly CommandService _commands;
private readonly IServiceProvider _services;


public CommandHandle(DiscordSocketClient client)
public CommandHandler(IServiceProvider services)
{ {
_client = client;
_commands = new CommandService();
_services = services;
_commands = services.GetRequiredService<CommandService>();
_client = services.GetRequiredService<DiscordSocketClient>();
} }
public async Task InstallCommandsAsync() public async Task InstallCommandsAsync()


src/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs → experiment/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs View File


+ 1
- 1
samples/02_commands_framework/02_commands_framework.csproj View File

@@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>


<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.0-preview1-final" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
</ItemGroup> </ItemGroup>


<ItemGroup> <ItemGroup>


+ 1
- 1
src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj View File

@@ -7,7 +7,7 @@
<TargetFramework>netstandard1.3</TargetFramework> <TargetFramework>netstandard1.3</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.6.0" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.8.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Discord.Net.Commands\Discord.Net.Commands.csproj" /> <ProjectReference Include="..\Discord.Net.Commands\Discord.Net.Commands.csproj" />


+ 28
- 6
src/Discord.Net.Commands/CommandParser.cs View File

@@ -1,4 +1,5 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -13,8 +14,7 @@ namespace Discord.Commands
Parameter, Parameter,
QuotedParameter QuotedParameter
} }
public static async Task<ParseResult> ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos)
public static async Task<ParseResult> ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos, IReadOnlyDictionary<char, char> aliasMap)
{ {
ParameterInfo curParam = null; ParameterInfo curParam = null;
StringBuilder argBuilder = new StringBuilder(input.Length); StringBuilder argBuilder = new StringBuilder(input.Length);
@@ -24,7 +24,27 @@ namespace Discord.Commands
var argList = ImmutableArray.CreateBuilder<TypeReaderResult>(); var argList = ImmutableArray.CreateBuilder<TypeReaderResult>();
var paramList = ImmutableArray.CreateBuilder<TypeReaderResult>(); var paramList = ImmutableArray.CreateBuilder<TypeReaderResult>();
bool isEscaping = false; bool isEscaping = false;
char c;
char c, matchQuote = '\0';

// local helper functions
bool IsOpenQuote(IReadOnlyDictionary<char, char> dict, char ch)
{
// return if the key is contained in the dictionary if it is populated
if (dict.Count != 0)
return dict.ContainsKey(ch);
// or otherwise if it is the default double quote
return c == '\"';
}

char GetMatch(IReadOnlyDictionary<char, char> dict, char ch)
{
// get the corresponding value for the key, if it exists
// and if the dictionary is populated
if (dict.Count != 0 && dict.TryGetValue(c, out var value))
return value;
// or get the default pair of the default double quote
return '\"';
}


for (int curPos = startPos; curPos <= endPos; curPos++) for (int curPos = startPos; curPos <= endPos; curPos++)
{ {
@@ -74,9 +94,11 @@ namespace Discord.Commands
argBuilder.Append(c); argBuilder.Append(c);
continue; continue;
} }
if (c == '\"')
if (IsOpenQuote(aliasMap, c))
{ {
curPart = ParserPart.QuotedParameter; curPart = ParserPart.QuotedParameter;
matchQuote = GetMatch(aliasMap, c);
continue; continue;
} }
curPart = ParserPart.Parameter; curPart = ParserPart.Parameter;
@@ -97,7 +119,7 @@ namespace Discord.Commands
} }
else if (curPart == ParserPart.QuotedParameter) else if (curPart == ParserPart.QuotedParameter)
{ {
if (c == '\"')
if (c == matchQuote)
{ {
argString = argBuilder.ToString(); //Remove quotes argString = argBuilder.ToString(); //Remove quotes
lastArgEndPos = curPos + 1; lastArgEndPos = curPos + 1;


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

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -38,6 +38,7 @@ namespace Discord.Commands
internal readonly RunMode _defaultRunMode; internal readonly RunMode _defaultRunMode;
internal readonly Logger _cmdLogger; internal readonly Logger _cmdLogger;
internal readonly LogManager _logManager; internal readonly LogManager _logManager;
internal readonly IReadOnlyDictionary<char, char> _quotationMarkAliasMap;


/// <summary> /// <summary>
/// Represents all modules loaded within <see cref="CommandService" />. /// Represents all modules loaded within <see cref="CommandService" />.
@@ -73,6 +74,7 @@ namespace Discord.Commands
_ignoreExtraArgs = config.IgnoreExtraArgs; _ignoreExtraArgs = config.IgnoreExtraArgs;
_separatorChar = config.SeparatorChar; _separatorChar = config.SeparatorChar;
_defaultRunMode = config.DefaultRunMode; _defaultRunMode = config.DefaultRunMode;
_quotationMarkAliasMap = (config.QuotationMarkAliasMap ?? new Dictionary<char, char>()).ToImmutableDictionary();
if (_defaultRunMode == RunMode.Default) if (_defaultRunMode == RunMode.Default)
throw new InvalidOperationException("The default run mode cannot be set to Default."); throw new InvalidOperationException("The default run mode cannot be set to Default.");


@@ -93,6 +95,10 @@ namespace Discord.Commands
_defaultTypeReaders[typeof(Nullable<>).MakeGenericType(type)] = NullableTypeReader.Create(type, _defaultTypeReaders[type]); _defaultTypeReaders[typeof(Nullable<>).MakeGenericType(type)] = NullableTypeReader.Create(type, _defaultTypeReaders[type]);
} }


var tsreader = new TimeSpanTypeReader();
_defaultTypeReaders[typeof(TimeSpan)] = tsreader;
_defaultTypeReaders[typeof(TimeSpan?)] = NullableTypeReader.Create(typeof(TimeSpan), tsreader);

_defaultTypeReaders[typeof(string)] = _defaultTypeReaders[typeof(string)] =
new PrimitiveTypeReader<string>((string x, out string y) => { y = x; return true; }, 0); new PrimitiveTypeReader<string>((string x, out string y) => { y = x; return true; }, 0);


@@ -447,7 +453,6 @@ namespace Discord.Commands
public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) public async Task<IResult> ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception)
{ {
services = services ?? EmptyServiceProvider.Instance; services = services ?? EmptyServiceProvider.Instance;

var searchResult = Search(context, input); var searchResult = Search(context, input);
if (!searchResult.IsSuccess) if (!searchResult.IsSuccess)
return searchResult; return searchResult;


+ 21
- 1
src/Discord.Net.Commands/CommandServiceConfig.cs View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;


namespace Discord.Commands namespace Discord.Commands
{ {
@@ -34,8 +35,27 @@ namespace Discord.Commands
public bool ThrowOnError { get; set; } = true; public bool ThrowOnError { get; set; } = true;


/// <summary> /// <summary>
/// Gets or sets whether extra parameters should be ignored.
/// Collection of aliases for matching pairs of string delimiters.
/// The dictionary stores the opening delimiter as a key, and the matching closing delimiter as the value.
/// If no value is supplied <see cref="QuotationAliasUtils.GetDefaultAliasMap"/> will be used, which contains
/// many regional equivalents.
/// Only values that are specified in this map will be used as string delimiters, so if " is removed then
/// it won't be used.
/// If this map is set to null or empty, the default delimiter of " will be used.
/// </summary> /// </summary>
/// <example>
/// <code language="C#">
/// QuotationMarkAliasMap = new Dictionary&lt;char, char%gt;()
/// {
/// {'\"', '\"' },
/// {'“', '”' },
/// {'「', '」' },
/// }
/// </code>
/// </example>
public Dictionary<char, char> QuotationMarkAliasMap { get; set; } = QuotationAliasUtils.GetDefaultAliasMap;

/// <summary> Determines whether extra parameters should be ignored. </summary>
public bool IgnoreExtraArgs { get; set; } = false; public bool IgnoreExtraArgs { get; set; } = false;
} }
} }

+ 7
- 3
src/Discord.Net.Commands/Discord.Net.Commands.csproj View File

@@ -1,15 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" /> <Import Project="../../Discord.Net.targets" />
<PropertyGroup> <PropertyGroup>
<AssemblyName>Discord.Net.Commands</AssemblyName> <AssemblyName>Discord.Net.Commands</AssemblyName>
<RootNamespace>Discord.Commands</RootNamespace> <RootNamespace>Discord.Commands</RootNamespace>
<Description>A Discord.Net extension adding support for bot commands.</Description> <Description>A Discord.Net extension adding support for bot commands.</Description>
<TargetFrameworks>netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'netstandard2.0' ">
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="1.1.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

+ 3
- 2
src/Discord.Net.Commands/Info/CommandInfo.cs View File

@@ -1,4 +1,4 @@
using Discord.Commands.Builders;
using Discord.Commands.Builders;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -172,7 +172,8 @@ namespace Discord.Commands
return ParseResult.FromError(preconditionResult); return ParseResult.FromError(preconditionResult);


string input = searchResult.Text.Substring(startIndex); string input = searchResult.Text.Substring(startIndex);
return await CommandParser.ParseArgsAsync(this, context, services, input, 0).ConfigureAwait(false);

return await CommandParser.ParseArgsAsync(this, context, _commandService._ignoreExtraArgs, services, input, 0, _commandService._quotationMarkAliasMap).ConfigureAwait(false);
} }
public Task<IResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services) public Task<IResult> ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services)


+ 2
- 2
src/Discord.Net.Commands/PrimitiveParsers.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;


@@ -29,7 +29,7 @@ namespace Discord.Commands
parserBuilder[typeof(decimal)] = (TryParseDelegate<decimal>)decimal.TryParse; parserBuilder[typeof(decimal)] = (TryParseDelegate<decimal>)decimal.TryParse;
parserBuilder[typeof(DateTime)] = (TryParseDelegate<DateTime>)DateTime.TryParse; parserBuilder[typeof(DateTime)] = (TryParseDelegate<DateTime>)DateTime.TryParse;
parserBuilder[typeof(DateTimeOffset)] = (TryParseDelegate<DateTimeOffset>)DateTimeOffset.TryParse; parserBuilder[typeof(DateTimeOffset)] = (TryParseDelegate<DateTimeOffset>)DateTimeOffset.TryParse;
parserBuilder[typeof(TimeSpan)] = (TryParseDelegate<TimeSpan>)TimeSpan.TryParse;
//parserBuilder[typeof(TimeSpan)] = (TryParseDelegate<TimeSpan>)TimeSpan.TryParse;
parserBuilder[typeof(char)] = (TryParseDelegate<char>)char.TryParse; parserBuilder[typeof(char)] = (TryParseDelegate<char>)char.TryParse;
return parserBuilder.ToImmutable(); return parserBuilder.ToImmutable();
} }


+ 35
- 0
src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs View File

@@ -0,0 +1,35 @@
using System;
using System.Globalization;
using System.Threading.Tasks;

namespace Discord.Commands
{
internal class TimeSpanTypeReader : TypeReader
{
private static readonly string[] _formats = new[]
{
"%d'd'%h'h'%m'm'%s's'", //4d3h2m1s
"%d'd'%h'h'%m'm'", //4d3h2m
"%d'd'%h'h'%s's'", //4d3h 1s
"%d'd'%h'h'", //4d3h
"%d'd'%m'm'%s's'", //4d 2m1s
"%d'd'%m'm'", //4d 2m
"%d'd'%s's'", //4d 1s
"%d'd'", //4d
"%h'h'%m'm'%s's'", // 3h2m1s
"%h'h'%m'm'", // 3h2m
"%h'h'%s's'", // 3h 1s
"%h'h'", // 3h
"%m'm'%s's'", // 2m1s
"%m'm'", // 2m
"%s's'", // 1s
};

public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
{
return (TimeSpan.TryParseExact(input.ToLowerInvariant(), _formats, CultureInfo.InvariantCulture, out var timeSpan))
? Task.FromResult(TypeReaderResult.FromSuccess(timeSpan))
: Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse TimeSpan"));
}
}
}

+ 97
- 0
src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;

namespace Discord.Commands
{
/// <summary>
/// Utility class which contains the default matching pairs of quotation marks for CommandServiceConfig
/// </summary>
internal static class QuotationAliasUtils
{
/// <summary>
/// A default map of open-close pairs of quotation marks.
/// Contains many regional and Unicode equivalents.
/// Used in the <see cref="CommandServiceConfig"/>.
/// </summary>
/// <seealso cref="CommandServiceConfig.QuotationMarkAliasMap"/>
internal static Dictionary<char, char> GetDefaultAliasMap
{
get
{
// Output of a gist provided by https://gist.github.com/ufcpp
// https://gist.github.com/ufcpp/5b2cf9a9bf7d0b8743714a0b88f7edc5
// This was not used for the implementation because of incompatibility with netstandard1.1
return new Dictionary<char, char> {
{'\"', '\"' },
{'«', '»' },
{'‘', '’' },
{'“', '”' },
{'„', '‟' },
{'‹', '›' },
{'‚', '‛' },
{'《', '》' },
{'〈', '〉' },
{'「', '」' },
{'『', '』' },
{'〝', '〞' },
{'﹁', '﹂' },
{'﹃', '﹄' },
{'"', '"' },
{''', ''' },
{'「', '」' },
{'(', ')' },
{'༺', '༻' },
{'༼', '༽' },
{'᚛', '᚜' },
{'⁅', '⁆' },
{'⌈', '⌉' },
{'⌊', '⌋' },
{'❨', '❩' },
{'❪', '❫' },
{'❬', '❭' },
{'❮', '❯' },
{'❰', '❱' },
{'❲', '❳' },
{'❴', '❵' },
{'⟅', '⟆' },
{'⟦', '⟧' },
{'⟨', '⟩' },
{'⟪', '⟫' },
{'⟬', '⟭' },
{'⟮', '⟯' },
{'⦃', '⦄' },
{'⦅', '⦆' },
{'⦇', '⦈' },
{'⦉', '⦊' },
{'⦋', '⦌' },
{'⦍', '⦎' },
{'⦏', '⦐' },
{'⦑', '⦒' },
{'⦓', '⦔' },
{'⦕', '⦖' },
{'⦗', '⦘' },
{'⧘', '⧙' },
{'⧚', '⧛' },
{'⧼', '⧽' },
{'⸂', '⸃' },
{'⸄', '⸅' },
{'⸉', '⸊' },
{'⸌', '⸍' },
{'⸜', '⸝' },
{'⸠', '⸡' },
{'⸢', '⸣' },
{'⸤', '⸥' },
{'⸦', '⸧' },
{'⸨', '⸩' },
{'【', '】'},
{'〔', '〕' },
{'〖', '〗' },
{'〘', '〙' },
{'〚', '〛' }
};
}
}
}
}

+ 5
- 5
src/Discord.Net.Core/Discord.Net.Core.csproj View File

@@ -1,15 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" /> <Import Project="../../Discord.Net.targets" />
<PropertyGroup> <PropertyGroup>
<AssemblyName>Discord.Net.Core</AssemblyName> <AssemblyName>Discord.Net.Core</AssemblyName>
<RootNamespace>Discord</RootNamespace> <RootNamespace>Discord</RootNamespace>
<Description>The core components for the Discord.Net library.</Description> <Description>The core components for the Discord.Net library.</Description>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net45;netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="System.Collections.Immutable" Version="1.4.0" />
<PackageReference Include="System.Interactive.Async" Version="3.1.1" /> <PackageReference Include="System.Interactive.Async" Version="3.1.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

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

@@ -11,13 +11,21 @@ namespace Discord
/// Returns the API version Discord.Net uses. /// Returns the API version Discord.Net uses.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// A 32-bit integer representing the API version that Discord.Net uses to communicate with Discord.
/// An <see cref="int"/> representing the API version that Discord.Net uses to communicate with Discord.
/// <para>A list of available API version can be seen on the official /// <para>A list of available API version can be seen on the official
/// <see href="https://discordapp.com/developers/docs/reference#api-versioning">Discord API documentation</see> /// <see href="https://discordapp.com/developers/docs/reference#api-versioning">Discord API documentation</see>
/// .</para> /// .</para>
/// </returns> /// </returns>
public const int APIVersion = 6; public const int APIVersion = 6;
/// <summary> /// <summary>
/// Returns the Voice API version Discord.Net uses.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the API version that Discord.Net uses to communicate with Discord's
/// voice server.
/// </returns>
public const int VoiceAPIVersion = 3;
/// <summary>
/// Gets the Discord.Net version, including the build number. /// Gets the Discord.Net version, including the build number.
/// </summary> /// </summary>
/// <returns> /// <returns>
@@ -93,6 +101,19 @@ namespace Discord
/// The maximum number of guilds that can be gotten per-batch. /// The maximum number of guilds that can be gotten per-batch.
/// </returns> /// </returns>
public const int MaxGuildsPerBatch = 100; public const int MaxGuildsPerBatch = 100;
/// <summary>
/// Returns the max user reactions allowed to be in a request.
/// </summary>
/// <returns>
/// The maximum number of user reactions that can be gotten per-batch.
/// </returns>
public const int MaxUserReactionsPerBatch = 100;
/// <summary>
/// Returns the max audit log entries allowed to be in a request.
/// </summary>
/// <returns>
/// The maximum number of audit log entries that can be gotten per-batch.
/// </returns>
public const int MaxAuditLogEntriesPerBatch = 100; public const int MaxAuditLogEntriesPerBatch = 100;


/// <summary> /// <summary>


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

@@ -12,15 +12,26 @@ namespace Discord
/// <summary> /// <summary>
/// Gets the description of the asset. /// Gets the description of the asset.
/// </summary> /// </summary>
/// <returns>
/// A string containing the description of the asset.
/// </returns>
public string Text { get; internal set; } public string Text { get; internal set; }
/// <summary> /// <summary>
/// Gets the image ID of the asset. /// Gets the image ID of the asset.
/// </summary> /// </summary>
/// <returns>
/// A string containing the unique image identifier of the asset.
/// </returns>
public string ImageId { get; internal set; } public string ImageId { get; internal set; }


/// <summary> /// <summary>
/// Returns the image URL of the asset, or <c>null</c> when the application ID does not exist.
/// Returns the image URL of the asset.
/// </summary> /// </summary>
/// <param name="size">The size of the image to return in. This can be any power of two between 16 and 2048.</param>
/// <param name="format">The format to return.</param>
/// <returns>
/// A string pointing to the image URL of the asset; <c>null</c> when the application ID does not exist.
/// </returns>
public string GetImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128) public string GetImageUrl(ImageFormat format = ImageFormat.Auto, ushort size = 128)
=> ApplicationId.HasValue ? CDN.GetRichAssetUrl(ApplicationId.Value, ImageId, size, format) : null; => ApplicationId.HasValue ? CDN.GetRichAssetUrl(ApplicationId.Value, ImageId, size, format) : null;
} }


+ 6
- 0
src/Discord.Net.Core/Entities/Activities/GameParty.cs View File

@@ -10,11 +10,17 @@ namespace Discord
/// <summary> /// <summary>
/// Gets the ID of the party. /// Gets the ID of the party.
/// </summary> /// </summary>
/// <returns>
/// A string containing the unique identifier of the party.
/// </returns>
public string Id { get; internal set; } public string Id { get; internal set; }
public long Members { get; internal set; } public long Members { get; internal set; }
/// <summary> /// <summary>
/// Gets the party's current and maximum size. /// Gets the party's current and maximum size.
/// </summary> /// </summary>
/// <returns>
/// A <see cref="long"/> representing the capacity of the party.
/// </returns>
public long Capacity { get; internal set; } public long Capacity { get; internal set; }
} }
} }

+ 6
- 0
src/Discord.Net.Core/Entities/Activities/IActivity.cs View File

@@ -8,10 +8,16 @@ namespace Discord
/// <summary> /// <summary>
/// Gets the name of the activity. /// Gets the name of the activity.
/// </summary> /// </summary>
/// <returns>
/// A string containing the name of the activity that the user is doing.
/// </returns>
string Name { get; } string Name { get; }
/// <summary> /// <summary>
/// Gets the type of the activity. /// Gets the type of the activity.
/// </summary> /// </summary>
/// <returns>
/// The type of activity.
/// </returns>
ActivityType Type { get; } ActivityType Type { get; }
} }
} }

+ 79
- 7
src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs View File

@@ -1,50 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord namespace Discord
{ {
/// <summary> /// <summary>
/// The action type within a <see cref="IAuditLogEntry"/>
/// Representing a type of action within an <see cref="IAuditLogEntry"/>.
/// </summary> /// </summary>
public enum ActionType public enum ActionType
{ {
/// <summary>
/// this guild was updated.
/// </summary>
GuildUpdated = 1, GuildUpdated = 1,


/// <summary>
/// A channel was created.
/// </summary>
ChannelCreated = 10, ChannelCreated = 10,
/// <summary>
/// A channel was updated.
/// </summary>
ChannelUpdated = 11, ChannelUpdated = 11,
/// <summary>
/// A channel was deleted.
/// </summary>
ChannelDeleted = 12, ChannelDeleted = 12,


/// <summary>
/// A permission overwrite was created for a channel.
/// </summary>
OverwriteCreated = 13, OverwriteCreated = 13,
/// <summary>
/// A permission overwrite was updated for a channel.
/// </summary>
OverwriteUpdated = 14, OverwriteUpdated = 14,
/// <summary>
/// A permission overwrite was deleted for a channel.
/// </summary>
OverwriteDeleted = 15, OverwriteDeleted = 15,


/// <summary>
/// A user was kicked from this guild.
/// </summary>
Kick = 20, Kick = 20,
/// <summary>
/// A prune took place in this guild.
/// </summary>
Prune = 21, Prune = 21,
/// <summary>
/// A user banned another user from this guild.
/// </summary>
Ban = 22, Ban = 22,
/// <summary>
/// A user unbanned another user from this guild.
/// </summary>
Unban = 23, Unban = 23,


/// <summary>
/// A guild member whose information was updated.
/// </summary>
MemberUpdated = 24, MemberUpdated = 24,
/// <summary>
/// A guild member's role collection was updated.
/// </summary>
MemberRoleUpdated = 25, MemberRoleUpdated = 25,


/// <summary>
/// A role was created in this guild.
/// </summary>
RoleCreated = 30, RoleCreated = 30,
/// <summary>
/// A role was updated in this guild.
/// </summary>
RoleUpdated = 31, RoleUpdated = 31,
/// <summary>
/// A role was deleted from this guild.
/// </summary>
RoleDeleted = 32, RoleDeleted = 32,


/// <summary>
/// An invite was created in this guild.
/// </summary>
InviteCreated = 40, InviteCreated = 40,
/// <summary>
/// An invite was updated in this guild.
/// </summary>
InviteUpdated = 41, InviteUpdated = 41,
/// <summary>
/// An invite was deleted from this guild.
/// </summary>
InviteDeleted = 42, InviteDeleted = 42,


/// <summary>
/// A Webhook was created in this guild.
/// </summary>
WebhookCreated = 50, WebhookCreated = 50,
/// <summary>
/// A Webhook was updated in this guild.
/// </summary>
WebhookUpdated = 51, WebhookUpdated = 51,
/// <summary>
/// A Webhook was deleted from this guild.
/// </summary>
WebhookDeleted = 52, WebhookDeleted = 52,


/// <summary>
/// An emoji was created in this guild.
/// </summary>
EmojiCreated = 60, EmojiCreated = 60,
/// <summary>
/// An emoji was updated in this guild.
/// </summary>
EmojiUpdated = 61, EmojiUpdated = 61,
/// <summary>
/// An emoji was deleted from this guild.
/// </summary>
EmojiDeleted = 62, EmojiDeleted = 62,


/// <summary>
/// A message was deleted from this guild.
/// </summary>
MessageDeleted = 72 MessageDeleted = 72
} }
} }

+ 1
- 7
src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs View File

@@ -1,13 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord namespace Discord
{ {
/// <summary> /// <summary>
/// Represents data applied to an <see cref="IAuditLogEntry"/>
/// Represents data applied to an <see cref="IAuditLogEntry" />.
/// </summary> /// </summary>
public interface IAuditLogData public interface IAuditLogData
{ } { }


+ 17
- 11
src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs View File

@@ -1,34 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord namespace Discord
{ {
/// <summary> /// <summary>
/// Represents an entry in an audit log
/// Represents a generic audit log entry.
/// </summary> /// </summary>
public interface IAuditLogEntry : IEntity<ulong> public interface IAuditLogEntry : IEntity<ulong>
{ {
/// <summary> /// <summary>
/// The action which occured to create this entry
/// Gets the action which occurred to create this entry.
/// </summary> /// </summary>
/// <returns>
/// The type of action for this audit log entry.
/// </returns>
ActionType Action { get; } ActionType Action { get; }


/// <summary> /// <summary>
/// The data for this entry. May be <see cref="null"/> if no data was available.
/// Gets the data for this entry.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="IAuditLogData" /> for this audit log entry; <c>null</c> if no data is available.
/// </returns>
IAuditLogData Data { get; } IAuditLogData Data { get; }


/// <summary> /// <summary>
/// The user responsible for causing the changes
/// Gets the user responsible for causing the changes.
/// </summary> /// </summary>
/// <returns>
/// A user object.
/// </returns>
IUser User { get; } IUser User { get; }


/// <summary> /// <summary>
/// The reason behind the change. May be <see cref="null"/> if no reason was provided.
/// Gets the reason behind the change.
/// </summary> /// </summary>
/// <returns>
/// A string containing the reason for the change; <c>null</c> if none is provided.
/// </returns>
string Reason { get; } string Reason { get; }
} }
} }

+ 19
- 3
src/Discord.Net.Core/Entities/Channels/IChannel.cs View File

@@ -11,16 +11,32 @@ namespace Discord
/// <summary> /// <summary>
/// Gets the name of this channel. /// Gets the name of this channel.
/// </summary> /// </summary>
/// <returns>
/// A string containing the name of this channel.
/// </returns>
string Name { get; } string Name { get; }
/// <summary> /// <summary>
/// Gets a collection of all users in this channel. /// Gets a collection of all users in this channel.
/// </summary> /// </summary>
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A paged collection containing a collection of users that can access this channel. Flattening the
/// paginated response into a collection of users with
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> is required if you wish to access the users.
/// </returns>
IAsyncEnumerable<IReadOnlyCollection<IUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); IAsyncEnumerable<IReadOnlyCollection<IUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
/// <summary> /// <summary>
/// Gets a user in this channel with the provided ID.
/// Gets a user in this channel.
/// </summary> /// </summary>
/// <param name="id">The snowflake identifier of the user (e.g. 168693960628371456).</param>
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/> containing a user object that represents the user.
/// </returns>
Task<IUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task<IUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
} }
} }

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

@@ -20,29 +20,18 @@ namespace Discord
/// </returns> /// </returns>
int Position { get; } int Position { get; }


/// <summary>
/// Gets the parent ID (category) of this channel in the guild's channel list.
/// </summary>
/// <returns>
/// The parent category ID associated with this channel, or <c>null</c> if none is set.
/// </returns>
ulong? CategoryId { get; }
/// <summary>
/// Gets the parent channel (category) of this channel.
/// </summary>
Task<ICategoryChannel> GetCategoryAsync();
/// <summary> /// <summary>
/// Gets the guild associated with this channel. /// Gets the guild associated with this channel.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The guild that this channel belongs to.
/// A guild that this channel belongs to.
/// </returns> /// </returns>
IGuild Guild { get; } IGuild Guild { get; }
/// <summary> /// <summary>
/// Gets the guild ID associated with this channel. /// Gets the guild ID associated with this channel.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The guild ID that this channel belongs to.
/// A guild snowflake identifier for the guild that this channel belongs to.
/// </returns> /// </returns>
ulong GuildId { get; } ulong GuildId { get; }
/// <summary> /// <summary>
@@ -66,16 +55,25 @@ namespace Discord
/// If <c>true</c>, a user accepting this invite will be kicked from the guild after closing their client. /// If <c>true</c>, a user accepting this invite will be kicked from the guild after closing their client.
/// </param> /// </param>
/// <param name="isUnique"> /// <param name="isUnique">
/// If <c>true</c>, don't try to reuse a similar invite (useful for creating many unique one time use invites).
/// If <c>true</c>, don't try to reuse a similar invite (useful for creating many unique one time use
/// invites).
/// </param> /// </param>
/// <param name="options"> /// <param name="options">
/// The options to be used when sending the request. /// The options to be used when sending the request.
/// </param> /// </param>
/// <returns>
/// An awaitable <see cref="Task"/> containing an invite metadata object containing information for the
/// created invite.
/// </returns>
Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null); Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null);
/// <summary> /// <summary>
/// Returns a collection of all invites to this channel. /// Returns a collection of all invites to this channel.
/// </summary> /// </summary>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/> containing a read-only collection of invite metadata that are created
/// for this channel.
/// </returns>
Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null); Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null);


/// <summary> /// <summary>
@@ -83,29 +81,44 @@ namespace Discord
/// </summary> /// </summary>
/// <param name="func">The properties to modify the channel with.</param> /// <param name="func">The properties to modify the channel with.</param>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/>.
/// </returns>
Task ModifyAsync(Action<GuildChannelProperties> func, RequestOptions options = null); Task ModifyAsync(Action<GuildChannelProperties> func, RequestOptions options = null);


/// <summary> /// <summary>
/// Gets the permission overwrite for a specific role, or <c>null</c> if one does not exist. /// Gets the permission overwrite for a specific role, or <c>null</c> if one does not exist.
/// </summary> /// </summary>
/// <param name="role">The role to get the overwrite from.</param> /// <param name="role">The role to get the overwrite from.</param>
/// <returns>
/// An overwrite object for the targeted role; <c>null</c> if none is set.
/// </returns>
OverwritePermissions? GetPermissionOverwrite(IRole role); OverwritePermissions? GetPermissionOverwrite(IRole role);
/// <summary> /// <summary>
/// Gets the permission overwrite for a specific user, or <c>null</c> if one does not exist. /// Gets the permission overwrite for a specific user, or <c>null</c> if one does not exist.
/// </summary> /// </summary>
/// <param name="user">The user to get the overwrite from.</param> /// <param name="user">The user to get the overwrite from.</param>
/// <returns>
/// An overwrite object for the targeted user; <c>null</c> if none is set.
/// </returns>
OverwritePermissions? GetPermissionOverwrite(IUser user); OverwritePermissions? GetPermissionOverwrite(IUser user);
/// <summary> /// <summary>
/// Removes the permission overwrite for the given role, if one exists. /// Removes the permission overwrite for the given role, if one exists.
/// </summary> /// </summary>
/// <param name="role">The role to remove the overwrite from.</param> /// <param name="role">The role to remove the overwrite from.</param>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/>.
/// </returns>
Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null); Task RemovePermissionOverwriteAsync(IRole role, RequestOptions options = null);
/// <summary> /// <summary>
/// Removes the permission overwrite for the given user, if one exists. /// Removes the permission overwrite for the given user, if one exists.
/// </summary> /// </summary>
/// <param name="user">The user to remove the overwrite from.</param> /// <param name="user">The user to remove the overwrite from.</param>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/>.
/// </returns>
Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null); Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null);


/// <summary> /// <summary>
@@ -114,6 +127,9 @@ namespace Discord
/// <param name="role">The role to add the overwrite to.</param> /// <param name="role">The role to add the overwrite to.</param>
/// <param name="permissions">The overwrite to add to the role.</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> /// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/>.
/// </returns>
Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null); Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options = null);
/// <summary> /// <summary>
/// Adds or updates the permission overwrite for the given user. /// Adds or updates the permission overwrite for the given user.
@@ -121,6 +137,9 @@ namespace Discord
/// <param name="user">The user to add the overwrite to.</param> /// <param name="user">The user to add the overwrite to.</param>
/// <param name="permissions">The overwrite to add to the user.</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> /// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/>.
/// </returns>
Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null); Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options = null);


/// <summary> /// <summary>
@@ -130,6 +149,11 @@ namespace Discord
/// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.
/// </param> /// </param>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A paged collection containing a collection of guild users that can access this channel. Flattening the
/// paginated response into a collection of users with
/// <see cref="AsyncEnumerableExtensions.FlattenAsync{T}"/> is required if you wish to access the users.
/// </returns>
new IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); new IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> GetUsersAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
/// <summary> /// <summary>
/// Gets a user in this channel with the provided ID. /// Gets a user in this channel with the provided ID.
@@ -139,6 +163,9 @@ namespace Discord
/// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache. /// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.
/// </param> /// </param>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/> containing a guild user object that represents the user.
/// </returns>
new Task<IGuildUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); new Task<IGuildUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
} }
} }

+ 6
- 3
src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs View File

@@ -20,8 +20,7 @@ namespace Discord
/// <returns> /// <returns>
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
Task<IUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#if FILESYSTEM
Task<IUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
/// <summary> /// <summary>
/// Sends a file to this message channel with an optional caption. /// Sends a file to this message channel with an optional caption.
/// </summary> /// </summary>
@@ -39,7 +38,6 @@ namespace Discord
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); Task<IUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#endif
/// <summary> /// <summary>
/// Sends a file to this message channel with an optional caption. /// Sends a file to this message channel with an optional caption.
/// </summary> /// </summary>
@@ -121,6 +119,11 @@ namespace Discord
/// </returns> /// </returns>
Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync(RequestOptions options = null); Task<IReadOnlyCollection<IMessage>> GetPinnedMessagesAsync(RequestOptions options = null);


/// <summary> Deletes a message based on the message ID in this channel. </summary>
Task DeleteMessageAsync(ulong messageId, RequestOptions options = null);
/// <summary> Deletes a message based on the provided message in this channel. </summary>
Task DeleteMessageAsync(IMessage message, RequestOptions options = null);

/// <summary> /// <summary>
/// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. /// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds.
/// </summary> /// </summary>


+ 28
- 0
src/Discord.Net.Core/Entities/Channels/INestedChannel.cs View File

@@ -0,0 +1,28 @@
using System.Threading.Tasks;

namespace Discord
{
/// <summary>
/// Represents a type of guild channel that can be nested within a category.
/// </summary>
public interface INestedChannel : IGuildChannel
{
/// <summary>
/// Gets the parent (category) ID of this channel in the guild's channel list.
/// </summary>
/// <returns>
/// An <see cref="System.UInt64"/> representing the snowflake identifier of the parent of this channel;
/// <c>null</c> if none is set.
/// </returns>
ulong? CategoryId { get; }
/// <summary>
/// Gets the parent (category) channel of this channel.
/// </summary>
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <remarks>
/// A category channel representing the parent of this channel; <c>null</c> if none is set.
/// </remarks>
Task<ICategoryChannel> GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
}
}

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

@@ -8,7 +8,7 @@ namespace Discord
/// <summary> /// <summary>
/// Represents a generic channel in a guild that can send and receive messages. /// Represents a generic channel in a guild that can send and receive messages.
/// </summary> /// </summary>
public interface ITextChannel : IMessageChannel, IMentionable, IGuildChannel
public interface ITextChannel : IMessageChannel, IMentionable, INestedChannel
{ {
/// <summary> /// <summary>
/// Determines whether the channel is NSFW. /// Determines whether the channel is NSFW.
@@ -80,7 +80,7 @@ namespace Discord
/// </summary> /// </summary>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns> /// <returns>
/// A collection of webhooks.
/// An awaitable <see cref="Task"/> containing a collection of webhooks.
/// </returns> /// </returns>
Task<IReadOnlyCollection<IWebhook>> GetWebhooksAsync(RequestOptions options = null); Task<IReadOnlyCollection<IWebhook>> GetWebhooksAsync(RequestOptions options = null);
} }


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

@@ -4,9 +4,9 @@ using System.Threading.Tasks;
namespace Discord namespace Discord
{ {
/// <summary> /// <summary>
/// Represents a voice channel in a guild.
/// Represents a generic voice channel in a guild.
/// </summary> /// </summary>
public interface IVoiceChannel : IGuildChannel, IAudioChannel
public interface IVoiceChannel : INestedChannel, IAudioChannel
{ {
/// <summary> /// <summary>
/// Gets the bitrate, in bits per second, clients in this voice channel are requested to use. /// Gets the bitrate, in bits per second, clients in this voice channel are requested to use.


+ 6
- 0
src/Discord.Net.Core/Entities/Channels/ReorderChannelProperties.cs View File

@@ -8,10 +8,16 @@ namespace Discord
/// <summary> /// <summary>
/// Gets the ID of the channel to apply this position to. /// Gets the ID of the channel to apply this position to.
/// </summary> /// </summary>
/// <returns>
/// A <see cref="ulong"/> representing the snowflake identififer of this channel.
/// </returns>
public ulong Id { get; } public ulong Id { get; }
/// <summary> /// <summary>
/// Gets the new zero-based position of this channel. /// Gets the new zero-based position of this channel.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="int"/> representing the new position of this channel.
/// </returns>
public int Position { get; } public int Position { get; }


/// <summary> Creates a <see cref="ReorderChannelProperties"/> used to reorder a channel. </summary> /// <summary> Creates a <see cref="ReorderChannelProperties"/> used to reorder a channel. </summary>


+ 7
- 1
src/Discord.Net.Core/Entities/Guilds/IBan.cs View File

@@ -8,10 +8,16 @@ namespace Discord
/// <summary> /// <summary>
/// Gets the banned user. /// Gets the banned user.
/// </summary> /// </summary>
/// <returns>
/// A user that was banned.
/// </returns>
IUser User { get; } IUser User { get; }
/// <summary> /// <summary>
/// Gets the reason why the user is banned.
/// Gets the reason why the user is banned if specified.
/// </summary> /// </summary>
/// <returns>
/// A string containing the reason behind the ban; <c>null</c> if none is specified.
/// </returns>
string Reason { get; } string Reason { get; }
} }
} }

+ 27
- 7
src/Discord.Net.Core/Entities/Guilds/IGuild.cs View File

@@ -22,7 +22,8 @@ namespace Discord
/// automatically moved to the AFK voice channel. /// automatically moved to the AFK voice channel.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The amount of time in seconds for a user to be marked as inactive and moved into the AFK voice channel.
/// An <see cref="Int32"/> representing the amount of time in seconds for a user to be marked as inactive
/// and moved into the AFK voice channel.
/// </returns> /// </returns>
int AFKTimeout { get; } int AFKTimeout { get; }
/// <summary> /// <summary>
@@ -94,8 +95,12 @@ namespace Discord
bool Available { get; } bool Available { get; }


/// <summary> /// <summary>
/// Gets the ID of the AFK voice channel for this guild, or <c>null</c> if none is set.
/// Gets the ID of the AFK voice channel for this guild.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="UInt64" /> representing the snowflake identifier of the AFK voice channel; <c>null</c> if
/// none is set.
/// </returns>
ulong? AFKChannelId { get; } ulong? AFKChannelId { get; }
/// <summary> /// <summary>
/// Gets the ID of the the default channel for this guild. /// Gets the ID of the the default channel for this guild.
@@ -214,18 +219,20 @@ namespace Discord
/// Gets a ban object for a banned user. /// Gets a ban object for a banned user.
/// </summary> /// </summary>
/// <param name="user">The banned user.</param> /// <param name="user">The banned user.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns> /// <returns>
/// An awaitable <see cref="Task"/> containing the ban object, which contains the user information and the /// An awaitable <see cref="Task"/> containing the ban object, which contains the user information and the
/// reason for the ban; <c>null<c/> if the ban entry cannot be found.
/// reason for the ban; <c>null</c> if the ban entry cannot be found.
/// </returns> /// </returns>
Task<IBan> GetBanAsync(IUser user, RequestOptions options = null); Task<IBan> GetBanAsync(IUser user, RequestOptions options = null);
/// <summary> /// <summary>
/// Gets a ban object for a banned user. /// Gets a ban object for a banned user.
/// </summary> /// </summary>
/// <param name="userId">The snowflake identifier for the banned user.</param> /// <param name="userId">The snowflake identifier for the banned user.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns> /// <returns>
/// An awaitable <see cref="Task"/> containing the ban object, which contains the user information and the /// An awaitable <see cref="Task"/> containing the ban object, which contains the user information and the
/// reason for the ban; <c>null<c/> if the ban entry cannot be found.
/// reason for the ban; <c>null</c> if the ban entry cannot be found.
/// </returns> /// </returns>
Task<IBan> GetBanAsync(ulong userId, RequestOptions options = null); Task<IBan> GetBanAsync(ulong userId, RequestOptions options = null);
/// <summary> /// <summary>
@@ -406,24 +413,27 @@ namespace Discord
/// <c>null</c> if none is set. /// <c>null</c> if none is set.
/// </returns> /// </returns>
Task<IGuildChannel> GetEmbedChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task<IGuildChannel> GetEmbedChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);

/// <summary> /// <summary>
/// Creates a new text channel. /// Creates a new text channel.
/// </summary> /// </summary>
/// <param name="name">The new name for the text channel.</param> /// <param name="name">The new name for the text channel.</param>
/// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns> /// <returns>
/// An awaitable <see cref="Task"/> containing the newly created text channel. /// An awaitable <see cref="Task"/> containing the newly created text channel.
/// </returns> /// </returns>
Task<ITextChannel> CreateTextChannelAsync(string name, RequestOptions options = null);
Task<ITextChannel> CreateTextChannelAsync(string name, Action<TextChannelProperties> func = null, RequestOptions options = null);
/// <summary> /// <summary>
/// Creates a new voice channel. /// Creates a new voice channel.
/// </summary> /// </summary>
/// <param name="name">The new name for the voice channel.</param> /// <param name="name">The new name for the voice channel.</param>
/// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param>
/// <param name="options">The options to be used when sending the request.</param> /// <param name="options">The options to be used when sending the request.</param>
/// <returns> /// <returns>
/// An awaitable <see cref="Task"/> containing the newly created voice channel. /// An awaitable <see cref="Task"/> containing the newly created voice channel.
/// </returns> /// </returns>
Task<IVoiceChannel> CreateVoiceChannelAsync(string name, RequestOptions options = null);
Task<IVoiceChannel> CreateVoiceChannelAsync(string name, Action<VoiceChannelProperties> func = null, RequestOptions options = null);
/// <summary> /// <summary>
/// Creates a new channel category. /// Creates a new channel category.
/// </summary> /// </summary>
@@ -537,7 +547,17 @@ namespace Discord
/// </returns> /// </returns>
Task<int> PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null); Task<int> PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null);


/// <summary> Gets the specified number of audit log entries for this guild. </summary>
/// <summary>
/// Gets the specified number of audit log entries for this guild.
/// </summary>
/// <param name="limit">The number of audit log entries to fetch.</param>
/// <param name="mode">
/// The <see cref="CacheMode" /> that determines whether the object should be fetched from cache.
/// </param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// An awaitable <see cref="Task"/> containing a collection of requested audit log entries.
/// </returns>
Task<IReadOnlyCollection<IAuditLogEntry>> GetAuditLogAsync(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch, Task<IReadOnlyCollection<IAuditLogEntry>> GetAuditLogAsync(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch,
CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);




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

@@ -1,4 +1,3 @@
using System;
using System.IO; using System.IO;
namespace Discord namespace Discord
{ {
@@ -22,7 +21,7 @@ namespace Discord
{ {
Stream = stream; Stream = stream;
} }
#if FILESYSTEM
/// <summary> /// <summary>
/// Create the image from a file path. /// Create the image from a file path.
/// </summary> /// </summary>
@@ -31,21 +30,21 @@ namespace Discord
/// <see cref="File.OpenRead"/>. /// <see cref="File.OpenRead"/>.
/// </remarks> /// </remarks>
/// <param name="path">The path to the file.</param> /// <param name="path">The path to the file.</param>
/// <exception cref="ArgumentException">
/// <exception cref="System.ArgumentException">
/// <paramref name="path" /> is a zero-length string, contains only white space, or contains one or more invalid /// <paramref name="path" /> is a zero-length string, contains only white space, or contains one or more invalid
/// characters as defined by <see cref="Path.GetInvalidPathChars" />. /// characters as defined by <see cref="Path.GetInvalidPathChars" />.
/// </exception> /// </exception>
/// <exception cref="ArgumentNullException"><paramref name="path" /> is <c>null</c>.</exception>
/// <exception cref="System.ArgumentNullException"><paramref name="path" /> is <c>null</c>.</exception>
/// <exception cref="PathTooLongException"> /// <exception cref="PathTooLongException">
/// The specified path, file name, or both exceed the system-defined maximum length. For example, on /// The specified path, file name, or both exceed the system-defined maximum length. For example, on
/// Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 /// Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260
/// characters. /// characters.
/// </exception> /// </exception>
/// <exception cref="NotSupportedException"><paramref name="path" /> is in an invalid format.</exception>
/// <exception cref="System.NotSupportedException"><paramref name="path" /> is in an invalid format.</exception>
/// <exception cref="DirectoryNotFoundException"> /// <exception cref="DirectoryNotFoundException">
/// The specified <paramref name="path"/> is invalid, (for example, it is on an unmapped drive). /// The specified <paramref name="path"/> is invalid, (for example, it is on an unmapped drive).
/// </exception> /// </exception>
/// <exception cref="UnauthorizedAccessException">
/// <exception cref="System.UnauthorizedAccessException">
/// <paramref name="path" /> specified a directory.-or- The caller does not have the required permission. /// <paramref name="path" /> specified a directory.-or- The caller does not have the required permission.
/// </exception> /// </exception>
/// <exception cref="FileNotFoundException">The file specified in <paramref name="path" /> was not found. /// <exception cref="FileNotFoundException">The file specified in <paramref name="path" /> was not found.
@@ -55,6 +54,6 @@ namespace Discord
{ {
Stream = File.OpenRead(path); Stream = File.OpenRead(path);
} }
#endif
} }
} }

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

@@ -1,5 +1,3 @@
using System.Threading.Tasks;

namespace Discord namespace Discord
{ {
/// <summary> /// <summary>
@@ -10,44 +8,76 @@ namespace Discord
/// <summary> /// <summary>
/// Gets the unique identifier for this invite. /// Gets the unique identifier for this invite.
/// </summary> /// </summary>
/// <returns>
/// A string containing the invite code (e.g. <c>FTqNnyS</c>).
/// </returns>
string Code { get; } string Code { get; }
/// <summary> /// <summary>
/// Gets the URL used to accept this invite, using Code.
/// Gets the URL used to accept this invite using <see cref="Code"/>.
/// </summary> /// </summary>
/// <returns>
/// A string containing the full invite URL (e.g. <c>https://discord.gg/FTqNnyS</c>).
/// </returns>
string Url { get; } string Url { get; }


/// <summary> /// <summary>
/// Gets the channel this invite is linked to. /// Gets the channel this invite is linked to.
/// </summary> /// </summary>
/// <returns>
/// A generic channel that the invite points to.
/// </returns>
IChannel Channel { get; } IChannel Channel { get; }
/// <summary> /// <summary>
/// Gets the ID of the channel this invite is linked to. /// Gets the ID of the channel this invite is linked to.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="ulong"/> representing the channel snowflake identifier that the invite points to.
/// </returns>
ulong ChannelId { get; } ulong ChannelId { get; }
/// <summary> /// <summary>
/// Gets the name of the channel this invite is linked to. /// Gets the name of the channel this invite is linked to.
/// </summary> /// </summary>
/// <returns>
/// A string containing the name of the channel that the invite points to.
/// </returns>
string ChannelName { get; } string ChannelName { get; }


/// <summary> /// <summary>
/// Gets the guild this invite is linked to. /// Gets the guild this invite is linked to.
/// </summary> /// </summary>
/// <returns>
/// A guild object representing the guild that the invite points to.
/// </returns>
IGuild Guild { get; } IGuild Guild { get; }
/// <summary> /// <summary>
/// Gets the ID of the guild this invite is linked to. /// Gets the ID of the guild this invite is linked to.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="ulong"/> representing the guild snowflake identifier that the invite points to.
/// </returns>
ulong GuildId { get; } ulong GuildId { get; }
/// <summary> /// <summary>
/// Gets the name of the guild this invite is linked to. /// Gets the name of the guild this invite is linked to.
/// </summary> /// </summary>
/// <returns>
/// A string containing the name of the guild that the invite points to.
/// </returns>
string GuildName { get; } string GuildName { get; }
/// <summary> /// <summary>
/// Gets the approximated count of online members in the guild. /// Gets the approximated count of online members in the guild.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="System.Int32" /> representing the approximated online member count of the guild that the
/// invite points to; <c>null</c> if one cannot be obtained.
/// </returns>
int? PresenceCount { get; } int? PresenceCount { get; }
/// <summary> /// <summary>
/// Gets the approximated count of total members in the guild. /// Gets the approximated count of total members in the guild.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="System.Int32" /> representing the approximated total member count of the guild that the
/// invite points to; <c>null</c> if one cannot be obtained.
/// </returns>
int? MemberCount { get; } int? MemberCount { get; }
} }
} }

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

@@ -2,37 +2,63 @@ using System;


namespace Discord namespace Discord
{ {
/// <summary> Represents additional information regarding the generic invite object. </summary>
/// <summary>
/// Represents additional information regarding the generic invite object.
/// </summary>
public interface IInviteMetadata : IInvite public interface IInviteMetadata : IInvite
{ {
/// <summary> /// <summary>
/// Gets the user that created this invite. /// Gets the user that created this invite.
/// </summary> /// </summary>
/// <returns>
/// A user that created this invite.
/// </returns>
IUser Inviter { get; } IUser Inviter { get; }
/// <summary> /// <summary>
/// Returns <c>true</c> if this invite was revoked.
/// Determines whether the invite has been revoked.
/// </summary> /// </summary>
/// <returns>
/// <c>true</c> if this invite was revoked; otherwise <c>false</c>.
/// </returns>
bool IsRevoked { get; } bool IsRevoked { get; }
/// <summary> /// <summary>
/// Returns <c>true</c> if users accepting this invite will be removed from the guild when they
/// log off.
/// Determines whether the invite is a temporary one (i.e. whether the invite will be removed from the guild
/// when the user logs off).
/// </summary> /// </summary>
/// <returns>
/// <c>true</c> if users accepting this invite will be removed from the guild when they log off; otherwise
/// <c>false</c>.
/// </returns>
bool IsTemporary { get; } bool IsTemporary { get; }
/// <summary> /// <summary>
/// Gets the time (in seconds) until the invite expires, or <c>null</c> if it never expires.
/// Gets the time (in seconds) until the invite expires.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds until this invite expires; <c>null</c> if this
/// invite never expires.
/// </returns>
int? MaxAge { get; } int? MaxAge { get; }
/// <summary> /// <summary>
/// Gets the max amount of times this invite may be used, or <c>null</c> if there is no limit.
/// Gets the max number of uses this invite may have.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of uses this invite may be accepted until it is removed
/// from the guild; <c>null</c> if none is set.
/// </returns>
int? MaxUses { get; } int? MaxUses { get; }
/// <summary> /// <summary>
/// Gets the amount of times this invite has been used.
/// Gets the number of times this invite has been used.
/// </summary> /// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of times this invite has been used.
/// </returns>
int Uses { get; } int Uses { get; }
/// <summary> /// <summary>
/// Gets when this invite was created. /// Gets when this invite was created.
/// </summary> /// </summary>
/// <returns>
/// A <see cref="DateTimeOffset"/> representing the time of which the invite was first created.
/// </returns>
DateTimeOffset CreatedAt { get; } DateTimeOffset CreatedAt { get; }
} }
} }

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

@@ -42,7 +42,7 @@ namespace Discord
/// <summary> /// <summary>
/// Gets all users that reacted to a message with a given emote. /// Gets all users that reacted to a message with a given emote.
/// </summary> /// </summary>
Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null);
IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emoji, int limit, RequestOptions options = null);


/// <summary> /// <summary>
/// Transforms this message's text into a human-readable form by resolving its tags. /// Transforms this message's text into a human-readable form by resolving its tags.


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

@@ -88,12 +88,27 @@ namespace Discord
/// <summary> Creates a new <see cref="ChannelPermissions"/> with the provided packed value. </summary> /// <summary> Creates a new <see cref="ChannelPermissions"/> with the provided packed value. </summary>
public ChannelPermissions(ulong rawValue) { RawValue = rawValue; } public ChannelPermissions(ulong rawValue) { RawValue = rawValue; }


private ChannelPermissions(ulong initialValue, bool? createInstantInvite = null, bool? manageChannel = null,
private ChannelPermissions(ulong initialValue,
bool? createInstantInvite = null,
bool? manageChannel = null,
bool? addReactions = null, bool? addReactions = null,
bool? viewChannel = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
bool? moveMembers = null, bool? useVoiceActivation = null, bool? manageRoles = null, bool? manageWebhooks = null)
bool? viewChannel = null,
bool? sendMessages = null,
bool? sendTTSMessages = null,
bool? manageMessages = null,
bool? embedLinks = null,
bool? attachFiles = null,
bool? readMessageHistory = null,
bool? mentionEveryone = null,
bool? useExternalEmojis = null,
bool? connect = null,
bool? speak = null,
bool? muteMembers = null,
bool? deafenMembers = null,
bool? moveMembers = null,
bool? useVoiceActivation = null,
bool? manageRoles = null,
bool? manageWebhooks = null)
{ {
ulong value = initialValue; ulong value = initialValue;


@@ -122,27 +137,75 @@ namespace Discord
} }


/// <summary> Creates a new <see cref="ChannelPermissions"/> with the provided permissions. </summary> /// <summary> Creates a new <see cref="ChannelPermissions"/> with the provided permissions. </summary>
public ChannelPermissions(bool createInstantInvite = false, bool manageChannel = false,
public ChannelPermissions(
bool createInstantInvite = false,
bool manageChannel = false,
bool addReactions = false, bool addReactions = false,
bool viewChannel = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false,
bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false,
bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false,
bool moveMembers = false, bool useVoiceActivation = false, bool manageRoles = false, bool manageWebhooks = false)
bool viewChannel = false,
bool sendMessages = false,
bool sendTTSMessages = false,
bool manageMessages = false,
bool embedLinks = false,
bool attachFiles = false,
bool readMessageHistory = false,
bool mentionEveryone = false,
bool useExternalEmojis = false,
bool connect = false,
bool speak = false,
bool muteMembers = false,
bool deafenMembers = false,
bool moveMembers = false,
bool useVoiceActivation = false,
bool manageRoles = false,
bool manageWebhooks = false)
: this(0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, : this(0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages,
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect,
speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks) speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks)
{ } { }


/// <summary> Creates a new <see cref="ChannelPermissions"/> from this one, changing the provided non-null permissions. </summary> /// <summary> Creates a new <see cref="ChannelPermissions"/> from this one, changing the provided non-null permissions. </summary>
public ChannelPermissions Modify(bool? createInstantInvite = null, bool? manageChannel = null,
public ChannelPermissions Modify(
bool? createInstantInvite = null,
bool? manageChannel = null,
bool? addReactions = null, bool? addReactions = null,
bool? viewChannel = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool useExternalEmojis = false, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
bool? moveMembers = null, bool? useVoiceActivation = null, bool? manageRoles = null, bool? manageWebhooks = null)
=> new ChannelPermissions(RawValue, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages,
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect,
speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks);
bool? viewChannel = null,
bool? sendMessages = null,
bool? sendTTSMessages = null,
bool? manageMessages = null,
bool? embedLinks = null,
bool? attachFiles = null,
bool? readMessageHistory = null,
bool? mentionEveryone = null,
bool? useExternalEmojis = null,
bool? connect = null,
bool? speak = null,
bool? muteMembers = null,
bool? deafenMembers = null,
bool? moveMembers = null,
bool? useVoiceActivation = null,
bool? manageRoles = null,
bool? manageWebhooks = null)
=> new ChannelPermissions(RawValue,
createInstantInvite,
manageChannel,
addReactions,
viewChannel,
sendMessages,
sendTTSMessages,
manageMessages,
embedLinks,
attachFiles,
readMessageHistory,
mentionEveryone,
useExternalEmojis,
connect,
speak,
muteMembers,
deafenMembers,
moveMembers,
useVoiceActivation,
manageRoles,
manageWebhooks);


public bool Has(ChannelPermission permission) => Permissions.GetValue(RawValue, permission); public bool Has(ChannelPermission permission) => Permissions.GetValue(RawValue, permission);




+ 3
- 7
src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs View File

@@ -41,13 +41,9 @@ namespace Discord
/// Allows for viewing of audit logs. /// Allows for viewing of audit logs.
/// </summary> /// </summary>
ViewAuditLog = 0x00_00_00_80, ViewAuditLog = 0x00_00_00_80,
/// <summary>
/// Allows for reading of message.
/// </summary>
ReadMessages = 0x00_00_04_00,
/// <summary>
/// Allows for sending messages in a channel.
/// </summary>
[Obsolete("Use ViewChannel instead.")]
ReadMessages = ViewChannel,
ViewChannel = 0x00_00_04_00,
SendMessages = 0x00_00_08_00, SendMessages = 0x00_00_08_00,
/// <summary> /// <summary>
/// Allows for sending of text-to-speech messages. /// Allows for sending of text-to-speech messages.


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

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;


@@ -34,9 +35,12 @@ namespace Discord
/// <summary> If <c>true</c>, a user may view the audit log. </summary> /// <summary> If <c>true</c>, a user may view the audit log. </summary>
public bool ViewAuditLog => Permissions.GetValue(RawValue, GuildPermission.ViewAuditLog); public bool ViewAuditLog => Permissions.GetValue(RawValue, GuildPermission.ViewAuditLog);


/// <summary> If <c>true</c>, a user may join channels. </summary>
public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages);
/// <summary> If <c>true</c>, a user may send messages. </summary>
/// <summary> If True, a user may join channels. </summary>
[Obsolete("Use ViewChannel instead.")]
public bool ReadMessages => ViewChannel;
/// <summary> If True, a user may view channels. </summary>
public bool ViewChannel => Permissions.GetValue(RawValue, GuildPermission.ViewChannel);
/// <summary> If True, a user may send messages. </summary>
public bool SendMessages => Permissions.GetValue(RawValue, GuildPermission.SendMessages); public bool SendMessages => Permissions.GetValue(RawValue, GuildPermission.SendMessages);
/// <summary> If <c>true</c>, a user may send text-to-speech messages. </summary> /// <summary> If <c>true</c>, a user may send text-to-speech messages. </summary>
public bool SendTTSMessages => Permissions.GetValue(RawValue, GuildPermission.SendTTSMessages); public bool SendTTSMessages => Permissions.GetValue(RawValue, GuildPermission.SendTTSMessages);
@@ -80,14 +84,35 @@ namespace Discord
/// <summary> Creates a new <see cref="GuildPermissions"/> with the provided packed value. </summary> /// <summary> Creates a new <see cref="GuildPermissions"/> with the provided packed value. </summary>
public GuildPermissions(ulong rawValue) { RawValue = rawValue; } public GuildPermissions(ulong rawValue) { RawValue = rawValue; }


private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null,
bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null,
bool? addReactions = null, bool? viewAuditLog = null,
bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null,
bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null)
private GuildPermissions(ulong initialValue,
bool? createInstantInvite = null,
bool? kickMembers = null,
bool? banMembers = null,
bool? administrator = null,
bool? manageChannels = null,
bool? manageGuild = null,
bool? addReactions = null,
bool? viewAuditLog = null,
bool? viewChannel = null,
bool? sendMessages = null,
bool? sendTTSMessages = null,
bool? manageMessages = null,
bool? embedLinks = null,
bool? attachFiles = null,
bool? readMessageHistory = null,
bool? mentionEveryone = null,
bool? useExternalEmojis = null,
bool? connect = null,
bool? speak = null,
bool? muteMembers = null,
bool? deafenMembers = null,
bool? moveMembers = null,
bool? useVoiceActivation = null,
bool? changeNickname = null,
bool? manageNicknames = null,
bool? manageRoles = null,
bool? manageWebhooks = null,
bool? manageEmojis = null)
{ {
ulong value = initialValue; ulong value = initialValue;


@@ -99,7 +124,7 @@ namespace Discord
Permissions.SetValue(ref value, manageGuild, GuildPermission.ManageGuild); Permissions.SetValue(ref value, manageGuild, GuildPermission.ManageGuild);
Permissions.SetValue(ref value, addReactions, GuildPermission.AddReactions); Permissions.SetValue(ref value, addReactions, GuildPermission.AddReactions);
Permissions.SetValue(ref value, viewAuditLog, GuildPermission.ViewAuditLog); Permissions.SetValue(ref value, viewAuditLog, GuildPermission.ViewAuditLog);
Permissions.SetValue(ref value, readMessages, GuildPermission.ReadMessages);
Permissions.SetValue(ref value, viewChannel, GuildPermission.ViewChannel);
Permissions.SetValue(ref value, sendMessages, GuildPermission.SendMessages); Permissions.SetValue(ref value, sendMessages, GuildPermission.SendMessages);
Permissions.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages); Permissions.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages);
Permissions.SetValue(ref value, manageMessages, GuildPermission.ManageMessages); Permissions.SetValue(ref value, manageMessages, GuildPermission.ManageMessages);
@@ -124,34 +149,98 @@ namespace Discord
} }


/// <summary> Creates a new <see cref="GuildPermissions"/> with the provided permissions. </summary> /// <summary> Creates a new <see cref="GuildPermissions"/> with the provided permissions. </summary>
public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false,
bool banMembers = false, bool administrator = false, bool manageChannels = false, bool manageGuild = false,
bool addReactions = false, bool viewAuditLog = false,
bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false,
bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false,
bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false,
bool moveMembers = false, bool useVoiceActivation = false, bool? changeNickname = false, bool? manageNicknames = false,
bool manageRoles = false, bool manageWebhooks = false, bool manageEmojis = false)
: this(0, createInstantInvite: createInstantInvite, manageRoles: manageRoles, kickMembers: kickMembers, banMembers: banMembers,
administrator: administrator, manageChannels: manageChannels, manageGuild: manageGuild, addReactions: addReactions,
viewAuditLog: viewAuditLog, readMessages: readMessages, sendMessages: sendMessages, sendTTSMessages: sendTTSMessages,
manageMessages: manageMessages, embedLinks: embedLinks, attachFiles: attachFiles, readMessageHistory: readMessageHistory,
mentionEveryone: mentionEveryone, useExternalEmojis: useExternalEmojis, connect: connect, speak: speak, muteMembers: muteMembers,
deafenMembers: deafenMembers, moveMembers: moveMembers, useVoiceActivation: useVoiceActivation, changeNickname: changeNickname,
manageNicknames: manageNicknames, manageWebhooks: manageWebhooks, manageEmojis: manageEmojis)
public GuildPermissions(
bool createInstantInvite = false,
bool kickMembers = false,
bool banMembers = false,
bool administrator = false,
bool manageChannels = false,
bool manageGuild = false,
bool addReactions = false,
bool viewAuditLog = false,
bool viewChannel = false,
bool sendMessages = false,
bool sendTTSMessages = false,
bool manageMessages = false,
bool embedLinks = false,
bool attachFiles = false,
bool readMessageHistory = false,
bool mentionEveryone = false,
bool useExternalEmojis = false,
bool connect = false,
bool speak = false,
bool muteMembers = false,
bool deafenMembers = false,
bool moveMembers = false,
bool useVoiceActivation = false,
bool changeNickname = false,
bool manageNicknames = false,
bool manageRoles = false,
bool manageWebhooks = false,
bool manageEmojis = false)
: this(0,
createInstantInvite: createInstantInvite,
manageRoles: manageRoles,
kickMembers: kickMembers,
banMembers: banMembers,
administrator: administrator,
manageChannels: manageChannels,
manageGuild: manageGuild,
addReactions: addReactions,
viewAuditLog: viewAuditLog,
viewChannel: viewChannel,
sendMessages: sendMessages,
sendTTSMessages: sendTTSMessages,
manageMessages: manageMessages,
embedLinks: embedLinks,
attachFiles: attachFiles,
readMessageHistory: readMessageHistory,
mentionEveryone: mentionEveryone,
useExternalEmojis: useExternalEmojis,
connect: connect,
speak: speak,
muteMembers: muteMembers,
deafenMembers: deafenMembers,
moveMembers: moveMembers,
useVoiceActivation: useVoiceActivation,
changeNickname: changeNickname,
manageNicknames: manageNicknames,
manageWebhooks: manageWebhooks,
manageEmojis: manageEmojis)
{ } { }


/// <summary> Creates a new <see cref="GuildPermissions"/> from this one, changing the provided non-null permissions. </summary> /// <summary> Creates a new <see cref="GuildPermissions"/> from this one, changing the provided non-null permissions. </summary>
public GuildPermissions Modify(bool? createInstantInvite = null, bool? kickMembers = null,
bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null,
bool? addReactions = null, bool? viewAuditLog = null,
bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null,
bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null,
bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null,
bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null,
bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null)
public GuildPermissions Modify(
bool? createInstantInvite = null,
bool? kickMembers = null,
bool? banMembers = null,
bool? administrator = null,
bool? manageChannels = null,
bool? manageGuild = null,
bool? addReactions = null,
bool? viewAuditLog = null,
bool? viewChannel = null,
bool? sendMessages = null,
bool? sendTTSMessages = null,
bool? manageMessages = null,
bool? embedLinks = null,
bool? attachFiles = null,
bool? readMessageHistory = null,
bool? mentionEveryone = null,
bool? useExternalEmojis = null,
bool? connect = null,
bool? speak = null,
bool? muteMembers = null,
bool? deafenMembers = null,
bool? moveMembers = null,
bool? useVoiceActivation = null,
bool? changeNickname = null,
bool? manageNicknames = null,
bool? manageRoles = null,
bool? manageWebhooks = null,
bool? manageEmojis = null)
=> new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions, => new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions,
viewAuditLog, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles,
viewAuditLog, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles,
readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers,
useVoiceActivation, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis); useVoiceActivation, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis);




+ 61
- 17
src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs View File

@@ -84,12 +84,26 @@ namespace Discord
DenyValue = denyValue; DenyValue = denyValue;
} }


private OverwritePermissions(ulong allowValue, ulong denyValue, PermValue? createInstantInvite = null, PermValue? manageChannel = null,
private OverwritePermissions(ulong allowValue, ulong denyValue,
PermValue? createInstantInvite = null,
PermValue? manageChannel = null,
PermValue? addReactions = null, PermValue? addReactions = null,
PermValue? viewChannel = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null,
PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null,
PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null,
PermValue? deafenMembers = null, PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? manageRoles = null,
PermValue? viewChannel = null,
PermValue? sendMessages = null,
PermValue? sendTTSMessages = null,
PermValue? manageMessages = null,
PermValue? embedLinks = null,
PermValue? attachFiles = null,
PermValue? readMessageHistory = null,
PermValue? mentionEveryone = null,
PermValue? useExternalEmojis = null,
PermValue? connect = null,
PermValue? speak = null,
PermValue? muteMembers = null,
PermValue? deafenMembers = null,
PermValue? moveMembers = null,
PermValue? useVoiceActivation = null,
PermValue? manageRoles = null,
PermValue? manageWebhooks = null) PermValue? manageWebhooks = null)
{ {
Permissions.SetValue(ref allowValue, ref denyValue, createInstantInvite, ChannelPermission.CreateInstantInvite); Permissions.SetValue(ref allowValue, ref denyValue, createInstantInvite, ChannelPermission.CreateInstantInvite);
@@ -120,13 +134,28 @@ namespace Discord
/// <summary> /// <summary>
/// Creates a new <see cref="ChannelPermissions" /> with the provided permissions. /// Creates a new <see cref="ChannelPermissions" /> with the provided permissions.
/// </summary> /// </summary>
public OverwritePermissions(PermValue createInstantInvite = PermValue.Inherit, PermValue manageChannel = PermValue.Inherit,
public OverwritePermissions(
PermValue createInstantInvite = PermValue.Inherit,
PermValue manageChannel = PermValue.Inherit,
PermValue addReactions = PermValue.Inherit, PermValue addReactions = PermValue.Inherit,
PermValue readMessages = PermValue.Inherit, PermValue sendMessages = PermValue.Inherit, PermValue sendTTSMessages = PermValue.Inherit, PermValue manageMessages = PermValue.Inherit,
PermValue embedLinks = PermValue.Inherit, PermValue attachFiles = PermValue.Inherit, PermValue readMessageHistory = PermValue.Inherit, PermValue mentionEveryone = PermValue.Inherit,
PermValue useExternalEmojis = PermValue.Inherit, PermValue connect = PermValue.Inherit, PermValue speak = PermValue.Inherit, PermValue muteMembers = PermValue.Inherit, PermValue deafenMembers = PermValue.Inherit,
PermValue moveMembers = PermValue.Inherit, PermValue useVoiceActivation = PermValue.Inherit, PermValue manageRoles = PermValue.Inherit, PermValue manageWebhooks = PermValue.Inherit)
: this(0, 0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages,
PermValue viewChannel = PermValue.Inherit,
PermValue sendMessages = PermValue.Inherit,
PermValue sendTTSMessages = PermValue.Inherit,
PermValue manageMessages = PermValue.Inherit,
PermValue embedLinks = PermValue.Inherit,
PermValue attachFiles = PermValue.Inherit,
PermValue readMessageHistory = PermValue.Inherit,
PermValue mentionEveryone = PermValue.Inherit,
PermValue useExternalEmojis = PermValue.Inherit,
PermValue connect = PermValue.Inherit,
PermValue speak = PermValue.Inherit,
PermValue muteMembers = PermValue.Inherit,
PermValue deafenMembers = PermValue.Inherit,
PermValue moveMembers = PermValue.Inherit,
PermValue useVoiceActivation = PermValue.Inherit,
PermValue manageRoles = PermValue.Inherit,
PermValue manageWebhooks = PermValue.Inherit)
: this(0, 0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages,
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers,
moveMembers, useVoiceActivation, manageRoles, manageWebhooks) { } moveMembers, useVoiceActivation, manageRoles, manageWebhooks) { }


@@ -134,13 +163,28 @@ namespace Discord
/// Creates a new <see cref="OverwritePermissions" /> from this one, changing the provided non-null /// Creates a new <see cref="OverwritePermissions" /> from this one, changing the provided non-null
/// permissions. /// permissions.
/// </summary> /// </summary>
public OverwritePermissions Modify(PermValue? createInstantInvite = null, PermValue? manageChannel = null,
public OverwritePermissions Modify(
PermValue? createInstantInvite = null,
PermValue? manageChannel = null,
PermValue? addReactions = null, PermValue? addReactions = null,
PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null,
PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null,
PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null,
PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? manageRoles = null, PermValue? manageWebhooks = null)
=> new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages,
PermValue? viewChannel = null,
PermValue? sendMessages = null,
PermValue? sendTTSMessages = null,
PermValue? manageMessages = null,
PermValue? embedLinks = null,
PermValue? attachFiles = null,
PermValue? readMessageHistory = null,
PermValue? mentionEveryone = null,
PermValue? useExternalEmojis = null,
PermValue? connect = null,
PermValue? speak = null,
PermValue? muteMembers = null,
PermValue? deafenMembers = null,
PermValue? moveMembers = null,
PermValue? useVoiceActivation = null,
PermValue? manageRoles = null,
PermValue? manageWebhooks = null)
=> new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages,
embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers,
moveMembers, useVoiceActivation, manageRoles, manageWebhooks); moveMembers, useVoiceActivation, manageRoles, manageWebhooks);




+ 10
- 0
src/Discord.Net.Core/Entities/Roles/Color.cs View File

@@ -1,5 +1,8 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
#if NETSTANDARD2_0 || NET45
using StandardColor = System.Drawing.Color;
#endif


namespace Discord namespace Discord
{ {
@@ -125,6 +128,13 @@ namespace Discord
(uint)(b * 255.0f); (uint)(b * 255.0f);
} }


#if NETSTANDARD2_0 || NET45
public static implicit operator StandardColor(Color color) =>
StandardColor.FromArgb((int)color.RawValue);
public static explicit operator Color(StandardColor color) =>
new Color((uint)color.ToArgb() << 8 >> 8);
#endif

/// <summary> /// <summary>
/// Gets the hexadecimal representation of the color (e.g. <c>#000ccc</c>). /// Gets the hexadecimal representation of the color (e.g. <c>#000ccc</c>).
/// </summary> /// </summary>


+ 2
- 3
src/Discord.Net.Core/Extensions/UserExtensions.cs View File

@@ -18,7 +18,7 @@ namespace Discord
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
public static async Task<IUserMessage> SendMessageAsync(this IUser user, public static async Task<IUserMessage> SendMessageAsync(this IUser user,
string text,
string text = null,
bool isTTS = false, bool isTTS = false,
Embed embed = null, Embed embed = null,
RequestOptions options = null) RequestOptions options = null)
@@ -55,7 +55,6 @@ namespace Discord
return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
} }


#if FILESYSTEM
/// <summary> /// <summary>
/// Sends a file via DM with an optional caption. /// Sends a file via DM with an optional caption.
/// </summary> /// </summary>
@@ -81,7 +80,7 @@ namespace Discord
{ {
return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
} }
#endif
/// <summary> /// <summary>
/// Bans the provided user from the guild and optionally prunes their recent messages. /// Bans the provided user from the guild and optionally prunes their recent messages.
/// </summary> /// </summary>


+ 9
- 12
src/Discord.Net.Core/Logging/LogManager.cs View File

@@ -41,7 +41,7 @@ namespace Discord.Logging
// ignored // ignored
} }
} }
#if FORMATSTR
public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null) public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null)
{ {
try try
@@ -51,52 +51,49 @@ namespace Discord.Logging
} }
catch { } catch { }
} }
#endif


public Task ErrorAsync(string source, Exception ex) public Task ErrorAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Error, source, ex); => LogAsync(LogSeverity.Error, source, ex);
public Task ErrorAsync(string source, string message, Exception ex = null) public Task ErrorAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Error, source, message, ex); => LogAsync(LogSeverity.Error, source, message, ex);
#if FORMATSTR
public Task ErrorAsync(string source, FormattableString message, Exception ex = null) public Task ErrorAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Error, source, message, ex); => LogAsync(LogSeverity.Error, source, message, ex);
#endif


public Task WarningAsync(string source, Exception ex) public Task WarningAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Warning, source, ex); => LogAsync(LogSeverity.Warning, source, ex);
public Task WarningAsync(string source, string message, Exception ex = null) public Task WarningAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Warning, source, message, ex); => LogAsync(LogSeverity.Warning, source, message, ex);
#if FORMATSTR
public Task WarningAsync(string source, FormattableString message, Exception ex = null) public Task WarningAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Warning, source, message, ex); => LogAsync(LogSeverity.Warning, source, message, ex);
#endif


public Task InfoAsync(string source, Exception ex) public Task InfoAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Info, source, ex); => LogAsync(LogSeverity.Info, source, ex);
public Task InfoAsync(string source, string message, Exception ex = null) public Task InfoAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Info, source, message, ex); => LogAsync(LogSeverity.Info, source, message, ex);
#if FORMATSTR
public Task InfoAsync(string source, FormattableString message, Exception ex = null) public Task InfoAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Info, source, message, ex); => LogAsync(LogSeverity.Info, source, message, ex);
#endif


public Task VerboseAsync(string source, Exception ex) public Task VerboseAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Verbose, source, ex); => LogAsync(LogSeverity.Verbose, source, ex);
public Task VerboseAsync(string source, string message, Exception ex = null) public Task VerboseAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Verbose, source, message, ex); => LogAsync(LogSeverity.Verbose, source, message, ex);
#if FORMATSTR
public Task VerboseAsync(string source, FormattableString message, Exception ex = null) public Task VerboseAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Verbose, source, message, ex); => LogAsync(LogSeverity.Verbose, source, message, ex);
#endif


public Task DebugAsync(string source, Exception ex) public Task DebugAsync(string source, Exception ex)
=> LogAsync(LogSeverity.Debug, source, ex); => LogAsync(LogSeverity.Debug, source, ex);
public Task DebugAsync(string source, string message, Exception ex = null) public Task DebugAsync(string source, string message, Exception ex = null)
=> LogAsync(LogSeverity.Debug, source, message, ex); => LogAsync(LogSeverity.Debug, source, message, ex);
#if FORMATSTR
public Task DebugAsync(string source, FormattableString message, Exception ex = null) public Task DebugAsync(string source, FormattableString message, Exception ex = null)
=> LogAsync(LogSeverity.Debug, source, message, ex); => LogAsync(LogSeverity.Debug, source, message, ex);
#endif


public Logger CreateLogger(string name) => new Logger(this, name); public Logger CreateLogger(string name) => new Logger(this, name);




+ 12
- 13
src/Discord.Net.Core/Logging/Logger.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading.Tasks; using System.Threading.Tasks;


namespace Discord.Logging namespace Discord.Logging
@@ -20,54 +20,53 @@ namespace Discord.Logging
=> _manager.LogAsync(severity, Name, exception); => _manager.LogAsync(severity, Name, exception);
public Task LogAsync(LogSeverity severity, string message, Exception exception = null) public Task LogAsync(LogSeverity severity, string message, Exception exception = null)
=> _manager.LogAsync(severity, Name, message, exception); => _manager.LogAsync(severity, Name, message, exception);
#if FORMATSTR
public Task LogAsync(LogSeverity severity, FormattableString message, Exception exception = null) public Task LogAsync(LogSeverity severity, FormattableString message, Exception exception = null)
=> _manager.LogAsync(severity, Name, message, exception); => _manager.LogAsync(severity, Name, message, exception);
#endif


public Task ErrorAsync(Exception exception) public Task ErrorAsync(Exception exception)
=> _manager.ErrorAsync(Name, exception); => _manager.ErrorAsync(Name, exception);
public Task ErrorAsync(string message, Exception exception = null) public Task ErrorAsync(string message, Exception exception = null)
=> _manager.ErrorAsync(Name, message, exception); => _manager.ErrorAsync(Name, message, exception);
#if FORMATSTR
public Task ErrorAsync(FormattableString message, Exception exception = null) public Task ErrorAsync(FormattableString message, Exception exception = null)
=> _manager.ErrorAsync(Name, message, exception); => _manager.ErrorAsync(Name, message, exception);
#endif


public Task WarningAsync(Exception exception) public Task WarningAsync(Exception exception)
=> _manager.WarningAsync(Name, exception); => _manager.WarningAsync(Name, exception);
public Task WarningAsync(string message, Exception exception = null) public Task WarningAsync(string message, Exception exception = null)
=> _manager.WarningAsync(Name, message, exception); => _manager.WarningAsync(Name, message, exception);
#if FORMATSTR
public Task WarningAsync(FormattableString message, Exception exception = null) public Task WarningAsync(FormattableString message, Exception exception = null)
=> _manager.WarningAsync(Name, message, exception); => _manager.WarningAsync(Name, message, exception);
#endif


public Task InfoAsync(Exception exception) public Task InfoAsync(Exception exception)
=> _manager.InfoAsync(Name, exception); => _manager.InfoAsync(Name, exception);
public Task InfoAsync(string message, Exception exception = null) public Task InfoAsync(string message, Exception exception = null)
=> _manager.InfoAsync(Name, message, exception); => _manager.InfoAsync(Name, message, exception);
#if FORMATSTR
public Task InfoAsync(FormattableString message, Exception exception = null) public Task InfoAsync(FormattableString message, Exception exception = null)
=> _manager.InfoAsync(Name, message, exception); => _manager.InfoAsync(Name, message, exception);
#endif


public Task VerboseAsync(Exception exception) public Task VerboseAsync(Exception exception)
=> _manager.VerboseAsync(Name, exception); => _manager.VerboseAsync(Name, exception);
public Task VerboseAsync(string message, Exception exception = null) public Task VerboseAsync(string message, Exception exception = null)
=> _manager.VerboseAsync(Name, message, exception); => _manager.VerboseAsync(Name, message, exception);
#if FORMATSTR
public Task VerboseAsync(FormattableString message, Exception exception = null) public Task VerboseAsync(FormattableString message, Exception exception = null)
=> _manager.VerboseAsync(Name, message, exception); => _manager.VerboseAsync(Name, message, exception);
#endif


public Task DebugAsync(Exception exception) public Task DebugAsync(Exception exception)
=> _manager.DebugAsync(Name, exception); => _manager.DebugAsync(Name, exception);
public Task DebugAsync(string message, Exception exception = null) public Task DebugAsync(string message, Exception exception = null)
=> _manager.DebugAsync(Name, message, exception); => _manager.DebugAsync(Name, message, exception);
#if FORMATSTR
public Task DebugAsync(FormattableString message, Exception exception = null) public Task DebugAsync(FormattableString message, Exception exception = null)
=> _manager.DebugAsync(Name, message, exception); => _manager.DebugAsync(Name, message, exception);
#endif
} }
} }

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

@@ -25,9 +25,12 @@ namespace Discord
public RetryMode? RetryMode { get; set; } public RetryMode? RetryMode { get; set; }
public bool HeaderOnly { get; internal set; } public bool HeaderOnly { get; internal set; }
/// <summary> /// <summary>
/// Gets or sets the reason for this action in the guild's audit log. Note that this property may not apply
/// to every action.
/// Gets or sets the reason for this action in the guild's audit log.
/// </summary> /// </summary>
/// <remarks>
/// Gets or sets the reason that will be written to the guild's audit log if applicable. This may not apply
/// to all actions.
/// </remarks>
public string AuditLogReason { get; set; } public string AuditLogReason { get; set; }


internal bool IgnoreState { get; set; } internal bool IgnoreState { get; set; }


+ 2
- 45
src/Discord.Net.Core/Utils/DateTimeUtils.cs View File

@@ -1,57 +1,14 @@
using System;
using System;


namespace Discord namespace Discord
{ {
//Source: https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs //Source: https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs
internal static class DateTimeUtils internal static class DateTimeUtils
{ {
#if !UNIXTIME
private const long UnixEpochTicks = 621_355_968_000_000_000;
private const long UnixEpochSeconds = 62_135_596_800;
private const long UnixEpochMilliseconds = 62_135_596_800_000;
#endif

public static DateTimeOffset FromTicks(long ticks) public static DateTimeOffset FromTicks(long ticks)
=> new DateTimeOffset(ticks, TimeSpan.Zero); => new DateTimeOffset(ticks, TimeSpan.Zero);
public static DateTimeOffset? FromTicks(long? ticks) public static DateTimeOffset? FromTicks(long? ticks)
=> ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null; => ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null;

public static DateTimeOffset FromUnixSeconds(long seconds)
{
#if UNIXTIME
return DateTimeOffset.FromUnixTimeSeconds(seconds);
#else
long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks;
return new DateTimeOffset(ticks, TimeSpan.Zero);
#endif
}
public static DateTimeOffset FromUnixMilliseconds(long milliseconds)
{
#if UNIXTIME
return DateTimeOffset.FromUnixTimeMilliseconds(milliseconds);
#else
long ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks;
return new DateTimeOffset(ticks, TimeSpan.Zero);
#endif
}

public static long ToUnixSeconds(DateTimeOffset dto)
{
#if UNIXTIME
return dto.ToUnixTimeSeconds();
#else
long seconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerSecond;
return seconds - UnixEpochSeconds;
#endif
}
public static long ToUnixMilliseconds(DateTimeOffset dto)
{
#if UNIXTIME
return dto.ToUnixTimeMilliseconds();
#else
long milliseconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond;
return milliseconds - UnixEpochMilliseconds;
#endif
}
} }
} }

+ 2
- 2
src/Discord.Net.Core/Utils/SnowflakeUtils.cs View File

@@ -5,8 +5,8 @@ namespace Discord
public static class SnowflakeUtils public static class SnowflakeUtils
{ {
public static DateTimeOffset FromSnowflake(ulong value) public static DateTimeOffset FromSnowflake(ulong value)
=> DateTimeUtils.FromUnixMilliseconds((long)((value >> 22) + 1420070400000UL));
=> DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL));
public static ulong ToSnowflake(DateTimeOffset value) public static ulong ToSnowflake(DateTimeOffset value)
=> ((ulong)DateTimeUtils.ToUnixMilliseconds(value) - 1420070400000UL) << 22;
=> ((ulong)value.ToUnixTimeMilliseconds() - 1420070400000UL) << 22;
} }
} }

+ 7
- 1
src/Discord.Net.Rest/API/Common/Message.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;


@@ -12,10 +12,16 @@ namespace Discord.API
public MessageType Type { get; set; } public MessageType Type { get; set; }
[JsonProperty("channel_id")] [JsonProperty("channel_id")]
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
// ALWAYS sent on WebSocket messages
[JsonProperty("guild_id")]
public Optional<ulong> GuildId { get; set; }
[JsonProperty("webhook_id")] [JsonProperty("webhook_id")]
public Optional<ulong> WebhookId { get; set; } public Optional<ulong> WebhookId { get; set; }
[JsonProperty("author")] [JsonProperty("author")]
public Optional<User> Author { get; set; } public Optional<User> Author { get; set; }
// ALWAYS sent on WebSocket messages
[JsonProperty("member")]
public Optional<GuildMember> Member { get; set; }
[JsonProperty("content")] [JsonProperty("content")]
public Optional<string> Content { get; set; } public Optional<string> Content { get; set; }
[JsonProperty("timestamp")] [JsonProperty("timestamp")]


+ 4
- 1
src/Discord.Net.Rest/API/Common/VoiceState.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;


namespace Discord.API namespace Discord.API
@@ -11,6 +11,9 @@ namespace Discord.API
public ulong? ChannelId { get; set; } public ulong? ChannelId { get; set; }
[JsonProperty("user_id")] [JsonProperty("user_id")]
public ulong UserId { get; set; } public ulong UserId { get; set; }
// ALWAYS sent over WebSocket, never on REST
[JsonProperty("member")]
public Optional<GuildMember> Member { get; set; }
[JsonProperty("session_id")] [JsonProperty("session_id")]
public string SessionId { get; set; } public string SessionId { get; set; }
[JsonProperty("deaf")] [JsonProperty("deaf")]


+ 12
- 1
src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;


namespace Discord.API.Rest namespace Discord.API.Rest
@@ -10,9 +10,20 @@ namespace Discord.API.Rest
public string Name { get; } public string Name { get; }
[JsonProperty("type")] [JsonProperty("type")]
public ChannelType Type { get; } public ChannelType Type { get; }
[JsonProperty("parent_id")]
public Optional<ulong?> CategoryId { get; set; }


//Text channels
[JsonProperty("topic")]
public Optional<string> Topic { get; set; }
[JsonProperty("nsfw")]
public Optional<bool> IsNsfw { get; set; }

//Voice channels
[JsonProperty("bitrate")] [JsonProperty("bitrate")]
public Optional<int> Bitrate { get; set; } public Optional<int> Bitrate { get; set; }
[JsonProperty("user_limit")]
public Optional<int?> UserLimit { get; set; }


public CreateGuildChannelParams(string name, ChannelType type) public CreateGuildChannelParams(string name, ChannelType type)
{ {


+ 1
- 1
src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs View File

@@ -1,4 +1,4 @@
namespace Discord.API.Rest
namespace Discord.API.Rest
{ {
internal class GetReactionUsersParams internal class GetReactionUsersParams
{ {


+ 6
- 7
src/Discord.Net.Rest/Discord.Net.Rest.csproj View File

@@ -1,20 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../Discord.Net.targets" /> <Import Project="../../Discord.Net.targets" />
<PropertyGroup> <PropertyGroup>
<AssemblyName>Discord.Net.Rest</AssemblyName> <AssemblyName>Discord.Net.Rest</AssemblyName>
<RootNamespace>Discord.Rest</RootNamespace> <RootNamespace>Discord.Rest</RootNamespace>
<Description>A core Discord.Net library containing the REST client and models.</Description> <Description>A core Discord.Net library containing the REST client and models.</Description>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net45;netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.1;netstandard1.3</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net46;netstandard1.3;netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" /> <ProjectReference Include="..\Discord.Net.Core\Discord.Net.Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' != 'net45' ">
<PackageReference Include="System.Net.Http" Version="4.3.2" />
<!-- https://github.com/dotnet/corefx/issues/19535 -->
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Net.Http" Version="4.3.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' ">
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
</Project> </Project>

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

@@ -630,11 +630,11 @@ namespace Discord.API
Preconditions.NotNullOrWhitespace(emoji, nameof(emoji)); Preconditions.NotNullOrWhitespace(emoji, nameof(emoji));
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit));
Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit));
Preconditions.AtMost(args.Limit, DiscordConfig.MaxUserReactionsPerBatch, nameof(args.Limit));
Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);


int limit = args.Limit.GetValueOrDefault(int.MaxValue);
int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxUserReactionsPerBatch);
ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); ulong afterUserId = args.AfterUserId.GetValueOrDefault(0);


var ids = new BucketIds(channelId: channelId); var ids = new BucketIds(channelId: channelId);


+ 10
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs View File

@@ -1,10 +1,13 @@
using System.Linq;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to a ban.
/// </summary>
public class BanAuditLogData : IAuditLogData public class BanAuditLogData : IAuditLogData
{ {
private BanAuditLogData(IUser user) private BanAuditLogData(IUser user)
@@ -18,6 +21,12 @@ namespace Discord.Rest
return new BanAuditLogData(RestUser.Create(discord, userInfo)); return new BanAuditLogData(RestUser.Create(discord, userInfo));
} }


/// <summary>
/// Gets the user that was banned.
/// </summary>
/// <returns>
/// A user object representing the banned user.
/// </returns>
public IUser Target { get; } public IUser Target { get; }
} }
} }

+ 27
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs View File

@@ -1,4 +1,3 @@
using Newtonsoft.Json.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;


@@ -7,6 +6,9 @@ using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to a channel creation.
/// </summary>
public class ChannelCreateAuditLogData : IAuditLogData public class ChannelCreateAuditLogData : IAuditLogData
{ {
private ChannelCreateAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection<Overwrite> overwrites) private ChannelCreateAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection<Overwrite> overwrites)
@@ -44,9 +46,33 @@ namespace Discord.Rest
return new ChannelCreateAuditLogData(entry.TargetId.Value, name, type, overwrites.ToReadOnlyCollection()); return new ChannelCreateAuditLogData(entry.TargetId.Value, name, type, overwrites.ToReadOnlyCollection());
} }


/// <summary>
/// Gets the snowflake ID of the created channel.
/// </summary>
/// <returns>
/// An <see cref="ulong"/> representing the snowflake identifier for the created channel.
/// </returns>
public ulong ChannelId { get; } public ulong ChannelId { get; }
/// <summary>
/// Gets the name of the created channel.
/// </summary>
/// <returns>
/// A string containing the name of the created channel.
/// </returns>
public string ChannelName { get; } public string ChannelName { get; }
/// <summary>
/// Gets the type of the created channel.
/// </summary>
/// <returns>
/// The type of channel that was created.
/// </returns>
public ChannelType ChannelType { get; } public ChannelType ChannelType { get; }
/// <summary>
/// Gets a collection of permission overwrites that was assigned to the created channel.
/// </summary>
/// <returns>
/// A collection of permission <see cref="Overwrite" />.
/// </returns>
public IReadOnlyCollection<Overwrite> Overwrites { get; } public IReadOnlyCollection<Overwrite> Overwrites { get; }
} }
} }

+ 27
- 3
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs View File

@@ -1,14 +1,14 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to a channel deletion.
/// </summary>
public class ChannelDeleteAuditLogData : IAuditLogData public class ChannelDeleteAuditLogData : IAuditLogData
{ {
private ChannelDeleteAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection<Overwrite> overwrites) private ChannelDeleteAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection<Overwrite> overwrites)
@@ -37,9 +37,33 @@ namespace Discord.Rest
return new ChannelDeleteAuditLogData(id, name, type, overwrites.ToReadOnlyCollection()); return new ChannelDeleteAuditLogData(id, name, type, overwrites.ToReadOnlyCollection());
} }


/// <summary>
/// Gets the snowflake ID of the deleted channel.
/// </summary>
/// <returns>
/// An <see cref="ulong"/> representing the snowflake identifier for the deleted channel.
/// </returns>
public ulong ChannelId { get; } public ulong ChannelId { get; }
/// <summary>
/// Gets the name of the deleted channel.
/// </summary>
/// <returns>
/// A string containing the name of the deleted channel.
/// </returns>
public string ChannelName { get; } public string ChannelName { get; }
/// <summary>
/// Gets the type of the deleted channel.
/// </summary>
/// <returns>
/// The type of channel that was deleted.
/// </returns>
public ChannelType ChannelType { get; } public ChannelType ChannelType { get; }
/// <summary>
/// Gets a collection of permission overwrites that was assigned to the deleted channel.
/// </summary>
/// <returns>
/// A collection of permission <see cref="Overwrite" />.
/// </returns>
public IReadOnlyCollection<Overwrite> Overwrites { get; } public IReadOnlyCollection<Overwrite> Overwrites { get; }
} }
} }

+ 29
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs View File

@@ -1,5 +1,8 @@
namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents information for a channel.
/// </summary>
public struct ChannelInfo public struct ChannelInfo
{ {
internal ChannelInfo(string name, string topic, int? bitrate, int? limit) internal ChannelInfo(string name, string topic, int? bitrate, int? limit)
@@ -10,9 +13,35 @@ namespace Discord.Rest
UserLimit = limit; UserLimit = limit;
} }


/// <summary>
/// Gets the name of this channel.
/// </summary>
/// <returns>
/// A string containing the name of this channel.
/// </returns>
public string Name { get; } public string Name { get; }
/// <summary>
/// Gets the topic of this channel.
/// </summary>
/// <returns>
/// A string containing the topic of this channel, if any.
/// </returns>
public string Topic { get; } public string Topic { get; }
/// <summary>
/// Gets the bitrate of this channel if applicable.
/// </summary>
/// <returns>
/// An <see cref="System.Int32"/> representing the bitrate set for the voice channel; <c>null</c> if not
/// applicable.
/// </returns>
public int? Bitrate { get; } public int? Bitrate { get; }
/// <summary>
/// Gets the number of users allowed to be in this channel if applicable.
/// </summary>
/// <returns>
/// An <see cref="System.Int32" /> representing the number of users allowed to be in this voice channel;
/// <c>null</c> if not applicable.
/// </returns>
public int? UserLimit { get; } public int? UserLimit { get; }
} }
} }

+ 12
- 3
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs View File

@@ -1,10 +1,13 @@
using System.Linq;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to a channel update.
/// </summary>
public class ChannelUpdateAuditLogData : IAuditLogData public class ChannelUpdateAuditLogData : IAuditLogData
{ {
private ChannelUpdateAuditLogData(ulong id, ChannelInfo before, ChannelInfo after) private ChannelUpdateAuditLogData(ulong id, ChannelInfo before, ChannelInfo after)
@@ -38,8 +41,14 @@ namespace Discord.Rest
return new ChannelUpdateAuditLogData(entry.TargetId.Value, before, after); return new ChannelUpdateAuditLogData(entry.TargetId.Value, before, after);
} }


/// <summary>
/// Gets the snowflake ID of the updated channel.
/// </summary>
/// <returns>
/// An <see cref="ulong"/> representing the snowflake identifier for the updated channel.
/// </returns>
public ulong ChannelId { get; } public ulong ChannelId { get; }
public ChannelInfo Before { get; set; }
public ChannelInfo After { get; set; }
public ChannelInfo Before { get; }
public ChannelInfo After { get; }
} }
} }

+ 15
- 4
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs View File

@@ -1,14 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to an emoji creation.
/// </summary>
public class EmoteCreateAuditLogData : IAuditLogData public class EmoteCreateAuditLogData : IAuditLogData
{ {
private EmoteCreateAuditLogData(ulong id, string name) private EmoteCreateAuditLogData(ulong id, string name)
@@ -25,7 +24,19 @@ namespace Discord.Rest
return new EmoteCreateAuditLogData(entry.TargetId.Value, emoteName); return new EmoteCreateAuditLogData(entry.TargetId.Value, emoteName);
} }


/// <summary>
/// Gets the snowflake ID of the created emoji.
/// </summary>
/// <returns>
/// An <see cref="System.UInt64"/> representing the snowflake identifier for the created emoji.
/// </returns>
public ulong EmoteId { get; } public ulong EmoteId { get; }
/// <summary>
/// Gets the name of the created emoji.
/// </summary>
/// <returns>
/// A string containing the name of the created emoji.
/// </returns>
public string Name { get; } public string Name { get; }
} }
} }

+ 16
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs View File

@@ -1,10 +1,13 @@
using System.Linq;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to an emoji deletion.
/// </summary>
public class EmoteDeleteAuditLogData : IAuditLogData public class EmoteDeleteAuditLogData : IAuditLogData
{ {
private EmoteDeleteAuditLogData(ulong id, string name) private EmoteDeleteAuditLogData(ulong id, string name)
@@ -22,7 +25,19 @@ namespace Discord.Rest
return new EmoteDeleteAuditLogData(entry.TargetId.Value, emoteName); return new EmoteDeleteAuditLogData(entry.TargetId.Value, emoteName);
} }


/// <summary>
/// Gets the snowflake ID of the deleted emoji.
/// </summary>
/// <returns>
/// An <see cref="System.UInt64"/> representing the snowflake identifier for the deleted emoji.
/// </returns>
public ulong EmoteId { get; } public ulong EmoteId { get; }
/// <summary>
/// Gets the name of the deleted emoji.
/// </summary>
/// <returns>
/// A string containing the name of the deleted emoji.
/// </returns>
public string Name { get; } public string Name { get; }
} }
} }

+ 22
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs View File

@@ -1,10 +1,13 @@
using System.Linq;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to an emoji update.
/// </summary>
public class EmoteUpdateAuditLogData : IAuditLogData public class EmoteUpdateAuditLogData : IAuditLogData
{ {
private EmoteUpdateAuditLogData(ulong id, string oldName, string newName) private EmoteUpdateAuditLogData(ulong id, string oldName, string newName)
@@ -24,8 +27,26 @@ namespace Discord.Rest
return new EmoteUpdateAuditLogData(entry.TargetId.Value, oldName, newName); return new EmoteUpdateAuditLogData(entry.TargetId.Value, oldName, newName);
} }


/// <summary>
/// Gets the snowflake ID of the updated emoji.
/// </summary>
/// <returns>
/// An <see cref="System.UInt64"/> representing the snowflake identifier of the updated emoji.
/// </returns>
public ulong EmoteId { get; } public ulong EmoteId { get; }
/// <summary>
/// Gets the new name of the updated emoji.
/// </summary>
/// <returns>
/// A string containing the new name of the updated emoji.
/// </returns>
public string NewName { get; } public string NewName { get; }
/// <summary>
/// Gets the old name of the updated emoji.
/// </summary>
/// <returns>
/// A string containing the old name of the updated emoji.
/// </returns>
public string OldName { get; } public string OldName { get; }
} }
} }

+ 55
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs View File

@@ -1,5 +1,8 @@
namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents information for a guild.
/// </summary>
public struct GuildInfo public struct GuildInfo
{ {
internal GuildInfo(int? afkTimeout, DefaultMessageNotifications? defaultNotifs, internal GuildInfo(int? afkTimeout, DefaultMessageNotifications? defaultNotifs,
@@ -18,14 +21,66 @@ namespace Discord.Rest
ContentFilterLevel = filter; ContentFilterLevel = filter;
} }


/// <summary>
/// Gets the amount of time (in seconds) a user must be inactive in a voice channel for until they are
/// automatically moved to the AFK voice channel.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the amount of time in seconds for a user to be marked as inactive
/// and moved into the AFK voice channel.
/// </returns>
public int? AfkTimeout { get; } public int? AfkTimeout { get; }
/// <summary>
/// Gets the default message notifications for users who haven't explicitly set their notification settings.
/// </summary>
public DefaultMessageNotifications? DefaultMessageNotifications { get; } public DefaultMessageNotifications? DefaultMessageNotifications { get; }
/// <summary>
/// Gets the ID of the AFK voice channel for this guild.
/// </summary>
/// <returns>
/// An <see cref="ulong"/> representing the snowflake identifier of the AFK voice channel; <c>null</c> if
/// none is set.
/// </returns>
public ulong? AfkChannelId { get; } public ulong? AfkChannelId { get; }
/// <summary>
/// Gets the name of this guild.
/// </summary>
/// <returns>
/// A string containing the name of this guild.
/// </returns>
public string Name { get; } public string Name { get; }
/// <summary>
/// Gets the ID of the region hosting this guild's voice channels.
/// </summary>
public string RegionId { get; } public string RegionId { get; }
/// <summary>
/// Gets the ID of this guild's icon.
/// </summary>
/// <returns>
/// A string containing the identifier for the splash image; <c>null</c> if none is set.
/// </returns>
public string IconHash { get; } public string IconHash { get; }
/// <summary>
/// Gets the level of requirements a user must fulfill before being allowed to post messages in this guild.
/// </summary>
/// <returns>
/// The level of requirements.
/// </returns>
public VerificationLevel? VerificationLevel { get; } public VerificationLevel? VerificationLevel { get; }
/// <summary>
/// Gets the owner of this guild.
/// </summary>
/// <returns>
/// A user object representing the owner of this guild.
/// </returns>
public IUser Owner { get; } public IUser Owner { get; }
/// <summary>
/// Gets the level of Multi-Factor Authentication requirements a user must fulfill before being allowed to
/// perform administrative actions in this guild.
/// </summary>
/// <returns>
/// The level of MFA requirement.
/// </returns>
public MfaLevel? MfaLevel { get; } public MfaLevel? MfaLevel { get; }
public int? ContentFilterLevel { get; } public int? ContentFilterLevel { get; }
} }


+ 16
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs View File

@@ -1,10 +1,13 @@
using System.Linq;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to a guild update.
/// </summary>
public class GuildUpdateAuditLogData : IAuditLogData public class GuildUpdateAuditLogData : IAuditLogData
{ {
private GuildUpdateAuditLogData(GuildInfo before, GuildInfo after) private GuildUpdateAuditLogData(GuildInfo before, GuildInfo after)
@@ -73,7 +76,19 @@ namespace Discord.Rest
return new GuildUpdateAuditLogData(before, after); return new GuildUpdateAuditLogData(before, after);
} }


/// <summary>
/// Gets the guild information before the changes.
/// </summary>
/// <returns>
/// An information object containing the original guild information before the changes were made.
/// </returns>
public GuildInfo Before { get; } public GuildInfo Before { get; }
/// <summary>
/// Gets the guild information after the changes.
/// </summary>
/// <returns>
/// An information object containing the guild information after the changes were made.
/// </returns>
public GuildInfo After { get; } public GuildInfo After { get; }
} }
} }

+ 49
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs View File

@@ -1,10 +1,13 @@
using System.Linq;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to an invite creation.
/// </summary>
public class InviteCreateAuditLogData : IAuditLogData public class InviteCreateAuditLogData : IAuditLogData
{ {
private InviteCreateAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses) private InviteCreateAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses)
@@ -44,12 +47,57 @@ namespace Discord.Rest
return new InviteCreateAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses); return new InviteCreateAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses);
} }


/// <summary>
/// Gets the time (in seconds) until the invite expires.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds until this invite expires.
/// </returns>
public int MaxAge { get; } public int MaxAge { get; }
/// <summary>
/// Gets the unique identifier for this invite.
/// </summary>
/// <returns>
/// A string containing the invite code (e.g. <c>FTqNnyS</c>).
/// </returns>
public string Code { get; } public string Code { get; }
/// <summary>
/// Determines whether the invite is a temporary one (i.e. whether the invite will be removed from the guild
/// when the user logs off).
/// </summary>
/// <returns>
/// <c>true</c> if users accepting this invite will be removed from the guild when they log off; otherwise
/// <c>false</c>.
/// </returns>
public bool Temporary { get; } public bool Temporary { get; }
/// <summary>
/// Gets the user that created this invite.
/// </summary>
/// <returns>
/// A user that created this invite.
/// </returns>
public IUser Creator { get; } public IUser Creator { get; }
/// <summary>
/// Gets the ID of the channel this invite is linked to.
/// </summary>
/// <returns>
/// An <see cref="ulong"/> representing the channel snowflake identifier that the invite points to.
/// </returns>
public ulong ChannelId { get; } public ulong ChannelId { get; }
/// <summary>
/// Gets the number of times this invite has been used.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of times this invite has been used.
/// </returns>
public int Uses { get; } public int Uses { get; }
/// <summary>
/// Gets the max number of uses this invite may have.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of uses this invite may be accepted until it is removed
/// from the guild; <c>null</c> if none is set.
/// </returns>
public int MaxUses { get; } public int MaxUses { get; }
} }
} }

+ 49
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs View File

@@ -1,10 +1,13 @@
using System.Linq;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a piece of audit log data related to an invite removal.
/// </summary>
public class InviteDeleteAuditLogData : IAuditLogData public class InviteDeleteAuditLogData : IAuditLogData
{ {
private InviteDeleteAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses) private InviteDeleteAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses)
@@ -44,12 +47,57 @@ namespace Discord.Rest
return new InviteDeleteAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses); return new InviteDeleteAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses);
} }


/// <summary>
/// Gets the time (in seconds) until the invite expires.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds until this invite expires.
/// </returns>
public int MaxAge { get; } public int MaxAge { get; }
/// <summary>
/// Gets the unique identifier for this invite.
/// </summary>
/// <returns>
/// A string containing the invite code (e.g. <c>FTqNnyS</c>).
/// </returns>
public string Code { get; } public string Code { get; }
/// <summary>
/// Determines whether the invite is a temporary one (i.e. whether the invite will be removed from the guild
/// when the user logs off).
/// </summary>
/// <returns>
/// <c>true</c> if users accepting this invite will be removed from the guild when they log off; otherwise
/// <c>false</c>.
/// </returns>
public bool Temporary { get; } public bool Temporary { get; }
/// <summary>
/// Gets the user that created this invite.
/// </summary>
/// <returns>
/// A user that created this invite.
/// </returns>
public IUser Creator { get; } public IUser Creator { get; }
/// <summary>
/// Gets the ID of the channel this invite is linked to.
/// </summary>
/// <returns>
/// An <see cref="ulong"/> representing the channel snowflake identifier that the invite points to.
/// </returns>
public ulong ChannelId { get; } public ulong ChannelId { get; }
/// <summary>
/// Gets the number of times this invite has been used.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of times this invite has been used.
/// </returns>
public int Uses { get; } public int Uses { get; }
/// <summary>
/// Gets the max number of uses this invite may have.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of uses this invite may be accepted until it is removed
/// from the guild; <c>null</c> if none is set.
/// </returns>
public int MaxUses { get; } public int MaxUses { get; }
} }
} }

+ 38
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs View File

@@ -1,5 +1,8 @@
namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents information for an invite.
/// </summary>
public struct InviteInfo public struct InviteInfo
{ {
internal InviteInfo(int? maxAge, string code, bool? temporary, ulong? channelId, int? maxUses) internal InviteInfo(int? maxAge, string code, bool? temporary, ulong? channelId, int? maxUses)
@@ -11,10 +14,45 @@ namespace Discord.Rest
MaxUses = maxUses; MaxUses = maxUses;
} }


/// <summary>
/// Gets the time (in seconds) until the invite expires.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the time in seconds until this invite expires; <c>null</c> if this
/// invite never expires or not specified.
/// </returns>
public int? MaxAge { get; } public int? MaxAge { get; }
/// <summary>
/// Gets the unique identifier for this invite.
/// </summary>
/// <returns>
/// A string containing the invite code (e.g. <c>FTqNnyS</c>).
/// </returns>
public string Code { get; } public string Code { get; }
/// <summary>
/// Determines whether the invite is a temporary one (i.e. whether the invite will be removed from the guild
/// when the user logs off).
/// </summary>
/// <returns>
/// <c>true</c> if users accepting this invite will be removed from the guild when they log off,
/// <c>false</c> if not; <c>null</c> if not specified.
/// </returns>
public bool? Temporary { get; } public bool? Temporary { get; }
/// <summary>
/// Gets the ID of the channel this invite is linked to.
/// </summary>
/// <returns>
/// An <see cref="ulong"/> representing the channel snowflake identifier that the invite points to;
/// <c>null</c> if not specified.
/// </returns>
public ulong? ChannelId { get; } public ulong? ChannelId { get; }
/// <summary>
/// Gets the max number of uses this invite may have.
/// </summary>
/// <returns>
/// An <see cref="int"/> representing the number of uses this invite may be accepted until it is removed
/// from the guild; <c>null</c> if none is specified.
/// </returns>
public int? MaxUses { get; } public int? MaxUses { get; }
} }
} }

+ 3
- 18
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs View File

@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;


@@ -9,7 +8,7 @@ namespace Discord.Rest
{ {
public class MemberRoleAuditLogData : IAuditLogData public class MemberRoleAuditLogData : IAuditLogData
{ {
private MemberRoleAuditLogData(IReadOnlyCollection<RoleInfo> roles, IUser target)
private MemberRoleAuditLogData(IReadOnlyCollection<MemberRoleEditInfo> roles, IUser target)
{ {
Roles = roles; Roles = roles;
Target = target; Target = target;
@@ -21,7 +20,7 @@ namespace Discord.Rest


var roleInfos = changes.SelectMany(x => x.NewValue.ToObject<API.Role[]>(), var roleInfos = changes.SelectMany(x => x.NewValue.ToObject<API.Role[]>(),
(model, role) => new { model.ChangedProperty, Role = role }) (model, role) => new { model.ChangedProperty, Role = role })
.Select(x => new RoleInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add"))
.Select(x => new MemberRoleEditInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add"))
.ToList(); .ToList();


var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
@@ -30,21 +29,7 @@ namespace Discord.Rest
return new MemberRoleAuditLogData(roleInfos.ToReadOnlyCollection(), user); return new MemberRoleAuditLogData(roleInfos.ToReadOnlyCollection(), user);
} }


public IReadOnlyCollection<RoleInfo> Roles { get; }
public IReadOnlyCollection<MemberRoleEditInfo> Roles { get; }
public IUser Target { get; } public IUser Target { get; }

public struct RoleInfo
{
internal RoleInfo(string name, ulong roleId, bool added)
{
Name = name;
RoleId = roleId;
Added = added;
}

public string Name { get; }
public ulong RoleId { get; }
public bool Added { get; }
}
} }
} }

+ 16
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleEditInfo.cs View File

@@ -0,0 +1,16 @@
namespace Discord.Rest
{
public struct MemberRoleEditInfo
{
internal MemberRoleEditInfo(string name, ulong roleId, bool added)
{
Name = name;
RoleId = roleId;
Added = added;
}

public string Name { get; }
public ulong RoleId { get; }
public bool Added { get; }
}
}

+ 1
- 2
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs View File

@@ -1,8 +1,7 @@
using System.Linq;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;
using ChangeModel = Discord.API.AuditLogChange;


namespace Discord.Rest namespace Discord.Rest
{ {


+ 0
- 6
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs View File

@@ -1,13 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;
using ChangeModel = Discord.API.AuditLogChange;
using OptionModel = Discord.API.AuditLogOptions;


namespace Discord.Rest namespace Discord.Rest
{ {


+ 3
- 3
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs View File

@@ -7,7 +7,7 @@ namespace Discord.Rest
{ {
public class RoleCreateAuditLogData : IAuditLogData public class RoleCreateAuditLogData : IAuditLogData
{ {
private RoleCreateAuditLogData(ulong id, RoleInfo props)
private RoleCreateAuditLogData(ulong id, RoleEditInfo props)
{ {
RoleId = id; RoleId = id;
Properties = props; Properties = props;
@@ -38,10 +38,10 @@ namespace Discord.Rest
permissions = new GuildPermissions(permissionsRaw.Value); permissions = new GuildPermissions(permissionsRaw.Value);


return new RoleCreateAuditLogData(entry.TargetId.Value, return new RoleCreateAuditLogData(entry.TargetId.Value,
new RoleInfo(color, mentionable, hoist, name, permissions));
new RoleEditInfo(color, mentionable, hoist, name, permissions));
} }


public ulong RoleId { get; } public ulong RoleId { get; }
public RoleInfo Properties { get; }
public RoleEditInfo Properties { get; }
} }
} }

+ 3
- 3
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs View File

@@ -7,7 +7,7 @@ namespace Discord.Rest
{ {
public class RoleDeleteAuditLogData : IAuditLogData public class RoleDeleteAuditLogData : IAuditLogData
{ {
private RoleDeleteAuditLogData(ulong id, RoleInfo props)
private RoleDeleteAuditLogData(ulong id, RoleEditInfo props)
{ {
RoleId = id; RoleId = id;
Properties = props; Properties = props;
@@ -38,10 +38,10 @@ namespace Discord.Rest
permissions = new GuildPermissions(permissionsRaw.Value); permissions = new GuildPermissions(permissionsRaw.Value);


return new RoleDeleteAuditLogData(entry.TargetId.Value, return new RoleDeleteAuditLogData(entry.TargetId.Value,
new RoleInfo(color, mentionable, hoist, name, permissions));
new RoleEditInfo(color, mentionable, hoist, name, permissions));
} }


public ulong RoleId { get; } public ulong RoleId { get; }
public RoleInfo Properties { get; }
public RoleEditInfo Properties { get; }
} }
} }

+ 21
- 0
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleEditInfo.cs View File

@@ -0,0 +1,21 @@
namespace Discord.Rest
{
public struct RoleEditInfo
{
internal RoleEditInfo(Color? color, bool? mentionable, bool? hoist, string name,
GuildPermissions? permissions)
{
Color = color;
Mentionable = mentionable;
Hoist = hoist;
Name = name;
Permissions = permissions;
}

public Color? Color { get; }
public bool? Mentionable { get; }
public bool? Hoist { get; }
public string Name { get; }
public GuildPermissions? Permissions { get; }
}
}

+ 0
- 21
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs View File

@@ -1,21 +0,0 @@
namespace Discord.Rest
{
public struct RoleInfo
{
internal RoleInfo(Color? color, bool? mentionable, bool? hoist, string name,
GuildPermissions? permissions)
{
Color = color;
Mentionable = mentionable;
Hoist = hoist;
Name = name;
Permissions = permissions;
}

public Color? Color { get; }
public bool? Mentionable { get; }
public bool? Hoist { get; }
public string Name { get; }
public GuildPermissions? Permissions { get; }
}
}

+ 5
- 5
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs View File

@@ -7,7 +7,7 @@ namespace Discord.Rest
{ {
public class RoleUpdateAuditLogData : IAuditLogData public class RoleUpdateAuditLogData : IAuditLogData
{ {
private RoleUpdateAuditLogData(ulong id, RoleInfo oldProps, RoleInfo newProps)
private RoleUpdateAuditLogData(ulong id, RoleEditInfo oldProps, RoleEditInfo newProps)
{ {
RoleId = id; RoleId = id;
Before = oldProps; Before = oldProps;
@@ -49,14 +49,14 @@ namespace Discord.Rest
if (newPermissionsRaw.HasValue) if (newPermissionsRaw.HasValue)
newPermissions = new GuildPermissions(newPermissionsRaw.Value); newPermissions = new GuildPermissions(newPermissionsRaw.Value);


var oldProps = new RoleInfo(oldColor, oldMentionable, oldHoist, oldName, oldPermissions);
var newProps = new RoleInfo(newColor, newMentionable, newHoist, newName, newPermissions);
var oldProps = new RoleEditInfo(oldColor, oldMentionable, oldHoist, oldName, oldPermissions);
var newProps = new RoleEditInfo(newColor, newMentionable, newHoist, newName, newPermissions);


return new RoleUpdateAuditLogData(entry.TargetId.Value, oldProps, newProps); return new RoleUpdateAuditLogData(entry.TargetId.Value, oldProps, newProps);
} }


public ulong RoleId { get; } public ulong RoleId { get; }
public RoleInfo Before { get; }
public RoleInfo After { get; }
public RoleEditInfo Before { get; }
public RoleEditInfo After { get; }
} }
} }

+ 0
- 4
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs View File

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


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


+ 0
- 4
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs View File

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


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


+ 4
- 1
src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs View File

@@ -1,10 +1,13 @@
using System.Linq;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a REST-based audit log entry.
/// </summary>
public class RestAuditLogEntry : RestEntity<ulong>, IAuditLogEntry public class RestAuditLogEntry : RestEntity<ulong>, IAuditLogEntry
{ {
private RestAuditLogEntry(BaseDiscordClient discord, Model fullLog, EntryModel model, IUser user) private RestAuditLogEntry(BaseDiscordClient discord, Model fullLog, EntryModel model, IUser user)


+ 15
- 7
src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs View File

@@ -162,7 +162,6 @@ namespace Discord.Rest
return RestUserMessage.Create(client, channel, client.CurrentUser, model); return RestUserMessage.Create(client, channel, client.CurrentUser, model);
} }


#if FILESYSTEM
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more /// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more
/// invalid characters as defined by <see cref="System.IO.Path.InvalidPathChars" />. /// invalid characters as defined by <see cref="System.IO.Path.InvalidPathChars" />.
@@ -194,7 +193,7 @@ namespace Discord.Rest
using (var file = File.OpenRead(filePath)) using (var file = File.OpenRead(filePath))
return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options).ConfigureAwait(false); return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options).ConfigureAwait(false);
} }
#endif
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client, public static async Task<RestUserMessage> SendFileAsync(IMessageChannel channel, BaseDiscordClient client,
Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
@@ -204,6 +203,10 @@ namespace Discord.Rest
return RestUserMessage.Create(client, channel, client.CurrentUser, model); return RestUserMessage.Create(client, channel, client.CurrentUser, model);
} }


public static Task DeleteMessageAsync(IMessageChannel channel, ulong messageId, BaseDiscordClient client,
RequestOptions options)
=> MessageHelper.DeleteAsync(channel.Id, messageId, client, options);

public static async Task DeleteMessagesAsync(ITextChannel channel, BaseDiscordClient client, public static async Task DeleteMessagesAsync(ITextChannel channel, BaseDiscordClient client,
IEnumerable<ulong> messageIds, RequestOptions options) IEnumerable<ulong> messageIds, RequestOptions options)
{ {
@@ -334,6 +337,16 @@ namespace Discord.Rest
return models.Select(x => RestWebhook.Create(client, channel, x)) return models.Select(x => RestWebhook.Create(client, channel, x))
.ToImmutableArray(); .ToImmutableArray();
} }
// Categories
public static async Task<ICategoryChannel> GetCategoryAsync(INestedChannel channel, BaseDiscordClient client, RequestOptions options)
{
// if no category id specified, return null
if (!channel.CategoryId.HasValue)
return null;
// CategoryId will contain a value here
var model = await client.ApiClient.GetChannelAsync(channel.CategoryId.Value, options).ConfigureAwait(false);
return RestCategoryChannel.Create(client, model) as ICategoryChannel;
}


//Helpers //Helpers
private static IUser GetAuthor(BaseDiscordClient client, IGuild guild, UserModel model, ulong? webhookId) private static IUser GetAuthor(BaseDiscordClient client, IGuild guild, UserModel model, ulong? webhookId)
@@ -345,10 +358,5 @@ namespace Discord.Rest
author = RestUser.Create(client, guild, model, webhookId); author = RestUser.Create(client, guild, model, webhookId);
return author; return author;
} }

public static bool IsNsfw(IChannel channel)
=> IsNsfw(channel.Name);
public static bool IsNsfw(string channelName) =>
channelName == "nsfw" || channelName.StartsWith("nsfw-");
} }
} }

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

@@ -19,8 +19,7 @@ namespace Discord.Rest
/// <returns> /// <returns>
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
new Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#if FILESYSTEM
new Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
/// <summary> /// <summary>
/// Sends a file to this message channel, with an optional caption. /// Sends a file to this message channel, with an optional caption.
/// </summary> /// </summary>
@@ -38,7 +37,6 @@ namespace Discord.Rest
/// An awaitable Task containing the message sent to the channel. /// An awaitable Task containing the message sent to the channel.
/// </returns> /// </returns>
new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); new Task<RestUserMessage> SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null);
#endif
/// <summary> /// <summary>
/// Sends a file to this message channel, with an optional caption. /// Sends a file to this message channel, with an optional caption.
/// </summary> /// </summary>


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

@@ -28,14 +28,6 @@ namespace Discord.Rest
// IGuildChannel // IGuildChannel
/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="NotSupportedException">This method is not supported with category channels.</exception> /// <exception cref="NotSupportedException">This method is not supported with category channels.</exception>
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
=> throw new NotSupportedException();
/// <inheritdoc />
/// <exception cref="NotSupportedException">This method is not supported with category channels.</exception>
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
=> throw new NotSupportedException();
/// <inheritdoc />
/// <exception cref="NotSupportedException">This method is not supported with category channels.</exception>
Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options)
=> throw new NotSupportedException(); => throw new NotSupportedException();
/// <inheritdoc /> /// <inheritdoc />


+ 5
- 0
src/Discord.Net.Rest/Entities/Channels/RestChannel.cs View File

@@ -6,6 +6,9 @@ using Model = Discord.API.Channel;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a generic REST-based channel.
/// </summary>
public class RestChannel : RestEntity<ulong>, IChannel, IUpdateable public class RestChannel : RestEntity<ulong>, IChannel, IUpdateable
{ {
/// <inheritdoc /> /// <inheritdoc />
@@ -26,6 +29,8 @@ namespace Discord.Rest
case ChannelType.DM: case ChannelType.DM:
case ChannelType.Group: case ChannelType.Group:
return CreatePrivate(discord, model) as RestChannel; return CreatePrivate(discord, model) as RestChannel;
case ChannelType.Category:
return RestCategoryChannel.Create(discord, new RestGuild(discord, model.GuildId.Value), model);
default: default:
return new RestChannel(discord, model.Id); return new RestChannel(discord, model.Id);
} }


+ 37
- 8
src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs View File

@@ -10,7 +10,7 @@ using Model = Discord.API.Channel;
namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary> /// <summary>
/// Represents a REST-based DM channel.
/// Represents a REST-based direct-message channel.
/// </summary> /// </summary>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestDMChannel : RestChannel, IDMChannel, IRestPrivateChannel, IRestMessageChannel public class RestDMChannel : RestChannel, IDMChannel, IRestPrivateChannel, IRestMessageChannel
@@ -74,17 +74,49 @@ namespace Discord.Rest
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);


/// <inheritdoc /> /// <inheritdoc />
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentException">
/// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more
/// invalid characters as defined by <see cref="System.IO.Path.InvalidPathChars" />.
/// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="filePath" /> is <c>null</c>.
/// </exception>
/// <exception cref="PathTooLongException">
/// The specified path, file name, or both exceed the system-defined maximum length. For example, on
/// Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260
/// characters.
/// </exception>
/// <exception cref="DirectoryNotFoundException">
/// The specified path is invalid, (for example, it is on an unmapped drive).
/// </exception>
/// <exception cref="UnauthorizedAccessException">
/// <paramref name="filePath" /> specified a directory.-or- The caller does not have the required permission.
/// </exception>
/// <exception cref="FileNotFoundException">
/// The file specified in <paramref name="filePath" /> was not found.
/// </exception>
/// <exception cref="NotSupportedException"><paramref name="filePath" /> is in an invalid format.</exception>
/// <exception cref="IOException">An I/O error occurred while opening the file.</exception>
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options);


/// <inheritdoc />
public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
/// <inheritdoc />
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc /> /// <inheritdoc />
public Task TriggerTypingAsync(RequestOptions options = null) public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options); => ChannelHelper.TriggerTypingAsync(this, Discord, options);
@@ -143,12 +175,9 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);


#if FILESYSTEM
/// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
/// <inheritdoc />

async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />


+ 10
- 5
src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs View File

@@ -83,11 +83,18 @@ namespace Discord.Rest
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);


/// <inheritdoc />
public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
/// <inheritdoc />
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more /// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more
@@ -115,7 +122,6 @@ namespace Discord.Rest
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
/// <inheritdoc /> /// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
@@ -168,10 +174,9 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);


#if FILESYSTEM
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options)


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

@@ -8,7 +8,7 @@ using Model = Discord.API.Channel;
namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary> /// <summary>
/// Represents a private REST group channel.
/// Represents a private REST-based group channel.
/// </summary> /// </summary>
public class RestGuildChannel : RestChannel, IGuildChannel public class RestGuildChannel : RestChannel, IGuildChannel
{ {
@@ -23,8 +23,6 @@ namespace Discord.Rest
/// <inheritdoc /> /// <inheritdoc />
public int Position { get; private set; } public int Position { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public ulong? CategoryId { get; private set; }
/// <inheritdoc />
public ulong GuildId => Guild.Id; public ulong GuildId => Guild.Id;


internal RestGuildChannel(BaseDiscordClient discord, IGuild guild, ulong id) internal RestGuildChannel(BaseDiscordClient discord, IGuild guild, ulong id)
@@ -43,7 +41,6 @@ namespace Discord.Rest
case ChannelType.Category: case ChannelType.Category:
return RestCategoryChannel.Create(discord, guild, model); return RestCategoryChannel.Create(discord, guild, model);
default: default:
// TODO: Channel categories
return new RestGuildChannel(discord, guild, model.Id); return new RestGuildChannel(discord, guild, model.Id);
} }
} }
@@ -75,14 +72,6 @@ namespace Discord.Rest
public Task DeleteAsync(RequestOptions options = null) public Task DeleteAsync(RequestOptions options = null)
=> ChannelHelper.DeleteAsync(this, Discord, options); => ChannelHelper.DeleteAsync(this, Discord, options);


/// <inheritdoc />
public async Task<ICategoryChannel> GetCategoryAsync()
{
if (CategoryId.HasValue)
return (await Guild.GetChannelAsync(CategoryId.Value).ConfigureAwait(false)) as ICategoryChannel;
return null;
}

public OverwritePermissions? GetPermissionOverwrite(IUser user) public OverwritePermissions? GetPermissionOverwrite(IUser user)
{ {
for (int i = 0; i < _overwrites.Length; i++) for (int i = 0; i < _overwrites.Length; i++)


+ 87
- 8
src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs View File

@@ -8,15 +8,22 @@ using Model = Discord.API.Channel;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a REST-based channel in a guild that can send and receive messages.
/// </summary>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestTextChannel : RestGuildChannel, IRestMessageChannel, ITextChannel public class RestTextChannel : RestGuildChannel, IRestMessageChannel, ITextChannel
{ {
/// <inheritdoc />
public string Topic { get; private set; } public string Topic { get; private set; }
public ulong? CategoryId { get; private set; }


/// <inheritdoc />
public string Mention => MentionUtils.MentionChannel(Id); public string Mention => MentionUtils.MentionChannel(Id);


private bool _nsfw; private bool _nsfw;
public bool IsNsfw => _nsfw || ChannelHelper.IsNsfw(this);
/// <inheritdoc />
public bool IsNsfw => _nsfw;


internal RestTextChannel(BaseDiscordClient discord, IGuild guild, ulong id) internal RestTextChannel(BaseDiscordClient discord, IGuild guild, ulong id)
: base(discord, guild, id) : base(discord, guild, id)
@@ -31,11 +38,12 @@ namespace Discord.Rest
internal override void Update(Model model) internal override void Update(Model model)
{ {
base.Update(model); base.Update(model);
CategoryId = model.CategoryId;
Topic = model.Topic.Value; Topic = model.Topic.Value;
_nsfw = model.Nsfw.GetValueOrDefault(); _nsfw = model.Nsfw.GetValueOrDefault();
} }


/// <inheritdoc />
public async Task ModifyAsync(Action<TextChannelProperties> func, RequestOptions options = null) public async Task ModifyAsync(Action<TextChannelProperties> func, RequestOptions options = null)
{ {
var model = await ChannelHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); var model = await ChannelHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false);
@@ -47,36 +55,80 @@ namespace Discord.Rest
public IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(RequestOptions options = null) public IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(RequestOptions options = null)
=> ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options); => ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options);


/// <inheritdoc />
public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null) public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null)
=> ChannelHelper.GetMessageAsync(this, Discord, id, options); => ChannelHelper.GetMessageAsync(this, Discord, id, options);
/// <inheritdoc />
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, options); => ChannelHelper.GetMessagesAsync(this, Discord, null, Direction.Before, limit, options);
/// <inheritdoc />
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(ulong fromMessageId, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, options); => ChannelHelper.GetMessagesAsync(this, Discord, fromMessageId, dir, limit, options);
/// <inheritdoc />
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessage fromMessage, Direction dir, int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
=> ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, options); => ChannelHelper.GetMessagesAsync(this, Discord, fromMessage.Id, dir, limit, options);
/// <inheritdoc />
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);


public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM

/// <inheritdoc />
/// <exception cref="ArgumentException">
/// <paramref name="filePath" /> is a zero-length string, contains only white space, or contains one or more
/// invalid characters as defined by <see cref="System.IO.Path.InvalidPathChars" />.
/// </exception>
/// <exception cref="ArgumentNullException">
/// <paramref name="filePath" /> is <c>null</c>.
/// </exception>
/// <exception cref="PathTooLongException">
/// The specified path, file name, or both exceed the system-defined maximum length. For example, on
/// Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260
/// characters.
/// </exception>
/// <exception cref="DirectoryNotFoundException">
/// The specified path is invalid, (for example, it is on an unmapped drive).
/// </exception>
/// <exception cref="UnauthorizedAccessException">
/// <paramref name="filePath" /> specified a directory.-or- The caller does not have the required permission.
/// </exception>
/// <exception cref="FileNotFoundException">
/// The file specified in <paramref name="filePath" /> was not found.
/// </exception>
/// <exception cref="NotSupportedException"><paramref name="filePath" /> is in an invalid format.</exception>
/// <exception cref="IOException">An I/O error occurred while opening the file.</exception>
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif

/// <inheritdoc />
/// <exception cref="ArgumentOutOfRangeException">Message content is too long, length must be less or equal to <see cref="DiscordConfig.MaxMessageSize"/>.</exception>
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options);


/// <inheritdoc />
public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
/// <inheritdoc />
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

/// <inheritdoc />
public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null) public Task DeleteMessagesAsync(IEnumerable<IMessage> messages, RequestOptions options = null)
=> ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options); => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options);
/// <inheritdoc />
public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null) public Task DeleteMessagesAsync(IEnumerable<ulong> messageIds, RequestOptions options = null)
=> ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options); => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options);


/// <inheritdoc />
public Task TriggerTypingAsync(RequestOptions options = null) public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options); => ChannelHelper.TriggerTypingAsync(this, Discord, options);
public IDisposable EnterTypingState(RequestOptions options = null) public IDisposable EnterTypingState(RequestOptions options = null)
=> ChannelHelper.EnterTypingState(this, Discord, options); => ChannelHelper.EnterTypingState(this, Discord, options);

public Task<RestWebhook> CreateWebhookAsync(string name, Stream avatar = null, RequestOptions options = null) public Task<RestWebhook> CreateWebhookAsync(string name, Stream avatar = null, RequestOptions options = null)
=> ChannelHelper.CreateWebhookAsync(this, Discord, name, avatar, options); => ChannelHelper.CreateWebhookAsync(this, Discord, name, avatar, options);
public Task<RestWebhook> GetWebhookAsync(ulong id, RequestOptions options = null) public Task<RestWebhook> GetWebhookAsync(ulong id, RequestOptions options = null)
@@ -84,17 +136,24 @@ namespace Discord.Rest
public Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null) public Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null)
=> ChannelHelper.GetWebhooksAsync(this, Discord, options); => ChannelHelper.GetWebhooksAsync(this, Discord, options);


public Task<ICategoryChannel> GetCategoryAsync(RequestOptions options = null)
=> ChannelHelper.GetCategoryAsync(this, Discord, options);

private string DebuggerDisplay => $"{Name} ({Id}, Text)"; private string DebuggerDisplay => $"{Name} ({Id}, Text)";


//ITextChannel //ITextChannel
/// <inheritdoc />
async Task<IWebhook> ITextChannel.CreateWebhookAsync(string name, Stream avatar, RequestOptions options) async Task<IWebhook> ITextChannel.CreateWebhookAsync(string name, Stream avatar, RequestOptions options)
=> await CreateWebhookAsync(name, avatar, options).ConfigureAwait(false); => await CreateWebhookAsync(name, avatar, options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IWebhook> ITextChannel.GetWebhookAsync(ulong id, RequestOptions options) async Task<IWebhook> ITextChannel.GetWebhookAsync(ulong id, RequestOptions options)
=> await GetWebhookAsync(id, options).ConfigureAwait(false); => await GetWebhookAsync(id, options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IReadOnlyCollection<IWebhook>> ITextChannel.GetWebhooksAsync(RequestOptions options) async Task<IReadOnlyCollection<IWebhook>> ITextChannel.GetWebhooksAsync(RequestOptions options)
=> await GetWebhooksAsync(options).ConfigureAwait(false); => await GetWebhooksAsync(options).ConfigureAwait(false);


//IMessageChannel //IMessageChannel
/// <inheritdoc />
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options)
{ {
if (mode == CacheMode.AllowDownload) if (mode == CacheMode.AllowDownload)
@@ -102,6 +161,7 @@ namespace Discord.Rest
else else
return null; return null;
} }
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options) IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(int limit, CacheMode mode, RequestOptions options)
{ {
if (mode == CacheMode.AllowDownload) if (mode == CacheMode.AllowDownload)
@@ -109,6 +169,8 @@ namespace Discord.Rest
else else
return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>();
} }

/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options)
{ {
if (mode == CacheMode.AllowDownload) if (mode == CacheMode.AllowDownload)
@@ -116,6 +178,7 @@ namespace Discord.Rest
else else
return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>();
} }
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options) IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(IMessage fromMessage, Direction dir, int limit, CacheMode mode, RequestOptions options)
{ {
if (mode == CacheMode.AllowDownload) if (mode == CacheMode.AllowDownload)
@@ -123,21 +186,26 @@ namespace Discord.Rest
else else
return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>(); return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>();
} }
/// <inheritdoc />
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);


#if FILESYSTEM
/// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif

/// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false);
/// <inheritdoc />
IDisposable IMessageChannel.EnterTypingState(RequestOptions options) IDisposable IMessageChannel.EnterTypingState(RequestOptions options)
=> EnterTypingState(options); => EnterTypingState(options);


//IGuildChannel //IGuildChannel
/// <inheritdoc />
async Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) async Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
{ {
if (mode == CacheMode.AllowDownload) if (mode == CacheMode.AllowDownload)
@@ -145,6 +213,7 @@ namespace Discord.Rest
else else
return null; return null;
} }
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
{ {
if (mode == CacheMode.AllowDownload) if (mode == CacheMode.AllowDownload)
@@ -154,6 +223,7 @@ namespace Discord.Rest
} }


//IChannel //IChannel
/// <inheritdoc />
async Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) async Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
{ {
if (mode == CacheMode.AllowDownload) if (mode == CacheMode.AllowDownload)
@@ -161,6 +231,7 @@ namespace Discord.Rest
else else
return null; return null;
} }
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
{ {
if (mode == CacheMode.AllowDownload) if (mode == CacheMode.AllowDownload)
@@ -168,5 +239,13 @@ namespace Discord.Rest
else else
return AsyncEnumerable.Empty<IReadOnlyCollection<IGuildUser>>(); return AsyncEnumerable.Empty<IReadOnlyCollection<IGuildUser>>();
} }

// INestedChannel
async Task<ICategoryChannel> INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options)
{
if (CategoryId.HasValue && mode == CacheMode.AllowDownload)
return (await Guild.GetChannelAsync(CategoryId.Value, mode, options).ConfigureAwait(false)) as ICategoryChannel;
return null;
}
} }
} }

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

@@ -8,11 +8,17 @@ using Model = Discord.API.Channel;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a REST-based voice channel in a guild.
/// </summary>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestVoiceChannel : RestGuildChannel, IVoiceChannel, IRestAudioChannel public class RestVoiceChannel : RestGuildChannel, IVoiceChannel, IRestAudioChannel
{ {
/// <inheritdoc />
public int Bitrate { get; private set; } public int Bitrate { get; private set; }
/// <inheritdoc />
public int? UserLimit { get; private set; } public int? UserLimit { get; private set; }
public ulong? CategoryId { get; private set; }


internal RestVoiceChannel(BaseDiscordClient discord, IGuild guild, ulong id) internal RestVoiceChannel(BaseDiscordClient discord, IGuild guild, ulong id)
: base(discord, guild, id) : base(discord, guild, id)
@@ -24,20 +30,25 @@ namespace Discord.Rest
entity.Update(model); entity.Update(model);
return entity; return entity;
} }
/// <inheritdoc />
internal override void Update(Model model) internal override void Update(Model model)
{ {
base.Update(model); base.Update(model);
CategoryId = model.CategoryId;
Bitrate = model.Bitrate.Value; Bitrate = model.Bitrate.Value;
UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null; UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null;
} }


/// <inheritdoc />
public async Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOptions options = null) public async Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOptions options = null)
{ {
var model = await ChannelHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); var model = await ChannelHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false);
Update(model); Update(model);
} }


public Task<ICategoryChannel> GetCategoryAsync(RequestOptions options = null)
=> ChannelHelper.GetCategoryAsync(this, Discord, options);

private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; private string DebuggerDisplay => $"{Name} ({Id}, Voice)";


//IAudioChannel //IAudioChannel
@@ -46,9 +57,19 @@ namespace Discord.Rest
Task<IAudioClient> IAudioChannel.ConnectAsync(Action<IAudioClient> configAction) => throw new NotSupportedException(); Task<IAudioClient> IAudioChannel.ConnectAsync(Action<IAudioClient> configAction) => throw new NotSupportedException();


//IGuildChannel //IGuildChannel
/// <inheritdoc />
Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options)
=> Task.FromResult<IGuildUser>(null); => Task.FromResult<IGuildUser>(null);
/// <inheritdoc />
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
=> AsyncEnumerable.Empty<IReadOnlyCollection<IGuildUser>>(); => AsyncEnumerable.Empty<IReadOnlyCollection<IGuildUser>>();

// INestedChannel
async Task<ICategoryChannel> INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options)
{
if (CategoryId.HasValue && mode == CacheMode.AllowDownload)
return (await Guild.GetChannelAsync(CategoryId.Value, mode, options).ConfigureAwait(false)) as ICategoryChannel;
return null;
}
} }
} }

+ 11
- 8
src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs View File

@@ -33,15 +33,19 @@ namespace Discord.Rest
public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null) public Task<IReadOnlyCollection<RestMessage>> GetPinnedMessagesAsync(RequestOptions options = null)
=> ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options);


public Task<RestUserMessage> SendMessageAsync(string text, bool isTTS, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options);
#if FILESYSTEM
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options);
#endif
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed = null, RequestOptions options = null)
public Task<RestUserMessage> SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null)
=> ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options);


public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options);
public Task DeleteMessageAsync(IMessage message, RequestOptions options = null)
=> ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options);

public Task TriggerTypingAsync(RequestOptions options = null) public Task TriggerTypingAsync(RequestOptions options = null)
=> ChannelHelper.TriggerTypingAsync(this, Discord, options); => ChannelHelper.TriggerTypingAsync(this, Discord, options);
public IDisposable EnterTypingState(RequestOptions options = null) public IDisposable EnterTypingState(RequestOptions options = null)
@@ -81,10 +85,9 @@ namespace Discord.Rest
async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) async Task<IReadOnlyCollection<IMessage>> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options)
=> await GetPinnedMessagesAsync(options).ConfigureAwait(false); => await GetPinnedMessagesAsync(options).ConfigureAwait(false);


#if FILESYSTEM
async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false);
#endif
=> await SendFileAsync(filePath, text, isTTS, embed, options);
async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options)
=> await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false);
async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options)


+ 20
- 4
src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs View File

@@ -147,21 +147,37 @@ namespace Discord.Rest
} }
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception>
public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, public static async Task<RestTextChannel> CreateTextChannelAsync(IGuild guild, BaseDiscordClient client,
string name, RequestOptions options)
string name, RequestOptions options, Action<TextChannelProperties> func = null)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));


var args = new CreateGuildChannelParams(name, ChannelType.Text);
var props = new TextChannelProperties();
func?.Invoke(props);

var args = new CreateGuildChannelParams(name, ChannelType.Text)
{
CategoryId = props.CategoryId,
Topic = props.Topic,
IsNsfw = props.IsNsfw
};
var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false);
return RestTextChannel.Create(client, guild, model); return RestTextChannel.Create(client, guild, model);
} }
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception> /// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c>.</exception>
public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, public static async Task<RestVoiceChannel> CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client,
string name, RequestOptions options)
string name, RequestOptions options, Action<VoiceChannelProperties> func = null)
{ {
if (name == null) throw new ArgumentNullException(nameof(name)); if (name == null) throw new ArgumentNullException(nameof(name));


var args = new CreateGuildChannelParams(name, ChannelType.Voice);
var props = new VoiceChannelProperties();
func?.Invoke(props);

var args = new CreateGuildChannelParams(name, ChannelType.Voice)
{
CategoryId = props.CategoryId,
Bitrate = props.Bitrate,
UserLimit = props.UserLimit
};
var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false);
return RestVoiceChannel.Create(client, guild, model); return RestVoiceChannel.Create(client, guild, model);
} }


+ 15
- 0
src/Discord.Net.Rest/Entities/Guilds/RestBan.cs View File

@@ -3,9 +3,18 @@ using Model = Discord.API.Ban;


namespace Discord.Rest namespace Discord.Rest
{ {
/// <summary>
/// Represents a REST-based ban object.
/// </summary>
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestBan : IBan public class RestBan : IBan
{ {
/// <summary>
/// Gets the banned user.
/// </summary>
/// <returns>
/// A generic <see cref="RestUser"/> object that was banned.
/// </returns>
public RestUser User { get; } public RestUser User { get; }
/// <inheritdoc /> /// <inheritdoc />
public string Reason { get; } public string Reason { get; }
@@ -20,6 +29,12 @@ namespace Discord.Rest
return new RestBan(RestUser.Create(client, model.User), model.Reason); return new RestBan(RestUser.Create(client, model.User), model.Reason);
} }


/// <summary>
/// Gets the name of the banned user.
/// </summary>
/// <returns>
/// A string containing the name of the user that was banned.
/// </returns>
public override string ToString() => User.ToString(); public override string ToString() => User.ToString();
private string DebuggerDisplay => $"{User}: {Reason}"; private string DebuggerDisplay => $"{User}: {Reason}";




+ 8
- 8
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -258,10 +258,10 @@ namespace Discord.Rest
} }
return null; return null;
} }
public Task<RestTextChannel> CreateTextChannelAsync(string name, RequestOptions options = null)
=> GuildHelper.CreateTextChannelAsync(this, Discord, name, options);
public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name, RequestOptions options = null)
=> GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options);
public Task<RestTextChannel> CreateTextChannelAsync(string name, Action<TextChannelProperties> func = null, RequestOptions options = null)
=> GuildHelper.CreateTextChannelAsync(this, Discord, name, options, func);
public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name, Action<VoiceChannelProperties> func = null, RequestOptions options = null)
=> GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options, func);
public Task<RestCategoryChannel> CreateCategoryChannelAsync(string name, RequestOptions options = null) public Task<RestCategoryChannel> CreateCategoryChannelAsync(string name, RequestOptions options = null)
=> GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options);


@@ -448,11 +448,11 @@ namespace Discord.Rest
return null; return null;
} }
/// <inheritdoc /> /// <inheritdoc />
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name, RequestOptions options)
=> await CreateTextChannelAsync(name, options).ConfigureAwait(false);
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name, Action<TextChannelProperties> func, RequestOptions options)
=> await CreateTextChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, RequestOptions options)
=> await CreateVoiceChannelAsync(name, options).ConfigureAwait(false);
async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, Action<VoiceChannelProperties> func, RequestOptions options)
=> await CreateVoiceChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc /> /// <inheritdoc />
async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options) async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options)
=> await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false);


+ 35
- 8
src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs View File

@@ -27,10 +27,12 @@ namespace Discord.Rest
}; };
return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false);
} }
public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client,
public static Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options)
=> DeleteAsync(msg.Channel.Id, msg.Id, client, options);
public static async Task DeleteAsync(ulong channelId, ulong msgId, BaseDiscordClient client,
RequestOptions options) RequestOptions options)
{ {
await client.ApiClient.DeleteMessageAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false);
await client.ApiClient.DeleteMessageAsync(channelId, msgId, options).ConfigureAwait(false);
} }


public static async Task AddReactionAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options) public static async Task AddReactionAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options)
@@ -48,13 +50,38 @@ namespace Discord.Rest
await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false);
} }


public static async Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IMessage msg, IEmote emote,
Action<GetReactionUsersParams> func, BaseDiscordClient client, RequestOptions options)
public static IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IMessage msg, IEmote emote,
int? limit, BaseDiscordClient client, RequestOptions options)
{ {
var args = new GetReactionUsersParams();
func(args);
string emoji = (emote is Emote e ? $"{e.Name}:{e.Id}" : emote.Name);
return (await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false)).Select(u => RestUser.Create(client, u)).ToImmutableArray();
Preconditions.NotNull(emote, nameof(emote));
var emoji = (emote is Emote e ? $"{e.Name}:{e.Id}" : emote.Name);

return new PagedAsyncEnumerable<IUser>(
DiscordConfig.MaxUserReactionsPerBatch,
async (info, ct) =>
{
var args = new GetReactionUsersParams
{
Limit = info.PageSize
};

if (info.Position != null)
args.AfterUserId = info.Position.Value;

var models = await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false);
return models.Select(x => RestUser.Create(client, x)).ToImmutableArray();
},
nextPage: (info, lastPage) =>
{
if (lastPage.Count != DiscordConfig.MaxUsersPerBatch)
return false;

info.Position = lastPage.Max(x => x.Id);
return true;
},
count: limit
);

} }


public static async Task PinAsync(IMessage msg, BaseDiscordClient client, public static async Task PinAsync(IMessage msg, BaseDiscordClient client,


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

@@ -151,9 +151,9 @@ namespace Discord.Rest
public Task RemoveAllReactionsAsync(RequestOptions options = null) public Task RemoveAllReactionsAsync(RequestOptions options = null)
=> MessageHelper.RemoveAllReactionsAsync(this, Discord, options); => MessageHelper.RemoveAllReactionsAsync(this, Discord, options);
/// <inheritdoc /> /// <inheritdoc />
public Task<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null)
=> MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create<ulong>(); }, Discord, options);
public IAsyncEnumerable<IReadOnlyCollection<IUser>> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null)
=> MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options);
/// <inheritdoc /> /// <inheritdoc />
public Task PinAsync(RequestOptions options = null) public Task PinAsync(RequestOptions options = null)
=> MessageHelper.PinAsync(this, Discord, options); => MessageHelper.PinAsync(this, Discord, options);


+ 1
- 2
src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs View File

@@ -1,4 +1,4 @@
using Discord.API;
using Discord.API;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using System; using System;
@@ -25,7 +25,6 @@ namespace Discord.Net.Converters
if (converter != null) if (converter != null)
{ {
property.Converter = converter; property.Converter = converter;
property.MemberConverter = converter;
} }
} }
else else


+ 1
- 1
src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs View File

@@ -230,7 +230,7 @@ namespace Discord.Net.Queue
#endif #endif
} }


var now = DateTimeUtils.ToUnixSeconds(DateTimeOffset.UtcNow);
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
DateTimeOffset? resetTick = null; DateTimeOffset? resetTick = null;


//Using X-RateLimit-Remaining causes a race condition //Using X-RateLimit-Remaining causes a race condition


+ 1
- 1
src/Discord.Net.Rest/Net/RateLimitInfo.cs View File

@@ -21,7 +21,7 @@ namespace Discord.Net
Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) &&
int.TryParse(temp, out var remaining) ? remaining : (int?)null; int.TryParse(temp, out var remaining) ? remaining : (int?)null;
Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) && Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) &&
int.TryParse(temp, out var reset) ? DateTimeUtils.FromUnixSeconds(reset) : (DateTimeOffset?)null;
int.TryParse(temp, out var reset) ? DateTimeOffset.FromUnixTimeSeconds(reset) : (DateTimeOffset?)null;
RetryAfter = headers.TryGetValue("Retry-After", out temp) && RetryAfter = headers.TryGetValue("Retry-After", out temp) &&
int.TryParse(temp, out var retryAfter) ? retryAfter : (int?)null; int.TryParse(temp, out var retryAfter) ? retryAfter : (int?)null;
Lag = headers.TryGetValue("Date", out temp) && Lag = headers.TryGetValue("Date", out temp) &&


+ 5
- 1
src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;


namespace Discord.API.Gateway namespace Discord.API.Gateway
@@ -9,6 +9,10 @@ namespace Discord.API.Gateway
public ulong UserId { get; set; } public ulong UserId { get; set; }
[JsonProperty("channel_id")] [JsonProperty("channel_id")]
public ulong ChannelId { get; set; } public ulong ChannelId { get; set; }
[JsonProperty("guild_id")]
public ulong GuildId { get; set; }
[JsonProperty("member")]
public GuildMember Member { get; set; }
[JsonProperty("timestamp")] [JsonProperty("timestamp")]
public int Timestamp { get; set; } public int Timestamp { get; set; }
} }


+ 10
- 0
src/Discord.Net.WebSocket/API/Voice/HelloEvent.cs View File

@@ -0,0 +1,10 @@
using Newtonsoft.Json;

namespace Discord.API.Voice
{
internal class HelloEvent
{
[JsonProperty("heartbeat_interval")]
public int HeartbeatInterval { get; set; }
}
}

+ 3
- 1
src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs View File

@@ -1,5 +1,6 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
using Newtonsoft.Json; using Newtonsoft.Json;
using System;


namespace Discord.API.Voice namespace Discord.API.Voice
{ {
@@ -14,6 +15,7 @@ namespace Discord.API.Voice
[JsonProperty("modes")] [JsonProperty("modes")]
public string[] Modes { get; set; } public string[] Modes { get; set; }
[JsonProperty("heartbeat_interval")] [JsonProperty("heartbeat_interval")]
[Obsolete("This field is errorneous and should not be used", true)]
public int HeartbeatInterval { get; set; } public int HeartbeatInterval { get; set; }
} }
} }

+ 12
- 4
src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs View File

@@ -1,4 +1,4 @@
#pragma warning disable CS1591
#pragma warning disable CS1591
namespace Discord.API.Voice namespace Discord.API.Voice
{ {
internal enum VoiceOpCode : byte internal enum VoiceOpCode : byte
@@ -11,11 +11,19 @@ namespace Discord.API.Voice
Ready = 2, Ready = 2,
/// <summary> C→S - Used to keep the connection alive and measure latency. </summary> /// <summary> C→S - Used to keep the connection alive and measure latency. </summary>
Heartbeat = 3, Heartbeat = 3,
/// <summary> C←S - Used to reply to a client's heartbeat. </summary>
HeartbeatAck = 3,
/// <summary> C←S - Used to provide an encryption key to the client. </summary> /// <summary> C←S - Used to provide an encryption key to the client. </summary>
SessionDescription = 4, SessionDescription = 4,
/// <summary> C↔S - Used to inform that a certain user is speaking. </summary> /// <summary> C↔S - Used to inform that a certain user is speaking. </summary>
Speaking = 5
Speaking = 5,
/// <summary> C←S - Used to reply to a client's heartbeat. </summary>
HeartbeatAck = 6,
/// <summary> C→S - Used to resume a connection. </summary>
Resume = 7,
/// <summary> C←S - Used to inform the client the heartbeat interval. </summary>
Hello = 8,
/// <summary> C←S - Used to acknowledge a resumed connection. </summary>
Resumed = 9,
/// <summary> C←S - Used to notify that a client has disconnected. </summary>
ClientDisconnect = 13,
} }
} }

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

Loading…
Cancel
Save