@@ -0,0 +1,113 @@ | |||||
# EditorConfig is awesome: http://EditorConfig.org | |||||
root = true | |||||
[*] | |||||
charset = utf-8 | |||||
end_of_line = lf | |||||
insert_final_newline = true | |||||
indent_size = 4 | |||||
indent_style = space | |||||
trim_trailing_whitespace = true | |||||
[*.{csproj,json,md,nuspec,yml}] | |||||
indent_size = 2 | |||||
indent_style = space | |||||
[*.{sln,xml}] | |||||
indent_style = tab | |||||
[*.cs] | |||||
dotnet_style_qualification_for_field = false:suggestion | |||||
dotnet_style_qualification_for_property = false:suggestion | |||||
dotnet_style_qualification_for_method = false:suggestion | |||||
dotnet_style_qualification_for_event = false:suggestion | |||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion | |||||
dotnet_style_predefined_type_for_member_access = true:suggestion | |||||
dotnet_style_object_initializer = true:suggestion | |||||
dotnet_style_collection_initializer = true:suggestion | |||||
dotnet_style_explicit_tuple_names = true:suggestion | |||||
dotnet_style_coalesce_expression = true:suggestion | |||||
dotnet_style_null_propagation = true:suggestion | |||||
csharp_style_var_for_built_in_types = true:suggestion | |||||
csharp_style_var_when_type_is_apparent = true:suggestion | |||||
csharp_style_var_elsewhere = true:suggestion | |||||
csharp_style_expression_bodied_methods = true:none | |||||
csharp_style_expression_bodied_operators = true:none | |||||
csharp_style_expression_bodied_properties = true:none | |||||
csharp_style_expression_bodied_indexers = true:none | |||||
csharp_style_expression_bodied_accessors = true:none | |||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion | |||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion | |||||
csharp_style_inlined_variable_declaration = true:suggestion | |||||
csharp_prefer_simple_default_expression = false:none | |||||
csharp_style_throw_expression = true:none | |||||
csharp_style_conditional_delegate_call = true:none | |||||
csharp_prefer_braces = false:none | |||||
dotnet_sort_system_directives_first = false | |||||
csharp_new_line_before_open_brace = all | |||||
csharp_new_line_before_else = true | |||||
csharp_new_line_before_catch = true | |||||
csharp_new_line_before_finally = true | |||||
csharp_new_line_before_members_in_object_initializers = true | |||||
csharp_new_line_before_members_in_anonymous_types = true | |||||
csharp_new_line_within_query_expression_clauses = true | |||||
csharp_indent_case_contents = true | |||||
csharp_indent_switch_labels = true | |||||
csharp_indent_labels = flush_left | |||||
csharp_space_after_cast = false | |||||
csharp_space_after_keywords_in_control_flow_statements = true | |||||
csharp_space_between_method_declaration_parameter_list_parentheses = false | |||||
csharp_space_between_method_call_parameter_list_parentheses = false | |||||
csharp_preserve_single_line_statements = false | |||||
csharp_preserve_single_line_blocks = true | |||||
dotnet_naming_rule.all_of_const_fields_are_pascal_case.symbols = const_fields | |||||
dotnet_naming_rule.all_of_const_fields_are_pascal_case.style = pascal_case | |||||
dotnet_naming_rule.all_of_const_fields_are_pascal_case.severity = suggestion | |||||
dotnet_naming_rule.all_of_local_fields_without_const_are_camel_case.symbols = local_fields | |||||
dotnet_naming_rule.all_of_local_fields_without_const_are_camel_case.style = starts_with_low_line_camel_case | |||||
dotnet_naming_rule.all_of_local_fields_without_const_are_camel_case.severity = suggestion | |||||
dotnet_naming_rule.all_of_interfaces_starts_with_low_line_camel_case.symbols = interfaces | |||||
dotnet_naming_rule.all_of_interfaces_starts_with_low_line_camel_case.style = starts_with_i_pascal_case | |||||
dotnet_naming_rule.all_of_interfaces_starts_with_low_line_camel_case.severity = suggestion | |||||
dotnet_naming_rule.default_is_pascal_case.symbols = without_interfaces_and_fields | |||||
dotnet_naming_rule.default_is_pascal_case.style = pascal_case | |||||
dotnet_naming_rule.default_is_pascal_case.severity = suggestion | |||||
dotnet_naming_symbols.const_fields.applicable_kinds = field | |||||
dotnet_naming_symbols.const_fields.applicable_accessibilities = * | |||||
dotnet_naming_symbols.const_fields.required_modifiers = const | |||||
dotnet_naming_symbols.interfaces.applicable_kinds = interface | |||||
dotnet_naming_symbols.interfaces.applicable_accessibilities = * | |||||
dotnet_naming_symbols.local_fields.applicable_kinds = field | |||||
dotnet_naming_symbols.local_fields.applicable_accessibilities = internal, private, protected, protected_internal | |||||
dotnet_naming_symbols.local_fields.required_modifiers = abstract, must_inherit, readonly, static, shared | |||||
dotnet_naming_symbols.without_interfaces_and_fields.applicable_kinds = class, struct, enum, property, method, event, namespace, delegate, type_parameter | |||||
dotnet_naming_symbols.without_interfaces_and_fields.applicable_accessibilities = * | |||||
dotnet_naming_style.pascal_case.capitalization = pascal_case | |||||
dotnet_naming_style.starts_with_i_pascal_case.required_prefix = I | |||||
dotnet_naming_style.starts_with_i_pascal_case.capitalization = pascal_case | |||||
dotnet_naming_style.starts_with_low_line_camel_case.required_prefix = _ | |||||
dotnet_naming_style.starts_with_low_line_camel_case.capitalization = camel_case |
@@ -0,0 +1,44 @@ | |||||
# Contributing | |||||
Discord.Net is an open-source project, and we appreciate any and all | |||||
contributions made by our community. However, please conform to the | |||||
following guidelines when possible: | |||||
## Development Cycle | |||||
We prefer all changes to the library to be discussed beforehand, | |||||
either in a GitHub issue, or in a discussion in our Discord channel | |||||
with library regulars or other contributors. | |||||
Issues that are tagged as "up for grabs" are free to be picked up by | |||||
any member of the community. | |||||
### Pull Requests | |||||
We prefer pull-requests that are descriptive of the changes being made | |||||
and highlight any potential benefits/drawbacks of the change, but these | |||||
types of write-ups are not required. See this [merge request](https://github.com/RogueException/Discord.Net/pull/793) | |||||
for an example of a well-written description. | |||||
## Semantic Versioning | |||||
This project follows [Semantic Versioning](http://semver.org/). When | |||||
writing changes to this project, it is recommended to write changes | |||||
that are SemVer compliant with the latest version of the library in | |||||
development. | |||||
The working release should be the latest build off of the `dev` branch, | |||||
but can also be found on the [development board](https://github.com/RogueException/Discord.Net/projects/1). | |||||
We follow the .NET Foundation's [Breaking Change Rules](https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/breaking-change-rules.md) | |||||
when determining the SemVer compliance of a change. | |||||
Obsoleting a method is considered a **minor** increment. | |||||
## Coding Style | |||||
We attempt to conform to the .NET Foundation's [Coding Style](https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md) | |||||
where possible. | |||||
As a general rule, follow the coding style already set in the file you | |||||
are editing, or look at a similar file if you are adding a new one. |
@@ -1,6 +1,6 @@ | |||||
Microsoft Visual Studio Solution File, Format Version 12.00 | Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
# Visual Studio 15 | # Visual Studio 15 | ||||
VisualStudioVersion = 15.0.26730.12 | |||||
VisualStudioVersion = 15.0.27130.0 | |||||
MinimumVisualStudioVersion = 10.0.40219.1 | MinimumVisualStudioVersion = 10.0.40219.1 | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" | ||||
EndProject | EndProject | ||||
@@ -8,8 +8,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Impls", "Impls", "{288C363D | |||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Rest", "src\Discord.Net.Rest\Discord.Net.Rest.csproj", "{BFC6DC28-0351-4573-926A-D4124244C04F}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Rest", "src\Discord.Net.Rest\Discord.Net.Rest.csproj", "{BFC6DC28-0351-4573-926A-D4124244C04F}" | ||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Rpc", "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj", "{5688A353-121E-40A1-8BFA-B17B91FB48FB}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.csproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Commands", "src\Discord.Net.Commands\Discord.Net.Commands.csproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}" | ||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.WebSocket", "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj", "{688FD1D8-7F01-4539-B2E9-F473C5D699C7}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.WebSocket", "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj", "{688FD1D8-7F01-4539-B2E9-F473C5D699C7}" | ||||
@@ -24,6 +22,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests", "test\D | |||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Webhook", "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj", "{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Webhook", "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj", "{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}" | ||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.Analyzers", "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj", "{BBA8E7FB-C834-40DC-822F-B112CB7F0140}" | |||||
EndProject | |||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
Debug|Any CPU = Debug|Any CPU | Debug|Any CPU = Debug|Any CPU | ||||
@@ -58,18 +58,6 @@ Global | |||||
{BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x64.Build.0 = Debug|Any CPU | {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x64.Build.0 = Debug|Any CPU | ||||
{BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x86.ActiveCfg = Debug|Any CPU | {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x86.ActiveCfg = Debug|Any CPU | ||||
{BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x86.Build.0 = Debug|Any CPU | {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x86.Build.0 = Debug|Any CPU | ||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x64.ActiveCfg = Debug|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x64.Build.0 = Debug|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x86.ActiveCfg = Debug|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x86.Build.0 = Debug|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x64.ActiveCfg = Debug|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x64.Build.0 = Debug|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x86.ActiveCfg = Debug|Any CPU | |||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x86.Build.0 = Debug|Any CPU | |||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU | {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|x64.ActiveCfg = Debug|Any CPU | {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
@@ -130,17 +118,29 @@ Global | |||||
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x64.Build.0 = Release|Any CPU | {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x64.Build.0 = Release|Any CPU | ||||
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.ActiveCfg = Release|Any CPU | {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.ActiveCfg = Release|Any CPU | ||||
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.Build.0 = Release|Any CPU | {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}.Release|x86.Build.0 = Release|Any CPU | ||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|x64.ActiveCfg = Debug|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|x64.Build.0 = Debug|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|x86.ActiveCfg = Debug|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Debug|x86.Build.0 = Debug|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|Any CPU.Build.0 = Release|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x64.ActiveCfg = Release|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x64.Build.0 = Release|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x86.ActiveCfg = Release|Any CPU | |||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x86.Build.0 = Release|Any CPU | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(SolutionProperties) = preSolution | GlobalSection(SolutionProperties) = preSolution | ||||
HideSolutionNode = FALSE | HideSolutionNode = FALSE | ||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(NestedProjects) = preSolution | GlobalSection(NestedProjects) = preSolution | ||||
{BFC6DC28-0351-4573-926A-D4124244C04F} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | {BFC6DC28-0351-4573-926A-D4124244C04F} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | ||||
{5688A353-121E-40A1-8BFA-B17B91FB48FB} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | |||||
{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | ||||
{688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | {688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E} | ||||
{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} | {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} | ||||
{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | ||||
{BBA8E7FB-C834-40DC-822F-B112CB7F0140} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} | |||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(ExtensibilityGlobals) = postSolution | GlobalSection(ExtensibilityGlobals) = postSolution | ||||
SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495} | SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495} | ||||
@@ -1,7 +1,7 @@ | |||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
<PropertyGroup> | <PropertyGroup> | ||||
<VersionPrefix>2.0.0-alpha</VersionPrefix> | |||||
<VersionSuffix></VersionSuffix> | |||||
<VersionPrefix>2.0.0</VersionPrefix> | |||||
<VersionSuffix>beta</VersionSuffix> | |||||
<Authors>RogueException</Authors> | <Authors>RogueException</Authors> | ||||
<PackageTags>discord;discordapp</PackageTags> | <PackageTags>discord;discordapp</PackageTags> | ||||
<PackageProjectUrl>https://github.com/RogueException/Discord.Net</PackageProjectUrl> | <PackageProjectUrl>https://github.com/RogueException/Discord.Net</PackageProjectUrl> | ||||
@@ -2,11 +2,11 @@ | |||||
[](https://www.nuget.org/packages/Discord.Net) | [](https://www.nuget.org/packages/Discord.Net) | ||||
[](https://www.myget.org/feed/Packages/discord-net) | [](https://www.myget.org/feed/Packages/discord-net) | ||||
[](https://ci.appveyor.com/project/RogueException/discord-net/branch/dev) | [](https://ci.appveyor.com/project/RogueException/discord-net/branch/dev) | ||||
[](https://discord.gg/0SBTUU1wZTVjAMPx) | |||||
[](https://discord.gg/jkrBmQR) | |||||
An unofficial .NET API Wrapper for the Discord client (http://discordapp.com). | An unofficial .NET API Wrapper for the Discord client (http://discordapp.com). | ||||
Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Discord API Chat](https://discord.gg/0SBTUU1wZTVjAMPx). | |||||
Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Discord API Chat](https://discord.gg/jkrBmQR). | |||||
## Installation | ## Installation | ||||
### Stable (NuGet) | ### Stable (NuGet) | ||||
@@ -26,15 +26,15 @@ after_build: | |||||
- ps: dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | - ps: dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | ||||
- ps: dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | - ps: dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | ||||
- ps: dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | - ps: dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | ||||
- ps: dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | |||||
- ps: dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | - ps: dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | ||||
- ps: dotnet pack "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | - ps: dotnet pack "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | ||||
- ps: dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | - ps: dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | ||||
- ps: dotnet pack "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" /p:IsTagBuild="$Env:APPVEYOR_REPO_TAG" | |||||
- ps: >- | - ps: >- | ||||
if ($Env:APPVEYOR_REPO_TAG -eq "true") { | if ($Env:APPVEYOR_REPO_TAG -eq "true") { | ||||
nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="" | nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="" | ||||
} else { | } else { | ||||
nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="-build-$Env:BUILD" | |||||
nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties suffix="-$Env:BUILD" | |||||
} | } | ||||
- ps: Get-ChildItem artifacts\*.nupkg | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } | - ps: Get-ChildItem artifacts\*.nupkg | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } | ||||
@@ -93,9 +93,9 @@ If you would like a parameter to parse until the end of a Command, | |||||
flag the parameter with the [RemainderAttribute]. This will allow a | flag the parameter with the [RemainderAttribute]. This will allow a | ||||
user to invoke a Command without wrapping a parameter in quotes. | user to invoke a Command without wrapping a parameter in quotes. | ||||
Finally, flag your Command with the [CommandAttribute] (you must | |||||
Finally, flag your Command with the [CommandAttribute]. (you must | |||||
specify a name for this Command, except for when it is part of a | specify a name for this Command, except for when it is part of a | ||||
Module Group - see below). | |||||
Module Group - see below) | |||||
[RemainderAttribute]: xref:Discord.Commands.RemainderAttribute | [RemainderAttribute]: xref:Discord.Commands.RemainderAttribute | ||||
[CommandAttribute]: xref:Discord.Commands.CommandAttribute | [CommandAttribute]: xref:Discord.Commands.CommandAttribute | ||||
@@ -340,4 +340,4 @@ and must be explicitly added. | |||||
To install a TypeReader, invoke [CommandService.AddTypeReader]. | To install a TypeReader, invoke [CommandService.AddTypeReader]. | ||||
[CommandService.AddTypeReader]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_ | |||||
[CommandService.AddTypeReader]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_ |
@@ -10,6 +10,7 @@ namespace Discord.Rpc | |||||
{ | { | ||||
public ulong GuildId { get; } | public ulong GuildId { get; } | ||||
public int Position { get; private set; } | public int Position { get; private set; } | ||||
public ulong? CategoryId { get; private set; } | |||||
internal RpcGuildChannel(DiscordRpcClient discord, ulong id, ulong guildId) | internal RpcGuildChannel(DiscordRpcClient discord, ulong id, ulong guildId) | ||||
: base(discord, id) | : base(discord, id) | ||||
@@ -57,6 +58,12 @@ namespace Discord.Rpc | |||||
public override string ToString() => Name; | public override string ToString() => Name; | ||||
//IGuildChannel | //IGuildChannel | ||||
public Task<ICategoryChannel> GetCategoryAsync() | |||||
{ | |||||
//Always fails | |||||
throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | |||||
} | |||||
IGuild IGuildChannel.Guild | IGuild IGuildChannel.Guild | ||||
{ | { | ||||
get | get |
@@ -68,11 +68,25 @@ namespace Discord.Rpc | |||||
=> 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); | ||||
//Webhooks | |||||
public Task<RestWebhook> CreateWebhookAsync(string name, Stream avatar = null, RequestOptions options = null) | |||||
=> ChannelHelper.CreateWebhookAsync(this, Discord, name, avatar, options); | |||||
public Task<RestWebhook> GetWebhookAsync(ulong id, RequestOptions options = null) | |||||
=> ChannelHelper.GetWebhookAsync(this, Discord, id, options); | |||||
public Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null) | |||||
=> ChannelHelper.GetWebhooksAsync(this, Discord, options); | |||||
private string DebuggerDisplay => $"{Name} ({Id}, Text)"; | private string DebuggerDisplay => $"{Name} ({Id}, Text)"; | ||||
//ITextChannel | //ITextChannel | ||||
string ITextChannel.Topic { get { throw new NotSupportedException(); } } | string ITextChannel.Topic { get { throw new NotSupportedException(); } } | ||||
async Task<IWebhook> ITextChannel.CreateWebhookAsync(string name, Stream avatar, RequestOptions options) | |||||
=> await CreateWebhookAsync(name, avatar, options); | |||||
async Task<IWebhook> ITextChannel.GetWebhookAsync(ulong id, RequestOptions options) | |||||
=> await GetWebhookAsync(id, options); | |||||
async Task<IReadOnlyCollection<IWebhook>> ITextChannel.GetWebhooksAsync(RequestOptions options) | |||||
=> await GetWebhooksAsync(options); | |||||
//IMessageChannel | //IMessageChannel | ||||
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) | async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) |
@@ -18,7 +18,7 @@ namespace Discord.Rpc | |||||
public string Discriminator => DiscriminatorValue.ToString("D4"); | public string Discriminator => DiscriminatorValue.ToString("D4"); | ||||
public string Mention => MentionUtils.MentionUser(Id); | public string Mention => MentionUtils.MentionUser(Id); | ||||
public virtual bool IsWebhook => false; | public virtual bool IsWebhook => false; | ||||
public virtual Game? Game => null; | |||||
public virtual IActivity Activity => null; | |||||
public virtual UserStatus Status => UserStatus.Offline; | public virtual UserStatus Status => UserStatus.Offline; | ||||
internal RpcUser(DiscordRpcClient discord, ulong id) | internal RpcUser(DiscordRpcClient discord, ulong id) |
@@ -0,0 +1,15 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<Import Project="../../Discord.Net.targets" /> | |||||
<PropertyGroup> | |||||
<AssemblyName>Discord.Net.Analyzers</AssemblyName> | |||||
<RootNamespace>Discord.Analyzers</RootNamespace> | |||||
<Description>A Discord.Net extension adding support for design-time analysis of the API usage.</Description> | |||||
<TargetFramework>netstandard1.3</TargetFramework> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.6.0" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\Discord.Net.Commands\Discord.Net.Commands.csproj" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -0,0 +1,70 @@ | |||||
using System; | |||||
using System.Collections.Immutable; | |||||
using System.Linq; | |||||
using Microsoft.CodeAnalysis; | |||||
using Microsoft.CodeAnalysis.CSharp; | |||||
using Microsoft.CodeAnalysis.CSharp.Syntax; | |||||
using Microsoft.CodeAnalysis.Diagnostics; | |||||
using Discord.Commands; | |||||
namespace Discord.Analyzers | |||||
{ | |||||
[DiagnosticAnalyzer(LanguageNames.CSharp)] | |||||
public sealed class GuildAccessAnalyzer : DiagnosticAnalyzer | |||||
{ | |||||
private const string DiagnosticId = "DNET0001"; | |||||
private const string Title = "Limit command to Guild contexts."; | |||||
private const string MessageFormat = "Command method '{0}' is accessing 'Context.Guild' but is not restricted to Guild contexts."; | |||||
private const string Description = "Accessing 'Context.Guild' in a command without limiting the command to run only in guilds."; | |||||
private const string Category = "API Usage"; | |||||
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); | |||||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule); | |||||
public override void Initialize(AnalysisContext context) | |||||
{ | |||||
context.RegisterSyntaxNodeAction(AnalyzeMemberAccess, SyntaxKind.SimpleMemberAccessExpression); | |||||
} | |||||
private static void AnalyzeMemberAccess(SyntaxNodeAnalysisContext context) | |||||
{ | |||||
// Bail out if the accessed member isn't named 'Guild' | |||||
var memberAccessSymbol = context.SemanticModel.GetSymbolInfo(context.Node).Symbol; | |||||
if (memberAccessSymbol.Name != "Guild") | |||||
return; | |||||
// Bail out if it happens to be 'ContextType.Guild' in the '[RequireContext]' argument | |||||
if (context.Node.Parent is AttributeArgumentSyntax) | |||||
return; | |||||
// Bail out if the containing class doesn't derive from 'ModuleBase<T>' | |||||
var typeNode = context.Node.FirstAncestorOrSelf<TypeDeclarationSyntax>(); | |||||
var typeSymbol = context.SemanticModel.GetDeclaredSymbol(typeNode); | |||||
if (!typeSymbol.DerivesFromModuleBase()) | |||||
return; | |||||
// Bail out if the containing method isn't marked with '[Command]' | |||||
var methodNode = context.Node.FirstAncestorOrSelf<MethodDeclarationSyntax>(); | |||||
var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodNode); | |||||
var methodAttributes = methodSymbol.GetAttributes(); | |||||
if (!methodAttributes.Any(a => a.AttributeClass.Name == nameof(CommandAttribute))) | |||||
return; | |||||
// Is the '[RequireContext]' attribute not applied to either the | |||||
// method or the class, or its argument isn't 'ContextType.Guild'? | |||||
var ctxAttribute = methodAttributes.SingleOrDefault(_attributeDataPredicate) | |||||
?? typeSymbol.GetAttributes().SingleOrDefault(_attributeDataPredicate); | |||||
if (ctxAttribute == null || ctxAttribute.ConstructorArguments.Any(arg => !arg.Value.Equals((int)ContextType.Guild))) | |||||
{ | |||||
// Report the diagnostic | |||||
var diagnostic = Diagnostic.Create(Rule, context.Node.GetLocation(), methodSymbol.Name); | |||||
context.ReportDiagnostic(diagnostic); | |||||
} | |||||
} | |||||
private static readonly Func<AttributeData, bool> _attributeDataPredicate = | |||||
(a => a.AttributeClass.Name == nameof(RequireContextAttribute)); | |||||
} | |||||
} |
@@ -0,0 +1,21 @@ | |||||
using System; | |||||
using Microsoft.CodeAnalysis; | |||||
using Discord.Commands; | |||||
namespace Discord.Analyzers | |||||
{ | |||||
internal static class SymbolExtensions | |||||
{ | |||||
private static readonly string _moduleBaseName = typeof(ModuleBase<>).Name; | |||||
public static bool DerivesFromModuleBase(this ITypeSymbol symbol) | |||||
{ | |||||
for (var bType = symbol.BaseType; bType != null; bType = bType.BaseType) | |||||
{ | |||||
if (bType.MetadataName == _moduleBaseName) | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,30 @@ | |||||
# DNET0001 | |||||
<table> | |||||
<tr> | |||||
<td>TypeName</td> | |||||
<td>GuildAccessAnalyzer</td> | |||||
</tr> | |||||
<tr> | |||||
<td>CheckId</td> | |||||
<td>DNET0001</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Category</td> | |||||
<td>API Usage</td> | |||||
</tr> | |||||
</table> | |||||
## Cause | |||||
A method identified as a command is accessing `Context.Guild` without the requisite precondition. | |||||
## Rule description | |||||
The value of `Context.Guild` is `null` if a command is invoked in a DM channel. Attempting to access | |||||
guild properties in such a case will result in a `NullReferenceException` at runtime. | |||||
This exception is entirely avoidable by using the library's provided preconditions. | |||||
## How to fix violations | |||||
Add the precondition `[RequireContext(ContextType.Guild)]` to the command or module class. |
@@ -3,7 +3,7 @@ using System; | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
/// <summary> Provides aliases for a command. </summary> | /// <summary> Provides aliases for a command. </summary> | ||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] | |||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] | |||||
public class AliasAttribute : Attribute | public class AliasAttribute : Attribute | ||||
{ | { | ||||
/// <summary> The aliases which have been defined for the command. </summary> | /// <summary> The aliases which have been defined for the command. </summary> | ||||
@@ -1,8 +1,8 @@ | |||||
using System; | |||||
using System; | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
[AttributeUsage(AttributeTargets.Method)] | |||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] | |||||
public class CommandAttribute : Attribute | public class CommandAttribute : Attribute | ||||
{ | { | ||||
public string Text { get; } | public string Text { get; } | ||||
@@ -1,8 +1,8 @@ | |||||
using System; | |||||
using System; | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
[AttributeUsage(AttributeTargets.Class)] | |||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] | |||||
public class DontAutoLoadAttribute : Attribute | public class DontAutoLoadAttribute : Attribute | ||||
{ | { | ||||
} | } | ||||
@@ -2,7 +2,7 @@ using System; | |||||
namespace Discord.Commands { | namespace Discord.Commands { | ||||
[AttributeUsage(AttributeTargets.Property)] | |||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] | |||||
public class DontInjectAttribute : Attribute { | public class DontInjectAttribute : Attribute { | ||||
} | } | ||||
@@ -1,8 +1,8 @@ | |||||
using System; | |||||
using System; | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
[AttributeUsage(AttributeTargets.Class)] | |||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] | |||||
public class GroupAttribute : Attribute | public class GroupAttribute : Attribute | ||||
{ | { | ||||
public string Prefix { get; } | public string Prefix { get; } | ||||
@@ -3,7 +3,7 @@ using System; | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
// Override public name of command/module | // Override public name of command/module | ||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Parameter)] | |||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] | |||||
public class NameAttribute : Attribute | public class NameAttribute : Attribute | ||||
{ | { | ||||
public string Text { get; } | public string Text { get; } | ||||
@@ -4,7 +4,7 @@ using System.Reflection; | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
[AttributeUsage(AttributeTargets.Parameter)] | |||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] | |||||
public class OverrideTypeReaderAttribute : Attribute | public class OverrideTypeReaderAttribute : Attribute | ||||
{ | { | ||||
private static readonly TypeInfo _typeReaderTypeInfo = typeof(TypeReader).GetTypeInfo(); | private static readonly TypeInfo _typeReaderTypeInfo = typeof(TypeReader).GetTypeInfo(); | ||||
@@ -19,4 +19,4 @@ namespace Discord.Commands | |||||
TypeReader = overridenTypeReader; | TypeReader = overridenTypeReader; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,6 +1,5 @@ | |||||
using System; | using System; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Microsoft.Extensions.DependencyInjection; | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
@@ -9,4 +8,4 @@ namespace Discord.Commands | |||||
{ | { | ||||
public abstract Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, ParameterInfo parameter, object value, IServiceProvider services); | public abstract Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, ParameterInfo parameter, object value, IServiceProvider services); | ||||
} | } | ||||
} | |||||
} |
@@ -1,4 +1,4 @@ | |||||
using System; | |||||
using System; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Microsoft.Extensions.DependencyInjection; | using Microsoft.Extensions.DependencyInjection; | ||||
@@ -15,7 +15,7 @@ namespace Discord.Commands | |||||
/// <summary> | /// <summary> | ||||
/// Require that the command be invoked in a specified context. | /// Require that the command be invoked in a specified context. | ||||
/// </summary> | /// </summary> | ||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] | |||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] | |||||
public class RequireContextAttribute : PreconditionAttribute | public class RequireContextAttribute : PreconditionAttribute | ||||
{ | { | ||||
public ContextType Contexts { get; } | public ContextType Contexts { get; } | ||||
@@ -1,4 +1,4 @@ | |||||
using System; | |||||
using System; | |||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
namespace Discord.Commands | namespace Discord.Commands | ||||
@@ -6,7 +6,7 @@ namespace Discord.Commands | |||||
/// <summary> | /// <summary> | ||||
/// Require that the command is invoked in a channel marked NSFW | /// Require that the command is invoked in a channel marked NSFW | ||||
/// </summary> | /// </summary> | ||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] | |||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] | |||||
public class RequireNsfwAttribute : PreconditionAttribute | public class RequireNsfwAttribute : PreconditionAttribute | ||||
{ | { | ||||
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) | public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) | ||||
@@ -1,7 +1,6 @@ | |||||
#pragma warning disable CS0618 | |||||
#pragma warning disable CS0618 | |||||
using System; | using System; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
using Microsoft.Extensions.DependencyInjection; | |||||
namespace Discord.Commands | namespace Discord.Commands | ||||
{ | { | ||||
@@ -9,7 +8,7 @@ namespace Discord.Commands | |||||
/// Require that the command is invoked by the owner of the bot. | /// Require that the command is invoked by the owner of the bot. | ||||
/// </summary> | /// </summary> | ||||
/// <remarks>This precondition will only work if the bot is a bot account.</remarks> | /// <remarks>This precondition will only work if the bot is a bot account.</remarks> | ||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] | |||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] | |||||
public class RequireOwnerAttribute : PreconditionAttribute | public class RequireOwnerAttribute : PreconditionAttribute | ||||
{ | { | ||||
public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) | public override async Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services) | ||||