diff --git a/DisCatSharp.ApplicationCommands/Properties/AssemblyProperties.cs b/DisCatSharp.ApplicationCommands/Properties/AssemblyProperties.cs
index dc6e49603..62f2232fc 100644
--- a/DisCatSharp.ApplicationCommands/Properties/AssemblyProperties.cs
+++ b/DisCatSharp.ApplicationCommands/Properties/AssemblyProperties.cs
@@ -1,47 +1,48 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DisCatSharp")]
+[assembly: InternalsVisibleTo("DisCatSharp.Experimental")]
[assembly: InternalsVisibleTo("DisCatSharp.CommandsNext")]
[assembly: InternalsVisibleTo("DisCatSharp.Common")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.DependencyInjection")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Interactivity")]
[assembly: InternalsVisibleTo("DisCatSharp.Lavalink")]
[assembly: InternalsVisibleTo("DisCatSharp.Phabricator")]
[assembly: InternalsVisibleTo("DisCatSharp.Support")]
[assembly: InternalsVisibleTo("DisCatSharp.Test")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")]
[assembly: InternalsVisibleTo("Nyaw")]
[assembly: InternalsVisibleTo("DisCatSharp.DevTools")]
[assembly: InternalsVisibleTo("DisCatSharp.DocsGenerator")]
[assembly: InternalsVisibleTo("DisCatSharp.StaffApps")]
[assembly: InternalsVisibleTo("DisCatSharp.TranslationGenerator")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode.Metadata.ManagedReference")]
diff --git a/DisCatSharp.Attributes/DisCatSharp.Attributes.csproj b/DisCatSharp.Attributes/DisCatSharp.Attributes.csproj
new file mode 100644
index 000000000..41ab86d02
--- /dev/null
+++ b/DisCatSharp.Attributes/DisCatSharp.Attributes.csproj
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+ netstandard2.0
+ True
+ Library
+
+
+
+ DisCatSharp.Attributes
+ DisCatSharp.Attributes
+
+
+
+ DisCatSharp.Attributes
+
+ DisCatSharp.Attributes
+
+ Special Attributes for DisCatSharp.
+
+ DisCatSharp
+
+
diff --git a/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs b/DisCatSharp.Attributes/ExperimentalAttribute.cs
similarity index 69%
copy from DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs
copy to DisCatSharp.Attributes/ExperimentalAttribute.cs
index ad6586e29..1a010268c 100644
--- a/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs
+++ b/DisCatSharp.Attributes/ExperimentalAttribute.cs
@@ -1,49 +1,40 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-namespace DisCatSharp.Enums
+namespace DisCatSharp.Attributes
{
- public enum AutomodActionType : int
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
+ public sealed class ExperimentalAttribute : Attribute
{
- ///
- /// Blocks the content of a message according to the rule.
- ///
- BlockMessage = 1,
+ public string Message { get; set; }
- ///
- /// Logs user to a specified channel.
- ///
- SendAlertMessage = 2,
+ public ExperimentalAttribute(string message)
+ {
+ this.Message = message;
+ }
- ///
- /// Timeout user for a specified duration.
- /// Only valid for Keyword and MentionSpam rules
- ///
- Timeout = 3
+ public ExperimentalAttribute()
+ { }
}
}
diff --git a/DisCatSharp/Properties/AssemblyProperties.cs b/DisCatSharp.Attributes/Properties/AssemblyProperties.cs
similarity index 95%
copy from DisCatSharp/Properties/AssemblyProperties.cs
copy to DisCatSharp.Attributes/Properties/AssemblyProperties.cs
index c696ef7ce..5841f7937 100644
--- a/DisCatSharp/Properties/AssemblyProperties.cs
+++ b/DisCatSharp.Attributes/Properties/AssemblyProperties.cs
@@ -1,45 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Runtime.CompilerServices;
+[assembly: InternalsVisibleTo("DisCatSharp")]
[assembly: InternalsVisibleTo("DisCatSharp.ApplicationCommands")]
[assembly: InternalsVisibleTo("DisCatSharp.CommandsNext")]
[assembly: InternalsVisibleTo("DisCatSharp.Common")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.DependencyInjection")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Interactivity")]
[assembly: InternalsVisibleTo("DisCatSharp.Lavalink")]
[assembly: InternalsVisibleTo("DisCatSharp.Phabricator")]
[assembly: InternalsVisibleTo("DisCatSharp.Support")]
[assembly: InternalsVisibleTo("DisCatSharp.Test")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")]
[assembly: InternalsVisibleTo("Nyaw")]
[assembly: InternalsVisibleTo("DisCatSharp.DevTools")]
[assembly: InternalsVisibleTo("DisCatSharp.DocsGenerator")]
[assembly: InternalsVisibleTo("DisCatSharp.StaffApps")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode.Metadata.ManagedReference")]
+[assembly: InternalsVisibleTo("DisCatSharp.Experimental")]
diff --git a/DisCatSharp.CommandsNext/Converters/ArgumentBindingResult.cs b/DisCatSharp.CommandsNext/Converters/ArgumentBindingResult.cs
index 0f898cfa8..cfbec2be1 100644
--- a/DisCatSharp.CommandsNext/Converters/ArgumentBindingResult.cs
+++ b/DisCatSharp.CommandsNext/Converters/ArgumentBindingResult.cs
@@ -1,74 +1,74 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
namespace DisCatSharp.CommandsNext.Converters;
///
/// Represents a argument binding result.
///
-public struct ArgumentBindingResult
+public readonly struct ArgumentBindingResult
{
///
/// Gets a value indicating whether the binding is successful.
///
public bool IsSuccessful { get; }
///
/// Gets the converted.
///
public object[] Converted { get; }
///
/// Gets the raw.
///
public IReadOnlyList Raw { get; }
///
/// Gets the reason.
///
public Exception Reason { get; }
///
/// Initializes a new instance of the class.
///
/// The converted.
/// The raw.
public ArgumentBindingResult(object[] converted, IReadOnlyList raw)
{
this.IsSuccessful = true;
this.Reason = null;
this.Converted = converted;
this.Raw = raw;
}
///
/// Initializes a new instance of the class.
///
/// The ex.
public ArgumentBindingResult(Exception ex)
{
this.IsSuccessful = false;
this.Reason = ex;
this.Converted = null;
this.Raw = null;
}
}
diff --git a/DisCatSharp.CommandsNext/Entities/CommandHelpMessage.cs b/DisCatSharp.CommandsNext/Entities/CommandHelpMessage.cs
index cd4e26d96..597ccfaf2 100644
--- a/DisCatSharp.CommandsNext/Entities/CommandHelpMessage.cs
+++ b/DisCatSharp.CommandsNext/Entities/CommandHelpMessage.cs
@@ -1,52 +1,52 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using DisCatSharp.Entities;
namespace DisCatSharp.CommandsNext.Entities;
///
/// Represents a formatted help message.
///
-public struct CommandHelpMessage
+public readonly struct CommandHelpMessage
{
///
/// Gets the contents of the help message.
///
public string Content { get; }
///
/// Gets the embed attached to the help message.
///
public DiscordEmbed Embed { get; }
///
/// Creates a new instance of a help message.
///
/// Contents of the message.
/// Embed to attach to the message.
public CommandHelpMessage(string content = null, DiscordEmbed embed = null)
{
this.Content = content;
this.Embed = embed;
}
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandContext.cs b/DisCatSharp.CommandsNext/EventArgs/CommandContext.cs
index 69cba3138..3fa4d24cf 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandContext.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandContext.cs
@@ -1,207 +1,207 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using Microsoft.Extensions.DependencyInjection;
namespace DisCatSharp.CommandsNext;
///
/// Represents a context in which a command is executed.
///
public sealed class CommandContext
{
///
/// Gets the client which received the message.
///
public DiscordClient Client { get; internal set; }
///
/// Gets the message that triggered the execution.
///
public DiscordMessage Message { get; internal set; }
///
/// Gets the channel in which the execution was triggered,
///
public DiscordChannel Channel
=> this.Message.Channel;
///
/// Gets the guild in which the execution was triggered. This property is null for commands sent over direct messages.
///
public DiscordGuild Guild
=> this.Channel.Guild;
///
/// Gets the user who triggered the execution.
///
public DiscordUser User
=> this.Message.Author;
///
/// Gets the member who triggered the execution. This property is null for commands sent over direct messages.
///
public DiscordMember Member
=> this._lazyMember.Value;
private readonly Lazy _lazyMember;
///
/// Gets the CommandsNext service instance that handled this command.
///
public CommandsNextExtension CommandsNext { get; internal set; }
///
/// Gets the service provider for this CNext instance.
///
public IServiceProvider Services { get; internal set; }
///
/// Gets the command that is being executed.
///
public Command Command { get; internal set; }
///
/// Gets the overload of the command that is being executed.
///
public CommandOverload Overload { get; internal set; }
///
/// Gets the list of raw arguments passed to the command.
///
public IReadOnlyList RawArguments { get; internal set; }
///
/// Gets the raw string from which the arguments were extracted.
///
public string RawArgumentString { get; internal set; }
///
/// Gets the prefix used to invoke the command.
///
public string Prefix { get; internal set; }
///
/// Gets or sets the config.
///
internal CommandsNextConfiguration Config { get; set; }
///
/// Gets or sets the service scope context.
///
internal ServiceContext ServiceScopeContext { get; set; }
///
/// Initializes a new instance of the class.
///
internal CommandContext()
{
this._lazyMember = new Lazy(() => this.Guild != null && this.Guild.Members.TryGetValue(this.User.Id, out var member) ? member : this.Guild?.GetMemberAsync(this.User.Id).ConfigureAwait(false).GetAwaiter().GetResult());
}
///
/// Quickly respond to the message that triggered the command.
///
/// Message to respond with.
///
public Task RespondAsync(string content)
=> this.Message.RespondAsync(content);
///
/// Quickly respond to the message that triggered the command.
///
/// Embed to attach.
///
public Task RespondAsync(DiscordEmbed embed)
=> this.Message.RespondAsync(embed);
///
/// Quickly respond to the message that triggered the command.
///
/// Message to respond with.
/// Embed to attach.
///
public Task RespondAsync(string content, DiscordEmbed embed)
=> this.Message.RespondAsync(content, embed);
///
/// Quickly respond to the message that triggered the command.
///
/// The Discord Message builder.
///
public Task RespondAsync(DiscordMessageBuilder builder)
=> this.Message.RespondAsync(builder);
///
/// Quickly respond to the message that triggered the command.
///
/// The Discord Message builder.
///
public Task RespondAsync(Action action)
=> this.Message.RespondAsync(action);
///
/// Triggers typing in the channel containing the message that triggered the command.
///
///
public Task TriggerTypingAsync()
=> this.Channel.TriggerTypingAsync();
- internal struct ServiceContext : IDisposable
+ internal readonly struct ServiceContext : IDisposable
{
///
/// Gets the provider.
///
public IServiceProvider Provider { get; }
///
/// Gets the scope.
///
public IServiceScope Scope { get; }
///
/// Gets a value indicating whether is initialized.
///
public bool IsInitialized { get; }
///
/// Initializes a new instance of the class.
///
/// The services.
/// The scope.
public ServiceContext(IServiceProvider services, IServiceScope scope)
{
this.Provider = services;
this.Scope = scope;
this.IsInitialized = true;
}
///
/// Disposes the command context.
///
public void Dispose() => this.Scope?.Dispose();
}
}
diff --git a/DisCatSharp.CommandsNext/Properties/AssemblyProperties.cs b/DisCatSharp.CommandsNext/Properties/AssemblyProperties.cs
index 43fcabf9a..dc1b9e1bd 100644
--- a/DisCatSharp.CommandsNext/Properties/AssemblyProperties.cs
+++ b/DisCatSharp.CommandsNext/Properties/AssemblyProperties.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DisCatSharp.ApplicationCommands")]
+[assembly: InternalsVisibleTo("DisCatSharp.Experimental")]
[assembly: InternalsVisibleTo("DisCatSharp")]
[assembly: InternalsVisibleTo("DisCatSharp.Common")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.DependencyInjection")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Interactivity")]
[assembly: InternalsVisibleTo("DisCatSharp.Lavalink")]
[assembly: InternalsVisibleTo("DisCatSharp.Phabricator")]
[assembly: InternalsVisibleTo("DisCatSharp.Support")]
[assembly: InternalsVisibleTo("DisCatSharp.Test")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")]
[assembly: InternalsVisibleTo("Nyaw")]
[assembly: InternalsVisibleTo("DisCatSharp.DevTools")]
[assembly: InternalsVisibleTo("DisCatSharp.DocsGenerator")]
[assembly: InternalsVisibleTo("DisCatSharp.StaffApps")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode.Metadata.ManagedReference")]
diff --git a/DisCatSharp.Common/Properties/AssemblyProperties.cs b/DisCatSharp.Common/Properties/AssemblyProperties.cs
index 4fa7e4f3a..36ebee4ec 100644
--- a/DisCatSharp.Common/Properties/AssemblyProperties.cs
+++ b/DisCatSharp.Common/Properties/AssemblyProperties.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DisCatSharp.ApplicationCommands")]
[assembly: InternalsVisibleTo("DisCatSharp.CommandsNext")]
+[assembly: InternalsVisibleTo("DisCatSharp.Experimental")]
[assembly: InternalsVisibleTo("DisCatSharp")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.DependencyInjection")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Interactivity")]
[assembly: InternalsVisibleTo("DisCatSharp.Lavalink")]
[assembly: InternalsVisibleTo("DisCatSharp.Phabricator")]
[assembly: InternalsVisibleTo("DisCatSharp.Support")]
[assembly: InternalsVisibleTo("DisCatSharp.Test")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")]
[assembly: InternalsVisibleTo("Nyaw")]
[assembly: InternalsVisibleTo("DisCatSharp.DevTools")]
[assembly: InternalsVisibleTo("DisCatSharp.DocsGenerator")]
[assembly: InternalsVisibleTo("DisCatSharp.StaffApps")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode.Metadata.ManagedReference")]
diff --git a/DisCatSharp.Docs/docfx.json b/DisCatSharp.Docs/docfx.json
index e8ee5c578..648191741 100644
--- a/DisCatSharp.Docs/docfx.json
+++ b/DisCatSharp.Docs/docfx.json
@@ -1,264 +1,266 @@
{
"metadata":[
{
"src":[
{
"src":"../DisCatSharp/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp",
"filter":"filter_config.yml",
"disableDefaultFilter":false
},
{
"src":[
{
"src":"../DisCatSharp.Interactivity/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp.Interactivity",
"filter":"filter_config.yml",
"disableDefaultFilter":false
},
{
"src":[
{
"src":"../DisCatSharp.Common/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp.Common",
"filter":"filter_config.yml",
"disableDefaultFilter":false
},
{
"src":[
{
"src":"../DisCatSharp.Hosting/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp.Hosting",
"filter":"filter_config.yml",
"disableDefaultFilter":false
},
{
"src":[
{
"src":"../DisCatSharp.Configuration/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp.Configuration",
"filter":"filter_config.yml",
"disableDefaultFilter":false
},
{
"src":[
{
"src":"../DisCatSharp.ApplicationCommands/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp.ApplicationCommands",
"filter":"filter_config.yml",
"disableDefaultFilter":false
},
{
"src":[
{
"src":"../DisCatSharp.CommandsNext/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp.CommandsNext",
"filter":"filter_config.yml",
"disableDefaultFilter":false
},
{
"src":[
{
"src":"../DisCatSharp.Hosting.DependencyInjection/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp.Hosting.DependencyInjection",
"filter":"filter_config.yml",
"disableDefaultFilter":false
},
{
"src":[
{
"src":"../DisCatSharp.Lavalink/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp.Lavalink",
"filter":"filter_config.yml",
"disableDefaultFilter":false
},
{
"src":[
{
"src":"../DisCatSharp.VoiceNext/",
"files":[
"**.csproj"
],
"exclude":[
"**/obj/**",
"**/bin/**"
]
}
],
"dest":"api/DisCatSharp.VoiceNext",
"filter":"filter_config.yml",
"disableDefaultFilter":false
}
],
"build":{
"xrefService":[
"https://xref.docs.microsoft.com/query?uid={uid}"
],
"content":[
{
"files":[
"api/**/**.yml",
"api/**/**.md",
"api/**.yml",
"api/**.md"
],
"exclude":[
"libdev/**"
]
},
{
"files":[
"**.md",
"toc.yml",
"faq/**.yml",
"faq/**.md",
"articles/**.yml",
"articles/**.md",
"changelogs/**.yml",
"changelogs/**.md",
"natives/**.yml",
- "natives/**.md"
+ "natives/**.md",
+ "vs/**.yml",
+ "vs/**.md"
],
"exclude":[
"**/bin/**",
"**/obj/**",
"_site/**",
"dcs/**"
]
}
],
"resource":[
{
"files":[
"images/**",
"natives/**.zip",
"./.htaccess"
],
"exclude":[
"**/bin/**",
"**/obj/**",
"_site/**",
"images/_**"
]
}
],
"overwrite":[],
"dest":"_site",
"globalMetadata":{
"_appTitle": "DisCatSharp Docs",
"_appName": "DisCatSharp",
"_appFooter": "Made with ♥ by AITSYS",
"_copyrightFooter": "© Aiko IT Systems. All rights reserved.",
"_enableSearch":true,
"_disableSideFilter": false,
"_enableNewTab":true,
"_disableContribution": false,
"_disableBreadcrumb": false,
"_gitUrlPattern":"git",
"_gitContribute":{
"repo":"https://github.com/Aiko-IT-Systems/DisCatSharp",
"branch":"main"
}
},
"disableGitFeatures":false,
"exportRawModel":true,
"globalMetadataFiles":[],
"fileMetadataFiles":[],
"template":[
"dcs"
],
"postProcessors":[
"ExtractSearchIndex",
"CustomMemberIndexer"
],
"noLangKeyword":false,
"keepFileLink":false,
"cleanupCacheHistory":false,
"sitemap":{
"baseUrl":"https://docs.discatsharp.tech/",
"changefreq":"daily",
"priority":1.0
}
}
}
diff --git a/DisCatSharp.Docs/toc.yml b/DisCatSharp.Docs/toc.yml
index 9e912f0e3..9e3cf73ec 100644
--- a/DisCatSharp.Docs/toc.yml
+++ b/DisCatSharp.Docs/toc.yml
@@ -1,12 +1,14 @@
- name: Articles
href: articles/
- name: Changelogs
href: changelogs/index.md
- name: FAQ
href: faq.md
- name: API Documentation
href: api/index.md
-- name: Library Development Docs
- href: libdev/index.md
- name: Voice Natives
href: natives/index.md
+- name: Visual Studio Tools
+ href: vs/index.md
+- name: Discord API
+ href: https://discord.com/developers/docs/intro
diff --git a/DisCatSharp.Docs/vs/analyzer/dcs/0001.md b/DisCatSharp.Docs/vs/analyzer/dcs/0001.md
new file mode 100644
index 000000000..2a87a94d3
--- /dev/null
+++ b/DisCatSharp.Docs/vs/analyzer/dcs/0001.md
@@ -0,0 +1,10 @@
+---
+uid: vs_analyzer_dcs_0001
+title: Visual Studio Tools
+---
+
+# DisCatSharp Analyzer Rule DCS0001
+
+You try to use an experimental field, function or similar.
+This is mostly not stable and can change at any time.
+It's recommended to not use this in production.
diff --git a/DisCatSharp.Docs/vs/index.md b/DisCatSharp.Docs/vs/index.md
new file mode 100644
index 000000000..fc43c315f
--- /dev/null
+++ b/DisCatSharp.Docs/vs/index.md
@@ -0,0 +1,15 @@
+---
+uid: vs
+title: Visual Studio Tools
+---
+
+## Visual Studio Tools
+This section contains information on how to use the Visual Studio tools for DisCatSharp.
+
+### Analyzers
+
+
+#### DisCatSharp Analyzer
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+DCS0001 | Information | Warning | Experimental Attribute Analyzer
diff --git a/DisCatSharp.Docs/vs/toc.yml b/DisCatSharp.Docs/vs/toc.yml
new file mode 100644
index 000000000..a5021d011
--- /dev/null
+++ b/DisCatSharp.Docs/vs/toc.yml
@@ -0,0 +1,8 @@
+- name: Visual Studio Tools
+ href: index.md
+- name: Analyzer
+ items:
+ - name: DisCatSharp Analyzer
+ items:
+ - name: DCS0001
+ href: analyzer/dcs/0001.md
diff --git a/DisCatSharp.Experimental/DisCatSharp.Experimental.csproj b/DisCatSharp.Experimental/DisCatSharp.Experimental.csproj
new file mode 100644
index 000000000..5d862b3fb
--- /dev/null
+++ b/DisCatSharp.Experimental/DisCatSharp.Experimental.csproj
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+ DisCatSharp.Experimental
+ DisCatSharp.Experimental
+
+
+
+ DisCatSharp.Experimental
+
+ DisCatSharp.Experimental
+
+ Experimental changes for DisCatSharp.
+
+ DisCatSharp,Experimental,Discord API Wrapper,Discord,Bots,Discord Bots,AITSYS,Net6
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
diff --git a/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs b/DisCatSharp.Experimental/DisCatSharp.cs
similarity index 72%
copy from DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs
copy to DisCatSharp.Experimental/DisCatSharp.cs
index ad6586e29..bff0c2995 100644
--- a/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs
+++ b/DisCatSharp.Experimental/DisCatSharp.cs
@@ -1,49 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace DisCatSharp.Enums
+using DisCatSharp;
+using DisCatSharp.Attributes;
+using DisCatSharp.Entities;
+using DisCatSharp.Enums;
+using DisCatSharp.EventArgs;
+using DisCatSharp.Exceptions;
+
+namespace DisCatSharp.Experimental
{
- public enum AutomodActionType : int
+ public static class DisCatSharp
{
- ///
- /// Blocks the content of a message according to the rule.
- ///
- BlockMessage = 1,
-
- ///
- /// Logs user to a specified channel.
- ///
- SendAlertMessage = 2,
-
- ///
- /// Timeout user for a specified duration.
- /// Only valid for Keyword and MentionSpam rules
- ///
- Timeout = 3
+ [Experimental("This function is being tested and might change at any time.")]
+ public static async Task GetUsernameAsync(this DiscordClient client, ulong id)
+ {
+ var user = await client.ApiClient.GetUserAsync(id);
+ return user.UsernameWithDiscriminator;
+ }
}
}
diff --git a/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs b/DisCatSharp.Experimental/DiscordApiClientHook.cs
similarity index 75%
copy from DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs
copy to DisCatSharp.Experimental/DiscordApiClientHook.cs
index ad6586e29..4a5f3ab87 100644
--- a/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs
+++ b/DisCatSharp.Experimental/DiscordApiClientHook.cs
@@ -1,49 +1,42 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace DisCatSharp.Enums
+using DisCatSharp.Net;
+
+namespace DisCatSharp.Experimental
{
- public enum AutomodActionType : int
+ internal class DiscordApiClientHook
{
- ///
- /// Blocks the content of a message according to the rule.
- ///
- BlockMessage = 1,
-
- ///
- /// Logs user to a specified channel.
- ///
- SendAlertMessage = 2,
+ internal DiscordApiClient ApiClient { get; set; }
- ///
- /// Timeout user for a specified duration.
- /// Only valid for Keyword and MentionSpam rules
- ///
- Timeout = 3
+ public DiscordApiClientHook(DiscordApiClient apiClient)
+ {
+ this.ApiClient = apiClient;
+ }
}
}
diff --git a/DisCatSharp.Experimental/GlobalSuppressions.cs b/DisCatSharp.Experimental/GlobalSuppressions.cs
new file mode 100644
index 000000000..c09607e30
--- /dev/null
+++ b/DisCatSharp.Experimental/GlobalSuppressions.cs
@@ -0,0 +1,8 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("Style", "IDE0022:Use expression body for methods", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Experimental.Program.Main(System.String[])")]
diff --git a/DisCatSharp.Experimental/Program.cs b/DisCatSharp.Experimental/Program.cs
new file mode 100644
index 000000000..a0114c871
--- /dev/null
+++ b/DisCatSharp.Experimental/Program.cs
@@ -0,0 +1,36 @@
+using System;
+
+using DisCatSharp.Attributes;
+
+namespace DisCatSharp.Experimental;
+
+internal class Program
+{
+ internal static void Main(string[] args = null)
+ {
+ var test = new Test("test");
+ test.Invoke();
+ var str = test.TestString;
+ }
+}
+
+[Experimental("class")]
+internal class Test
+{
+ [Experimental("property")]
+ internal string TestString { get; set; }
+
+ [Experimental("construct")]
+ internal Test([Experimental("test arg")] string test = null)
+ {
+ this.TestString = test;
+ }
+
+ [Experimental("construct 2")]
+ internal Test()
+ { }
+
+ [Experimental("method")]
+ internal void Invoke()
+ => Console.WriteLine(this.TestString);
+}
diff --git a/DisCatSharp.Interactivity/Properties/AssemblyProperties.cs b/DisCatSharp.Interactivity/Properties/AssemblyProperties.cs
index 1eca4586b..a94e719fd 100644
--- a/DisCatSharp.Interactivity/Properties/AssemblyProperties.cs
+++ b/DisCatSharp.Interactivity/Properties/AssemblyProperties.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DisCatSharp.ApplicationCommands")]
[assembly: InternalsVisibleTo("DisCatSharp.CommandsNext")]
+[assembly: InternalsVisibleTo("DisCatSharp.Experimental")]
[assembly: InternalsVisibleTo("DisCatSharp.Common")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.DependencyInjection")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp")]
[assembly: InternalsVisibleTo("DisCatSharp.Lavalink")]
[assembly: InternalsVisibleTo("DisCatSharp.Phabricator")]
[assembly: InternalsVisibleTo("DisCatSharp.Support")]
[assembly: InternalsVisibleTo("DisCatSharp.Test")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")]
[assembly: InternalsVisibleTo("Nyaw")]
[assembly: InternalsVisibleTo("DisCatSharp.DevTools")]
[assembly: InternalsVisibleTo("DisCatSharp.DocsGenerator")]
[assembly: InternalsVisibleTo("DisCatSharp.StaffApps")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode.Metadata.ManagedReference")]
diff --git a/DisCatSharp.Lavalink/Entities/LavalinkEqualizerTypes.cs b/DisCatSharp.Lavalink/Entities/LavalinkEqualizerTypes.cs
index c06ba9c94..1d4af2375 100644
--- a/DisCatSharp.Lavalink/Entities/LavalinkEqualizerTypes.cs
+++ b/DisCatSharp.Lavalink/Entities/LavalinkEqualizerTypes.cs
@@ -1,84 +1,84 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace DisCatSharp.Lavalink;
///
/// Represents Lavalink equalizer band adjustment. This is used to alter the sound output by using Lavalink's equalizer.
///
-public struct LavalinkBandAdjustment
+public readonly struct LavalinkBandAdjustment
{
///
/// Gets the ID of the band to adjust.
///
[JsonProperty("band")]
public int BandId { get; }
///
/// Gets the gain of the specified band.
///
[JsonProperty("gain")]
public float Gain { get; }
///
/// Creates a new band adjustment with specified parameters.
///
/// Which band to adjust. Must be in 0-14 range.
/// By how much to adjust the band. Must be greater than or equal to -0.25 (muted), and less than or equal to +1.0. +0.25 means the band is doubled.
public LavalinkBandAdjustment(int bandId, float gain)
{
if (bandId < 0 || bandId > 14)
throw new ArgumentOutOfRangeException(nameof(bandId), "Band ID cannot be lower than 0 or greater than 14.");
if (gain < -0.25 || gain > 1.0)
throw new ArgumentOutOfRangeException(nameof(gain), "Gain cannot be lower than -0.25 or greater than 1.0.");
this.BandId = bandId;
this.Gain = gain;
}
}
///
/// The lavalink band adjustment comparer.
///
internal class LavalinkBandAdjustmentComparer : IEqualityComparer
{
///
/// Whether two band adjustments are equal.
///
/// The first band adjustments.
/// The second band adjustments.
public bool Equals(LavalinkBandAdjustment x, LavalinkBandAdjustment y)
=> x.BandId == y.BandId;
///
/// Gets the hash code.
///
/// The band adjustments.
public int GetHashCode(LavalinkBandAdjustment obj)
=> obj.BandId;
}
diff --git a/DisCatSharp.Lavalink/Properties/AssemblyProperties.cs b/DisCatSharp.Lavalink/Properties/AssemblyProperties.cs
index a6e0712a0..155f91856 100644
--- a/DisCatSharp.Lavalink/Properties/AssemblyProperties.cs
+++ b/DisCatSharp.Lavalink/Properties/AssemblyProperties.cs
@@ -1,45 +1,46 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DisCatSharp.ApplicationCommands")]
[assembly: InternalsVisibleTo("DisCatSharp.CommandsNext")]
[assembly: InternalsVisibleTo("DisCatSharp.Common")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration")]
+[assembly: InternalsVisibleTo("DisCatSharp.Experimental")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.DependencyInjection")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Interactivity")]
[assembly: InternalsVisibleTo("DisCatSharp")]
[assembly: InternalsVisibleTo("DisCatSharp.Phabricator")]
[assembly: InternalsVisibleTo("DisCatSharp.Support")]
[assembly: InternalsVisibleTo("DisCatSharp.Test")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")]
[assembly: InternalsVisibleTo("Nyaw")]
[assembly: InternalsVisibleTo("DisCatSharp.DevTools")]
[assembly: InternalsVisibleTo("DisCatSharp.DocsGenerator")]
[assembly: InternalsVisibleTo("DisCatSharp.StaffApps")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode.Metadata.ManagedReference")]
diff --git a/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/DisCatSharp.Analyzer.VSIX.csproj b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/DisCatSharp.Analyzer.VSIX.csproj
new file mode 100644
index 000000000..e0b719ec3
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/DisCatSharp.Analyzer.VSIX.csproj
@@ -0,0 +1,113 @@
+
+
+
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+ latest
+
+
+
+
+ Debug
+ AnyCPU
+ 2.0
+ {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}
+ Library
+ Properties
+ DisCatSharp.Analyzer
+ DisCatSharp.Analyzer
+ v4.7.2
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ Program
+ $(DevEnvDir)devenv.exe
+ /rootsuffix Exp
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ False
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+ True
+ True
+ source.extension.vsixmanifest
+
+
+
+
+ Always
+ true
+
+
+ Designer
+ VsixManifestGenerator
+ source.extension.cs
+
+
+ Always
+ true
+
+
+
+
+
+
+
+
+
+
+ compile; build; native; contentfiles; analyzers; buildtransitive
+
+
+ 10.3.1
+
+
+ 17.4.33103.184
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}
+ DisCatSharp.Analyzer
+ BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3b
+ DebugSymbolsProjectOutputGroup%3b
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/LICENSE.md b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/LICENSE.md
new file mode 100644
index 000000000..980420ad1
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/LICENSE.md
@@ -0,0 +1,346 @@
+The MIT License (MIT)
+
+Copyright (c) 2021-2022 Aiko IT Systems
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+# Package licenses for DisCatSharp
+Parts of this package are not provided under DisCatSharp's license. Said
+licenses are listed here.
+
+
+## Package base
+[Original license reading](https://github.com/DSharpPlus/DSharpPlus/blob/master/LICENSE)
+```
+The MIT License (MIT)
+
+Copyright (c) 2015 Mike Santiago
+
+Copyright (c) 2016-2021 DSharpPlus Development Team
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+```
+
+## Opus
+[Original license reading](https://github.com/xiph/opus/blob/master/COPYING)
+
+```
+Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic,
+ Jean-Marc Valin, Timothy B. Terriberry,
+ CSIRO, Gregory Maxwell, Mark Borgerding,
+ Erik de Castro Lopo
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific contributors, may be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Opus is subject to the royalty-free patent licenses which are
+specified at:
+
+Xiph.Org Foundation:
+https://datatracker.ietf.org/ipr/1524/
+
+Microsoft Corporation:
+https://datatracker.ietf.org/ipr/1914/
+
+Broadcom Corporation:
+https://datatracker.ietf.org/ipr/1526/
+```
+
+## Libsodium
+[Original license reading](https://github.com/jedisct1/libsodium/blob/master/LICENSE)
+
+```
+/*
+ * ISC License
+ *
+ * Copyright (c) 2013-2020
+ * Frank Denis
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+```
+
+
+## DisCatSharp.Common
+```
+Copyright 2021 Aiko IT Systems
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+```
+
+## DisCatSharp.Common Source
+[Original license reading](https://github.com/Emzi0767/Common/blob/master/LICENSE.TXT)
+
+```
+Copyright 2020-2021 Emzi0767
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-
+
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+```
diff --git a/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/Properties/AssemblyInfo.cs b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..be0af4760
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/Properties/AssemblyInfo.cs
@@ -0,0 +1,23 @@
+using DisCatSharp.Analyzer;
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("DisCatSharp Analyzer")]
+[assembly: AssemblyDescription("DisCatSharp Analyzer Extension")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("AITSYS")]
+[assembly: AssemblyProduct("DisCatSharp.Analyzer")]
+[assembly: AssemblyCopyright("2022 AITSYS")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+[assembly: AssemblyVersion("1.0")]
+[assembly: AssemblyFileVersion("1.0")]
+
+namespace System.Runtime.CompilerServices
+{
+ public class IsExternalInit { }
+}
diff --git a/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/logobig.png b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/logobig.png
new file mode 100644
index 000000000..3c47e76d7
Binary files /dev/null and b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/logobig.png differ
diff --git a/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/source.extension.cs b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/source.extension.cs
new file mode 100644
index 000000000..7415ffb63
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/source.extension.cs
@@ -0,0 +1,18 @@
+// ------------------------------------------------------------------------------
+//
+// This file was generated by VSIX Synchronizer
+//
+// ------------------------------------------------------------------------------
+namespace DisCatSharp.Analyzer
+{
+ internal sealed partial class Vsix
+ {
+ public const string Id = "DisCatSharp.Analyzer.a1051cd0-d47d-4397-9917-6a6b45737ea9";
+ public const string Name = "DisCatSharp Analyzer";
+ public const string Description = @"DisCatSharp Analyzer Extension for Development";
+ public const string Language = "en-US";
+ public const string Version = "4.8";
+ public const string Author = "AITSYS";
+ public const string Tags = "DisCatSharp,Analyzer";
+ }
+}
diff --git a/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/source.extension.vsixmanifest b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/source.extension.vsixmanifest
new file mode 100644
index 000000000..b781c00ac
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analytics.VSIX/source.extension.vsixmanifest
@@ -0,0 +1,29 @@
+
+
+
+
+ DisCatSharp Analyzer
+ DisCatSharp Analyzer Extension for Development
+ LICENSE.md
+ logobig.png
+ logobig.png
+ DisCatSharp,Analyzer
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/.editorconfig b/DisCatSharp.Tools/DisCatSharp.Analyzer/.editorconfig
new file mode 100644
index 000000000..c621d4802
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/.editorconfig
@@ -0,0 +1,33 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+#### Core EditorConfig Options ####
+
+# All files
+[*]
+
+# General
+charset = utf-8
+trim_trailing_whitespace = true
+
+# Indentation and spacing
+indent_size = 4
+indent_style =tab
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = true
+dotnet_style_readonly_field=true:suggestion
+
+# Project files
+[*.{csproj,targets,yml}]
+indent_size = 2
+
+[NuGet.config]
+indent_size = 2
+
+# Solution files
+[*.sln]
+indent_style = tab
+tab_width = 4
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/DisCatSharp.Analyzer.Package.csproj b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/DisCatSharp.Analyzer.Package.csproj
new file mode 100644
index 000000000..e6f4e5765
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/DisCatSharp.Analyzer.Package.csproj
@@ -0,0 +1,91 @@
+
+
+
+ netstandard2.0
+ false
+ true
+ true
+
+
+
+ DisCatSharp.Analyzer.Roselyn
+ 4.8.0
+ 4.8.0
+ AITSYS
+ false
+ DisCatSharp Analyzer
+ Various DisCatSharp Analyzers.
+ Copyright 2022 AITSYS
+ Analyzer, analyzers
+ true
+ true
+
+ $(TargetsForTfmSpecificContentInPackage);AddNuGetDlls;_AddAnalyzersToOutput
+
+ false
+
+ DisCatSharp Analyzer
+
+ logobig.png
+
+ README.md
+
+ git
+
+ https://github.com/Aiko-IT-Systems/DisCatSharp
+
+ https://github.com/Aiko-IT-Systems/DisCatSharp
+
+
+
+
+ True
+ \
+
+
+ True
+ \
+
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_PackagesToPack Remove="@(_PackagesToPack)" Condition="%(NuGetPackageId) == 'NETStandard.Library'" />
+ <_PackagesToPack Remove="@(_PackagesToPack)" Condition="%(NuGetPackageId) == 'Microsoft.CodeAnalysis.CSharp.Workspaces'" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/README.md b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/README.md
new file mode 100644
index 000000000..3a0007013
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/README.md
@@ -0,0 +1,3 @@
+# DisCatSharp.Analyzer.Roselyn
+
+Analyzer tool for building bots with DisCatSharp
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/tools/install.ps1 b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/tools/install.ps1
new file mode 100644
index 000000000..f9bd7896a
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/tools/install.ps1
@@ -0,0 +1,272 @@
+param($installPath, $toolsPath, $package, $project)
+
+if($project.Object.SupportsPackageDependencyResolution)
+{
+ if($project.Object.SupportsPackageDependencyResolution())
+ {
+ # Do not install analyzers via install.ps1, instead let the project system handle it.
+ return
+ }
+}
+
+$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ if (Test-Path $analyzersPath)
+ {
+ # Install the language agnostic analyzers.
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+
+# $project.Type gives the language name like (C# or VB.NET)
+$languageFolder = ""
+if($project.Type -eq "C#")
+{
+ $languageFolder = "cs"
+}
+if($project.Type -eq "VB.NET")
+{
+ $languageFolder = "vb"
+}
+if($languageFolder -eq "")
+{
+ return
+}
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Install language specific analyzers.
+ $languageAnalyzersPath = join-path $analyzersPath $languageFolder
+ if (Test-Path $languageAnalyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+# SIG # Begin signature block
+# MIInugYJKoZIhvcNAQcCoIInqzCCJ6cCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA/i+qRUHsWzI0s
+# FVk99zLgt/HOEQ33uvkFsWtHTHZgf6CCDYEwggX/MIID56ADAgECAhMzAAACUosz
+# qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
+# bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+# AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I
+# sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O
+# L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA
+# v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o
+# RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8
+# q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
+# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw
+# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
+# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu
+# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
+# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
+# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
+# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
+# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3
+# uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp
+# kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7
+# l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u
+# TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1
+# o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti
+# yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z
+# 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf
+# 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK
+# WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW
+# esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F
+# 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS
+# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
+# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
+# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
+# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
+# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
+# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
+# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
+# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
+# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
+# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
+# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
+# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
+# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
+# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
+# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
+# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
+# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
+# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
+# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
+# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
+# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
+# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
+# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
+# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
+# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
+# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
+# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
+# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
+# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
+# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
+# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
+# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
+# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
+# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
+# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
+# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
+# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
+# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
+# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
+# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZjzCCGYsCAQEwgZUwfjELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
+# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN
+# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
+# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgRjg7DcI6
+# uhYfXWwAQ6hK0mPW7iyr2tzHR0DHSDJkscIwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
+# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
+# BgkqhkiG9w0BAQEFAASCAQB3ERGpqvGnJrsyU0d9lERK2TJW4/OONhZAFjxrEvEk
+# PzdH0Fk0otvagAvjHzJ3q0G8C7gwRbXIyGgiYYIMefheNvgd/UKnubUGEzeG9h0/
+# biX5Ro1mxuHBYvc3vqvWD292jXMg00iRmexDsTny8YgSAAWsTdkE8/W2ooEfbG1T
+# QkCg6ds9btpA1D1znVYpEbviCJoAfHLbNBr5nzAadgWjQM8nnb3UTvmLDIs5b1LO
+# 3lm9w485IBFRnfrj6QinVsCbSD7PU/N1hPY7rKfM9ScZC6QT6kjyuVVa1Ft+VYLH
+# qlV9hE6B4CGeB8qkko4x+MKovgbdpCgYz3eePWCakZywoYIXGTCCFxUGCisGAQQB
+# gjcDAwExghcFMIIXAQYJKoZIhvcNAQcCoIIW8jCCFu4CAQMxDzANBglghkgBZQME
+# AgEFADCCAVkGCyqGSIb3DQEJEAEEoIIBSASCAUQwggFAAgEBBgorBgEEAYRZCgMB
+# MDEwDQYJYIZIAWUDBAIBBQAEIC58WTh4Q8r6c2kVXmD8xoHEhya2jc6YZ43KUAIy
+# flB4AgZh/WKJ50gYEzIwMjIwMjExMTkwMzQwLjE1M1owBIACAfSggdikgdUwgdIx
+# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
+# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1p
+# Y3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhh
+# bGVzIFRTUyBFU046M0JENC00QjgwLTY5QzMxJTAjBgNVBAMTHE1pY3Jvc29mdCBU
+# aW1lLVN0YW1wIFNlcnZpY2WgghFoMIIHFDCCBPygAwIBAgITMwAAAYm0v4YwhBxL
+# jwABAAABiTANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
+# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
+# IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg
+# MjAxMDAeFw0yMTEwMjgxOTI3NDFaFw0yMzAxMjYxOTI3NDFaMIHSMQswCQYDVQQG
+# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
+# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQg
+# SXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg
+# RVNOOjNCRDQtNEI4MC02OUMzMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt
+# cCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvQZXxZFm
+# a6plmuOyvNpV8xONOwcYolZG/BjyZWGSk5JOGaLyrKId5VxVHWHlsmJE4Svnzsdp
+# sKmVx8otONveIUFvSceEZp8VXmu5m1fu8L7c+3lwXcibjccqtEvtQslokQVx0r+L
+# 54abrNDarwFG73IaRidIS1i9c+unJ8oYyhDRLrCysFAVxyQhPNZkWK7Z8/VGukaK
+# LAWHXCh/+R53h42gFL+9/mAALxzCXXuofi8f/XKCm7xNwVc1hONCCz6oq94AufzV
+# NkkIW4brUQgYpCcJm9U0XNmQvtropYDn9UtY8YQ0NKenXPtdgLHdQ8Nnv3igErKL
+# rWI0a5n5jjdKfwk+8mvakqdZmlOseeOS1XspQNJAK1uZllAITcnQZOcO5ofjOQ33
+# ujWckAXdz+/x3o7l4AU/TSOMzGZMwhUdtVwC3dSbItpSVFgnjM2COEJ9zgCadvOi
+# rGDLN471jZI2jClkjsJTdgPk343TQA4JFvds/unZq0uLr+niZ3X44OBx2x+gVlln
+# 2c4UbZXNueA4yS1TJGbbJFIILAmTUA9Auj5eISGTbNiyWx79HnCOTar39QEKozm4
+# LnTmDXy0/KI/H/nYZGKuTHfckP28wQS06rD+fDS5xLwcRMCW92DkHXmtbhGyRilB
+# OL5LxZelQfxt54wl4WUC0AdAEolPekODwO8CAwEAAaOCATYwggEyMB0GA1UdDgQW
+# BBSXbx+zR1p4IIAeguA6rHKkrfl7UDAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJl
+# pxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
+# b20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAx
+# MCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3
+# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3Rh
+# bXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG
+# CCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4ICAQCOtLdpWUI4KwfLLrfaKrLB92Dq
+# bAspGWM41TaO4Jl+sHxPo522uu3GKQCjmkRWreHtlfyy9kOk7LWax3k3ke8Gtfet
+# fbh7qH0LeV2XOWg39BOnHf6mTcZq7FYSZZch1JDQjc98+Odlow+oWih0Dbt4CV/e
+# 19ZcE+1n1zzWkskUEd0f5jPIUis33p+vkY8szduAtCcIcPFUhI8Hb5alPUAPMjGz
+# wKb7NIKbnf8j8cP18As5IveckF0oh1cw63RY/vPK62LDYdpi7WnG2ObvngfWVKtw
+# iwTI4jHj2cO9q37HDe/PPl216gSpUZh0ap24mKmMDfcKp1N4mEdsxz4oseOrPYeF
+# sHHWJFJ6Aivvqn70KTeJpp5r+DxSqbeSy0mxIUOq/lAaUxgNSQVUX26t8r+fciko
+# fKv23WHrtRV3t7rVTsB9YzrRaiikmz68K5HWdt9MqULxPQPo+ppZ0LRqkOae466+
+# UKRY0JxWtdrMc5vHlHZfnqjawj/RsM2S6Q6fa9T9CnY1Nz7DYBG3yZJyCPFsrgU0
+# 5s9ljqfsSptpFdUh9R4ce+L71SWDLM2x/1MFLLHAMbXsEp8KloEGtaDULnxtfS2t
+# YhfuKGqRXoEfDPAMnIdTvQPh3GHQ4SjkkBARHL0MY75alhGTKHWjC2aLVOo8obKI
+# Bk8hfnFDUf/EyVw4uTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUw
+# DQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
+# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
+# YXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhv
+# cml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
+# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+# ggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg
+# 4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aO
+# RmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41
+# JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5
+# LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL
+# 64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9
+# QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj
+# 0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqE
+# UUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0
+# kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435
+# UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB
+# 3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTE
+# mr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwG
+# A1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93
+# d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNV
+# HSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNV
+# HQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo
+# 0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29m
+# dC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5j
+# cmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jv
+# c29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDAN
+# BgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4
+# sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th54
+# 2DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRX
+# ud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBew
+# VIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0
+# DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+Cljd
+# QDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFr
+# DZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFh
+# bHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7n
+# tdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+
+# oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6Fw
+# ZvKhggLXMIICQAIBATCCAQChgdikgdUwgdIxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
+# ZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
+# dGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046M0JENC00Qjgw
+# LTY5QzMxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoB
+# ATAHBgUrDgMCGgMVACGlCa3ketyeuey7bJNpWkMuiCcQoIGDMIGApH4wfDELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
+# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDlsRtBMCIY
+# DzIwMjIwMjEyMDEyODMzWhgPMjAyMjAyMTMwMTI4MzNaMHcwPQYKKwYBBAGEWQoE
+# ATEvMC0wCgIFAOWxG0ECAQAwCgIBAAICDbMCAf8wBwIBAAICEW8wCgIFAOWybMEC
+# AQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEK
+# MAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQCImCpEJ2AlAWBBkDABmkqIh1kM
+# LPDyea3b7evhOk+YSwXCzxnBIXuppujFT3tnk7w0p0a5YS9uwqbDM/M6rAUMBAR0
+# boHamumEITNF5nVh0rlYyRZQ3WraVD2YPhouUINQavmS8ueYoh6r3HeM9QPBAnNB
+# vv7GDrZ637+2Dfe60jGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
+# IFBDQSAyMDEwAhMzAAABibS/hjCEHEuPAAEAAAGJMA0GCWCGSAFlAwQCAQUAoIIB
+# SjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIL86
+# iebNndOm+CAgIp67s6y+HI1wHdhaMPILGf48RtXXMIH6BgsqhkiG9w0BCRACLzGB
+# 6jCB5zCB5DCBvQQgZndHMdxQV1VsbpWHOTHqWEycvcRJm7cY69l/UmT8j0UwgZgw
+# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
+# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
+# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAYm0v4YwhBxL
+# jwABAAABiTAiBCDET+l3keOFFxaIqOZWSSuWNO774Ng/t5pe3p4QXoKcvjANBgkq
+# hkiG9w0BAQsFAASCAgADYrNFej7RbihwGcC0jF+cTik+HJog++dPEDXeIyBB+2pw
+# 23hC5KaX9H05ZknluIq2oxf2MLpKL+gA+76T3k5PnzPNJFDogUn5eFIIsMRpNF0h
+# MtPWoPJWYFK2odvKz1HwsuqHRg6hO//NwORcv4xPeAWEFO5+DOXzZKKp/BVDGe/D
+# c++y9/l41qpz/F2c3a1lugdqnZz7ZeoaQ8/JMlwrmMbciqcAytCn9A59EWJ1xYd/
+# DaDhQ5Rd8hkcckuxJksjWf6URmc91cb4Jdatkyupq3dDGwCkjGNd2xetrOpqMLOZ
+# quoDONSgc9rGrhkf3xgKKVRhLg9bxd3f2oQ0IsOBg2AC5td1eqp6TILc0gei2E3I
+# uEAW1d+KXDnajvQmvQkaFHr5wEocTTLgrDglOPPhEaEumSTJS7jKFzUKHiBU005p
+# CgQ1So2WJ2RqFx0ppez1N1AFczOVLFllK3WGPLkDsN1GgT0nFfoqvs1WKkzyb2d2
+# /v6PVER9xGky7LCu62dhsJCAFUbxF2dJxaC5ofrl98VaO/z72J9on9BTz+eCtcJ9
+# rDIpqktGeL02f6+4zctFCyi2wgm6eh8kKvRlAPmN4/MNt9pWHtEV//xFGzGeDajr
+# diRhDoMZwsuon4QwS8b2YcKMoZ6gZ2lZah3960fTTmvBTBNqeBtR94KWCy0C0A==
+# SIG # End signature block
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/tools/uninstall.ps1 b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/tools/uninstall.ps1
new file mode 100644
index 000000000..17fd92001
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer.Package/tools/uninstall.ps1
@@ -0,0 +1,279 @@
+param($installPath, $toolsPath, $package, $project)
+
+if($project.Object.SupportsPackageDependencyResolution)
+{
+ if($project.Object.SupportsPackageDependencyResolution())
+ {
+ # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it.
+ return
+ }
+}
+
+$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Uninstall the language agnostic analyzers.
+ if (Test-Path $analyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
+ }
+ }
+ }
+}
+
+# $project.Type gives the language name like (C# or VB.NET)
+$languageFolder = ""
+if($project.Type -eq "C#")
+{
+ $languageFolder = "cs"
+}
+if($project.Type -eq "VB.NET")
+{
+ $languageFolder = "vb"
+}
+if($languageFolder -eq "")
+{
+ return
+}
+
+foreach($analyzersPath in $analyzersPaths)
+{
+ # Uninstall language specific analyzers.
+ $languageAnalyzersPath = join-path $analyzersPath $languageFolder
+ if (Test-Path $languageAnalyzersPath)
+ {
+ foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll)
+ {
+ if($project.Object.AnalyzerReferences)
+ {
+ try
+ {
+ $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
+ }
+ catch
+ {
+
+ }
+ }
+ }
+ }
+}
+# SIG # Begin signature block
+# MIInugYJKoZIhvcNAQcCoIInqzCCJ6cCAQExDzANBglghkgBZQMEAgEFADB5Bgor
+# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
+# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDC68wb97fg0QGL
+# yXrxJhYfmibzcOh8caqC0uZprfczDaCCDYEwggX/MIID56ADAgECAhMzAAACUosz
+# qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
+# bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw
+# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
+# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+# AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I
+# sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O
+# L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA
+# v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o
+# RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8
+# q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
+# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw
+# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
+# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu
+# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
+# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
+# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
+# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
+# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3
+# uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp
+# kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7
+# l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u
+# TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1
+# o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti
+# yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z
+# 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf
+# 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK
+# WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW
+# esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F
+# 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS
+# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
+# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
+# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
+# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
+# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
+# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
+# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
+# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
+# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
+# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
+# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
+# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
+# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
+# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
+# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
+# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
+# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
+# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
+# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
+# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
+# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
+# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
+# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
+# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
+# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
+# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
+# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
+# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
+# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
+# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
+# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
+# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
+# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
+# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
+# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
+# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
+# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
+# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
+# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
+# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZjzCCGYsCAQEwgZUwfjELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
+# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN
+# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
+# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgF1ypFyzl
+# AvvWGVCeXczrfpXmJNm9vpyjcwd4y4ivfqowQgYKKwYBBAGCNwIBDDE0MDKgFIAS
+# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
+# BgkqhkiG9w0BAQEFAASCAQAvi2rSDkhC82RJ4uqq/0WbHkOkzq1hrF6HxneBTNj8
+# KX+niFtee3CYVfWaSAQ6xvOiLupRX3fsSfhabRQ+Jl8k28voGrTK1OC906OO3tUN
+# jdmv1PooWdxJNt2EbzQrap5Ui9KTUv4mJ4c836HAVMBPCJiq5NwmzAHfbgBxCaYq
+# +hupIf+gk8vuNB1bltILgNmU/smJt9OuGqSo5TrFajzb+35SqjnCz9JtAtbPNZvA
+# X9N37UPhITOecceAQmrHiEPbA7eu6VDp6VPjPfCEO7a+frWa83chEd4qzyou9xu5
+# 3gnj7Ro8nFDnGyUe0+0oCaYGXO9fbIMN1HG2IZg5suj5oYIXGTCCFxUGCisGAQQB
+# gjcDAwExghcFMIIXAQYJKoZIhvcNAQcCoIIW8jCCFu4CAQMxDzANBglghkgBZQME
+# AgEFADCCAVkGCyqGSIb3DQEJEAEEoIIBSASCAUQwggFAAgEBBgorBgEEAYRZCgMB
+# MDEwDQYJYIZIAWUDBAIBBQAEIH+XBTHuyyHZnIXrFWIe64WLvHx5GUFMCM6A56T1
+# KwBtAgZh/WKJ52UYEzIwMjIwMjExMTkwMzQwLjU0OFowBIACAfSggdikgdUwgdIx
+# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
+# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1p
+# Y3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhh
+# bGVzIFRTUyBFU046M0JENC00QjgwLTY5QzMxJTAjBgNVBAMTHE1pY3Jvc29mdCBU
+# aW1lLVN0YW1wIFNlcnZpY2WgghFoMIIHFDCCBPygAwIBAgITMwAAAYm0v4YwhBxL
+# jwABAAABiTANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
+# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
+# IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg
+# MjAxMDAeFw0yMTEwMjgxOTI3NDFaFw0yMzAxMjYxOTI3NDFaMIHSMQswCQYDVQQG
+# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
+# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQg
+# SXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg
+# RVNOOjNCRDQtNEI4MC02OUMzMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt
+# cCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvQZXxZFm
+# a6plmuOyvNpV8xONOwcYolZG/BjyZWGSk5JOGaLyrKId5VxVHWHlsmJE4Svnzsdp
+# sKmVx8otONveIUFvSceEZp8VXmu5m1fu8L7c+3lwXcibjccqtEvtQslokQVx0r+L
+# 54abrNDarwFG73IaRidIS1i9c+unJ8oYyhDRLrCysFAVxyQhPNZkWK7Z8/VGukaK
+# LAWHXCh/+R53h42gFL+9/mAALxzCXXuofi8f/XKCm7xNwVc1hONCCz6oq94AufzV
+# NkkIW4brUQgYpCcJm9U0XNmQvtropYDn9UtY8YQ0NKenXPtdgLHdQ8Nnv3igErKL
+# rWI0a5n5jjdKfwk+8mvakqdZmlOseeOS1XspQNJAK1uZllAITcnQZOcO5ofjOQ33
+# ujWckAXdz+/x3o7l4AU/TSOMzGZMwhUdtVwC3dSbItpSVFgnjM2COEJ9zgCadvOi
+# rGDLN471jZI2jClkjsJTdgPk343TQA4JFvds/unZq0uLr+niZ3X44OBx2x+gVlln
+# 2c4UbZXNueA4yS1TJGbbJFIILAmTUA9Auj5eISGTbNiyWx79HnCOTar39QEKozm4
+# LnTmDXy0/KI/H/nYZGKuTHfckP28wQS06rD+fDS5xLwcRMCW92DkHXmtbhGyRilB
+# OL5LxZelQfxt54wl4WUC0AdAEolPekODwO8CAwEAAaOCATYwggEyMB0GA1UdDgQW
+# BBSXbx+zR1p4IIAeguA6rHKkrfl7UDAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJl
+# pxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
+# b20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAx
+# MCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3
+# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3Rh
+# bXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG
+# CCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4ICAQCOtLdpWUI4KwfLLrfaKrLB92Dq
+# bAspGWM41TaO4Jl+sHxPo522uu3GKQCjmkRWreHtlfyy9kOk7LWax3k3ke8Gtfet
+# fbh7qH0LeV2XOWg39BOnHf6mTcZq7FYSZZch1JDQjc98+Odlow+oWih0Dbt4CV/e
+# 19ZcE+1n1zzWkskUEd0f5jPIUis33p+vkY8szduAtCcIcPFUhI8Hb5alPUAPMjGz
+# wKb7NIKbnf8j8cP18As5IveckF0oh1cw63RY/vPK62LDYdpi7WnG2ObvngfWVKtw
+# iwTI4jHj2cO9q37HDe/PPl216gSpUZh0ap24mKmMDfcKp1N4mEdsxz4oseOrPYeF
+# sHHWJFJ6Aivvqn70KTeJpp5r+DxSqbeSy0mxIUOq/lAaUxgNSQVUX26t8r+fciko
+# fKv23WHrtRV3t7rVTsB9YzrRaiikmz68K5HWdt9MqULxPQPo+ppZ0LRqkOae466+
+# UKRY0JxWtdrMc5vHlHZfnqjawj/RsM2S6Q6fa9T9CnY1Nz7DYBG3yZJyCPFsrgU0
+# 5s9ljqfsSptpFdUh9R4ce+L71SWDLM2x/1MFLLHAMbXsEp8KloEGtaDULnxtfS2t
+# YhfuKGqRXoEfDPAMnIdTvQPh3GHQ4SjkkBARHL0MY75alhGTKHWjC2aLVOo8obKI
+# Bk8hfnFDUf/EyVw4uTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUw
+# DQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
+# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
+# YXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhv
+# cml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
+# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+# ggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg
+# 4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aO
+# RmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41
+# JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5
+# LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL
+# 64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9
+# QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj
+# 0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqE
+# UUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0
+# kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435
+# UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB
+# 3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTE
+# mr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwG
+# A1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93
+# d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNV
+# HSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNV
+# HQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo
+# 0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29m
+# dC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5j
+# cmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jv
+# c29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDAN
+# BgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4
+# sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th54
+# 2DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRX
+# ud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBew
+# VIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0
+# DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+Cljd
+# QDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFr
+# DZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFh
+# bHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7n
+# tdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+
+# oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6Fw
+# ZvKhggLXMIICQAIBATCCAQChgdikgdUwgdIxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
+# ZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
+# dGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046M0JENC00Qjgw
+# LTY5QzMxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoB
+# ATAHBgUrDgMCGgMVACGlCa3ketyeuey7bJNpWkMuiCcQoIGDMIGApH4wfDELMAkG
+# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
+# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
+# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDlsRtBMCIY
+# DzIwMjIwMjEyMDEyODMzWhgPMjAyMjAyMTMwMTI4MzNaMHcwPQYKKwYBBAGEWQoE
+# ATEvMC0wCgIFAOWxG0ECAQAwCgIBAAICDbMCAf8wBwIBAAICEW8wCgIFAOWybMEC
+# AQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEK
+# MAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQCImCpEJ2AlAWBBkDABmkqIh1kM
+# LPDyea3b7evhOk+YSwXCzxnBIXuppujFT3tnk7w0p0a5YS9uwqbDM/M6rAUMBAR0
+# boHamumEITNF5nVh0rlYyRZQ3WraVD2YPhouUINQavmS8ueYoh6r3HeM9QPBAnNB
+# vv7GDrZ637+2Dfe60jGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYD
+# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
+# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
+# IFBDQSAyMDEwAhMzAAABibS/hjCEHEuPAAEAAAGJMA0GCWCGSAFlAwQCAQUAoIIB
+# SjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIKY2
+# Onyhltfi0+oc/UMKaXc0H6Ckw2gGK1/qmjRZNiXnMIH6BgsqhkiG9w0BCRACLzGB
+# 6jCB5zCB5DCBvQQgZndHMdxQV1VsbpWHOTHqWEycvcRJm7cY69l/UmT8j0UwgZgw
+# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
+# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
+# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAYm0v4YwhBxL
+# jwABAAABiTAiBCDET+l3keOFFxaIqOZWSSuWNO774Ng/t5pe3p4QXoKcvjANBgkq
+# hkiG9w0BAQsFAASCAgB7AQ0Dv3muHoNAt+cccMfYk23lHgh8LGBitCSFwu0q7ufv
+# sXkoaIpwW0U0GikWhQoCH0U38SuzVbafg49FiE6ftkjOtiE03PwPYi1S6NSoDdaV
+# kUuvjz3OcuN1IHg3CyLn2dO8xbUlWCUfgoWhI1nax9ch7wT4Sw8RdmGKdYTZoZmq
+# vPXFRtDyZdmJDMDbTql/Brye8oEsDMoYKMmEYhY1t9TlusnWfUbxuBnyMqg/FkBy
+# QF78WFfT8mygMqUGmINxPGT6daxqmq3nfAC2vOtLT4DplNYMEymfDceJzBhb8VCT
+# UHc2CWK0qKT+eqwn30NBkwh//8aNHlXaA9Yq/9k2y+axIGdxFfG+X0stipRRpEXb
+# xCFm7FPD5/S4ddBH829yEZLZ4XTwSZ6YS/d3mFzu5rgZl3UhjOJPXx40GQtUiDP4
+# XQZ/wW3154X/KtTypv62/Hl+CiMUrsO7MXtgwClfbJ3osg+zlpJgdraetVgmAUc1
+# mjz2GCYX7rIliGkAJREKn4rV2MZzuGLEpTjz9dB+1Xp9Ndi9q3jQgs6k3IDIUube
+# YjPFFuPmFWRyi6oPTXmc4ExtTIewPvrOhwQ5q4ysxylkXoTS+UQt94BY2SvR+TMu
+# 6doU+0Y73xsO8Zz+lREh3fjBsDbPAgOV5989X6bmkJEEIwIK8LYgqvyED8XXTg==
+# SIG # End signature block
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/AnalyzerReleases.Shipped.md b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/AnalyzerReleases.Shipped.md
new file mode 100644
index 000000000..37bdb3dc0
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/AnalyzerReleases.Shipped.md
@@ -0,0 +1,43 @@
+; Shipped analyzer releases
+; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
+## Release 4.2
+
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|--------------------
+DcsExp | Information | Warning | Informs if a method or attribute or similar is considered experimental
+
+
+## Release 4.3
+
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|--------------------
+DCS0001 | Information | Warning | Experimental Attribute Analyzer
+
+### Removed Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|--------------------
+DcsExp | Information | Warning | Informs if a method or attribute or similar is considered experimental
+
+
+## Release 4.4
+
+### Changed Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|--------------------
+DCS0001 | Information | Warning | Experimental Attribute Analyzer
+
+
+## Release 4.5
+
+### Changed Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|--------------------
+DCS0001 | Information | Warning | Experimental Attribute Analyzer
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/AnalyzerReleases.Unshipped.md b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/AnalyzerReleases.Unshipped.md
new file mode 100644
index 000000000..7b845f486
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/AnalyzerReleases.Unshipped.md
@@ -0,0 +1,7 @@
+; Unshipped analyzer release
+; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/AttributeAnalyzer.cs b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/AttributeAnalyzer.cs
new file mode 100644
index 000000000..ec46ef5fa
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/AttributeAnalyzer.cs
@@ -0,0 +1,156 @@
+// This file is part of the DisCatSharp project, based off DSharpPlus.
+//
+// Copyright (c) 2021-2022 AITSYS
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+using DisCatSharp.Attributes;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+
+namespace DisCatSharp.Analyzer
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class AttributeAnalyzer : DiagnosticAnalyzer
+ {
+ public const string DiagnosticIdPrefix = "DCS";
+
+ private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), Resources.ResourceManager, typeof(Resources));
+ private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
+ private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), Resources.ResourceManager, typeof(Resources));
+ private const string Category = "Information";
+
+ private static readonly DiagnosticDescriptor ExperimentalRule = new DiagnosticDescriptor(DiagnosticIdPrefix + "0001", Title, MessageFormat, Category, DiagnosticSeverity.Warning, true, Description, "https://docs.discatsharp.tech/vs/analyzer/dcs/0001.html");
+
+ public override ImmutableArray SupportedDiagnostics { get { return ImmutableArray.Create(ExperimentalRule); } }
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterSymbolAction(ExperimentalAnalyzer, SymbolKind.Parameter);
+ context.RegisterSymbolAction(ExperimentalAnalyzer, SymbolKind.Property);
+ context.RegisterSymbolAction(ExperimentalAnalyzer, SymbolKind.NamedType);
+ context.RegisterSymbolAction(ExperimentalAnalyzer, SymbolKind.Method);
+ context.RegisterSymbolAction(ExperimentalAnalyzer, SymbolKind.Field);
+ context.RegisterSymbolAction(ExperimentalAnalyzer, SymbolKind.Event);
+ context.RegisterSyntaxNodeAction(ExperimentalAnalyzer, SyntaxKind.InvocationExpression);
+ context.RegisterSyntaxNodeAction(ExperimentalAnalyzer, SyntaxKind.ObjectCreationExpression);
+ context.RegisterSyntaxNodeAction(ExperimentalAnalyzer, SyntaxKind.FieldDeclaration); // Don't work
+ context.RegisterSyntaxNodeAction(ExperimentalAnalyzer, SyntaxKind.PropertyDeclaration); // Don't work, one of the not working ones should create a report if a property is used. I.e.:
+ /*
+
+ var test = new Test("test");
+ test.Invoke();
+ var str = test.TestString; <- Fire here for TestString
+
+ */
+ }
+ private static void ExperimentalAnalyzer(SyntaxNodeAnalysisContext context)
+ {
+ var invocation = context.Node;
+ var declaration = context.SemanticModel.GetSymbolInfo(invocation, context.CancellationToken).Symbol;
+ if (null == declaration)
+ {
+ Console.WriteLine("Faulty");
+ //context.ReportDiagnostic(Diagnostic.Create(ExperimentalRule, invocation.GetLocation(), "unknown", "unknown", "unknown"));
+ return;
+ }
+ var attributes = declaration.GetAttributes();
+ var attributeData = attributes.FirstOrDefault(attr => IsRequiredAttribute(context.SemanticModel, attr, typeof(ExperimentalAttribute)));
+ if (null == attributeData)
+ {
+ Console.WriteLine("Faulty 2");
+ return;
+ }
+ var name = declaration.Name;
+ var kind = declaration.Kind.ToString();
+ if (name == ".ctor")
+ {
+ name = declaration.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ kind = "Constructor";
+ }
+ var message = GetMessage(attributeData);
+ var diagnostic = Diagnostic.Create(ExperimentalRule, invocation.GetLocation(), kind, name, message);
+ context.ReportDiagnostic(diagnostic);
+
+ }
+
+ private static void ExperimentalAnalyzer(SymbolAnalysisContext context)
+ {
+ Console.WriteLine("Handling " + context.Symbol.Kind.ToString());
+ var syntaxTrees = from x in context.Symbol.Locations
+ where x.IsInSource
+ select x.SourceTree;
+ var declaration = context.Symbol;
+ if (null == declaration)
+ {
+ Console.WriteLine("Faulty");
+ return;
+ }
+ var attributes = declaration.GetAttributes();
+ var attributeData = attributes.FirstOrDefault(attr => IsRequiredAttribute(context.Compilation.GetSemanticModel(syntaxTrees.First()), attr, typeof(ExperimentalAttribute)));
+ if (null == attributeData)
+ {
+ Console.WriteLine("Faulty 2");
+ return;
+ }
+
+ var message = GetMessage(attributeData);
+ var name = declaration.Name;
+ var kind = declaration.Kind.ToString();
+ if (name == ".ctor")
+ {
+ name = declaration.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ kind = "Constructor";
+ }
+ else if (kind == "NamedType")
+ {
+ kind = "Class";
+ }
+ var diagnostic = Diagnostic.Create(ExperimentalRule, context.Symbol.Locations.First(x => x.IsInSource), kind, name, message);
+ context.ReportDiagnostic(diagnostic);
+ }
+
+ static bool IsRequiredAttribute(SemanticModel semanticModel, AttributeData attribute, Type desiredAttributeType)
+ {
+ var desiredTypeNamedSymbol = semanticModel.Compilation.GetTypeByMetadataName(desiredAttributeType.FullName);
+
+ var result = attribute.AttributeClass.Equals(desiredTypeNamedSymbol, SymbolEqualityComparer.Default);
+ return result;
+ }
+
+ static string GetMessage(AttributeData attribute)
+ {
+ if (attribute.ConstructorArguments.Length < 1)
+ {
+ return "Do not use in production.";
+ }
+ return attribute.ConstructorArguments[0].Value as string;
+ }
+ }
+}
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/DisCatSharp.Analyzer.csproj b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/DisCatSharp.Analyzer.csproj
new file mode 100644
index 000000000..43d5c2451
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/DisCatSharp.Analyzer.csproj
@@ -0,0 +1,35 @@
+
+
+
+ netstandard2.0
+ false
+
+
+ *$(MSBuildProjectFile)*
+ AITSYS
+
+
+
+
+
+
+
+ true
+ true
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/Resources.Designer.cs b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/Resources.Designer.cs
new file mode 100644
index 000000000..b4cfed55c
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/Resources.Designer.cs
@@ -0,0 +1,90 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace DisCatSharp.Analyzer {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DisCatSharp.Analyzer.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to This is considered experimental and shouldn't be used in production..
+ ///
+ internal static string AnalyzerDescription {
+ get {
+ return ResourceManager.GetString("AnalyzerDescription", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} '{1}' is considered experimental: {2}.
+ ///
+ internal static string AnalyzerMessageFormat {
+ get {
+ return ResourceManager.GetString("AnalyzerMessageFormat", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Experimental.
+ ///
+ internal static string AnalyzerTitle {
+ get {
+ return ResourceManager.GetString("AnalyzerTitle", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/Resources.resx b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/Resources.resx
new file mode 100644
index 000000000..ff7ebd2fa
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/Resources.resx
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ This is considered experimental and shouldn't be used in production.
+ An optional longer localizable description of the diagnostic.
+
+
+ {0} '{1}' is considered experimental: {2}
+ The format-able message the diagnostic displays.
+
+
+ Experimental
+ The title of the diagnostic.
+
+
\ No newline at end of file
diff --git a/DisCatSharp.Tools/DisCatSharp.Tools.sln b/DisCatSharp.Tools/DisCatSharp.Tools.sln
new file mode 100644
index 000000000..21adeb13b
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.Tools.sln
@@ -0,0 +1,109 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.33103.201
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DisCatSharp.Analyzer", "DisCatSharp.Analyzer", "{122968B7-2707-4FC2-819D-7E90B804A8E4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Analyzer", "DisCatSharp.Analyzer\DisCatSharp.Analyzer\DisCatSharp.Analyzer.csproj", "{DA33A3DA-DD7F-4E16-B503-79581CCEA974}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Analyzer.Package", "DisCatSharp.Analyzer\DisCatSharp.Analyzer.Package\DisCatSharp.Analyzer.Package.csproj", "{B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ref", "Ref", "{C88304B3-5BF3-4F25-A36A-42CC42BB2D3B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Attributes", "..\DisCatSharp.Attributes\DisCatSharp.Attributes.csproj", "{FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Experimental", "..\DisCatSharp.Experimental\DisCatSharp.Experimental.csproj", "{239A4661-461C-4956-8512-2A3537F6A2E8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Analyzer.VSIX", "DisCatSharp.Analytics.VSIX\DisCatSharp.Analyzer.VSIX.csproj", "{E2AAF92A-5655-446D-ACBE-3D4EC88249BE}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F8C18D6C-510D-4D0A-A82E-4A86ED63D10E}"
+ ProjectSection(SolutionItems) = preProject
+ DisCatSharp.ruleset = DisCatSharp.ruleset
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|arm64 = Debug|arm64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|arm64 = Release|arm64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Debug|arm64.Build.0 = Debug|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Debug|x86.Build.0 = Debug|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Release|arm64.ActiveCfg = Release|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Release|arm64.Build.0 = Release|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Release|x86.ActiveCfg = Release|Any CPU
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974}.Release|x86.Build.0 = Release|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Debug|arm64.Build.0 = Debug|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Debug|x86.Build.0 = Debug|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Release|arm64.ActiveCfg = Release|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Release|arm64.Build.0 = Release|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Release|x86.ActiveCfg = Release|Any CPU
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B}.Release|x86.Build.0 = Release|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Debug|arm64.Build.0 = Debug|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Debug|x86.Build.0 = Debug|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Release|arm64.ActiveCfg = Release|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Release|arm64.Build.0 = Release|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Release|x86.ActiveCfg = Release|Any CPU
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7}.Release|x86.Build.0 = Release|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Debug|arm64.Build.0 = Debug|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Debug|x86.Build.0 = Debug|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Release|arm64.ActiveCfg = Release|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Release|arm64.Build.0 = Release|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Release|x86.ActiveCfg = Release|Any CPU
+ {239A4661-461C-4956-8512-2A3537F6A2E8}.Release|x86.Build.0 = Release|Any CPU
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Debug|arm64.ActiveCfg = Debug|arm64
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Debug|arm64.Build.0 = Debug|arm64
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Debug|x86.ActiveCfg = Debug|x86
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Debug|x86.Build.0 = Debug|x86
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Release|arm64.ActiveCfg = Release|arm64
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Release|arm64.Build.0 = Release|arm64
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Release|x86.ActiveCfg = Release|x86
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {DA33A3DA-DD7F-4E16-B503-79581CCEA974} = {122968B7-2707-4FC2-819D-7E90B804A8E4}
+ {B3122D6C-A0FE-42C3-9FCE-4A9789E66E1B} = {122968B7-2707-4FC2-819D-7E90B804A8E4}
+ {FBEB40B3-4D62-462D-9F44-2B0BCDA0D2E7} = {C88304B3-5BF3-4F25-A36A-42CC42BB2D3B}
+ {239A4661-461C-4956-8512-2A3537F6A2E8} = {C88304B3-5BF3-4F25-A36A-42CC42BB2D3B}
+ {E2AAF92A-5655-446D-ACBE-3D4EC88249BE} = {122968B7-2707-4FC2-819D-7E90B804A8E4}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C9C1591B-8805-457A-BC51-D975D32BCC3A}
+ EndGlobalSection
+EndGlobal
diff --git a/DisCatSharp.Tools/DisCatSharp.ruleset b/DisCatSharp.Tools/DisCatSharp.ruleset
new file mode 100644
index 000000000..95b194b9c
--- /dev/null
+++ b/DisCatSharp.Tools/DisCatSharp.ruleset
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DisCatSharp.VoiceNext/AudioFormat.cs b/DisCatSharp.VoiceNext/AudioFormat.cs
index a2405bc26..2b037f789 100644
--- a/DisCatSharp.VoiceNext/AudioFormat.cs
+++ b/DisCatSharp.VoiceNext/AudioFormat.cs
@@ -1,163 +1,163 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.CompilerServices;
namespace DisCatSharp.VoiceNext;
///
/// Defines the format of PCM data consumed or produced by Opus.
///
-public struct AudioFormat
+public readonly struct AudioFormat
{
///
/// Gets the collection of sampling rates (in Hz) the Opus encoder can use.
///
public static IReadOnlyCollection AllowedSampleRates { get; } = new ReadOnlyCollection(new[] { 8000, 12000, 16000, 24000, 48000 });
///
/// Gets the collection of channel counts the Opus encoder can use.
///
public static IReadOnlyCollection AllowedChannelCounts { get; } = new ReadOnlyCollection(new[] { 1, 2 });
///
/// Gets the collection of sample durations (in ms) the Opus encoder can use.
///
public static IReadOnlyCollection AllowedSampleDurations { get; } = new ReadOnlyCollection(new[] { 5, 10, 20, 40, 60 });
///
/// Gets the default audio format. This is a format configured for 48kHz sampling rate, 2 channels, with music quality preset.
///
public static AudioFormat Default { get; } = new(48000, 2, VoiceApplication.Music);
///
/// Gets the audio sampling rate in Hz.
///
public int SampleRate { get; }
///
/// Gets the audio channel count.
///
public int ChannelCount { get; }
///
/// Gets the voice application, which dictates the quality preset.
///
public VoiceApplication VoiceApplication { get; }
///
/// Creates a new audio format for use with Opus encoder.
///
/// Audio sampling rate in Hz.
/// Number of audio channels in the data.
/// Encoder preset to use.
public AudioFormat(int sampleRate = 48000, int channelCount = 2, VoiceApplication voiceApplication = VoiceApplication.Music)
{
if (!AllowedSampleRates.Contains(sampleRate))
throw new ArgumentOutOfRangeException(nameof(sampleRate), "Invalid sample rate specified.");
if (!AllowedChannelCounts.Contains(channelCount))
throw new ArgumentOutOfRangeException(nameof(channelCount), "Invalid channel count specified.");
if (voiceApplication != VoiceApplication.Music && voiceApplication != VoiceApplication.Voice && voiceApplication != VoiceApplication.LowLatency)
throw new ArgumentOutOfRangeException(nameof(voiceApplication), "Invalid voice application specified.");
this.SampleRate = sampleRate;
this.ChannelCount = channelCount;
this.VoiceApplication = voiceApplication;
}
///
/// Calculates a sample size in bytes.
///
/// Millisecond duration of a sample.
/// Calculated sample size in bytes.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CalculateSampleSize(int sampleDuration)
{
if (!AllowedSampleDurations.Contains(sampleDuration))
throw new ArgumentOutOfRangeException(nameof(sampleDuration), "Invalid sample duration specified.");
// Sample size in bytes is a product of the following:
// - duration in milliseconds
// - number of channels
// - sample rate in kHz
// - size of data (in this case, sizeof(int16_t))
// which comes down to below:
return sampleDuration * this.ChannelCount * (this.SampleRate / 1000) * 2;
}
///
/// Gets the maximum buffer size for decoding. This method should be called when decoding Opus data to PCM, to ensure sufficient buffer size.
///
/// Buffer size required to decode data.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetMaximumBufferSize()
=> this.CalculateMaximumFrameSize();
///
/// Calculates the sample duration.
///
/// The sample size.
/// An int.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int CalculateSampleDuration(int sampleSize)
=> sampleSize / (this.SampleRate / 1000) / this.ChannelCount / 2 /* sizeof(int16_t) */;
///
/// Calculates the frame size.
///
/// The sample duration.
/// An int.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int CalculateFrameSize(int sampleDuration)
=> sampleDuration * (this.SampleRate / 1000);
///
/// Calculates the maximum frame size.
///
/// An int.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int CalculateMaximumFrameSize()
=> 120 * (this.SampleRate / 1000);
///
/// Samples the count to sample size.
///
/// The sample count.
/// An int.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int SampleCountToSampleSize(int sampleCount)
=> sampleCount * this.ChannelCount * 2 /* sizeof(int16_t) */;
///
/// Are the valid.
///
/// A bool.
internal bool IsValid()
=> AllowedSampleRates.Contains(this.SampleRate) && AllowedChannelCounts.Contains(this.ChannelCount) &&
(this.VoiceApplication == VoiceApplication.Music || this.VoiceApplication == VoiceApplication.Voice || this.VoiceApplication == VoiceApplication.LowLatency);
}
diff --git a/DisCatSharp.VoiceNext/Properties/AssemblyProperties.cs b/DisCatSharp.VoiceNext/Properties/AssemblyProperties.cs
index 29c9334ce..43392eec5 100644
--- a/DisCatSharp.VoiceNext/Properties/AssemblyProperties.cs
+++ b/DisCatSharp.VoiceNext/Properties/AssemblyProperties.cs
@@ -1,46 +1,47 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("DisCatSharp.ApplicationCommands")]
[assembly: InternalsVisibleTo("DisCatSharp.CommandsNext")]
[assembly: InternalsVisibleTo("DisCatSharp.Common")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration")]
+[assembly: InternalsVisibleTo("DisCatSharp.Experimental")]
[assembly: InternalsVisibleTo("DisCatSharp.Configuration.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.DependencyInjection")]
[assembly: InternalsVisibleTo("DisCatSharp.Hosting.Tests")]
[assembly: InternalsVisibleTo("DisCatSharp.Interactivity")]
[assembly: InternalsVisibleTo("DisCatSharp.Lavalink")]
[assembly: InternalsVisibleTo("DisCatSharp.Phabricator")]
[assembly: InternalsVisibleTo("DisCatSharp.Support")]
[assembly: InternalsVisibleTo("DisCatSharp.Test")]
[assembly: InternalsVisibleTo("DisCatSharp")]
[assembly: InternalsVisibleTo("DisCatSharp.VoiceNext.Natives")]
[assembly: InternalsVisibleTo("Nyaw")]
[assembly: InternalsVisibleTo("DisCatSharp.DevTools")]
[assembly: InternalsVisibleTo("DisCatSharp.DocsGenerator")]
[assembly: InternalsVisibleTo("DisCatSharp.StaffApps")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode")]
[assembly: InternalsVisibleTo("Microsoft.DocAsCode.Metadata.ManagedReference")]
diff --git a/DisCatSharp.sln b/DisCatSharp.sln
index 0aba3f024..7735b4bc5 100644
--- a/DisCatSharp.sln
+++ b/DisCatSharp.sln
@@ -1,151 +1,159 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.31911.260
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp", "DisCatSharp\DisCatSharp.csproj", "{EB3D8310-DFAD-4295-97F9-82E253647583}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext", "DisCatSharp.VoiceNext\DisCatSharp.VoiceNext.csproj", "{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4255B64D-92EC-46B3-BC3B-ED2C3A8073EE}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
BUILDING.md = BUILDING.md
CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md
CONTRIBUTING.md = CONTRIBUTING.md
LICENSE.md = LICENSE.md
README.md = README.md
SECURITY.md = SECURITY.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext\DisCatSharp.CommandsNext.csproj", "{C8ED55FB-E028-468D-955F-1534C20274EF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Interactivity", "DisCatSharp.Interactivity\DisCatSharp.Interactivity.csproj", "{DD32BEC3-0189-479F-86DC-CCF95E5634A9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F953F5D0-F0C9-41E6-ADBF-60A76D295899}"
ProjectSection(SolutionItems) = preProject
.nuget\NuGet.config = .nuget\NuGet.config
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build Items", "Build Items", "{84464D70-687B-40A8-836D-C4F737698969}"
ProjectSection(SolutionItems) = preProject
appveyor.yml = appveyor.yml
.github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml
.github\dependabot.yml = .github\dependabot.yml
DisCatSharp.targets = DisCatSharp.targets
docs-oneclick-rebuild.ps1 = docs-oneclick-rebuild.ps1
.github\workflows\docs.yml = .github\workflows\docs.yml
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
Library.targets = Library.targets
NuGet.targets = NuGet.targets
oneclick-rebuild.ps1 = oneclick-rebuild.ps1
Package.targets = Package.targets
rebuild-all.ps1 = rebuild-all.ps1
rebuild-docs.ps1 = rebuild-docs.ps1
rebuild-lib.ps1 = rebuild-lib.ps1
Version.targets = Version.targets
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{430C28D8-5F85-4D6E-AA68-211549435245}"
ProjectSection(SolutionItems) = preProject
.github\ISSUE_TEMPLATE\bug_report.md = .github\ISSUE_TEMPLATE\bug_report.md
.github\CODEOWNERS = .github\CODEOWNERS
.github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml
.github\workflows\docs-preview.yml = .github\workflows\docs-preview.yml
.github\workflows\docs.yml = .github\workflows\docs.yml
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
.github\ISSUE_TEMPLATE\feature_request.md = .github\ISSUE_TEMPLATE\feature_request.md
.github\FUNDING.yml = .github\FUNDING.yml
.github\pull_request_template.md = .github\pull_request_template.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Lavalink", "DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext.Natives", "DisCatSharp.VoiceNext.Natives\DisCatSharp.VoiceNext.Natives.csproj", "{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Common", "DisCatSharp.Common\DisCatSharp.Common.csproj", "{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.ApplicationCommands", "DisCatSharp.ApplicationCommands\DisCatSharp.ApplicationCommands.csproj", "{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Configuration", "DisCatSharp.Configuration\DisCatSharp.Configuration.csproj", "{603287D3-1EF2-47F1-A611-C7F25869DE14}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Configuration.Tests", "DisCatSharp.Configuration.Tests\DisCatSharp.Configuration.Tests.csproj", "{E15E88B4-63AD-42DE-B685-D31697C62194}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting", "DisCatSharp.Hosting\DisCatSharp.Hosting.csproj", "{72CCE5D5-926B-432A-876A-065FA2BC9B7B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting.Tests", "DisCatSharp.Hosting.Tests\DisCatSharp.Hosting.Tests.csproj", "{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Hosting.DependencyInjection", "DisCatSharp.Hosting.DependencyInjection\DisCatSharp.Hosting.DependencyInjection.csproj", "{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Experimental", "DisCatSharp.Experimental\DisCatSharp.Experimental.csproj", "{CF03EADC-E178-45B3-BD72-B6F70B625C8F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Attributes", "DisCatSharp.Attributes\DisCatSharp.Attributes.csproj", "{7082AF2C-C64A-46DF-BF60-BEF1D51DD6BF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.Build.0 = Release|Any CPU
{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.Build.0 = Release|Any CPU
{C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.Build.0 = Release|Any CPU
{DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.Build.0 = Release|Any CPU
{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.Build.0 = Release|Any CPU
{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.Build.0 = Release|Any CPU
{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.Build.0 = Release|Any CPU
{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.Build.0 = Release|Any CPU
{603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.Build.0 = Release|Any CPU
{E15E88B4-63AD-42DE-B685-D31697C62194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E15E88B4-63AD-42DE-B685-D31697C62194}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E15E88B4-63AD-42DE-B685-D31697C62194}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E15E88B4-63AD-42DE-B685-D31697C62194}.Release|Any CPU.Build.0 = Release|Any CPU
{72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.Build.0 = Release|Any CPU
{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Release|Any CPU.Build.0 = Release|Any CPU
{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.Build.0 = Release|Any CPU
- {1407FAE8-DAC1-4E89-AA15-4E4592CCC8A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1407FAE8-DAC1-4E89-AA15-4E4592CCC8A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1407FAE8-DAC1-4E89-AA15-4E4592CCC8A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1407FAE8-DAC1-4E89-AA15-4E4592CCC8A0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CF03EADC-E178-45B3-BD72-B6F70B625C8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CF03EADC-E178-45B3-BD72-B6F70B625C8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CF03EADC-E178-45B3-BD72-B6F70B625C8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CF03EADC-E178-45B3-BD72-B6F70B625C8F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7082AF2C-C64A-46DF-BF60-BEF1D51DD6BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7082AF2C-C64A-46DF-BF60-BEF1D51DD6BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7082AF2C-C64A-46DF-BF60-BEF1D51DD6BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7082AF2C-C64A-46DF-BF60-BEF1D51DD6BF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {23F3A981-51B8-4285-A38C-3267F1D25FE7}
EndGlobalSection
EndGlobal
diff --git a/DisCatSharp/Entities/Application/DiscordRpcApplication.cs b/DisCatSharp/Entities/Application/DiscordRpcApplication.cs
index 1215b86df..a9c95af3d 100644
--- a/DisCatSharp/Entities/Application/DiscordRpcApplication.cs
+++ b/DisCatSharp/Entities/Application/DiscordRpcApplication.cs
@@ -1,156 +1,155 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using Newtonsoft.Json;
namespace DisCatSharp.Entities;
///
/// Represents an OAuth2 application.
///
public sealed class DiscordRpcApplication : SnowflakeObject, IEquatable
{
[JsonProperty("name")]
public string Name;
[JsonProperty("icon")]
public string? IconHash;
[JsonIgnore]
public string? Icon
=> this.IconHash != null ? $"https://cdn.discordapp.com{Endpoints.APP_ICONS}/{this.Id}/{this.IconHash}.png" : null;
[JsonProperty("description")]
public string? Description;
[JsonProperty("summary")]
public string? Summary;
[JsonProperty("type")]
public string Type;
[JsonProperty("hook")]
public bool Hook;
[JsonProperty("guild_id")]
public ulong? GuildId;
[JsonProperty("bot_public")]
public bool IsPublic;
[JsonProperty("bot_require_code_grant")]
public bool RequiresCodeGrant;
[JsonProperty("terms_of_service_url")]
public string? TermsOfServiceUrl;
[JsonProperty("privacy_policy_url")]
public string? PrivacyPolicyUrl;
[JsonProperty("install_params")]
public DiscordApplicationInstallParams InstallParams;
[JsonProperty("verify_key")]
public string VerifyKey;
[JsonProperty("flags")]
public ApplicationFlags Flags;
[JsonProperty("tags")]
public List? Tags;
///
/// Initializes a new instance of the class.
///
internal DiscordRpcApplication()
{ }
///
/// Generates an oauth url for the application.
///
/// The permissions.
/// OAuth Url
public string GenerateBotOAuth(Permissions permissions = Permissions.None)
{
permissions &= PermissionMethods.FullPerms;
// hey look, it's not all annoying and blue :P
return new QueryUriBuilder($"{DiscordDomain.GetDomain(CoreDomain.Discord).Url}{Endpoints.OAUTH2}{Endpoints.AUTHORIZE}")
.AddParameter("client_id", this.Id.ToString(CultureInfo.InvariantCulture))
.AddParameter("scope", "bot")
.AddParameter("permissions", ((long)permissions).ToString(CultureInfo.InvariantCulture))
.ToString();
}
///
/// Checks whether this is equal to another object.
///
/// Object to compare to.
/// Whether the object is equal to this .
public override bool Equals(object obj)
=> this.Equals(obj as DiscordRpcApplication);
///
/// Checks whether this is equal to another .
///
/// to compare to.
/// Whether the is equal to this .
public bool Equals(DiscordRpcApplication e)
=> e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
///
/// Gets the hash code for this .
///
/// The hash code for this .
public override int GetHashCode()
=> this.Id.GetHashCode();
///
/// Gets whether the two objects are equal.
///
/// First application to compare.
/// Second application to compare.
/// Whether the two applications are equal.
public static bool operator ==(DiscordRpcApplication e1, DiscordRpcApplication e2)
{
var o1 = e1 as object;
var o2 = e2 as object;
return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
}
///
/// Gets whether the two objects are not equal.
///
/// First application to compare.
/// Second application to compare.
/// Whether the two applications are not equal.
public static bool operator !=(DiscordRpcApplication e1, DiscordRpcApplication e2)
=> !(e1 == e2);
}
diff --git a/DisCatSharp/Entities/Color/DiscordColor.Colors.cs b/DisCatSharp/Entities/Color/DiscordColor.Colors.cs
index f451a0845..82542b1c9 100644
--- a/DisCatSharp/Entities/Color/DiscordColor.Colors.cs
+++ b/DisCatSharp/Entities/Color/DiscordColor.Colors.cs
@@ -1,242 +1,242 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
namespace DisCatSharp.Entities;
-public partial struct DiscordColor
+public readonly partial struct DiscordColor
{
#region Black and White
///
/// Represents no color, or integer 0;
///
public static DiscordColor None { get; } = new(0);
///
/// A near-black color. Due to API limitations, the color is #010101, rather than #000000, as the latter is treated as no color.
///
public static DiscordColor Black { get; } = new(0x010101);
///
/// White, or #FFFFFF.
///
public static DiscordColor White { get; } = new(0xFFFFFF);
///
/// Gray, or #808080.
///
public static DiscordColor Gray { get; } = new(0x808080);
///
/// Dark gray, or #A9A9A9.
///
public static DiscordColor DarkGray { get; } = new(0xA9A9A9);
///
/// Light gray, or #808080.
///
public static DiscordColor LightGray { get; } = new(0xD3D3D3);
///
/// Very dark gray, or #666666.
///
public static DiscordColor VeryDarkGray { get; } = new(0x666666);
#endregion
#region Discord branding colors
// See https://discord.com/branding.
///
/// Discord Blurple, or #5865F2.
///
public static DiscordColor Blurple { get; } = new(0x5865F2);
///
/// Discord Fuchsia, or #EB459E.
///
public static DiscordColor Fuchsia { get; } = new(0xEB459E);
///
/// Discord Green, or #57F287.
///
public static DiscordColor Green { get; } = new(0x57F287);
///
/// Discord Yellow, or #FEE75C.
///
public static DiscordColor Yellow { get; } = new(0xFEE75C);
///
/// Discord Red, or #ED4245.
///
public static DiscordColor Red { get; } = new(0xED4245);
#endregion
#region Other colors
///
/// Dark red, or #7F0000.
///
public static DiscordColor DarkRed { get; } = new(0x7F0000);
///
/// Dark green, or #007F00.
///
public static DiscordColor DarkGreen { get; } = new(0x007F00);
///
/// Blue, or #0000FF.
///
public static DiscordColor Blue { get; } = new(0x0000FF);
///
/// Dark blue, or #00007F.
///
public static DiscordColor DarkBlue { get; } = new(0x00007F);
///
/// Cyan, or #00FFFF.
///
public static DiscordColor Cyan { get; } = new(0x00FFFF);
///
/// Magenta, or #FF00FF.
///
public static DiscordColor Magenta { get; } = new(0xFF00FF);
///
/// Teal, or #008080.
///
public static DiscordColor Teal { get; } = new(0x008080);
// meme
///
/// Aquamarine, or #00FFBF.
///
public static DiscordColor Aquamarine { get; } = new(0x00FFBF);
///
/// Gold, or #FFD700.
///
public static DiscordColor Gold { get; } = new(0xFFD700);
///
/// Goldenrod, or #DAA520.
///
public static DiscordColor Goldenrod { get; } = new(0xDAA520);
///
/// Azure, or #007FFF.
///
public static DiscordColor Azure { get; } = new(0x007FFF);
///
/// Rose, or #FF007F.
///
public static DiscordColor Rose { get; } = new(0xFF007F);
///
/// Spring green, or #00FF7F.
///
public static DiscordColor SpringGreen { get; } = new(0x00FF7F);
///
/// Chartreuse, or #7FFF00.
///
public static DiscordColor Chartreuse { get; } = new(0x7FFF00);
///
/// Orange, or #FFA500.
///
public static DiscordColor Orange { get; } = new(0xFFA500);
///
/// Purple, or #800080.
///
public static DiscordColor Purple { get; } = new(0x800080);
///
/// Violet, or #EE82EE.
///
public static DiscordColor Violet { get; } = new(0xEE82EE);
///
/// Brown, or #A52A2A.
///
public static DiscordColor Brown { get; } = new(0xA52A2A);
///
/// Hot pink, or #FF69B4
///
public static DiscordColor HotPink { get; } = new(0xFF69B4);
///
/// Lilac, or #C8A2C8.
///
public static DiscordColor Lilac { get; } = new(0xC8A2C8);
///
/// Cornflower blue, or #6495ED.
///
public static DiscordColor CornflowerBlue { get; } = new(0x6495ED);
///
/// Midnight blue, or #191970.
///
public static DiscordColor MidnightBlue { get; } = new(0x191970);
///
/// Wheat, or #F5DEB3.
///
public static DiscordColor Wheat { get; } = new(0xF5DEB3);
///
/// Indian red, or #CD5C5C.
///
public static DiscordColor IndianRed { get; } = new(0xCD5C5C);
///
/// Turquoise, or #30D5C8.
///
public static DiscordColor Turquoise { get; } = new(0x30D5C8);
///
/// Sap green, or #507D2A.
///
public static DiscordColor SapGreen { get; } = new(0x507D2A);
// meme, specifically bob ross
///
/// Phthalo blue, or #000F89.
///
public static DiscordColor PhthaloBlue { get; } = new(0x000F89);
// meme, specifically bob ross
///
/// Phthalo green, or #123524.
///
public static DiscordColor PhthaloGreen { get; } = new(0x123524);
///
/// Sienna, or #882D17.
///
public static DiscordColor Sienna { get; } = new(0x882D17);
#endregion
}
diff --git a/DisCatSharp/Entities/User/DiscordUser.cs b/DisCatSharp/Entities/User/DiscordUser.cs
index ac95b680f..630e9891b 100644
--- a/DisCatSharp/Entities/User/DiscordUser.cs
+++ b/DisCatSharp/Entities/User/DiscordUser.cs
@@ -1,513 +1,513 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Exceptions;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using Newtonsoft.Json;
namespace DisCatSharp.Entities;
///
/// Represents a Discord user.
///
public class DiscordUser : SnowflakeObject, IEquatable
{
///
/// Initializes a new instance of the class.
///
internal DiscordUser()
{ }
///
/// Initializes a new instance of the class.
///
/// The transport user.
internal DiscordUser(TransportUser transport)
{
this.Id = transport.Id;
this.Username = transport.Username;
this.Discriminator = transport.Discriminator;
this.AvatarHash = transport.AvatarHash;
this.AvatarDecorationHash = transport.AvatarDecorationHash;
this.BannerHash = transport.BannerHash;
this.BannerColorInternal = transport.BannerColor;
this.ThemeColorsInternal = (transport.ThemeColors ?? Array.Empty()).ToList();
this.IsBot = transport.IsBot;
this.MfaEnabled = transport.MfaEnabled;
this.Verified = transport.Verified;
this.Email = transport.Email;
this.PremiumType = transport.PremiumType;
this.Locale = transport.Locale;
this.Flags = transport.Flags;
this.OAuthFlags = transport.OAuthFlags;
this.Bio = transport.Bio;
this.Pronouns = transport.Pronouns;
}
///
/// Gets this user's username.
///
[JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
public virtual string Username { get; internal set; }
///
/// Gets this user's username with the discriminator.
/// Example: Discord#0000
///
[JsonIgnore]
public virtual string UsernameWithDiscriminator
=> $"{this.Username}#{this.Discriminator}";
///
/// Gets the user's 4-digit discriminator.
///
[JsonProperty("discriminator", NullValueHandling = NullValueHandling.Ignore)]
public virtual string Discriminator { get; internal set; }
///
/// Gets the discriminator integer.
///
[JsonIgnore]
internal int DiscriminatorInt
=> int.Parse(this.Discriminator, NumberStyles.Integer, CultureInfo.InvariantCulture);
///
/// Gets the user's banner color, if set. Mutually exclusive with .
///
[JsonIgnore]
public virtual DiscordColor? BannerColor
=> !this.BannerColorInternal.HasValue ? null : new DiscordColor(this.BannerColorInternal.Value);
///
/// Gets the user's theme colors, if set.
///
[JsonIgnore]
public virtual IReadOnlyList? ThemeColors
=> !(this.ThemeColorsInternal is not null && this.ThemeColorsInternal.Count != 0) ? null : this.ThemeColorsInternal.Select(x => new DiscordColor(x)).ToList();
///
/// Gets the user's banner color integer.
///
[JsonProperty("accent_color")]
internal int? BannerColorInternal;
///
/// Gets the user's theme color integers.
///
[JsonProperty("theme_colors", NullValueHandling = NullValueHandling.Ignore)]
internal List? ThemeColorsInternal;
///
/// Gets the user's banner url
///
[JsonIgnore]
public string BannerUrl
=> string.IsNullOrWhiteSpace(this.BannerHash) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.BANNERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.BannerHash}.{(this.BannerHash.StartsWith("a_") ? "gif" : "png")}?size=4096";
///
/// Gets the user's profile banner hash. Mutually exclusive with .
///
[JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
public virtual string BannerHash { get; internal set; }
///
/// Gets the users bio.
/// This is not available to bots tho.
///
[JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
public virtual string Bio { get; internal set; }
///
/// Gets the user's avatar hash.
///
[JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
public virtual string AvatarHash { get; internal set; }
///
/// Gets the user's avatar decoration hash.
///
[JsonProperty("avatar_decoration", NullValueHandling = NullValueHandling.Ignore)]
public virtual string AvatarDecorationHash { get; internal set; }
///
/// Returns a uri to this users profile.
///
[JsonIgnore]
public Uri ProfileUri => new($"{DiscordDomain.GetDomain(CoreDomain.Discord).Url}{Endpoints.USERS}/{this.Id}");
///
/// Returns a string representing the direct URL to this users profile.
///
/// The URL of this users profile.
[JsonIgnore]
public string ProfileUrl => this.ProfileUri.AbsoluteUri;
///
/// Gets the user's avatar url.
///
[JsonIgnore]
public string AvatarUrl
=> string.IsNullOrWhiteSpace(this.AvatarHash) ? this.DefaultAvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.AvatarHash}.{(this.AvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
///
/// Gets the user's avatar decoration url.
///
[JsonIgnore]
public string? AvatarDecorationUrl
=> string.IsNullOrWhiteSpace(this.AvatarDecorationHash) ? null : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS_DECORATIONS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/{this.AvatarDecorationHash}.{(this.AvatarDecorationHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
///
/// Gets the URL of default avatar for this user.
///
[JsonIgnore]
public string DefaultAvatarUrl
=> $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMBED}{Endpoints.AVATARS}/{(this.DiscriminatorInt % 5).ToString(CultureInfo.InvariantCulture)}.png?size=1024";
///
/// Gets whether the user is a bot.
///
[JsonProperty("bot", NullValueHandling = NullValueHandling.Ignore)]
public virtual bool IsBot { get; internal set; }
///
/// Gets whether the user has multi-factor authentication enabled.
///
[JsonProperty("mfa_enabled", NullValueHandling = NullValueHandling.Ignore)]
public virtual bool? MfaEnabled { get; internal set; }
///
/// Gets whether the user is an official Discord system user.
///
[JsonProperty("system", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsSystem { get; internal set; }
///
/// Gets whether the user is verified.
/// This is only present in OAuth.
///
[JsonProperty("verified", NullValueHandling = NullValueHandling.Ignore)]
public virtual bool? Verified { get; internal set; }
///
/// Gets the user's email address.
/// This is only present in OAuth.
///
[JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)]
public virtual string Email { get; internal set; }
///
/// Gets the user's premium type.
///
[JsonProperty("premium_type", NullValueHandling = NullValueHandling.Ignore)]
public virtual PremiumType? PremiumType { get; internal set; }
///
/// Gets the user's chosen language
///
[JsonProperty("locale", NullValueHandling = NullValueHandling.Ignore)]
public virtual string Locale { get; internal set; }
///
/// Gets the user's flags for OAuth.
///
[JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
public virtual UserFlags? OAuthFlags { get; internal set; }
///
/// Gets the user's flags.
///
[JsonProperty("public_flags", NullValueHandling = NullValueHandling.Ignore)]
public virtual UserFlags? Flags { get; internal set; }
///
/// Gets the user's pronouns.
///
[JsonProperty("pronouns", NullValueHandling = NullValueHandling.Ignore)]
public virtual string Pronouns { get; internal set; }
///
/// Gets the user's mention string.
///
[JsonIgnore]
public string Mention
=> Formatter.Mention(this, this is DiscordMember);
///
/// Gets whether this user is the Client which created this object.
///
[JsonIgnore]
public bool IsCurrent
=> this.Id == this.Discord.CurrentUser.Id;
#region Extension of DiscordUser
///
/// Whether this member is a
///
///
[JsonIgnore]
public bool IsMod
=> this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.CertifiedModerator);
///
/// Whether this member is a
///
///
[JsonIgnore]
public bool IsPartner
=> this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.Partner);
///
/// Whether this member is a
///
///
[JsonIgnore]
public bool IsVerifiedBot
=> this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.VerifiedBot);
///
/// Whether this member is a
///
///
[JsonIgnore]
public bool IsBotDev
=> this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.VerifiedDeveloper);
-
+
///
/// Whether this member is a
///
///
[JsonIgnore]
public bool IsActiveDeveloper
=> this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.ActiveDeveloper);
///
/// Whether this member is a
///
///
[JsonIgnore]
public bool IsStaff
=> this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.Staff);
#endregion
///
/// Fetches the user from the API.
///
/// The user with fresh data from the API.
public async Task GetFromApiAsync()
=> await this.Discord.ApiClient.GetUserAsync(this.Id);
///
/// Gets additional information about an application if the user is an bot.
///
/// The rpc info or
/// Thrown when the application does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task GetRpcInfoAsync()
=> this.IsBot ? await this.Discord.ApiClient.GetApplicationInfoAsync(this.Id) : await Task.FromResult(null);
///
/// Whether this user is in a
///
///
///
/// DiscordGuild guild = await Client.GetGuildAsync(806675511555915806);
/// DiscordUser user = await Client.GetUserAsync(469957180968271873);
/// Console.WriteLine($"{user.Username} {(user.IsInGuild(guild) ? "is a" : "is not a")} member of {guild.Name}");
///
/// results to J_M_Lutra is a member of Project Nyaw~.
///
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")]
public async Task IsInGuild(DiscordGuild guild)
{
try
{
var member = await guild.GetMemberAsync(this.Id);
return member is not null;
}
catch (NotFoundException)
{
return false;
}
}
///
/// Whether this user is not in a
///
///
///
public async Task IsNotInGuild(DiscordGuild guild)
=> !await this.IsInGuild(guild);
///
/// Returns the DiscordMember in the specified
///
/// The to get this user on.
/// The .
/// Thrown when the user is not part of the guild.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task ConvertToMember(DiscordGuild guild)
=> await guild.GetMemberAsync(this.Id);
///
/// Unbans this user from a guild.
///
/// Guild to unban this user from.
/// Reason for audit logs.
/// Thrown when the client does not have the permission.
/// Thrown when the user does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task UnbanAsync(DiscordGuild guild, string reason = null)
=> guild.UnbanMemberAsync(this, reason);
///
/// Gets this user's presence.
///
[JsonIgnore]
public DiscordPresence Presence
=> this.Discord is DiscordClient dc && dc.Presences.TryGetValue(this.Id, out var presence) ? presence : null;
///
/// Gets the user's avatar URL, in requested format and size.
///
/// Format of the avatar to get.
/// Maximum size of the avatar. Must be a power of two, minimum 16, maximum 2048.
/// URL of the user's avatar.
public string GetAvatarUrl(ImageFormat fmt, ushort size = 1024)
{
if (fmt == ImageFormat.Unknown)
throw new ArgumentException("You must specify valid image format.", nameof(fmt));
if (size < 16 || size > 2048)
throw new ArgumentOutOfRangeException(nameof(size));
var log = Math.Log(size, 2);
if (log < 4 || log > 11 || log % 1 != 0)
throw new ArgumentOutOfRangeException(nameof(size));
var sfmt = "";
sfmt = fmt switch
{
ImageFormat.Gif => "gif",
ImageFormat.Jpeg => "jpg",
ImageFormat.Png => "png",
ImageFormat.WebP => "webp",
ImageFormat.Auto => !string.IsNullOrWhiteSpace(this.AvatarHash) ? this.AvatarHash.StartsWith("a_") ? "gif" : "png" : "png",
_ => throw new ArgumentOutOfRangeException(nameof(fmt)),
};
var ssize = size.ToString(CultureInfo.InvariantCulture);
if (!string.IsNullOrWhiteSpace(this.AvatarHash))
{
var id = this.Id.ToString(CultureInfo.InvariantCulture);
return $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.AVATARS}/{id}/{this.AvatarHash}.{sfmt}?size={ssize}";
}
else
{
var type = (this.DiscriminatorInt % 5).ToString(CultureInfo.InvariantCulture);
return $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.EMBED}{Endpoints.AVATARS}/{type}.{sfmt}?size={ssize}";
}
}
///
/// Returns a string representation of this user.
///
/// String representation of this user.
public override string ToString() => $"User {this.Id}; {this.Username}#{this.Discriminator}";
///
/// Checks whether this is equal to another object.
///
/// Object to compare to.
/// Whether the object is equal to this .
public override bool Equals(object obj) => this.Equals(obj as DiscordUser);
///
/// Checks whether this is equal to another .
///
/// to compare to.
/// Whether the is equal to this .
public bool Equals(DiscordUser e) => e is not null && (ReferenceEquals(this, e) || this.Id == e.Id);
///
/// Gets the hash code for this .
///
/// The hash code for this .
public override int GetHashCode() => this.Id.GetHashCode();
///
/// Gets whether the two objects are equal.
///
/// First user to compare.
/// Second user to compare.
/// Whether the two users are equal.
public static bool operator ==(DiscordUser e1, DiscordUser e2)
{
var o1 = e1 as object;
var o2 = e2 as object;
return (o1 != null || o2 == null) && (o1 == null || o2 != null) && ((o1 == null && o2 == null) || e1.Id == e2.Id);
}
///
/// Gets whether the two objects are not equal.
///
/// First user to compare.
/// Second user to compare.
/// Whether the two users are not equal.
public static bool operator !=(DiscordUser e1, DiscordUser e2)
=> !(e1 == e2);
}
///
/// Represents a user comparer.
///
internal class DiscordUserComparer : IEqualityComparer
{
///
/// Whether the users are equal.
///
/// The first user
/// The second user.
public bool Equals(DiscordUser x, DiscordUser y) => x.Equals(y);
///
/// Gets the hash code.
///
/// The user.
public int GetHashCode(DiscordUser obj) => obj.Id.GetHashCode();
}
diff --git a/DisCatSharp/Enums/Application/ApplicationFlags.cs b/DisCatSharp/Enums/Application/ApplicationFlags.cs
index 4e615400f..eeae30b2b 100644
--- a/DisCatSharp/Enums/Application/ApplicationFlags.cs
+++ b/DisCatSharp/Enums/Application/ApplicationFlags.cs
@@ -1,133 +1,138 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
namespace DisCatSharp.Enums;
///
/// Represents additional details of an application.
///
[Flags]
public enum ApplicationFlags : long
{
///
/// The application is embedded and can be used by users.
/// This was introduced to avoid users using in-dev apps.
///
EmbeddedReleased = 1L << 1,
///
/// The application is a managed emoji.
///
ManagedEmoji = 1L << 2,
+ ///
+ /// Unknown, relates to in app purchase.
+ ///
+ EmbeddedIap = 1L << 3,
+
///
/// The application can create group dms.
///
- GroupDmCreate = 1L << 5,
+ GroupDmCreate = 1L << 4,
///
/// Allows the application to access the local RPC server.
///
- RpcPrivateBeta = 1L << 5,
+ RpcPrivateBeta = 1L << 6,
///
/// Allows the application to create activity assets.
///
AllowAssets = 1L<<8,
///
/// Allows the application to enable activity spectating.
///
AllowActivityActionSpectate = 1L<<9,
///
/// Allows the application to enable join requests for activities.
///
AllowActivityActionJoinRequest = 1L<<10,
///
/// The application has connected to RPC.
///
RpcHasConnected = 1L << 11,
///
/// The application can track presence data.
///
GatewayPresence = 1L << 12,
///
/// The application can track presence data (limited).
///
GatewayPresenceLimited = 1L << 13,
///
/// The application can track guild members.
///
GatewayGuildMembers = 1L << 14,
///
/// The application can track guild members (limited).
///
GatewayGuildMembersLimited = 1L << 15,
///
/// The application can track pending guild member verifications (limited).
///
VerificationPendingGuildLimit = 1L << 16,
///
/// The application is embedded.
///
Embedded = 1L << 17,
///
/// The application can track message content.
///
GatewayMessageContent = 1L << 18,
///
/// The application can track message content (limited).
///
GatewayMessageContentLimited = 1L << 19,
///
/// Related to embedded applications.
///
EmbeddedFirstParty = 1L << 20,
///
/// To be datamined.
///
UnknownFlag = 1L << 21,
///
/// The application has registered global application commands.
///
ApplicationCommandBadge = 1L << 23,
///
/// Indicates if an app is considered active. This means that it has had any global command executed in the past 30 days.
///
Active = 1L << 24
}
diff --git a/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs b/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs
index ad6586e29..80de4cb2d 100644
--- a/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs
+++ b/DisCatSharp/Enums/Guild/Automod/AutomodActionType.cs
@@ -1,49 +1,43 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
namespace DisCatSharp.Enums
{
public enum AutomodActionType : int
{
///
/// Blocks the content of a message according to the rule.
///
BlockMessage = 1,
///
/// Logs user to a specified channel.
///
SendAlertMessage = 2,
///
/// Timeout user for a specified duration.
/// Only valid for Keyword and MentionSpam rules
///
Timeout = 3
}
}
diff --git a/DisCatSharp/EventArgs/Guild/Automod/AutomodActionExecutedEventArgs.cs b/DisCatSharp/EventArgs/Guild/Automod/AutomodActionExecutedEventArgs.cs
index 11b83545b..91a433510 100644
--- a/DisCatSharp/EventArgs/Guild/Automod/AutomodActionExecutedEventArgs.cs
+++ b/DisCatSharp/EventArgs/Guild/Automod/AutomodActionExecutedEventArgs.cs
@@ -1,103 +1,103 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
namespace DisCatSharp.EventArgs
{
///
/// Represents arguments for event.
///
public class AutomodActionExecutedEventArgs : DiscordEventArgs
{
///
/// The guild associated with this event.
///
public DiscordGuild Guild { get; internal set; }
///
/// The action that was executed.
///
public AutomodAction Action { get; internal set; }
///
/// The id of the rule the action belongs to.
///
public ulong RuleId { get; internal set; }
///
/// The type of trigger of the rule which was executed.
///
- public AutomodTriggerType TriggerType { get; internal set; }
+ public AutomodTriggerType TriggerType { get; internal set; }
///
/// The member which caused this event.
///
public DiscordMember Member
=> this.Guild.Members.TryGetValue(this.UserId, out var member) ? member : this.Guild.GetMemberAsync(this.UserId, true).Result;
///
/// The user id which caused this event.
///
public ulong UserId { get; internal set; }
public DiscordChannel? Channel
=> this.ChannelId.HasValue ? this.Guild.GetChannel(this.ChannelId.Value) : null;
///
/// Fall-back channel id this event happened in.
///
public ulong? ChannelId { get; internal set; }
///
/// The id of any user message the content belongs to.
/// This will not exist if the message was blocked or content was not part of message.
///
public ulong? MessageId { get; internal set; }
///
/// The id of any system auto moderation messages posted as a result of this action.
/// This will not exist if the event doesn't correspond to an action with type SendAlertMessage.
///
public ulong? AlertMessageId { get; internal set; }
///
/// The user-generated text content.
///
public string? MessageContent { get; internal set; }
///
/// The word or phrase configured in the rule that triggered this.
///
public string? MatchedKeyword { get; internal set; }
///
/// The substring in the content which triggered the rule.
///
public string? MatchedContent { get; internal set; }
public AutomodActionExecutedEventArgs(IServiceProvider provider) : base(provider) { }
}
}
diff --git a/DisCatSharp/GlobalSuppressions.cs b/DisCatSharp/GlobalSuppressions.cs
index 753943309..7fc0c701e 100644
--- a/DisCatSharp/GlobalSuppressions.cs
+++ b/DisCatSharp/GlobalSuppressions.cs
@@ -1,80 +1,79 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Net.Abstractions.ClientProperties.OperatingSystem")]
[assembly: SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Entities.DiscordUnicodeEmoji._1SkinTone1")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Entities.DiscordUnicodeEmoji._1SkinTone2")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Entities.DiscordUnicodeEmoji._1SkinTone3")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Entities.DiscordUnicodeEmoji._1SkinTone4")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Entities.DiscordUnicodeEmoji._1SkinTone5")]
[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Entities.DiscordUnicodeEmoji._8ball")]
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.OnEmbeddedActivityUpdateAsync(Newtonsoft.Json.Linq.JObject,DisCatSharp.Entities.DiscordGuild,System.UInt64,Newtonsoft.Json.Linq.JArray,System.UInt64)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.UpdateCachedScheduledEvent(DisCatSharp.Entities.DiscordGuild,Newtonsoft.Json.Linq.JArray)")]
[assembly: SuppressMessage("Style", "IDE0150:Prefer 'null' check over type check", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.OnPresenceUpdateEventAsync(Newtonsoft.Json.Linq.JObject,Newtonsoft.Json.Linq.JObject)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Internals.s_permissionStrings")]
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Internals.s_versionHeader")]
[assembly: SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.DiscordClient._heartbeatTask")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.DiscordApiClient.BulkOverwriteGlobalApplicationCommandsAsync(System.UInt64,System.Collections.Generic.IEnumerable{DisCatSharp.Entities.DiscordApplicationCommand})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyList{DisCatSharp.Entities.DiscordApplicationCommand}}")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.DiscordApiClient.BulkOverwriteGuildApplicationCommandsAsync(System.UInt64,System.UInt64,System.Collections.Generic.IEnumerable{DisCatSharp.Entities.DiscordApplicationCommand})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyList{DisCatSharp.Entities.DiscordApplicationCommand}}")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.EventErrorHandler``2(DisCatSharp.Common.Utilities.AsyncEvent{``0,``1},System.Exception,DisCatSharp.Common.Utilities.AsyncEventHandler{``0,``1},``0,``1)")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.InternalConnectAsync~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.OnGuildRoleDeleteEventAsync(System.UInt64,DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.WsSendAsync(System.String)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordShardedClient.EventErrorHandler``1(DisCatSharp.Common.Utilities.AsyncEvent{DisCatSharp.DiscordClient,``0},System.Exception,DisCatSharp.Common.Utilities.AsyncEventHandler{DisCatSharp.DiscordClient,``0},DisCatSharp.DiscordClient,``0)")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.RestClient.BuildRequest(DisCatSharp.Net.BaseRestRequest)~System.Net.Http.HttpRequestMessage")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.RestClient.ExecuteRequestAsync(DisCatSharp.Net.BaseRestRequest,DisCatSharp.Net.RateLimitBucket,System.Threading.Tasks.TaskCompletionSource{System.Boolean})~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.OnEmbeddedActivityUpdateAsync(Newtonsoft.Json.Linq.JObject,DisCatSharp.Entities.DiscordGuild,System.UInt64,Newtonsoft.Json.Linq.JArray,System.UInt64)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.Abstractions.GamePartySizeConverter.ReadArrayObject(Newtonsoft.Json.JsonReader,Newtonsoft.Json.JsonSerializer)~Newtonsoft.Json.Linq.JArray")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.Abstractions.ShardInfoConverter.ReadArrayObject(Newtonsoft.Json.JsonReader,Newtonsoft.Json.JsonSerializer)~Newtonsoft.Json.Linq.JArray")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.RestClient.Handle429(DisCatSharp.Net.RestResponse,System.Threading.Tasks.Task@,System.Boolean@)")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Net.Abstractions.ClientProperties.OperatingSystem")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Net.Abstractions.ClientProperties.Referrer")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~P:DisCatSharp.Net.Abstractions.ClientProperties.ReferringDomain")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.HandleDispatchAsync(DisCatSharp.Net.Abstractions.GatewayPayload)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.HandleSocketMessageAsync(System.String)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.InternalConnectAsync~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.OnHeartbeatAckAsync~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.SendIdentifyAsync(DisCatSharp.Net.Abstractions.StatusUpdate)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.EventErrorHandler``2(DisCatSharp.Common.Utilities.AsyncEvent{``0,``1},System.Exception,DisCatSharp.Common.Utilities.AsyncEventHandler{``0,``1},``0,``1)")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.Goof``2(DisCatSharp.Common.Utilities.AsyncEvent{``0,``1},System.Exception,DisCatSharp.Common.Utilities.AsyncEventHandler{``0,``1},``0,``1)")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.ConnectAsync(DisCatSharp.Entities.DiscordActivity,System.Nullable{DisCatSharp.Entities.UserStatus},System.Nullable{System.DateTimeOffset})~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.RestClient.CleanupBucketsAsync~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.RestClient.ExecuteRequestAsync(DisCatSharp.Net.BaseRestRequest,DisCatSharp.Net.RateLimitBucket,System.Threading.Tasks.TaskCompletionSource{System.Boolean})~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.RestClient.UpdateHashCaches(DisCatSharp.Net.BaseRestRequest,DisCatSharp.Net.RateLimitBucket,System.String)")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Net.RestClient.WaitForInitialRateLimit(DisCatSharp.Net.RateLimitBucket)~System.Threading.Tasks.Task{System.Threading.Tasks.TaskCompletionSource{System.Boolean}}")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordShardedClient.GetGatewayInfoAsync~System.Threading.Tasks.Task{DisCatSharp.Net.GatewayInfo}")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Entities.DisCatSharpTeam.Get(System.Net.Http.HttpClient,Microsoft.Extensions.Logging.ILogger,DisCatSharp.Net.DiscordApiClient)~System.Threading.Tasks.Task{DisCatSharp.Entities.DisCatSharpTeam}")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordShardedClient.StartAsync~System.Threading.Tasks.Task")]
-[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Utilities.CheckThreadAutoArchiveDurationFeature(DisCatSharp.Entities.DiscordGuild,DisCatSharp.ThreadAutoArchiveDuration)~System.Boolean")]
[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Utilities.LogTaskFault(System.Threading.Tasks.Task,Microsoft.Extensions.Logging.ILogger,Microsoft.Extensions.Logging.LogLevel,Microsoft.Extensions.Logging.EventId,System.String)")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordShardedClient.ConnectShardAsync(System.Int32)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordShardedClient.EventErrorHandler``1(DisCatSharp.Common.Utilities.AsyncEvent{DisCatSharp.DiscordClient,``0},System.Exception,DisCatSharp.Common.Utilities.AsyncEventHandler{DisCatSharp.DiscordClient,``0},DisCatSharp.DiscordClient,``0)")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordShardedClient.Goof``1(DisCatSharp.Common.Utilities.AsyncEvent{DisCatSharp.DiscordClient,``0},System.Exception,DisCatSharp.Common.Utilities.AsyncEventHandler{DisCatSharp.DiscordClient,``0},DisCatSharp.DiscordClient,``0)")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordShardedClient.InternalStopAsync(System.Boolean)~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Usage", "CA2253:Named placeholders should not be numeric values", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordShardedClient.StartAsync~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Performance", "CA1826:Do not use Enumerable methods on indexable collections", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Entities.DiscordChannel.GetMessagesInternalAsync(System.Int32,System.Nullable{System.UInt64},System.Nullable{System.UInt64},System.Nullable{System.UInt64})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyList{DisCatSharp.Entities.DiscordMessage}}")]
[assembly: SuppressMessage("Performance", "CA1826:Do not use Enumerable methods on indexable collections", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Entities.DiscordGuild.GetAllMembersAsync~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyCollection{DisCatSharp.Entities.DiscordMember}}")]
[assembly: SuppressMessage("Performance", "CA1826:Do not use Enumerable methods on indexable collections", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Entities.DiscordMessage.GetReactionsInternalAsync(DisCatSharp.Entities.DiscordEmoji,System.Int32,System.Nullable{System.UInt64})~System.Threading.Tasks.Task{System.Collections.Generic.IReadOnlyList{DisCatSharp.Entities.DiscordUser}}")]
[assembly: SuppressMessage("Style", "IDE0030:Use coalesce expression", Justification = "", Scope = "member", Target = "~M:DisCatSharp.DiscordClient.OnInteractionCreateAsync(System.Nullable{System.UInt64},System.UInt64,DisCatSharp.Net.Abstractions.TransportUser,DisCatSharp.Net.Abstractions.TransportMember,DisCatSharp.Entities.DiscordInteraction,System.String)~System.Threading.Tasks.Task")]
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs
index 8eb192689..c60a596f2 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs
@@ -1,282 +1,282 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System.Collections.Generic;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using Newtonsoft.Json;
namespace DisCatSharp.Net.Abstractions;
///
/// Represents a application command create payload.
///
internal class RestApplicationCommandCreatePayload
{
///
/// Gets the type.
///
[JsonProperty("type")]
public ApplicationCommandType Type { get; set; }
///
/// Gets the name.
///
[JsonProperty("name")]
public string Name { get; set; }
///
/// Gets the name localizations.
///
[JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
public Optional> NameLocalizations { get; set; }
///
/// Gets the description.
///
[JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
public string Description { get; set; }
///
/// Gets the description localizations.
///
[JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)]
public Optional> DescriptionLocalizations { get; set; }
///
/// Gets the options.
///
[JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable Options { get; set; }
///
/// Whether the command is allowed for everyone.
///
[JsonProperty("default_permission", NullValueHandling = NullValueHandling.Include)]
public bool? DefaultPermission { get; set; } = null;
///
/// The command needed permissions.
///
[JsonProperty("default_member_permissions", NullValueHandling = NullValueHandling.Include)]
public Permissions? DefaultMemberPermission { get; set; }
///
/// Whether the command is allowed for dms.
///
[JsonProperty("dm_permission", NullValueHandling = NullValueHandling.Include)]
public bool? DmPermission { get; set; }
-
+
///
/// Whether the command is marked as NSFW.
///
[JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
public bool Nsfw { get; set; }
}
///
/// Represents a application command edit payload.
///
internal class RestApplicationCommandEditPayload
{
///
/// Gets the name.
///
[JsonProperty("name")]
public Optional Name { get; set; }
///
/// Gets the name localizations.
///
[JsonProperty("name_localizations")]
public Optional> NameLocalizations { get; set; }
///
/// Gets the description.
///
[JsonProperty("description")]
public Optional Description { get; set; }
///
/// Gets the description localizations.
///
[JsonProperty("description_localizations")]
public Optional> DescriptionLocalizations { get; set; }
///
/// Gets the options.
///
[JsonProperty("options", NullValueHandling = NullValueHandling.Ignore)]
public Optional> Options { get; set; }
///
/// The command needed permissions.
///
[JsonProperty("default_member_permissions", NullValueHandling = NullValueHandling.Include)]
public Optional DefaultMemberPermission { get; set; }
///
/// Whether the command is allowed for dms.
///
[JsonProperty("dm_permission", NullValueHandling = NullValueHandling.Include)]
public Optional DmPermission { get; set; }
-
+
///
/// Whether the command is marked as NSFW.
///
[JsonProperty("nsfw", NullValueHandling = NullValueHandling.Ignore)]
public Optional Nsfw { get; set; }
}
///
/// Represents a interaction response payload.
///
internal class RestInteractionResponsePayload
{
///
/// Gets the type.
///
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
public InteractionResponseType Type { get; set; }
///
/// Gets the data.
///
[JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
public DiscordInteractionApplicationCommandCallbackData Data { get; set; }
///
/// Gets the attachments.
///
[JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
public List Attachments { get; set; }
}
///
/// Represents a interaction response payload.
///
internal class RestInteractionModalResponsePayload
{
///
/// Gets the type.
///
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
public InteractionResponseType Type { get; set; }
///
/// Gets the data.
///
[JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)]
public DiscordInteractionApplicationCommandModalCallbackData Data { get; set; }
}
///
/// Represents a followup message create payload.
///
internal class RestFollowupMessageCreatePayload
{
///
/// Gets the content.
///
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
public string Content { get; set; }
///
/// Get whether the message is tts.
///
[JsonProperty("tts", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsTts { get; set; }
///
/// Gets the embeds.
///
[JsonProperty("embeds", NullValueHandling = NullValueHandling.Ignore)]
public IEnumerable Embeds { get; set; }
///
/// Gets the mentions.
///
[JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
public DiscordMentions Mentions { get; set; }
///
/// Gets the flags.
///
[JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
public int? Flags { get; set; }
///
/// Gets the components.
///
[JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
public IReadOnlyCollection Components { get; set; }
///
/// Gets attachments.
///
[JsonProperty("attachments", NullValueHandling = NullValueHandling.Ignore)]
public List Attachments { get; set; }
}
///
/// Represents a role connection metadata payload.
///
internal class RestApplicationRoleConnectionMetadataPayload
{
///
/// Gets the metadata type.
///
[JsonProperty("type")]
public ApplicationRoleConnectionMetadataType Type { get; set; }
///
/// Gets the metadata key.
///
[JsonProperty("key")]
public string Key { get; set; }
///
/// Gets the metadata name.
///
[JsonProperty("name")]
public string Name { get; set; }
///
/// Gets the metadata description.
///
[JsonProperty("description")]
public string Description { get; set; }
///
/// Gets the metadata name translations.
///
[JsonProperty("name_localizations", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary NameLocalizations { get; set; }
///
/// Gets the metadata description localizations.
///
[JsonProperty("description_localizations", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary DescriptionLocalizations { get; set; }
}
diff --git a/DisCatSharp/Net/Rest/DiscordApiClient.cs b/DisCatSharp/Net/Rest/DiscordApiClient.cs
index b6e1e7c8f..22dbb0c12 100644
--- a/DisCatSharp/Net/Rest/DiscordApiClient.cs
+++ b/DisCatSharp/Net/Rest/DiscordApiClient.cs
@@ -1,5726 +1,5726 @@
// This file is part of the DisCatSharp project, based off DSharpPlus.
//
// Copyright (c) 2021-2022 AITSYS
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using DisCatSharp.Entities;
using DisCatSharp.Enums;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Serialization;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using static System.Net.Mime.MediaTypeNames;
namespace DisCatSharp.Net;
///
/// Represents a discord api client.
///
public sealed class DiscordApiClient
{
///
/// The audit log reason header name.
///
private const string REASON_HEADER_NAME = "X-Audit-Log-Reason";
///
/// Gets the discord client.
///
internal BaseDiscordClient Discord { get; }
///
/// Gets the rest client.
///
internal RestClient Rest { get; }
///
/// Initializes a new instance of the class.
///
/// The client.
internal DiscordApiClient(BaseDiscordClient client)
{
this.Discord = client;
this.Rest = new RestClient(client);
}
///
/// Initializes a new instance of the class.
///
/// The proxy.
/// The timeout.
/// If true, use relative rate limit.
/// The logger.
internal DiscordApiClient(IWebProxy proxy, TimeSpan timeout, bool useRelativeRateLimit, ILogger logger) // This is for meta-clients, such as the webhook client
{
this.Rest = new RestClient(proxy, timeout, useRelativeRateLimit, logger);
}
///
/// Builds the query string.
///
/// The values.
/// Whether this query will be transmitted via POST.
private static string BuildQueryString(IDictionary values, bool post = false)
{
if (values == null || values.Count == 0)
return string.Empty;
var valsCollection = values.Select(xkvp =>
$"{WebUtility.UrlEncode(xkvp.Key)}={WebUtility.UrlEncode(xkvp.Value)}");
var vals = string.Join("&", valsCollection);
return !post ? $"?{vals}" : vals;
}
///
/// Prepares the message.
///
/// The msg_raw.
/// A DiscordMessage.
private DiscordMessage PrepareMessage(JToken msgRaw)
{
var author = msgRaw["author"].ToObject();
var ret = msgRaw.ToDiscordObject();
ret.Discord = this.Discord;
this.PopulateMessage(author, ret);
var referencedMsg = msgRaw["referenced_message"];
if (ret.MessageType == MessageType.Reply && !string.IsNullOrWhiteSpace(referencedMsg?.ToString()))
{
author = referencedMsg["author"].ToObject();
ret.ReferencedMessage.Discord = this.Discord;
this.PopulateMessage(author, ret.ReferencedMessage);
}
if (ret.Channel != null)
return ret;
var channel = !ret.GuildId.HasValue
? new DiscordDmChannel
{
Id = ret.ChannelId,
Discord = this.Discord,
Type = ChannelType.Private
}
: new DiscordChannel
{
Id = ret.ChannelId,
GuildId = ret.GuildId,
Discord = this.Discord
};
ret.Channel = channel;
return ret;
}
///
/// Populates the message.
///
/// The author.
/// The message.
private void PopulateMessage(TransportUser author, DiscordMessage ret)
{
var guild = ret.Channel?.Guild;
//If this is a webhook, it shouldn't be in the user cache.
if (author.IsBot && int.Parse(author.Discriminator) == 0)
{
ret.Author = new DiscordUser(author) { Discord = this.Discord };
}
else
{
if (!this.Discord.UserCache.TryGetValue(author.Id, out var usr))
{
this.Discord.UserCache[author.Id] = usr = new DiscordUser(author) { Discord = this.Discord };
}
if (guild != null)
{
if (!guild.Members.TryGetValue(author.Id, out var mbr))
mbr = new DiscordMember(usr) { Discord = this.Discord, GuildId = guild.Id };
ret.Author = mbr;
}
else
{
ret.Author = usr;
}
}
ret.PopulateMentions();
ret.ReactionsInternal ??= new List();
foreach (var xr in ret.ReactionsInternal)
xr.Emoji.Discord = this.Discord;
}
///
/// Executes a rest request.
///
/// The client.
/// The bucket.
/// The url.
/// The method.
/// The route.
/// The headers.
/// The payload.
/// The ratelimit wait override.
internal Task DoRequestAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary headers = null, string payload = null, double? ratelimitWaitOverride = null)
{
var req = new RestRequest(client, bucket, url, method, route, headers, payload, ratelimitWaitOverride);
if (this.Discord != null)
this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, $"Error while executing request. Url: {url.AbsoluteUri}");
else
_ = this.Rest.ExecuteRequestAsync(req);
return req.WaitForCompletionAsync();
}
///
/// Executes a multipart rest request for stickers.
///
/// The client.
/// The bucket.
/// The url.
/// The method.
/// The route.
/// The headers.
/// The file.
/// The sticker name.
/// The sticker tag.
/// The sticker description.
/// The ratelimit wait override.
private Task DoStickerMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary headers = null,
DiscordMessageFile file = null, string name = "", string tags = "", string description = "", double? ratelimitWaitOverride = null)
{
var req = new MultipartStickerWebRequest(client, bucket, url, method, route, headers, file, name, tags, description, ratelimitWaitOverride);
if (this.Discord != null)
this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
else
_ = this.Rest.ExecuteRequestAsync(req);
return req.WaitForCompletionAsync();
}
///
/// Executes a multipart request.
///
/// The client.
/// The bucket.
/// The url.
/// The method.
/// The route.
/// The headers.
/// The values.
/// The files.
/// The ratelimit wait override.
private Task DoMultipartAsync(BaseDiscordClient client, RateLimitBucket bucket, Uri url, RestRequestMethod method, string route, IReadOnlyDictionary headers = null, IReadOnlyDictionary values = null,
IReadOnlyCollection files = null, double? ratelimitWaitOverride = null)
{
var req = new MultipartWebRequest(client, bucket, url, method, route, headers, values, files, ratelimitWaitOverride);
if (this.Discord != null)
this.Rest.ExecuteRequestAsync(req).LogTaskFault(this.Discord.Logger, LogLevel.Error, LoggerEvents.RestError, "Error while executing request");
else
_ = this.Rest.ExecuteRequestAsync(req);
return req.WaitForCompletionAsync();
}
#region Guild
///
/// Searches the members async.
///
/// The guild_id.
/// The name.
/// The limit.
internal async Task> SearchMembersAsync(ulong guildId, string name, int? limit)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}{Endpoints.SEARCH}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var querydict = new Dictionary
{
["query"] = name,
["limit"] = limit.ToString()
};
var url = Utilities.GetApiUriFor(path, BuildQueryString(querydict), this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var json = JArray.Parse(res.Response);
var tms = json.ToObject>();
var mbrs = new List();
foreach (var xtm in tms)
{
var usr = new DiscordUser(xtm.User) { Discord = this.Discord };
this.Discord.UserCache.AddOrUpdate(xtm.User.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discord = usr.Discord;
old.AvatarHash = usr.AvatarHash;
return old;
});
mbrs.Add(new DiscordMember(xtm) { Discord = this.Discord, GuildId = guildId });
}
return mbrs;
}
///
/// Gets the guild ban async.
///
/// The guild_id.
/// The user_id.
internal async Task GetGuildBanAsync(ulong guildId, ulong userId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId, user_id = userId}, out var path);
var uri = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, uri, RestRequestMethod.GET, route).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var ban = json.ToObject();
return ban;
}
///
/// Creates the guild async.
///
/// The name.
/// The region_id.
/// The iconb64.
/// The verification_level.
/// The default_message_notifications.
/// The system_channel_flags.
internal async Task CreateGuildAsync(string name, string regionId, Optional iconb64, VerificationLevel? verificationLevel,
DefaultMessageNotifications? defaultMessageNotifications, SystemChannelFlags? systemChannelFlags)
{
var pld = new RestGuildCreatePayload
{
Name = name,
RegionId = regionId,
DefaultMessageNotifications = defaultMessageNotifications,
VerificationLevel = verificationLevel,
IconBase64 = iconb64,
SystemChannelFlags = systemChannelFlags
};
var route = $"{Endpoints.GUILDS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var rawMembers = (JArray)json["members"];
var guild = json.ToDiscordObject();
if (this.Discord is DiscordClient dc)
await dc.OnGuildCreateEventAsync(guild, rawMembers, null).ConfigureAwait(false);
return guild;
}
///
/// Creates the guild from template async.
///
/// The template_code.
/// The name.
/// The iconb64.
internal async Task CreateGuildFromTemplateAsync(string templateCode, string name, Optional iconb64)
{
var pld = new RestGuildCreateFromTemplatePayload
{
Name = name,
IconBase64 = iconb64
};
var route = $"{Endpoints.GUILDS}{Endpoints.TEMPLATES}/:template_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {template_code = templateCode }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var rawMembers = (JArray)json["members"];
var guild = json.ToDiscordObject();
if (this.Discord is DiscordClient dc)
await dc.OnGuildCreateEventAsync(guild, rawMembers, null).ConfigureAwait(false);
return guild;
}
///
/// Deletes the guild async.
///
/// The guild_id.
internal async Task DeleteGuildAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route).ConfigureAwait(false);
if (this.Discord is DiscordClient dc)
{
var gld = dc.GuildsInternal[guildId];
await dc.OnGuildDeleteEventAsync(gld).ConfigureAwait(false);
}
}
///
/// Modifies the guild.
///
/// The guild id.
/// The name.
/// The verification level.
/// The default message notifications.
/// The mfa level.
/// The explicit content filter.
/// The afk channel id.
/// The afk timeout.
/// The iconb64.
/// The owner id.
/// The splashb64.
/// The system channel id.
/// The system channel flags.
/// The public updates channel id.
/// The rules channel id.
/// The description.
/// The banner base64.
/// The discovery base64.
/// The preferred locale.
/// Whether the premium progress bar should be enabled.
/// The reason.
internal async Task ModifyGuildAsync(ulong guildId, Optional name, Optional verificationLevel,
Optional defaultMessageNotifications, Optional mfaLevel,
Optional explicitContentFilter, Optional afkChannelId,
Optional afkTimeout, Optional iconb64, Optional ownerId, Optional splashb64,
Optional systemChannelId, Optional systemChannelFlags,
Optional publicUpdatesChannelId, Optional rulesChannelId, Optional description,
Optional bannerb64, Optional discoverySplashb64, Optional preferredLocale, Optional premiumProgressBarEnabled, string reason)
{
var pld = new RestGuildModifyPayload
{
Name = name,
VerificationLevel = verificationLevel,
DefaultMessageNotifications = defaultMessageNotifications,
MfaLevel = mfaLevel,
ExplicitContentFilter = explicitContentFilter,
AfkChannelId = afkChannelId,
AfkTimeout = afkTimeout,
IconBase64 = iconb64,
SplashBase64 = splashb64,
BannerBase64 = bannerb64,
DiscoverySplashBase64 = discoverySplashb64,
OwnerId = ownerId,
SystemChannelId = systemChannelId,
SystemChannelFlags = systemChannelFlags,
RulesChannelId = rulesChannelId,
PublicUpdatesChannelId = publicUpdatesChannelId,
PreferredLocale = preferredLocale,
Description = description,
PremiumProgressBarEnabled = premiumProgressBarEnabled
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var rawMembers = (JArray)json["members"];
var guild = json.ToDiscordObject();
foreach (var r in guild.RolesInternal.Values)
r.GuildId = guild.Id;
if (this.Discord is DiscordClient dc)
await dc.OnGuildUpdateEventAsync(guild, rawMembers).ConfigureAwait(false);
return guild;
}
///
/// Modifies the guild community settings.
///
/// The guild id.
/// The guild features.
/// The rules channel id.
/// The public updates channel id.
/// The preferred locale.
/// The description.
/// The default message notifications.
/// The explicit content filter.
/// The verification level.
/// The reason.
internal async Task ModifyGuildCommunitySettingsAsync(ulong guildId, List features, Optional rulesChannelId, Optional publicUpdatesChannelId, string preferredLocale, string description, DefaultMessageNotifications defaultMessageNotifications, ExplicitContentFilter explicitContentFilter, VerificationLevel verificationLevel, string reason)
{
var pld = new RestGuildCommunityModifyPayload
{
VerificationLevel = verificationLevel,
DefaultMessageNotifications = defaultMessageNotifications,
ExplicitContentFilter = explicitContentFilter,
RulesChannelId = rulesChannelId,
PublicUpdatesChannelId = publicUpdatesChannelId,
PreferredLocale = preferredLocale,
Description = Optional.FromNullable(description),
Features = features
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var rawMembers = (JArray)json["members"];
var guild = json.ToDiscordObject();
foreach (var r in guild.RolesInternal.Values)
r.GuildId = guild.Id;
if (this.Discord is DiscordClient dc)
await dc.OnGuildUpdateEventAsync(guild, rawMembers).ConfigureAwait(false);
return guild;
}
///
/// Modifies the guild features.
///
/// The guild id.
/// The guild features.
/// The reason.
///
internal async Task ModifyGuildFeaturesAsync(ulong guildId, List features, string reason)
{
var pld = new RestGuildFeatureModifyPayload
{
Features = features
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var rawMembers = (JArray)json["members"];
var guild = json.ToDiscordObject();
foreach (var r in guild.RolesInternal.Values)
r.GuildId = guild.Id;
if (this.Discord is DiscordClient dc)
await dc.OnGuildUpdateEventAsync(guild, rawMembers).ConfigureAwait(false);
return guild;
}
///
/// Enables the guilds mfa requirement.
///
/// The guild id.
/// The reason.
internal async Task EnableGuildMfaAsync(ulong guildId, string reason)
{
var pld = new RestGuildMfaLevelModifyPayload
{
Level = MfaLevel.Enabled
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MFA}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
}
///
/// Disables the guilds mfa requirement.
///
/// The guild id.
/// The reason.
internal async Task DisableGuildMfaAsync(ulong guildId, string reason)
{
var pld = new RestGuildMfaLevelModifyPayload
{
Level = MfaLevel.Disabled
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MFA}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
}
///
/// Implements https://discord.com/developers/docs/resources/guild#get-guild-bans.
///
internal async Task> GetGuildBansAsync(ulong guildId, int? limit, ulong? before, ulong? after)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var urlParams = new Dictionary();
if (limit != null)
urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
if (before != null)
urlParams["before"] = before.Value.ToString(CultureInfo.InvariantCulture);
if (after != null)
urlParams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var bansRaw = JsonConvert.DeserializeObject>(res.Response).Select(xb =>
{
if (!this.Discord.TryGetCachedUserInternal(xb.RawUser.Id, out var usr))
{
usr = new DiscordUser(xb.RawUser) { Discord = this.Discord };
usr = this.Discord.UserCache.AddOrUpdate(usr.Id, usr, (id, old) =>
{
old.Username = usr.Username;
old.Discriminator = usr.Discriminator;
old.AvatarHash = usr.AvatarHash;
return old;
});
}
xb.User = usr;
return xb;
});
var bans = new ReadOnlyCollection(new List(bansRaw));
return bans;
}
///
/// Creates the guild ban async.
///
/// The guild_id.
/// The user_id.
/// The delete_message_days.
/// The reason.
internal Task CreateGuildBanAsync(ulong guildId, ulong userId, int deleteMessageDays, string reason)
{
if (deleteMessageDays < 0 || deleteMessageDays > 7)
throw new ArgumentException("Delete message days must be a number between 0 and 7.", nameof(deleteMessageDays));
var urlParams = new Dictionary
{
["delete_message_days"] = deleteMessageDays.ToString(CultureInfo.InvariantCulture)
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, user_id = userId }, out var path);
var url = Utilities.GetApiUriFor(path, BuildQueryString(urlParams), this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers);
}
///
/// Removes the guild ban async.
///
/// The guild_id.
/// The user_id.
/// The reason.
internal Task RemoveGuildBanAsync(ulong guildId, ulong userId, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.BANS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, user_id = userId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Leaves the guild async.
///
/// The guild_id.
internal Task LeaveGuildAsync(ulong guildId)
{
var route = $"{Endpoints.USERS}{Endpoints.ME}{Endpoints.GUILDS}/:guild_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route);
}
///
/// Adds the guild member async.
///
/// The guild_id.
/// The user_id.
/// The access_token.
/// The nick.
/// The roles.
/// If true, muted.
/// If true, deafened.
internal async Task AddGuildMemberAsync(ulong guildId, ulong userId, string accessToken, string nick, IEnumerable roles, bool muted, bool deafened)
{
var pld = new RestGuildMemberAddPayload
{
AccessToken = accessToken,
Nickname = nick ?? "",
Roles = roles ?? new List(),
Deaf = deafened,
Mute = muted
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, user_id = userId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var tm = JsonConvert.DeserializeObject(res.Response);
return new DiscordMember(tm) { Discord = this.Discord, GuildId = guildId };
}
///
/// Lists the guild members async.
///
/// The guild_id.
/// The limit.
/// The after.
internal async Task> ListGuildMembersAsync(ulong guildId, int? limit, ulong? after)
{
var urlParams = new Dictionary();
if (limit != null && limit > 0)
urlParams["limit"] = limit.Value.ToString(CultureInfo.InvariantCulture);
if (after != null)
urlParams["after"] = after.Value.ToString(CultureInfo.InvariantCulture);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var membersRaw = JsonConvert.DeserializeObject>(res.Response);
return new ReadOnlyCollection(membersRaw);
}
///
/// Adds the guild member role async.
///
/// The guild_id.
/// The user_id.
/// The role_id.
/// The reason.
internal Task AddGuildMemberRoleAsync(ulong guildId, ulong userId, ulong roleId, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id{Endpoints.ROLES}/:role_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, user_id = userId, role_id = roleId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, headers);
}
///
/// Removes the guild member role async.
///
/// The guild_id.
/// The user_id.
/// The role_id.
/// The reason.
internal Task RemoveGuildMemberRoleAsync(ulong guildId, ulong userId, ulong roleId, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBERS}/:user_id{Endpoints.ROLES}/:role_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, user_id = userId, role_id = roleId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers);
}
///
/// Modifies the guild channel position async.
///
/// The guild_id.
/// The pld.
/// The reason.
internal Task ModifyGuildChannelPositionAsync(ulong guildId, IEnumerable pld, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Modifies the guild channel parent async.
///
/// The guild_id.
/// The pld.
/// The reason.
internal Task ModifyGuildChannelParentAsync(ulong guildId, IEnumerable pld, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Detaches the guild channel parent async.
///
/// The guild_id.
/// The pld.
/// The reason.
internal Task DetachGuildChannelParentAsync(ulong guildId, IEnumerable pld, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.CHANNELS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Modifies the guild role position async.
///
/// The guild_id.
/// The pld.
/// The reason.
internal Task ModifyGuildRolePositionAsync(ulong guildId, IEnumerable pld, string reason)
{
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.ROLES}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
return this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
}
///
/// Gets the audit logs async.
///
/// The guild_id.
/// The limit.
/// The after.
/// The before.
/// The responsible.
/// The action_type.
internal async Task GetAuditLogsAsync(ulong guildId, int limit, ulong? after, ulong? before, ulong? responsible, int? actionType)
{
var urlParams = new Dictionary
{
["limit"] = limit.ToString(CultureInfo.InvariantCulture)
};
if (after != null)
urlParams["after"] = after?.ToString(CultureInfo.InvariantCulture);
if (before != null)
urlParams["before"] = before?.ToString(CultureInfo.InvariantCulture);
if (responsible != null)
urlParams["user_id"] = responsible?.ToString(CultureInfo.InvariantCulture);
if (actionType != null)
urlParams["action_type"] = actionType?.ToString(CultureInfo.InvariantCulture);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.AUDIT_LOGS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, urlParams.Any() ? BuildQueryString(urlParams) : "", this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var auditLogDataRaw = JsonConvert.DeserializeObject(res.Response);
return auditLogDataRaw;
}
///
/// Gets the guild vanity url async.
///
/// The guild_id.
internal async Task GetGuildVanityUrlAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VANITY_URL}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var invite = JsonConvert.DeserializeObject(res.Response);
return invite;
}
///
/// Gets the guild widget async.
///
/// The guild_id.
internal async Task GetGuildWidgetAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET_JSON}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var json = JObject.Parse(res.Response);
var rawChannels = (JArray)json["channels"];
var ret = json.ToDiscordObject();
ret.Discord = this.Discord;
ret.Guild = this.Discord.Guilds.ContainsKey(guildId) ? this.Discord.Guilds[guildId] : null;
ret.Channels = ret.Guild == null
? rawChannels.Select(r => new DiscordChannel
{
Id = (ulong)r["id"],
Name = r["name"].ToString(),
Position = (int)r["position"]
}).ToList()
: rawChannels.Select(r =>
{
var c = ret.Guild.GetChannel((ulong)r["id"]);
c.Position = (int)r["position"];
return c;
}).ToList();
return ret;
}
///
/// Gets the guild widget settings async.
///
/// The guild_id.
internal async Task GetGuildWidgetSettingsAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Guild = this.Discord.Guilds[guildId];
return ret;
}
///
/// Modifies the guild widget settings async.
///
/// The guild_id.
/// If true, is enabled.
/// The channel id.
/// The reason.
internal async Task ModifyGuildWidgetSettingsAsync(ulong guildId, bool? isEnabled, ulong? channelId, string reason)
{
var pld = new RestGuildWidgetSettingsPayload
{
Enabled = isEnabled,
ChannelId = channelId
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WIDGET}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
ret.Guild = this.Discord.Guilds[guildId];
return ret;
}
///
/// Gets the guild templates async.
///
/// The guild_id.
internal async Task> GetGuildTemplatesAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var templatesRaw = JsonConvert.DeserializeObject>(res.Response);
return new ReadOnlyCollection(new List(templatesRaw));
}
///
/// Creates the guild template async.
///
/// The guild_id.
/// The name.
/// The description.
internal async Task CreateGuildTemplateAsync(ulong guildId, string name, string description)
{
var pld = new RestGuildTemplateCreateOrModifyPayload
{
Name = name,
Description = description
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
return ret;
}
///
/// Syncs the guild template async.
///
/// The guild_id.
/// The template_code.
internal async Task SyncGuildTemplateAsync(ulong guildId, string templateCode)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.PUT, route, new {guild_id = guildId, template_code = templateCode }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route).ConfigureAwait(false);
var templateRaw = JsonConvert.DeserializeObject(res.Response);
return templateRaw;
}
///
/// Modifies the guild template async.
///
/// The guild_id.
/// The template_code.
/// The name.
/// The description.
internal async Task ModifyGuildTemplateAsync(ulong guildId, string templateCode, string name, string description)
{
var pld = new RestGuildTemplateCreateOrModifyPayload
{
Name = name,
Description = description
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, template_code = templateCode }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var templateRaw = JsonConvert.DeserializeObject(res.Response);
return templateRaw;
}
///
/// Deletes the guild template async.
///
/// The guild_id.
/// The template_code.
internal async Task DeleteGuildTemplateAsync(ulong guildId, string templateCode)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.TEMPLATES}/:template_code";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new {guild_id = guildId, template_code = templateCode }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route).ConfigureAwait(false);
var templateRaw = JsonConvert.DeserializeObject(res.Response);
return templateRaw;
}
///
/// Gets the guild membership screening form async.
///
/// The guild_id.
internal async Task GetGuildMembershipScreeningFormAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBER_VERIFICATION}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var screeningRaw = JsonConvert.DeserializeObject(res.Response);
return screeningRaw;
}
///
/// Modifies the guild membership screening form async.
///
/// The guild_id.
/// The enabled.
/// The fields.
/// The description.
internal async Task ModifyGuildMembershipScreeningFormAsync(ulong guildId, Optional enabled, Optional fields, Optional description)
{
var pld = new RestGuildMembershipScreeningFormModifyPayload
{
Enabled = enabled,
Description = description,
Fields = fields
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.MEMBER_VERIFICATION}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var screeningRaw = JsonConvert.DeserializeObject(res.Response);
return screeningRaw;
}
///
/// Gets the guild welcome screen async.
///
/// The guild_id.
internal async Task GetGuildWelcomeScreenAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WELCOME_SCREEN}";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject(res.Response);
return ret;
}
///
/// Modifies the guild welcome screen async.
///
/// The guild_id.
/// The enabled.
/// The welcome channels.
/// The description.
internal async Task ModifyGuildWelcomeScreenAsync(ulong guildId, Optional enabled, Optional> welcomeChannels, Optional description)
{
var pld = new RestGuildWelcomeScreenModifyPayload
{
Enabled = enabled,
WelcomeChannels = welcomeChannels,
Description = description
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.WELCOME_SCREEN}";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject(res.Response);
return ret;
}
///
/// Updates the current user voice state async.
///
/// The guild_id.
/// The channel id.
/// If true, suppress.
/// The request to speak timestamp.
internal async Task UpdateCurrentUserVoiceStateAsync(ulong guildId, ulong channelId, bool? suppress, DateTimeOffset? requestToSpeakTimestamp)
{
var pld = new RestGuildUpdateCurrentUserVoiceStatePayload
{
ChannelId = channelId,
Suppress = suppress,
RequestToSpeakTimestamp = requestToSpeakTimestamp
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VOICE_STATES}/@me";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
}
///
/// Updates the user voice state async.
///
/// The guild_id.
/// The user_id.
/// The channel id.
/// If true, suppress.
internal async Task UpdateUserVoiceStateAsync(ulong guildId, ulong userId, ulong channelId, bool? suppress)
{
var pld = new RestGuildUpdateUserVoiceStatePayload
{
ChannelId = channelId,
Suppress = suppress
};
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.VOICE_STATES}/:user_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, user_id = userId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, payload: DiscordJson.SerializeObject(pld));
}
///
/// Gets all auto mod rules for a guild.
///
/// The guild id.
/// A collection of all auto mod rules in the guild.
internal async Task> GetAutomodRulesAsync(ulong guildId)
{
var route = $"{Endpoints.GUILDS}/:guild_id/auto-moderation/rules";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject>(res.Response);
return ret.AsReadOnly();
}
///
/// Gets a specific auto mod rule in the guild.
///
/// The guild id for the rule.
/// The rule id.
/// The rule if one is found.
internal async Task GetAutomodRuleAsync(ulong guildId, ulong ruleId)
{
var route = $"{Endpoints.GUILDS}/:guild_id/auto-moderation/rules/:rule_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.GET, route, new { guild_id = guildId, rule_id = ruleId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route);
var ret = JsonConvert.DeserializeObject(res.Response);
return ret;
}
///
/// Creates an auto mod rule.
///
/// The guild id of the rule.
/// The name of the rule.
/// The event type of the rule.
/// The trigger type.
/// The actions of the rule.
/// The metadata of the rule.
/// Whether this rule is enabled.
/// The exempt roles of the rule.
/// The exempt channels of the rule.
/// The reason for this addition.
/// The new auto mod rule.
internal async Task CreateAutomodRuleAsync(ulong guildId, string name, AutomodEventType eventType, AutomodTriggerType triggerType, IEnumerable actions,
AutomodTriggerMetadata triggerMetadata = null, bool enabled = false, IEnumerable exemptRoles = null, IEnumerable exemptChannels = null, string reason = null)
{
var route = $"{Endpoints.GUILDS}/:guild_id/auto-moderation/rules";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new { guild_id = guildId }, out var path);
RestAutomodRuleModifyPayload pld = new()
{
Name = name,
EventType = eventType,
TriggerType = triggerType,
Actions = actions.ToArray(),
Enabled = enabled,
TriggerMetadata = triggerMetadata ?? null
};
if (exemptChannels != null)
pld.ExemptChannels = exemptChannels.ToArray();
if (exemptRoles != null)
pld.ExemptRoles = exemptRoles.ToArray();
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
if (this.Discord is DiscordClient dc)
{
await dc.OnAutomodRuleCreated(ret).ConfigureAwait(false);
}
return ret;
}
///
/// Modifies an auto mod role
///
/// The guild id.
/// The rule id.
/// The new name of the rule.
/// The new event type of the rule.
/// The new metadata of the rule.
/// The new actions of the rule.
/// Whether this rule is enabled.
/// The new exempt roles of the rule.
/// The new exempt channels of the rule.
/// The reason for this modification.
/// The updated automod rule
internal async Task ModifyAutomodRuleAsync(ulong guildId, ulong ruleId, Optional name, Optional eventType, Optional metadata, Optional> actions,
Optional enabled, Optional> exemptRoles, Optional> exemptChannels, string reason = null)
{
var pld = new RestAutomodRuleModifyPayload
{
Name = name,
EventType = eventType,
TriggerMetadata = metadata,
Enabled = enabled
};
if (actions.HasValue)
pld.Actions = actions.Value?.ToArray();
if (exemptChannels.HasValue)
pld.ExemptChannels = exemptChannels.Value?.ToArray();
if (exemptRoles.HasValue)
pld.ExemptRoles = exemptRoles.Value?.ToArray();
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var route = $"{Endpoints.GUILDS}/:guild_id/auto-moderation/rules/:rule_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new { guild_id = guildId, rule_id = ruleId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, payload: DiscordJson.SerializeObject(pld));
var ret = JsonConvert.DeserializeObject(res.Response);
if (this.Discord is DiscordClient dc)
{
await dc.OnAutomodRuleUpdated(ret).ConfigureAwait(false);
}
return ret;
}
///
/// Deletes an auto mod rule.
///
/// The guild id of the rule.
/// The rule id.
/// The reason for this deletion.
/// The deleted auto mod rule.
internal async Task DeleteAutomodRuleAsync(ulong guildId, ulong ruleId, string reason = null)
{
var route = $"{Endpoints.GUILDS}/:guild_id/auto-moderation/rules/:rule_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.DELETE, route, new { guild_id = guildId, rule_id = ruleId }, out var path);
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers.Add(REASON_HEADER_NAME, reason);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.DELETE, route, headers).ConfigureAwait(false);
var ret = JsonConvert.DeserializeObject(res.Response);
if (this.Discord is DiscordClient dc)
{
await dc.OnAutomodRuleDeleted(ret).ConfigureAwait(false);
}
return ret;
}
#endregion
#region Guild Scheduled Events
///
/// Creates a scheduled event.
///
internal async Task CreateGuildScheduledEventAsync(ulong guildId, ulong? channelId, DiscordScheduledEventEntityMetadata metadata, string name, DateTimeOffset scheduledStartTime, DateTimeOffset? scheduledEndTime, string description, ScheduledEventEntityType type, Optional coverb64, string reason = null)
{
var pld = new RestGuildScheduledEventCreatePayload
{
ChannelId = channelId,
EntityMetadata = metadata,
Name = name,
ScheduledStartTime = scheduledStartTime,
ScheduledEndTime = scheduledEndTime,
Description = description,
EntityType = type,
CoverBase64 = coverb64
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}";
var bucket = this.Rest.GetBucket(RestRequestMethod.POST, route, new {guild_id = guildId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.POST, route, headers, DiscordJson.SerializeObject(pld));
var scheduledEvent = JsonConvert.DeserializeObject(res.Response);
var guild = this.Discord.Guilds[guildId];
scheduledEvent.Discord = this.Discord;
if (scheduledEvent.Creator != null)
scheduledEvent.Creator.Discord = this.Discord;
if (this.Discord is DiscordClient dc)
await dc.OnGuildScheduledEventCreateEventAsync(scheduledEvent, guild);
return scheduledEvent;
}
///
/// Modifies a scheduled event.
///
internal async Task ModifyGuildScheduledEventAsync(ulong guildId, ulong scheduledEventId, Optional channelId, Optional metadata, Optional name, Optional scheduledStartTime, Optional scheduledEndTime, Optional description, Optional type, Optional status, Optional coverb64, string reason = null)
{
var pld = new RestGuildScheduledEventModifyPayload
{
ChannelId = channelId,
EntityMetadata = metadata,
Name = name,
ScheduledStartTime = scheduledStartTime,
ScheduledEndTime = scheduledEndTime,
Description = description,
EntityType = type,
Status = status,
CoverBase64 = coverb64
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
var scheduledEvent = JsonConvert.DeserializeObject(res.Response);
var guild = this.Discord.Guilds[guildId];
scheduledEvent.Discord = this.Discord;
if (scheduledEvent.Creator != null)
{
scheduledEvent.Creator.Discord = this.Discord;
this.Discord.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
{
old.Username = scheduledEvent.Creator.Username;
old.Discriminator = scheduledEvent.Creator.Discriminator;
old.AvatarHash = scheduledEvent.Creator.AvatarHash;
old.Flags = scheduledEvent.Creator.Flags;
return old;
});
}
if (this.Discord is DiscordClient dc)
await dc.OnGuildScheduledEventUpdateEventAsync(scheduledEvent, guild);
return scheduledEvent;
}
///
/// Modifies a scheduled event.
///
internal async Task ModifyGuildScheduledEventStatusAsync(ulong guildId, ulong scheduledEventId, ScheduledEventStatus status, string reason = null)
{
var pld = new RestGuildScheduledEventModifyPayload
{
Status = status
};
var headers = Utilities.GetBaseHeaders();
if (!string.IsNullOrWhiteSpace(reason))
headers[REASON_HEADER_NAME] = reason;
var route = $"{Endpoints.GUILDS}/:guild_id{Endpoints.SCHEDULED_EVENTS}/:scheduled_event_id";
var bucket = this.Rest.GetBucket(RestRequestMethod.PATCH, route, new {guild_id = guildId, scheduled_event_id = scheduledEventId }, out var path);
var url = Utilities.GetApiUriFor(path, this.Discord.Configuration);
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PATCH, route, headers, DiscordJson.SerializeObject(pld));
var scheduledEvent = JsonConvert.DeserializeObject(res.Response);
var guild = this.Discord.Guilds[guildId];
scheduledEvent.Discord = this.Discord;
if (scheduledEvent.Creator != null)
{
scheduledEvent.Creator.Discord = this.Discord;
this.Discord.UserCache.AddOrUpdate(scheduledEvent.Creator.Id, scheduledEvent.Creator, (id, old) =>
{
old.Username = scheduledEvent.Creator.Username;
old.Discriminator = scheduledEvent.Creator.Discriminator;
old.AvatarHash = scheduledEvent.Creator.AvatarHash;
old.Flags = scheduledEvent.Creator.Flags;
return old;
});
}
if (this.Discord is DiscordClient dc)
await dc.OnGuildScheduledEventUpdateEventAsync(scheduledEvent, guild);
return scheduledEvent;
}
///