mirror of https://github.com/oliverbooth/VPLink
refactor: separate interfaces from project ready for unit testing
This commit is contained in:
parent
ade948ccab
commit
073d704ce6
|
@ -0,0 +1,33 @@
|
|||
namespace VPLink.Common.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the bot configuration.
|
||||
/// </summary>
|
||||
public interface IBotConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the bot should announce avatar events.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="true" /> if the bot should announce avatar events; otherwise, <see langword="false" />.
|
||||
/// </value>
|
||||
bool AnnounceAvatarEvents { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the bot should announce avatar events for bots.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="true" /> if the bot should announce avatar events for bots; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </value>
|
||||
bool AnnounceBots { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the bot should relay messages from other bots.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="true" /> if the bot should relay messages from other bots; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </value>
|
||||
bool RelayBotMessages { get; }
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
using VpSharp;
|
||||
|
||||
namespace VPLink.Common.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the chat configuration.
|
||||
/// </summary>
|
||||
public interface IChatConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the message.
|
||||
/// </summary>
|
||||
/// <value>The message color.</value>
|
||||
uint Color { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font style of the message.
|
||||
/// </summary>
|
||||
/// <value>The font style.</value>
|
||||
FontStyle Style { get; set; }
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
namespace VPLink.Common.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the Discord configuration.
|
||||
/// </summary>
|
||||
public interface IDiscordConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the channel ID to which the bot should relay messages.
|
||||
/// </summary>
|
||||
/// <value>The channel ID.</value>
|
||||
ulong ChannelId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Discord token.
|
||||
/// </summary>
|
||||
/// <value>The Discord token.</value>
|
||||
string Token { get; }
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
namespace VPLink.Common.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the Virtual Paradise configuration.
|
||||
/// </summary>
|
||||
public interface IVirtualParadiseConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the display name of the bot.
|
||||
/// </summary>
|
||||
/// <value>The display name.</value>
|
||||
string BotName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the chat configuration.
|
||||
/// </summary>
|
||||
/// <value>The chat configuration.</value>
|
||||
IChatConfiguration Chat { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password with which to log in to Virtual Paradise.
|
||||
/// </summary>
|
||||
/// <value>The login password.</value>
|
||||
string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the username with which to log in to Virtual Paradise.
|
||||
/// </summary>
|
||||
/// <value>The login username.</value>
|
||||
string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the world into which the bot should enter.
|
||||
/// </summary>
|
||||
/// <value>The world to enter.</value>
|
||||
string World { get; set; }
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
namespace VPLink.Data;
|
||||
namespace VPLink.Common.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a message that is relayed between Discord and Virtual Paradise.
|
|
@ -1,6 +1,6 @@
|
|||
using VpSharp.Entities;
|
||||
|
||||
namespace VPLink.Services;
|
||||
namespace VPLink.Common.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a service that listens for, and triggers, avatar events.
|
|
@ -1,6 +1,6 @@
|
|||
using VPLink.Configuration;
|
||||
using VPLink.Common.Configuration;
|
||||
|
||||
namespace VPLink.Services;
|
||||
namespace VPLink.Common.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the configuration service.
|
||||
|
@ -11,17 +11,17 @@ public interface IConfigurationService
|
|||
/// Gets the bot configuration.
|
||||
/// </summary>
|
||||
/// <value>The bot configuration.</value>
|
||||
BotConfiguration BotConfiguration { get; }
|
||||
IBotConfiguration BotConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Discord configuration.
|
||||
/// </summary>
|
||||
/// <value>The Discord configuration.</value>
|
||||
DiscordConfiguration DiscordConfiguration { get; }
|
||||
IDiscordConfiguration DiscordConfiguration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Virtual Paradise configuration.
|
||||
/// </summary>
|
||||
/// <value>The Virtual Paradise configuration.</value>
|
||||
VirtualParadiseConfiguration VirtualParadiseConfiguration { get; }
|
||||
IVirtualParadiseConfiguration VirtualParadiseConfiguration { get; }
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
using VPLink.Data;
|
||||
using VPLink.Common.Data;
|
||||
using VpSharp.Entities;
|
||||
|
||||
namespace VPLink.Services;
|
||||
namespace VPLink.Common.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a service that listens for messages from the Discord bridge channel.
|
|
@ -1,6 +1,6 @@
|
|||
using VPLink.Data;
|
||||
using VPLink.Common.Data;
|
||||
|
||||
namespace VPLink.Services;
|
||||
namespace VPLink.Common.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an object that can be used as a relay target.
|
|
@ -1,6 +1,6 @@
|
|||
using VPLink.Data;
|
||||
using VPLink.Common.Data;
|
||||
|
||||
namespace VPLink.Services;
|
||||
namespace VPLink.Common.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a service that listens for messages from the Virtual Paradise world.
|
|
@ -0,0 +1,13 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="VpSharp" Version="0.1.0-nightly.43"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VPLink", "VPLink\VPLink.csproj", "{CD488A1E-0232-4EB5-A381-38A42B267B11}"
|
||||
EndProject
|
||||
|
@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Workflows", "Workflows", "{
|
|||
.github\workflows\release.yml = .github\workflows\release.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VPLink.Common", "VPLink.Common\VPLink.Common.csproj", "{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -29,5 +31,9 @@ Global
|
|||
{CD488A1E-0232-4EB5-A381-38A42B267B11}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CD488A1E-0232-4EB5-A381-38A42B267B11}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CD488A1E-0232-4EB5-A381-38A42B267B11}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F1CE3BA6-05AC-47F3-BB7E-C489DC132367}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
@ -1,33 +1,16 @@
|
|||
using VPLink.Common.Configuration;
|
||||
|
||||
namespace VPLink.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the bot configuration.
|
||||
/// </summary>
|
||||
public sealed class BotConfiguration
|
||||
/// <inheritdoc />
|
||||
internal sealed class BotConfiguration : IBotConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the bot should announce avatar events.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="true" /> if the bot should announce avatar events; otherwise, <see langword="false" />.
|
||||
/// </value>
|
||||
/// <inheritdoc />
|
||||
public bool AnnounceAvatarEvents { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the bot should announce avatar events for bots.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="true" /> if the bot should announce avatar events for bots; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </value>
|
||||
/// <inheritdoc />
|
||||
public bool AnnounceBots { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the bot should relay messages from other bots.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <see langword="true" /> if the bot should relay messages from other bots; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </value>
|
||||
/// <inheritdoc />
|
||||
public bool RelayBotMessages { get; set; } = false;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
using VPLink.Common.Configuration;
|
||||
using VpSharp;
|
||||
|
||||
namespace VPLink.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the chat configuration.
|
||||
/// </summary>
|
||||
public sealed class ChatConfiguration
|
||||
/// <inheritdoc />
|
||||
internal sealed class ChatConfiguration : IChatConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the message.
|
||||
/// </summary>
|
||||
/// <value>The message color.</value>
|
||||
/// <inheritdoc />
|
||||
public uint Color { get; set; } = 0x191970;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font style of the message.
|
||||
/// </summary>
|
||||
/// <value>The font style.</value>
|
||||
/// <inheritdoc />
|
||||
public FontStyle Style { get; set; } = FontStyle.Regular;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
using VPLink.Common.Configuration;
|
||||
|
||||
namespace VPLink.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the Discord configuration.
|
||||
/// </summary>
|
||||
public sealed class DiscordConfiguration
|
||||
/// <inheritdoc />
|
||||
internal sealed class DiscordConfiguration : IDiscordConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the channel ID to which the bot should relay messages.
|
||||
/// </summary>
|
||||
/// <value>The channel ID.</value>
|
||||
/// <inheritdoc />
|
||||
public ulong ChannelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Discord token.
|
||||
/// </summary>
|
||||
/// <value>The Discord token.</value>
|
||||
/// <inheritdoc />
|
||||
public string Token { get; set; } = string.Empty;
|
||||
}
|
||||
|
|
|
@ -1,37 +1,22 @@
|
|||
using VPLink.Common.Configuration;
|
||||
|
||||
namespace VPLink.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the Virtual Paradise configuration.
|
||||
/// </summary>
|
||||
public sealed class VirtualParadiseConfiguration
|
||||
/// <inheritdoc />
|
||||
internal sealed class VirtualParadiseConfiguration : IVirtualParadiseConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the display name of the bot.
|
||||
/// </summary>
|
||||
/// <value>The display name.</value>
|
||||
/// <inheritdoc />
|
||||
public string BotName { get; set; } = "VPLink";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the chat configuration.
|
||||
/// </summary>
|
||||
/// <value>The chat configuration.</value>
|
||||
public ChatConfiguration Chat { get; } = new();
|
||||
/// <inheritdoc />
|
||||
public IChatConfiguration Chat { get; } = new ChatConfiguration();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password with which to log in to Virtual Paradise.
|
||||
/// </summary>
|
||||
/// <value>The login password.</value>
|
||||
/// <inheritdoc />
|
||||
public string Password { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the username with which to log in to Virtual Paradise.
|
||||
/// </summary>
|
||||
/// <value>The login username.</value>
|
||||
/// <inheritdoc />
|
||||
public string Username { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the world into which the bot should enter.
|
||||
/// </summary>
|
||||
/// <value>The world to enter.</value>
|
||||
/// <inheritdoc />
|
||||
public string World { get; set; } = string.Empty;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ using Microsoft.Extensions.Hosting;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using Tomlyn.Extensions.Configuration;
|
||||
using VPLink.Common.Services;
|
||||
using VPLink.Services;
|
||||
using VpSharp;
|
||||
using X10D.Hosting.DependencyInjection;
|
||||
|
|
|
@ -2,7 +2,8 @@ using System.Reactive.Linq;
|
|||
using System.Reactive.Subjects;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using VPLink.Configuration;
|
||||
using VPLink.Common.Configuration;
|
||||
using VPLink.Common.Services;
|
||||
using VpSharp;
|
||||
using VpSharp.Entities;
|
||||
|
||||
|
@ -50,7 +51,7 @@ internal sealed class AvatarService : BackgroundService, IAvatarService
|
|||
{
|
||||
_logger.LogInformation("{Avatar} joined", avatar);
|
||||
|
||||
BotConfiguration configuration = _configurationService.BotConfiguration;
|
||||
IBotConfiguration configuration = _configurationService.BotConfiguration;
|
||||
if (!configuration.AnnounceAvatarEvents || avatar.IsBot && !configuration.AnnounceBots)
|
||||
return;
|
||||
|
||||
|
@ -61,7 +62,7 @@ internal sealed class AvatarService : BackgroundService, IAvatarService
|
|||
{
|
||||
_logger.LogInformation("{Avatar} left", avatar);
|
||||
|
||||
BotConfiguration configuration = _configurationService.BotConfiguration;
|
||||
IBotConfiguration configuration = _configurationService.BotConfiguration;
|
||||
if (!configuration.AnnounceAvatarEvents || avatar.IsBot && !configuration.AnnounceBots)
|
||||
return;
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using Microsoft.Extensions.Configuration;
|
||||
using VPLink.Common.Configuration;
|
||||
using VPLink.Common.Services;
|
||||
using VPLink.Configuration;
|
||||
|
||||
namespace VPLink.Services;
|
||||
|
@ -18,19 +20,19 @@ internal sealed class ConfigurationService : IConfigurationService
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public BotConfiguration BotConfiguration
|
||||
public IBotConfiguration BotConfiguration
|
||||
{
|
||||
get => _configuration.GetSection("Bot").Get<BotConfiguration>() ?? new BotConfiguration();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public DiscordConfiguration DiscordConfiguration
|
||||
public IDiscordConfiguration DiscordConfiguration
|
||||
{
|
||||
get => _configuration.GetSection("Discord").Get<DiscordConfiguration>() ?? new DiscordConfiguration();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public VirtualParadiseConfiguration VirtualParadiseConfiguration
|
||||
public IVirtualParadiseConfiguration VirtualParadiseConfiguration
|
||||
{
|
||||
get => _configuration.GetSection("VirtualParadise").Get<VirtualParadiseConfiguration>() ??
|
||||
new VirtualParadiseConfiguration();
|
||||
|
|
|
@ -8,9 +8,11 @@ using Discord;
|
|||
using Discord.WebSocket;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using VPLink.Configuration;
|
||||
using VPLink.Data;
|
||||
using VPLink.Common.Configuration;
|
||||
using VPLink.Common.Data;
|
||||
using VPLink.Common.Services;
|
||||
using VpSharp.Entities;
|
||||
using IUser = Discord.IUser;
|
||||
|
||||
namespace VPLink.Services;
|
||||
|
||||
|
@ -144,7 +146,7 @@ internal sealed partial class DiscordMessageService : BackgroundService, IDiscor
|
|||
|
||||
private bool TryGetRelayChannel([NotNullWhen(true)] out ITextChannel? channel)
|
||||
{
|
||||
DiscordConfiguration configuration = _configurationService.DiscordConfiguration;
|
||||
IDiscordConfiguration configuration = _configurationService.DiscordConfiguration;
|
||||
ulong channelId = configuration.ChannelId;
|
||||
|
||||
if (_discordClient.GetChannel(channelId) is ITextChannel textChannel)
|
||||
|
|
|
@ -4,7 +4,8 @@ using Discord.WebSocket;
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using VPLink.Commands;
|
||||
using VPLink.Configuration;
|
||||
using VPLink.Common.Configuration;
|
||||
using VPLink.Common.Services;
|
||||
|
||||
namespace VPLink.Services;
|
||||
|
||||
|
@ -48,7 +49,7 @@ internal sealed class DiscordService : BackgroundService
|
|||
_discordClient.Ready += OnReady;
|
||||
_discordClient.InteractionCreated += OnInteractionCreated;
|
||||
|
||||
DiscordConfiguration configuration = _configurationService.DiscordConfiguration;
|
||||
IDiscordConfiguration configuration = _configurationService.DiscordConfiguration;
|
||||
string token = configuration.Token ?? throw new InvalidOperationException("Token is not set.");
|
||||
|
||||
_logger.LogDebug("Connecting to Discord");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using VPLink.Common.Services;
|
||||
using VpSharp.Extensions;
|
||||
|
||||
namespace VPLink.Services;
|
||||
|
|
|
@ -3,15 +3,16 @@ using System.Reactive.Linq;
|
|||
using System.Reactive.Subjects;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using VPLink.Configuration;
|
||||
using VPLink.Data;
|
||||
using VPLink.Common.Configuration;
|
||||
using VPLink.Common.Data;
|
||||
using VPLink.Common.Services;
|
||||
using VpSharp;
|
||||
using VpSharp.Entities;
|
||||
using FontStyle = VpSharp.FontStyle;
|
||||
|
||||
namespace VPLink.Services;
|
||||
|
||||
/// <inheritdoc cref="VPLink.Services.IVirtualParadiseMessageService" />
|
||||
/// <inheritdoc cref="IVirtualParadiseMessageService" />
|
||||
internal sealed class VirtualParadiseMessageService : BackgroundService, IVirtualParadiseMessageService
|
||||
{
|
||||
private readonly ILogger<VirtualParadiseMessageService> _logger;
|
||||
|
@ -40,7 +41,7 @@ internal sealed class VirtualParadiseMessageService : BackgroundService, IVirtua
|
|||
/// <inheritdoc />
|
||||
public Task SendMessageAsync(RelayedMessage message)
|
||||
{
|
||||
ChatConfiguration configuration = _configurationService.VirtualParadiseConfiguration.Chat;
|
||||
IChatConfiguration configuration = _configurationService.VirtualParadiseConfiguration.Chat;
|
||||
|
||||
Color color = Color.FromArgb((int)configuration.Color);
|
||||
FontStyle style = configuration.Style;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
using System.Reactive.Subjects;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using VPLink.Common.Configuration;
|
||||
using VPLink.Common.Services;
|
||||
using VpSharp;
|
||||
using VpSharp.Entities;
|
||||
using VirtualParadiseConfiguration = VPLink.Configuration.VirtualParadiseConfiguration;
|
||||
|
||||
namespace VPLink.Services;
|
||||
|
||||
|
@ -35,7 +36,7 @@ internal sealed class VirtualParadiseService : BackgroundService
|
|||
_logger.LogInformation("Establishing relay");
|
||||
_virtualParadiseClient.MessageReceived.Subscribe(_messageReceived);
|
||||
|
||||
VirtualParadiseConfiguration configuration = _configurationService.VirtualParadiseConfiguration;
|
||||
IVirtualParadiseConfiguration configuration = _configurationService.VirtualParadiseConfiguration;
|
||||
|
||||
string username = configuration.Username ?? throw new InvalidOperationException("Username is not set.");
|
||||
string password = configuration.Password ?? throw new InvalidOperationException("Password is not set.");
|
||||
|
|
|
@ -55,4 +55,8 @@
|
|||
<PackageReference Include="ZString" Version="2.5.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\VPLink.Common\VPLink.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
Loading…
Reference in New Issue