Options { get; internal set; }
///
/// The option to autocomplete.
///
public DiscordInteractionDataOption FocusedOption { get; internal set; }
}
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs
index 2fbbc238a..c1baae975 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuErrorEventArgs.cs
@@ -1,48 +1,52 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.Common.Utilities;
using System;
using DisCatSharp.EventArgs;
namespace DisCatSharp.ApplicationCommands.EventArgs
{
///
/// Represents arguments for a
///
public class ContextMenuErrorEventArgs : DiscordEventArgs
{
///
/// The context of the command.
///
public ContextMenuContext Context { get; internal set; }
///
/// The exception thrown.
///
public Exception Exception { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public ContextMenuErrorEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs
index 2f756a143..badea6f28 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/ContextMenu/ContextMenuExecutedEventArgs.cs
@@ -1,42 +1,46 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.Common.Utilities;
using DisCatSharp.EventArgs;
namespace DisCatSharp.ApplicationCommands.EventArgs
{
///
/// Represents the arguments for a event
///
public sealed class ContextMenuExecutedEventArgs : DiscordEventArgs
{
///
/// The context of the command.
///
public ContextMenuContext Context { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public ContextMenuExecutedEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs
index ba7abb0d6..10e86999c 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandErrorEventArgs.cs
@@ -1,47 +1,51 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.Common.Utilities;
using System;
using DisCatSharp.EventArgs;
namespace DisCatSharp.ApplicationCommands.EventArgs
{
///
/// Represents arguments for a event
///
public class SlashCommandErrorEventArgs : DiscordEventArgs
{
///
/// The context of the command.
///
public InteractionContext Context { get; internal set; }
///
/// The exception thrown.
///
public Exception Exception { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public SlashCommandErrorEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs
index 80b2f5aee..25c76260b 100644
--- a/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs
+++ b/DisCatSharp.ApplicationCommands/EventArgs/SlashCommand/SlashCommandExecutedEventArgs.cs
@@ -1,42 +1,46 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.Common.Utilities;
using DisCatSharp.EventArgs;
namespace DisCatSharp.ApplicationCommands.EventArgs
{
///
/// Represents the arguments for a event
///
public class SlashCommandExecutedEventArgs : DiscordEventArgs
{
///
/// The context of the command.
///
public InteractionContext Context { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public SlashCommandExecutedEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs b/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs
index c05c96867..e6f99d7c7 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandErrorEventArgs.cs
@@ -1,40 +1,44 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.CommandsNext
{
///
/// Represents arguments for event.
///
public class CommandErrorEventArgs : CommandEventArgs
{
///
/// Gets the exception.
///
public Exception Exception { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public CommandErrorEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs b/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs
index 528678582..13b1150a5 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandEventArgs.cs
@@ -1,48 +1,52 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.Common.Utilities;
using DisCatSharp.EventArgs;
namespace DisCatSharp.CommandsNext
{
///
/// Base class for all CNext-related events.
///
public class CommandEventArgs : DiscordEventArgs
{
///
/// Gets the context in which the command was executed.
///
public CommandContext Context { get; internal set; }
///
/// Gets the command that was executed.
///
public Command Command
=> this.Context.Command;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public CommandEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs b/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs
index ebd2246a3..bd6ed3455 100644
--- a/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs
+++ b/DisCatSharp.CommandsNext/EventArgs/CommandExecutionEventArgs.cs
@@ -1,35 +1,39 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.CommandsNext
{
///
/// Represents arguments for event.
///
public class CommandExecutionEventArgs : CommandEventArgs
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public CommandExecutionEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp.Docs/dcs/ManagedReference.extension.js b/DisCatSharp.Docs/dcs/ManagedReference.extension.js
index e75cec347..10a4697ca 100644
--- a/DisCatSharp.Docs/dcs/ManagedReference.extension.js
+++ b/DisCatSharp.Docs/dcs/ManagedReference.extension.js
@@ -1,97 +1,97 @@
// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.
var Reset = "\x1b[0m"
var Bright = "\x1b[1m"
var Dim = "\x1b[2m"
var Underscore = "\x1b[4m"
var Blink = "\x1b[5m"
var Reverse = "\x1b[7m"
var Hidden = "\x1b[8m"
var FgBlack = "\x1b[30m"
var FgRed = "\x1b[31m"
var FgGreen = "\x1b[32m"
var FgYellow = "\x1b[33m"
var FgBlue = "\x1b[34m"
var FgMagenta = "\x1b[35m"
var FgCyan = "\x1b[36m"
var FgWhite = "\x1b[37m"
var BgBlack = "\x1b[40m"
var BgRed = "\x1b[41m"
var BgGreen = "\x1b[42m"
var BgYellow = "\x1b[43m"
var BgBlue = "\x1b[44m"
var BgMagenta = "\x1b[45m"
var BgCyan = "\x1b[46m"
var BgWhite = "\x1b[47m"
/**
* This method will be called at the start of exports.transform in ManagedReference.html.primary.js
*/
exports.preTransform = function (model) {
return model;
}
/**
* This method will be called at the end of exports.transform in ManagedReference.html.primary.js
*/
exports.postTransform = function (model) {
try {
//console.log('hi model: ' + model.type);
-
+
model.aliases = [];
//handleItem(model, model.aliases); // type
if (model.children) {
model.children.forEach(function(item) { // "id": "methods" or others
//console.log('hi container: ' + item.typePropertyName)
if (item.children) {
item.children.forEach(function(child) { // actual method (or other member)
//console.log('hi member: ' + child.id)
handleItem(child, model.aliases);
});
}
});
}
-
+
model.hasAliases = model.aliases.length > 0;
-
+
} catch (e) {
console.log(Bright + FgRed + '\nFail: ' + e + ',' + Object.keys(e) + Reset);
}
return model;
}
function handleItem(item, aliases) {
//console.log('item: ' + JSON.stringify(item));
if (item.remarks) {
- console.log('Remarks: ' + item.remarks);
+ //console.log('Remarks: ' + item.remarks);
var itemAliases = [];
item.remarks = item.remarks.replace(/\[alias=(['"]|")(.*?)\1]/g, function($$, $quot, $aliasName) {
- console.log($$);
- console.log($quot);
- console.log($aliasName);
+ //console.log($$);
+ //console.log($quot);
+ //console.log($aliasName);
var alias = {
isAlias: true,
name: item.name[0].value.replace(/^.*?(\(|$)/, $aliasName + '$1'),
id: $aliasName,
aliasTo: item.uid,
-
+
targetXref: item.specName[0].value
//targetName: item.name[0].value,
//targetFullName: item.fullName[0].value
}
itemAliases.push(alias.id);
aliases.push(alias);
return '';
});
//item.itemHasAliases = item.aliases.length > 0;
item.aliasesString = itemAliases.join(', ');
itemAliases = null;
-
+
item.remarks = item.remarks.trim().replace(/^\s*<\/p>$/, '');
if (item.remarks.length == 0) item.remarks = null;
//console.log('item: ' + JSON.stringify(item));
}
}
diff --git a/DisCatSharp.Docs/docfx-pdf.json b/DisCatSharp.Docs/docfx-pdf.json
new file mode 100644
index 000000000..3f58d157b
--- /dev/null
+++ b/DisCatSharp.Docs/docfx-pdf.json
@@ -0,0 +1,66 @@
+{
+"pdf": {
+ "content": [
+ {
+ "files": [
+ "api/**.yml",
+ "api/index.md"
+ ],
+ "exclude": [
+ "**/toc.yml",
+ "**/toc.md"
+ ]
+ },
+ {
+ "files": [
+ "articles/**.md",
+ "articles/toc.yml",
+ "articles/**/toc.yml",
+ "natives/**.md",
+ "faq/**.md",
+ "toc.yml",
+ "*.md",
+ "pdf/*"
+ ],
+ "exclude": [
+ "**/bin/**",
+ "**/obj/**",
+ "_site_pdf/**",
+ "**/toc.yml",
+ "**/toc.md"
+ ]
+ },
+ {
+ "files": "pdf/toc.yml"
+ }
+ ],
+ "resource": [
+ {
+ "files": [
+ "images/**"
+ ],
+ "exclude": [
+ "**/bin/**",
+ "**/obj/**",
+ "_site_pdf/**"
+ ]
+ }
+ ],
+ "overwrite": [
+ {
+ "files": [
+ "apidoc/**.md"
+ ],
+ "exclude": [
+ "**/bin/**",
+ "**/obj/**",
+ "_site_pdf/**"
+ ]
+ }
+ ],
+ "wkhtmltopdf": {
+ "additionalArguments": "--enable-local-file-access"
+ },
+ "dest": "_site_pdf"
+ }
+}
diff --git a/DisCatSharp.Docs/docfx.json b/DisCatSharp.Docs/docfx.json
index 30a40d32c..301ec4fee 100644
--- a/DisCatSharp.Docs/docfx.json
+++ b/DisCatSharp.Docs/docfx.json
@@ -1,83 +1,90 @@
{
"metadata": [
{
"src": [
{
"src": "..",
"files": [
"**.cs"
],
"exclude": [
"**/obj/**",
"**/bin/**",
"_site/**"
]
}
],
"dest": "api",
"filter": "filter_config.yml"
}
],
"build": {
"content": [
{
"files": [
"api/**.yml",
"api/index.md"
]
},
{
"files": [
- "articles/**.md",
- "articles/**/toc.yml",
- "natives/**.md",
- "faq/**.md",
- "toc.yml",
- "*.md"
+ "articles/**.md",
+ "articles/**/toc.yml",
+ "natives/**.md",
+ "faq/**.md",
+ "toc.yml",
+ "*.md"
],
"exclude": [
- "obj/**",
- "_site/**"
+ "**/bin/**",
+ "**/obj/**",
+ "_site/**",
+ "_site_pdf/**"
]
}
],
"resource": [
{
"files": [
"images/**",
"natives/**.zip"
],
"exclude": [
- "obj/**",
- "_site/**"
+ "**/bin/**",
+ "**/obj/**",
+ "_site/**",
+ "_site_pdf/**"
]
}
],
"overwrite": [
{
"files": [
- "apidoc/**.md"
],
"exclude": [
- "obj/**",
- "_site/**"
+ "**/bin/**",
+ "**/obj/**",
+ "_site/**",
+ "_site_pdf/**"
]
}
],
"dest": "_site",
"globalMetadata": {
"_appFooter": "© 2021 Aiko IT Systems",
- "_enableSearch": "true"
+ "_enableSearch": true,
+ "_enableNewTab": true,
+ "_appTitle": "DisCatSharp Docs"
},
"globalMetadataFiles": [],
"fileMetadataFiles": [],
"template": [
"dcs"
],
"postProcessors": ["ExtractSearchIndex", "CustomMemberIndexer"],
"noLangKeyword": false,
"keepFileLink": false,
"cleanupCacheHistory": false,
"disableGitFeatures": false
}
}
diff --git a/DisCatSharp.Docs/pdf/toc.yml b/DisCatSharp.Docs/pdf/toc.yml
new file mode 100644
index 000000000..199b8eae6
--- /dev/null
+++ b/DisCatSharp.Docs/pdf/toc.yml
@@ -0,0 +1,6 @@
+- name: Articles
+ href: ../articles/toc.yml
+- name: FAQ
+ href: ../faq.md
+- name: API Documentation
+ href: ../api/toc.yml
diff --git a/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs b/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs
index 25642d608..472c007d3 100644
--- a/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs
+++ b/DisCatSharp.Hosting.Tests/GlobalSuppressions.cs
@@ -1,26 +1,29 @@
-// This file is used by Code Analysis to maintain SuppressMessage
+// 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("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "")]
[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "")]
[assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._discordConfig")]
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._discordConfig")]
[assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._interactivityConfig")]
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._interactivityConfig")]
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._lavalinkConfig")]
[assembly: SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "", Scope = "member", Target = "~F:DisCatSharp.Hosting.Tests.HostExtensionTests._lavalinkConfig")]
[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.Bot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.DiscordHostedService},System.IServiceProvider)")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostExtensionTests.DefaultDiscord~System.Collections.Generic.Dictionary{System.String,System.String}")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostExtensionTests.DiscordInteractivityAndLavaLinkConfiguration~Microsoft.Extensions.Configuration.IConfiguration")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.Create(System.Collections.Generic.Dictionary{System.String,System.String})~Microsoft.Extensions.Hosting.IHostBuilder")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.DefaultDiscord~System.Collections.Generic.Dictionary{System.String,System.String}")]
[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.Bot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.Bot},System.IServiceProvider)")]
[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.BotTwoService.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.BotTwoService},System.IServiceProvider)")]
[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.MyCustomBot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.MyCustomBot},System.IServiceProvider)")]
[assembly: SuppressMessage("DocumentationHeader", "InterfaceDocumentationHeader:The interface must have a documentation header.", Justification = "", Scope = "type", Target = "~T:DisCatSharp.Hosting.Tests.IBotTwoService")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.Create(System.String)~Microsoft.Extensions.Hosting.IHostBuilder")]
[assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.HostTests.Create``2(System.String)~Microsoft.Extensions.Hosting.IHostBuilder")]
+[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.Bot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.Bot},System.IServiceProvider,Microsoft.Extensions.Hosting.IHostApplicationLifetime)")]
+[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.BotTwoService.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.BotTwoService},System.IServiceProvider,Microsoft.Extensions.Hosting.IHostApplicationLifetime)")]
+[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:DisCatSharp.Hosting.Tests.MyCustomBot.#ctor(Microsoft.Extensions.Configuration.IConfiguration,Microsoft.Extensions.Logging.ILogger{DisCatSharp.Hosting.Tests.MyCustomBot},System.IServiceProvider,Microsoft.Extensions.Hosting.IHostApplicationLifetime)")]
diff --git a/DisCatSharp.Hosting/DiscordHostedService.cs b/DisCatSharp.Hosting/DiscordHostedService.cs
index ccef9cfa0..ce4028df4 100644
--- a/DisCatSharp.Hosting/DiscordHostedService.cs
+++ b/DisCatSharp.Hosting/DiscordHostedService.cs
@@ -1,78 +1,90 @@
// This file is part of the DisCatSharp project, a fork of DSharpPlus.
//
// Copyright (c) 2021 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.Threading.Tasks;
using DisCatSharp.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace DisCatSharp.Hosting
{
///
/// Simple implementation for to work as a
///
public abstract class DiscordHostedService : BaseHostedService, IDiscordHostedService
{
///
public DiscordClient Client { get; protected set; }
#pragma warning disable 8618
/// IConfiguration provided via Dependency Injection. Aggregate method to access configuration files
/// An ILogger to work with, provided via Dependency Injection
/// ServiceProvider reference which contains all items currently registered for Dependency Injection
/// Contains the appropriate methods for disposing / stopping BackgroundServices during runtime
/// The name of the JSON/Config Key which contains the configuration for this Discord Service
protected DiscordHostedService(IConfiguration config,
ILogger logger,
IServiceProvider serviceProvider,
IHostApplicationLifetime applicationLifetime,
string configBotSection = DisCatSharp.Configuration.ConfigurationExtensions.DefaultRootLib)
: base(config, logger, serviceProvider, applicationLifetime, configBotSection)
{
-
+ this.Logger = logger;
+ this.ApplicationLifetime = applicationLifetime;
+ this.Configuration = config;
+ this._botSection = configBotSection;
+ this.ServiceProvider = provider;
+ this.Initialize();
}
+ #pragma warning restore 8618
+
+ ///
+ /// When the bot fails to start, this method will be invoked. (Default behavior is to shutdown)
+ ///
+ /// The exception/reason the bot couldn't start
+ protected virtual void OnInitializationError(Exception ex) => this.ApplicationLifetime.StopApplication();
protected override Task ConfigureAsync()
{
try
{
this.Client = this.Configuration.BuildClient(this.ServiceProvider, this.BotSection);
}
catch (Exception ex)
{
this.Logger.LogError($"Was unable to build {nameof(DiscordClient)} for {this.GetType().Name}");
this.OnInitializationError(ex);
}
return Task.CompletedTask;
}
protected sealed override async Task ConnectAsync() => await this.Client.ConnectAsync();
protected override Task ConfigureExtensionsAsync()
{
this.InitializeExtensions(this.Client);
return Task.CompletedTask;
}
}
}
diff --git a/DisCatSharp/Entities/Guild/DiscordMember.cs b/DisCatSharp/Entities/Guild/DiscordMember.cs
index 5522d1efc..bc2584018 100644
--- a/DisCatSharp/Entities/Guild/DiscordMember.cs
+++ b/DisCatSharp/Entities/Guild/DiscordMember.cs
@@ -1,702 +1,731 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.Threading.Tasks;
using DisCatSharp.Enums;
using DisCatSharp.Net;
using DisCatSharp.Net.Abstractions;
using DisCatSharp.Net.Models;
using Newtonsoft.Json;
namespace DisCatSharp.Entities
{
///
/// Represents a Discord guild member.
///
public class DiscordMember : DiscordUser, IEquatable
{
///
/// Initializes a new instance of the class.
///
internal DiscordMember()
{
this._role_ids_lazy = new Lazy>(() => new ReadOnlyCollection(this._role_ids));
}
///
/// Initializes a new instance of the class.
///
/// The user.
internal DiscordMember(DiscordUser user)
{
this.Discord = user.Discord;
this.Id = user.Id;
this._role_ids = new List();
this._role_ids_lazy = new Lazy>(() => new ReadOnlyCollection(this._role_ids));
}
///
/// Initializes a new instance of the class.
///
/// The mbr.
internal DiscordMember(TransportMember mbr)
{
this.Id = mbr.User.Id;
this.IsDeafened = mbr.IsDeafened;
this.IsMuted = mbr.IsMuted;
this.JoinedAt = mbr.JoinedAt;
this.Nickname = mbr.Nickname;
this.PremiumSince = mbr.PremiumSince;
this.IsPending = mbr.IsPending;
this.GuildAvatarHash = mbr.GuildAvatarHash;
+ this.GuildBannerHash = mbr.GuildBannerHash;
+ this.GuildBio = mbr.GuildBio;
+ this.CommunicationDisabledUntil = mbr.CommunicationDisabledUntil;
this._avatarHash = mbr.AvatarHash;
this._role_ids = mbr.Roles ?? new List();
this._role_ids_lazy = new Lazy>(() => new ReadOnlyCollection(this._role_ids));
}
///
/// Gets the members avatar hash.
///
[JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
public virtual string GuildAvatarHash { get; internal set; }
///
/// Gets the members avatar URL.
///
[JsonIgnore]
public string GuildAvatarUrl
- => string.IsNullOrWhiteSpace(this.GuildAvatarHash) ? this.User.AvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this._guild_id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}/avatars/{this.GuildAvatarHash}.{(this.GuildAvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
+ => string.IsNullOrWhiteSpace(this.GuildAvatarHash) ? this.User.AvatarUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this._guild_id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.AVATARS}/{this.GuildAvatarHash}.{(this.GuildAvatarHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
///
/// Gets this member's banner url.
///
[JsonIgnore]
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
public string BannerUrl => this.User.BannerUrl;
#pragma warning restore CS0108 // Member hides inherited member; missing new keyword
///
/// Gets the member's banner hash.
///
[JsonIgnore]
public override string BannerHash
{
get => this.User.BannerHash;
internal set => this.User.BannerHash = value;
}
+ ///
+ /// Gets the members banner hash.
+ ///
+ [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
+ public virtual string GuildBannerHash { get; internal set; }
+
+ ///
+ /// Gets the members banner URL.
+ ///
+ [JsonIgnore]
+ public string GuildBannerUrl
+ => string.IsNullOrWhiteSpace(this.GuildBannerHash) ? this.User.BannerUrl : $"{DiscordDomain.GetDomain(CoreDomain.DiscordCdn).Url}{Endpoints.GUILDS}/{this._guild_id.ToString(CultureInfo.InvariantCulture)}{Endpoints.USERS}/{this.Id.ToString(CultureInfo.InvariantCulture)}{Endpoints.BANNERS}/{this.GuildBannerHash}.{(this.GuildBannerHash.StartsWith("a_") ? "gif" : "png")}?size=1024";
+
///
/// The color of this member's banner. Mutually exclusive with .
///
[JsonIgnore]
public override DiscordColor? BannerColor => this.User.BannerColor;
///
/// Gets this member's nickname.
///
[JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
public string Nickname { get; internal set; }
+ ///
+ /// Gets the members guild bio.
+ /// This is not available to bots tho.
+ ///
+ [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
+ public string GuildBio { get; internal set; }
+
[JsonIgnore]
internal string _avatarHash;
///
/// Gets this member's display name.
///
[JsonIgnore]
public string DisplayName
=> this.Nickname ?? this.Username;
///
/// List of role ids
///
[JsonIgnore]
internal IReadOnlyList RoleIds
=> this._role_ids_lazy.Value;
[JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
internal List _role_ids;
[JsonIgnore]
private readonly Lazy> _role_ids_lazy;
///
/// Gets the list of roles associated with this member.
///
[JsonIgnore]
public IEnumerable Roles
=> this.RoleIds.Select(id => this.Guild.GetRole(id)).Where(x => x != null);
///
/// Gets the color associated with this user's top color-giving role, otherwise 0 (no color).
///
[JsonIgnore]
public DiscordColor Color
{
get
{
var role = this.Roles.OrderByDescending(xr => xr.Position).FirstOrDefault(xr => xr.Color.Value != 0);
return role != null ? role.Color : new DiscordColor();
}
}
///
/// Date the user joined the guild
///
[JsonProperty("joined_at", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset JoinedAt { get; internal set; }
///
/// Date the user started boosting this server
///
[JsonProperty("premium_since", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset? PremiumSince { get; internal set; }
+ ///
+ /// Date until the can communicate again.
+ ///
+ [JsonProperty("communication_disabled_until", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTimeOffset? CommunicationDisabledUntil { get; internal set; }
+
///
/// If the user is deafened
///
[JsonProperty("is_deafened", NullValueHandling = NullValueHandling.Ignore)]
public bool IsDeafened { get; internal set; }
///
/// If the user is muted
///
[JsonProperty("is_muted", NullValueHandling = NullValueHandling.Ignore)]
public bool IsMuted { get; internal set; }
///
/// Whether the user has not passed the guild's Membership Screening requirements yet.
///
[JsonProperty("pending", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsPending { get; internal set; }
///
/// Gets this member's voice state.
///
[JsonIgnore]
public DiscordVoiceState VoiceState
=> this.Discord.Guilds[this._guild_id].VoiceStates.TryGetValue(this.Id, out var voiceState) ? voiceState : null;
[JsonIgnore]
internal ulong _guild_id = 0;
///
/// Gets the guild of which this member is a part of.
///
[JsonIgnore]
public DiscordGuild Guild
=> this.Discord.Guilds[this._guild_id];
///
/// Gets whether this member is the Guild owner.
///
[JsonIgnore]
public bool IsOwner
=> this.Id == this.Guild.OwnerId;
///
/// Gets the member's position in the role hierarchy, which is the member's highest role's position. Returns for the guild's owner.
///
[JsonIgnore]
public int Hierarchy
=> this.IsOwner ? int.MaxValue : this.RoleIds.Count == 0 ? 0 : this.Roles.Max(x => x.Position);
///
/// Gets the permissions for the current member.
///
[JsonIgnore]
public Permissions Permissions => this.GetPermissions();
#region Overridden user properties
///
/// Gets the user.
///
[JsonIgnore]
internal DiscordUser User
=> this.Discord.UserCache[this.Id];
///
/// Gets this member's username.
///
public override string Username
{
get => this.User.Username;
internal set => this.User.Username = value;
}
///
/// Gets the member's 4-digit discriminator.
///
public override string Discriminator
{
get => this.User.Discriminator;
internal set => this.User.Discriminator = value;
}
///
/// Gets the member's avatar hash.
///
[JsonIgnore]
public override string AvatarHash
{
get => this.User.AvatarHash;
internal set => this.User.AvatarHash = value;
}
///
/// Gets whether the member is a bot.
///
public override bool IsBot
{
get => this.User.IsBot;
internal set => this.User.IsBot = value;
}
///
/// Gets the member's email address.
/// This is only present in OAuth.
///
public override string Email
{
get => this.User.Email;
internal set => this.User.Email = value;
}
///
/// Gets whether the member has multi-factor authentication enabled.
///
public override bool? MfaEnabled
{
get => this.User.MfaEnabled;
internal set => this.User.MfaEnabled = value;
}
///
/// Gets whether the member is verified.
/// This is only present in OAuth.
///
public override bool? Verified
{
get => this.User.Verified;
internal set => this.User.Verified = value;
}
///
/// Gets the member's chosen language
///
public override string Locale
{
get => this.User.Locale;
internal set => this.User.Locale = value;
}
///
/// Gets the user's flags.
///
public override UserFlags? OAuthFlags
{
get => this.User.OAuthFlags;
internal set => this.User.OAuthFlags = value;
}
///
/// Gets the member's flags for OAuth.
///
public override UserFlags? Flags
{
get => this.User.Flags;
internal set => this.User.Flags = value;
}
#endregion
///
/// Creates a direct message channel to this member.
///
/// Direct message channel to this member.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task CreateDmChannelAsync()
=> this.Discord.ApiClient.CreateDmAsync(this.Id);
///
/// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
///
/// Content of the message to send.
/// The sent message.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task SendMessageAsync(string content)
{
if (this.IsBot && this.Discord.CurrentUser.IsBot)
throw new ArgumentException("Bots cannot DM each other.");
var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
return await chn.SendMessageAsync(content).ConfigureAwait(false);
}
///
/// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
///
/// Embed to attach to the message.
/// The sent message.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task SendMessageAsync(DiscordEmbed embed)
{
if (this.IsBot && this.Discord.CurrentUser.IsBot)
throw new ArgumentException("Bots cannot DM each other.");
var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
return await chn.SendMessageAsync(embed).ConfigureAwait(false);
}
///
/// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
///
/// Content of the message to send.
/// Embed to attach to the message.
/// The sent message.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task SendMessageAsync(string content, DiscordEmbed embed)
{
if (this.IsBot && this.Discord.CurrentUser.IsBot)
throw new ArgumentException("Bots cannot DM each other.");
var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
return await chn.SendMessageAsync(content, embed).ConfigureAwait(false);
}
///
/// Sends a direct message to this member. Creates a direct message channel if one does not exist already.
///
/// Builder to with the message.
/// The sent message.
/// Thrown when the member has the bot blocked, the member is no longer in the guild, or if the member has Allow DM from server members off.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task SendMessageAsync(DiscordMessageBuilder message)
{
if (this.IsBot && this.Discord.CurrentUser.IsBot)
throw new ArgumentException("Bots cannot DM each other.");
var chn = await this.CreateDmChannelAsync().ConfigureAwait(false);
return await chn.SendMessageAsync(message).ConfigureAwait(false);
}
///
/// Sets this member's voice mute status.
///
/// Whether the member is to be muted.
/// Reason for audit logs.
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task SetMuteAsync(bool mute, string reason = null)
=> this.Discord.ApiClient.ModifyGuildMemberAsync(this._guild_id, this.Id, default, default, mute, default, default, reason);
///
/// Sets this member's voice deaf status.
///
/// Whether the member is to be deafened.
/// Reason for audit logs.
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task SetDeafAsync(bool deaf, string reason = null)
=> this.Discord.ApiClient.ModifyGuildMemberAsync(this._guild_id, this.Id, default, default, default, deaf, default, reason);
///
/// Modifies this member.
///
/// Action to perform on this member.
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task ModifyAsync(Action action)
{
var mdl = new MemberEditModel();
action(mdl);
if (mdl.VoiceChannel.HasValue && mdl.VoiceChannel.Value != null && mdl.VoiceChannel.Value.Type != ChannelType.Voice && mdl.VoiceChannel.Value.Type != ChannelType.Stage)
throw new ArgumentException("Given channel is not a voice or stage channel.", nameof(mdl.VoiceChannel));
if (mdl.Nickname.HasValue && this.Discord.CurrentUser.Id == this.Id)
{
await this.Discord.ApiClient.ModifyCurrentMemberNicknameAsync(this.Guild.Id, mdl.Nickname.Value,
mdl.AuditLogReason).ConfigureAwait(false);
await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, Optional.FromNoValue(),
mdl.Roles.IfPresent(e => e.Select(xr => xr.Id)), mdl.Muted, mdl.Deafened,
mdl.VoiceChannel.IfPresent(e => e?.Id), mdl.AuditLogReason).ConfigureAwait(false);
}
else
{
await this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, mdl.Nickname,
mdl.Roles.IfPresent(e => e.Select(xr => xr.Id)), mdl.Muted, mdl.Deafened,
mdl.VoiceChannel.IfPresent(e => e?.Id), mdl.AuditLogReason).ConfigureAwait(false);
}
}
///
/// Grants a role to the member.
///
/// Role to grant.
/// Reason for audit logs.
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task GrantRoleAsync(DiscordRole role, string reason = null)
=> this.Discord.ApiClient.AddGuildMemberRoleAsync(this.Guild.Id, this.Id, role.Id, reason);
///
/// Revokes a role from a member.
///
/// Role to revoke.
/// Reason for audit logs.
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RevokeRoleAsync(DiscordRole role, string reason = null)
=> this.Discord.ApiClient.RemoveGuildMemberRoleAsync(this.Guild.Id, this.Id, role.Id, reason);
///
/// Sets the member's roles to ones specified.
///
/// Roles to set.
/// Reason for audit logs.
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ReplaceRolesAsync(IEnumerable roles, string reason = null)
=> this.Discord.ApiClient.ModifyGuildMemberAsync(this.Guild.Id, this.Id, default,
new Optional>(roles.Select(xr => xr.Id)), default, default, default, reason);
///
/// Bans this member from their guild.
///
/// How many days to remove messages from.
/// Reason for audit logs.
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task BanAsync(int delete_message_days = 0, string reason = null)
=> this.Guild.BanMemberAsync(this, delete_message_days, reason);
///
/// Unbans this member from their guild.
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task UnbanAsync(string reason = null) => this.Guild.UnbanMemberAsync(this, reason);
///
/// Kicks this member from their guild.
///
/// Reason for audit logs.
///
/// [alias="KickAsync"]
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task RemoveAsync(string reason = null)
=> this.Discord.ApiClient.RemoveGuildMemberAsync(this._guild_id, this.Id, reason);
///
/// Moves this member to the specified voice channel
///
///
///
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task PlaceInAsync(DiscordChannel channel)
=> channel.PlaceMemberAsync(this);
///
/// Updates the member's suppress state in a stage channel.
///
/// The channel the member is currently in.
/// Toggles the member's suppress state.
/// Thrown when the channel in not a voice channel.
public async Task UpdateVoiceStateAsync(DiscordChannel channel, bool? suppress)
{
if (channel.Type != ChannelType.Stage)
throw new ArgumentException("Voice state can only be updated in a stage channel.");
await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, channel.Id, suppress).ConfigureAwait(false);
}
///
/// Makes the user a speaker.
///
/// Thrown when the user is not inside an stage channel.
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task MakeSpeakerAsync()
{
var vs = this.VoiceState;
if (vs == null || vs.Channel.Type != ChannelType.Stage)
throw new ArgumentException("Voice state can only be updated when the user is inside an stage channel.");
await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, vs.Channel.Id, false).ConfigureAwait(false);
}
///
/// Moves the user to audience.
///
/// Thrown when the user is not inside an stage channel.
/// Thrown when the client does not have the permission.
/// Thrown when the member does not exist.
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public async Task MoveToAudienceAsync()
{
var vs = this.VoiceState;
if (vs == null || vs.Channel.Type != ChannelType.Stage)
throw new ArgumentException("Voice state can only be updated when the user is inside an stage channel.");
await this.Discord.ApiClient.UpdateUserVoiceStateAsync(this.Guild.Id, this.Id, vs.Channel.Id, true).ConfigureAwait(false);
}
///
/// Calculates permissions in a given channel for this member.
///
/// Channel to calculate permissions for.
/// Calculated permissions for this member in the channel.
public Permissions PermissionsIn(DiscordChannel channel)
=> channel.PermissionsFor(this);
///
/// Get's the current member's roles based on the sum of the permissions of their given roles.
///
private Permissions GetPermissions()
{
if (this.Guild.OwnerId == this.Id)
return PermissionMethods.FULL_PERMS;
Permissions perms;
// assign @everyone permissions
var everyoneRole = this.Guild.EveryoneRole;
perms = everyoneRole.Permissions;
// assign permissions from member's roles (in order)
perms |= this.Roles.Aggregate(Permissions.None, (c, role) => c | role.Permissions);
// Adminstrator grants all permissions and cannot be overridden
return (perms & Permissions.Administrator) == Permissions.Administrator ? PermissionMethods.FULL_PERMS : perms;
}
///
/// Returns a string representation of this member.
///
/// String representation of this member.
public override string ToString() => $"Member {this.Id}; {this.Username}#{this.Discriminator} ({this.DisplayName})";
///
/// 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 DiscordMember);
///
/// Checks whether this is equal to another .
///
/// to compare to.
/// Whether the is equal to this .
public bool Equals(DiscordMember e) => e is not null && (ReferenceEquals(this, e) || (this.Id == e.Id && this._guild_id == e._guild_id));
///
/// Gets the hash code for this .
///
/// The hash code for this .
public override int GetHashCode()
{
var hash = 13;
hash = (hash * 7) + this.Id.GetHashCode();
hash = (hash * 7) + this._guild_id.GetHashCode();
return hash;
}
///
/// Gets whether the two objects are equal.
///
/// First member to compare.
/// Second member to compare.
/// Whether the two members are equal.
public static bool operator ==(DiscordMember e1, DiscordMember 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 && e1._guild_id == e2._guild_id));
}
///
/// Gets whether the two objects are not equal.
///
/// First member to compare.
/// Second member to compare.
/// Whether the two members are not equal.
public static bool operator !=(DiscordMember e1, DiscordMember e2)
=> !(e1 == e2);
}
}
diff --git a/DisCatSharp/Entities/User/DiscordUser.cs b/DisCatSharp/Entities/User/DiscordUser.cs
index 230d1d7cc..45a4465dd 100644
--- a/DisCatSharp/Entities/User/DiscordUser.cs
+++ b/DisCatSharp/Entities/User/DiscordUser.cs
@@ -1,425 +1,433 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.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.
internal DiscordUser(TransportUser transport)
{
this.Id = transport.Id;
this.Username = transport.Username;
this.Discriminator = transport.Discriminator;
this.AvatarHash = transport.AvatarHash;
this.BannerHash = transport.BannerHash;
this._bannerColor = transport.BannerColor;
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;
}
///
/// 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 .
///
public virtual DiscordColor? BannerColor
=> !this._bannerColor.HasValue ? null : new DiscordColor(this._bannerColor.Value);
[JsonProperty("accent_color")]
internal int? _bannerColor;
///
/// 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; }
///
/// Returns a uri to this users profile.
///
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.
public string ProfileUrl => this.ProfileUri.AbsoluteUri;
///
/// Gets the user's avatar URL.s
///
[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 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 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.DiscordCertifiedModerator);
///
/// Whether this member is a
///
///
[JsonIgnore]
public bool IsPartner
=> this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.DiscordPartner);
///
/// 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.VerifiedBotDeveloper);
///
/// Whether this member is a
///
///
[JsonIgnore]
public bool IsStaff
=> this.Flags.HasValue && this.Flags.Value.HasFlag(UserFlags.DiscordEmployee);
#endregion
///
/// 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
///
///
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")]
public async Task IsNotInGuild(DiscordGuild guild) => !await this.IsInGuild(guild);
///
/// 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 : 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/Permission.cs b/DisCatSharp/Enums/Permission.cs
index 690556373..97c4a90d8 100644
--- a/DisCatSharp/Enums/Permission.cs
+++ b/DisCatSharp/Enums/Permission.cs
@@ -1,357 +1,363 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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
{
///
/// Represents permission methods.
///
public static class PermissionMethods
{
///
/// Gets the full permissions enum (long).
///
- internal static Permissions FULL_PERMS { get; } = (Permissions)1099511627775L;
+ internal static Permissions FULL_PERMS { get; } = (Permissions)1099511627775L; // 2199023255551L
///
/// Calculates whether this permission set contains the given permission.
///
/// The permissions to calculate from
/// permission you want to check
///
public static bool HasPermission(this Permissions p, Permissions permission)
=> p.HasFlag(Permissions.Administrator) || (p & permission) == permission;
///
/// Grants permissions.
///
/// The permissions to add to.
/// Permission to add.
///
public static Permissions Grant(this Permissions p, Permissions grant) => p | grant;
///
/// Revokes permissions.
///
/// The permissions to take from.
/// Permission to take.
///
public static Permissions Revoke(this Permissions p, Permissions revoke) => p & ~revoke;
}
///
/// Whether a permission is allowed, denied or unset
///
public enum PermissionLevel
{
///
/// Said permission is Allowed
///
Allowed,
///
/// Said permission is Denied
///
Denied,
///
/// Said permission is Unset
///
Unset
}
///
/// Bitwise permission flags.
///
[Flags]
public enum Permissions : long
{
///
/// Indicates no permissions given.
///
[PermissionString("No permissions")]
None = 0x0000000000000000,
///
/// Indicates all permissions are granted
///
[PermissionString("All permissions")]
- All = 1099511627775,
+ All = 1099511627775, // 2199023255551
///
/// Allows creation of instant channel invites.
///
[PermissionString("Create instant invites")]
CreateInstantInvite = 0x0000000000000001,
///
/// Allows kicking members.
///
[PermissionString("Kick members")]
KickMembers = 0x0000000000000002,
///
/// Allows banning and unbanning members.
///
[PermissionString("Ban members")]
BanMembers = 0x0000000000000004,
///
/// Enables full access on a given guild. This also overrides other permissions.
///
[PermissionString("Administrator")]
Administrator = 0x0000000000000008,
///
/// Allows managing channels.
///
[PermissionString("Manage channels")]
ManageChannels = 0x0000000000000010,
///
/// Allows managing the guild.
///
[PermissionString("Manage guild")]
ManageGuild = 0x0000000000000020,
///
/// Allows adding reactions to messages.
///
[PermissionString("Add reactions")]
AddReactions = 0x0000000000000040,
///
/// Allows viewing audit log entries.
///
[PermissionString("View audit log")]
ViewAuditLog = 0x0000000000000080,
///
/// Allows the use of priority speaker.
///
[PermissionString("Use priority speaker")]
PrioritySpeaker = 0x0000000000000100,
///
/// Allows accessing text and voice channels. Disabling this permission hides channels.
///
[PermissionString("Read messages")]
AccessChannels = 0x0000000000000400,
///
/// Allows sending messages (does not allow sending messages in threads).
///
[PermissionString("Send messages")]
SendMessages = 0x0000000000000800,
///
/// Allows sending text-to-speech messages.
///
[PermissionString("Send TTS messages")]
SendTtsMessages = 0x0000000000001000,
///
/// Allows managing messages of other users.
///
[PermissionString("Manage messages")]
ManageMessages = 0x0000000000002000,
///
/// Allows embedding content in messages.
///
[PermissionString("Use embeds")]
EmbedLinks = 0x0000000000004000,
///
/// Allows uploading files.
///
[PermissionString("Attach files")]
AttachFiles = 0x0000000000008000,
///
/// Allows reading message history.
///
[PermissionString("Read message history")]
ReadMessageHistory = 0x0000000000010000,
///
/// Allows using @everyone and @here mentions.
///
[PermissionString("Mention everyone")]
MentionEveryone = 0x0000000000020000,
///
/// Allows using emojis from external servers, such as twitch or nitro emojis.
///
[PermissionString("Use external emojis")]
UseExternalEmojis = 0x0000000000040000,
///
/// Allows connecting to voice chat.
///
[PermissionString("Use voice chat")]
UseVoice = 0x0000000000100000,
///
/// Allows speaking in voice chat.
///
[PermissionString("Speak")]
Speak = 0x0000000000200000,
///
/// Allows muting other members in voice chat.
///
[PermissionString("Mute voice chat members")]
MuteMembers = 0x0000000000400000,
///
/// Allows deafening other members in voice chat.
///
[PermissionString("Deafen voice chat members")]
DeafenMembers = 0x0000000000800000,
///
/// Allows moving voice chat members.
///
[PermissionString("Move voice chat members")]
MoveMembers = 0x0000000001000000,
///
/// Allows using voice activation in voice chat. Revoking this will usage of push-to-talk.
///
[PermissionString("Use voice activity detection")]
UseVoiceDetection = 0x0000000002000000,
///
/// Allows changing of own nickname.
///
[PermissionString("Change own nickname")]
ChangeNickname = 0x0000000004000000,
///
/// Allows managing nicknames of other members.
///
[PermissionString("Manage nicknames")]
ManageNicknames = 0x0000000008000000,
///
/// Allows managing roles in a guild.
///
[PermissionString("Manage roles")]
ManageRoles = 0x0000000010000000,
///
/// Allows managing webhooks in a guild.
///
[PermissionString("Manage webhooks")]
ManageWebhooks = 0x0000000020000000,
///
/// Allows managing guild emojis and stickers.
///
[PermissionString("Manage emojis & stickers")]
ManageEmojisAndStickers = 0x0000000040000000,
///
/// Allows the user to go live.
///
[PermissionString("Allow stream")]
Stream = 0x0000000000000200,
///
/// Allows the user to use slash commands.
///
[PermissionString("Use application commands")]
UseApplicationCommands = 0x0000000080000000,
///
/// Allows for requesting to speak in stage channels.
///
[PermissionString("Request to speak")]
RequestToSpeak = 0x0000000100000000,
///
/// Allows managing guild events.
///
[PermissionString("Manage Events")]
ManageEvents = 0x0000000200000000,
///
/// Allows for deleting and archiving threads, and viewing all private threads.
///
[PermissionString("Manage Threads")]
ManageThreads = 0x0000000400000000,
///
/// Allows for creating threads.
///
[PermissionString("Create Public Threads")]
CreatePublicThreads = 0x0000000800000000,
///
/// Allows for creating private threads.
///
[PermissionString("Create Private Threads")]
CreatePrivateThreads = 0x0000001000000000,
///
/// Allows the usage of custom stickers from other servers.
///
[PermissionString("Use external Stickers")]
UseExternalStickers = 0x0000002000000000,
///
/// Allows for sending messages in threads.
///
[PermissionString("Send messages in Threads")]
SendMessagesInThreads = 0x0000004000000000,
///
/// Allows for launching activities (applications with the `EMBEDDED` flag) in a voice channel.
///
[PermissionString("Start Embedded Activities")]
- StartEmbeddedActivities = 0x0000008000000000
+ StartEmbeddedActivities = 0x0000008000000000//,
+
+ /*///
+ /// Allows to time-out a member.
+ ///
+ [PermissionString("Time-out Members")]
+ TimeOutMembers = 0x0000010000000000*/
}
///
/// Defines a readable name for this permission.
///
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class PermissionStringAttribute : Attribute
{
///
/// Gets the readable name for this permission.
///
public string String { get; }
///
/// Defines a readable name for this permission.
///
/// Readable name for this permission.
public PermissionStringAttribute(string str)
{
this.String = str;
}
}
}
diff --git a/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs b/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs
index 1f4de0005..2cd209b9a 100644
--- a/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs
+++ b/DisCatSharp/EventArgs/Application/ApplicationCommandEventArgs.cs
@@ -1,46 +1,50 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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;
namespace DisCatSharp.EventArgs
{
///
/// Represents arguments for application command events.
///
public sealed class ApplicationCommandEventArgs : DiscordEventArgs
{
///
/// Gets the command that was modified.
///
public DiscordApplicationCommand Command { get; internal set; }
///
/// Gets the optional guild of the command.
///
public DiscordGuild Guild { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public ApplicationCommandEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs b/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs
index 53eaa4481..58643e17b 100644
--- a/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Application/ApplicationCommandPermissionsUpdateEventArgs.cs
@@ -1,57 +1,61 @@
// This file is part of the DisCatSharp project, a fork of DSharpPlus.
//
// Copyright (c) 2021 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 DisCatSharp.Entities;
namespace DisCatSharp.EventArgs
{
///
/// Represents arguments for application command permissions update events.
///
public sealed class ApplicationCommandPermissionsUpdateEventArgs : DiscordEventArgs
{
///
/// Gets the application command permissions.
///
public List Permissions { get; internal set; }
///
/// Gets the application command.
///
public DiscordApplicationCommand Command { get; internal set; }
///
/// Gets the application id.
///
public ulong ApplicationId { get; internal set; }
///
/// Gets the guild.
///
public DiscordGuild Guild { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public ApplicationCommandPermissionsUpdateEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs b/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs
index 6f5212e69..753f9a143 100644
--- a/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs
+++ b/DisCatSharp/EventArgs/Application/GuildApplicationCommandCountEventArgs.cs
@@ -1,56 +1,60 @@
// This file is part of the DisCatSharp project, a fork of DSharpPlus.
//
// Copyright (c) 2021 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;
namespace DisCatSharp.EventArgs
{
///
/// Represents arguments for application command events.
///
public sealed class GuildApplicationCommandCountEventArgs : DiscordEventArgs
{
///
/// Gets the count of slash commands.
///
public int SlashCommands { get; internal set; }
///
/// Gets the count of user context menu commands.
///
public int UserContextMenuCommands { get; internal set; }
///
/// Gets the count of message context menu commands.
///
public int MessageContextMenuCommands { get; internal set; }
///
/// Gets the guild.
///
public DiscordGuild Guild { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public GuildApplicationCommandCountEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs b/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs
index 5738be1e7..c50242a4b 100644
--- a/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Interaction/ContextMenuInteractionCreateEventArgs.cs
@@ -1,57 +1,61 @@
// This file is part of the DisCatSharp project, a fork of DSharpPlus.
//
// Copyright (c) 2021 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
{
///
/// The context menu interaction create event args.
///
public sealed class ContextMenuInteractionCreateEventArgs : InteractionCreateEventArgs
{
///
/// The type of context menu that was used. This is never .
///
public ApplicationCommandType Type { get; internal set; }
///
/// The user that invoked this interaction. Can be casted to a member if this was on a guild.
///
public DiscordUser User => this.Interaction.User;
///
/// The user this interaction targets, if applicable.
///
public DiscordUser TargetUser { get; internal set; }
///
/// The message this interaction targets, if applicable.
///
public DiscordMessage TargetMessage { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public ContextMenuInteractionCreateEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs b/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs
index 56526c674..40638d5c7 100644
--- a/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs
+++ b/DisCatSharp/EventArgs/Interaction/InteractionCreateEventArgs.cs
@@ -1,41 +1,45 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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;
namespace DisCatSharp.EventArgs
{
///
/// Represents arguments for
///
public class InteractionCreateEventArgs : DiscordEventArgs
{
///
/// Gets the interaction data that was invoked.
///
public DiscordInteraction Interaction { get; internal set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The provider.
public InteractionCreateEventArgs(IServiceProvider provider) : base(provider)
{ }
}
}
diff --git a/DisCatSharp/Net/Abstractions/Transport/TransportMember.cs b/DisCatSharp/Net/Abstractions/Transport/TransportMember.cs
index f95a98f2e..14f9d669e 100644
--- a/DisCatSharp/Net/Abstractions/Transport/TransportMember.cs
+++ b/DisCatSharp/Net/Abstractions/Transport/TransportMember.cs
@@ -1,94 +1,113 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.Net.Abstractions
{
///
/// Represents a transport member.
///
internal class TransportMember
{
///
/// Gets the avatar hash.
///
[JsonIgnore]
public string AvatarHash { get; internal set; }
///
/// Gets the guild avatar hash.
///
[JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
public string GuildAvatarHash { get; internal set; }
+ ///
+ /// Gets the guild banner hash.
+ ///
+ [JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
+ public string GuildBannerHash { get; internal set; }
+
+ ///
+ /// Gets the guild bio.
+ /// This is not available to bots tho.
+ ///
+ [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
+ public string GuildBio { get; internal set; }
+
///
/// Gets the user.
///
[JsonProperty("user", NullValueHandling = NullValueHandling.Ignore)]
public TransportUser User { get; internal set; }
///
/// Gets the nickname.
///
[JsonProperty("nick", NullValueHandling = NullValueHandling.Ignore)]
public string Nickname { get; internal set; }
///
/// Gets the roles.
///
[JsonProperty("roles", NullValueHandling = NullValueHandling.Ignore)]
public List Roles { get; internal set; }
///
/// Gets the joined at.
///
[JsonProperty("joined_at", NullValueHandling = NullValueHandling.Ignore)]
public DateTime JoinedAt { get; internal set; }
///
/// Whether this member is deafened.
///
[JsonProperty("deaf", NullValueHandling = NullValueHandling.Ignore)]
public bool IsDeafened { get; internal set; }
///
/// Whether this member is muted.
///
[JsonProperty("mute", NullValueHandling = NullValueHandling.Ignore)]
public bool IsMuted { get; internal set; }
///
/// Gets the premium since.
///
[JsonProperty("premium_since", NullValueHandling = NullValueHandling.Ignore)]
public DateTime? PremiumSince { get; internal set; }
///
/// Whether this member is marked as pending.
///
[JsonProperty("pending", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsPending { get; internal set; }
+
+ ///
+ /// Gets the timeout time.
+ ///
+ [JsonProperty("communication_disabled_until", NullValueHandling = NullValueHandling.Ignore)]
+ public DateTime? CommunicationDisabledUntil { get; internal set; }
}
}
diff --git a/DisCatSharp/Net/Abstractions/Transport/TransportUser.cs b/DisCatSharp/Net/Abstractions/Transport/TransportUser.cs
index 9b64d5147..fd621d2ab 100644
--- a/DisCatSharp/Net/Abstractions/Transport/TransportUser.cs
+++ b/DisCatSharp/Net/Abstractions/Transport/TransportUser.cs
@@ -1,149 +1,157 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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 Newtonsoft.Json;
namespace DisCatSharp.Net.Abstractions
{
///
/// Represents a transport user.
///
internal class TransportUser
{
///
/// Gets the id.
///
[JsonProperty("id")]
public ulong Id { get; internal set; }
///
/// Gets the username.
///
[JsonProperty("username", NullValueHandling = NullValueHandling.Ignore)]
public string Username { get; internal set; }
///
/// Gets or sets the discriminator.
///
[JsonProperty("discriminator", NullValueHandling = NullValueHandling.Ignore)]
internal string Discriminator { get; set; }
///
/// Gets the username with discriminator.
///
internal string UsernameWithDiscriminator
=> $"{this.Username}#{this.Discriminator}";
///
/// Gets the avatar hash.
///
[JsonProperty("avatar", NullValueHandling = NullValueHandling.Ignore)]
public string AvatarHash { get; internal set; }
///
/// Gets the banner hash.
///
[JsonProperty("banner", NullValueHandling = NullValueHandling.Ignore)]
public string BannerHash { get; internal set; }
///
/// Gets the banner color.
///
[JsonProperty("accent_color")]
public int? BannerColor { get; internal set; }
///
/// Gets a value indicating whether is bot.
///
[JsonProperty("bot", NullValueHandling = NullValueHandling.Ignore)]
public bool IsBot { get; internal set; }
///
/// Gets a value indicating whether mfa enabled.
///
[JsonProperty("mfa_enabled", NullValueHandling = NullValueHandling.Ignore)]
public bool? MfaEnabled { get; internal set; }
///
/// Gets a value indicating whether verified.
///
[JsonProperty("verified", NullValueHandling = NullValueHandling.Ignore)]
public bool? Verified { get; internal set; }
///
/// Gets the email.
///
[JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)]
public string Email { get; internal set; }
///
/// Gets the premium type.
///
[JsonProperty("premium_type", NullValueHandling = NullValueHandling.Ignore)]
public PremiumType? PremiumType { get; internal set; }
///
/// Gets the locale.
///
[JsonProperty("locale", NullValueHandling = NullValueHandling.Ignore)]
public string Locale { get; internal set; }
///
/// Gets the OAuth flags.
///
[JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
public UserFlags? OAuthFlags { get; internal set; }
///
/// Gets the flags.
///
[JsonProperty("public_flags", NullValueHandling = NullValueHandling.Ignore)]
public UserFlags? Flags { get; internal set; }
+ ///
+ /// Gets the users bio.
+ /// This is not available to bots tho.
+ ///
+ [JsonProperty("bio", NullValueHandling = NullValueHandling.Ignore)]
+ public string Bio { get; internal set; }
+
///
/// Initializes a new instance of the class.
///
internal TransportUser() { }
///
/// Initializes a new instance of the class from an existing .
///
/// The other transport user.
internal TransportUser(TransportUser other)
{
this.Id = other.Id;
this.Username = other.Username;
this.Discriminator = other.Discriminator;
this.AvatarHash = other.AvatarHash;
this.BannerHash = other.BannerHash;
this.BannerColor = other.BannerColor;
this.IsBot = other.IsBot;
this.MfaEnabled = other.MfaEnabled;
this.Verified = other.Verified;
this.Email = other.Email;
this.PremiumType = other.PremiumType;
this.Locale = other.Locale;
this.Flags = other.Flags;
this.OAuthFlags = other.OAuthFlags;
+ this.Bio = other.Bio;
}
}
}
diff --git a/DisCatSharp/Net/WebSocket/WebSocketClient.cs b/DisCatSharp/Net/WebSocket/WebSocketClient.cs
index 54916243e..cc679399a 100644
--- a/DisCatSharp/Net/WebSocket/WebSocketClient.cs
+++ b/DisCatSharp/Net/WebSocket/WebSocketClient.cs
@@ -1,413 +1,416 @@
// This file is part of the DisCatSharp project.
//
// Copyright (c) 2021 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.IO;
using System.Net;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using DisCatSharp.EventArgs;
using DisCatSharp.Common.Utilities;
using Microsoft.Extensions.DependencyInjection;
namespace DisCatSharp.Net.WebSocket
{
// weebsocket
// not even sure whether emzi or I posted this. much love, naam.
///
/// The default, native-based WebSocket client implementation.
///
public class WebSocketClient : IWebSocketClient
{
///
/// The outgoing chunk size.
///
private const int OutgoingChunkSize = 8192; // 8 KiB
///
/// The incoming chunk size.
///
private const int IncomingChunkSize = 32768; // 32 KiB
///
/// Gets the proxy settings for this client.
///
public IWebProxy Proxy { get; }
///
/// Gets the collection of default headers to send when connecting to the remote endpoint.
///
public IReadOnlyDictionary DefaultHeaders { get; }
+ ///
+ /// Gets or sets the service provider.
+ ///
IServiceProvider IWebSocketClient.ServiceProvider
{
get => this._serviceProvider;
set => this._serviceProvider = value;
}
private readonly Dictionary _defaultHeaders;
private Task _receiverTask;
private CancellationTokenSource _receiverTokenSource;
private CancellationToken _receiverToken;
private readonly SemaphoreSlim _senderLock;
private CancellationTokenSource _socketTokenSource;
private CancellationToken _socketToken;
private ClientWebSocket _ws;
private volatile bool _isClientClose = false;
private volatile bool _isConnected = false;
private bool _isDisposed = false;
///
/// Instantiates a new WebSocket client with specified proxy settings.
///
/// Proxy settings for the client.
/// Service provider.
private WebSocketClient(IWebProxy proxy, IServiceProvider provider)
{
this._connected = new AsyncEvent("WS_CONNECT", TimeSpan.Zero, this.EventErrorHandler);
this._disconnected = new AsyncEvent("WS_DISCONNECT", TimeSpan.Zero, this.EventErrorHandler);
this._messageReceived = new AsyncEvent("WS_MESSAGE", TimeSpan.Zero, this.EventErrorHandler);
this._exceptionThrown = new AsyncEvent("WS_ERROR", TimeSpan.Zero, null);
this.Proxy = proxy;
this._defaultHeaders = new Dictionary();
this.DefaultHeaders = new ReadOnlyDictionary(this._defaultHeaders);
this._receiverTokenSource = null;
this._receiverToken = CancellationToken.None;
this._senderLock = new SemaphoreSlim(1);
this._socketTokenSource = null;
this._socketToken = CancellationToken.None;
this._serviceProvider = provider;
}
///
/// Connects to a specified remote WebSocket endpoint.
///
/// The URI of the WebSocket endpoint.
public async Task ConnectAsync(Uri uri)
{
// Disconnect first
try { await this.DisconnectAsync().ConfigureAwait(false); } catch { }
// Disallow sending messages
await this._senderLock.WaitAsync().ConfigureAwait(false);
try
{
// This can be null at this point
this._receiverTokenSource?.Dispose();
this._socketTokenSource?.Dispose();
this._ws?.Dispose();
this._ws = new ClientWebSocket();
this._ws.Options.Proxy = this.Proxy;
this._ws.Options.KeepAliveInterval = TimeSpan.Zero;
if (this._defaultHeaders != null)
foreach (var (k, v) in this._defaultHeaders)
this._ws.Options.SetRequestHeader(k, v);
this._receiverTokenSource = new CancellationTokenSource();
this._receiverToken = this._receiverTokenSource.Token;
this._socketTokenSource = new CancellationTokenSource();
this._socketToken = this._socketTokenSource.Token;
this._isClientClose = false;
this._isDisposed = false;
await this._ws.ConnectAsync(uri, this._socketToken).ConfigureAwait(false);
this._receiverTask = Task.Run(this.ReceiverLoopAsync, this._receiverToken);
}
finally
{
this._senderLock.Release();
}
}
///
/// Disconnects the WebSocket connection.
///
/// The code
/// The message
/// Lala Sabathil,06.07.2021
/// Lala Sabathil,06.07.2021
public async Task DisconnectAsync(int code = 1000, string message = "")
{
// Ensure that messages cannot be sent
await this._senderLock.WaitAsync().ConfigureAwait(false);
try
{
this._isClientClose = true;
if (this._ws != null && (this._ws.State == WebSocketState.Open || this._ws.State == WebSocketState.CloseReceived))
await this._ws.CloseOutputAsync((WebSocketCloseStatus)code, message, CancellationToken.None).ConfigureAwait(false);
if (this._receiverTask != null)
await this._receiverTask.ConfigureAwait(false); // Ensure that receiving completed
if (this._isConnected)
this._isConnected = false;
if (!this._isDisposed)
{
// Cancel all running tasks
if (this._socketToken.CanBeCanceled)
this._socketTokenSource?.Cancel();
this._socketTokenSource?.Dispose();
if (this._receiverToken.CanBeCanceled)
this._receiverTokenSource?.Cancel();
this._receiverTokenSource?.Dispose();
this._isDisposed = true;
}
}
catch { }
finally
{
this._senderLock.Release();
}
}
///
/// Send a message to the WebSocket server.
///
/// The message to send.
public async Task SendMessageAsync(string message)
{
if (this._ws == null)
return;
if (this._ws.State != WebSocketState.Open && this._ws.State != WebSocketState.CloseReceived)
return;
var bytes = Utilities.UTF8.GetBytes(message);
await this._senderLock.WaitAsync().ConfigureAwait(false);
try
{
var len = bytes.Length;
var segCount = len / OutgoingChunkSize;
if (len % OutgoingChunkSize != 0)
segCount++;
for (var i = 0; i < segCount; i++)
{
var segStart = OutgoingChunkSize * i;
var segLen = Math.Min(OutgoingChunkSize, len - segStart);
await this._ws.SendAsync(new ArraySegment(bytes, segStart, segLen), WebSocketMessageType.Text, i == segCount - 1, CancellationToken.None).ConfigureAwait(false);
}
}
finally
{
this._senderLock.Release();
}
}
///
/// Adds a header to the default header collection.
///
/// Name of the header to add.
/// Value of the header to add.
/// Whether the operation succeeded.
public bool AddDefaultHeader(string name, string value)
{
this._defaultHeaders[name] = value;
return true;
}
///
/// Removes a header from the default header collection.
///
/// Name of the header to remove.
/// Whether the operation succeeded.
public bool RemoveDefaultHeader(string name)
=> this._defaultHeaders.Remove(name);
///
/// Disposes of resources used by this WebSocket client instance.
///
public void Dispose()
{
if (this._isDisposed)
return;
this._isDisposed = true;
this.DisconnectAsync().ConfigureAwait(false).GetAwaiter().GetResult();
this._receiverTokenSource?.Dispose();
this._socketTokenSource?.Dispose();
}
///
/// Receivers the loop.
///
internal async Task ReceiverLoopAsync()
{
await Task.Yield();
var token = this._receiverToken;
var buffer = new ArraySegment(new byte[IncomingChunkSize]);
try
{
using var bs = new MemoryStream();
while (!token.IsCancellationRequested)
{
// See https://github.com/RogueException/Discord.Net/commit/ac389f5f6823e3a720aedd81b7805adbdd78b66d
// for explanation on the cancellation token
WebSocketReceiveResult result;
byte[] resultBytes;
do
{
result = await this._ws.ReceiveAsync(buffer, CancellationToken.None).ConfigureAwait(false);
if (result.MessageType == WebSocketMessageType.Close)
break;
bs.Write(buffer.Array, 0, result.Count);
}
while (!result.EndOfMessage);
resultBytes = new byte[bs.Length];
bs.Position = 0;
bs.Read(resultBytes, 0, resultBytes.Length);
bs.Position = 0;
bs.SetLength(0);
if (!this._isConnected && result.MessageType != WebSocketMessageType.Close)
{
this._isConnected = true;
await this._connected.InvokeAsync(this, new SocketEventArgs(this._serviceProvider)).ConfigureAwait(false);
}
if (result.MessageType == WebSocketMessageType.Binary)
{
await this._messageReceived.InvokeAsync(this, new SocketBinaryMessageEventArgs(resultBytes)).ConfigureAwait(false);
}
else if (result.MessageType == WebSocketMessageType.Text)
{
await this._messageReceived.InvokeAsync(this, new SocketTextMessageEventArgs(Utilities.UTF8.GetString(resultBytes))).ConfigureAwait(false);
}
else // close
{
if (!this._isClientClose)
{
var code = result.CloseStatus.Value;
code = code == WebSocketCloseStatus.NormalClosure || code == WebSocketCloseStatus.EndpointUnavailable
? (WebSocketCloseStatus)4000
: code;
await this._ws.CloseOutputAsync(code, result.CloseStatusDescription, CancellationToken.None).ConfigureAwait(false);
}
await this._disconnected.InvokeAsync(this, new SocketCloseEventArgs(this._serviceProvider) { CloseCode = (int)result.CloseStatus, CloseMessage = result.CloseStatusDescription }).ConfigureAwait(false);
break;
}
}
}
catch (Exception ex)
{
await this._exceptionThrown.InvokeAsync(this, new SocketErrorEventArgs(this._serviceProvider) { Exception = ex }).ConfigureAwait(false);
await this._disconnected.InvokeAsync(this, new SocketCloseEventArgs(this._serviceProvider) { CloseCode = -1, CloseMessage = "" }).ConfigureAwait(false);
}
// Don't await or you deadlock
// DisconnectAsync waits for this method
_ = this.DisconnectAsync().ConfigureAwait(false);
}
///
/// Creates a new instance of .
///
/// Proxy to use for this client instance.
/// Service provider.
/// An instance of .
public static IWebSocketClient CreateNew(IWebProxy proxy, IServiceProvider provider)
=> new WebSocketClient(proxy, provider);
#region Events
///
/// Triggered when the client connects successfully.
///
public event AsyncEventHandler Connected
{
add => this._connected.Register(value);
remove => this._connected.Unregister(value);
}
private readonly AsyncEvent _connected;
///
/// Triggered when the client is disconnected.
///
public event AsyncEventHandler Disconnected
{
add => this._disconnected.Register(value);
remove => this._disconnected.Unregister(value);
}
private readonly AsyncEvent _disconnected;
///
/// Triggered when the client receives a message from the remote party.
///
public event AsyncEventHandler MessageReceived
{
add => this._messageReceived.Register(value);
remove => this._messageReceived.Unregister(value);
}
private readonly AsyncEvent _messageReceived;
///
/// Triggered when an error occurs in the client.
///
public event AsyncEventHandler ExceptionThrown
{
add => this._exceptionThrown.Register(value);
remove => this._exceptionThrown.Unregister(value);
}
private readonly AsyncEvent _exceptionThrown;
private IServiceProvider _serviceProvider;
///
/// Events the error handler.
///
/// The event.
/// The exeption.
/// The handler.
/// The sender.
/// The event args.
private void EventErrorHandler(AsyncEvent asyncEvent, Exception ex, AsyncEventHandler handler, WebSocketClient sender, TArgs eventArgs)
where TArgs : AsyncEventArgs
=> this._exceptionThrown.InvokeAsync(this, new SocketErrorEventArgs(this._serviceProvider) { Exception = ex }).ConfigureAwait(false).GetAwaiter().GetResult();
#endregion
}
}
diff --git a/rebuild-docs.ps1 b/rebuild-docs.ps1
index 7ab015b45..75e3e6033 100644
--- a/rebuild-docs.ps1
+++ b/rebuild-docs.ps1
@@ -1,492 +1,492 @@
#!/usr/bin/env pwsh
# Rebuild-docs
#
# Rebuilds the documentation for DSharpPlus NextGen project, and places artifacts in specified directory.
#
# Author: Emzi0767
# Version: 2017-09-11 14:20
#
# Arguments:
# .\rebuild-docs.ps1