Refactor and enhance proxy and mafile handling

- Introduced `Filename` property in `Mafile` for better file management.
- Refactored `Storage` methods to support `Filename` and simplify `.mafile` handling.
- Enhanced `ProxyManagerView` with new controls for protocol and credentials display.
- Implemented `HintBox` control for displaying errors and tips in UI.
- Updated `MainWindow` with improved UI elements and event handling.
- Improved command execution checks in `MainVM` for better UX.
- Added localization strings for new features like proxy display options.
- General code cleanups and formatting improvements.
This commit is contained in:
achiez
2025-11-03 22:44:46 +02:00
parent b3e7436e95
commit ffcc7405a7
67 changed files with 815 additions and 556 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
using System.Reflection;
using AchiesUtilities.Extensions;
using AchiesUtilities.Extensions;
using NebulaAuth.LegacyConverter;
using Newtonsoft.Json;
using SteamLib.Utility.MafileSerialization;
using System.Reflection;
try
{
+1
View File
@@ -33,6 +33,7 @@
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
+1 -10
View File
@@ -1,8 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Windows;
using AchiesUtilities.Extensions;
using NebulaAuth.Core;
using NebulaAuth.Model;
using NebulaAuth.Model.Exceptions;
@@ -21,13 +18,7 @@ public partial class App
LocManager.Init();
LocManager.SetApplicationLocalization(Settings.Instance.Language);
Shell.Initialize();
var files = 0;
if (Directory.Exists(Storage.MAFILE_F))
files = Directory.GetFiles(Storage.MafileFolder)
.Count(f => Path.GetExtension(f).EqualsIgnoreCase(".mafile"));
var threads = files > 0 ? files / 100 + 1 : 1;
var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1;
await Storage.Initialize(threads);
var mainWindow = new MainWindow();
Current.MainWindow = mainWindow;
@@ -3,7 +3,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace NebulaAuth.Theme.Controls;
namespace NebulaAuth;
public class CodeProgressBar : ProgressBar
{
+57
View File
@@ -0,0 +1,57 @@
<UserControl x:Class="NebulaAuth.HintBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance nebulaAuth:HintBox}"
d:DesignHeight="100" d:DesignWidth="400">
<Border Padding="5"
BorderThickness="1"
CornerRadius="12"
Visibility="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource NullableToVisibilityConverter}}">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BaseShadowColor}" Opacity="0.5" />
</Border.BorderBrush>
<Border.Background>
<SolidColorBrush Color="{DynamicResource Base300Color}" Opacity="0.2" />
</Border.Background>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Иконка типа (info/error) -->
<materialDesign:PackIcon Height="22" Width="22"
VerticalAlignment="Center"
Margin="5,0,0,0"
Foreground="{Binding IconBrush, RelativeSource={RelativeSource AncestorType=UserControl}}"
Kind="{Binding IconKind, RelativeSource={RelativeSource AncestorType=UserControl}}" />
<!-- Текст подсказки -->
<TextBlock Grid.Column="1"
FontSize="{Binding FontSize, RelativeSource={RelativeSource AncestorType=UserControl}}"
TextWrapping="WrapWithOverflow"
VerticalAlignment="Center"
Margin="10,10,10,10"
Text="{Binding Text, RelativeSource={RelativeSource AncestorType=UserControl}}" />
<!-- Крестик (кнопка закрытия) -->
<Button Grid.Column="2"
Visibility="{Binding ShowCloseButton, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource BooleanToVisibilityConverter}}"
Command="{Binding CloseCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
Style="{StaticResource MaterialDesignToolButton}"
ToolTip="Закрыть"
Margin="0,0,5,0"
Padding="2">
<materialDesign:PackIcon Kind="Close" Width="18" Height="18" />
</Button>
</Grid>
</Border>
</UserControl>
+98
View File
@@ -0,0 +1,98 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using CommunityToolkit.Mvvm.Input;
using MaterialDesignThemes.Wpf;
namespace NebulaAuth;
public partial class HintBox : UserControl
{
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(string), typeof(HintBox));
public static readonly DependencyProperty SeverityProperty =
DependencyProperty.Register(nameof(Severity), typeof(HintBoxSeverity), typeof(HintBox),
new PropertyMetadata(HintBoxSeverity.Info, OnSeverityChanged));
public static readonly DependencyProperty ShowCloseButtonProperty =
DependencyProperty.Register(nameof(ShowCloseButton), typeof(bool), typeof(HintBox),
new PropertyMetadata(false));
public static readonly DependencyProperty CloseCommandProperty =
DependencyProperty.Register(nameof(CloseCommand), typeof(ICommand), typeof(HintBox),
new PropertyMetadata(null));
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public HintBoxSeverity Severity
{
get => (HintBoxSeverity) GetValue(SeverityProperty);
set => SetValue(SeverityProperty, value);
}
public ICommand CloseCommand
{
get => (ICommand) GetValue(CloseCommandProperty) ?? InternalCloseCommand;
set => SetValue(CloseCommandProperty, value);
}
public bool ShowCloseButton
{
get => (bool) GetValue(ShowCloseButtonProperty);
set => SetValue(ShowCloseButtonProperty, value);
}
private ICommand InternalCloseCommand { get; }
public PackIconKind IconKind { get; private set; }
public Brush IconBrush { get; private set; }
public HintBox()
{
InitializeComponent();
ApplySeverityVisuals();
InternalCloseCommand = new RelayCommand(Close);
}
public event RoutedEventHandler Closed;
private static void OnSeverityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((HintBox) d).ApplySeverityVisuals();
}
private void ApplySeverityVisuals()
{
switch (Severity)
{
case HintBoxSeverity.Error:
IconKind = PackIconKind.ErrorOutline;
IconBrush = (Brush) Application.Current.FindResource("ErrorBrush")!;
break;
default:
IconKind = PackIconKind.InfoCircleOutline;
IconBrush = (Brush) Application.Current.FindResource("InfoBrush")!;
break;
}
}
private void Close()
{
Visibility = Visibility.Collapsed;
Closed?.Invoke(this, new RoutedEventArgs());
}
}
public enum HintBoxSeverity
{
Info,
Error
}
@@ -8,6 +8,7 @@
<converters:ReverseBooleanConverter x:Key="ReverseBooleanConverter" />
<converters:ProxyTextConverter x:Key="ProxyTextConverter" />
<converters:ProxyDataTextConverter x:Key="ProxyDataTextConverter" />
<converters:ProxyDataTextMultiConverter x:Key="ProxyDataTextMultiConverter" />
<converters:MultiCommandParameterConverter x:Key="MultiCommandParameterConverter" />
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter" />
<converters:AnyMafilesToVisibilityConverter x:Key="AnyMafilesToVisibilityConverter" />
@@ -1,5 +1,6 @@
using System;
using System.Globalization;
using System.Text;
using System.Windows.Data;
using AchiesUtilities.Web.Proxy;
using NebulaAuth.Model.Entities;
@@ -33,11 +34,50 @@ public class ProxyDataTextConverter : IValueConverter
return string.Empty;
}
return $"{p.Address}:{p.Port}";
return $"{p.Protocol.ToString().ToLowerInvariant()}://{p.Address}:{p.Port}";
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
public class ProxyDataTextMultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var value = values[0] as ProxyData;
var displayProtocol = values.Length > 1 && values[1] is bool dp && dp;
var displayCredentials = values.Length > 2 && values[2] is bool dc && dc;
if (value == null)
{
return string.Empty;
}
var sb = new StringBuilder();
if (displayProtocol)
{
sb.Append(value.Protocol.ToString().ToLowerInvariant());
sb.Append("://");
}
sb.Append(value.Address);
sb.Append(':');
sb.Append(value.Port);
if (displayCredentials && value.AuthEnabled)
{
sb.Append(':');
sb.Append(value.Username);
sb.Append(':');
sb.Append(value.Password);
}
return sb.ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
+72 -59
View File
@@ -8,7 +8,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:entities="clr-namespace:NebulaAuth.Model.Entities"
xmlns:viewModel="clr-namespace:NebulaAuth.ViewModel"
xmlns:controls="clr-namespace:NebulaAuth.Theme.Controls"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
WindowStartupLocation="CenterScreen"
MinHeight="500" MinWidth="500"
Title="NebulaAuth" Height="800" Width="730"
@@ -73,8 +73,7 @@
<MenuItem Header="{Tr MainWindow.Menu.File.Caption}">
<MenuItem Header="{Tr MainWindow.Menu.File.Import}"
Command="{Binding AddMafileCommand}" />
<MenuItem IsEnabled="{Binding IsMafileSelected}"
Header="{Tr MainWindow.Menu.File.Remove}"
<MenuItem Header="{Tr MainWindow.Menu.File.Remove}"
Command="{Binding RemoveMafileCommand}" />
<MenuItem Header="{Tr MainWindow.Menu.File.OpenFolder}"
Command="{Binding OpenMafileFolderCommand}" />
@@ -93,11 +92,9 @@
Command="{Binding RemoveAuthenticatorCommand}" />
<MenuItem IsEnabled="False" Header="⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" Height="15" />
<MenuItem IsEnabled="{Binding IsMafileSelected}"
Header="{Tr MainWindow.Menu.Account.RefreshSession}"
<MenuItem Header="{Tr MainWindow.Menu.Account.RefreshSession}"
Command="{Binding RefreshSessionCommand}" />
<MenuItem IsEnabled="{Binding IsMafileSelected}"
Header="{Tr MainWindow.Menu.Account.LoginAgain}"
<MenuItem Header="{Tr MainWindow.Menu.Account.LoginAgain}"
Command="{Binding LoginAgainCommand}" />
</MenuItem>
</Menu>
@@ -254,8 +251,11 @@
Text="{Tr MainWindow.LeftPart.NoMafiles}" />
<ListBox FontSize="17" Margin="2"
x:Name="MafileListBox"
SelectionMode="Single"
ItemsSource="{Binding MaFiles}"
SelectedValue="{Binding SelectedMafile}">
SelectedValue="{Binding SelectedMafile}"
PreviewMouseDown="MafileListBox_OnPreviewMouseDown">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem"
BasedOn="{StaticResource MaterialDesignListBoxItem}">
@@ -265,7 +265,7 @@
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid ToolTip="{Binding Filename}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
@@ -358,57 +358,70 @@
</Border>
</Grid>
<md:Card BorderThickness="1" Style="{StaticResource MaterialDesignOutlinedCard}" Grid.Column="1"
Margin="10,10,15,10" UniformCornerRadius="12" Opacity="{Binding Settings.RightOpacity}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="115*" />
<RowDefinition Height="436*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Row="0">
<Grid Grid.Column="1">
<Border Background="{DynamicResource MaterialDesign.Brush.Card.Background}"
Opacity="{Binding Settings.RightOpacity}"
BorderThickness="1"
Margin="10,10,15,10" CornerRadius="12" VerticalAlignment="Stretch">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource Base300Color}" Opacity="0.3" />
</Border.BorderBrush>
</Border>
<md:Card Margin="10,10,15,10" Padding="2" UniformCornerRadius="12"
Background="Transparent" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="115*" />
<RowDefinition Height="436*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBox FontSize="24"
IsReadOnly="True" HorizontalContentAlignment="Center" Background="#00FFFFFF"
<Grid Row="0">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBox FontSize="24"
IsReadOnly="True" HorizontalContentAlignment="Center"
Background="#00FFFFFF"
Style="{StaticResource MaterialDesignTextBox}"
Style="{StaticResource MaterialDesignTextBox}"
Text="{Binding Code, FallbackValue=Code}">
<TextBox.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding CopyCodeCommand}" />
</TextBox.InputBindings>
</TextBox>
<controls:CodeProgressBar
Visibility="{Binding SelectedMafile, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="1" Height="4" Style="{StaticResource MaterialDesignLinearProgressBar}"
MaxTime="30" TimeRemaining="{Binding CodeProgress, Mode=OneWay}" />
Text="{Binding Code, FallbackValue=Code}">
<TextBox.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding CopyCodeCommand}" />
</TextBox.InputBindings>
</TextBox>
<nebulaAuth:CodeProgressBar
Visibility="{Binding SelectedMafile, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="1" Height="4"
Style="{StaticResource MaterialDesignLinearProgressBar}"
MaxTime="30" TimeRemaining="{Binding CodeProgress, Mode=OneWay}"
/>
</Grid>
<Button IsEnabled="{Binding IsMafileSelected}"
FontSize="14"
Grid.Row="1"
Style="{StaticResource MaterialDesignOutlinedButton}" Margin="0,10,0,10"
HorizontalAlignment="Center"
Content="{Tr MainWindow.RightPart.LoadConfirmations}"
Command="{Binding GetConfirmationsCommand}"
Width="{Binding Path=ActualWidth, Converter={StaticResource CoefficientConverter}, ConverterParameter=0.6, RelativeSource={RelativeSource AncestorType=md:Card}}" />
<ItemsControl md:RippleAssist.IsDisabled="True" Focusable="False" Grid.Row="2"
FontSize="16"
Margin="10,10,10,10"
Visibility="{Binding ConfirmationsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
ItemsSource="{Binding Confirmations}" Grid.RowSpan="2" />
<Button FontSize="16"
Style="{StaticResource MaterialDesignFlatButton}" Grid.Row="4"
Command="{Binding ConfirmLoginCommand}"
Content="{Tr MainWindow.RightPart.ConfirmLogin}" />
</Grid>
<Button IsEnabled="{Binding IsMafileSelected}"
FontSize="14"
Grid.Row="1"
Style="{StaticResource MaterialDesignOutlinedButton}" Margin="0,10,0,10"
HorizontalAlignment="Center"
Content="{Tr MainWindow.RightPart.LoadConfirmations}"
Command="{Binding GetConfirmationsCommand}"
Width="{Binding Path=ActualWidth, Converter={StaticResource CoefficientConverter}, ConverterParameter=0.6, RelativeSource={RelativeSource AncestorType=md:Card}}" />
<ItemsControl md:RippleAssist.IsDisabled="True" Focusable="False" Grid.Row="2"
FontSize="16"
Margin="10,10,10,10"
Visibility="{Binding ConfirmationsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
ItemsSource="{Binding Confirmations}" Grid.RowSpan="2" />
<Button Style="{StaticResource MaterialDesignFlatButton}" Grid.Row="4"
FontSize="18"
Command="{Binding ConfirmLoginCommand}"
Content="{Tr MainWindow.RightPart.ConfirmLogin}" />
</Grid>
</md:Card>
</md:Card>
</Grid>
</Grid>
<Border Grid.Row="2" BorderBrush="#1e1e24">
<Border.Background>
@@ -417,12 +430,12 @@
<Grid>
<ToolBarPanel TextElement.Foreground="{DynamicResource SecondaryContentBrush}"
Orientation="Horizontal" Margin="5">
<TextBlock FontSize="15" FontWeight="Bold" Text="{Tr MainWindow.Footer.Account}" />
<TextBlock FontSize="15" Text="{Binding SelectedMafile.AccountName}" />
<TextBlock FontSize="15" Text="{Tr MainWindow.Footer.AccountsCount}" />
<TextBlock FontSize="15" Text="{Binding MaFiles.Count}" />
<TextBlock FontSize="15" Text="|" Margin="10,0,10,0" />
<TextBlock FontSize="15" FontWeight="Bold" Text="{Tr MainWindow.Footer.Group}" />
<TextBlock FontSize="15" FontWeight="Normal"
Text="{Binding SelectedMafile.Group, TargetNullValue=' ', FallbackValue=' ', Mode=OneWay}" />
<TextBlock FontSize="15" Text="{Tr MainWindow.Footer.SelectedAccount}" />
<TextBlock FontSize="15"
Text="{Binding SelectedMafile.AccountName, TargetNullValue=' ', FallbackValue=' ', Mode=OneWay}" />
<!--<Button Command="{Binding DebugCommand}">Debug</Button>-->
</ToolBarPanel>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Right" FontSize="16"
+13
View File
@@ -2,6 +2,7 @@
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using MaterialDesignThemes.Wpf;
@@ -57,6 +58,18 @@ public partial class MainWindow
Keyboard.ClearFocus();
}
private void MafileListBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) return;
if (ItemsControl.ContainerFromElement((ListBox) sender, (DependencyObject) e.OriginalSource) is ListBoxItem
{
IsSelected: true
} item)
{
e.Handled = true;
}
}
#region Dran'n'Drop
private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
+2 -1
View File
@@ -20,8 +20,9 @@ public partial class Mafile : MobileDataExtended
set => SetProperty(ref _linkedClient, value);
}
[JsonIgnore] private PortableMaClient? _linkedClient;
[JsonIgnore] public string? Filename { get; set; }
[JsonIgnore] private PortableMaClient? _linkedClient;
public void SetSessionData(MobileSessionData? sessionData)
{
+11 -13
View File
@@ -40,21 +40,19 @@ public static class MaClient
public static void SetAccount(Mafile? account)
{
ClientHandler.CookieContainer.ClearAllCookies();
if (account != null)
if (account == null) return;
if (account.SessionData != null)
{
if (account.SessionData != null)
{
ClientHandler.CookieContainer.SetSteamMobileCookiesWithMobileToken(account.SessionData);
}
else
{
ClientHandler.CookieContainer.ClearSteamCookies();
ClientHandler.CookieContainer.AddMinimalMobileCookies();
AdmissionHelper.TransferCommunityCookies(ClientHandler.CookieContainer);
}
Proxy.SetData(account.Proxy?.Data);
ClientHandler.CookieContainer.SetSteamMobileCookiesWithMobileToken(account.SessionData);
}
else
{
ClientHandler.CookieContainer.ClearSteamCookies();
ClientHandler.CookieContainer.AddMinimalMobileCookies();
AdmissionHelper.TransferCommunityCookies(ClientHandler.CookieContainer);
}
Proxy.SetData(account.Proxy?.Data);
}
public static Task<IEnumerable<Confirmation>> GetConfirmations(Mafile mafile)
+4 -2
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using NebulaAuth.Model.Entities;
using NebulaAuth.Model.Exceptions;
using Newtonsoft.Json;
@@ -32,7 +33,7 @@ public static class NebulaSerializer
}
public static Mafile Deserialize(string cont)
public static Mafile Deserialize(string cont, string path)
{
var data = Serializer.Deserialize(cont);
var mobileData = data.Data;
@@ -41,12 +42,13 @@ public static class NebulaSerializer
throw new FormatException("Mafile is not extended data");
var props = info.UnusedProperties ?? new Dictionary<string, JProperty>();
var props = info.UnusedProperties ?? [];
var proxy = GetPropertyValue<MaProxy>("Proxy", props);
var group = GetPropertyValue<string>("Group", props);
var password = GetPropertyValue<string>("Password", props);
var mafile = Mafile.FromMobileDataExtended((MobileDataExtended) mobileData, proxy, group, password);
mafile.Filename = Path.GetFileName(path);
if (!info.SteamIdValid)
throw new MafileNeedReloginException(mafile);
+1 -1
View File
@@ -93,7 +93,7 @@ public static class ProxyStorage
Save();
}
public static void OrderCollection() //RETHINK: maybe there is a better way to handle it
public static void SortCollection() //RETHINK: maybe there is a better way to handle it
{
var proxies = Proxies.OrderBy(p => p.Key)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
+5 -2
View File
@@ -49,6 +49,7 @@ public partial class Settings : ObservableObject
Instance.PropertyChanged += SettingsOnPropertyChanged;
}
private static void SettingsOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
Save();
@@ -70,7 +71,7 @@ public partial class Settings : ObservableObject
Instance.BackgroundOpacity = 1.0;
Instance.BackgroundGamma = 0.0;
Instance.LeftOpacity = 0.4;
Instance.RightOpacity = 0.8;
Instance.RightOpacity = 0.4;
Instance.ApplyBlurBackground = true;
Instance.RippleDisabled = false;
Save();
@@ -96,13 +97,15 @@ public partial class Settings : ObservableObject
[ObservableProperty] private BackgroundMode _backgroundMode = BackgroundMode.Default;
[ObservableProperty] private double _leftOpacity = 0.4;
[ObservableProperty] private double _rightOpacity = 1.0;
[ObservableProperty] private double _rightOpacity = 0.4;
[ObservableProperty] private double _backgroundBlur;
[ObservableProperty] private double _backgroundOpacity = 1;
[ObservableProperty] private double _backgroundGamma;
[ObservableProperty] private bool _applyBlurBackground = true;
[ObservableProperty] private ThemeType _themeType = ThemeType.Default;
[ObservableProperty] private bool _rippleDisabled;
[ObservableProperty] private bool _proxyManagerDisplayProtocol;
[ObservableProperty] private bool _proxyManagerDisplayCredentials;
#endregion
}
+23 -53
View File
@@ -19,9 +19,6 @@ public static class Storage
{
public const string MAFILE_F = "maFiles";
public const string REMOVED_F = "maFiles_removed";
private static int _duplicateFound;
public static int DuplicateFound => _duplicateFound;
public static string MafileFolder { get; } = Path.GetFullPath(MAFILE_F);
public static string RemovedMafileFolder { get; } = Path.GetFullPath(REMOVED_F);
@@ -46,12 +43,7 @@ public static class Storage
.ToList();
var hashNames = new ConcurrentDictionary<string, byte>();
var hashIds = new ConcurrentDictionary<SteamId, byte>();
var localList = new ConcurrentBag<Mafile>();
var processed = 0;
await Task.Run(() =>
{
return Parallel.ForEachAsync(files,
@@ -61,14 +53,6 @@ public static class Storage
try
{
var data = await ReadMafileAsync(file);
if (!hashNames.TryAdd(data.AccountName, 0) ||
(data.SessionData != null && !hashIds.TryAdd(data.SteamId, 0)))
{
Interlocked.Increment(ref _duplicateFound);
Shell.Logger.Error("Duplicate mafile {file}", Path.GetFileName(file));
}
localList.Add(data);
}
catch (Exception ex)
@@ -114,31 +98,30 @@ public static class Storage
throw new FormatException("Can't generate code on this mafile", ex);
}
if (overwrite == false && File.Exists(CreatePathForMafile(data)))
if (overwrite == false && File.Exists(GetMafilePath(data)))
{
throw new IOException("File already exist and overwrite is False");
throw new IOException("File already exist and overwrite is False"); //TODO: Custom Exception
}
SaveMafile(data);
}
public static Mafile ReadMafile(string path)
{
var str = File.ReadAllText(path);
return NebulaSerializer.Deserialize(str);
return NebulaSerializer.Deserialize(str, path);
}
public static async Task<Mafile> ReadMafileAsync(string path)
{
var str = await File.ReadAllTextAsync(path);
return NebulaSerializer.Deserialize(str);
return NebulaSerializer.Deserialize(str, path);
}
public static void SaveMafile(Mafile data)
{
var path = CreatePathForMafile(data);
var path = GetMafilePath(data);
var str = NebulaSerializer.SerializeMafile(data);
File.WriteAllText(Path.GetFullPath(path), str);
@@ -156,34 +139,40 @@ public static class Storage
public static void UpdateMafile(Mafile data)
{
var path = CreatePathForMafile(data);
var path = GetMafilePath(data);
var str = NebulaSerializer.SerializeMafile(data);
File.WriteAllText(Path.GetFullPath(path), str);
}
public static void MoveToRemoved(Mafile data)
{
var path = CreatePathForMafile(data);
var copyPath = Path.Combine(REMOVED_F, data.SteamId + ".mafile");
var copyPathCompleted = copyPath;
var sourcePath = GetMafilePath(data);
var destinationPath = Path.Combine(REMOVED_F, data.Filename + ".mafile");
var destinationPathFinal = destinationPath;
var i = 0;
while (File.Exists(copyPathCompleted))
while (File.Exists(destinationPathFinal))
{
i++;
copyPathCompleted = copyPath + $" ({i})";
destinationPathFinal = destinationPath + $" ({i})";
}
File.Copy(path, copyPathCompleted, false);
File.Delete(path);
File.Copy(sourcePath, destinationPathFinal, false);
File.Delete(sourcePath);
MaFiles.Remove(data);
}
private static string CreatePathForMafile(Mafile data)
private static string GetMafilePath(Mafile data)
{
if (data.Filename != null)
{
return Path.Combine(MafileFolder, data.Filename);
}
var fileName = Settings.Instance.UseAccountNameAsMafileName
? CreateFileNameWithAccountName(data.AccountName)
: CreateFileNameWithSteamId(data.SteamId);
data.Filename = fileName;
return Path.Combine(MafileFolder, fileName);
}
@@ -197,29 +186,10 @@ public static class Storage
return steamId.Steam64.Id + ".mafile";
}
public static string? TryFindMafilePath(Mafile data)
public static string? TryGetMafilePath(Mafile data)
{
var pathFileName = Path.Combine(MafileFolder, CreateFileNameWithAccountName(data.AccountName));
string? pathSteamId = null;
if (data.SessionData != null)
{
pathSteamId = Path.Combine(MafileFolder, CreateFileNameWithSteamId(data.SteamId));
}
var steamIdExist = pathSteamId != null && File.Exists(pathSteamId);
var accountNameExist = File.Exists(pathFileName);
if (steamIdExist && accountNameExist)
{
return Settings.Instance.UseAccountNameAsMafileName ? pathFileName : pathSteamId;
}
if (steamIdExist ^ accountNameExist)
{
return steamIdExist ? pathSteamId : pathFileName;
}
return null;
if (data.Filename == null) return null;
return Path.Combine(MafileFolder, data.Filename);
}
public static void BackupHandler(MobileDataExtended data)
+5
View File
@@ -65,4 +65,9 @@
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Theme\Controls\" />
</ItemGroup>
</Project>
+3 -6
View File
@@ -1,6 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml"
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=viewmodel_005Clinker_005Csteps/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=components/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=viewmodel_005Clinker_005Csteps/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -23,6 +23,7 @@
Margin="0,10,0,0" />
<TextBox
Padding="8"
TabIndex="1"
FontSize="14"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}"
+10 -36
View File
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:linker="clr-namespace:NebulaAuth.ViewModel.Linker"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d"
TextElement.Foreground="{DynamicResource BaseContentBrush}"
FontFamily="{materialDesign:MaterialDesignFont}"
@@ -29,8 +30,6 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="materialDesign:PackIcon">
@@ -81,45 +80,20 @@
</Button>
</Grid>
<Separator Background="DarkGray" Opacity="0.4" Grid.Row="1" />
<Border Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="2" Margin="10,10,10,0" Padding="5"
BorderThickness="1"
CornerRadius="12">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BaseShadowColor}" Opacity="0.5" />
</Border.BorderBrush>
<Border.Background>
<SolidColorBrush Color="{DynamicResource Base300Color}" Opacity="0.2" />
</Border.Background>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="InfoCircleOutline" Foreground="{DynamicResource InfoBrush}" Height="22"
Width="22" VerticalAlignment="Center" Margin="5,0,0,0" />
<TextBlock VerticalAlignment="Center" MaxWidth="330" FontSize="14" TextWrapping="WrapWithOverflow"
Margin="10,10,10,10" Text="{Binding Tip}" />
</StackPanel>
</Border>
<nebulaAuth:HintBox FontSize="14"
Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="2" Margin="10,10,10,0"
Text="{Binding Tip}" MaxWidth="330" />
<ContentControl
Grid.Row="3" Margin="20"
d:DataContext="{d:DesignInstance Type=linker:DesignLinkAccountAuthStepVM, IsDesignTimeCreatable=True}"
Content="{Binding CurrentStep}" />
<Border Visibility="{Binding Error, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="4" Margin="10,0,10,10" Padding="5"
BorderThickness="1"
CornerRadius="12">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BaseShadowColor}" Opacity="0.5" />
</Border.BorderBrush>
<Border.Background>
<SolidColorBrush Color="{DynamicResource Base300Color}" Opacity="0.2" />
</Border.Background>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="ErrorOutline" Foreground="{DynamicResource ErrorBrush}" Height="22"
Width="22" VerticalAlignment="Center" Margin="5,0,0,0" />
<TextBlock VerticalAlignment="Center" MaxWidth="330" FontSize="14" TextWrapping="WrapWithOverflow"
Margin="10,10,10,10" Text="{Binding Error}" />
</StackPanel>
</Border>
<nebulaAuth:HintBox FontSize="14"
Visibility="{Binding Error, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="4" Margin="10,0,10,10"
Severity="Error"
Text="{Binding Error}" />
</Grid>
</UserControl>
@@ -26,6 +26,7 @@
<TextBox
Padding="8"
FontSize="14"
TabIndex="1"
Style="{StaticResource MaterialDesignOutlinedTextBox}"
Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}"
materialDesign:HintAssist.Hint="{Tr LinkerDialog.Password}"
+9 -35
View File
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mafileMover="clr-namespace:NebulaAuth.ViewModel.MafileMover"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d"
TextElement.Foreground="{DynamicResource BaseContentBrush}"
FontFamily="{materialDesign:MaterialDesignFont}"
@@ -76,43 +77,16 @@
</Button>
</Grid>
<Separator Background="DarkGray" Opacity="0.4" Grid.Row="1" />
<Border Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="2" Margin="10,10,10,0" Padding="5"
BorderThickness="1"
CornerRadius="12">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BaseShadowColor}" Opacity="0.5" />
</Border.BorderBrush>
<Border.Background>
<SolidColorBrush Color="{DynamicResource Base300Color}" Opacity="0.2" />
</Border.Background>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="InfoCircleOutline" Foreground="{DynamicResource InfoBrush}" Height="22"
Width="22" VerticalAlignment="Center" Margin="5,0,0,0" />
<TextBlock VerticalAlignment="Center" MaxWidth="330" FontSize="14" TextWrapping="WrapWithOverflow"
Margin="10,10,10,10" Text="{Binding Tip}" />
</StackPanel>
</Border>
<nebulaAuth:HintBox FontSize="14"
Visibility="{Binding Tip, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="2" Margin="10,10,10,0"
Text="{Binding Tip}" />
<ContentControl
Grid.Row="3" Margin="20" Content="{Binding CurrentStep}" />
<Border Visibility="{Binding Error, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="4" Margin="10,0,10,10" Padding="5"
BorderThickness="1"
CornerRadius="12">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource BaseShadowColor}" Opacity="0.5" />
</Border.BorderBrush>
<Border.Background>
<SolidColorBrush Color="{DynamicResource Base300Color}" Opacity="0.2" />
</Border.Background>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="ErrorOutline" Foreground="{DynamicResource ErrorBrush}" Height="22"
Width="22" VerticalAlignment="Center" Margin="5,0,0,0" />
<TextBlock VerticalAlignment="Center" MaxWidth="330" FontSize="14" TextWrapping="WrapWithOverflow"
Margin="10,10,10,10" Text="{Binding Error}" />
</StackPanel>
</Border>
<nebulaAuth:HintBox FontSize="14"
Visibility="{Binding Error, Converter={StaticResource NullableToVisibilityConverter}}"
Grid.Row="4" Margin="10,0,10,10"
Text="{Binding Error}" />
</Grid>
</UserControl>
+91 -36
View File
@@ -5,15 +5,19 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
xmlns:nebulaAuth="clr-namespace:NebulaAuth"
mc:Ignorable="d"
d:DesignHeight="500" d:DesignWidth="400"
FontFamily="{md:MaterialDesignFont}"
MinHeight="500"
MinWidth="400"
MaxHeight="550"
MaxWidth="500"
d:DataContext="{d:DesignInstance other:ProxyManagerVM}"
Background="Transparent">
d:Background="#141119"
d:Foreground="White"
FontFamily="{md:MaterialDesignFont}"
MinHeight="700"
MinWidth="600"
MaxHeight="700"
MaxWidth="600"
Background="Transparent"
FontSize="16">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -42,21 +46,32 @@
</Grid>
<Separator Grid.Row="1" />
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Stretch"
Margin="15" FontSize="16">
<Run Text="{Tr ProxyManagerDialog.DefaultProxy, IsDynamic=False}" />
<Run Text="{Binding DefaultProxy.Key, StringFormat='&#x0a;0:', FallbackValue='-', Mode=OneWay}" />
<Run
Text="{Binding DefaultProxy.Value, Converter='{StaticResource ProxyDataTextConverter}', Mode=OneWay, FallbackValue=''}" />
</TextBlock>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Margin="15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Stretch">
<Run Text="{Tr ProxyManagerDialog.DefaultProxy, IsDynamic=False}" />
<Run Text="{Binding DefaultProxy.Key, StringFormat='&#x0a;0:', FallbackValue='-', Mode=OneWay}" />
<Run
Text="{Binding DefaultProxy.Value, Converter='{StaticResource ProxyDataTextConverter}', Mode=OneWay, FallbackValue=''}" />
</TextBlock>
<Button Grid.Column="1" Command="{Binding RemoveDefaultCommand}">
<md:PackIcon Kind="ClearBox" Width="20" Height="20" />
</Button>
<Button Grid.Column="1" Command="{Binding RemoveDefaultCommand}">
<md:PackIcon Kind="ClearBox" Width="20" Height="20" />
</Button>
</Grid>
<StackPanel Grid.Row="1" Margin="15,0,15,15">
<CheckBox Content="{Tr ProxyManagerDialog.DisplayProxyProtocol}" IsChecked="{Binding DisplayProtocol}" />
<CheckBox Content="{Tr ProxyManagerDialog.DisplayProxyCredentials}"
IsChecked="{Binding DisplayCredentials}" />
</StackPanel>
</Grid>
<md:Card Grid.Row="3" Margin="10">
<ListBox VirtualizingStackPanel.VirtualizationMode="Recycling" FontSize="14"
@@ -78,6 +93,9 @@
<MenuItem Header="{Tr MainWindow.ContextMenus.Proxy.CopyAddress}"
Command="{Binding PlacementTarget.Tag.CopyProxyAddressCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding Value}" />
<MenuItem Header="{Tr Common.Delete}"
Command="{Binding PlacementTarget.Tag.RemoveProxyCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding Value}" />
</ContextMenu>
</Setter.Value>
</Setter>
@@ -96,8 +114,18 @@
</Grid.ColumnDefinitions>
<TextBlock VerticalAlignment="Center">
<Run Text="{Binding Key, Mode=OneWay}" /><Run Text=": " />
<Run
Text="{Binding Value, Mode=OneWay, Converter={StaticResource ProxyDataTextConverter}}" />
<Run>
<Run.Text>
<MultiBinding Mode="OneWay"
Converter="{StaticResource ProxyDataTextMultiConverter}">
<Binding Path="Value" />
<Binding Path="DataContext.DisplayProtocol"
RelativeSource="{RelativeSource AncestorType=UserControl}" />
<Binding Path="DataContext.DisplayCredentials"
RelativeSource="{RelativeSource AncestorType=UserControl}" />
</MultiBinding>
</Run.Text>
</Run>
</TextBlock>
<Button Style="{StaticResource MaterialDesignIconButton}" Padding="0" Width="24"
@@ -115,20 +143,47 @@
</ListBox>
</md:Card>
<Grid Grid.Row="4" Margin="5,0,15,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding AddProxyField}" FontSize="12" md:TextFieldAssist.HasClearButton="True"
AcceptsReturn="True" MaxHeight="100" Style="{StaticResource MaterialDesignOutlinedTextBox}"
Margin="15" md:HintAssist.Hint="IP:PORT:USER:PASS{ID}" />
<Button IsDefault="True" Grid.Column="1" Command="{Binding AddProxyCommand}">
<md:PackIcon Kind="Add" />
</Button>
<Button Grid.Column="2" Command="{Binding RemoveProxyCommand}">
<md:PackIcon Kind="Trash" />
</Button>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<nebulaAuth:HintBox
Visibility="{Binding ErrorText, Converter={StaticResource NullableToVisibilityConverter}}"
Margin="15,0,15,0"
Grid.Row="0"
Severity="Error"
FontSize="14"
Text="{Binding ErrorText}"
CloseCommand="{Binding ClearErrorCommand}"
ShowCloseButton="True" />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding AddProxyField}"
KeyDown="ProxyInput_KeyDown"
FontSize="12"
md:TextFieldAssist.HasClearButton="True"
AcceptsReturn="True"
MaxHeight="100" Style="{StaticResource MaterialDesignOutlinedTextBox}"
Margin="15"
Height="90"
md:HintAssist.IsFloating="False"
md:HintAssist.Hint="IP:PORT&#x0a;IP:PORT:USER:PASS&#x0a;PROTOCOL://IP:PORT:USER:PASS&#x0a;IP:PORT:USER:PASS{ID}
" />
<Button x:Name="AddProxyBtn" ToolTip="CTRL+ENTER" IsDefault="True" Grid.Column="1" Height="90"
Command="{Binding AddProxyCommand}">
<md:PackIcon Kind="Add" />
</Button>
<Button Grid.Column="2" Height="90" Command="{Binding RemoveProxyCommand}" CommandParameter="{x:Null}">
<md:PackIcon Kind="Trash" />
</Button>
</Grid>
</Grid>
+12 -1
View File
@@ -1,4 +1,7 @@
namespace NebulaAuth.View;
using System.Windows.Controls;
using System.Windows.Input;
namespace NebulaAuth.View;
/// <summary>
/// Логика взаимодействия для ProxyManagerView.xaml
@@ -9,4 +12,12 @@ public partial class ProxyManagerView
{
InitializeComponent();
}
private void ProxyInput_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Enter || !Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) return;
var tb = sender as TextBox;
tb?.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
AddProxyBtn.Command.Execute(null);
}
}
+28 -18
View File
@@ -9,11 +9,11 @@
d:DesignHeight="650"
FontFamily="{materialDesign:MaterialDesignFont}"
MinHeight="400"
MaxHeight="600"
MinWidth="400"
MaxWidth="400"
d:DataContext="{d:DesignInstance other:SettingsVM}"
Background="Transparent">
Background="Transparent"
FontSize="15">
<Grid>
<Grid>
<Grid.RowDefinitions>
@@ -49,23 +49,24 @@
<TabItem Header="{Tr SettingsDialog.MainSettings}">
<StackPanel Width="340" Margin="10,20,10,40" Orientation="Vertical" HorizontalAlignment="Center">
<ComboBox Style="{StaticResource MaterialDesignFloatingHintComboBox}" Margin="0,15,0,0"
FontSize="16"
ItemsSource="{Binding Languages}" DisplayMemberPath="Value" SelectedValuePath="Key"
SelectedValue="{Binding Language}"
materialDesign:HintAssist.Hint="{Tr LanguageWord}" />
<CheckBox Margin="0,15,0,0" FontSize="16" IsChecked="{Binding HideToTray}"
<CheckBox Margin="0,15,0,0" IsChecked="{Binding HideToTray}"
Content="{Tr SettingsDialog.MinimizeToTray}" />
<CheckBox Margin="0,15,0,0" FontSize="16" IsChecked="{Binding UseIcon}"
<CheckBox Margin="0,15,0,0" IsChecked="{Binding UseIcon}"
Content="{Tr SettingsDialog.UseIndicator}"
ToolTip="{Tr SettingsDialog.UseIndicatorHint}" />
<materialDesign:ColorPicker IsEnabled="{Binding UseIcon}" Color="{Binding IconColor, Delay=50}" />
<Grid Margin="0,20,0,10">
<materialDesign:ColorPicker Margin="-5,0,-5,0" IsEnabled="{Binding UseIcon}"
Color="{Binding IconColor, Delay=50}" />
<Grid Margin="0,10,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<PasswordBox MinWidth="250" materialDesign:PasswordBoxAssist.Password="{Binding Password}"
Height="Auto" VerticalAlignment="Center" FontSize="16"
Height="Auto" VerticalAlignment="Center"
Style="{StaticResource MaterialDesignFloatingHintRevealPasswordBox}"
materialDesign:HintAssist.Hint="{Tr SettingsDialog.PasswordBox.CurrentCryptPassword}"
materialDesign:HintAssist.HelperText="{Tr SettingsDialog.PasswordBox.Hint}" />
@@ -74,30 +75,38 @@
<materialDesign:PackIcon Kind="ContentSave" />
</Button>
</Grid>
<CheckBox Margin="0,15,0,0" FontSize="16" IsChecked="{Binding LegacyMode}"
<CheckBox Margin="0,16,0,0" IsChecked="{Binding LegacyMode}"
Content="{Tr SettingsDialog.LegacyMafileMode}"
ToolTip="{Tr SettingsDialog.LegacyMafileModeHint}" />
<!--<CheckBox IsEnabled="False" Margin="0,10,0,0" FontSize="16" IsChecked="{Binding AllowAutoUpdate}" Content="{Tr SettingsDialog.AllowAutoUpdate}"/>-->
<CheckBox Margin="0,15,0,0" FontSize="16" IsChecked="{Binding UseAccountNameAsMafileName}">
<TextBlock TextWrapping="WrapWithOverflow" Text="{Tr SettingsDialog.UseAccountName}" />
</CheckBox>
<CheckBox Style="{StaticResource MaterialDesignCheckBox}" BorderBrush="AliceBlue"
BorderThickness="2" IsChecked="{Binding IgnorePatchTuesdayErrors}" Margin="0,15,0,0"
FontSize="16"
Margin="0,12,0,0"
BorderThickness="2"
IsChecked="{Binding IgnorePatchTuesdayErrors}"
ToolTip="{Tr SettingsDialog.IgnorePatchTuesdayErrorsHint}">
<TextBlock TextWrapping="WrapWithOverflow"
Text="{Tr SettingsDialog.IgnorePatchTuesdayErrors}" />
</CheckBox>
<!--<CheckBox Margin="0,12,0,0" IsChecked="{Binding UseAccountNameAsMafileName}">
<TextBlock TextWrapping="WrapWithOverflow" Text="{Tr SettingsDialog.UseAccountName}" />
</CheckBox>-->
<ComboBox materialDesign:HintAssist.Hint="Именование мафайлов"
Margin="0,12,0,0"
Style="{StaticResource MaterialDesignFloatingHintComboBox}">
<ComboBoxItem Content="steamId.mafile" />
<ComboBoxItem Content="login.mafile" />
</ComboBox>
<Button>Применить и переименовать</Button>
</StackPanel>
</TabItem>
<TabItem IsEnabled="True" Header="{Tr SettingsDialog.ThemeSettings}">
<TabItem TextElement.FontSize="13" IsEnabled="True" Header="{Tr SettingsDialog.ThemeSettings}">
<StackPanel Width="340" Margin="10,20,10,10" Orientation="Vertical" HorizontalAlignment="Center">
<ComboBox Style="{StaticResource MaterialDesignOutlinedComboBox}"
Padding="8"
Margin="0,15,0,0"
FontSize="16"
FontSize="15"
ItemsSource="{Binding ThemeTypes}" DisplayMemberPath="Value" SelectedValuePath="Key"
SelectedValue="{Binding ThemeType}"
materialDesign:HintAssist.Hint="{Tr SettingsDialog.Theme.CurrentTheme}" />
@@ -105,7 +114,7 @@
<ComboBox Style="{StaticResource MaterialDesignOutlinedComboBox}"
Padding="8"
Margin="0,15,0,0"
FontSize="16"
FontSize="15"
ItemsSource="{Binding BackgroundModes}" DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding BackgroundMode}"
@@ -113,6 +122,7 @@
<TextBlock Text="{Tr SettingsDialog.Theme.BackgroundBlur}" Margin="0,15,0,0" />
<Slider materialDesign:SliderAssist.HideActiveTrack="True"
TickFrequency="5"
Minimum="0" Maximum="100" Value="{Binding BackgroundBlur, Mode=TwoWay}"
materialDesign:HintAssist.Hint="Blur Amount"
@@ -230,6 +230,90 @@ public partial class LinkAccountVM : ObservableObject, ISmsCodeProvider, IPhoneN
await Done(mafile.RevocationCode ?? string.Empty, mafile.SteamId.Steam64.ToString(), login);
}
private void SetCurrentStep(LinkAccountStepVM step)
{
Dispatcher.CurrentDispatcher.Invoke(() =>
{
CurrentStep = step;
Tip = CurrentStep.Tip;
});
}
[RelayCommand]
private void OpenTroubleshooting()
{
const string troubleshootingURI =
"https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/docs/{0}/LinkingTroubleshooting";
var localized = string.Format(troubleshootingURI, LocManager.GetCurrentLanguageCode());
Process.Start(new ProcessStartInfo(new Uri(localized).ToString())
{
UseShellExecute = true
});
}
private static string GetLocalizationOrDefault(string key)
{
return LocManager.GetCodeBehindOrDefault(key, LOCALIZATION_KEY, key);
}
private static string GetLocalizationCommon(string key)
{
return LocManager.GetCommonOrDefault(key, key);
}
public void Dispose()
{
_client.Dispose();
_handler.Dispose();
try
{
_cts.Cancel();
}
catch
{
//Ignored, may be cancelled or disposed
}
_cts.Dispose();
}
// @formatter:off
#region Step 2: Email Code
public bool IsSupportedGuardType(ILoginConsumer consumer, EAuthSessionGuardType type)
{
return type == EAuthSessionGuardType.EmailCode;
}
// Step 2: Email Code
public async Task UpdateAuthSession(HttpClient authClient, ILoginConsumer loginConsumer,
UpdateAuthSessionModel model,
CancellationToken cancellationToken = default)
{
while (true)
{
try
{
var step = new LinkAccountEmailAuthStepVM();
SetCurrentStep(step);
var res = await step.GetResultAsync();
var req = AuthRequestHelper.CreateEmailCodeRequest(res, model.ClientId, model.SteamId);
await AuthenticationServiceApi.UpdateAuthSessionWithSteamGuardCode(authClient, req, cancellationToken);
Error = null;
break;
}
catch (SteamStatusCodeException ex)
when (ex.StatusCode.Equals(SteamStatusCode.InvalidLoginAuthCode))
{
Error = ExceptionHandler.GetExceptionString(ex);
}
}
}
#endregion
#region Step 3: Phone Number
// Step 3: Phone number
@@ -289,87 +373,5 @@ public partial class LinkAccountVM : ObservableObject, ISmsCodeProvider, IPhoneN
}
#endregion
private void SetCurrentStep(LinkAccountStepVM step)
{
Dispatcher.CurrentDispatcher.Invoke(() =>
{
CurrentStep = step;
Tip = CurrentStep.Tip;
});
}
[RelayCommand]
private void OpenTroubleshooting()
{
const string troubleshootingURI =
"https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/docs/{0}/LinkingTroubleshooting";
var localized = string.Format(troubleshootingURI, LocManager.GetCurrentLanguageCode());
Process.Start(new ProcessStartInfo(new Uri(localized).ToString())
{
UseShellExecute = true
});
}
private static string GetLocalizationOrDefault(string key)
{
return LocManager.GetCodeBehindOrDefault(key, LOCALIZATION_KEY, key);
}
private static string GetLocalizationCommon(string key)
{
return LocManager.GetCommonOrDefault(key, key);
}
public void Dispose()
{
_client.Dispose();
_handler.Dispose();
try
{
_cts.Cancel();
}
catch
{
//Ignored, may be cancelled or disposed
}
_cts.Dispose();
}
#region Step 2: Email Code
public bool IsSupportedGuardType(ILoginConsumer consumer, EAuthSessionGuardType type)
{
return type == EAuthSessionGuardType.EmailCode;
}
// Step 2: Email Code
public async Task UpdateAuthSession(HttpClient authClient, ILoginConsumer loginConsumer,
UpdateAuthSessionModel model,
CancellationToken cancellationToken = default)
{
while (true)
{
try
{
var step = new LinkAccountEmailAuthStepVM();
SetCurrentStep(step);
var res = await step.GetResultAsync();
var req = AuthRequestHelper.CreateEmailCodeRequest(res, model.ClientId, model.SteamId);
await AuthenticationServiceApi.UpdateAuthSessionWithSteamGuardCode(authClient, req, cancellationToken);
Error = null;
break;
}
catch (SteamStatusCodeException ex)
when (ex.StatusCode.Equals(SteamStatusCode.InvalidLoginAuthCode))
{
Error = ExceptionHandler.GetExceptionString(ex);
}
}
}
#endregion
// @formatter:on
}
@@ -258,29 +258,6 @@ public partial class MafileMoverVM : ObservableObject, IAuthProvider, IDisposabl
await Done(mafile.RevocationCode ?? string.Empty, mafile.SteamId.Steam64.ToString());
}
#region Step 3: Sms code
// Step 3: Sms code
public Task<int> GetSms()
{
var step = new MafileMoverSmsStepVM();
SetCurrentStep(step);
return step.GetResultAsync();
}
#endregion
#region Step 4: Done
public async Task Done(string rCode, string steamId)
{
var step = new MafileMoverDoneStepVM(rCode, steamId);
SetCurrentStep(step);
await step.GetResultAsync();
}
#endregion
private void SetCurrentStep(MafileMoverStepVM step)
{
Dispatcher.CurrentDispatcher.Invoke(() =>
@@ -335,13 +312,15 @@ public partial class MafileMoverVM : ObservableObject, IAuthProvider, IDisposabl
_cts.Dispose();
}
#region Step 2: Guard Code
public bool IsSupportedGuardType(ILoginConsumer consumer, EAuthSessionGuardType type)
{
return type == EAuthSessionGuardType.DeviceCode;
}
// @formatter:off
#region Step 2: Guard Code
// Step 2: Guard Code
public async Task UpdateAuthSession(HttpClient authClient, ILoginConsumer loginConsumer,
UpdateAuthSessionModel model,
@@ -374,4 +353,29 @@ public partial class MafileMoverVM : ObservableObject, IAuthProvider, IDisposabl
}
#endregion
#region Step 3: Sms code
// Step 3: Sms code
public Task<int> GetSms()
{
var step = new MafileMoverSmsStepVM();
SetCurrentStep(step);
return step.GetResultAsync();
}
#endregion
#region Step 4: Done
public async Task Done(string rCode, string steamId)
{
var step = new MafileMoverDoneStepVM(rCode, steamId);
SetCurrentStep(step);
await step.GetResultAsync();
}
#endregion
// @formatter:on
}
+19 -23
View File
@@ -44,12 +44,6 @@ public partial class MainVM : ObservableObject
Storage.MaFiles.CollectionChanged += MaFilesOnCollectionChanged;
QueryGroups();
UpdateManager.CheckForUpdates();
if (Storage.DuplicateFound > 0)
{
SnackbarController.SendSnackbar(
GetLocalization("DuplicateMafilesFound") + " " + Storage.DuplicateFound,
TimeSpan.FromSeconds(4));
}
}
[RelayCommand]
@@ -72,20 +66,21 @@ public partial class MainVM : ObservableObject
OnPropertyChanged(nameof(IsDefaultProxy));
if (mafile != null) Code = SteamGuardCodeGenerator.GenerateCode(mafile.SharedSecret);
OnPropertyChanged(nameof(IsMafileSelected));
RefreshSessionCommand.NotifyCanExecuteChanged();
LoginAgainCommand.NotifyCanExecuteChanged();
RemoveAuthenticatorCommand.NotifyCanExecuteChanged();
ConfirmLoginCommand.NotifyCanExecuteChanged();
}
}
[RelayCommand]
[RelayCommand(CanExecute = nameof(IsMafileSelected))]
public async Task LoginAgain()
{
if (SelectedMafile == null)
{
return;
}
var currentPassword = PHandler.DecryptPassword(SelectedMafile.Password);
var loginAgainVm = await DialogsController.ShowLoginAgainDialog(SelectedMafile.AccountName, currentPassword);
var selectedMafile = SelectedMafile;
if (selectedMafile == null) return;
var currentPassword = PHandler.DecryptPassword(selectedMafile.Password);
var loginAgainVm = await DialogsController.ShowLoginAgainDialog(selectedMafile.AccountName, currentPassword);
if (loginAgainVm == null)
{
return;
@@ -96,7 +91,7 @@ public partial class MainVM : ObservableObject
var wait = DialogHost.Show(waitDialog);
try
{
await MaClient.LoginAgain(SelectedMafile, password, loginAgainVm.SavePassword);
await MaClient.LoginAgain(selectedMafile, password, loginAgainVm.SavePassword);
SnackbarController.SendSnackbar(GetLocalization("SuccessfulLogin"));
}
catch (LoginException ex)
@@ -122,13 +117,14 @@ public partial class MainVM : ObservableObject
}
[RelayCommand]
[RelayCommand(CanExecute = nameof(IsMafileSelected))]
private async Task RefreshSession()
{
if (SelectedMafile == null) return;
var selectedMafile = SelectedMafile;
if (selectedMafile == null) return;
try
{
await MaClient.RefreshSession(SelectedMafile);
await MaClient.RefreshSession(selectedMafile);
SnackbarController.SendSnackbar(GetLocalization("SessionRefreshed"));
}
catch (Exception ex) when (ExceptionHandler.Handle(ex))
@@ -149,7 +145,7 @@ public partial class MainVM : ObservableObject
await DialogsController.ShowMafileMoverDialog();
}
[RelayCommand]
[RelayCommand(CanExecute = nameof(IsMafileSelected))]
private async Task RemoveAuthenticator()
{
var selectedMafile = SelectedMafile;
@@ -190,14 +186,14 @@ public partial class MainVM : ObservableObject
}
}
[RelayCommand]
[RelayCommand(CanExecute = nameof(IsMafileSelected))]
private async Task ConfirmLogin()
{
if (SelectedMafile == null) return;
var selectedMafile = SelectedMafile;
if (selectedMafile == null) return;
try
{
var res = await SessionHandler.Handle(() => MaClient.ConfirmLoginRequest(SelectedMafile), SelectedMafile);
var res = await SessionHandler.Handle(() => MaClient.ConfirmLoginRequest(selectedMafile), selectedMafile);
if (res.Success)
{
SnackbarController.SendSnackbar($"{GetLocalization("ConfirmLoginSuccess")} {res.IP} ({res.Country})");
+2 -2
View File
@@ -34,7 +34,7 @@ public partial class MainVM //File //TODO: Refactor
string? mafilePath = null;
if (mafile != null)
{
mafilePath = Storage.TryFindMafilePath(mafile);
mafilePath = Storage.TryGetMafilePath(mafile);
}
if (mafilePath != null)
@@ -266,7 +266,7 @@ public partial class MainVM //File //TODO: Refactor
private void CopyMafile(object? mafile)
{
if (mafile is not Mafile maf) return;
var path = Storage.TryFindMafilePath(maf);
var path = Storage.TryGetMafilePath(maf);
if (ClipboardHelper.SetFiles([path]))
SnackbarController.SendSnackbar(GetLocalization("MafileCopied"));
}
@@ -18,11 +18,31 @@ public partial class ProxyManagerVM : ObservableObject
private static readonly Regex IdRegex = new(@"\{(\d+)\}$", RegexOptions.Compiled);
public ObservableDictionary<int, ProxyData> Proxies => ProxyStorage.Proxies;
public bool AnyChanges { get; private set; }
public bool DisplayProtocol
{
get => Settings.Instance.ProxyManagerDisplayProtocol;
set
{
Settings.Instance.ProxyManagerDisplayProtocol = value;
OnPropertyChanged();
}
}
public bool DisplayCredentials
{
get => Settings.Instance.ProxyManagerDisplayCredentials;
set
{
Settings.Instance.ProxyManagerDisplayCredentials = value;
OnPropertyChanged();
}
}
[ObservableProperty] private string _addProxyField = string.Empty;
[ObservableProperty] private KeyValuePair<int, ProxyData>? _defaultProxy;
[ObservableProperty] private string? _errorText;
[ObservableProperty] private KeyValuePair<int, ProxyData>? _selectedProxy;
public ProxyManagerVM()
@@ -67,7 +87,7 @@ public partial class ProxyManagerVM : ObservableObject
idPresent ??= idMatch.Success;
if (idPresent.Value != idMatch.Success)
{
SnackbarController.SendSnackbar(GetLocalizationOrDefault("WrongFormatSomeIdsMissing"));
SetError(GetLocalizationOrDefault("WrongFormatSomeIdsMissing"));
return;
}
@@ -76,7 +96,7 @@ public partial class ProxyManagerVM : ObservableObject
{
if (id != null && proxies.Any(kvp => kvp.Key == id))
{
SnackbarController.SendSnackbar(string.Format(GetLocalizationOrDefault("DuplicateId"), id));
SetError(string.Format(GetLocalizationOrDefault("DuplicateId"), id));
return;
}
@@ -86,21 +106,32 @@ public partial class ProxyManagerVM : ObservableObject
{
if (split.Length == 1)
{
SnackbarController.SendSnackbar(GetLocalizationOrDefault("WrongFormat"));
SetError(GetLocalizationOrDefault("WrongFormat"));
return;
}
SnackbarController.SendSnackbar(string.Format(GetLocalizationOrDefault("WrongFormatOnLine"), i));
SetError(string.Format(GetLocalizationOrDefault("WrongFormatOnLine"), i));
return;
}
}
ProxyStorage.SetProxies(proxies);
ProxyStorage.OrderCollection();
ProxyStorage.SortCollection();
AddProxyField = string.Empty;
SetError(null);
CheckIfDefaultProxyStay();
}
private void SetError(string? err)
{
ErrorText = err;
}
[RelayCommand]
public void ClearError()
{
SetError(null);
}
private void CheckIfDefaultProxyStay()
{
@@ -110,12 +141,13 @@ public partial class ProxyManagerVM : ObservableObject
}
[RelayCommand]
private void RemoveProxy()
private void RemoveProxy(object? target)
{
var targetProxy = target as KeyValuePair<int, ProxyData>? ?? SelectedProxy;
AnyChanges = true;
var selected = SelectedProxy;
if (selected == null) return;
var s = selected.Value;
if (targetProxy == null) return;
var s = targetProxy.Value;
KeyValuePair<int, ProxyData>? nextNeighbor = null;
@@ -116,6 +116,7 @@ public partial class SettingsVM : ObservableObject
OnPropertyChanged(nameof(LeftOpacity));
OnPropertyChanged(nameof(RightOpacity));
OnPropertyChanged(nameof(ApplyBlurBackground));
OnPropertyChanged(nameof(RippleDisabled));
}
+30 -19
View File
@@ -40,6 +40,11 @@
"ru": "Прокси",
"ua": "Проксі"
},
"Delete": {
"en": "Delete",
"ru": "Удалить",
"ua": "Видалити"
},
"Abbreviations": {
"Time": {
"Seconds": {
@@ -224,15 +229,16 @@
}
},
"Footer": {
"Account": {
"en": "Account: ",
"ru": "Аккаунт: ",
"ua": "Акаунт: "
"AccountsCount": {
"en": "Accounts: ",
"ru": "Аккаунты: ",
"ua": "Акаунти: "
},
"Group": {
"en": "Group: ",
"ru": "Группа: ",
"ua": "Група: "
"SelectedAccount": {
"en": "Current: ",
"ru": "Текущий: ",
"ua": "Поточний: "
}
},
"ConfirmationTemplates": {
@@ -405,9 +411,9 @@
"ua": "Згортати в трей"
},
"UseIndicator": {
"en": "Use indicator",
"ru": "Использовать индикатор",
"ua": "Використовувати індикатор"
"en": "Use color indicator in taskbar",
"ru": "Цветной индикатор в панели задач",
"ua": "Кольоровий індикатор в панелі завдань"
},
"UseIndicatorHint": {
"en": "Small color indicator in windows toolbar to identify program instance",
@@ -448,9 +454,9 @@
"ua": "Дозволити автооновлення"
},
"UseAccountName": {
"en": "Use account name on mafiles",
"ru": "Использовать имя аккаунта на мафайлах",
"ua": "Використовувати ім'я акаунта на мафайлах"
"en": "Use account login on mafiles",
"ru": "Использовать логин аккаунта на мафайлах",
"ua": "Використовувати логін акаунта на мафайлах"
},
"IgnorePatchTuesdayErrors": {
"en": "Ignore Patch Tuesday errors in timer (exp.)",
@@ -525,6 +531,16 @@
"en": "Use random default proxy",
"ru": "Cлучайный прокси по умолчанию",
"ua": "Випадковий проксі за замовчуванням"
},
"DisplayProxyProtocol": {
"en": "Display protocol",
"ru": "Отображать протокол",
"ua": "Відображати протокол"
},
"DisplayProxyCredentials": {
"en": "Display credentials",
"ru": "Отображать логин и пароль",
"ua": "Відображати логін та пароль"
}
},
"WaitLoginDialog": {
@@ -806,11 +822,6 @@
"en": "Too fast timer.",
"ua": "Занадто швидкий таймер."
},
"DuplicateMafilesFound": {
"en": "Duplicate mafile(s) found and were not loaded. Check log to get lists of duplicate files.",
"ru": "Найдены дубликаты мафайлов, они не были загружены. Проверьте лог чтобы получить списки дубликатов.",
"ua": "Знайдено дублікати мафайлів, вони не були завантажені. Перевірте лог щоб отримати списки дублікатів."
},
"TimerChanged": {
"en": "Timer changed",
"ru": "Таймер изменен",
@@ -67,14 +67,14 @@ public static class SteamAuthenticatorLinkerApi
var reqUri = AddAccessToken(Routes.LINK_REQUEST, data.GetAccessToken());
var req = new AddAuthenticator_Request
{
SteamId = (ulong) data.SteamId.Steam64.Id,
SteamId = (ulong)data.SteamId.Steam64.Id,
AuthenticatorType = 1,
DeviceIdentifier = deviceId,
Version = 2
};
var resp = await client.PostProtoMsg<AddAuthenticator_Response>(reqUri, req);
if (resp is {Result: EResult.InvalidState, ResponseMsg.Status: 2})
if (resp is { Result: EResult.InvalidState, ResponseMsg.Status: 2 })
{
throw new AuthenticatorLinkerException(AuthenticatorLinkerError.InvalidStateWithStatus2);
}
@@ -100,7 +100,7 @@ public static class SteamAuthenticatorLinkerApi
{
SteamId = data.SteamId.Steam64.ToUlong(),
AuthenticatorCode = code,
AuthenticatorTime = (ulong) time,
AuthenticatorTime = (ulong)time,
ConfirmationCode = confirmationCode,
ValidateConfirmationCode = validateSmsCode
};
@@ -108,7 +108,7 @@ public static class SteamAuthenticatorLinkerApi
var resp = await client.PostProto<FinalizeAddAuthenticator_Response>(reqUri, req);
if (resp is {Success: true, WantMore: false})
if (resp is { Success: true, WantMore: false })
{
return new LinkResult();
}
@@ -1,11 +1,11 @@
using System.Net;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using SteamLib.Core;
using SteamLib.Core.StatusCodes;
using SteamLib.Exceptions;
using SteamLib.Exceptions.Authorization;
using SteamLib.ProtoCore;
using SteamLib.ProtoCore.Services;
using System.Net;
namespace SteamLib.Api.Mobile;
@@ -1,5 +1,4 @@
using System.Net;
using AchiesUtilities.Web.Extensions;
using AchiesUtilities.Web.Extensions;
using SteamLib.Core;
using SteamLib.Core.StatusCodes;
using SteamLib.Exceptions;
@@ -8,6 +7,7 @@ using SteamLib.SteamMobile;
using SteamLib.SteamMobile.Confirmations;
using SteamLib.Utility;
using SteamLib.Web.Scrappers.JSON;
using System.Net;
namespace SteamLib.Api.Mobile;
@@ -1,9 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Net;
using SteamLib.Core;
using SteamLib.Core;
using SteamLib.Exceptions.Authorization;
using SteamLib.ProtoCore;
using SteamLib.ProtoCore.Services;
using System.Diagnostics.CodeAnalysis;
using System.Net;
namespace SteamLib.Api.Services;
+3 -3
View File
@@ -1,8 +1,8 @@
using System.Net;
using System.Web;
using SteamLib.Core;
using SteamLib.Core;
using SteamLib.Models;
using SteamLib.Web.Scrappers.HTML;
using System.Net;
using System.Web;
namespace SteamLib.Api;
@@ -1,5 +1,4 @@
using System.Net;
using AchiesUtilities.Extensions;
using AchiesUtilities.Extensions;
using AchiesUtilities.Models;
using Newtonsoft.Json;
using SteamLib.Core;
@@ -7,6 +6,7 @@ using SteamLib.Models.Account;
using SteamLibForked.Abstractions;
using SteamLibForked.Models.Core;
using SteamLibForked.Models.Session;
using System.Net;
namespace SteamLib.Authentication;
@@ -131,7 +131,7 @@ public static class AdmissionHelper
}
var mobileToken = mobileSession.GetMobileToken();
if (domainCookieSet == false && mobileToken is {IsExpired: false})
if (domainCookieSet == false && mobileToken is { IsExpired: false })
{
var domain = SteamDomains.GetDomainUri(SteamDomain.Community);
container.Add(domain, new Cookie(ACCESS_COOKIE_NAME, mobileToken.Value.SignedToken)
@@ -208,7 +208,7 @@ public static class AdmissionHelper
static Cookie CloneCookie(Cookie cookie)
{
return new Cookie(cookie.Name, cookie.Value, cookie.Path)
{Expires = cookie.Expires, Secure = cookie.Secure, HttpOnly = cookie.HttpOnly};
{ Expires = cookie.Expires, Secure = cookie.Secure, HttpOnly = cookie.HttpOnly };
}
}
@@ -1,6 +1,6 @@
using System.Security.Cryptography;
using SteamLib.ProtoCore.Enums;
using SteamLib.ProtoCore.Enums;
using SteamLib.ProtoCore.Services;
using System.Security.Cryptography;
namespace SteamLib.Authentication;
@@ -1,11 +1,11 @@
using System.Collections.ObjectModel;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
using AchiesUtilities.Models;
using AchiesUtilities.Models;
using Microsoft.IdentityModel.JsonWebTokens;
using SteamLibForked.Models.Core;
using SteamLibForked.Models.Session;
using SteamLibForked.Models.SteamIds;
using System.Collections.ObjectModel;
using System.Net.Http.Headers;
using System.Text.RegularExpressions;
namespace SteamLib.Authentication;
@@ -67,7 +67,7 @@ public partial class SteamStatusCode : Enumeration<SteamStatusCode>
public static SteamStatusCode FromEResult(EResult result)
{
var r = (int) result;
var r = (int)result;
var fromId = FromId(r);
return fromId ?? new SteamStatusCode(r, "Unknown");
}
+2 -2
View File
@@ -1,5 +1,5 @@
using System.Collections.ObjectModel;
using SteamLibForked.Models.Core;
using SteamLibForked.Models.Core;
using System.Collections.ObjectModel;
namespace SteamLib.Core;
@@ -1,7 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ExceptionServices;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using SteamLib.Exceptions.General;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ExceptionServices;
namespace SteamLib.Core;
@@ -9,13 +9,13 @@ public static class DeviceDetailsDefaultBuilder
[Obsolete("Not recommended")]
public static DeviceDetails CreateDefault(string? deviceFriendlyName)
{
return new DeviceDetails(deviceFriendlyName ?? string.Empty, EAuthTokenPlatformType.WebBrowser, (int?) null,
return new DeviceDetails(deviceFriendlyName ?? string.Empty, EAuthTokenPlatformType.WebBrowser, (int?)null,
null);
}
public static DeviceDetails CreateCommunityDetails(string userAgent)
{
return new DeviceDetails(userAgent, EAuthTokenPlatformType.WebBrowser, (int?) null, null);
return new DeviceDetails(userAgent, EAuthTokenPlatformType.WebBrowser, (int?)null, null);
}
public static DeviceDetails CreateMobileDetails(string deviceName)
@@ -1,7 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json;
using SteamLibForked.Abstractions;
using SteamLibForked.Models.Core;
using System.Diagnostics.CodeAnalysis;
namespace SteamLibForked.Models.Session;
@@ -38,7 +38,7 @@ public sealed class MobileSessionData : SessionData, IMobileSessionData
if (token.Type != SteamAccessTokenType.Mobile)
throw new ArgumentException("Token must be of type MobileAccess", nameof(token))
{
Data = {{"ActualType", token.Type}}
Data = { { "ActualType", token.Type } }
};
MobileToken = token;
@@ -46,7 +46,7 @@ public sealed class MobileSessionData : SessionData, IMobileSessionData
public override MobileSessionData Clone()
{
return (MobileSessionData) ((ISessionData) this).Clone();
return (MobileSessionData)((ISessionData)this).Clone();
}
object ICloneable.Clone()
@@ -1,8 +1,8 @@
using System.Collections.Concurrent;
using Newtonsoft.Json;
using Newtonsoft.Json;
using SteamLib.Web.Converters;
using SteamLibForked.Abstractions;
using SteamLibForked.Models.Core;
using System.Collections.Concurrent;
namespace SteamLibForked.Models.Session;
@@ -61,7 +61,7 @@ public class SessionData : ISessionData
public virtual SessionData Clone()
{
return (SessionData) ((ICloneable) this).Clone();
return (SessionData)((ICloneable)this).Clone();
}
object ICloneable.Clone()
+1 -1
View File
@@ -43,7 +43,7 @@ public readonly struct SteamId : IEquatable<SteamId> //TODO: validation in parse
public static SteamId FromSteam64(ulong steam64, char type = 'U', short universe = 0)
{
return new SteamId(new SteamId64((long) steam64), type, universe);
return new SteamId(new SteamId64((long)steam64), type, universe);
}
public static SteamId FromSteam2(byte lowestBit, int highestBit, byte universe = 0, char type = 'U')
@@ -19,7 +19,7 @@ public readonly struct SteamId3 : IEquatable<SteamId3>
{
var bit = Id % 2;
var highestBits = Id / 2;
return new SteamId2(universe, (byte) bit, highestBits);
return new SteamId2(universe, (byte)bit, highestBits);
}
@@ -16,15 +16,15 @@ public readonly struct SteamId64 : IEquatable<SteamId64>
public SteamId2 ToSteam2(short universe = 0)
{
var accountIdLowBit = (byte) (Id & 1);
var accountIdHighBits = (int) (Id >> 1) & 0x7FFFFFF;
return new SteamId2((byte) universe, accountIdLowBit, accountIdHighBits);
var accountIdLowBit = (byte)(Id & 1);
var accountIdHighBits = (int)(Id >> 1) & 0x7FFFFFF;
return new SteamId2((byte)universe, accountIdLowBit, accountIdHighBits);
}
public SteamId3 ToSteam3(char type = 'U')
{
var accountIdLowBit = (byte) (Id & 1);
var accountIdHighBits = (int) (Id >> 1) & 0x7FFFFFF;
var accountIdLowBit = (byte)(Id & 1);
var accountIdHighBits = (int)(Id >> 1) & 0x7FFFFFF;
return new SteamId3(accountIdLowBit + accountIdHighBits * 2, type);
}
@@ -36,7 +36,7 @@ public readonly struct SteamId64 : IEquatable<SteamId64>
public ulong ToUlong()
{
return (ulong) Id;
return (ulong)Id;
}
public long ToLong()
+3 -3
View File
@@ -1,7 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using ProtoBuf;
using ProtoBuf;
using SteamLib.ProtoCore.Enums;
using SteamLib.ProtoCore.Interfaces;
using System.Diagnostics.CodeAnalysis;
namespace SteamLib.ProtoCore;
@@ -25,7 +25,7 @@ public static class ProtoHelpers
if (response.Headers.TryGetValues("x-eresult", out var val))
{
var eResultInt = Convert.ToInt32(val.Single());
return (EResult) eResultInt;
return (EResult)eResultInt;
}
return EResult.Invalid;
@@ -22,7 +22,7 @@ public class ProtoResponse<TProtoResponse> : ProtoResponse
{
public TProtoResponse? ResponseMsg
{
get => ProtoObject == null ? default : (TProtoResponse) ProtoObject;
get => ProtoObject == null ? default : (TProtoResponse)ProtoObject;
set => ProtoObject = value;
}
@@ -37,7 +37,7 @@ public class ProtoResponse<TProtoResponse> : ProtoResponse
if (response.Headers.TryGetValues("x-eresult", out var val))
{
var eResultInt = Convert.ToInt32(val.Single());
eResult = (EResult) eResultInt;
eResult = (EResult)eResultInt;
}
var msg = default(TProtoResponse);
@@ -67,7 +67,7 @@ public class ProtoResponse<TProtoResponse> : ProtoResponse
if (response.Headers.TryGetValues("x-eresult", out var val))
{
var eResultInt = Convert.ToInt32(val.Single());
eResult = (EResult) eResultInt;
eResult = (EResult)eResultInt;
}
var msg = default(TProtoResponse);
@@ -1,8 +1,8 @@
using System.Security.Cryptography;
using ProtoBuf;
using ProtoBuf;
using SteamLib.ProtoCore.Enums;
using SteamLib.ProtoCore.Interfaces;
using SteamLib.Utility;
using System.Security.Cryptography;
// ReSharper disable InconsistentNaming
// ReSharper disable IdentifierTypo
@@ -81,18 +81,18 @@ public class DeviceDetails : IProtoMsg
{
DeviceFriendlyName = deviceFriendlyName;
PlatformType = platformType;
OsType = (int?) osType;
OsType = (int?)osType;
GamingDeviceType = gamingDeviceType;
}
public static DeviceDetails CreateDefault()
{
return new DeviceDetails("", EAuthTokenPlatformType.WebBrowser, (int?) null, null);
return new DeviceDetails("", EAuthTokenPlatformType.WebBrowser, (int?)null, null);
}
public static DeviceDetails CreateCommunityDetails(string userAgent)
{
return new DeviceDetails(userAgent, EAuthTokenPlatformType.WebBrowser, (int?) null, null);
return new DeviceDetails(userAgent, EAuthTokenPlatformType.WebBrowser, (int?)null, null);
}
public static DeviceDetails CreateMobileDetails(string deviceName)
@@ -1,10 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml"
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Ccore/@EntryIndexedValue">True</s:Boolean>
<s:Boolean
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Ccore_005Csteamids/@EntryIndexedValue">True</s:Boolean>
<s:Boolean
x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cnative/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions_005Cauthorization/@EntryIndexedValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Ccore/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Ccore_005Csteamids/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=models_005Cnative/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -103,7 +103,7 @@ public class SteamAuthenticatorLinker
var sendSms = await this.SendSmsCode();
if (sendSms != EResult.OK)
throw new AuthenticatorLinkerException($"Can't send SMS code: {sendSms} ({(int) sendSms})");
throw new AuthenticatorLinkerException($"Can't send SMS code: {sendSms} ({(int)sendSms})");
Logger?.LogDebug("SMS code sent");
}
@@ -155,14 +155,14 @@ public class SteamAuthenticatorLinker
var error = result.Error switch
{
LinkError.GeneralFailure => throw new AuthenticatorLinkerException(result.Code!.Value)
{OnFinalization = true},
{ OnFinalization = true },
LinkError.BadConfirmationCode => AuthenticatorLinkerError.BadConfirmationCode,
LinkError.UnableToGenerateCorrectCodes => AuthenticatorLinkerError.UnableToGenerateCorrectCodes,
_ => throw new ArgumentOutOfRangeException(nameof(result.Error), result.Error,
$"LinkError {result.Error} not supported")
};
throw new AuthenticatorLinkerException(error) {OnFinalization = true};
throw new AuthenticatorLinkerException(error) { OnFinalization = true };
}
Logger?.LogInformation("Linking completed");
@@ -1,11 +1,11 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using SteamLib.Abstractions;
using SteamLib.Abstractions;
using SteamLib.Api.Services;
using SteamLib.Authentication;
using SteamLib.ProtoCore.Enums;
using SteamLibForked.Abstractions.Auth;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace SteamLib.SteamMobile;
@@ -53,7 +53,7 @@ public class SteamGuardCodeGenerator : ISteamGuardProvider
for (var i = 8; i > 0; i--)
{
timeArray[i - 1] = (byte) time;
timeArray[i - 1] = (byte)time;
time >>= 8;
}
@@ -62,7 +62,7 @@ public class SteamGuardCodeGenerator : ISteamGuardProvider
var hashedData = hmacGenerator.ComputeHash(timeArray);
var codeArray = new byte[5];
var b = (byte) (hashedData[19] & 0xF);
var b = (byte)(hashedData[19] & 0xF);
var codePoint = ((hashedData[b] & 0x7F) << 24) | ((hashedData[b + 1] & 0xFF) << 16) |
((hashedData[b + 2] & 0xFF) << 8) | (hashedData[b + 3] & 0xFF);
@@ -1,7 +1,7 @@
using System.Diagnostics;
using AchiesUtilities.Models;
using AchiesUtilities.Models;
using Newtonsoft.Json.Linq;
using SteamLib.Core;
using System.Diagnostics;
namespace SteamLib.SteamMobile;
@@ -50,7 +50,7 @@ public static class TimeAligner
var j = JObject.Parse(respStr);
var time = j["response"]!["server_time"]!.Value<long>();
var now = UtcNow - sw.Elapsed;
_timeDifference = (int) (time - now.ToLong());
_timeDifference = (int)(time - now.ToLong());
_aligned = true;
}
finally
@@ -72,7 +72,7 @@ public static class TimeAligner
var j = JObject.Parse(respStr);
var time = j["response"]!["server_time"]!.Value<long>();
var now = UtcNow - sw.Elapsed;
_timeDifference = (int) (time - now.ToLong());
_timeDifference = (int)(time - now.ToLong());
_aligned = true;
}
finally
@@ -60,7 +60,7 @@ public static class EncryptionHelper
break;
}
array[n4] = (byte) time;
array[n4] = (byte)time;
time >>= 8;
n3 = n4;
}
@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using SteamLibForked.Models.SteamIds;
using System.Diagnostics.CodeAnalysis;
namespace SteamLib.Utility.MafileSerialization;
@@ -73,13 +73,13 @@ public class DeserializedMafileInfo
public bool IsActual => Version == MafileSerializer.MAFILE_VERSION;
[MemberNotNullWhen(true, nameof(UnusedProperties))]
public bool HasUnusedProperties => UnusedProperties is {Count: > 0};
public bool HasUnusedProperties => UnusedProperties is { Count: > 0 };
[MemberNotNullWhen(true, nameof(MissingProperties))]
public bool HasMissingProperties => MissingProperties is {Count: > 0};
public bool HasMissingProperties => MissingProperties is { Count: > 0 };
[MemberNotNullWhen(true, nameof(MissingImportantProperties))]
public bool HasMissingImportantProperties => MissingImportantProperties is {Count: > 0};
public bool HasMissingImportantProperties => MissingImportantProperties is { Count: > 0 };
public bool HasSession => SessionResult == DeserializedMafileSessionResult.Valid;
public bool HasIdentificationProperty { get; init; }
@@ -100,7 +100,7 @@ public class DeserializedMafileInfo
isExtended = true;
}
if (isExtended && missingProperties is {Count: > 0})
if (isExtended && missingProperties is { Count: > 0 })
{
var important = missingProperties.Intersect(ImportantProperties).ToList();
if (important.Count > 0) missingImportantProperties = important.ToHashSet();
@@ -29,7 +29,7 @@ public partial class MafileSerializer
var versionToken = GetToken(j, SIGNATURE_PROPERTY_NAME);
unusedProperties.Remove(CREDITS_PROPERTY_NAME);
int? version = null;
if (versionToken is {Type: JTokenType.Integer})
if (versionToken is { Type: JTokenType.Integer })
{
version = versionToken.Value<int>();
}
@@ -1,9 +1,9 @@
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using SteamLib.Authentication;
using SteamLibForked.Models.Session;
using SteamLibForked.Models.SteamIds;
using System.Security.Cryptography;
using System.Text;
namespace SteamLib.Utility.MafileSerialization;
@@ -17,14 +17,14 @@ public partial class MafileSerializer //SessionData
var refreshToken = GetAuthToken(j, nameof(MobileSessionData.RefreshToken), "refreshtoken", "refresh_token",
"refresh", "OAuthToken");
if (refreshToken is not {Type: SteamAccessTokenType.MobileRefresh})
if (refreshToken is not { Type: SteamAccessTokenType.MobileRefresh })
{
result = DeserializedMafileSessionResult.Invalid;
return null;
}
var accessToken = GetAuthToken(j, "accesstoken", "access_token", "access", "steamLoginSecure");
if (accessToken is not {Type: SteamAccessTokenType.Mobile})
if (accessToken is not { Type: SteamAccessTokenType.Mobile })
{
accessToken = null;
}
@@ -1,6 +1,6 @@
using System.Numerics;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using SteamLibForked.Models.SteamIds;
using System.Numerics;
namespace SteamLib.Utility.MafileSerialization;
@@ -167,11 +167,11 @@ public partial class MafileSerializer //Utility
{
res = settings.DeserializationOptions.RestrictOverflowSerialNumberRecovery
? 0
: GetFromOverflow((long) bigInt);
: GetFromOverflow((long)bigInt);
}
else if (bigInt > ulong.MinValue && bigInt < ulong.MaxValue) //Valid range
{
res = (ulong) bigInt;
res = (ulong)bigInt;
}
else
{
@@ -192,7 +192,7 @@ public partial class MafileSerializer //Utility
ulong originalValue;
unchecked
{
originalValue = (ulong) overflow + ulong.MaxValue + 1;
originalValue = (ulong)overflow + ulong.MaxValue + 1;
}
return originalValue;
@@ -1,7 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
using SteamLib.Authentication;
using SteamLibForked.Models.Session;
using System.Diagnostics.CodeAnalysis;
namespace SteamLib.Utility.MafileSerialization;
+2 -2
View File
@@ -1,5 +1,5 @@
using System.Text.RegularExpressions;
using SteamLibForked.Models.SteamIds;
using SteamLibForked.Models.SteamIds;
using System.Text.RegularExpressions;
namespace SteamLib.Utility;
+3 -3
View File
@@ -1,8 +1,8 @@
using System.Net;
using System.Net.Http.Headers;
using AchiesUtilities.Web.Models;
using AchiesUtilities.Web.Models;
using SteamLib.Authentication;
using SteamLibForked.Abstractions;
using System.Net;
using System.Net.Http.Headers;
namespace SteamLib.Web;
@@ -18,7 +18,7 @@ public class SteamIdToSteam64Converter : JsonConverter<SteamId>
return SteamId.FromSteam64(l);
}
var str = (string) reader.Value!;
var str = (string)reader.Value!;
return new SteamId(SteamId64.Parse(str));
}
}
@@ -33,7 +33,7 @@ public class SteamIdToSteam2Converter : JsonConverter<SteamId>
public override SteamId ReadJson(JsonReader reader, Type objectType, SteamId existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
var str = (string) reader.Value!;
var str = (string)reader.Value!;
return new SteamId(SteamId2.Parse(str));
}
}
@@ -48,7 +48,7 @@ public class SteamIdToSteam3Converter : JsonConverter<SteamId>
public override SteamId ReadJson(JsonReader reader, Type objectType, SteamId existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
var str = (string) reader.Value!;
var str = (string)reader.Value!;
return new SteamId(SteamId3.Parse(str));
}
}
@@ -69,7 +69,7 @@ public class Steam64ToLongConverter : JsonConverter<SteamId64>
return new SteamId64(l);
}
var str = (string) reader.Value!;
var str = (string)reader.Value!;
return SteamId64.Parse(str);
}
}
@@ -84,7 +84,7 @@ public class SteamId2ToStringConverter : JsonConverter<SteamId2>
public override SteamId2 ReadJson(JsonReader reader, Type objectType, SteamId2 existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
var str = (string) reader.Value!;
var str = (string)reader.Value!;
return SteamId2.Parse(str);
}
}
@@ -99,7 +99,7 @@ public class SteamId3ToStringConverter : JsonConverter<SteamId3>
public override SteamId3 ReadJson(JsonReader reader, Type objectType, SteamId3 existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
var str = (string) reader.Value!;
var str = (string)reader.Value!;
return SteamId3.Parse(str);
}
}
@@ -1,9 +1,9 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using HtmlAgilityPack;
using HtmlAgilityPack;
using JetBrains.Annotations;
using SteamLib.Exceptions.General;
using SteamLib.Models;
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
namespace SteamLib.Web.Scrappers.HTML;
@@ -12,7 +12,9 @@ public static class ScriptHeaderScrapper
{
private const string XPATH = "//div[@class='responsive_page_content']/script";
[RegexPattern] [SuppressMessage("ReSharper", "UseRawString")] [SuppressMessage("ReSharper", "StringLiteralTypo")]
[RegexPattern]
[SuppressMessage("ReSharper", "UseRawString")]
[SuppressMessage("ReSharper", "StringLiteralTypo")]
private static readonly string _regexTip =
@"g_sessionID = ""(?<g_sessionID>.*)"";"
+ @"\s*g_steamID = (?<g_steamID>.*);";
@@ -78,7 +78,7 @@ public static class MobileConfirmationScrapper
ConfirmationType.MarketSellTransaction => GetMarketConfirmation(json),
ConfirmationType.RegisterApiKey => GetRegisterApiKeyConfirmation(json),
ConfirmationType.Purchase => GetPurchaseConfirmation(json),
_ => new Confirmation(json.Id, json.Nonce, (int) json.Type, json.CreatorId, json.Type, json.TypeName)
_ => new Confirmation(json.Id, json.Nonce, (int)json.Type, json.CreatorId, json.Type, json.TypeName)
{
Time = json.CreationTime.ToLocalDateTime()
}