Merge 1.7.1 to master

This commit is contained in:
achiez
2025-07-09 22:33:33 +03:00
277 changed files with 5855 additions and 4185 deletions
+19 -22
View File
@@ -3,15 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.4.33205.214 VisualStudioVersion = 17.4.33205.214
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NebulaAuth", "NebulaAuth\NebulaAuth.csproj", "{0FD01700-6D5C-451B-93BA-0860647E8F13}"
ProjectSection(ProjectDependencies) = postProject
{09F02072-F91D-4DAA-87BC-A34D3E150570} = {09F02072-F91D-4DAA-87BC-A34D3E150570}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NebulaAuth.LegacyConverter", "NebulaAuth.LegacyConverter\NebulaAuth.LegacyConverter.csproj", "{2D78A7D9-986A-4890-8A91-7ABD57A91830}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SteamLibForked", "SteamLibForked\SteamLibForked.csproj", "{09F02072-F91D-4DAA-87BC-A34D3E150570}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "changelog", "changelog", "{161BFADE-FCF3-45D1-82EA-8A1B187529F7}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "changelog", "changelog", "{161BFADE-FCF3-45D1-82EA-8A1B187529F7}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
changelog\1.3.4.html = changelog\1.3.4.html changelog\1.3.4.html = changelog\1.3.4.html
@@ -28,27 +19,33 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "changelog", "changelog", "{
changelog\1.5.4.html = changelog\1.5.4.html changelog\1.5.4.html = changelog\1.5.4.html
changelog\1.5.5.html = changelog\1.5.5.html changelog\1.5.5.html = changelog\1.5.5.html
changelog\1.5.6.html = changelog\1.5.6.html changelog\1.5.6.html = changelog\1.5.6.html
changelog\1.5.7.html = changelog\1.5.7.html changelog\1.7.1.html = changelog\1.7.1.html
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamLibForked", "src\SteamLibForked\SteamLibForked.csproj", "{224F9DB0-3D20-A614-BA2A-12F22B13A2C6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NebulaAuth.LegacyConverter", "src\NebulaAuth.LegacyConverter\NebulaAuth.LegacyConverter.csproj", "{C8235305-E5C4-155B-C718-C0F239CA3AB7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NebulaAuth", "src\NebulaAuth\NebulaAuth.csproj", "{C42F63B6-32F7-ED08-5B86-CD51989761AD}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0FD01700-6D5C-451B-93BA-0860647E8F13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {224F9DB0-3D20-A614-BA2A-12F22B13A2C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0FD01700-6D5C-451B-93BA-0860647E8F13}.Debug|Any CPU.Build.0 = Debug|Any CPU {224F9DB0-3D20-A614-BA2A-12F22B13A2C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0FD01700-6D5C-451B-93BA-0860647E8F13}.Release|Any CPU.ActiveCfg = Release|Any CPU {224F9DB0-3D20-A614-BA2A-12F22B13A2C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0FD01700-6D5C-451B-93BA-0860647E8F13}.Release|Any CPU.Build.0 = Release|Any CPU {224F9DB0-3D20-A614-BA2A-12F22B13A2C6}.Release|Any CPU.Build.0 = Release|Any CPU
{2D78A7D9-986A-4890-8A91-7ABD57A91830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C8235305-E5C4-155B-C718-C0F239CA3AB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2D78A7D9-986A-4890-8A91-7ABD57A91830}.Debug|Any CPU.Build.0 = Debug|Any CPU {C8235305-E5C4-155B-C718-C0F239CA3AB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D78A7D9-986A-4890-8A91-7ABD57A91830}.Release|Any CPU.ActiveCfg = Release|Any CPU {C8235305-E5C4-155B-C718-C0F239CA3AB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2D78A7D9-986A-4890-8A91-7ABD57A91830}.Release|Any CPU.Build.0 = Release|Any CPU {C8235305-E5C4-155B-C718-C0F239CA3AB7}.Release|Any CPU.Build.0 = Release|Any CPU
{09F02072-F91D-4DAA-87BC-A34D3E150570}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C42F63B6-32F7-ED08-5B86-CD51989761AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09F02072-F91D-4DAA-87BC-A34D3E150570}.Debug|Any CPU.Build.0 = Debug|Any CPU {C42F63B6-32F7-ED08-5B86-CD51989761AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09F02072-F91D-4DAA-87BC-A34D3E150570}.Release|Any CPU.ActiveCfg = Release|Any CPU {C42F63B6-32F7-ED08-5B86-CD51989761AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09F02072-F91D-4DAA-87BC-A34D3E150570}.Release|Any CPU.Build.0 = Release|Any CPU {C42F63B6-32F7-ED08-5B86-CD51989761AD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
-34
View File
@@ -1,34 +0,0 @@
using System;
using System.Windows;
using NebulaAuth.Core;
using NebulaAuth.Model;
using NebulaAuth.Model.Exceptions;
namespace NebulaAuth;
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
LocManager.Init();
LocManager.SetApplicationLocalization(Settings.Instance.Language);
try
{
Shell.Initialize();
}
catch (Exception ex)
{
var msg = ex.ToString();
if (ex is CantAlignTimeException)
{
msg = LocManager.Get("CantAlignTimeError");
}
MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Stop, MessageBoxResult.OK,
MessageBoxOptions.DefaultDesktopOnly);
throw;
}
}
}
-68
View File
@@ -1,68 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows7.0</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<LangVersion>latest</LangVersion>
<SatelliteResourceLanguages>en;ru;ua</SatelliteResourceLanguages>
<ApplicationIcon>Theme\lock.ico</ApplicationIcon>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<AssemblyVersion>1.5.7</AssemblyVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Remove="Theme\1140x641.jpg" />
<None Remove="Theme\Background.jpg" />
<None Remove="Theme\lock.ico" />
<None Remove="Theme\SplashScreen.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.9.2" />
<PackageReference Include="CodingSeb.Localization.JsonFileLoader" Version="1.3.1" />
<PackageReference Include="CodingSeb.Localization.WPF" Version="1.3.3" />
<PackageReference Include="CodingSebLocalization.Fody" Version="1.3.1" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageReference Include="MaterialDesignColors" Version="2.1.4" />
<PackageReference Include="MaterialDesignThemes" Version="4.9.0" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.122" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.14" />
</ItemGroup>
<ItemGroup>
<Resource Include="Theme\Background.jpg">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Resource>
<Resource Include="Theme\lock.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SteamLibForked\SteamLibForked.csproj" />
</ItemGroup>
<ItemGroup>
<SplashScreen Include="Theme\SplashScreen.png" />
</ItemGroup>
<ItemGroup>
<Compile Update="View\Dialogs\LoginAgainOnImportDialog.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="localization.loc.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
-9
View File
@@ -1,9 +0,0 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--Card-->
<SolidColorBrush x:Key="MaterialDesignCardBackground" Color="#26292E" />
<SolidColorBrush x:Key="PrimaryHueMidBrush" Color="#FF9056B1" />
<SolidColorBrush x:Key="PrimaryHueLightBrush" Color="#FF9C70B5" />
<SolidColorBrush x:Key="PrimaryHueDarkForegroundBrush" Color="#FF851BC1" />
</ResourceDictionary>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 906 KiB

@@ -1,48 +0,0 @@
<UserControl x:Class="NebulaAuth.View.Dialogs.LoginAgainDialog"
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:theme="clr-namespace:NebulaAuth.Theme"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
xmlns:model="clr-namespace:NebulaAuth.Model"
mc:Ignorable="d"
theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True"
Foreground="WhiteSmoke"
d:DataContext="{d:DesignInstance other:LoginAgainVM}"
Background="{DynamicResource WindowBackground}">
<Grid MinHeight="100" MinWidth="300" Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True" Margin="10,0,0,0"
HorizontalAlignment="Left">
<Run Text="{Tr LoginAgainDialog.LoginFor, IsDynamic=False}" />
<Run FontWeight="Bold" Text="{Binding UserName}" />
</TextBlock>
<TextBox Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,0" Grid.Row="1"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
materialDesign:HintAssist.Hint="{Tr LoginAgainDialog.PasswordBox}" />
<CheckBox Grid.Row="2" Margin="10,10,10,0" IsEnabled="{Binding Source={x:Static model:PHandler.IsPasswordSet}}"
IsChecked="{Binding SavePassword}" Content="{Tr LoginAgainDialog.SaveEncryptedPassword}" />
<Grid Grid.Row="3" Margin="10,10,10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button IsDefault="True" IsEnabled="{Binding IsFormValid}" Margin="0,0,5,5"
Style="{StaticResource MaterialDesignOutlinedButton}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="{StaticResource True}" Content="{Tr LoginAgainDialog.LoginButton}" />
<Button IsCancel="True" Grid.Column="1" Margin="5,0,0,5"
Style="{StaticResource MaterialDesignOutlinedButton}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="{StaticResource False}" Content="{Tr LoginAgainDialog.CancelButton}" />
</Grid>
</Grid>
</UserControl>
@@ -1,65 +0,0 @@
<UserControl x:Class="NebulaAuth.View.Dialogs.LoginAgainOnImportDialog"
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:theme="clr-namespace:NebulaAuth.Theme"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:other="clr-namespace:NebulaAuth.ViewModel.Other"
xmlns:model="clr-namespace:NebulaAuth.Model"
xmlns:entities="clr-namespace:NebulaAuth.Model.Entities"
mc:Ignorable="d"
theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True"
Foreground="WhiteSmoke"
d:DataContext="{d:DesignInstance other:LoginAgainOnImportVM}"
Background="{DynamicResource WindowBackground}">
<Grid MinHeight="100" MinWidth="300" Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock theme:FontScaleWindow.Scale="0.9" theme:FontScaleWindow.ResizeFont="True" Margin="10,0,0,0"
HorizontalAlignment="Left">
<Run Text="{Tr LoginAgainDialog.LoginFor, IsDynamic=False}" />
<Run FontWeight="Bold" Text="{Binding UserName}" />
</TextBlock>
<TextBox Text="{Binding Password, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,0" Grid.Row="1"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
materialDesign:HintAssist.Hint="{Tr LoginAgainDialog.PasswordBox}" />
<ComboBox ToolTip="{Tr LoginAgainDialog.ProxyToolTip}" Grid.Row="2" Margin="10,18,10,0"
materialDesign:HintAssist.Hint="{Tr Common.Proxy}" ItemsSource="{Binding Proxies}"
SelectedItem="{Binding SelectedProxy}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type entities:MaProxy}">
<TextBlock Text="{Binding Converter={StaticResource ProxyTextConverter}, Mode=OneWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<UIElement.InputBindings>
<KeyBinding Key="Delete" Command="{Binding RemoveProxyCommand}" />
</UIElement.InputBindings>
</ComboBox>
<CheckBox Grid.Row="3" Margin="10,10,10,0" IsEnabled="{Binding Source={x:Static model:PHandler.IsPasswordSet}}"
IsChecked="{Binding SavePassword}" Content="{Tr LoginAgainDialog.SaveEncryptedPassword}" />
<CheckBox Grid.Row="4" Margin="10,10,10,0" IsEnabled="{Binding MafileHasProxy}"
Content="{Tr LoginAgainDialog.UseMafileProxy}" IsChecked="{Binding UseMafileProxy}" />
<Grid Grid.Row="5" Margin="10,10,10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button IsEnabled="{Binding IsFormValid}" IsDefault="True" Margin="0,5,5,5"
Style="{StaticResource MaterialDesignOutlinedButton}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="{StaticResource True}" Content="{Tr LoginAgainDialog.LoginButton}" />
<Button IsCancel="True" Grid.Column="1" Margin="5,5,0,5"
Style="{StaticResource MaterialDesignOutlinedButton}"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
CommandParameter="{StaticResource False}" Content="{Tr LoginAgainDialog.CancelButton}" />
</Grid>
</Grid>
</UserControl>
@@ -1,71 +0,0 @@
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using SteamLib.Core.Interfaces;
using SteamLib.Exceptions;
namespace NebulaAuth.View.Dialogs;
/// <summary>
/// Логика взаимодействия для WaitLoginDialog.xaml
/// </summary>
public partial class WaitLoginDialog : ICaptchaResolver
{
private TaskCompletionSource<string> _tcs = new();
public WaitLoginDialog()
{
InitializeComponent();
}
public async Task<string> Resolve(Uri imageUrl, HttpClient client)
{
CaptchaGrid.Visibility = Visibility.Visible;
var stream = await client.GetStreamAsync(imageUrl);
return await Application.Current.Dispatcher.Invoke(async () =>
{
var image = await LoadImage(stream);
CaptchaImage.Source = image;
try
{
return await _tcs.Task;
}
catch (TaskCanceledException)
{
throw new LoginException(LoginError.CaptchaRequired);
}
});
}
private async Task<BitmapImage> LoadImage(Stream stream)
{
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
ms.Position = 0;
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = ms;
image.EndInit();
await stream.DisposeAsync();
return image;
}
private void CancelButton_OnClick(object sender, RoutedEventArgs e)
{
_tcs.SetCanceled();
}
private void SendCaptchaBtn_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(CaptchaTB.Text)) return;
var oldTcs = _tcs;
_tcs = new TaskCompletionSource<string>();
oldTcs.SetResult(CaptchaTB.Text);
}
}
-166
View File
@@ -1,166 +0,0 @@
<UserControl x:Class="NebulaAuth.View.LinkerView"
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:other="clr-namespace:NebulaAuth.ViewModel.Other"
mc:Ignorable="d"
Foreground="WhiteSmoke"
FontFamily="{materialDesign:MaterialDesignFont}"
MinHeight="640"
MinWidth="400"
Background="{DynamicResource WindowBackground}"
d:DataContext="{d:DesignInstance other:LinkAccountVM}"
FontSize="18">
<d:DesignerProperties.DesignStyle>
<Style TargetType="UserControl">
<Setter Property="Background" Value="{DynamicResource WindowBackground}" />
</Style>
</d:DesignerProperties.DesignStyle>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="materialDesign:PackIcon">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=Tag}"
Value="False">
<Setter Property="Foreground" Value="IndianRed" />
<Setter Property="Kind" Value="CheckBoxOutlineBlank" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=Tag}"
Value="True">
<Setter Property="Foreground" Value="Green" />
<Setter Property="Kind" Value="CheckBoxOutline" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}, Path=Tag}"
Value="True">
<Setter Property="Foreground" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock FontStyle="Normal" Foreground="DarkGray" HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr LinkerDialog.Title}" />
<TextBlock Margin="5,0,0,0" VerticalAlignment="Center" FontSize="12">
<Hyperlink Command="{Binding OpenTroubleshootingCommand}">
<Run Text="{Tr LinkerDialog.GotErrorHyperlinkText, IsDynamic=False}" />
</Hyperlink>
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Width="30" Height="30" Style="{StaticResource MaterialDesignIconForegroundButton}"
HorizontalAlignment="Right" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Background="DarkGray" Grid.Row="1" />
<Grid Grid.Row="2" Margin="10,20,10,10">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox Padding="10" FontSize="16" Style="{StaticResource MaterialDesignOutlinedComboBox}"
SelectedValue="{Binding SelectedProxy}" ItemsSource="{Binding Proxies}"
materialDesign:HintAssist.Hint="{Tr LinkerDialog.Proxy}"
IsEnabled="{Binding IsPasswordFieldVisible}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Key, Mode=OneWay}" /><Run Text=":" />
<Run Text="{Binding Value.Address, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Grid.Column="1" IsEnabled="{Binding IsPasswordFieldVisible}" Margin="5,0,0,0"
Command="{Binding ResetProxyCommand}" Content="{materialDesign:PackIcon Trash}" />
</Grid>
<StackPanel Grid.Row="3" Orientation="Horizontal" Margin="7" Tag="{Binding IsLogin}">
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
<TextBlock FontSize="16" Text="{Tr LinkerDialog.Authorization}" />
</StackPanel>
<StackPanel Grid.Row="4" Orientation="Horizontal" Margin="7" Tag="{Binding IsEmailCode}">
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
<TextBlock FontSize="16" Text="{Tr LinkerDialog.EmailCode}" />
</StackPanel>
<StackPanel Grid.Row="5" Orientation="Horizontal" Margin="7" Tag="{Binding IsPhoneNumber}">
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
<TextBlock FontSize="16" Text="{Tr LinkerDialog.PhoneNumber}" />
</StackPanel>
<StackPanel Grid.Row="6" Orientation="Horizontal" Margin="7" Tag="{Binding IsEmailConfirmation}">
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
<TextBlock FontSize="16" Text="{Tr LinkerDialog.EmailLink}" />
</StackPanel>
<StackPanel Grid.Row="7" Orientation="Horizontal" Margin="7" Tag="{Binding IsLinkCode}">
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
<TextBlock FontSize="16" Text="{Tr LinkerDialog.SmsOrCode}" />
</StackPanel>
<StackPanel Grid.Row="8" Orientation="Horizontal" Margin="7" Tag="{Binding IsCompleted}">
<materialDesign:PackIcon Width="20" Height="20" VerticalAlignment="Center" Margin="0,0,5,0" />
<TextBlock FontSize="16" Text="{Tr LinkerDialog.Completed}" />
</StackPanel>
<Grid Grid.Row="9" Margin="10,20,10,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Padding="7" Text="{Binding FieldText}"
Visibility="{Binding IsFieldVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
Style="{StaticResource MaterialDesignOutlinedTextBox}" FontSize="16" Margin="0,0,0,10" />
<TextBox Grid.Row="1" Padding="7" Text="{Binding PassFieldText}"
Visibility="{Binding IsPasswordFieldVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
Style="{StaticResource MaterialDesignOutlinedTextBox}" FontSize="16" />
<GroupBox Grid.Row="2" MaxWidth="400" BorderBrush="{StaticResource PrimaryHueMidBrush}"
VerticalAlignment="Bottom" BorderThickness="1" Margin="0,10,0,0" Padding="5">
<GroupBox.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Tr LinkerDialog.Message}" FontSize="16" Foreground="GhostWhite" />
</DataTemplate>
</GroupBox.HeaderTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Foreground="GhostWhite" FontSize="16" Text="{Binding HintText}"
HorizontalAlignment="Stretch" TextWrapping="Wrap" />
<Button Command="{Binding CopyCodeCommand}"
Visibility="{Binding IsCompleted, Converter={StaticResource BooleanToVisibilityConverter}}"
Grid.Row="1">
<materialDesign:PackIcon Kind="ContentCopy" />
</Button>
</Grid>
</GroupBox>
</Grid>
<Button Grid.Row="10" Height="35" FontSize="17" Command="{Binding ProceedCommand}"
Content="{Tr LinkerDialog.ProceedButton}" />
</Grid>
</UserControl>
-90
View File
@@ -1,90 +0,0 @@
<UserControl x:Class="NebulaAuth.View.SettingsView"
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:other="clr-namespace:NebulaAuth.ViewModel.Other"
mc:Ignorable="d"
d:DesignHeight="650"
Foreground="WhiteSmoke"
FontFamily="{materialDesign:MaterialDesignFont}"
MinHeight="600"
MinWidth="400"
MaxWidth="200"
d:DataContext="{d:DesignInstance other:SettingsVM}"
Background="{DynamicResource WindowBackground}">
<Grid>
<Grid Margin="15,10,15,15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock FontStyle="Normal" Foreground="DarkGray" HorizontalAlignment="Left"
VerticalAlignment="Center" FontSize="18" Text="{Tr SettingsDialog.Title}" />
<Button IsCancel="True" Grid.Column="1" Width="30" Height="30"
Style="{StaticResource MaterialDesignIconForegroundButton}" HorizontalAlignment="Right"
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}">
<materialDesign:PackIcon Kind="Close" Width="24" Height="24" Foreground="IndianRed" />
</Button>
</Grid>
<Separator Grid.Row="1" Margin="0,10,0,0" />
<StackPanel Grid.Row="2" Margin="10,30,10,10" Orientation="Vertical" HorizontalAlignment="Center">
<ComboBox Style="{StaticResource MaterialDesignOutlinedComboBox}" FontSize="16"
ItemsSource="{Binding BackgroundModes}" DisplayMemberPath="Value" SelectedValuePath="Key"
SelectedValue="{Binding BackgroundMode}"
materialDesign:HintAssist.Hint="{Tr SettingsDialog.BackgroundHint}" />
<ComboBox Style="{StaticResource MaterialDesignFloatingHintComboBox}" Margin="0,20,0,0" FontSize="16"
ItemsSource="{Binding Languages}" DisplayMemberPath="Value" SelectedValuePath="Key"
SelectedValue="{Binding Language}" materialDesign:HintAssist.Hint="{Tr LanguageWord}" />
<CheckBox Margin="0,10,0,0" FontSize="16" IsChecked="{Binding HideToTray}"
Content="{Tr SettingsDialog.MinimizeToTray}" />
<CheckBox Margin="0,10,0,0" FontSize="16" IsChecked="{Binding UseIcon}"
Content="{Tr SettingsDialog.UseIndicator}" ToolTip="{Tr SettingsDialog.UseIndicatorHint}" />
<materialDesign:ColorPicker IsEnabled="{Binding UseIcon}" Color="{Binding IconColor, Delay=50}" />
<CheckBox Margin="0,10,0,5" FontSize="16" IsChecked="{Binding UseBackground}"
Content="{Tr SettingsDialog.UseCustomColor}" />
<materialDesign:ColorPicker Height="100" IsEnabled="{Binding UseBackground}"
Color="{Binding BackgroundColor, Delay=50}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<PasswordBox MinWidth="250" materialDesign:PasswordBoxAssist.Password="{Binding Password}"
Height="Auto" VerticalAlignment="Center" FontSize="16" Margin="0,10,0,0"
Style="{StaticResource MaterialDesignFloatingHintRevealPasswordBox}"
materialDesign:HintAssist.Hint="{Tr SettingsDialog.PasswordBox.CurrentCryptPassword}"
materialDesign:HintAssist.HelperText="{Tr SettingsDialog.PasswordBox.Hint}" />
<Button Command="{Binding SetPasswordCommand}" Margin="5,0,0,0" VerticalAlignment="Bottom"
Grid.Column="1">
<materialDesign:PackIcon Kind="ContentSave" />
</Button>
</Grid>
<CheckBox Margin="0,20,0,0" FontSize="16" 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,10,0,0" FontSize="16" IsChecked="{Binding UseAccountNameAsMafileName}">
<TextBlock TextWrapping="WrapWithOverflow" Text="{Tr SettingsDialog.UseAccountName}" />
</CheckBox>
<CheckBox IsChecked="{Binding IgnorePatchTuesdayErrors}" Margin="0,10,0,0" FontSize="16"
ToolTip="{Tr SettingsDialog.IgnorePatchTuesdayErrorsHint}">
<TextBlock TextWrapping="WrapWithOverflow" Text="{Tr SettingsDialog.IgnorePatchTuesdayErrors}" />
</CheckBox>
</StackPanel>
</Grid>
</Grid>
</UserControl>
-12
View File
@@ -1,12 +0,0 @@
namespace NebulaAuth.View;
/// <summary>
/// Логика взаимодействия для SettingsView.xaml
/// </summary>
public partial class SettingsView
{
public SettingsView()
{
InitializeComponent();
}
}
-457
View File
@@ -1,457 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
using AchiesUtilities.Collections;
using AchiesUtilities.Web.Proxy;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MaterialDesignThemes.Wpf;
using NebulaAuth.Core;
using NebulaAuth.Model;
using NebulaAuth.Model.Entities;
using NebulaAuth.Utility;
using NLog;
using SteamLib;
using SteamLib.Account;
using SteamLib.Authentication;
using SteamLib.Authentication.LoginV2;
using SteamLib.Core.Interfaces;
using SteamLib.Exceptions;
using SteamLib.Exceptions.Mobile;
using SteamLib.ProtoCore.Exceptions;
using SteamLib.SteamMobile.AuthenticatorLinker;
using SteamLib.Web;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace NebulaAuth.ViewModel.Other;
public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNumberProvider, ISmsCodeProvider
{
private const string LOCALIZATION_KEY = "LinkVM";
private static Logger Logger => Shell.Logger;
private static ILogger Logger2 => Shell.ExtensionsLogger;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public LinkAccountVM()
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{
if (MaClient.DefaultProxy != null)
{
var def = Proxies.FirstOrDefault(p => p.Value.Equals(MaClient.DefaultProxy));
if (def.Value != null!)
{
SelectedProxy = def;
}
}
}
[RelayCommand(AllowConcurrentExecutions = true, CanExecute = nameof(CanProceed))]
public async Task Proceed()
{
if (IsCompleted)
DialogHost.Close(null);
CanProceed = false;
#region Login
if (IsLogin == false)
{
SetProxy();
ClearCookies();
_loginV2ExecutorOptions = new LoginV2ExecutorOptions(LoginV2Executor.NullConsumer, Client)
{
DeviceDetails = LoginV2ExecutorOptions.GetMobileDefaultDevice(),
WebsiteId = "Mobile",
EmailAuthProvider = this,
Logger = Logger2
};
try
{
IsLogin = true;
var userName = FieldText;
var pass = PassFieldText;
_password = pass;
FieldText = string.Empty;
IsFieldVisible = false;
HintText = string.Empty;
_sessionData =
(MobileSessionData) await LoginV2Executor.DoLogin(_loginV2ExecutorOptions, userName, pass);
Handler.CookieContainer.SetSteamMobileCookiesWithMobileToken(_sessionData);
IsEmailCode = true;
}
catch (EResultException ex)
{
Logger.Error(ex, "Link exception on login");
HintText = GetLocalizationOrDefault("CantLogin") + ErrorTranslatorHelper.TranslateEResult(ex.Result);
InvokeOnDispatcher(ResetState);
return;
}
catch (LoginException ex)
{
Logger.Error(ex, "Link exception on login");
HintText = GetLocalizationOrDefault("CantLogin") + ErrorTranslatorHelper.TranslateLoginError(ex.Error);
InvokeOnDispatcher(ResetState);
return;
}
catch (Exception ex)
{
Logger.Error(ex, "Link exception on login");
HintText = GetLocalizationOrDefault("CantLogin") + ex.Message;
InvokeOnDispatcher(ResetState);
return;
}
}
if (IsEmailCode == false)
{
_emailCodeTcs.SetResult(FieldText);
HintText = string.Empty;
FieldText = string.Empty;
_emailCodeTcs = new TaskCompletionSource<string>();
IsFieldVisible = false;
return;
}
#endregion
if (_isLinkStarted)
goto linkStarted;
try
{
_isLinkStarted = true;
var linkOptions = new LinkOptions(Client, LoginV2Executor.NullConsumer, this,
null, this, this, Backup, Logger2);
_linker = new SteamAuthenticatorLinker(linkOptions);
var result = await _linker.LinkAccount(_sessionData);
IsLinkCode = true;
IsCompleted = true;
var mafile = Mafile.FromMobileDataExtended(result);
try
{
if (SelectedProxy.HasValue)
mafile.Proxy = new MaProxy(SelectedProxy.Value.Key, SelectedProxy.Value.Value);
if (Settings.Instance.IsPasswordSet)
mafile.Password = PHandler.Encrypt(_password);
}
catch (Exception ex)
{
Logger.Error(ex, "Error during saving Nebula data to mafile");
}
Storage.SaveMafile(mafile);
File.Delete(Path.Combine("mafiles_backup", mafile.AccountName + ".mafile"));
HintText =
string.Format(GetLocalizationOrDefault("MafileLinked"),
mafile.RevocationCode,
mafile.SessionData?.SteamId.Steam64);
_rCode = mafile.RevocationCode ?? string.Empty;
CanProceed = true;
return;
}
catch (AuthenticatorLinkerException ex)
{
Logger.Error(ex, "Link exception");
HintText = $"{GetLocalizationCommon("Error")}: {ErrorTranslatorHelper.TranslateLinkerError(ex.Error)}";
InvokeOnDispatcher(ResetState);
return;
}
catch (HttpRequestException ex)
{
var msg = ex.StatusCode?.ToString() ?? ex.Message;
HintText = $"{GetLocalizationCommon("RequestError")}: {msg}";
InvokeOnDispatcher(ResetState);
return;
}
catch (EResultException ex)
{
Logger.Error(ex, "Link exception");
HintText = GetLocalizationOrDefault("ErrorWithCode") + ErrorTranslatorHelper.TranslateEResult(ex.Result);
InvokeOnDispatcher(ResetState);
return;
}
catch (Exception ex)
{
Logger.Error(ex, "Link exception");
HintText = GetLocalizationOrDefault("UnknownError") + ex.Message;
InvokeOnDispatcher(ResetState);
return;
}
linkStarted:
if (IsPhoneNumber == false)
{
var phoneText = FieldText;
FieldText = string.Empty;
if (string.IsNullOrWhiteSpace(phoneText))
{
HintText = string.Empty;
IsFieldVisible = false;
_phoneNumberTcs.SetResult(null);
_phoneNumberTcs = new TaskCompletionSource<long?>();
return;
}
if (!string.IsNullOrWhiteSpace(phoneText) && phoneText.Length >= 4 &&
long.TryParse(phoneText, out var phone))
{
HintText = string.Empty;
IsFieldVisible = false;
_phoneNumberTcs.SetResult(phone);
_phoneNumberTcs = new TaskCompletionSource<long?>();
return;
}
HintText = GetLocalizationOrDefault("PleaseEnterCorrectPhone");
CanProceed = true;
return;
}
if (IsEmailConfirmation == false)
{
HintText = string.Empty;
_emailConfTcs.SetResult();
_emailConfTcs = new TaskCompletionSource();
CanProceed = false;
return;
}
if (IsLinkCode == false)
{
var linkCode = FieldText;
FieldText = string.Empty;
if (!string.IsNullOrWhiteSpace(linkCode) && linkCode.Length >= 4)
{
HintText = string.Empty;
IsFieldVisible = false;
_linkCodeTcs.SetResult(linkCode);
_linkCodeTcs = new TaskCompletionSource<string>();
}
else
{
HintText = GetLocalizationOrDefault("PleaseEnterCorrectCode");
CanProceed = true;
}
}
}
[RelayCommand]
public void ResetProxy()
{
if (IsPasswordFieldVisible == false) return;
SelectedProxy = null;
}
private void InvokeOnDispatcher(Action action)
{
Application.Current.Dispatcher.BeginInvoke(action, null);
}
private void ResetState()
{
PassFieldText = string.Empty;
IsLogin = false;
IsFieldVisible = true;
IsEmailCode = false;
_isLinkStarted = false;
IsPhoneNumber = false;
IsEmailConfirmation = false;
CanProceed = true;
_emailCodeTcs = new TaskCompletionSource<string>();
_rCode = string.Empty;
_password = string.Empty;
}
private void Backup(MobileDataExtended data)
{
if (Directory.Exists("mafiles_backup") == false)
{
Directory.CreateDirectory("mafiles_backup");
}
var json = NebulaSerializer.SerializeMafile(data, null);
File.WriteAllText(Path.Combine("mafiles_backup", data.AccountName + ".mafile"),
json); //TODO: Move logic to Storage
}
[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
});
}
[RelayCommand]
private void CopyCode()
{
try
{
Clipboard.SetText(_rCode);
}
catch (Exception ex)
{
Shell.Logger.Error(ex, "Error whily copying RCode");
}
}
private static string GetLocalizationOrDefault(string key)
{
return LocManager.GetCodeBehindOrDefault(key, LOCALIZATION_KEY, key);
}
private static string GetLocalizationCommon(string key)
{
return LocManager.GetCommonOrDefault(key, key);
}
#region Properties
[ObservableProperty] [NotifyPropertyChangedFor(nameof(IsPasswordFieldVisible))]
private bool _isLogin;
[ObservableProperty] private bool _isEmailCode;
[ObservableProperty] private bool _isPhoneNumber;
[ObservableProperty] private bool _isEmailConfirmation;
[ObservableProperty] private bool _isLinkCode;
[ObservableProperty] private bool _isCompleted;
[ObservableProperty] private bool _isFieldVisible = true;
[ObservableProperty] private string _fieldText;
[ObservableProperty] private string _passFieldText;
[ObservableProperty] private string _hintText = GetLocalizationOrDefault("EnterLoginAndPassword");
private TaskCompletionSource<string> _emailCodeTcs = new();
private TaskCompletionSource<long?> _phoneNumberTcs = new();
private TaskCompletionSource _emailConfTcs = new();
private TaskCompletionSource<string> _linkCodeTcs = new();
private bool _isLinkStarted;
private string _rCode = string.Empty;
private string _password = string.Empty;
[ObservableProperty] [NotifyCanExecuteChangedFor(nameof(ProceedCommand))]
private bool _canProceed = true;
public bool IsPasswordFieldVisible => !IsLogin;
private LoginV2ExecutorOptions _loginV2ExecutorOptions;
private SteamAuthenticatorLinker _linker;
private MobileSessionData _sessionData;
#endregion
#region HttpClient
private static HttpClient Client { get; }
private static HttpClientHandler Handler { get; }
private static DynamicProxy Proxy { get; }
public ObservableDictionary<int, ProxyData> Proxies => ProxyStorage.Proxies;
public KeyValuePair<int, ProxyData>? SelectedProxy
{
get => _selectedProxy;
set
{
SetProperty(ref _selectedProxy, value);
SetProxy();
}
}
private KeyValuePair<int, ProxyData>? _selectedProxy;
#endregion
#region Providers
public int MaxRetryCount { get; }
public Task<string> GetEmailAuthCode(ILoginConsumer caller)
{
CanProceed = true;
HintText = GetLocalizationOrDefault("EnterEmailCode");
IsFieldVisible = true;
return _emailCodeTcs.Task;
}
public Task<string> GetAddAuthenticatorCode(ILoginConsumer caller)
{
IsPhoneNumber = true;
IsEmailConfirmation = true;
CanProceed = true;
HintText = GetLocalizationOrDefault("EnterEmailCode");
IsFieldVisible = true;
return _linkCodeTcs.Task;
}
public Task ConfirmEmailLink(ILoginConsumer caller, EmailConfirmationType confirmationType)
{
IsPhoneNumber = true;
CanProceed = true;
HintText = GetLocalizationOrDefault("ClickOnEmailLink");
return _emailConfTcs.Task;
}
public Task<long?> GetPhoneNumber(ILoginConsumer caller)
{
CanProceed = true;
HintText = GetLocalizationOrDefault("EnterPhoneNumber");
IsFieldVisible = true;
return _phoneNumberTcs.Task;
}
public async Task<int> GetSmsCode(ILoginConsumer caller, long? phoneNumber, string? hint)
{
IsPhoneNumber = true;
IsEmailConfirmation = true;
CanProceed = true;
HintText = string.Format(GetLocalizationOrDefault("PhoneHint"), hint);
IsFieldVisible = true;
var code = await _linkCodeTcs.Task;
return int.Parse(code);
}
#endregion
#region Client
static LinkAccountVM()
{
Proxy = new DynamicProxy();
var clientPair = ClientBuilder.BuildMobileClient(Proxy, null);
Client = clientPair.Client;
Handler = clientPair.Handler;
}
private void ClearCookies()
{
Handler.CookieContainer.ClearMobileSessionCookies();
}
private void SetProxy()
{
Proxy.SetData(SelectedProxy?.Value);
}
#endregion
}
+3 -3
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<item> <item>
<version>1.5.7.0</version> <version>1.7.1.0</version>
<url>https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/releases/download/1.5.7/NebulaAuth.1.5.7.zip</url> <url>https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/releases/download/1.7.1/NebulaAuth.1.7.1.zip</url>
<changelog>https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/changelog/1.5.7.html</changelog> <changelog>https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/changelog/1.7.1.html</changelog>
<mandatory>false</mandatory> <mandatory>false</mandatory>
</item> </item>
@@ -1,59 +0,0 @@
using System.Net;
using SteamLib.Core;
using SteamLib.Exceptions;
using SteamLib.ProtoCore;
using SteamLib.ProtoCore.Services;
namespace SteamLib.Api.Mobile;
public static class SteamMobileAuthenticatorApi
{
public static async Task<GetAuthSessionsForAccount_Response> GetAuthSessionsForAccount(HttpClient client,
string accessToken, CancellationToken cancellationToken = default)
{
var req = SteamConstants.STEAM_API + "IAuthenticationService/GetAuthSessionsForAccount/v1?access_token=" +
accessToken;
try
{
return await client.GetProto<GetAuthSessionsForAccount_Response>(req, new EmptyMessage(),
cancellationToken);
}
catch (HttpRequestException ex)
when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
throw new SessionInvalidException(SessionInvalidException.GOT_401_MSG, ex);
}
}
public static async Task<GetAuthSessionInfo_Response> GetAuthSessionInfo(HttpClient client, string accessToken,
ulong clientId, CancellationToken cancellationToken = default)
{
var req = SteamConstants.STEAM_API + "IAuthenticationService/GetAuthSessionInfo/v1?access_token=" + accessToken;
var reqData = new GetAuthSessionInfo_Request
{
ClientId = clientId
};
try
{
return await client.PostProto<GetAuthSessionInfo_Response>(req, reqData, cancellationToken);
}
catch (HttpRequestException ex)
when (ex.StatusCode == HttpStatusCode.Unauthorized)
{
throw new SessionInvalidException(SessionInvalidException.GOT_401_MSG, ex);
}
}
public static async Task<bool> UpdateAuthSessionStatus(HttpClient client, string accessToken, string sharedSecret,
UpdateAuthSessionWithMobileConfirmation_Request request)
{
var req = SteamConstants.STEAM_API +
"IAuthenticationService/UpdateAuthSessionWithMobileConfirmation/v1?access_token=" + accessToken;
request.ComputeSignature(sharedSecret);
await client.PostProtoEnsureSuccess(req, request);
return true;
}
}
-114
View File
@@ -1,114 +0,0 @@
using AchiesUtilities.Models;
using AchiesUtilities.Newtonsoft.JSON.Converters.Special;
using Newtonsoft.Json;
using SteamLib.Authentication;
using SteamLib.Core;
using SteamLib.Core.Enums;
using SteamLib.Core.StatusCodes;
using SteamLib.Exceptions;
using SteamLib.Exceptions.General;
namespace SteamLib.Api;
public static class SteamGlobalApi
{
/// <summary>
/// Raw AccessToken value
/// </summary>
/// <param name="client"></param>
/// <param name="domain"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="SessionInvalidException"></exception>
public static async Task<string> RefreshJwt(HttpClient client, SteamDomain domain,
CancellationToken cancellationToken = default)
{
var domainUri = SteamDomains.GetDomain(domain);
var data = new Dictionary<string, string>
{
{"redir", domainUri}
};
var cont = new FormUrlEncodedContent(data);
var resp = await client.PostAsync("https://login.steampowered.com/jwt/ajaxrefresh", cont, cancellationToken);
var respStr = await resp.EnsureSuccessStatusCode().Content.ReadAsStringAsync(cancellationToken);
JwtRefreshJson? jwtRefresh;
try
{
jwtRefresh = JsonConvert.DeserializeObject<JwtRefreshJson>(respStr);
}
catch (Exception ex)
{
throw new UnsupportedResponseException(respStr, ex);
}
if (jwtRefresh?.Success != true)
{
Exception? inner = null;
if (jwtRefresh?.Error != null)
{
var errorCode = jwtRefresh.Error.Value;
var translated = SteamStatusCode.Translate<SteamStatusCode>(errorCode, out _);
inner = new SteamStatusCodeException(translated, respStr);
}
throw new SessionInvalidException(
"AjaxRefresh was not successful. 'steamRefresh_steam' cookie is missing or refresh token is invalid",
inner);
}
data = new Dictionary<string, string>
{
{"steamID", jwtRefresh.SteamId.ToString()},
{"nonce", jwtRefresh.Nonce},
{"redir", jwtRefresh.Redir},
{"auth", jwtRefresh.Auth}
};
cont = new FormUrlEncodedContent(data);
var update = await client.PostAsync(jwtRefresh.LoginUrl, cont, cancellationToken);
var updateResp = await update.Content.ReadAsStringAsync(cancellationToken);
JwtUpdateJson result;
try
{
result = JsonConvert.DeserializeObject<JwtUpdateJson>(updateResp) ?? throw new NullReferenceException();
}
catch (Exception ex)
{
throw new UnsupportedResponseException(updateResp, ex);
}
var resultStatus = SteamStatusCode.Translate<SteamStatusCode>(result.Result, out _);
if (resultStatus.Equals(SteamStatusCode.Ok) == false)
{
throw new SessionInvalidException(
"AjaxRefresh (set-token) response was not successful. Response string stored in Exception.Data")
{
Data = {{"Response", updateResp}}
};
}
return SteamTokenHelper.ExtractJwtFromSetCookiesHeader(update.Headers);
}
private class JwtRefreshJson
{
[JsonProperty("success")] public bool Success { get; set; }
[JsonProperty("login_url")] public string LoginUrl { get; set; } = string.Empty;
[JsonProperty("steamID")] public long SteamId { get; set; }
[JsonProperty("nonce")] public string Nonce { get; set; } = string.Empty;
[JsonProperty("redir")] public string Redir { get; set; } = string.Empty;
[JsonProperty("auth")] public string Auth { get; set; } = string.Empty;
[JsonProperty("error")] public int? Error { get; set; }
}
private class JwtUpdateJson
{
[JsonProperty("result")] public int Result { get; set; }
[JsonProperty("rtExpiry")]
[JsonConverter(typeof(UnixTimeStampConverter))]
public UnixTimeStamp RtExpiry { get; set; }
}
}
@@ -1,112 +0,0 @@
using Microsoft.Extensions.Logging;
using SteamLib.Authentication;
using SteamLib.Core.Interfaces;
using SteamLib.Exceptions;
namespace SteamLib.Login.Default;
[Obsolete("Method removed from Steam")]
public class LoginExecutor
{
public static ILoginConsumer NullConsumer { get; } = new NullLoginConsumer();
public ILoginConsumer Caller { get; }
public HttpClient HttpClient { get; }
public ILogger? Logger { get; init; }
public IEmailProvider? EmailAuthProvider { get; init; }
public ICaptchaResolver? CaptchaResolver { get; init; }
public ISteamGuardProvider? SteamGuardProvider { get; init; }
private LoginExecutor(LoginExecutorOptions options)
{
Caller = options.Consumer;
HttpClient = options.HttpClient;
Logger = options.Logger;
EmailAuthProvider = options.EmailAuthProvider;
SteamGuardProvider = options.SteamGuardProvider;
CaptchaResolver = options.CaptchaResolver;
}
public static async Task<TransferParameters> DoLogin(LoginExecutorOptions options, string username, string password,
CancellationToken cancellationToken = default) //TODO: logs
{
var executor = new LoginExecutor(options);
var client = executor.HttpClient;
LoginStage loginStage = new GetRsaStage(username);
loginStage = await ((GetRsaStage) loginStage).Proceed(client, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
if (loginStage is ReadyToLoginStage rdyToLoginStage)
{
loginStage = await rdyToLoginStage.Proceed(client, password, string.Empty, cancellationToken);
}
cancellationToken.ThrowIfCancellationRequested();
var twoFactorTry = 0;
var emailTry = 0;
while (loginStage.GetType() != typeof(LoginErrorStage) && loginStage is not LoginSuccessStage)
{
cancellationToken.ThrowIfCancellationRequested();
switch (loginStage)
{
case CaptchaNeededStage captchaNeededStage:
{
if (executor.CaptchaResolver == null) throw new LoginException(LoginError.CaptchaRequired);
var captchaText = await executor.CaptchaResolver.Resolve(captchaNeededStage.CaptchaImage, client);
loginStage = await captchaNeededStage.Proceed(client, captchaText, cancellationToken);
break;
}
case EmailAuthStage emailAuthStage:
{
if (executor.EmailAuthProvider == null) throw new LoginException(LoginError.EmailAuthRequired);
if (emailTry > executor.EmailAuthProvider.MaxRetryCount)
throw new LoginException(LoginError.InvalidEmailAuthCode);
var code = await executor.EmailAuthProvider.GetEmailAuthCode(executor.Caller);
loginStage = await emailAuthStage.Proceed(client, code, cancellationToken);
emailTry++;
break;
}
case TwoFactorStage twoFactorStage:
{
if (executor.SteamGuardProvider == null) throw new LoginException(LoginError.SteamGuardRequired);
if (twoFactorTry > executor.SteamGuardProvider.MaxRetryCount)
throw new LoginException(LoginError.InvalidTwoFactorCode);
var twoFactor = await executor.SteamGuardProvider.GetSteamGuardCode(executor.Caller);
loginStage = await twoFactorStage.Proceed(client, twoFactor, cancellationToken);
twoFactorTry++;
break;
}
case ReadyToLoginStage readyToLoginStage: //When captcha proceeded, stage goes there
{
loginStage = await readyToLoginStage.Proceed(client, password, string.Empty, cancellationToken);
break;
}
default:
throw new ArgumentOutOfRangeException(nameof(loginStage));
}
}
if (loginStage is LoginErrorStage error)
{
var content = await error.Response.Content.ReadAsStringAsync(cancellationToken);
if (content.Contains("The account name or password that you have entered is incorrect"))
{
throw new LoginException(LoginError.InvalidCredentials);
}
throw new LoginException(content);
}
if (loginStage is not LoginSuccessStage successStage)
{
throw new InvalidOperationException("Unexpected login stage at this point. " + loginStage.GetType())
{
Data = {{"stage", loginStage}}
};
}
return successStage.TransferParameters;
}
}
@@ -1,20 +0,0 @@
using Microsoft.Extensions.Logging;
using SteamLib.Core.Interfaces;
namespace SteamLib.Login.Default;
public class LoginExecutorOptions
{
public ILoginConsumer Consumer { get; }
public HttpClient HttpClient { get; }
public ILogger? Logger { get; init; }
public IEmailProvider? EmailAuthProvider { get; init; }
public ICaptchaResolver? CaptchaResolver { get; init; }
public ISteamGuardProvider? SteamGuardProvider { get; init; }
public LoginExecutorOptions(ILoginConsumer consumer, HttpClient httpClient)
{
Consumer = consumer;
HttpClient = httpClient;
}
}
@@ -1,18 +0,0 @@
using Newtonsoft.Json;
namespace SteamLib.Login.Default;
#pragma warning disable CS8618
/// <summary>
/// Class to Deserialize the json response strings after the login/>
/// </summary>
internal class LoginResultJson
{
[JsonProperty("success")] public bool Success { get; set; }
[JsonProperty("message")] public string Message { get; set; }
[JsonProperty("captcha_needed")] public bool CaptchaNeeded { get; set; }
[JsonProperty("captcha_gid")] public string CaptchaGid { get; set; }
[JsonProperty("emailauth_needed")] public bool EmailAuthNeeded { get; set; }
[JsonProperty("emailsteamid")] public string EmailSteamId { get; set; }
[JsonProperty("requires_twofactor")] public bool RequiresTwoFactor { get; set; }
}
@@ -1,252 +0,0 @@
using AchiesUtilities.Web.Extensions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SteamLib.Authentication;
using SteamLib.Utility;
namespace SteamLib.Login.Default;
internal abstract class LoginStage
{
protected Dictionary<string, string> Data { get; }
protected LoginStage()
{
Data = new Dictionary<string, string>();
}
protected LoginStage(Dictionary<string, string> data)
{
Data = data;
}
protected bool CheckIfLoginCompleted(LoginResultJson json)
{
return !json.CaptchaNeeded && !json.EmailAuthNeeded && !json.RequiresTwoFactor && json.Success;
}
protected async Task<LoginResultJson> ConvertJson(HttpResponseMessage response)
{
response.EnsureSuccessStatusCode();
var webResponseString = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<LoginResultJson>(webResponseString)!;
}
protected async Task<LoginSuccessStage> CompleteLogin(HttpResponseMessage response)
{
var jsonStr = await response.Content.ReadAsStringAsync();
var json = JObject.Parse(jsonStr);
var transferParameters = json["transfer_parameters"]!.ToObject<TransferParameters>()!;
return new LoginSuccessStage(transferParameters);
}
protected async Task<HttpResponseMessage> LoginRequest(HttpClient client,
CancellationToken cancellationToken = default)
{
return await client.PostAsync("https://steamcommunity.com/login/dologin", new FormUrlEncodedContent(Data),
cancellationToken);
}
}
internal class LoginErrorStage : LoginStage
{
public readonly int ErrorCode;
public readonly string ErrorString;
public readonly HttpResponseMessage Response;
public readonly LoginStage Stage;
public LoginErrorStage(string errorString, HttpResponseMessage responseMessage, LoginStage stage)
{
ErrorString = errorString;
Stage = stage;
ErrorCode = Utilities.GetSuccessCode(responseMessage.Content.ReadAsStringSync());
Response = responseMessage;
}
}
internal class GetRsaStage : LoginStage
{
public GetRsaStage(Dictionary<string, string> data) : base(data)
{
}
public GetRsaStage(string userName)
{
Data["username"] = userName;
}
/// <summary>
/// </summary>
/// <param name="client"></param>
/// <param name="cancellationToken"></param>
/// <returns> <see cref="ReadyToLoginStage" /> if success</returns>
public async Task<LoginStage> Proceed(HttpClient client, CancellationToken cancellationToken = default)
{
var content = new FormUrlEncodedContent(Data);
var response = await client.PostAsync("https://steamcommunity.com/login/getrsakey", content, cancellationToken);
var responseString = await response.Content.ReadAsStringAsync(cancellationToken);
var rsaJson = JsonConvert.DeserializeObject<RsaKeyJson>(responseString)!;
if (!rsaJson.success)
return new LoginErrorStage("Can't get RSA", response, this);
Data["rsatimestamp"] = rsaJson.timestamp;
return new ReadyToLoginStage(Data, rsaJson.publickey_exp, rsaJson.publickey_mod);
}
}
internal class ReadyToLoginStage : LoginStage
{
private readonly string _publicKeyExp;
private readonly string _publicKeyMod;
internal ReadyToLoginStage(Dictionary<string, string> data, string publicKeyExp, string publicKeyMod) : base(data)
{
_publicKeyExp = publicKeyExp;
_publicKeyMod = publicKeyMod;
}
public async Task<LoginStage> Proceed(HttpClient client, string password, string loginFriendlyName,
CancellationToken cancellationToken = default)
{
var encryptedBase64Password =
EncryptionHelper.ToBase64EncryptedPassword(_publicKeyExp, _publicKeyMod, password);
var unixTimestamp = (int) DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
Data["password"] = encryptedBase64Password;
Data["remember_login"] = "true";
Data["loginfriendlyname"] = loginFriendlyName;
Data["donotcache"] =
unixTimestamp + "000"; // Added three "0"'s because Steam has a weird unix timestamp interpretation.
_ = await client.GetAsync("https://steamcommunity.com/", cancellationToken);
var webResponse = await LoginRequest(client, cancellationToken);
var result = await ConvertJson(webResponse);
if (CheckIfLoginCompleted(result))
{
return await CompleteLogin(webResponse);
}
if (result.CaptchaNeeded)
{
return new CaptchaNeededStage(Data, result.CaptchaGid);
}
if (result.EmailAuthNeeded)
{
return new EmailAuthStage(Data);
}
if (result.RequiresTwoFactor)
{
return new TwoFactorStage(Data);
}
return new LoginErrorStage("Can't proceed login.", webResponse, this);
}
}
internal class TwoFactorStage : LoginStage
{
internal TwoFactorStage(Dictionary<string, string> data) : base(data)
{
}
public async Task<LoginStage> Proceed(HttpClient client, string twoFactorCode,
CancellationToken cancellationToken = default)
{
Data["twofactorcode"] = twoFactorCode;
var webResponse = await LoginRequest(client, cancellationToken);
var result = await ConvertJson(webResponse);
if (CheckIfLoginCompleted(result))
{
return await CompleteLogin(webResponse);
}
if (result.CaptchaNeeded)
{
return new CaptchaNeededStage(Data, result.CaptchaGid);
}
if (result.RequiresTwoFactor)
{
return new TwoFactorStage(Data);
}
return new LoginErrorStage("Can't proceed login. Bad TwoFactor code", webResponse, this);
}
}
internal class EmailAuthStage : LoginStage
{
internal EmailAuthStage(Dictionary<string, string> data) : base(data)
{
}
public async Task<LoginStage> Proceed(HttpClient client, string emailCode,
CancellationToken cancellationToken = default)
{
Data["emailauth"] = emailCode;
var webResponse = await LoginRequest(client, cancellationToken);
var result = await ConvertJson(webResponse);
if (CheckIfLoginCompleted(result))
{
return await CompleteLogin(webResponse);
}
if (result.CaptchaNeeded)
{
return new CaptchaNeededStage(Data, result.CaptchaGid);
}
if (result.EmailAuthNeeded)
{
return new EmailAuthStage(Data);
}
return new LoginErrorStage("Can't proceed login. Bad Email code", webResponse, this);
}
}
internal class CaptchaNeededStage : LoginStage
{
internal Uri CaptchaImage { get; }
public CaptchaNeededStage(Dictionary<string, string> data, string captchaGid) : base(data)
{
captchaGid = Uri.EscapeDataString(captchaGid);
Data["captchagid"] = captchaGid;
CaptchaImage = new Uri("https://steamcommunity.com/login/rendercaptcha?gid=" + captchaGid);
}
/// <summary>
/// </summary>
/// <param name="client"></param>
/// <param name="captchaText"></param>
/// <param name="cancellationToken"></param>
/// <returns><see cref="ReadyToLoginStage" /> or <see cref="LoginErrorStage" /></returns>
public async Task<LoginStage> Proceed(HttpClient client, string captchaText,
CancellationToken cancellationToken = default)
{
Data["captcha_text"] = captchaText;
//When captcha required we need to do login from start
var rsaStage = new GetRsaStage(Data);
var res = await rsaStage.Proceed(client, cancellationToken);
return res;
}
}
internal class LoginSuccessStage : LoginStage
{
public TransferParameters TransferParameters { get; }
public LoginSuccessStage(TransferParameters transferParameters)
{
TransferParameters = transferParameters;
}
}
@@ -1,8 +0,0 @@
using SteamLib.Core.Interfaces;
namespace SteamLib.Login.Default;
internal class NullLoginConsumer : ILoginConsumer
{
public string FriendlyName { get; } = "null";
}
@@ -1,17 +0,0 @@
// ReSharper disable InconsistentNaming
// ReSharper disable IdentifierTypo
#pragma warning disable CS8618
namespace SteamLib.Login.Default;
/// <summary>
/// Class to Deserialize the json response strings of the getResKey request/>
/// </summary>
internal class RsaKeyJson
{
public bool success { get; set; }
public string publickey_mod { get; set; }
public string publickey_exp { get; set; }
public string timestamp { get; set; }
}
@@ -1,254 +0,0 @@
using AchiesUtilities.Web.Extensions;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SteamLib.Account;
using SteamLib.Core;
using SteamLib.Core.Enums;
using SteamLib.Core.Interfaces;
using SteamLib.Core.Models;
using SteamLib.Core.StatusCodes;
using SteamLib.Exceptions;
using SteamLib.Login.Default;
using SteamLib.ProtoCore;
using SteamLib.ProtoCore.Enums;
using SteamLib.ProtoCore.Exceptions;
using SteamLib.ProtoCore.Services;
using SteamLib.Utility;
using SteamLib.Web;
namespace SteamLib.Authentication.LoginV2;
public class LoginV2Executor
{
public static ILoginConsumer NullConsumer { get; } = new NullLoginConsumer();
public ILoginConsumer Caller { get; }
public HttpClient HttpClient { get; }
public ILogger? Logger { get; init; }
public IEmailProvider? EmailAuthProvider { get; init; }
public ICaptchaResolver? CaptchaResolver { get; init; }
public ISteamGuardProvider? SteamGuardProvider { get; init; }
public string WebsiteId { get; init; }
public DeviceDetails DeviceDetails { get; init; }
private LoginV2Executor(LoginV2ExecutorOptions options)
{
Caller = options.Consumer;
HttpClient = options.HttpClient;
Logger = options.Logger;
EmailAuthProvider = options.EmailAuthProvider;
SteamGuardProvider = options.SteamGuardProvider;
WebsiteId = options.GetWebsiteIdOrDefault();
DeviceDetails = options.DeviceDetails ?? DeviceDetails.CreateDefault();
}
/// <summary>
/// Do log in on <see href="https://steamcommunity.com/">SteamCommunity</see>.<br />
/// Some functions require proper SessionId. But <see cref="SessionData" /> contains only SteamCommunity related
/// SessionId and some functions on other services may not work
/// </summary>
/// <param name="options"></param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="cancellationToken"></param>
/// <returns><see cref="SessionData" /> or <see cref="MobileSessionData" /> depending on which token type is returned</returns>
/// <exception cref="LoginException"></exception>
/// <exception cref="EResultException"></exception>
/// <exception cref="NotSupportedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static async Task<ISessionData> DoLogin(LoginV2ExecutorOptions options, string username, string password,
CancellationToken cancellationToken = default) //TODO: logs
{
var executor = new LoginV2Executor(options);
var client = executor.HttpClient;
var sessionId = await SteamWebApi.GetLoginSessionId(client, cancellationToken);
var rsgMsg = new GetPasswordRSAPublicKey_Request
{
AccountName = username
};
var rsaResp = await client.GetProto<GetPasswordRSAPublicKey_Response>(
"https://api.steampowered.com/IAuthenticationService/GetPasswordRSAPublicKey/v1", rsgMsg,
cancellationToken);
var encodedPassword =
EncryptionHelper.ToBase64EncryptedPassword(rsaResp.PublickKeyExp, rsaResp.PublickKeyMod, password);
var beginAuthMsg = new BeginAuthSessionViaCredentials_Request
{
DeviceFriendlyName = string.Empty,
AccountName = username,
EncryptedPassword = encodedPassword,
EncryptionTimestamp = rsaResp.Timestamp,
RememberLogin = true,
PlatformType = executor.DeviceDetails.PlatformType,
Persistence = 1,
WebsiteId = executor.WebsiteId,
DeviceDetails = executor.DeviceDetails
};
BeginAuthSessionViaCredentials_Response beginAuthResp;
try
{
beginAuthResp = await client.PostProto<BeginAuthSessionViaCredentials_Response>(
"https://api.steampowered.com/IAuthenticationService/BeginAuthSessionViaCredentials/v1", beginAuthMsg,
cancellationToken);
}
catch (EResultException ex)
when (ex.Result == EResult.InvalidPassword)
{
throw new LoginException(LoginError.InvalidCredentials);
}
var clientId = beginAuthResp.ClientId;
var steamId = (long) beginAuthResp.Steamid;
var conf = beginAuthResp.AllowedConfirmations.FirstOrDefault(c =>
c.ConfirmationType is EAuthSessionGuardType.EmailCode or EAuthSessionGuardType.DeviceCode);
conf ??= beginAuthResp.AllowedConfirmations.FirstOrDefault();
switch (conf?.ConfirmationType)
{
case EAuthSessionGuardType.None:
break;
case EAuthSessionGuardType.DeviceCode:
case EAuthSessionGuardType.EmailCode:
await UpdateWithCode(executor, clientId, (ulong) steamId, conf.ConfirmationType);
break;
case EAuthSessionGuardType.Unknown:
case EAuthSessionGuardType.DeviceConfirmation:
case EAuthSessionGuardType.EmailConfirmation:
case EAuthSessionGuardType.MachineToken:
case EAuthSessionGuardType.LegacyMachineAuth:
throw new NotSupportedException(
$"Auth confirmation type of {conf.ConfirmationType} is not implemented yet");
default:
throw new ArgumentOutOfRangeException(nameof(conf.ConfirmationType), conf?.ConfirmationType,
"Unknown confirmation type or null");
}
var pollSessionMsg = new PollAuthSessionStatus_Request
{
ClientId = clientId,
RequestId = beginAuthResp.RequestId
};
var pollResp =
await client.PostProto<PollAuthSessionStatus_Response>(
"https://api.steampowered.com/IAuthenticationService/PollAuthSessionStatus/v1", pollSessionMsg,
cancellationToken);
SteamAuthToken refreshToken;
try
{
refreshToken = SteamTokenHelper.Parse(pollResp.RefreshToken);
if (refreshToken.Type is not (SteamAccessTokenType.Refresh or SteamAccessTokenType.MobileRefresh))
throw new ArgumentException(
"Refresh token must be of type Refresh or MobileRefresh. No 'renew' audience found in JWT.",
nameof(pollResp.RefreshToken)); //Argument exception for less code
}
catch (ArgumentException ex)
{
throw new LoginException(null,
"Steam returned invalid refresh token or it's schema was unsupported. See inner exception for more details",
LoginError.UndefinedError, ex);
}
var data = new Dictionary<string, string>
{
{"nonce", pollResp.RefreshToken},
{"sessionid", sessionId}
};
var finalize = await client.PostAsync("https://login.steampowered.com/jwt/finalizelogin",
new FormUrlEncodedContent(data), cancellationToken);
var finalizeContent = await finalize.EnsureSuccessStatusCode().Content.ReadAsStringAsync(cancellationToken);
var finalizeResp =
SteamLibErrorMonitor.HandleResponse(finalizeContent,
() => JsonConvert.DeserializeObject<FinalizeLoginJson>(finalizeContent)!);
List<SteamAuthToken> tokens = new();
foreach (var transferInfo in finalizeResp.TransferInfo)
{
var transferData = new Dictionary<string, string>
{
{"nonce", transferInfo.TransferInfoParams.Nonce},
{"auth", transferInfo.TransferInfoParams.Auth},
{"steamID", steamId.ToString()}
};
try
{
var req = new HttpRequestMessage(HttpMethod.Post, transferInfo.Url);
req.Content = new FormUrlEncodedContent(transferData);
req.Headers.Referrer = SteamDomains.GetDomainUri(SteamDomain.Store);
var transferResp = await client.SendAsync(req, cancellationToken);
var transferContent = await transferResp.ReadAsStringEnsureSuccessAsync(cancellationToken);
var status = JObject.Parse(transferContent);
var result = status["result"]?.Value<int>();
if (result != null)
SteamStatusCode
.ValidateSuccessOrThrow(result
.Value); //TODO: Fix steam.tv token transfer (result always 8). But who really cares..
var tokenStr = SteamTokenHelper.ExtractJwtFromSetCookiesHeader(transferResp.Headers);
var token = SteamTokenHelper.Parse(tokenStr);
tokens.Add(token);
}
catch (Exception ex)
{
executor.Logger?.Log(LogLevel.Warning, ex, "Can't transfer tokens for URI: {uri}", transferInfo.Url);
}
}
var accessToken = SteamTokenHelper.Parse(pollResp.AccessToken);
if (accessToken.Type == SteamAccessTokenType.Mobile)
{
return new MobileSessionData(sessionId, SteamId.FromSteam64(steamId), refreshToken, accessToken, tokens);
}
return new SessionData(sessionId, SteamId.FromSteam64(steamId), refreshToken, tokens);
}
private static async Task UpdateWithCode(LoginV2Executor executor, ulong clientId, ulong steamId,
EAuthSessionGuardType guardType)
{
string? code;
if (guardType == EAuthSessionGuardType.DeviceCode)
{
if (executor.SteamGuardProvider != null)
code = await executor.SteamGuardProvider.GetSteamGuardCode(executor.Caller);
else
{
throw new LoginException(LoginError.SteamGuardRequired);
}
}
else
{
var t = executor.EmailAuthProvider?.GetEmailAuthCode(executor.Caller) ??
throw new LoginException(LoginError.EmailAuthRequired);
code = await t;
}
var updateCodeMsg = new UpdateAuthSessionWithSteamGuardCode_Request
{
ClientId = clientId,
Code = code,
CodeType = guardType,
Steamid = steamId
};
await executor.HttpClient.PostProtoEnsureSuccess(
new Uri("https://api.steampowered.com/IAuthenticationService/UpdateAuthSessionWithSteamGuardCode/v1"),
updateCodeMsg);
}
}
-757
View File
@@ -1,757 +0,0 @@
// ReSharper disable All
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace SteamLib.Core.Enums;
public enum Country
{
Afghanistan,
Aland_Islands,
Albania,
Algeria,
American_Samoa,
Andorra,
Angola,
Anguilla,
Antigua_and_Barbuda,
Argentina,
Armenia,
Aruba,
Australia,
Austria,
Azerbaijan,
Bahamas,
Bahrain,
Bangladesh,
Barbados,
Belarus,
Belgium,
Belize,
Bhutan,
Bolivia,
Bosnia_and_Herzegovina,
Botswana,
Brazil,
Brunei_Darussalam,
Bulgaria,
Burundi,
Cambodia,
Cameroon,
Canada,
Cape_Verde,
Cayman_Islands,
Chad,
Chile,
China,
Colombia,
Congo,
Costa_Rica,
Cote_d_Ivoire,
Croatia,
Cyprus,
Czech_Republic,
Denmark,
Dominica,
Dominican_Republic,
Ecuador,
Egypt,
El_Salvador,
Estonia,
Eswatini,
Ethiopia,
Faroe_Islands,
Finland,
France,
French_Guiana,
French_Polynesia,
Gabon,
Georgia,
Germany,
Ghana,
Gibraltar,
Greece,
Greenland,
Grenada,
Guadeloupe,
Guam,
Guatemala,
Guernsey,
Guyana,
Haiti,
Honduras,
Hong_Kong,
Hungary,
Iceland,
India,
Indonesia,
Iran,
Iraq,
Ireland,
Isle_of_Man,
Israel,
Italy,
Jamaica,
Japan,
Jersey,
Jordan,
Kazakhstan,
Kenya,
Korea,
Kuwait,
Kyrgyzstan,
Laos,
Latvia,
Lebanon,
Libya,
Liechtenstein,
Lithuania,
Luxembourg,
Macau,
Macedonia,
Madagascar,
Malaysia,
Maldives,
Mali,
Malta,
Martinique,
Mauritius,
Mayotte,
Mexico,
Moldova,
Monaco,
Mongolia,
Montenegro,
Morocco,
Mozambique,
Myanmar,
Namibia,
Nepal,
Netherlands,
New_Zealand,
Nicaragua,
Nigeria,
Northern_Mariana_Islands,
Norway,
Oman,
Pakistan,
Palestinian_Territory,
Panama,
Papua_New_Guinea,
Paraguay,
Peru,
Philippines,
Poland,
Portugal,
Puerto_Rico,
Qatar,
Reunion,
Romania,
Russian_Federation,
Rwanda,
Saint_Kitts_and_Nevis,
Saint_Lucia,
Saint_Vincent_and_the_Grenadines,
Saudi_Arabia,
Senegal,
Serbia,
Seychelles,
Singapore,
Slovakia,
Slovenia,
South_Africa,
Spain,
Sri_Lanka,
Sudan,
Suriname,
Sweden,
Switzerland,
Taiwan,
Tajikistan,
Tanzania,
Thailand,
Togo,
Trinidad_and_Tobago,
Tunisia,
Turkey,
Turkmenistan,
Turks_and_Caicos_Islands,
Uganda,
Ukraine,
United_Arab_Emirates,
United_Kingdom,
United_States,
Uruguay,
Uzbekistan,
Venezuela,
Viet_Nam,
Virgin_Islands,
Yemen,
Zambia,
Zimbabwe
}
public static class CountryResolver
{
public static IReadOnlyDictionary<string, Country> ResolveByCountryCode = new ReadOnlyDictionary<string, Country>(
new Dictionary<string, Country>()
{
{"US", Country.United_States},
{"CA", Country.Canada},
{"AF", Country.Afghanistan},
{"AX", Country.Aland_Islands},
{"AL", Country.Albania},
{"DZ", Country.Algeria},
{"AS", Country.American_Samoa},
{"AD", Country.Andorra},
{"AO", Country.Angola},
{"AI", Country.Anguilla},
{"AG", Country.Antigua_and_Barbuda},
{"AR", Country.Argentina},
{"AM", Country.Armenia},
{"AW", Country.Aruba},
{"AU", Country.Australia},
{"AT", Country.Austria},
{"AZ", Country.Azerbaijan},
{"BS", Country.Bahamas},
{"BH", Country.Bahrain},
{"BD", Country.Bangladesh},
{"BB", Country.Barbados},
{"BY", Country.Belarus},
{"BE", Country.Belgium},
{"BZ", Country.Belize},
{"BT", Country.Bhutan},
{"BO", Country.Bolivia},
{"BA", Country.Bosnia_and_Herzegovina},
{"BW", Country.Botswana},
{"BR", Country.Brazil},
{"BN", Country.Brunei_Darussalam},
{"BG", Country.Bulgaria},
{"BI", Country.Burundi},
{"KH", Country.Cambodia},
{"CM", Country.Cameroon},
{"CV", Country.Cape_Verde},
{"KY", Country.Cayman_Islands},
{"TD", Country.Chad},
{"CL", Country.Chile},
{"CN", Country.China},
{"CO", Country.Colombia},
{"CD", Country.Congo},
{"CR", Country.Costa_Rica},
{"CI", Country.Cote_d_Ivoire},
{"HR", Country.Croatia},
{"CY", Country.Cyprus},
{"CZ", Country.Czech_Republic},
{"DK", Country.Denmark},
{"DM", Country.Dominica},
{"DO", Country.Dominican_Republic},
{"EC", Country.Ecuador},
{"EG", Country.Egypt},
{"SV", Country.El_Salvador},
{"EE", Country.Estonia},
{"ET", Country.Ethiopia},
{"FO", Country.Faroe_Islands},
{"FI", Country.Finland},
{"FR", Country.France},
{"GF", Country.French_Guiana},
{"PF", Country.French_Polynesia},
{"GA", Country.Gabon},
{"GE", Country.Georgia},
{"DE", Country.Germany},
{"GH", Country.Ghana},
{"GI", Country.Gibraltar},
{"GR", Country.Greece},
{"GL", Country.Greenland},
{"GD", Country.Grenada},
{"GP", Country.Guadeloupe},
{"GU", Country.Guam},
{"GT", Country.Guatemala},
{"GG", Country.Guernsey},
{"GY", Country.Guyana},
{"HT", Country.Haiti},
{"HN", Country.Honduras},
{"HK", Country.Hong_Kong},
{"HU", Country.Hungary},
{"IS", Country.Iceland},
{"IN", Country.India},
{"ID", Country.Indonesia},
{"IQ", Country.Iraq},
{"IE", Country.Ireland},
{"IR", Country.Iran},
{"IM", Country.Isle_of_Man},
{"IL", Country.Israel},
{"IT", Country.Italy},
{"JM", Country.Jamaica},
{"JP", Country.Japan},
{"JE", Country.Jersey},
{"JO", Country.Jordan},
{"KZ", Country.Kazakhstan},
{"KE", Country.Kenya},
{"KR", Country.Korea},
{"KW", Country.Kuwait},
{"KG", Country.Kyrgyzstan},
{"LA", Country.Laos},
{"LV", Country.Latvia},
{"LB", Country.Lebanon},
{"LY", Country.Libya},
{"LI", Country.Liechtenstein},
{"LT", Country.Lithuania},
{"LU", Country.Luxembourg},
{"MO", Country.Macau},
{"MK", Country.Macedonia},
{"MG", Country.Madagascar},
{"MY", Country.Malaysia},
{"MV", Country.Maldives},
{"ML", Country.Mali},
{"MT", Country.Malta},
{"MQ", Country.Martinique},
{"MU", Country.Mauritius},
{"YT", Country.Mayotte},
{"MX", Country.Mexico},
{"MD", Country.Moldova},
{"MC", Country.Monaco},
{"MN", Country.Mongolia},
{"ME", Country.Montenegro},
{"MA", Country.Morocco},
{"MZ", Country.Mozambique},
{"MM", Country.Myanmar},
{"NA", Country.Namibia},
{"NP", Country.Nepal},
{"NL", Country.Netherlands},
{"NZ", Country.New_Zealand},
{"NI", Country.Nicaragua},
{"NG", Country.Nigeria},
{"MP", Country.Northern_Mariana_Islands},
{"NO", Country.Norway},
{"OM", Country.Oman},
{"PK", Country.Pakistan},
{"PS", Country.Palestinian_Territory},
{"PA", Country.Panama},
{"PG", Country.Papua_New_Guinea},
{"PY", Country.Paraguay},
{"PE", Country.Peru},
{"PH", Country.Philippines},
{"PL", Country.Poland},
{"PT", Country.Portugal},
{"PR", Country.Puerto_Rico},
{"QA", Country.Qatar},
{"RE", Country.Reunion},
{"RO", Country.Romania},
{"RU", Country.Russian_Federation},
{"RW", Country.Rwanda},
{"LC", Country.Saint_Lucia},
{"SA", Country.Saudi_Arabia},
{"SN", Country.Senegal},
{"RS", Country.Serbia},
{"SC", Country.Seychelles},
{"SG", Country.Singapore},
{"SK", Country.Slovakia},
{"SI", Country.Slovenia},
{"ZA", Country.South_Africa},
{"ES", Country.Spain},
{"LK", Country.Sri_Lanka},
{"KN", Country.Saint_Kitts_and_Nevis},
{"PM", Country.Saint_Vincent_and_the_Grenadines},
{"SD", Country.Sudan},
{"SR", Country.Suriname},
{"SE", Country.Sweden},
{"CH", Country.Switzerland},
{"TW", Country.Taiwan},
{"TJ", Country.Tajikistan},
{"TZ", Country.Tanzania},
{"TH", Country.Thailand},
{"TG", Country.Togo},
{"TT", Country.Trinidad_and_Tobago},
{"TN", Country.Tunisia},
{"TR", Country.Turkey},
{"TM", Country.Turkmenistan},
{"TC", Country.Turks_and_Caicos_Islands},
{"UG", Country.Uganda},
{"UA", Country.Ukraine},
{"GB", Country.United_Kingdom},
{"VI", Country.Virgin_Islands},
{"AE", Country.United_Arab_Emirates},
{"UY", Country.Uruguay},
{"UZ", Country.Uzbekistan},
{"VE", Country.Venezuela},
{"VN", Country.Viet_Nam},
{"YE", Country.Yemen},
{"ZM", Country.Zambia},
{"ZW", Country.Zimbabwe}
});
public static IReadOnlyDictionary<Country, string> ResolveString { get; } = new Dictionary<Country, string>
{
{Country.Afghanistan, "Afghanistan"},
{Country.Aland_Islands, "Aland Islands"},
{Country.Albania, "Albania"},
{Country.Algeria, "Algeria"},
{Country.American_Samoa, "American Samoa"},
{Country.Andorra, "Andorra"},
{Country.Angola, "Angola"},
{Country.Anguilla, "Anguilla"},
{Country.Antigua_and_Barbuda, "Antigua and Barbuda"},
{Country.Argentina, "Argentina"},
{Country.Armenia, "Armenia"},
{Country.Aruba, "Aruba"},
{Country.Australia, "Australia"},
{Country.Austria, "Austria"},
{Country.Azerbaijan, "Azerbaijan"},
{Country.Bahamas, "Bahamas"},
{Country.Bahrain, "Bahrain"},
{Country.Bangladesh, "Bangladesh"},
{Country.Barbados, "Barbados"},
{Country.Belarus, "Belarus"},
{Country.Belgium, "Belgium"},
{Country.Belize, "Belize"},
{Country.Bhutan, "Bhutan"},
{Country.Bolivia, "Bolivia"},
{Country.Bosnia_and_Herzegovina, "Bosnia and Herzegovina"},
{Country.Botswana, "Botswana"},
{Country.Brazil, "Brazil"},
{Country.Brunei_Darussalam, "Brunei Darussalam"},
{Country.Bulgaria, "Bulgaria"},
{Country.Burundi, "Burundi"},
{Country.Cambodia, "Cambodia"},
{Country.Cameroon, "Cameroon"},
{Country.Canada, "Canada"},
{Country.Cape_Verde, "Cape Verde"},
{Country.Cayman_Islands, "Cayman Islands"},
{Country.Chad, "Chad"},
{Country.Chile, "Chile"},
{Country.China, "China"},
{Country.Colombia, "Colombia"},
{Country.Congo, "Congo, the Democratic Republic of the"},
{Country.Costa_Rica, "Costa Rica"},
{Country.Cote_d_Ivoire, "Cote d'Ivoire"},
{Country.Croatia, "Croatia"},
{Country.Cyprus, "Cyprus"},
{Country.Czech_Republic, "Czech Republic"},
{Country.Denmark, "Denmark"},
{Country.Dominica, "Dominica"},
{Country.Dominican_Republic, "Dominican Republic"},
{Country.Ecuador, "Ecuador"},
{Country.Egypt, "Egypt"},
{Country.El_Salvador, "El Salvador"},
{Country.Estonia, "Estonia"},
{Country.Eswatini, "Eswatini"},
{Country.Ethiopia, "Ethiopia"},
{Country.Faroe_Islands, "Faroe Islands"},
{Country.Finland, "Finland"},
{Country.France, "France"},
{Country.French_Guiana, "French Guiana"},
{Country.French_Polynesia, "French Polynesia"},
{Country.Gabon, "Gabon"},
{Country.Georgia, "Georgia"},
{Country.Germany, "Germany"},
{Country.Ghana, "Ghana"},
{Country.Gibraltar, "Gibraltar"},
{Country.Greece, "Greece"},
{Country.Greenland, "Greenland"},
{Country.Grenada, "Grenada"},
{Country.Guadeloupe, "Guadeloupe"},
{Country.Guam, "Guam"},
{Country.Guatemala, "Guatemala"},
{Country.Guernsey, "Guernsey"},
{Country.Guyana, "Guyana"},
{Country.Haiti, "Haiti"},
{Country.Honduras, "Honduras"},
{Country.Hong_Kong, "Hong Kong"},
{Country.Hungary, "Hungary"},
{Country.Iceland, "Iceland"},
{Country.India, "India"},
{Country.Indonesia, "Indonesia"},
{Country.Iran, "Iran"},
{Country.Iraq, "Iraq"},
{Country.Ireland, "Ireland"},
{Country.Isle_of_Man, "Isle of Man"},
{Country.Israel, "Israel"},
{Country.Italy, "Italy"},
{Country.Jamaica, "Jamaica"},
{Country.Japan, "Japan"},
{Country.Jersey, "Jersey"},
{Country.Jordan, "Jordan"},
{Country.Kazakhstan, "Kazakhstan"},
{Country.Kenya, "Kenya"},
{Country.Korea, "Korea, Republic of"},
{Country.Kuwait, "Kuwait"},
{Country.Kyrgyzstan, "Kyrgyzstan"},
{Country.Laos, "Lao People's Democratic Republic"},
{Country.Latvia, "Latvia"},
{Country.Lebanon, "Lebanon"},
{Country.Libya, "Libya"},
{Country.Liechtenstein, "Liechtenstein"},
{Country.Lithuania, "Lithuania"},
{Country.Luxembourg, "Luxembourg"},
{Country.Macau, "Macau"},
{Country.Macedonia, "Macedonia, the former Yugoslav Republic of"},
{Country.Madagascar, "Madagascar"},
{Country.Malaysia, "Malaysia"},
{Country.Maldives, "Maldives"},
{Country.Mali, "Mali"},
{Country.Malta, "Malta"},
{Country.Martinique, "Martinique"},
{Country.Mauritius, "Mauritius"},
{Country.Mayotte, "Mayotte"},
{Country.Mexico, "Mexico"},
{Country.Moldova, "Moldova, Republic of"},
{Country.Monaco, "Monaco"},
{Country.Mongolia, "Mongolia"},
{Country.Montenegro, "Montenegro"},
{Country.Morocco, "Morocco"},
{Country.Mozambique, "Mozambique"},
{Country.Myanmar, "Myanmar"},
{Country.Namibia, "Namibia"},
{Country.Nepal, "Nepal"},
{Country.Netherlands, "Netherlands"},
{Country.New_Zealand, "New Zealand"},
{Country.Nicaragua, "Nicaragua"},
{Country.Nigeria, "Nigeria"},
{Country.Northern_Mariana_Islands, "Northern Mariana Islands"},
{Country.Norway, "Norway"},
{Country.Oman, "Oman"},
{Country.Pakistan, "Pakistan"},
{Country.Palestinian_Territory, "Palestinian Territory, Occupied"},
{Country.Panama, "Panama"},
{Country.Papua_New_Guinea, "Papua New Guinea"},
{Country.Paraguay, "Paraguay"},
{Country.Peru, "Peru"},
{Country.Philippines, "Philippines"},
{Country.Poland, "Poland"},
{Country.Portugal, "Portugal"},
{Country.Puerto_Rico, "Puerto Rico"},
{Country.Qatar, "Qatar"},
{Country.Reunion, "Reunion"},
{Country.Romania, "Romania"},
{Country.Russian_Federation, "Russian Federation"},
{Country.Rwanda, "Rwanda"},
{Country.Saint_Kitts_and_Nevis, "Saint Kitts and Nevis"},
{Country.Saint_Lucia, "Saint Lucia"},
{Country.Saint_Vincent_and_the_Grenadines, "Saint Vincent and the Grenadines"},
{Country.Saudi_Arabia, "Saudi Arabia"},
{Country.Senegal, "Senegal"},
{Country.Serbia, "Serbia"},
{Country.Seychelles, "Seychelles"},
{Country.Singapore, "Singapore"},
{Country.Slovakia, "Slovakia"},
{Country.Slovenia, "Slovenia"},
{Country.South_Africa, "South Africa"},
{Country.Spain, "Spain"},
{Country.Sri_Lanka, "Sri Lanka"},
{Country.Sudan, "Sudan"},
{Country.Suriname, "Suriname"},
{Country.Sweden, "Sweden"},
{Country.Switzerland, "Switzerland"},
{Country.Taiwan, "Taiwan"},
{Country.Tajikistan, "Tajikistan"},
{Country.Tanzania, "Tanzania, United Republic of"},
{Country.Thailand, "Thailand"},
{Country.Togo, "Togo"},
{Country.Trinidad_and_Tobago, "Trinidad and Tobago"},
{Country.Tunisia, "Tunisia"},
{Country.Turkey, "Turkey"},
{Country.Turkmenistan, "Turkmenistan"},
{Country.Turks_and_Caicos_Islands, "Turks and Caicos Islands"},
{Country.Uganda, "Uganda"},
{Country.Ukraine, "Ukraine"},
{Country.United_Arab_Emirates, "United Arab Emirates"},
{Country.United_Kingdom, "United Kingdom"},
{Country.United_States, "United States"},
{Country.Uruguay, "Uruguay"},
{Country.Uzbekistan, "Uzbekistan"},
{Country.Venezuela, "Venezuela"},
{Country.Viet_Nam, "Viet Nam"},
{Country.Virgin_Islands, "Virgin Islands, U.S."},
{Country.Yemen, "Yemen"},
{Country.Zambia, "Zambia"},
{Country.Zimbabwe, "Zimbabwe"}
};
public static IReadOnlyDictionary<string, Country> ResolveCountry { get; } = new Dictionary<string, Country>()
{
{"Afghanistan", Country.Afghanistan},
{"Aland Islands", Country.Aland_Islands},
{"Albania", Country.Albania},
{"Algeria", Country.Algeria},
{"American Samoa", Country.American_Samoa},
{"Andorra", Country.Andorra},
{"Angola", Country.Angola},
{"Anguilla", Country.Anguilla},
{"Antigua and Barbuda", Country.Antigua_and_Barbuda},
{"Argentina", Country.Argentina},
{"Armenia", Country.Armenia},
{"Aruba", Country.Aruba},
{"Australia", Country.Australia},
{"Austria", Country.Austria},
{"Azerbaijan", Country.Azerbaijan},
{"Bahamas", Country.Bahamas},
{"Bahrain", Country.Bahrain},
{"Bangladesh", Country.Bangladesh},
{"Barbados", Country.Barbados},
{"Belarus", Country.Belarus},
{"Belgium", Country.Belgium},
{"Belize", Country.Belize},
{"Bhutan", Country.Bhutan},
{"Bolivia", Country.Bolivia},
{"Bosnia and Herzegovina", Country.Bosnia_and_Herzegovina},
{"Botswana", Country.Botswana},
{"Brazil", Country.Brazil},
{"Brunei Darussalam", Country.Brunei_Darussalam},
{"Bulgaria", Country.Bulgaria},
{"Burundi", Country.Burundi},
{"Cambodia", Country.Cambodia},
{"Cameroon", Country.Cameroon},
{"Canada", Country.Canada},
{"Cape Verde", Country.Cape_Verde},
{"Cayman Islands", Country.Cayman_Islands},
{"Chad", Country.Chad},
{"Chile", Country.Chile},
{"China", Country.China},
{"Colombia", Country.Colombia},
{"Congo, the Democratic Republic of the", Country.Congo},
{"Costa Rica", Country.Costa_Rica},
{"Cote d'Ivoire", Country.Cote_d_Ivoire},
{"Croatia", Country.Croatia},
{"Cyprus", Country.Cyprus},
{"Czech Republic", Country.Czech_Republic},
{"Denmark", Country.Denmark},
{"Dominica", Country.Dominica},
{"Dominican Republic", Country.Dominican_Republic},
{"Ecuador", Country.Ecuador},
{"Egypt", Country.Egypt},
{"El Salvador", Country.El_Salvador},
{"Estonia", Country.Estonia},
{"Eswatini", Country.Eswatini},
{"Ethiopia", Country.Ethiopia},
{"Faroe Islands", Country.Faroe_Islands},
{"Finland", Country.Finland},
{"France", Country.France},
{"French Guiana", Country.French_Guiana},
{"French Polynesia", Country.French_Polynesia},
{"Gabon", Country.Gabon},
{"Georgia", Country.Georgia},
{"Germany", Country.Germany},
{"Ghana", Country.Ghana},
{"Gibraltar", Country.Gibraltar},
{"Greece", Country.Greece},
{"Greenland", Country.Greenland},
{"Grenada", Country.Grenada},
{"Guadeloupe", Country.Guadeloupe},
{"Guam", Country.Guam},
{"Guatemala", Country.Guatemala},
{"Guernsey", Country.Guernsey},
{"Guyana", Country.Guyana},
{"Haiti", Country.Haiti},
{"Honduras", Country.Honduras},
{"Hong Kong", Country.Hong_Kong},
{"Hungary", Country.Hungary},
{"Iceland", Country.Iceland},
{"India", Country.India},
{"Indonesia", Country.Indonesia},
{"Iran", Country.Iran},
{"Iraq", Country.Iraq},
{"Ireland", Country.Ireland},
{"Isle of Man", Country.Isle_of_Man},
{"Israel", Country.Israel},
{"Italy", Country.Italy},
{"Jamaica", Country.Jamaica},
{"Japan", Country.Japan},
{"Jersey", Country.Jersey},
{"Jordan", Country.Jordan},
{"Kazakhstan", Country.Kazakhstan},
{"Kenya", Country.Kenya},
{"Korea, Republic of", Country.Korea},
{"Kuwait", Country.Kuwait},
{"Kyrgyzstan", Country.Kyrgyzstan},
{"Lao People's Democratic Republic", Country.Laos},
{"Latvia", Country.Latvia},
{"Lebanon", Country.Lebanon},
{"Libya", Country.Libya},
{"Liechtenstein", Country.Liechtenstein},
{"Lithuania", Country.Lithuania},
{"Luxembourg", Country.Luxembourg},
{"Macau", Country.Macau},
{"Macedonia, the former Yugoslav Republic of", Country.Macedonia},
{"Madagascar", Country.Madagascar},
{"Malaysia", Country.Malaysia},
{"Maldives", Country.Maldives},
{"Mali", Country.Mali},
{"Malta", Country.Malta},
{"Martinique", Country.Martinique},
{"Mauritius", Country.Mauritius},
{"Mayotte", Country.Mayotte},
{"Mexico", Country.Mexico},
{"Moldova, Republic of", Country.Moldova},
{"Monaco", Country.Monaco},
{"Mongolia", Country.Mongolia},
{"Montenegro", Country.Montenegro},
{"Morocco", Country.Morocco},
{"Mozambique", Country.Mozambique},
{"Myanmar", Country.Myanmar},
{"Namibia", Country.Namibia},
{"Nepal", Country.Nepal},
{"Netherlands", Country.Netherlands},
{"New Zealand", Country.New_Zealand},
{"Nicaragua", Country.Nicaragua},
{"Nigeria", Country.Nigeria},
{"Northern Mariana Islands", Country.Northern_Mariana_Islands},
{"Norway", Country.Norway},
{"Oman", Country.Oman},
{"Pakistan", Country.Pakistan},
{"Palestinian Territory, Occupied", Country.Palestinian_Territory},
{"Panama", Country.Panama},
{"Papua New Guinea", Country.Papua_New_Guinea},
{"Paraguay", Country.Paraguay},
{"Peru", Country.Peru},
{"Philippines", Country.Philippines},
{"Poland", Country.Poland},
{"Portugal", Country.Portugal},
{"Puerto Rico", Country.Puerto_Rico},
{"Qatar", Country.Qatar},
{"Reunion", Country.Reunion},
{"Romania", Country.Romania},
{"Russian Federation", Country.Russian_Federation},
{"Rwanda", Country.Rwanda},
{"Saint Kitts and Nevis", Country.Saint_Kitts_and_Nevis},
{"Saint Lucia", Country.Saint_Lucia},
{"Saint Vincent and the Grenadines", Country.Saint_Vincent_and_the_Grenadines},
{"Saudi Arabia", Country.Saudi_Arabia},
{"Senegal", Country.Senegal},
{"Serbia", Country.Serbia},
{"Seychelles", Country.Seychelles},
{"Singapore", Country.Singapore},
{"Slovakia", Country.Slovakia},
{"Slovenia", Country.Slovenia},
{"South Africa", Country.South_Africa},
{"Spain", Country.Spain},
{"Sri Lanka", Country.Sri_Lanka},
{"Sudan", Country.Sudan},
{"Suriname", Country.Suriname},
{"Sweden", Country.Sweden},
{"Switzerland", Country.Switzerland},
{"Taiwan", Country.Taiwan},
{"Tajikistan", Country.Tajikistan},
{"Tanzania, United Republic of", Country.Tanzania},
{"Thailand", Country.Thailand},
{"Togo", Country.Togo},
{"Trinidad and Tobago", Country.Trinidad_and_Tobago},
{"Tunisia", Country.Tunisia},
{"Turkey", Country.Turkey},
{"Turkmenistan", Country.Turkmenistan},
{"Turks and Caicos Islands", Country.Turks_and_Caicos_Islands},
{"Uganda", Country.Uganda},
{"Ukraine", Country.Ukraine},
{"United Arab Emirates", Country.United_Arab_Emirates},
{"United Kingdom", Country.United_Kingdom},
{"United States", Country.United_States},
{"Uruguay", Country.Uruguay},
{"Uzbekistan", Country.Uzbekistan},
{"Venezuela", Country.Venezuela},
{"Viet Nam", Country.Viet_Nam},
{"Virgin Islands, U.S.", Country.Virgin_Islands},
{"Yemen", Country.Yemen},
{"Zambia", Country.Zambia},
{"Zimbabwe", Country.Zimbabwe}
};
}
-103
View File
@@ -1,103 +0,0 @@
// ReSharper disable InconsistentNaming
namespace SteamLib.Core.Enums;
public enum Currency
{
None = 0,
USD = 1,
GBP = 2,
EUR = 3,
CHF = 4,
RUB = 5,
PLN = 6,
BRL = 7,
JPY = 8,
NOK = 9,
IDR = 10,
MYR = 11,
PHP = 12,
SGD = 13,
THB = 14,
VND = 15,
KRW = 16,
TRY = 17,
UAH = 18,
MXN = 19,
CAD = 20,
AUD = 21,
NZD = 22,
CNY = 23,
INR = 24,
CLP = 25,
PEN = 26,
COP = 27,
ZAR = 28,
HKD = 29,
TWD = 30,
SAR = 31,
AED = 32,
ARS = 34,
ILS = 35,
KZT = 37,
KWD = 38,
QAR = 39,
CRC = 40,
UYU = 41
}
public static class CurrencyInfo
{
public static IReadOnlyDictionary<Currency, string> CurrencySymbols { get; } = new Dictionary<Currency, string>
{
{Currency.USD, "$"},
{Currency.GBP, "£"},
{Currency.EUR, "€"},
{Currency.CHF, "CHF"},
{Currency.RUB, "pуб."},
{Currency.PLN, "zł"},
{Currency.BRL, "R$"},
{Currency.JPY, "¥"},
{Currency.NOK, "kr"},
{Currency.IDR, "Rp"},
{Currency.MYR, "RM"},
{Currency.PHP, "P"},
{Currency.SGD, "S$"},
{Currency.THB, "฿"},
{Currency.VND, "₫"},
{Currency.KRW, "₩"},
{Currency.TRY, "TL"},
{Currency.UAH, "₴"},
{Currency.MXN, "Mex$"},
{Currency.CAD, "CDN$"},
{Currency.AUD, "A$"},
{Currency.NZD, "NZ$"},
{Currency.CNY, "¥"},
{Currency.INR, "₹"},
{Currency.CLP, "CLP$"},
{Currency.PEN, "S/."},
{Currency.COP, "COL$"},
{Currency.ZAR, "R"},
{Currency.HKD, "HK$"},
{Currency.TWD, "NT$"},
{Currency.SAR, "SR"},
{Currency.AED, "AED"},
{Currency.ARS, "ARS$"},
{Currency.ILS, "₪"},
{Currency.KZT, "₸"},
{Currency.KWD, "KD"},
{Currency.QAR, "QR"},
{Currency.CRC, "₡"},
{Currency.UYU, "$U"}
};
public static int ToInt(this Currency currency)
{
return (int) currency;
}
public static string ToIntString(this Currency currency)
{
return currency.ToInt().ToString();
}
}
-11
View File
@@ -1,11 +0,0 @@
namespace SteamLib.Core.Enums;
public enum SteamDomain
{
Undefined = 0,
Community = 1,
Store = 2,
Help = 3,
TV = 4,
Checkout = 5
}
@@ -1,6 +0,0 @@
namespace SteamLib.Core.Interfaces;
public interface ICaptchaResolver
{
public Task<string> Resolve(Uri imageUrl, HttpClient client);
}
@@ -1,15 +0,0 @@
namespace SteamLib.Core.Interfaces;
public enum EmailConfirmationType
{
AttachPhoneAuthenticator
}
public interface IEmailProvider
{
public int MaxRetryCount { get; }
public Task<string> GetEmailAuthCode(ILoginConsumer caller);
public Task<string> GetAddAuthenticatorCode(ILoginConsumer caller);
public Task ConfirmEmailLink(ILoginConsumer caller, EmailConfirmationType confirmationType);
}
@@ -1,6 +0,0 @@
namespace SteamLib.Core.Interfaces;
public interface ILoginConsumer
{
public string FriendlyName { get; }
}
@@ -1,7 +0,0 @@
namespace SteamLib.Core.Interfaces;
public interface ISteamGuardProvider
{
public int MaxRetryCount { get; }
public ValueTask<string> GetSteamGuardCode(ILoginConsumer caller);
}
@@ -1,174 +0,0 @@
using System.Collections.ObjectModel;
using AchiesUtilities.Models;
using SteamLib.Exceptions;
using SteamLib.Utility;
namespace SteamLib.Core.StatusCodes;
public partial class SteamStatusCode : Enumeration
{
public static IReadOnlyDictionary<int, SteamStatusCode> StatusCodes { get; }
static SteamStatusCode()
{
StatusCodes =
new ReadOnlyDictionary<int, SteamStatusCode>(GetAll<SteamStatusCode>()
.ToDictionary(ssc => ssc.Id, ssc => ssc));
}
protected SteamStatusCode(int id, string name) : base(id, name)
{
}
/// <summary>
/// Tries to translate status code. If no status code was found <see cref="SteamStatusCodeException" /> with
/// <see cref="Undefined" /> will be thrown
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="response"></param>
/// <param name="isUniversal"></param>
/// <returns>
/// <see cref="SteamStatusCode" /> if value is universal. <typeparamref name="T" /> if value is
/// <typeparamref name="T" /> specific
/// </returns>
/// <exception cref="SteamStatusCodeException"></exception>
public static SteamStatusCode Translate<T>(string response, out bool isUniversal) where T : SteamStatusCode
{
int statusCode;
try
{
statusCode = Utilities.GetSuccessCode(response);
}
catch
{
throw new SteamStatusCodeException(Undefined, response);
}
return Translate<T>(statusCode, out isUniversal);
}
/// <summary>
/// Translate <paramref name="statusCode" /> to specific <see cref="T" /> or <see cref="SteamStatusCode" />
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="statusCode"></param>
/// <param name="isUniversal"></param>
/// <returns>
/// <see cref="SteamStatusCode" /> if value is universal. <typeparamref name="T" /> if value is
/// <typeparamref name="T" /> specific
/// </returns>
/// <exception cref="SteamStatusCodeException"></exception>
public static SteamStatusCode Translate<T>(int statusCode, out bool isUniversal) where T : SteamStatusCode
{
if (statusCode < 2)
{
var status = StatusCodes[statusCode];
isUniversal = true;
return status;
}
var allSpecific = GetAll<T>();
var translated = allSpecific.FirstOrDefault(s => s.Id == statusCode);
isUniversal = translated == null;
if (translated != null)
{
return translated;
}
if (translated == null && StatusCodes.TryGetValue(statusCode, out var universal))
{
return universal;
}
return translated ?? new SteamStatusCode(statusCode, nameof(Unknown));
}
public override bool Equals(object? obj)
{
if (obj is not SteamStatusCode translated)
return false;
if (Id < 2)
{
return translated.Id == Id;
}
return base.Equals(translated);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
/// <summary>
/// </summary>
/// <param name="response"></param>
/// <exception cref="SteamStatusCodeException"></exception>
public static void ValidateSuccessOrThrow<T>(string response) where T : SteamStatusCode
{
var translated = Translate<T>(response, out _);
if (translated.Id != 1)
throw new SteamStatusCodeException(translated, response);
}
/// <summary>
/// Same as <see cref="ValidateSuccessOrThrow{T}(string)" /> with <see cref="SteamStatusCode" /> generic parameter
/// <br />
/// <b>(Used in case when steam status code is not defined for this operation)</b>
/// </summary>
/// <param name="response"></param>
/// <exception cref="SteamStatusCodeException"></exception>
public static void ValidateSuccessOrThrow(string response)
{
ValidateSuccessOrThrow<SteamStatusCode>(response);
}
/// <summary>
/// </summary>
/// <param name="statusCode"></param>
/// <exception cref="SteamStatusCodeException"></exception>
public static void ValidateSuccessOrThrow<T>(int statusCode) where T : SteamStatusCode
{
if (statusCode == 1) return;
var translated = Translate<T>(statusCode, out _);
throw new SteamStatusCodeException(translated, statusCode.ToString());
}
/// <summary>
/// Same as <see cref="ValidateSuccessOrThrow{T}(int)" /> with <see cref="SteamStatusCode" /> generic parameter <br />
/// <b>(Used in case when steam status code is not defined for this operation)</b>
/// </summary>
/// <param name="statusCode"></param>
/// <exception cref="SteamStatusCodeException"></exception>
public static void ValidateSuccessOrThrow(int statusCode)
{
ValidateSuccessOrThrow<SteamStatusCode>(statusCode);
}
/// <summary>
/// Validates that status code is <see cref="Ok" /> or <typeparamref name="T" /> specific
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="response"></param>
/// <param name="isUniversal"></param>
/// <exception cref="SteamStatusCodeException"></exception>
public static SteamStatusCode TranslateOrThrow<T>(string response, out bool isUniversal) where T : SteamStatusCode
{
var translated = Translate<T>(response, out isUniversal);
if (translated.Id < 1)
throw new SteamStatusCodeException(translated, response);
return translated;
}
public override string ToString()
{
return $"{Name} ({Id})";
}
}
@@ -1,51 +0,0 @@
using SteamLib.Core.Interfaces;
namespace SteamLib.Exceptions;
public class LoginException : Exception
{
public LoginError Error { get; }
public string? Response { get; }
public LoginException(LoginError error)
: base($"Login was unsuccessful. Error {error}")
{
Error = error;
}
public LoginException(string response)
{
Error = LoginError.UndefinedError;
Response = response;
}
public LoginException(string? response, string? message, LoginError? error, Exception? innerException) : base(
message, innerException)
{
Response = response;
Error = error ?? LoginError.UndefinedError;
}
}
public enum LoginError
{
CaptchaRequired,
InvalidCredentials,
InvalidEmailAuthCode,
InvalidTwoFactorCode,
/// <summary>
/// SteamEmail authentication is required to login but no <see cref="IEmailProvider" /> was provided
/// </summary>
EmailAuthRequired,
/// <summary>
/// SteamGuard is required to login but no <see cref="ISteamGuardProvider" /> was provided
/// </summary>
SteamGuardRequired,
/// <summary>
/// Some error occurred while trying to login.
/// </summary>
UndefinedError
}
@@ -1,24 +0,0 @@
using SteamLib.Core.StatusCodes;
namespace SteamLib.Exceptions;
public class SteamStatusCodeException : Exception
{
public SteamStatusCode StatusCode { get; }
public string? Response { get; }
public SteamStatusCodeException(SteamStatusCode statusCode, string? response, Exception? innerException = null)
: base($"Steam return not successful status code {statusCode}", innerException)
{
StatusCode = statusCode;
Response = response;
}
public SteamStatusCodeException(string message, SteamStatusCode statusCode, string? response = null,
Exception? innerException = null)
: base(message, innerException)
{
StatusCode = statusCode;
Response = response;
}
}
@@ -1,28 +0,0 @@
using SteamLib.ProtoCore.Enums;
namespace SteamLib.ProtoCore.Exceptions;
[Serializable]
public class EResultException : Exception
{
public EResult Result { get; }
public EResultException()
{
}
public EResultException(EResult result) : base("EResult error: " + result)
{
Result = result;
}
public EResultException(string message, EResult result, Exception inner) : base(message, inner)
{
Result = result;
}
public EResultException(EResult result, Exception inner) : base("EResult error: " + result, inner)
{
Result = result;
}
}
@@ -1,16 +0,0 @@
namespace SteamLib.ProtoCore.Exceptions;
[Serializable]
public class UnknownEResultException : Exception
{
public int EResult { get; }
public UnknownEResultException()
{
}
public UnknownEResultException(int eResult) : base("Got unknown EResult: " + eResult)
{
EResult = eResult;
}
}
-21
View File
@@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AchiesUtilities.Newtonsoft.JSON" Version="1.2.11" />
<PackageReference Include="AchiesUtilities.Web" Version="1.0.14" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.67" />
<PackageReference Include="JetBrains.Annotations" Version="2024.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="protobuf-net" Version="3.2.30" />
<PackageReference Include="protobuf-net.Core" Version="3.2.30" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.1.2" />
</ItemGroup>
</Project>
@@ -1,110 +0,0 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace SteamLib.Utility.MafileSerialization;
public partial class MafileSerializer //Write
{
private const string CREDITS_PROPERTY_NAME = "Credits";
public static string Serialize(MobileDataExtended mobileData, Formatting formatting = Formatting.Indented,
bool sign = true, MafileCredits? credits = null)
{
using var w = new StringWriter();
using var write = new JsonTextWriter(w);
write.Formatting = formatting;
var j = JObject.FromObject(mobileData);
j.Add(SIGNATURE_PROPERTY_NAME, MAFILE_VERSION);
if (sign)
{
credits ??= MafileCredits.Instance;
var obj = JObject.FromObject(credits);
j.Add(CREDITS_PROPERTY_NAME, obj);
}
j.WriteTo(write);
return w.ToString();
}
public static async Task<string> SerializeAsync(MobileDataExtended mobileData,
Formatting formatting = Formatting.Indented, bool sign = true, MafileCredits? credits = null)
{
await using var w = new StringWriter();
await using var write = new JsonTextWriter(w);
write.Formatting = formatting;
var j = JObject.FromObject(mobileData);
j.Add(SIGNATURE_PROPERTY_NAME, j);
if (sign)
{
credits ??= MafileCredits.Instance;
var obj = JObject.FromObject(credits);
j.Add(CREDITS_PROPERTY_NAME, obj);
}
await j.WriteToAsync(write);
return w.ToString();
}
public static string SerializeLegacy(MobileData mobileData, Formatting formatting,
Dictionary<string, object?>? additionalProperties = null, bool sign = true, MafileCredits? credits = null)
{
var result = new LegacyMafile
{
SharedSecret = mobileData.SharedSecret,
IdentitySecret = mobileData.IdentitySecret,
DeviceId = mobileData.DeviceId
};
if (mobileData is MobileDataExtended ext)
{
result.RevocationCode = ext.RevocationCode ?? string.Empty;
result.AccountName = ext.AccountName;
result.SessionData = ext.SessionData == null
? null
: new
{
AccessToken = ext.SessionData?.MobileToken?.Token,
steamLoginSecure = ext.SessionData?.MobileToken?.SignedToken,
RefreshToken = ext.SessionData?.RefreshToken.Token,
SteamID = ext.SessionData?.SteamId.Steam64.Id,
SessionID = ext.SessionData?.SessionId
};
result.ServerTime = ext.ServerTime;
result.SerialNumber = ext.SerialNumber.ToString();
result.Uri = ext.Uri;
result.TokenGid = ext.TokenGid;
result.Secret1 = ext.Secret1;
result.SteamId = ext.SteamId.Steam64.Id;
}
using var w = new StringWriter();
using var write = new JsonTextWriter(w);
write.Formatting = formatting;
var j = JObject.FromObject(result);
if (additionalProperties != null)
{
foreach (var (name, value) in additionalProperties)
{
JToken? jToken = null;
if (value != null)
{
jToken = JToken.FromObject(value);
}
j.Add(name, jToken);
}
}
if (sign)
{
credits ??= MafileCredits.Instance;
var obj = JObject.FromObject(credits);
j.Add(CREDITS_PROPERTY_NAME, obj);
}
j.WriteTo(write);
return w.ToString();
}
}
@@ -1,17 +0,0 @@
using Newtonsoft.Json;
namespace SteamLib.Web.Converters;
public class StringToLongIfNeededConverter : JsonConverter<string>
{
public override void WriteJson(JsonWriter writer, string? value, JsonSerializer serializer)
{
writer.WriteValue(value);
}
public override string ReadJson(JsonReader reader, Type objectType, string? existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
return reader.Value?.ToString() ?? "null";
}
}
@@ -1,25 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations;
#pragma warning disable CS8618
namespace SteamLib.Web.Models.GlobalMarketInfo;
[PublicAPI]
public class GlobalInfoModel
{
[MemberNotNullWhen(true, nameof(SteamId))]
public bool IsLoggedIn { get; set; }
/// <summary>
/// <see langword="null" /> if <see cref="IsLoggedIn" /> is <see langword="null" /> or wallet doesn't exist
/// </summary>
public MarketWalletSchema? WalletInfo { get; set; }
public bool RequiresBillingInfo { get; set; }
public bool HasBillingStates { get; set; }
public string CountryCode { get; set; }
public string Language { get; set; }
public long? SteamId { get; set; }
public string SessionId { get; set; }
}
@@ -1,56 +0,0 @@
using AchiesUtilities.Newtonsoft.JSON.Converters.Common;
using Newtonsoft.Json;
using SteamLib.Core.Enums;
#pragma warning disable CS8618
namespace SteamLib.Web.Models.GlobalMarketInfo;
public class MarketWalletSchema
{
[JsonProperty("wallet_currency")] public Currency WalletCurrency { get; set; }
[JsonProperty("wallet_country")] public string WalletCountry { get; set; }
[JsonProperty("wallet_state")] public string WalletState { get; set; }
[JsonProperty("wallet_fee")]
[JsonConverter(typeof(IntToStringConverter))]
public int WalletFee { get; set; }
[JsonProperty("wallet_fee_minimum")]
[JsonConverter(typeof(IntToStringConverter))]
public int WalletFeeMinimum { get; set; }
[JsonProperty("wallet_fee_percent")]
[JsonConverter(typeof(DecimalToStringConverter))]
public decimal WalletFeePercent { get; set; }
[JsonProperty("wallet_publisher_fee_percent_default")]
[JsonConverter(typeof(DecimalToStringConverter))]
public decimal WalletPublisherFeePercentDefault { get; set; }
[JsonProperty("wallet_fee_base")]
[JsonConverter(typeof(IntToStringConverter))]
public int WalletFeeBase { get; set; }
[JsonProperty("wallet_balance")]
[JsonConverter(typeof(LongToStringConverter))]
public long WalletBalance { get; set; }
[JsonProperty("wallet_delayed_balance")]
[JsonConverter(typeof(LongToStringConverter))]
public long WalletDelayedBalance { get; set; }
[JsonProperty("wallet_max_balance")]
[JsonConverter(typeof(LongToStringConverter))]
public long WalletMaxBalance { get; set; }
[JsonProperty("wallet_trade_max_balance")]
[JsonConverter(typeof(LongToStringConverter))]
public long WalletTradeMaxBalance { get; set; }
[JsonProperty("success")] public int Success { get; set; }
[JsonProperty("rwgrsn")] public int Rwgrsn { get; set; }
}
@@ -1,81 +0,0 @@
using System.Text.RegularExpressions;
using HtmlAgilityPack;
using Newtonsoft.Json;
using SteamLib.Web.Models.GlobalMarketInfo;
namespace SteamLib.Web.Scrappers.HTML;
public static class MarketGlobalInfoScrapper
{
private static readonly Regex LoggedInRegex = new("var g_bLoggedIn = (.+);", RegexOptions.Compiled);
private static readonly Regex ReqBillingInfoRegex =
new("var g_bRequiresBillingInfo = (.+);", RegexOptions.Compiled);
private static readonly Regex CountryCodeRegex = new("var g_strCountryCode = \"(.+)\";", RegexOptions.Compiled);
private static readonly Regex HasBillingStates = new("var g_bHasBillingStates = (.+);", RegexOptions.Compiled);
private static readonly Regex LanguageRegex = new("var g_strLanguage = \"(.+)\";", RegexOptions.Compiled);
private static readonly Regex WalletInfoRegex = new("var g_rgWalletInfo = (.+);", RegexOptions.Compiled);
private static readonly Regex SessionIdRegex = new("g_sessionID = \"(.+)\";", RegexOptions.Compiled);
private static readonly Regex SteamIdRegex = new("g_steamID = \"(.+)\";", RegexOptions.Compiled);
public static GlobalInfoModel Scrap(string html)
{
var document = new HtmlDocument();
document.LoadHtml(html);
var scriptNodes = document.DocumentNode.SelectNodes("//*[@id=\"responsive_page_template_content\"]/script");
var index = scriptNodes.Count > 2
? 1
: 0; //If account is limited or market unavailable elements will be displaced
var scriptNode = scriptNodes[index];
var script = scriptNode.InnerText!;
var logged = GetBool(script, LoggedInRegex);
var hasBillingStates = GetBool(script, HasBillingStates);
var reqBillingInfo = GetBool(script, ReqBillingInfoRegex);
var country = CountryCodeRegex.Match(html).Groups[1].Value;
var language = LanguageRegex.Match(html).Groups[1].Value;
var walletInfoStr = WalletInfoRegex.Match(html).Groups[1].Value;
MarketWalletSchema? wallet = null;
var sessionScriptNode =
document.DocumentNode.SelectSingleNode("//div[@class='responsive_page_content']/script");
var sessionScript = sessionScriptNode.InnerText!;
var sessionId = SessionIdRegex.Match(sessionScript).Groups[1].Value;
long? steamId = null;
if (logged)
{
wallet = JsonConvert.DeserializeObject<MarketWalletSchema>(walletInfoStr);
var steamIdStr = SteamIdRegex.Match(sessionScript).Groups[1].Value;
steamId = long.Parse(new string(steamIdStr.Where(char.IsDigit).ToArray()));
}
return new GlobalInfoModel
{
CountryCode = country,
HasBillingStates = hasBillingStates,
IsLoggedIn = logged,
Language = language,
RequiresBillingInfo = reqBillingInfo,
WalletInfo = wallet,
SessionId = sessionId,
SteamId = steamId
};
}
private static bool GetBool(string script, Regex regex)
{
var value = regex.Match(script).Groups[1].Value;
return bool.Parse(value);
}
}
@@ -1,60 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using SteamLib.Core;
using SteamLib.Exceptions.General;
using SteamLib.Web.Models.GlobalMarketInfo;
using SteamLib.Web.Scrappers.HTML;
using System.Threading;
using JetBrains.Annotations;
using HtmlAgilityPack;
using SteamLib.Core.Models;
namespace SteamLib.Web;
public static class SteamWebApi
{
/// <summary>
/// Login is not required
/// </summary>
/// <param name="client"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public static async Task<GlobalInfoModel> GetMarketGlobalInfo(HttpClient client,
CancellationToken cancellationToken = default)
{
var resp = await client.GetStringAsync(SteamConstants.STEAM_MARKET, cancellationToken);
try
{
return MarketGlobalInfoScrapper.Scrap(resp);
}
catch (Exception ex)
{
throw new UnsupportedResponseException(resp, ex);
}
}
[RegexPattern]
[SuppressMessage("ReSharper", "UseRawString")]
[SuppressMessage("ReSharper", "StringLiteralTypo")]
private static readonly string _regexTip =
@"g_sessionID = ""(?<g_sessionID>.*)"";"
+ @"\s*g_steamID = (?<g_steamID>.*);";
private static readonly Regex Regex = new(_regexTip, RegexOptions.Compiled);
private const string XPATH = "//div[@class='responsive_page_content']/script";
public static async Task<string> GetLoginSessionId(HttpClient client, CancellationToken cancellationToken = default)
{
var resp = await client.GetStringAsync("https://steamcommunity.com/login/home", cancellationToken);
var document = new HtmlDocument();
document.LoadHtml(resp);
var scriptNode = document.DocumentNode.SelectSingleNode(XPATH) ?? throw new NullReferenceException("Script Node was null");
var script = scriptNode.InnerText;
var match = Regex.Match(script);
if (!match.Success)
throw new UnsupportedResponseException(script, "Page contains script but regex was unsuccessful");
return match.Groups["g_sessionID"].Value;
}
}
+89
View File
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Changelog</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #eeeeee;
color: #333;
line-height: 1.6;
}
.changelog-container {
background-color: #fff;
border-radius: 10px;
padding: 25px;
margin: 25px auto;
max-width: 800px;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.change {
margin-bottom: 20px;
padding: 0;
border-left: 4px solid #a50ec7;
background-color: #f9f9f9;
}
.version {
font-weight: 600;
font-size: 1.5em;
color: #a50ec7;
margin-left: 10px;
}
.date {
font-style: italic;
color: #888;
margin-bottom: 10px;
margin-left: 15px;
}
.description {
font-size: 1em;
padding: 0 15px;
}
.description ul {
list-style: inside square;
padding: 0;
}
@media only screen and (max-width: 600px) {
.changelog-container {
width: 90%;
margin: 25px auto;
padding: 25px;
}
}
</style>
</head>
<body>
<div class="changelog-container">
<!-- Changelog entry -->
<div class="change">
<div class="version">Version 1.7.1</div>
<div class="date">09.07.2025</div>
<div class="description">
<ul>
<li>
<b>NEWS:</b> Official Telegram group now available! Join us at
<b>
<a href="https://t.me/nebulaauth">t.me/nebulaauth</a>
</b>
</li>
<li><b>UPDATE:</b> Added support for a new confirmation type — Purchase.</li>
<li><b>UPDATE:</b> Merged Global 1.7.0 updates into the release version (<a href="https://t.me/nebulaauth/32">patch notes</a>).</li>
<li><b>FIX:</b> Fixed a crash occurring during confirmation loading.</li>
<li><b>ENHANCEMENT:</b> Improved localization and system messages.</li>
</ul>
</div>
</div>
</div>
</body>
</html>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>1.5.6.0</version>
<url>https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/releases/download/1.5.6/NebulaAuth.1.5.6.zip</url>
<changelog>https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/changelog/1.5.6.html</changelog>
<mandatory>false</mandatory>
</item>
@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
+31 -15
View File
@@ -4,13 +4,11 @@
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:NebulaAuth.Converters" xmlns:converters="clr-namespace:NebulaAuth.Converters"
xmlns:system="clr-namespace:System;assembly=System.Runtime" xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:background="clr-namespace:NebulaAuth.Converters.Background" xmlns:background="clr-namespace:NebulaAuth.Converters.Background">
StartupUri="MainWindow.xaml">
<Application.Resources> <Application.Resources>
<ResourceDictionary> <ResourceDictionary>
<SolidColorBrush x:Key="MaterialDesignPaper">#1E2025</SolidColorBrush>
<FontFamily x:Key="Roboto">pack://application:,,,/Fonts/Roboto/#Roboto</FontFamily>
<FontFamily x:Key="RobotoSymbols">pack://application:,,,/Fonts/Roboto/#Roboto Symbols</FontFamily>
<converters:CoefficientConverter x:Key="CoefficientConverter" /> <converters:CoefficientConverter x:Key="CoefficientConverter" />
<converters:ReverseBooleanConverter x:Key="ReverseBooleanConverter" /> <converters:ReverseBooleanConverter x:Key="ReverseBooleanConverter" />
<converters:ProxyTextConverter x:Key="ProxyTextConverter" /> <converters:ProxyTextConverter x:Key="ProxyTextConverter" />
@@ -19,32 +17,50 @@
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter" /> <converters:ColorToBrushConverter x:Key="ColorToBrushConverter" />
<converters:AnyMafilesToVisibilityConverter x:Key="AnyMafilesToVisibilityConverter" /> <converters:AnyMafilesToVisibilityConverter x:Key="AnyMafilesToVisibilityConverter" />
<converters:PortableMaClientStatusToColorConverter x:Key="PortableMaClientStatusToColorConverter" /> <converters:PortableMaClientStatusToColorConverter x:Key="PortableMaClientStatusToColorConverter" />
<converters:NullableToBooleanConverter x:Key="NullableToBooleanConverter" />
<!-- Background converters--> <!-- Background converters-->
<background:BackgroundImageVisibleConverter x:Key="BackgroundImageVisibleConverter" /> <background:BackgroundImageVisibleConverter x:Key="BackgroundImageVisibleConverter" />
<background:BackgroundSourceConverter x:Key="BackgroundSourceConverter" /> <background:BackgroundSourceConverter x:Key="BackgroundSourceConverter" />
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<materialDesign:NullableToVisibilityConverter x:Key="NullableToVisibilityConverter" />
<system:Boolean x:Key="True">True</system:Boolean> <system:Boolean x:Key="True">True</system:Boolean>
<system:Boolean x:Key="False">False</system:Boolean> <system:Boolean x:Key="False">False</system:Boolean>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Theme/Themes/DefaultTheme.xaml" />
<materialDesign:BundledTheme BaseTheme="Dark" PrimaryColor="LightGreen" SecondaryColor="Purple" />
<ResourceDictionary <ResourceDictionary
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" /> Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
<ResourceDictionary Source="Theme/Brushes.xaml" />
<!--Controls-->
<ResourceDictionary <ResourceDictionary
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/materialdesigntheme.snackbar.xaml" /> Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/materialdesigntheme.snackbar.xaml" />
<ResourceDictionary
Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/materialdesigntheme.Dark.xaml" />
<!-- Theme-->
<ResourceDictionary Source="Theme/Brushes.xaml" />
<ResourceDictionary Source="Theme/WindowStyle/WindowStyle.xaml" />
<!--Controls-->
<ResourceDictionary Source="View/ConfirmationTemplates.xaml" /> <ResourceDictionary Source="View/ConfirmationTemplates.xaml" />
<ResourceDictionary Source="View/LinkerStepTemplates.xaml" />
<ResourceDictionary Source="View/MafileMoverStepTemplates.xaml" />
<!--Theme-->
<ResourceDictionary Source="Theme/WindowStyle/WindowStyle.xaml" />
<ResourceDictionary Source="Theme/MaterialDesignThemes.Overrides.xaml" />
<ResourceDictionary>
<Style TargetType="ToolTip" BasedOn="{StaticResource {x:Type ToolTip}}">
<Setter Property="Foreground" Value="{DynamicResource Base100Brush}" />
<Setter Property="Background" Value="{DynamicResource BaseContentBrush}" />
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
+52
View File
@@ -0,0 +1,52 @@
using System;
using System.IO;
using System.Linq;
using System.Windows;
using AchiesUtilities.Extensions;
using NebulaAuth.Core;
using NebulaAuth.Model;
using NebulaAuth.Model.Exceptions;
namespace NebulaAuth;
public partial class App
{
protected override async void OnStartup(StartupEventArgs e)
{
try
{
var splashScreen = new SplashScreen("Theme\\SplashScreen.png");
splashScreen.Show(false, true);
base.OnStartup(e);
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;
await Storage.Initialize(threads);
var mainWindow = new MainWindow();
Current.MainWindow = mainWindow;
mainWindow.Show();
splashScreen.Close(TimeSpan.Zero);
}
catch (Exception ex)
{
var msg = ex.ToString();
if (ex is CantAlignTimeException)
{
msg = LocManager.Get("CantAlignTimeError");
}
MessageBox.Show(msg, "Error", MessageBoxButton.OK, MessageBoxImage.Stop, MessageBoxResult.OK,
MessageBoxOptions.DefaultDesktopOnly);
Shell.Logger.Fatal(ex, "Application startup failed");
Current.Shutdown(1);
}
}
}
@@ -11,10 +11,10 @@ public class AnyMafilesToVisibilityConverter : IValueConverter
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{ {
if (_everAnyMafiles) //if (_everAnyMafiles)
{ //{
return Visibility.Collapsed; // return Visibility.Collapsed;
} //}
if (value is 0) if (value is 0)
{ {
@@ -18,7 +18,7 @@ public class BackgroundSourceConverter : IValueConverter
} }
return new BitmapImage(new Uri("pack://application:,,,/Theme/Background.jpg")); return new BitmapImage(new Uri("pack://application:,,,/Theme/Background.png"));
} }
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+28
View File
@@ -0,0 +1,28 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:converters="clr-namespace:NebulaAuth.Converters"
xmlns:background="clr-namespace:NebulaAuth.Converters.Background">
<converters:CoefficientConverter x:Key="CoefficientConverter" />
<converters:ReverseBooleanConverter x:Key="ReverseBooleanConverter" />
<converters:ProxyTextConverter x:Key="ProxyTextConverter" />
<converters:ProxyDataTextConverter x:Key="ProxyDataTextConverter" />
<converters:MultiCommandParameterConverter x:Key="MultiCommandParameterConverter" />
<converters:ColorToBrushConverter x:Key="ColorToBrushConverter" />
<converters:AnyMafilesToVisibilityConverter x:Key="AnyMafilesToVisibilityConverter" />
<converters:PortableMaClientStatusToColorConverter x:Key="PortableMaClientStatusToColorConverter" />
<converters:NullableToBooleanConverter x:Key="NullableToBooleanConverter" />
<!-- Background converters-->
<background:BackgroundImageVisibleConverter x:Key="BackgroundImageVisibleConverter" />
<background:BackgroundSourceConverter x:Key="BackgroundSourceConverter" />
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<materialDesign:NullableToVisibilityConverter x:Key="NullableToVisibilityConverter" />
<system:Boolean x:Key="True">True</system:Boolean>
<system:Boolean x:Key="False">False</system:Boolean>
</ResourceDictionary>
@@ -0,0 +1,18 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace NebulaAuth.Converters;
public class NullableToBooleanConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
@@ -14,6 +14,7 @@ public class PortableMaClientStatusToColorConverter : IValueConverter
return new SolidColorBrush(Color.FromRgb(187, 224, 139)); return new SolidColorBrush(Color.FromRgb(187, 224, 139));
} }
return new SolidColorBrush(Color.FromRgb(224, 139, 139)); return new SolidColorBrush(Color.FromRgb(224, 139, 139));
} }
@@ -5,6 +5,8 @@ using NebulaAuth.Model;
using NebulaAuth.Model.Entities; using NebulaAuth.Model.Entities;
using NebulaAuth.View; using NebulaAuth.View;
using NebulaAuth.View.Dialogs; using NebulaAuth.View.Dialogs;
using NebulaAuth.ViewModel.Linker;
using NebulaAuth.ViewModel.MafileMover;
using NebulaAuth.ViewModel.Other; using NebulaAuth.ViewModel.Other;
namespace NebulaAuth.Core; namespace NebulaAuth.Core;
@@ -86,11 +88,37 @@ public static class DialogsController
public static async Task ShowLinkerDialog() public static async Task ShowLinkerDialog()
{ {
var vm = new LinkAccountVM(); LinkAccountVM? vm = null;
try
{
vm = new LinkAccountVM();
var view = new LinkerView var view = new LinkerView
{ {
DataContext = vm DataContext = vm
}; };
await DialogHost.Show(view); await DialogHost.Show(view);
} }
finally
{
vm?.Dispose();
}
}
public static async Task ShowMafileMoverDialog()
{
MafileMoverVM? vm = null;
try
{
vm = new MafileMoverVM();
var view = new MafileMoverView
{
DataContext = vm
};
await DialogHost.Show(view);
}
finally
{
vm?.Dispose();
}
}
} }
@@ -38,7 +38,7 @@ public static class LocManager
public static void Init() public static void Init()
{ {
Loc.LogOutMissingTranslations = true; Loc.Instance.LogOutMissingTranslations = true;
LocalizationLoader.Instance.FileLanguageLoaders.Add(new JsonFileLoader()); LocalizationLoader.Instance.FileLanguageLoaders.Add(new JsonFileLoader());
@@ -44,7 +44,7 @@ public class SnackbarController
private static TimeSpan GetSnackbarTime(string str) private static TimeSpan GetSnackbarTime(string str)
{ {
var duration = str.Length / 0.03; var duration = str.Length / 0.022;
if (duration < MIN_SNACKBAR_TIME) if (duration < MIN_SNACKBAR_TIME)
{ {
duration = MIN_SNACKBAR_TIME; duration = MIN_SNACKBAR_TIME;
@@ -2,9 +2,9 @@
using System.ComponentModel; using System.ComponentModel;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Shell; using System.Windows.Shell;
using NebulaAuth.Model; using NebulaAuth.Model;
@@ -14,7 +14,6 @@ namespace NebulaAuth.Core;
public static class ThemeManager public static class ThemeManager
{ {
public static System.Windows.Media.Color DefaultBackgroundColor = System.Windows.Media.Color.FromRgb(30, 32, 37);
private static readonly Window MainWindow = Application.Current.MainWindow!; private static readonly Window MainWindow = Application.Current.MainWindow!;
static ThemeManager() static ThemeManager()
@@ -24,11 +23,7 @@ public static class ThemeManager
private static void SettingsOnPropertyChanged(object? sender, PropertyChangedEventArgs e) private static void SettingsOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == nameof(Settings.BackgroundColor)) if (e.PropertyName == nameof(Settings.IconColor))
{
UpdateBackground();
}
else if (e.PropertyName == nameof(Settings.IconColor))
{ {
UpdateIcon(); UpdateIcon();
} }
@@ -58,15 +53,40 @@ public static class ThemeManager
} }
} }
private static void UpdateBackground()
public static void ApplyTheme(string themeName)
{ {
var color = Settings.Instance.BackgroundColor ?? DefaultBackgroundColor; var colorDict = new ResourceDictionary
Application.Current.Resources["WindowBackground"] = new SolidColorBrush(color); {
Source = new Uri($"Theme/Themes/{themeName}.xaml", UriKind.Relative)
};
var brushDict = new ResourceDictionary
{
Source = new Uri("Theme/Brushes.xaml", UriKind.Relative)
};
var mergedDictionaries = Application.Current.Resources.MergedDictionaries;
var toRemove = mergedDictionaries
.Where(d => d.Source?.OriginalString.Contains("Theme/Themes/") == true ||
d.Source?.OriginalString.EndsWith("Brushes.xaml") == true)
.ToList();
foreach (var dict in toRemove)
{
mergedDictionaries.Remove(dict);
} }
mergedDictionaries.Insert(0, colorDict);
mergedDictionaries.Insert(0, brushDict);
}
public static void InitializeTheme() public static void InitializeTheme()
{ {
UpdateIcon(); UpdateIcon();
UpdateBackground(); ApplyTheme(Settings.Instance.GetTheme());
} }
} }
@@ -8,7 +8,7 @@ namespace NebulaAuth.Core;
public static class UpdateManager public static class UpdateManager
{ {
private const string UPDATE_URL = private const string UPDATE_URL =
"https://raw.githubusercontent.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/master/NebulaAuth/update.xml"; "https://raw.githubusercontent.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/master/misc/update.xml";
public static void CheckForUpdates() public static void CheckForUpdates()
{ {
@@ -9,12 +9,14 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:entities="clr-namespace:NebulaAuth.Model.Entities" xmlns:entities="clr-namespace:NebulaAuth.Model.Entities"
xmlns:viewModel="clr-namespace:NebulaAuth.ViewModel" xmlns:viewModel="clr-namespace:NebulaAuth.ViewModel"
xmlns:controls="clr-namespace:NebulaAuth.Theme.Controls"
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
MinHeight="500" MinWidth="500" DefaultFontSize="18" ScaleCoefficient="0.4" MinHeight="500" MinWidth="500" DefaultFontSize="18" ScaleCoefficient="0.4"
Title="NebulaAuth" Height="800" Width="730" Style="{StaticResource MainWindow}" Title="NebulaAuth" Height="800" Width="730"
RenderOptions.BitmapScalingMode="HighQuality" Foreground="#FFF5F5F5" Style="{StaticResource MainWindow}"
RenderOptions.BitmapScalingMode="HighQuality"
FontFamily="{md:MaterialDesignFont}" FontFamily="{md:MaterialDesignFont}"
TextElement.Foreground="{DynamicResource MaterialDesignBody}" TextElement.Foreground="{DynamicResource BaseContentBrush}"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance viewModel:MainVM}"> d:DataContext="{d:DesignInstance viewModel:MainVM}">
<b:Interaction.Behaviors> <b:Interaction.Behaviors>
@@ -28,20 +30,31 @@
</Window.InputBindings> </Window.InputBindings>
<Border Name="DragNDropBorder" Panel.ZIndex="3" Opacity="1" AllowDrop="True" <Border Name="DragNDropBorder" Panel.ZIndex="3" Opacity="1" AllowDrop="True"
DragDrop.DragEnter="Rectangle_DragEnter" DragLeave="Rectangle_DragLeave" Drop="Rectangle_Drop"> DragDrop.DragEnter="Rectangle_DragEnter" DragLeave="Rectangle_DragLeave" Drop="Rectangle_Drop">
<md:DialogHost DialogOpened="DialogHost_DialogOpened" DialogClosed="DialogHost_DialogClosed"> <md:DialogHost DialogOpened="DialogHost_DialogOpened"
DialogClosed="DialogHost_DialogClosed"
ApplyBlurBackground="{Binding Settings.ApplyBlurBackground}"
BlurRadius="12"
x:Name="DialogHostInstance">
<Grid ZIndex="0"> <Grid ZIndex="0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition /> <RowDefinition />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Image Grid.RowSpan="2" Opacity="0.6" Stretch="UniformToFill" <Image Grid.RowSpan="2" Opacity="{Binding Settings.BackgroundOpacity}" Stretch="UniformToFill"
RenderOptions.BitmapScalingMode="LowQuality" HorizontalAlignment="Center" RenderOptions.BitmapScalingMode="LowQuality" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Visibility="{Binding Settings.BackgroundMode, Converter={StaticResource BackgroundImageVisibleConverter}}" Visibility="{Binding Settings.BackgroundMode, Converter={StaticResource BackgroundImageVisibleConverter}}"
Source="{Binding Settings.BackgroundMode, Mode=OneWay, Converter={StaticResource BackgroundSourceConverter}}" /> Source="{Binding Settings.BackgroundMode, Mode=OneWay, Converter={StaticResource BackgroundSourceConverter}}">
<Rectangle Grid.Row="0" Grid.RowSpan="2" Opacity="0.5" Fill="#FF1F1D1D" <Image.Effect>
Visibility="{Binding Settings.BackgroundMode, Converter={StaticResource BackgroundImageVisibleConverter}}" /> <BlurEffect Radius="{Binding Settings.BackgroundBlur}" />
</Image.Effect>
</Image>
<Rectangle Grid.Row="0" Grid.RowSpan="2" Fill="Black" Stretch="UniformToFill"
Opacity="{Binding Settings.BackgroundGamma}"
IsHitTestVisible="False" />
<Rectangle Name="DragNDropOverlay" Grid.Row="0" Grid.RowSpan="3" Panel.ZIndex="1" Fill="#242123" <Rectangle Name="DragNDropOverlay" Grid.Row="0" Grid.RowSpan="3" Panel.ZIndex="1" Fill="#242123"
Opacity="0.9" Visibility="Hidden" /> Opacity="0.9" Visibility="Hidden" />
<StackPanel Name="DragNDropPanel" Grid.Row="0" ZIndex="2" w:FontScaleWindow.Scale="1" <StackPanel Name="DragNDropPanel" Grid.Row="0" ZIndex="2" w:FontScaleWindow.Scale="1"
@@ -51,13 +64,10 @@
<md:PackIcon Foreground="#9a65b8" HorizontalAlignment="Center" Width="36" Height="36" <md:PackIcon Foreground="#9a65b8" HorizontalAlignment="Center" Width="36" Height="36"
Kind="FileReplaceOutline" /> Kind="FileReplaceOutline" />
</StackPanel> </StackPanel>
<ToolBarTray IsLocked="True" Grid.Row="0"> <ToolBarTray>
<ToolBarTray.Background> <ToolBar ClipToBounds="True" HorizontalContentAlignment="Stretch"
<SolidColorBrush Opacity="0.6" Color="#FF1A1C25" /> Style="{StaticResource MaterialDesignToolBar}" FontSize="16">
</ToolBarTray.Background> <Menu FontSize="16">
<ToolBar Background="#00FFFFFF" ClipToBounds="False" Style="{StaticResource MaterialDesignToolBar}"
w:FontScaleWindow.Scale="0.68" w:FontScaleWindow.ResizeFont="True">
<Menu w:FontScaleWindow.Scale="0.68" w:FontScaleWindow.ResizeFont="True">
<MenuItem Header="{Tr MainWindow.Menu.File.Caption}"> <MenuItem Header="{Tr MainWindow.Menu.File.Caption}">
<MenuItem Header="{Tr MainWindow.Menu.File.Import}" <MenuItem Header="{Tr MainWindow.Menu.File.Import}"
Command="{Binding AddMafileCommand}" /> Command="{Binding AddMafileCommand}" />
@@ -75,8 +85,11 @@
<MenuItem Header="{Tr MainWindow.Menu.Account.Caption}"> <MenuItem Header="{Tr MainWindow.Menu.Account.Caption}">
<MenuItem Header="{Tr MainWindow.Menu.Account.Link}" <MenuItem Header="{Tr MainWindow.Menu.Account.Link}"
Command="{Binding LinkAccountCommand}" /> Command="{Binding LinkAccountCommand}" />
<MenuItem Header="{Tr MainWindow.Menu.Account.MoveSteamGuard}"
Command="{Binding MoveAccountCommand}" />
<MenuItem Header="{Tr MainWindow.Menu.Account.Unlink}" <MenuItem Header="{Tr MainWindow.Menu.Account.Unlink}"
Command="{Binding RemoveAuthenticatorCommand}" /> Command="{Binding RemoveAuthenticatorCommand}" />
<MenuItem IsEnabled="False" Header="⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" Height="15" /> <MenuItem IsEnabled="False" Header="⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" Height="15" />
<MenuItem IsEnabled="{Binding IsMafileSelected}" <MenuItem IsEnabled="{Binding IsMafileSelected}"
Header="{Tr MainWindow.Menu.Account.RefreshSession}" Header="{Tr MainWindow.Menu.Account.RefreshSession}"
@@ -91,7 +104,8 @@
md:HintAssist.Hint="{Tr MainWindow.AppBar.GroupsHint}" md:HintAssist.Hint="{Tr MainWindow.AppBar.GroupsHint}"
MinWidth="100" Margin="8,0,8,0" VerticalAlignment="Center" IsEditable="True" MinWidth="100" Margin="8,0,8,0" VerticalAlignment="Center" IsEditable="True"
ItemsSource="{Binding Groups}" ItemsSource="{Binding Groups}"
SelectedValue="{Binding SelectedGroup}"> SelectedValue="{Binding SelectedGroup}"
md:TextFieldAssist.HasClearButton="True">
<FrameworkElement.Resources> <FrameworkElement.Resources>
<ResourceDictionary> <ResourceDictionary>
@@ -114,6 +128,7 @@
md:HintAssist.Hint="{Tr MainWindow.AppBar.Proxy.ProxyHint}" md:HintAssist.Hint="{Tr MainWindow.AppBar.Proxy.ProxyHint}"
md:ComboBoxAssist.ShowSelectedItem="False" md:ComboBoxAssist.ShowSelectedItem="False"
SelectedValue="{Binding SelectedProxy}" SelectedValue="{Binding SelectedProxy}"
ItemsSource="{Binding Proxies}"> ItemsSource="{Binding Proxies}">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type entities:MaProxy}"> <DataTemplate DataType="{x:Type entities:MaProxy}">
@@ -132,7 +147,8 @@
</UIElement.InputBindings> </UIElement.InputBindings>
</ComboBox> </ComboBox>
<md:PackIcon Kind="StopAlert" VerticalAlignment="Center" HorizontalAlignment="Center" <md:PackIcon Kind="StopAlert" VerticalAlignment="Center" HorizontalAlignment="Center"
Foreground="#FFFF0000" Margin="3" ToolTipService.InitialShowDelay="300"> Foreground="{DynamicResource ErrorBrush}" Margin="3"
ToolTipService.InitialShowDelay="300">
<md:PackIcon.ToolTip> <md:PackIcon.ToolTip>
<TextBlock> <TextBlock>
<Run <Run
@@ -153,12 +169,11 @@
</UIElement.Visibility> </UIElement.Visibility>
</md:PackIcon> </md:PackIcon>
<md:PackIcon Kind="StopAlert" VerticalAlignment="Center" HorizontalAlignment="Center" <md:PackIcon Kind="StopAlert" VerticalAlignment="Center" HorizontalAlignment="Center"
Foreground="#FFFFA500" Margin="3" Foreground="{DynamicResource WarningBrush}" Margin="3"
ToolTip="{Tr MainWindow.AppBar.Proxy.ProxyAlert.DefaultInUse}" ToolTip="{Tr MainWindow.AppBar.Proxy.ProxyAlert.DefaultInUse}"
ToolTipService.InitialShowDelay="300" ToolTipService.InitialShowDelay="300"
Visibility="{Binding IsDefaultProxy, Converter={StaticResource BooleanToVisibilityConverter}}" /> Visibility="{Binding IsDefaultProxy, Converter={StaticResource BooleanToVisibilityConverter}}" />
<ToggleButton ToolTip="{Tr MainWindow.AppBar.MarketTimerHint}" <ToggleButton ToolTip="{Tr MainWindow.AppBar.MarketTimerHint}"
Foreground="{DynamicResource PrimaryHueDarkForegroundBrush}"
IsEnabled="{Binding IsMafileSelected}" Margin="2" IsEnabled="{Binding IsMafileSelected}" Margin="2"
Style="{StaticResource MaterialDesignFlatToggleButton}" Style="{StaticResource MaterialDesignFlatToggleButton}"
IsChecked="{Binding MarketTimerEnabled}"> IsChecked="{Binding MarketTimerEnabled}">
@@ -175,7 +190,6 @@
</FrameworkElement.ContextMenu> </FrameworkElement.ContextMenu>
</ToggleButton> </ToggleButton>
<ToggleButton ToolTip="{Tr MainWindow.AppBar.TradeTimerHint}" <ToggleButton ToolTip="{Tr MainWindow.AppBar.TradeTimerHint}"
Foreground="{DynamicResource PrimaryHueDarkForegroundBrush}"
IsEnabled="{Binding IsMafileSelected}" Margin="2" IsEnabled="{Binding IsMafileSelected}" Margin="2"
Style="{StaticResource MaterialDesignFlatToggleButton}" Style="{StaticResource MaterialDesignFlatToggleButton}"
IsChecked="{Binding TradeTimerEnabled}"> IsChecked="{Binding TradeTimerEnabled}">
@@ -197,7 +211,7 @@
PreviewTextInput="UIElement_OnPreviewTextInput" /> PreviewTextInput="UIElement_OnPreviewTextInput" />
<ToggleButton ToolTip="{Tr MainWindow.AppBar.ShowAutoConfirmAccountsHint}" <ToggleButton ToolTip="{Tr MainWindow.AppBar.ShowAutoConfirmAccountsHint}"
HorizontalAlignment="Right" IsChecked="{Binding MaacDisplay}" HorizontalAlignment="Right" IsChecked="{Binding MaacDisplay}"
Foreground="WhiteSmoke" VerticalAlignment="Center" Foreground="{DynamicResource BaseContentBrush}" VerticalAlignment="Center"
Style="{StaticResource MaterialDesignFlatPrimaryToggleButton}" Margin="4"> Style="{StaticResource MaterialDesignFlatPrimaryToggleButton}" Margin="4">
<md:PackIcon Kind="Accounts" /> <md:PackIcon Kind="Accounts" />
</ToggleButton> </ToggleButton>
@@ -214,11 +228,33 @@
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ListBox w:FontScaleWindow.Scale="0.8" w:FontScaleWindow.ResizeFont="True" Grid.Row="0"
Margin="10,15,0,0" ItemsSource="{Binding MaFiles}" <Border Grid.Row="0" Margin="10" Padding="5"
BorderThickness="1"
CornerRadius="12">
<Border.BorderBrush>
<SolidColorBrush Color="{DynamicResource Base300Color}" Opacity="0.3" />
</Border.BorderBrush>
<Border.Background>
<SolidColorBrush Color="{DynamicResource Base100Color}"
Opacity="{Binding Settings.LeftOpacity}" />
</Border.Background>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Margin="4"
Visibility="{Binding MaFiles.Count, Converter={StaticResource AnyMafilesToVisibilityConverter}}"
TextWrapping="WrapWithOverflow" FontSize="16"
Text="{Tr MainWindow.LeftPart.NoMafiles}" />
<ListBox w:FontScaleWindow.Scale="0.8" w:FontScaleWindow.ResizeFont="True" Margin="2"
ItemsSource="{Binding MaFiles}"
SelectedValue="{Binding SelectedMafile}"> SelectedValue="{Binding SelectedMafile}">
<ListBox.ItemContainerStyle> <ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" BasedOn="{StaticResource MaterialDesignListBoxItem}"> <Style TargetType="ListBoxItem"
BasedOn="{StaticResource MaterialDesignListBoxItem}">
<Setter Property="HorizontalAlignment" Value="Stretch" /> <Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" /> <Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style> </Style>
@@ -231,13 +267,15 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Stretch" Text="{Binding AccountName}"> <TextBlock HorizontalAlignment="Stretch" Text="{Binding AccountName}">
<TextBlock.Resources>
<SolidColorBrush x:Key="BaseContentBrushProxy"
Color="{DynamicResource BaseContentColor}" />
</TextBlock.Resources>
<TextBlock.Foreground> <TextBlock.Foreground>
<Binding Mode="OneWay" Path="LinkedClient.IsError" <Binding Path="LinkedClient.IsError"
Converter="{StaticResource PortableMaClientStatusToColorConverter}"> Converter="{StaticResource PortableMaClientStatusToColorConverter}"
<Binding.FallbackValue> FallbackValue="{StaticResource BaseContentBrushProxy}" />
<SolidColorBrush Color="WhiteSmoke" />
</Binding.FallbackValue>
</Binding>
</TextBlock.Foreground> </TextBlock.Foreground>
</TextBlock> </TextBlock>
<StackPanel <StackPanel
@@ -298,33 +336,30 @@
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.CopyPassword}" <MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.CopyPassword}"
Command="{Binding Path=CopyPasswordCommand}" Command="{Binding Path=CopyPasswordCommand}"
CommandParameter="{Binding Path=PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" /> CommandParameter="{Binding Path=PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
</ContextMenu> </ContextMenu>
</FrameworkElement.ContextMenu> </FrameworkElement.ContextMenu>
</ListBox> </ListBox>
<Border Grid.Row="0" Margin="10" Padding="5"
Visibility="{Binding MaFiles.Count, Converter={StaticResource AnyMafilesToVisibilityConverter}, Mode=OneWay}">
<Border.Background>
<SolidColorBrush Color="DarkGray" Opacity="0.5" />
</Border.Background>
<TextBlock TextWrapping="WrapWithOverflow" FontSize="16"
Text="{Tr MainWindow.Global.StartTip}" />
</Border>
<TextBox Style="{StaticResource MaterialDesignFloatingHintTextBox}" <TextBox Style="{StaticResource MaterialDesignFloatingHintTextBox}"
md:TextFieldAssist.HasClearButton="True" w:FontScaleWindow.Scale="0.7" md:TextFieldAssist.HasClearButton="True" w:FontScaleWindow.Scale="0.7"
w:FontScaleWindow.ResizeFont="True" Grid.Row="1" Margin="10,5,10,10" w:FontScaleWindow.ResizeFont="True" Grid.Row="1" Margin="10,5,10,10"
md:HintAssist.Hint="{Tr MainWindow.LeftPart.SearchBoxHint}" md:HintAssist.Hint="{Tr MainWindow.LeftPart.SearchBoxHint}"
Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid> </Grid>
<md:Card Grid.Column="1" Margin="10,10,15,10" UniformCornerRadius="15">
<Control.Background> </Border>
<SolidColorBrush Color="#FF1A1D25" Opacity="0.6" />
</Control.Background> </Grid>
<md:Card BorderThickness="1" Style="{StaticResource MaterialDesignOutlinedCard}" Grid.Column="1"
Margin="10,10,15,10" UniformCornerRadius="12" Opacity="{Binding Settings.RightOpacity}">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition /> <RowDefinition Height="115*" />
<RowDefinition Height="436*" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Row="0"> <Grid Row="0">
@@ -334,47 +369,50 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBox w:FontScaleWindow.Scale="1.1" w:FontScaleWindow.ResizeFont="True" <TextBox w:FontScaleWindow.Scale="1.1" w:FontScaleWindow.ResizeFont="True"
IsReadOnly="True" HorizontalContentAlignment="Center" Background="#00FFFFFF" IsReadOnly="True" HorizontalContentAlignment="Center" Background="#00FFFFFF"
Style="{StaticResource MaterialDesignFilledTextBox}"
Style="{StaticResource MaterialDesignTextBox}"
Text="{Binding Code, FallbackValue=Code}"> Text="{Binding Code, FallbackValue=Code}">
<TextBox.InputBindings> <TextBox.InputBindings>
<MouseBinding Gesture="LeftClick" Command="{Binding CopyCodeCommand}" /> <MouseBinding Gesture="LeftClick" Command="{Binding CopyCodeCommand}" />
</TextBox.InputBindings> </TextBox.InputBindings>
</TextBox> </TextBox>
<Grid Row="1"> <controls:CodeProgressBar
<Grid.ColumnDefinitions> Visibility="{Binding SelectedMafile, Converter={StaticResource NullableToVisibilityConverter}}"
<ColumnDefinition /> Grid.Row="1" Height="4" Style="{StaticResource MaterialDesignLinearProgressBar}"
<ColumnDefinition Width="Auto" /> MaxTime="30" TimeRemaining="{Binding CodeProgress, Mode=OneWay}" />
</Grid.ColumnDefinitions>
<ProgressBar Margin="5" Foreground="#FF9932CC" Height="15"
md:TransitionAssist.DisableTransitions="True"
Value="{Binding CodeProgress}" />
</Grid>
</Grid> </Grid>
<Button IsEnabled="{Binding IsMafileSelected}" w:FontScaleWindow.Scale="0.7" <Button IsEnabled="{Binding IsMafileSelected}" w:FontScaleWindow.Scale="0.7"
w:FontScaleWindow.ResizeFont="True" Grid.Row="1" w:FontScaleWindow.ResizeFont="True" Grid.Row="1"
Style="{StaticResource MaterialDesignOutlinedButton}" Margin="10" Style="{StaticResource MaterialDesignOutlinedButton}" Margin="0,10,0,10"
HorizontalAlignment="Center"
Content="{Tr MainWindow.RightPart.LoadConfirmations}"
Command="{Binding GetConfirmationsCommand}" Command="{Binding GetConfirmationsCommand}"
Width="{Binding Path=ActualWidth, Converter={StaticResource CoefficientConverter}, ConverterParameter=0.6, RelativeSource={RelativeSource AncestorType=md:Card}}"> Width="{Binding Path=ActualWidth, Converter={StaticResource CoefficientConverter}, ConverterParameter=0.6, RelativeSource={RelativeSource AncestorType=md:Card}}" />
<TextBlock TextWrapping="Wrap" TextTrimming="WordEllipsis"
Text="{Tr MainWindow.RightPart.LoadConfirmations}" />
</Button>
<ItemsControl md:RippleAssist.IsDisabled="True" Focusable="False" Grid.Row="2" <ItemsControl md:RippleAssist.IsDisabled="True" Focusable="False" Grid.Row="2"
w:FontScaleWindow.Scale="0.72" w:FontScaleWindow.ResizeFont="True" w:FontScaleWindow.Scale="0.72" w:FontScaleWindow.ResizeFont="True"
Margin="10"
Margin="10,10,10,10"
Visibility="{Binding ConfirmationsVisible, Converter={StaticResource BooleanToVisibilityConverter}}" Visibility="{Binding ConfirmationsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"
ItemsSource="{Binding Confirmations}" /> ItemsSource="{Binding Confirmations}" Grid.RowSpan="2" />
<Button Grid.Row="3" Margin="1,0,1,3" w:FontScaleWindow.Scale="0.8" <Button Style="{StaticResource MaterialDesignFlatButton}" Grid.Row="4"
w:FontScaleWindow.ResizeFont="True" Command="{Binding ConfirmLoginCommand}" w:FontScaleWindow.Scale="0.8" w:FontScaleWindow.ResizeFont="True"
Command="{Binding ConfirmLoginCommand}"
Content="{Tr MainWindow.RightPart.ConfirmLogin}" /> Content="{Tr MainWindow.RightPart.ConfirmLogin}" />
</Grid> </Grid>
</md:Card> </md:Card>
</Grid> </Grid>
<Border Grid.Row="2" BorderBrush="#FF696969"> <Border Grid.Row="2" BorderBrush="#1e1e24">
<Border.Background> <Border.Background>
<SolidColorBrush Opacity="0.5" Color="DimGray" /> <SolidColorBrush Opacity="1" Color="{DynamicResource Base300Color}" />
</Border.Background> </Border.Background>
<Grid> <Grid>
<ToolBarPanel Orientation="Horizontal" Margin="5" w:FontScaleWindow.Scale="0.73"
<ToolBarPanel TextElement.Foreground="{DynamicResource SecondaryContentBrush}"
Orientation="Horizontal" Margin="5" w:FontScaleWindow.Scale="0.73"
w:FontScaleWindow.ResizeFont="True"> w:FontScaleWindow.ResizeFont="True">
<TextBlock FontWeight="Bold" Text="{Tr MainWindow.Footer.Account}" /> <TextBlock FontWeight="Bold" Text="{Tr MainWindow.Footer.Account}" />
<TextBlock Text="{Binding SelectedMafile.AccountName}" /> <TextBlock Text="{Binding SelectedMafile.AccountName}" />
@@ -382,19 +420,21 @@
<TextBlock FontWeight="Bold" Text="{Tr MainWindow.Footer.Group}" /> <TextBlock FontWeight="Bold" Text="{Tr MainWindow.Footer.Group}" />
<TextBlock FontWeight="Normal" <TextBlock FontWeight="Normal"
Text="{Binding SelectedMafile.Group, TargetNullValue=' ', FallbackValue=' ', Mode=OneWay}" /> Text="{Binding SelectedMafile.Group, TargetNullValue=' ', FallbackValue=' ', Mode=OneWay}" />
<!--<Button Command="{Binding DebugCommand}">Debug</Button>-->
</ToolBarPanel> </ToolBarPanel>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Right" w:FontScaleWindow.Scale="0.7" <TextBlock VerticalAlignment="Center" HorizontalAlignment="Right" w:FontScaleWindow.Scale="0.7"
w:FontScaleWindow.ResizeFont="True" Margin="0,0,10,0"> w:FontScaleWindow.ResizeFont="True" Margin="0,0,10,0">
<Hyperlink <Hyperlink
NavigateUri="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies" NavigateUri="https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies"
Foreground="{DynamicResource PrimaryHueMidBrush}"
RequestNavigate="Hyperlink_OnRequestNavigate"> Foreground="{DynamicResource AccentBrush}"
Command="{Binding OpenLinksViewCommand}">
by achies by achies
</Hyperlink> </Hyperlink>
</TextBlock> </TextBlock>
</Grid> </Grid>
</Border> </Border>
<md:Snackbar Grid.Row="1" FontSize="20" MessageQueue="{Binding MessageQueue}" /> <md:Snackbar Grid.Row="1" MessageQueue="{Binding MessageQueue}" />
</Grid> </Grid>
</md:DialogHost> </md:DialogHost>
</Border> </Border>
@@ -1,10 +1,8 @@
using System; using System;
using System.Diagnostics;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Navigation;
using System.Windows.Threading; using System.Windows.Threading;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using NebulaAuth.Core; using NebulaAuth.Core;
@@ -30,10 +28,13 @@ public partial class MainWindow
private async void OnApplicationStarted(object? sender, EventArgs e) private async void OnApplicationStarted(object? sender, EventArgs e)
{ {
((MainVM) DataContext).CurrentDialogHost = DialogHostInstance;
if (Settings.Instance.IsPasswordSet == false) return; if (Settings.Instance.IsPasswordSet == false) return;
await Dispatcher.BeginInvoke(ShowSetPasswordDialog, DispatcherPriority.ContextIdle); Topmost = false;
await Dispatcher.InvokeAsync(ShowSetPasswordDialog, DispatcherPriority.ContextIdle);
} }
private async Task ShowSetPasswordDialog() private async Task ShowSetPasswordDialog()
{ {
var vm = new SetEncryptPasswordVM(); var vm = new SetEncryptPasswordVM();
@@ -41,6 +42,7 @@ public partial class MainWindow
{ {
DataContext = vm DataContext = vm
}; };
var result = await DialogHost.Show(dialog); var result = await DialogHost.Show(dialog);
var pass = vm.Password; var pass = vm.Password;
if (result is true && string.IsNullOrWhiteSpace(pass) == false) if (result is true && string.IsNullOrWhiteSpace(pass) == false)
@@ -49,15 +51,6 @@ public partial class MainWindow
} }
} }
private void Hyperlink_OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.ToString())
{
UseShellExecute = true
});
}
#region Dran'n'Drop #region Dran'n'Drop
private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e) private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
@@ -2,7 +2,7 @@
using NebulaAuth.Model.MAAC; using NebulaAuth.Model.MAAC;
using Newtonsoft.Json; using Newtonsoft.Json;
using SteamLib; using SteamLib;
using SteamLib.Account; using SteamLibForked.Models.Session;
namespace NebulaAuth.Model.Entities; namespace NebulaAuth.Model.Entities;
@@ -8,7 +8,6 @@ using System.Threading.Tasks;
using AchiesUtilities.Extensions; using AchiesUtilities.Extensions;
using NebulaAuth.Core; using NebulaAuth.Core;
using NebulaAuth.Model.Entities; using NebulaAuth.Model.Entities;
using NLog;
namespace NebulaAuth.Model.MAAC; namespace NebulaAuth.Model.MAAC;
@@ -16,6 +15,8 @@ public static class MultiAccountAutoConfirmer
{ {
private const string LOC_PATH = "MAAC"; private const string LOC_PATH = "MAAC";
private static readonly ReaderWriterLockSlim Lock = new(); private static readonly ReaderWriterLockSlim Lock = new();
private static readonly SemaphoreSlim ExecutionLock = new(1, 1);
public static ObservableCollection<Mafile> Clients { get; } public static ObservableCollection<Mafile> Clients { get; }
private static Timer Timer { get; } private static Timer Timer { get; }
@@ -27,12 +28,10 @@ public static class MultiAccountAutoConfirmer
UpdateTimer(); UpdateTimer();
} }
private static readonly SemaphoreSlim ExecutionLock = new(1, 1);
// ReSharper disable once AsyncVoidMethod //Already safe // ReSharper disable once AsyncVoidMethod //Already safe
private static async void TimerConfirm(object? state) private static async void TimerConfirm(object? state)
{ {
bool isHeld = false; var isHeld = false;
try try
{ {
isHeld = await ExecutionLock.WaitAsync(0); isHeld = await ExecutionLock.WaitAsync(0);
@@ -41,8 +40,8 @@ public static class MultiAccountAutoConfirmer
SnackbarController.SendSnackbar(GetLocalization("TimerPreventedOverlap")); SnackbarController.SendSnackbar(GetLocalization("TimerPreventedOverlap"));
return; return;
} }
await TimerConfirmInternal();
await TimerConfirmInternal();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -60,7 +59,7 @@ public static class MultiAccountAutoConfirmer
private static async Task TimerConfirmInternal() private static async Task TimerConfirmInternal()
{ {
var clients = Lock.ReadLock(() => Clients.ToArray()); var clients = Lock.ReadLock(() => Clients.ToArray());
var enabledClients = clients.Where(x => x.LinkedClient is { IsError: false }).ToArray(); var enabledClients = clients.Where(x => x.LinkedClient is {IsError: false}).ToArray();
enabledClients = DistributeEvenly(enabledClients).ToArray(); enabledClients = DistributeEvenly(enabledClients).ToArray();
var confirmed = 0; var confirmed = 0;
await Task.Run(async () => await Task.Run(async () =>
@@ -14,10 +14,10 @@ using NebulaAuth.Model.Entities;
using NebulaAuth.Utility; using NebulaAuth.Utility;
using SteamLib.Api.Mobile; using SteamLib.Api.Mobile;
using SteamLib.Authentication; using SteamLib.Authentication;
using SteamLib.Core.Interfaces; using SteamLib.Exceptions.Authorization;
using SteamLib.Exceptions;
using SteamLib.SteamMobile.Confirmations; using SteamLib.SteamMobile.Confirmations;
using SteamLib.Web; using SteamLib.Web;
using SteamLibForked.Abstractions;
namespace NebulaAuth.Model.MAAC; namespace NebulaAuth.Model.MAAC;
@@ -90,7 +90,8 @@ public partial class PortableMaClient : ObservableObject, IDisposable
var toConfirm = new List<Confirmation>(); var toConfirm = new List<Confirmation>();
if (AutoConfirmMarket) if (AutoConfirmMarket)
{ {
var market = conf.Where(c => c.ConfType == ConfirmationType.MarketSellTransaction); var market = conf.Where(c =>
c.ConfType is ConfirmationType.MarketSellTransaction or ConfirmationType.Purchase);
toConfirm.AddRange(market); toConfirm.AddRange(market);
} }
@@ -7,9 +7,9 @@ using AchiesUtilities.Web.Models;
using AchiesUtilities.Web.Proxy; using AchiesUtilities.Web.Proxy;
using NebulaAuth.Model.Entities; using NebulaAuth.Model.Entities;
using SteamLib.Api.Mobile; using SteamLib.Api.Mobile;
using SteamLib.Api.Services;
using SteamLib.Authentication; using SteamLib.Authentication;
using SteamLib.Core.Interfaces; using SteamLib.Exceptions.Authorization;
using SteamLib.Exceptions;
using SteamLib.ProtoCore.Services; using SteamLib.ProtoCore.Services;
using SteamLib.SteamMobile.Confirmations; using SteamLib.SteamMobile.Confirmations;
using SteamLib.Web; using SteamLib.Web;
@@ -26,6 +26,7 @@ public static class MaClient
public static ProxyData? DefaultProxy { get; set; } public static ProxyData? DefaultProxy { get; set; }
static MaClient() static MaClient()
{ {
Proxy = new DynamicProxy(); Proxy = new DynamicProxy();
@@ -62,7 +63,7 @@ public static class MaClient
return SteamMobileConfirmationsApi.GetConfirmations(Client, mafile, mafile.SessionData!.SteamId); return SteamMobileConfirmationsApi.GetConfirmations(Client, mafile, mafile.SessionData!.SteamId);
} }
public static Task LoginAgain(Mafile mafile, string password, bool savePassword, ICaptchaResolver? resolver) public static Task LoginAgain(Mafile mafile, string password, bool savePassword)
{ {
SetProxy(mafile); SetProxy(mafile);
return SessionHandler.LoginAgain(new HttpClientHandlerPair(Client, ClientHandler), mafile, password, return SessionHandler.LoginAgain(new HttpClientHandlerPair(Client, ClientHandler), mafile, password,
@@ -137,7 +138,7 @@ public static class MaClient
ValidateMafile(mafile); ValidateMafile(mafile);
SetProxy(mafile); SetProxy(mafile);
var token = mafile.SessionData!.GetMobileToken()!.Value; var token = mafile.SessionData!.GetMobileToken()!.Value;
var sessions = await SteamMobileAuthenticatorApi.GetAuthSessionsForAccount(Client, token.Token); var sessions = await AuthenticationServiceApi.GetAuthSessionsForAccount(Client, token.Token);
if (sessions.ClientIds.Count == 0) if (sessions.ClientIds.Count == 0)
{ {
@@ -156,16 +157,11 @@ public static class MaClient
} }
var clientId = sessions.ClientIds.Single(); var clientId = sessions.ClientIds.Single();
var clientInfo = await SteamMobileAuthenticatorApi.GetAuthSessionInfo(Client, token.Token, clientId); var clientInfo = await AuthenticationServiceApi.GetAuthSessionInfo(Client, token.Token, clientId);
var updateReq = new UpdateAuthSessionWithMobileConfirmation_Request var updateReq =
{ AuthRequestHelper.CreateMobileConfirmationRequest(1, clientId, mafile.SessionData.SteamId.Steam64,
ClientId = clientId, mafile.SharedSecret);
Confirm = true, await AuthenticationServiceApi.UpdateAuthSessionWithMobileConfirmation(Client, token.Token, updateReq);
Persistence = 1,
Steamid = mafile.SessionData.SteamId.Steam64.ToUlong(),
Version = 1
};
await SteamMobileAuthenticatorApi.UpdateAuthSessionStatus(Client, token.Token, mafile.SharedSecret, updateReq);
return new LoginConfirmationResult return new LoginConfirmationResult
{ {
Country = clientInfo.Country, Country = clientInfo.Country,
@@ -22,6 +22,11 @@ public static class NebulaSerializer
AllowDeviceIdGeneration = true, AllowDeviceIdGeneration = true,
AllowSessionIdGeneration = true, AllowSessionIdGeneration = true,
ThrowIfInvalidSteamId = false ThrowIfInvalidSteamId = false
},
SerializationOptions =
{
Sign = true,
Formatting = Formatting.Indented
} }
}); });
} }
@@ -80,9 +85,9 @@ public static class NebulaSerializer
{ {
if (Settings.Instance.LegacyMode) if (Settings.Instance.LegacyMode)
{ {
return MafileSerializer.SerializeLegacy(data, Formatting.Indented, properties); return MafileSerializer.SerializeLegacy(data, Serializer.Settings.SerializationOptions, properties);
} }
return MafileSerializer.Serialize(data); return Serializer.Serialize(data);
} }
} }
@@ -7,7 +7,7 @@ using MaterialDesignThemes.Wpf;
using NebulaAuth.Core; using NebulaAuth.Core;
using NebulaAuth.Model.Entities; using NebulaAuth.Model.Entities;
using NebulaAuth.View.Dialogs; using NebulaAuth.View.Dialogs;
using SteamLib.Exceptions; using SteamLib.Exceptions.Authorization;
namespace NebulaAuth.Model; namespace NebulaAuth.Model;
@@ -1,12 +1,13 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using AchiesUtilities.Web.Models; using AchiesUtilities.Web.Models;
using NebulaAuth.Model.Entities; using NebulaAuth.Model.Entities;
using SteamLib.Account;
using SteamLib.Api.Mobile; using SteamLib.Api.Mobile;
using SteamLib.Authentication; using SteamLib.Authentication;
using SteamLib.Authentication.LoginV2; using SteamLib.Authentication.LoginV2;
using SteamLib.Exceptions; using SteamLib.Exceptions.Authorization;
using SteamLib.Factory.Helpers;
using SteamLib.SteamMobile; using SteamLib.SteamMobile;
using SteamLibForked.Models.Session;
namespace NebulaAuth.Model; namespace NebulaAuth.Model;
@@ -35,11 +36,11 @@ public partial class SessionHandler //API
public static async Task LoginAgain(HttpClientHandlerPair chp, Mafile mafile, string password, bool savePassword) public static async Task LoginAgain(HttpClientHandlerPair chp, Mafile mafile, string password, bool savePassword)
{ {
var sgGenerator = new SteamGuardCodeGenerator(mafile.SharedSecret); var sgGenerator = new SteamGuardCodeGenerator(mafile.SharedSecret);
var options = new LoginV2ExecutorOptions(LoginV2Executor.NullConsumer, chp.Client) var options = new LoginV2ExecutorOptions(StaticLoginConsumer.Instance, chp.Client)
{ {
Logger = Shell.ExtensionsLogger, Logger = Shell.ExtensionsLogger,
SteamGuardProvider = sgGenerator, AuthProviders = [sgGenerator],
DeviceDetails = LoginV2ExecutorOptions.GetMobileDefaultDevice(), DeviceDetails = DeviceDetailsDefaultBuilder.GetMobileDefaultDevice(),
WebsiteId = "Mobile" WebsiteId = "Mobile"
}; };
chp.Handler.CookieContainer.ClearMobileSessionCookies(); chp.Handler.CookieContainer.ClearMobileSessionCookies();
@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Windows.Media; using System.Windows.Media;
@@ -12,6 +13,15 @@ public partial class Settings : ObservableObject
{ {
public static Settings Instance { get; } public static Settings Instance { get; }
private static IReadOnlyDictionary<ThemeType, string> ThemeNames { get; } = new Dictionary<ThemeType, string>
{
{ThemeType.Default, "DefaultTheme"},
{ThemeType.Light, "LightTheme"},
{ThemeType.Black, "BlackTheme"},
{ThemeType.Luxury, "LuxuryTheme"},
{ThemeType.Shadcn, "ShadcnTheme"}
};
static Settings() static Settings()
{ {
if (File.Exists("settings.json") == false) if (File.Exists("settings.json") == false)
@@ -40,6 +50,10 @@ public partial class Settings : ObservableObject
private static void SettingsOnPropertyChanged(object? sender, PropertyChangedEventArgs e) private static void SettingsOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{ {
Save(); Save();
if (e.PropertyName == nameof(ThemeType))
{
ThemeManager.ApplyTheme(ThemeNames[Instance.ThemeType]);
}
} }
public static void Save() public static void Save()
@@ -48,12 +62,27 @@ public partial class Settings : ObservableObject
File.WriteAllText("settings.json", json); File.WriteAllText("settings.json", json);
} }
public static void ResetThemeDefaults()
{
Instance.BackgroundBlur = 0.0;
Instance.BackgroundOpacity = 1.0;
Instance.BackgroundGamma = 0.0;
Instance.LeftOpacity = 0.4;
Instance.RightOpacity = 0.8;
Instance.ApplyBlurBackground = true;
Save();
}
public string GetTheme()
{
return ThemeNames.TryGetValue(ThemeType, out var themeName) ? themeName : ThemeNames[ThemeType.Default];
}
#region Properties #region Properties
[ObservableProperty] private BackgroundMode _backgroundMode = BackgroundMode.Default;
[ObservableProperty] private bool _hideToTray; [ObservableProperty] private bool _hideToTray;
[ObservableProperty] private int _timerSeconds = 60; [ObservableProperty] private int _timerSeconds = 60;
[ObservableProperty] private Color? _backgroundColor;
[ObservableProperty] private Color? _iconColor; [ObservableProperty] private Color? _iconColor;
[ObservableProperty] private bool _isPasswordSet; [ObservableProperty] private bool _isPasswordSet;
[ObservableProperty] private LocalizationLanguage _language = LocalizationLanguage.English; [ObservableProperty] private LocalizationLanguage _language = LocalizationLanguage.English;
@@ -62,6 +91,15 @@ public partial class Settings : ObservableObject
[ObservableProperty] private bool _useAccountNameAsMafileName; [ObservableProperty] private bool _useAccountNameAsMafileName;
[ObservableProperty] private bool _ignorePatchTuesdayErrors; [ObservableProperty] private bool _ignorePatchTuesdayErrors;
[ObservableProperty] private BackgroundMode _backgroundMode = BackgroundMode.Default;
[ObservableProperty] private double _leftOpacity = 0.4;
[ObservableProperty] private double _rightOpacity = 1.0;
[ObservableProperty] private double _backgroundBlur;
[ObservableProperty] private double _backgroundOpacity = 1;
[ObservableProperty] private double _backgroundGamma;
[ObservableProperty] private bool _applyBlurBackground = true;
[ObservableProperty] private ThemeType _themeType = ThemeType.Default;
#endregion #endregion
} }
@@ -71,3 +109,12 @@ public enum BackgroundMode
Custom, Custom,
Color Color
} }
public enum ThemeType
{
Default = 0,
Black = 1,
Light = 2,
Luxury = 3,
Shadcn = 4
}
@@ -11,11 +11,12 @@ namespace NebulaAuth.Model;
public static class Shell public static class Shell
{ {
public static Logger Logger { get; } = LogManager.GetLogger("Logger"); public static Logger Logger { get; private set; } = null!;
public static ILogger ExtensionsLogger { get; private set; } = null!; public static ILogger ExtensionsLogger { get; private set; } = null!;
public static void Initialize() public static void Initialize()
{ {
Logger = LogManager.GetLogger("Logger");
var lp = new NLogLoggerProvider(); var lp = new NLogLoggerProvider();
var logger = lp.CreateLogger("SteamLib"); var logger = lp.CreateLogger("SteamLib");
SteamLibErrorMonitor.MonitorLogger = logger; SteamLibErrorMonitor.MonitorLogger = logger;
@@ -39,5 +40,6 @@ public static class Shell
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e) private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{ {
Logger.Fatal((Exception) e.ExceptionObject); Logger.Fatal((Exception) e.ExceptionObject);
LogManager.Shutdown();
} }
} }
@@ -1,71 +1,84 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AchiesUtilities.Extensions; using AchiesUtilities.Extensions;
using NebulaAuth.Model.Entities; using NebulaAuth.Model.Entities;
using NebulaAuth.Model.Exceptions; using NebulaAuth.Model.Exceptions;
using SteamLib.Core.Models; using SteamLib;
using SteamLib.Exceptions; using SteamLib.Exceptions.Authorization;
using SteamLib.SteamMobile; using SteamLib.SteamMobile;
namespace NebulaAuth.Model; namespace NebulaAuth.Model;
//RETHINK
public static class Storage public static class Storage
{ {
public const string MAFILE_F = "maFiles"; public const string MAFILE_F = "maFiles";
public const string REMOVED_F = "maFiles_removed"; public const string REMOVED_F = "maFiles_removed";
private static int _duplicateFound;
public static readonly int DuplicateFound; public static int DuplicateFound => _duplicateFound;
public static string MafileFolder { get; } = Path.GetFullPath(MAFILE_F); public static string MafileFolder { get; } = Path.GetFullPath(MAFILE_F);
public static string RemovedMafileFolder { get; } = Path.GetFullPath(REMOVED_F); public static string RemovedMafileFolder { get; } = Path.GetFullPath(REMOVED_F);
public static ObservableCollection<Mafile> MaFiles { get; } = new(); public static ObservableCollection<Mafile> MaFiles { get; private set; } = new();
static Storage() static Storage()
{ {
if (Directory.Exists(MafileFolder) == false) }
public static async Task Initialize(int threadCount, CancellationToken token = default)
{ {
if (!Directory.Exists(MafileFolder))
Directory.CreateDirectory(MafileFolder); Directory.CreateDirectory(MafileFolder);
}
if (Directory.Exists(RemovedMafileFolder) == false) if (!Directory.Exists(RemovedMafileFolder))
{
Directory.CreateDirectory(RemovedMafileFolder); Directory.CreateDirectory(RemovedMafileFolder);
}
var files = Directory.GetFiles(MafileFolder);
var hashNames = new HashSet<string>();
var hashIds = new HashSet<SteamId>();
var comparer = new MafileNameComparer(Settings.Instance.UseAccountNameAsMafileName); var comparer = new MafileNameComparer(Settings.Instance.UseAccountNameAsMafileName);
var ordered = files.Order(comparer).ToList(); var files = Directory
foreach (var file in ordered.Where(file => Path.GetExtension(file).EqualsIgnoreCase(".mafile"))) .GetFiles(MafileFolder)
.Where(file => Path.GetExtension(file).EqualsIgnoreCase(".mafile"))
.Order(comparer)
.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,
new ParallelOptions {CancellationToken = token, MaxDegreeOfParallelism = threadCount},
async (file, ct) =>
{ {
try try
{ {
var data = ReadMafile(file); var data = await ReadMafileAsync(file);
if (hashNames.Contains(data.AccountName) || hashIds.Contains(data.SteamId)) if (!hashNames.TryAdd(data.AccountName, 0) ||
(data.SessionData != null && !hashIds.TryAdd(data.SteamId, 0)))
{ {
DuplicateFound++; Interlocked.Increment(ref _duplicateFound);
Shell.Logger.Error("Duplicate mafile {file}", Path.GetFileName(file)); Shell.Logger.Error("Duplicate mafile {file}", Path.GetFileName(file));
continue;
} }
hashNames.Add(data.AccountName); localList.Add(data);
if (data.SessionData != null) hashIds.Add(data.SteamId);
MaFiles.Add(data);
} }
catch (Exception ex) catch (Exception ex)
{ {
Shell.Logger.Error(ex, "Can't load mafile {file}", Path.GetFileName(file)); Shell.Logger.Error(ex, "Can't load mafile {file}", Path.GetFileName(file));
} }
} });
}, token);
MaFiles = new ObservableCollection<Mafile>(MaFiles.OrderBy(m => m.AccountName)); MaFiles = new ObservableCollection<Mafile>(localList.OrderBy(m => m.AccountName));
} }
/// <summary> /// <summary>
@@ -117,6 +130,12 @@ public static class Storage
return NebulaSerializer.Deserialize(str); return NebulaSerializer.Deserialize(str);
} }
public static async Task<Mafile> ReadMafileAsync(string path)
{
var str = await File.ReadAllTextAsync(path);
return NebulaSerializer.Deserialize(str);
}
public static void SaveMafile(Mafile data) public static void SaveMafile(Mafile data)
{ {
var path = CreatePathForMafile(data); var path = CreatePathForMafile(data);
@@ -202,9 +221,35 @@ public static class Storage
return null; return null;
} }
public static void BackupHandler(MobileDataExtended data)
{
if (Directory.Exists("mafiles_backup") == false)
{
Directory.CreateDirectory("mafiles_backup");
}
var json = NebulaSerializer.SerializeMafile(data, null);
File.WriteAllText(Path.Combine("mafiles_backup", data.AccountName + ".mafile"),
json);
}
public static void BackupHandlerStr(string accountName, string data)
{
if (Directory.Exists("mafiles_backup") == false)
{
Directory.CreateDirectory("mafiles_backup");
}
File.WriteAllText(Path.Combine("mafiles_backup", accountName + ".mafile"),
data);
}
} }
//TODO: Refactor //TODO: Refactor
//TODO: use numeric orderer when .net 10 released
internal class MafileNameComparer : IComparer<string> internal class MafileNameComparer : IComparer<string>
{ {
private const string MAF_64_START = "765"; private const string MAF_64_START = "765";
@@ -16,7 +16,6 @@
</layout> </layout>
<layout type='SimpleLayout' text="," /> <layout type='SimpleLayout' text="," />
</layout> </layout>
</target> </target>
</targets> </targets>
+68
View File
@@ -0,0 +1,68 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows7.0</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<LangVersion>latest</LangVersion>
<SatelliteResourceLanguages>en;ru;ua</SatelliteResourceLanguages>
<ApplicationIcon>Theme\lock.ico</ApplicationIcon>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
<AssemblyVersion>1.7.1</AssemblyVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Remove="Theme\1140x641.jpg" />
<None Remove="Theme\Background.png" />
<None Remove="Theme\lock.ico" />
<None Remove="Theme\SplashScreen.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.9.2" />
<PackageReference Include="CodingSeb.Localization.JsonFileLoader" Version="1.4.0" />
<PackageReference Include="CodingSeb.Localization.WPF" Version="1.4.1" />
<PackageReference Include="CodingSebLocalization.Fody" Version="1.4.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="MaterialDesignThemes" Version="5.2.1" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
<PackageReference Include="NLog" Version="5.5.0" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.5.0" />
</ItemGroup>
<ItemGroup>
<Resource Include="Theme\Background.png">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Resource>
<Resource Include="Theme\Background_Old.jpg">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Resource>
<Resource Include="Theme\lock.ico" />
<Resource Include="Theme\SplashScreen.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SteamLibForked\SteamLibForked.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="View\Dialogs\LoginAgainOnImportDialog.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="localization.loc.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,6 @@
<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>
@@ -7,3 +7,8 @@
• Функция переноса гуарда с мобильного устройства на ПК с созданием мафайла • Функция переноса гуарда с мобильного устройства на ПК с созданием мафайла
• Добавить полное шифрование мафайлов по аналогии с SDA • Добавить полное шифрование мафайлов по аналогии с SDA
• Сделать автоматический билд и загрузку обновления на Github при изменении в мастер ветке • Сделать автоматический билд и загрузку обновления на Github при изменении в мастер ветке
• Антик-окно как в MarketApp, возможность сразу открыть браузер напр. на CefSharp с залогиненым аккаунтом
• Стабильность авто-подтверждений, возможность задать пользователю порог времени когда льются ошибки и только тогда выключать авто-подтверждение
• Безопасное сохранение мафайлов через .tmp / .bak
• Создание механизма накопления ошибок, чтобы исправить Patch Tuesday, условно аккаунт может игнорировать ошибки 1-2 часа (настриавемо)
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Before

Width:  |  Height:  |  Size: 713 KiB

After

Width:  |  Height:  |  Size: 713 KiB

+92
View File
@@ -0,0 +1,92 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource PrimaryColor}" />
<SolidColorBrush x:Key="PrimaryContentBrush" Color="{StaticResource PrimaryContentColor}" />
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource SecondaryColor}" />
<SolidColorBrush x:Key="SecondaryContentBrush" Color="{StaticResource SecondaryContentColor}" />
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}" />
<SolidColorBrush x:Key="AccentContentBrush" Color="{StaticResource AccentContentColor}" />
<SolidColorBrush x:Key="NeutralBrush" Color="{StaticResource NeutralColor}" />
<SolidColorBrush x:Key="NeutralContentBrush" Color="{StaticResource NeutralContentColor}" />
<SolidColorBrush x:Key="Base100Brush" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="Base200Brush" Color="{StaticResource Base200Color}" />
<SolidColorBrush x:Key="Base300Brush" Color="{StaticResource Base300Color}" />
<!--BackgroundBrush conflict-->
<SolidColorBrush x:Key="BaseContentBrush" Color="{StaticResource BaseContentColor}" />
<SolidColorBrush x:Key="BaseShadowBrush" Color="{StaticResource BaseShadowColor}" />
<SolidColorBrush x:Key="InfoBrush" Color="{StaticResource InfoColor}" />
<SolidColorBrush x:Key="InfoContentBrush" Color="{StaticResource InfoContentColor}" />
<SolidColorBrush x:Key="SuccessBrush" Color="{StaticResource SuccessColor}" />
<SolidColorBrush x:Key="SuccessContentBrush" Color="{StaticResource SuccessContentColor}" />
<SolidColorBrush x:Key="WarningBrush" Color="{StaticResource WarningColor}" />
<SolidColorBrush x:Key="WarningContentBrush" Color="{StaticResource WarningContentColor}" />
<SolidColorBrush x:Key="ErrorBrush" Color="{StaticResource ErrorColor}" />
<SolidColorBrush x:Key="ErrorContentBrush" Color="{StaticResource ErrorContentColor}" />
<SolidColorBrush x:Key="MainBackgroundBrush" Color="{StaticResource NeutralColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Primary.Light" Color="{StaticResource PrimaryColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Primary.Light.Foreground" Color="{StaticResource BaseContentColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Primary" Color="{StaticResource PrimaryColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Primary.Foreground" Color="{StaticResource BaseContentColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Primary.Dark" Color="{StaticResource PrimaryColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Primary.Dark.Foreground" Color="{StaticResource PrimaryContentColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Background" Color="{StaticResource Base200Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Foreground" Color="{StaticResource BaseContentColor}" />
<!--Unchecked checkbox uses it-->
<SolidColorBrush x:Key="MaterialDesign.Brush.ForegroundLight" Color="{StaticResource PrimaryColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ToolBar.Item.Background" Color="{StaticResource PrimaryColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ToolBar.Item.Foreground" Color="{StaticResource BaseContentColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Card.Background" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.TextBox.HoverBorder" Color="{StaticResource BaseContentColor}"
Opacity="0.4" />
<SolidColorBrush x:Key="MaterialDesign.Brush.TextBox.Border" Color="{StaticResource BaseContentColor}"
Opacity="0.4" />
<SolidColorBrush x:Key="MaterialDesign.Brush.TextBox.OutlineBorder" Color="{StaticResource BaseContentColor}"
Opacity="0.4" />
<SolidColorBrush x:Key="MaterialDesign.Brush.TextBox.DisabledBackground" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.TextBox.FilledBackground" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.TextBox.HoverBackground" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.TextBox.OutlineInactiveBorder" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.SnackBar.Background" Color="{StaticResource BaseContentColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.Disabled" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.FilledBackground" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.HoverBackground" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.OutlineInactiveBorder"
Color="{StaticResource BaseContentColor}" Opacity="0.4" />
<!--<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.Popup.DarkBackground" Color="Red" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.Popup.DarkForeground" Color="Red" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.Popup.LightBackground" Color="Red" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.Popup.LightForeground" Color="Red" />-->
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.HoverBorder" Color="{StaticResource BaseContentColor}"
Opacity="0.4" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.Border" Color="{StaticResource BaseContentColor}"
Opacity="0.4" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ComboBox.OutlineBorder" Color="{StaticResource BaseContentColor}"
Opacity="0.4" />
<SolidColorBrush x:Key="MaterialDesign.Brush.ToolBar.Background" Color="{StaticResource Base100Color}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Secondary.Light"
Color="{StaticResource SecondaryColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Secondary.Light.Foreground"
Color="{StaticResource BaseContentColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Secondary" Color="{StaticResource SecondaryColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Secondary.Foreground"
Color="{StaticResource BaseContentColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Secondary.Dark"
Color="{StaticResource SecondaryColor}" />
<SolidColorBrush x:Key="MaterialDesign.Brush.Secondary.Dark.Foreground"
Color="{StaticResource BaseContentColor}" />
</ResourceDictionary>
@@ -0,0 +1,58 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace NebulaAuth.Theme.Controls;
public class CodeProgressBar : ProgressBar
{
public static readonly DependencyProperty TimeRemainingProperty =
DependencyProperty.Register(nameof(TimeRemaining), typeof(double), typeof(CodeProgressBar),
new PropertyMetadata(-1.0, OnTimeRemainingChanged));
public static readonly DependencyProperty MaxTimeProperty =
DependencyProperty.Register(nameof(MaxTime), typeof(double), typeof(CodeProgressBar),
new PropertyMetadata(30.0)); // По умолчанию 30 сек
public double TimeRemaining
{
get => (double) GetValue(TimeRemainingProperty);
set => SetValue(TimeRemainingProperty, value);
}
public double MaxTime
{
get => (double) GetValue(MaxTimeProperty);
set => SetValue(MaxTimeProperty, value);
}
private static void OnTimeRemainingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is CodeProgressBar progressBar)
{
var newValue = (double) e.NewValue;
progressBar.StartProgressAnimation(newValue);
}
}
private void StartProgressAnimation(double timeRemaining)
{
if (timeRemaining <= 0 || MaxTime <= 0) return;
var progress = (1 - timeRemaining / MaxTime) * 100;
Value = 0;
Value = 100;
var animation = new DoubleAnimation
{
From = progress,
To = 100,
Duration = TimeSpan.FromSeconds(timeRemaining),
AccelerationRatio = 0,
DecelerationRatio = 0
};
BeginAnimation(ValueProperty, animation);
}
}

Some files were not shown because too many files have changed in this diff Show More