diff --git a/DisCatSharp.Docs/articles/basics/web_app.md b/DisCatSharp.Docs/articles/basics/web_app.md new file mode 100644 index 000000000..cdd6a3647 --- /dev/null +++ b/DisCatSharp.Docs/articles/basics/web_app.md @@ -0,0 +1,140 @@ +--- +uid: basics_web_app +title: Bot as Hosted Service +--- + +# Prerequisites +Install the following packages: + - DisCatSharp + - DisCatSharp.Hosting + +# Bot.cs +Create a new class called `Bot` which inherits from `DiscordHostedService`. + +```cs +public class Bot : DiscordHostedService +{ + public Bot(IConfiguration config, ILogger logger, IServiceProvider) : base(config,logger,provider) + { + } +} +``` + +# Startup.cs +By using the `DisCatSharp.Hosting.DependencyInjection` module, this 1 line is enough to get +your basic bot running... + +```cs +public void ConfigureServices(IServiceCollection services) +{ + services.AddDiscordHostedService(); +} +``` + +If you prefer another DI approach / the manual route -- the following two +lines are all you need! + +```cs +public void ConfigureServices(IServiceCollection services) +{ + services.AddSingleton(); + services.AddHostedService(provider => provider.GetRequiredService()); +} +``` + +Singleton - we only want 1 instance of Bot to ever run during runtime.
+Then we take the registered singleton to run as a `HostedService`. + +# How to reference +Within a DI environment, when you want to reference your `Bot` all you have to do is add `IDiscordHostedService` +as a parameter in the constructor. + +# How to Configure +You must provide a token in order for the bot to work. + +Add the following to `appsettings.json` +```json +{ + "DisCatSharp": { + "Discord": { + "Token": "YOUR TOKEN HERE" + } + } +} +``` + +## Extensions +If you wish to add additional modules/extensions you can do so one of two ways. +1. Use the full namespace name +2. Namespace without the `DisCatSharp` prefix - because we assume the extension starts with DisCatSharp. + +To add the extensions `Interactivity` and `CommandsNext`: +```json +{ + "DisCatSharp": { + "Using": [ + "DisCatSharp.Interactivity", + "CommandsNext" + ], + + "Discord": { + "Token": "YOUR TOKEN HERE" + }, + + "Interactivity": { + "PollBehaviour": "KeepEmojis" + }, + + "CommandsNext": { + "StringPrefixes": [ "!" ] + } + } +} +``` + +Note: to configure an extension, you simply add a section for it under `DisCatSharp` in `appsettings.json`. You only have +to include values you **WISH TO OVERRIDE**. There is no need to include all config options if you only need to change 1 value. + +For more info on which values are available checkout the following classes: + - `ApplicationCommandsConfiguration` + - `CommandsNextConfiguration` + - `DiscordConfiguration` + - `InteractivityConfiguration` + - `LavalinkConfiguration` + - `VoiceNextConfiguration` + +____ +## Values +It's worth mentioning the required formats for certain value types + +### Enum +- Single Flag/Value + - "`Value`" +- Multiple Flags + - "`Flag1|Flag2|Flag3`" + +#### Example +```json +{ + "DisCatSharp": { + "Discord": { + "Intents": "GuildMembers|GuildsBans" + } + } +} +``` + +### TimeSpan +Hours:Minutes:Seconds "`HH:mm:ss`" + +#### Example +HttpTimeout of 5 minutes +```json +{ + "DisCatSharp": { + "Discord": { + "HttpTimeout": "00:05:00" + } + } +} +``` diff --git a/DisCatSharp.Docs/articles/toc.yml b/DisCatSharp.Docs/articles/toc.yml index d59c77dde..cbabde468 100644 --- a/DisCatSharp.Docs/articles/toc.yml +++ b/DisCatSharp.Docs/articles/toc.yml @@ -1,91 +1,93 @@ - name: Preamble href: preamble.md - name: Important Changes items: - name: Version 9.8.3 href: important_changes/9_8_3.md - name: Version 9.8.2 href: important_changes/9_8_2.md - name: The Basics items: - name: Creating a Bot Account href: basics/bot_account.md - name: Writing Your First Bot href: basics/first_bot.md + - name: Web Application + href: basics/web_app.md - name: Beyond Basics items: - name: Events href: beyond_basics/events.md - name: Logging href: beyond_basics/logging/default.md items: - name: The Default Logger href: beyond_basics/logging/default.md - name: Third Party Loggers href: beyond_basics/logging/third_party.md - name: Intents href: beyond_basics/intents.md - name: Sharding href: beyond_basics/sharding.md - name: Message Builder href: beyond_basics/messagebuilder.md - name: Components items: - name: Buttons href: beyond_basics/components/buttons.md - name: Select Menu href: beyond_basics/components/select_menus.md - name: Workarounds href: beyond_basics/workarounds.md - name: Application Commands items: - name: Introduction href: application_commands/intro.md - name: Options href: application_commands/options.md - name: Events href: application_commands/events.md - name: Commands items: - name: Introduction href: commands/intro.md - name: Command Attributes href: commands/command_attributes.md - name: Dependency Injection href: commands/dependency_injection.md - name: Customization items: - name: Help Formatter href: commands/help_formatter.md - name: Argument Converters href: commands/argument_converters.md - name: Command Handler href: commands/command_handler.md - name: Audio items: - name: Lavalink items: - name: Setup href: audio/lavalink/setup.md - name: Configuration href: audio/lavalink/configuration.md - name: Music Commands href: audio/lavalink/music_commands.md - name: VoiceNext items: - name: Prerequisites href: audio/voicenext/prerequisites.md - name: Transmitting href: audio/voicenext/transmit.md - name: Receiving href: audio/voicenext/receive.md - name: Interactivity href: interactivity.md - name: Hosting href: hosting.md - name: Miscellaneous items: - name: Nightly Builds href: misc/nightly_builds.md - name: Reporting Issues href: misc/reporting_issues.md diff --git a/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs b/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs index fbb58b3be..092098806 100644 --- a/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs +++ b/DisCatSharp.Hosting.DependencyInjection/ServiceCollectionExtensions.cs @@ -1,42 +1,25 @@ using Microsoft.Extensions.DependencyInjection; namespace DisCatSharp.Hosting.DependencyInjection { public static class ServiceCollectionExtensions { /// /// Add as a background service /// /// - /// is scoped to .
- /// Maps to implementation of - ///
- /// - /// - public static IServiceCollection AddDiscordHostedService(this IServiceCollection services) - { - services.AddSingleton(); - services.AddHostedService(provider => provider.GetRequiredService()); - - return services; - } - - /// - /// Add as a background service - /// - /// - /// is scoped to .
+ /// is scoped to ServiceLifetime.Singleton.
/// Maps to Implementation of ///
/// /// /// public static IServiceCollection AddDiscordHostedService(this IServiceCollection services) where TService : class, IDiscordHostedService { services.AddSingleton(); services.AddHostedService(provider => provider.GetRequiredService()); return services; } } } diff --git a/DisCatSharp.Hosting/DiscordHostedService.cs b/DisCatSharp.Hosting/DiscordHostedService.cs index cd584e04e..a3f3ba128 100644 --- a/DisCatSharp.Hosting/DiscordHostedService.cs +++ b/DisCatSharp.Hosting/DiscordHostedService.cs @@ -1,148 +1,149 @@ // 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 System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using DisCatSharp.Configuration; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace DisCatSharp.Hosting { /// /// Simple implementation for to work as a /// - public class DiscordHostedService : BackgroundService, IDiscordHostedService + public abstract class DiscordHostedService : BackgroundService, IDiscordHostedService { /// public DiscordClient Client { get; private set; } private readonly ILogger _logger; #pragma warning disable 8618 - public DiscordHostedService(IConfiguration config, ILogger logger, IServiceProvider provider) + protected DiscordHostedService(IConfiguration config, ILogger logger, IServiceProvider provider) { this._logger = logger; this.Initialize(config, provider); } + #pragma warning restore 8618 /// /// Automatically search for and configure /// /// /// private void Initialize(IConfiguration config, IServiceProvider provider) { var typeMap = config.FindImplementedExtensions(); this._logger.LogDebug($"Found the following config types: {string.Join("\n\t", typeMap.Keys)}"); this.Client = config.BuildClient(); foreach (var typePair in typeMap) try { /* If section is null --> utilize the default constructor This means the extension was explicitly added in the 'Using' array, but user did not wish to override any value(s) in the extension's config */ object configInstance = typePair.Value.Section.HasValue ? typePair.Value.Section.Value.ExtractConfig(() => ActivatorUtilities.CreateInstance(provider, typePair.Value.ConfigType)) : ActivatorUtilities.CreateInstance(provider, typePair.Value.ConfigType); /* Explanation for bindings Internal Constructors --> NonPublic Public Constructors --> Public Constructors --> Instance */ BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; var ctors = typePair.Value.ImplementationType.GetConstructors(flags); object? instance; /* Certain extensions do not require a configuration argument Those who do -- pass config instance in, Those who don't -- simply instantiate ActivatorUtilities requires a public constructor, anything with internal breaks */ if (ctors.Any(x => x.GetParameters().Length == 1 && x.GetParameters().First().ParameterType == typePair.Value.ConfigType)) instance = Activator.CreateInstance(typePair.Value.ImplementationType, flags, null, new[] { configInstance }, null); else instance = Activator.CreateInstance(typePair.Value.ImplementationType, true); if (instance == null) { this._logger.LogError($"Unable to instantiate '{typePair.Value.ImplementationType.Name}'"); continue; } // Add an easy reference to our extensions for later use this.Client.AddExtension((BaseExtension)instance); } catch (Exception ex) { this._logger.LogError($"Unable to register '{typePair.Value.ImplementationType.Name}': \n\t{ex.Message}"); } } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { if (this.Client == null) throw new NullReferenceException("Discord Client cannot be null"); await this.PreConnect(); await this.Client.ConnectAsync(); await this.PostConnect(); // Wait indefinitely -- but use stopping token so we can properly cancel if needed await Task.Delay(-1, stoppingToken); } /// /// Runs just prior to the bot connecting /// protected virtual Task PreConnect() => Task.CompletedTask; /// /// Runs immediately after the bot connects /// protected virtual Task PostConnect() => Task.CompletedTask; } } diff --git a/DisCatSharp.sln b/DisCatSharp.sln index 48770a423..2a0af2afc 100644 --- a/DisCatSharp.sln +++ b/DisCatSharp.sln @@ -1,141 +1,145 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29613.14 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp", "DisCatSharp\DisCatSharp.csproj", "{EB3D8310-DFAD-4295-97F9-82E253647583}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext", "DisCatSharp.VoiceNext\DisCatSharp.VoiceNext.csproj", "{FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4255B64D-92EC-46B3-BC3B-ED2C3A8073EE}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore BUILDING.md = BUILDING.md CODE_OF_CONDUCT.md = CODE_OF_CONDUCT.md CONTRIBUTING.md = CONTRIBUTING.md LICENSE.md = LICENSE.md README.md = README.md SECURITY.md = SECURITY.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext\DisCatSharp.CommandsNext.csproj", "{C8ED55FB-E028-468D-955F-1534C20274EF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Interactivity", "DisCatSharp.Interactivity\DisCatSharp.Interactivity.csproj", "{DD32BEC3-0189-479F-86DC-CCF95E5634A9}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F953F5D0-F0C9-41E6-ADBF-60A76D295899}" ProjectSection(SolutionItems) = preProject .nuget\NuGet.config = .nuget\NuGet.config EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build Items", "Build Items", "{84464D70-687B-40A8-836D-C4F737698969}" ProjectSection(SolutionItems) = preProject appveyor.yml = appveyor.yml .github\dependabot.yml = .github\dependabot.yml DisCatSharp.targets = DisCatSharp.targets docs-oneclick-rebuild.ps1 = docs-oneclick-rebuild.ps1 NuGet.targets = NuGet.targets oneclick-rebuild.ps1 = oneclick-rebuild.ps1 Package.targets = Package.targets rebuild-all.ps1 = rebuild-all.ps1 rebuild-docs.ps1 = rebuild-docs.ps1 rebuild-lib.ps1 = rebuild-lib.ps1 Version.targets = Version.targets EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{430C28D8-5F85-4D6E-AA68-211549435245}" ProjectSection(SolutionItems) = preProject .github\ISSUE_TEMPLATE\bug_report.md = .github\ISSUE_TEMPLATE\bug_report.md .github\ISSUE_TEMPLATE\feature_request.md = .github\ISSUE_TEMPLATE\feature_request.md .github\FUNDING.yml = .github\FUNDING.yml .github\pull_request_template.md = .github\pull_request_template.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Lavalink", "DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.VoiceNext.Natives", "DisCatSharp.VoiceNext.Natives\DisCatSharp.VoiceNext.Natives.csproj", "{BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.Common", "DisCatSharp.Common\DisCatSharp.Common.csproj", "{CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DisCatSharp.ApplicationCommands", "DisCatSharp.ApplicationCommands\DisCatSharp.ApplicationCommands.csproj", "{AD530FD0-523C-4DE7-9AF6-B9A3785492C2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Configuration", "DisCatSharp.Configuration\DisCatSharp.Configuration.csproj", "{603287D3-1EF2-47F1-A611-C7F25869DE14}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Configuration.Tests", "DisCatSharp.Configuration.Tests\DisCatSharp.Configuration.Tests.csproj", "{E15E88B4-63AD-42DE-B685-D31697C62194}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Hosting", "DisCatSharp.Hosting\DisCatSharp.Hosting.csproj", "{72CCE5D5-926B-432A-876A-065FA2BC9B7B}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Hosting.Tests", "DisCatSharp.Hosting.Tests\DisCatSharp.Hosting.Tests.csproj", "{D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Hosting.DependencyInjection", "DisCatSharp.Hosting.DependencyInjection\DisCatSharp.Hosting.DependencyInjection.csproj", "{2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B15E40E0-03FD-4852-B19B-2C50BCC67704}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EB3D8310-DFAD-4295-97F9-82E253647583}.Debug|Any CPU.Build.0 = Debug|Any CPU {EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.ActiveCfg = Release|Any CPU {EB3D8310-DFAD-4295-97F9-82E253647583}.Release|Any CPU.Build.0 = Release|Any CPU {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Debug|Any CPU.Build.0 = Debug|Any CPU {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.ActiveCfg = Release|Any CPU {FB6B9EE9-65FB-4DFB-8D51-06F0BE6C1BA5}.Release|Any CPU.Build.0 = Release|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Debug|Any CPU.Build.0 = Debug|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8ED55FB-E028-468D-955F-1534C20274EF}.Release|Any CPU.Build.0 = Release|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD32BEC3-0189-479F-86DC-CCF95E5634A9}.Release|Any CPU.Build.0 = Release|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8B8FB09-C6AF-4F28-89B8-B53EE0DCE6E5}.Release|Any CPU.Build.0 = Release|Any CPU {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEC47B41-71E4-41D1-A4F9-BB7C56A1B82B}.Release|Any CPU.Build.0 = Release|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Debug|Any CPU.Build.0 = Debug|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD84A5C7-C7FF-48CA-B23D-FA726CF80E09}.Release|Any CPU.Build.0 = Release|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {AD530FD0-523C-4DE7-9AF6-B9A3785492C2}.Release|Any CPU.Build.0 = Release|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Debug|Any CPU.Build.0 = Debug|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.ActiveCfg = Release|Any CPU {603287D3-1EF2-47F1-A611-C7F25869DE14}.Release|Any CPU.Build.0 = Release|Any CPU {E15E88B4-63AD-42DE-B685-D31697C62194}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E15E88B4-63AD-42DE-B685-D31697C62194}.Debug|Any CPU.Build.0 = Debug|Any CPU {E15E88B4-63AD-42DE-B685-D31697C62194}.Release|Any CPU.ActiveCfg = Release|Any CPU {E15E88B4-63AD-42DE-B685-D31697C62194}.Release|Any CPU.Build.0 = Release|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Debug|Any CPU.Build.0 = Debug|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.ActiveCfg = Release|Any CPU {72CCE5D5-926B-432A-876A-065FA2BC9B7B}.Release|Any CPU.Build.0 = Release|Any CPU {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Debug|Any CPU.Build.0 = Debug|Any CPU {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Release|Any CPU.ActiveCfg = Release|Any CPU {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94}.Release|Any CPU.Build.0 = Release|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D67D1DD-E5B2-40C7-80E2-54D63730E7F0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {430C28D8-5F85-4D6E-AA68-211549435245} = {4255B64D-92EC-46B3-BC3B-ED2C3A8073EE} + {D02B598A-F0C9-4A8C-B8DE-7C0BAC8C9B94} = {B15E40E0-03FD-4852-B19B-2C50BCC67704} + {E15E88B4-63AD-42DE-B685-D31697C62194} = {B15E40E0-03FD-4852-B19B-2C50BCC67704} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {23F3A981-51B8-4285-A38C-3267F1D25FE7} EndGlobalSection EndGlobal