Compare commits

..

No commits in common. "8002236c1f6e822721193e4972fd977febfaa809" and "f7f32c3f6a4e670902e3c9fbe43d363b64ddd3e2" have entirely different histories.

12 changed files with 44 additions and 276 deletions

View File

@ -4,11 +4,11 @@ using Avalonia.Data.Converters;
namespace DHT.Desktop.Common { namespace DHT.Desktop.Common {
sealed class NumberValueConverter : IValueConverter { sealed class NumberValueConverter : IValueConverter {
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return string.Format(Program.Culture, "{0:n0}", value); return string.Format(Program.Culture, "{0:n0}", value);
} }
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotSupportedException(); throw new NotSupportedException();
} }
} }

View File

@ -12,24 +12,22 @@
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages> <SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<AssemblyName>DiscordHistoryTracker</AssemblyName> <AssemblyName>DiscordHistoryTracker</AssemblyName>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute> <Version>33.1.0.0</Version>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute> <AssemblyVersion>$(Version)</AssemblyVersion>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute> <FileVersion>$(Version)</FileVersion>
<PackageVersion>$(Version)</PackageVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>none</DebugType> <DebugType>none</DebugType>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.12" /> <PackageReference Include="Avalonia" Version="0.10.10" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.12" /> <PackageReference Include="Avalonia.Desktop" Version="0.10.10" />
<ProjectReference Include="..\Server\Server.csproj" /> <ProjectReference Include="..\Server\Server.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Debug' "> <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.12" /> <PackageReference Include="Avalonia.Diagnostics" Version="0.10.10" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Version.cs" Link="Version.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="Windows\MainWindow.axaml.cs"> <Compile Update="Windows\MainWindow.axaml.cs">

View File

@ -1,119 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading.Tasks;
using DHT.Utils.Logging;
using static System.Environment.SpecialFolder;
using static System.Environment.SpecialFolderOption;
namespace DHT.Desktop.Discord {
static class DiscordAppSettings {
private static readonly Log Log = Log.ForType(typeof(DiscordAppSettings));
private const string JsonKeyDevTools = "DANGEROUS_ENABLE_DEVTOOLS_ONLY_ENABLE_IF_YOU_KNOW_WHAT_YOURE_DOING";
public static string JsonFilePath { get; }
private static string JsonBackupFilePath { get; }
[SuppressMessage("ReSharper", "ConvertIfStatementToConditionalTernaryExpression")]
static DiscordAppSettings() {
string rootFolder;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
rootFolder = Path.Combine(Environment.GetFolderPath(ApplicationData, DoNotVerify), "Discord");
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
rootFolder = Path.Combine(Environment.GetFolderPath(UserProfile, DoNotVerify), "Library", "Application Support", "Discord");
}
else {
rootFolder = Path.Combine(Environment.GetFolderPath(ApplicationData, DoNotVerify), "discord");
}
JsonFilePath = Path.Combine(rootFolder, "settings.json");
JsonBackupFilePath = JsonFilePath + ".bak";
}
public static async Task<bool?> AreDevToolsEnabled() {
try {
return AreDevToolsEnabled(await ReadSettingsJson());
} catch (Exception) {
return null;
}
}
private static bool AreDevToolsEnabled(Dictionary<string, object?> json) {
return json.TryGetValue(JsonKeyDevTools, out var value) && value is JsonElement { ValueKind: JsonValueKind.True };
}
public static async Task<SettingsJsonResult> ConfigureDevTools(bool enable) {
Dictionary<string, object?> json;
try {
json = await ReadSettingsJson();
} catch (FileNotFoundException) {
return SettingsJsonResult.FileNotFound;
} catch (JsonException) {
return SettingsJsonResult.InvalidJson;
} catch (Exception e) {
Log.Error(e);
return SettingsJsonResult.ReadError;
}
if (enable == AreDevToolsEnabled(json)) {
return SettingsJsonResult.AlreadySet;
}
if (enable) {
json[JsonKeyDevTools] = true;
}
else {
json.Remove(JsonKeyDevTools);
}
try {
if (!File.Exists(JsonBackupFilePath)) {
File.Copy(JsonFilePath, JsonBackupFilePath);
}
await WriteSettingsJson(json);
} catch (Exception e) {
Log.Error("An error occurred when writing settings file.");
Log.Error(e);
if (File.Exists(JsonBackupFilePath)) {
try {
File.Move(JsonBackupFilePath, JsonFilePath, true);
Log.Info("Restored settings file from backup.");
} catch (Exception e2) {
Log.Error("Cannot restore settings file from backup.");
Log.Error(e2);
}
}
return SettingsJsonResult.WriteError;
}
try {
File.Delete(JsonBackupFilePath);
} catch (Exception e) {
Log.Error("Cannot delete backup file.");
Log.Error(e);
}
return SettingsJsonResult.Success;
}
private static async Task<Dictionary<string, object?>> ReadSettingsJson() {
await using var stream = new FileStream(JsonFilePath, FileMode.Open, FileAccess.Read, FileShare.Read);
return await JsonSerializer.DeserializeAsync<Dictionary<string, object?>?>(stream) ?? throw new JsonException();
}
private static async Task WriteSettingsJson(Dictionary<string, object?> json) {
await using var stream = new FileStream(JsonFilePath, FileMode.Truncate, FileAccess.Write, FileShare.None);
await JsonSerializer.SerializeAsync(stream, json, new JsonSerializerOptions { WriteIndented = true });
}
}
}

View File

@ -1,10 +0,0 @@
namespace DHT.Desktop.Discord {
enum SettingsJsonResult {
Success,
AlreadySet,
FileNotFound,
ReadError,
InvalidJson,
WriteError
}
}

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using DHT.Desktop.Main.Controls; using DHT.Desktop.Main.Controls;
using DHT.Desktop.Main.Pages; using DHT.Desktop.Main.Pages;
@ -46,8 +45,8 @@ namespace DHT.Desktop.Main {
StatusBarModel.CurrentStatus = ServerLauncher.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped; StatusBarModel.CurrentStatus = ServerLauncher.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped;
} }
public async Task Initialize() { public void Initialize() {
await TrackingPageModel.Initialize(); TrackingPageModel.Initialize();
} }
private void TrackingPageModelOnServerStatusChanged(object? sender, StatusBarModel.Status e) { private void TrackingPageModelOnServerStatusChanged(object? sender, StatusBarModel.Status e) {

View File

@ -69,7 +69,7 @@ namespace DHT.Desktop.Main {
} }
} }
private async void WelcomeScreenModelOnPropertyChanged(object? sender, PropertyChangedEventArgs e) { private void WelcomeScreenModelOnPropertyChanged(object? sender, PropertyChangedEventArgs e) {
if (e.PropertyName == nameof(WelcomeScreenModel.Db)) { if (e.PropertyName == nameof(WelcomeScreenModel.Db)) {
if (MainContentScreenModel != null) { if (MainContentScreenModel != null) {
MainContentScreenModel.DatabaseClosed -= MainContentScreenModelOnDatabaseClosed; MainContentScreenModel.DatabaseClosed -= MainContentScreenModelOnDatabaseClosed;
@ -87,7 +87,7 @@ namespace DHT.Desktop.Main {
else { else {
Title = Path.GetFileName(db.Path) + " - " + DefaultTitle; Title = Path.GetFileName(db.Path) + " - " + DefaultTitle;
MainContentScreenModel = new MainContentScreenModel(window, db); MainContentScreenModel = new MainContentScreenModel(window, db);
await MainContentScreenModel.Initialize(); MainContentScreenModel.Initialize();
MainContentScreenModel.DatabaseClosed += MainContentScreenModelOnDatabaseClosed; MainContentScreenModel.DatabaseClosed += MainContentScreenModelOnDatabaseClosed;
MainContentScreen = new MainContentScreen { DataContext = MainContentScreenModel }; MainContentScreen = new MainContentScreen { DataContext = MainContentScreenModel };
OnPropertyChanged(nameof(MainContentScreen)); OnPropertyChanged(nameof(MainContentScreen));

View File

@ -23,13 +23,13 @@
<StackPanel Spacing="10"> <StackPanel Spacing="10">
<TextBlock TextWrapping="Wrap"> <TextBlock TextWrapping="Wrap">
To start tracking messages, copy the tracking script and paste it into the console of either the Discord app, or your browser. The console is usually opened by pressing Ctrl+Shift+I. To start tracking messages, copy the tracking script and paste it into the console of either the Discord app (Ctrl+Shift+I), or your browser with Discord open.
</TextBlock> </TextBlock>
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal" Spacing="10"> <StackPanel Orientation="Horizontal" Spacing="10">
<Button x:Name="CopyTrackingScript" Click="CopyTrackingScriptButton_OnClick">Copy Tracking Script</Button> <Button x:Name="CopyTrackingScript" Click="CopyTrackingScriptButton_OnClick">Copy Tracking Script</Button>
<Button Command="{Binding OnClickToggleTrackingButton}" Content="{Binding ToggleTrackingButtonText}" IsEnabled="{Binding IsToggleButtonEnabled}" /> <Button Command="{Binding OnClickToggleButton}" Content="{Binding ToggleButtonText}" IsEnabled="{Binding IsToggleButtonEnabled}" />
</StackPanel> </StackPanel>
<Expander Header="Advanced Tracking Settings"> <Expander Header="Advanced Settings">
<StackPanel Spacing="10"> <StackPanel Spacing="10">
<TextBlock TextWrapping="Wrap"> <TextBlock TextWrapping="Wrap">
The following settings determine how the tracking script communicates with this application. If you change them, you will have to copy and apply the tracking script again. The following settings determine how the tracking script communicates with this application. If you change them, you will have to copy and apply the tracking script again.
@ -55,10 +55,6 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Expander> </Expander>
<TextBlock TextWrapping="Wrap" Margin="0 15 0 0">
By default, the Discord app does not allow opening the console. The button below will change a hidden setting in the Discord app that controls whether the Ctrl+Shift+I shortcut is enabled.
</TextBlock>
<Button DockPanel.Dock="Right" Command="{Binding OnClickToggleAppDevTools}" Content="{Binding ToggleAppDevToolsButtonText}" IsEnabled="{Binding IsToggleAppDevToolsButtonEnabled}" />
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@ -24,7 +24,9 @@ namespace DHT.Desktop.Main.Pages {
var originalText = button.Content; var originalText = button.Content;
button.MinWidth = button.Bounds.Width; button.MinWidth = button.Bounds.Width;
if (await model.OnClickCopyTrackingScript() && !isCopyingScript) { await model.OnClickCopyTrackingScript();
if (!isCopyingScript) {
isCopyingScript = true; isCopyingScript = true;
button.Content = "Script Copied!"; button.Content = "Script Copied!";

View File

@ -4,7 +4,6 @@ using System.Web;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using DHT.Desktop.Dialogs.Message; using DHT.Desktop.Dialogs.Message;
using DHT.Desktop.Discord;
using DHT.Desktop.Main.Controls; using DHT.Desktop.Main.Controls;
using DHT.Server.Database; using DHT.Server.Database;
using DHT.Server.Service; using DHT.Server.Service;
@ -41,36 +40,14 @@ namespace DHT.Desktop.Main.Pages {
public bool HasMadeChanges => ServerPort != InputPort || ServerToken != InputToken; public bool HasMadeChanges => ServerPort != InputPort || ServerToken != InputToken;
private bool isToggleTrackingButtonEnabled = true; private bool isToggleButtonEnabled = true;
public bool IsToggleButtonEnabled { public bool IsToggleButtonEnabled {
get => isToggleTrackingButtonEnabled; get => isToggleButtonEnabled;
set => Change(ref isToggleTrackingButtonEnabled, value); set => Change(ref isToggleButtonEnabled, value);
} }
public string ToggleTrackingButtonText => ServerLauncher.IsRunning ? "Pause Tracking" : "Resume Tracking"; public string ToggleButtonText => ServerLauncher.IsRunning ? "Pause Tracking" : "Resume Tracking";
private bool areDevToolsEnabled;
private bool AreDevToolsEnabled {
get => areDevToolsEnabled;
set {
Change(ref areDevToolsEnabled, value);
OnPropertyChanged(nameof(ToggleAppDevToolsButtonText));
}
}
public bool IsToggleAppDevToolsButtonEnabled { get; private set; } = true;
public string ToggleAppDevToolsButtonText {
get {
if (!IsToggleAppDevToolsButtonEnabled) {
return "Unavailable";
}
return AreDevToolsEnabled ? "Disable Ctrl+Shift+I" : "Enable Ctrl+Shift+I";
}
}
public event EventHandler<StatusBarModel.Status>? ServerStatusChanged; public event EventHandler<StatusBarModel.Status>? ServerStatusChanged;
@ -85,7 +62,7 @@ namespace DHT.Desktop.Main.Pages {
this.db = db; this.db = db;
} }
public async Task Initialize() { public void Initialize() {
ServerLauncher.ServerStatusChanged += ServerLauncherOnServerStatusChanged; ServerLauncher.ServerStatusChanged += ServerLauncherOnServerStatusChanged;
ServerLauncher.ServerManagementExceptionCaught += ServerLauncherOnServerManagementExceptionCaught; ServerLauncher.ServerManagementExceptionCaught += ServerLauncherOnServerManagementExceptionCaught;
@ -93,15 +70,6 @@ namespace DHT.Desktop.Main.Pages {
string token = ServerToken; string token = ServerToken;
ServerLauncher.Relaunch(port, token, db); ServerLauncher.Relaunch(port, token, db);
} }
bool? devToolsEnabled = await DiscordAppSettings.AreDevToolsEnabled();
if (devToolsEnabled.HasValue) {
AreDevToolsEnabled = devToolsEnabled.Value;
}
else {
IsToggleAppDevToolsButtonEnabled = false;
OnPropertyChanged(nameof(IsToggleAppDevToolsButtonEnabled));
}
} }
public void Dispose() { public void Dispose() {
@ -110,17 +78,6 @@ namespace DHT.Desktop.Main.Pages {
ServerLauncher.Stop(); ServerLauncher.Stop();
} }
private void ServerLauncherOnServerStatusChanged(object? sender, EventArgs e) {
ServerStatusChanged?.Invoke(this, ServerLauncher.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped);
OnPropertyChanged(nameof(ToggleTrackingButtonText));
IsToggleButtonEnabled = true;
}
private async void ServerLauncherOnServerManagementExceptionCaught(object? sender, Exception ex) {
Log.Error(ex);
await Dialog.ShowOk(window, "Server Error", ex.Message);
}
private async Task<bool> StartServer() { private async Task<bool> StartServer() {
if (!int.TryParse(InputPort, out int port) || port is < 0 or > 65535) { if (!int.TryParse(InputPort, out int port) || port is < 0 or > 65535) {
await Dialog.ShowOk(window, "Invalid Port", "Port must be a number between 0 and 65535."); await Dialog.ShowOk(window, "Invalid Port", "Port must be a number between 0 and 65535.");
@ -139,7 +96,7 @@ namespace DHT.Desktop.Main.Pages {
ServerLauncher.Stop(); ServerLauncher.Stop();
} }
public async Task<bool> OnClickToggleTrackingButton() { public async Task<bool> OnClickToggleButton() {
if (ServerLauncher.IsRunning) { if (ServerLauncher.IsRunning) {
StopServer(); StopServer();
return true; return true;
@ -149,7 +106,7 @@ namespace DHT.Desktop.Main.Pages {
} }
} }
public async Task<bool> OnClickCopyTrackingScript() { public async Task OnClickCopyTrackingScript() {
string bootstrap = await Resources.ReadTextAsync("Tracker/bootstrap.js"); string bootstrap = await Resources.ReadTextAsync("Tracker/bootstrap.js");
string script = bootstrap.Replace("= 0; /*[PORT]*/", "= " + ServerPort + ";") string script = bootstrap.Replace("= 0; /*[PORT]*/", "= " + ServerPort + ";")
.Replace("/*[TOKEN]*/", HttpUtility.JavaScriptStringEncode(ServerToken)) .Replace("/*[TOKEN]*/", HttpUtility.JavaScriptStringEncode(ServerToken))
@ -157,19 +114,7 @@ namespace DHT.Desktop.Main.Pages {
.Replace("/*[CSS-CONTROLLER]*/", await Resources.ReadTextAsync("Tracker/styles/controller.css")) .Replace("/*[CSS-CONTROLLER]*/", await Resources.ReadTextAsync("Tracker/styles/controller.css"))
.Replace("/*[CSS-SETTINGS]*/", await Resources.ReadTextAsync("Tracker/styles/settings.css")); .Replace("/*[CSS-SETTINGS]*/", await Resources.ReadTextAsync("Tracker/styles/settings.css"));
var clipboard = Application.Current?.Clipboard; await Application.Current.Clipboard.SetTextAsync(script);
if (clipboard == null) {
await Dialog.ShowOk(window, "Copy Tracking Script", "Clipboard is not available on this system.");
return false;
}
try {
await clipboard.SetTextAsync(script);
return true;
} catch {
await Dialog.ShowOk(window, "Copy Tracking Script", "An error occurred while copying to clipboard.");
return false;
}
} }
public void OnClickRandomizeToken() { public void OnClickRandomizeToken() {
@ -189,42 +134,15 @@ namespace DHT.Desktop.Main.Pages {
InputToken = ServerToken; InputToken = ServerToken;
} }
public async void OnClickToggleAppDevTools() { private void ServerLauncherOnServerStatusChanged(object? sender, EventArgs e) {
const string DialogTitle = "Discord App Settings File"; ServerStatusChanged?.Invoke(this, ServerLauncher.IsRunning ? StatusBarModel.Status.Ready : StatusBarModel.Status.Stopped);
OnPropertyChanged(nameof(ToggleButtonText));
bool oldState = AreDevToolsEnabled; IsToggleButtonEnabled = true;
bool newState = !oldState;
switch (await DiscordAppSettings.ConfigureDevTools(newState)) {
case SettingsJsonResult.Success:
AreDevToolsEnabled = newState;
await Dialog.ShowOk(window, DialogTitle, "Ctrl+Shift+I was " + (newState ? "enabled." : "disabled.") + " Restart the Discord app for the change to take effect.");
break;
case SettingsJsonResult.AlreadySet:
await Dialog.ShowOk(window, DialogTitle, "Ctrl+Shift+I is already " + (newState ? "enabled." : "disabled."));
AreDevToolsEnabled = newState;
break;
case SettingsJsonResult.FileNotFound:
await Dialog.ShowOk(window, DialogTitle, "Cannot find the settings file:\n" + DiscordAppSettings.JsonFilePath);
break;
case SettingsJsonResult.ReadError:
await Dialog.ShowOk(window, DialogTitle, "Cannot read the settings file:\n" + DiscordAppSettings.JsonFilePath);
break;
case SettingsJsonResult.InvalidJson:
await Dialog.ShowOk(window, DialogTitle, "Unknown format of the settings file:\n" + DiscordAppSettings.JsonFilePath);
break;
case SettingsJsonResult.WriteError:
await Dialog.ShowOk(window, DialogTitle, "Cannot save the settings file:\n" + DiscordAppSettings.JsonFilePath);
break;
default:
throw new ArgumentOutOfRangeException();
} }
private async void ServerLauncherOnServerManagementExceptionCaught(object? sender, Exception ex) {
Log.Error(ex);
await Dialog.ShowOk(window, "Server Error", ex.Message);
} }
} }
} }

View File

@ -7,9 +7,10 @@
<Authors>chylex</Authors> <Authors>chylex</Authors>
<Company>DiscordHistoryTracker</Company> <Company>DiscordHistoryTracker</Company>
<Product>DiscordHistoryTrackerServer</Product> <Product>DiscordHistoryTrackerServer</Product>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute> <Version>33.1.0.0</Version>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute> <AssemblyVersion>$(Version)</AssemblyVersion>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute> <FileVersion>$(Version)</FileVersion>
<PackageVersion>$(Version)</PackageVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -24,7 +25,4 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Utils\Utils.csproj" /> <ProjectReference Include="..\Utils\Utils.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Include="..\Version.cs" Link="Version.cs" />
</ItemGroup>
</Project> </Project>

View File

@ -7,9 +7,10 @@
<Authors>chylex</Authors> <Authors>chylex</Authors>
<Company>DiscordHistoryTracker</Company> <Company>DiscordHistoryTracker</Company>
<Product>DiscordHistoryTrackerUtils</Product> <Product>DiscordHistoryTrackerUtils</Product>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute> <Version>33.1.0.0</Version>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute> <AssemblyVersion>$(Version)</AssemblyVersion>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute> <FileVersion>$(Version)</FileVersion>
<PackageVersion>$(Version)</PackageVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -18,7 +19,4 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="10.3.0" /> <PackageReference Include="JetBrains.Annotations" Version="10.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Compile Include="..\Version.cs" Link="Version.cs" />
</ItemGroup>
</Project> </Project>

View File

@ -1,12 +0,0 @@
using System.Reflection;
using DHT.Utils;
[assembly: AssemblyVersion(Version.Tag)]
[assembly: AssemblyFileVersion(Version.Tag)]
[assembly: AssemblyInformationalVersion(Version.Tag)]
namespace DHT.Utils {
static class Version {
public const string Tag = "32.2.0.0";
}
}