mirror of
https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies.git
synced 2026-07-25 06:14:31 +00:00
feat(proxy): add bulk assignment and proxy usage stats
- Implement bulk proxy assignment for accounts with flexible input (login:proxy, login:ID, or login) - Add UI for mass assignment with counters and behavior selection for unspecified proxies - Show assigned account count badges in proxy list for better visibility - Enable quick assignment of a free proxy to an account - Ensure fast and consistent proxy assignment state via in-memory cache - Perform ReSharper cleanup
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
<converters:BoolToMafileNamingConverter x:Key="BoolToMafileNamingConverter" />
|
<converters:BoolToMafileNamingConverter x:Key="BoolToMafileNamingConverter" />
|
||||||
<converters:BoolToValueConverter x:Key="BoolToValueConverter" />
|
<converters:BoolToValueConverter x:Key="BoolToValueConverter" />
|
||||||
<converters:NullableToBooleanConverter x:Key="NullableToBooleanConverter" />
|
<converters:NullableToBooleanConverter x:Key="NullableToBooleanConverter" />
|
||||||
|
<converters:ProxyAccountCountConverter x:Key="ProxyAccountCountConverter" />
|
||||||
<!-- Background converters-->
|
<!-- Background converters-->
|
||||||
<background:BackgroundImageVisibleConverter x:Key="BackgroundImageVisibleConverter" />
|
<background:BackgroundImageVisibleConverter x:Key="BackgroundImageVisibleConverter" />
|
||||||
<background:BackgroundSourceConverter x:Key="BackgroundSourceConverter" />
|
<background:BackgroundSourceConverter x:Key="BackgroundSourceConverter" />
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using NebulaAuth.Model;
|
||||||
|
|
||||||
|
namespace NebulaAuth.Converters;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a proxy ID (int) to the number of accounts assigned to it.
|
||||||
|
/// Returns empty string when count is zero so the badge is invisible.
|
||||||
|
/// </summary>
|
||||||
|
public class ProxyAccountCountConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is not int proxyId) return string.Empty;
|
||||||
|
var count = ProxyAssignmentCache.GetAccountCount(proxyId);
|
||||||
|
return count > 0 ? count.ToString() : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,26 @@
|
|||||||
"es": "El proxy tiene un formato incorrecto",
|
"es": "El proxy tiene un formato incorrecto",
|
||||||
"tr": "Proxy yanlış formatta",
|
"tr": "Proxy yanlış formatta",
|
||||||
"kk": "Прокси қате форматта"
|
"kk": "Прокси қате форматта"
|
||||||
|
},
|
||||||
|
"AssignmentSuccess": {
|
||||||
|
"ru": "Прокси назначены: {0}",
|
||||||
|
"en": "Assigned: {0}",
|
||||||
|
"zh": "已分配:{0}",
|
||||||
|
"uk": "Проксі призначено: {0}",
|
||||||
|
"fr": "Attribués: {0}",
|
||||||
|
"es": "Asignados: {0}",
|
||||||
|
"tr": "Atandı: {0}",
|
||||||
|
"kk": "Тағайындалды: {0}"
|
||||||
|
},
|
||||||
|
"AssignmentPartialSuccess": {
|
||||||
|
"ru": "Назначено: {0}, не найдено: {1}, ошибки: {2}",
|
||||||
|
"en": "Assigned: {0}, not found: {1}, errors: {2}",
|
||||||
|
"zh": "已分配:{0},未找到:{1},错误:{2}",
|
||||||
|
"uk": "Призначено: {0}, не знайдено: {1}, помилки: {2}",
|
||||||
|
"fr": "Attribués: {0}, non trouvés: {1}, erreurs: {2}",
|
||||||
|
"es": "Asignados: {0}, no encontrados: {1}, errores: {2}",
|
||||||
|
"tr": "Atandı: {0}, bulunamadı: {1}, hatalar: {2}",
|
||||||
|
"kk": "Тағайындалды: {0}, табылмады: {1}, қателер: {2}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -93,6 +113,96 @@
|
|||||||
"es": "Mostrar credenciales",
|
"es": "Mostrar credenciales",
|
||||||
"tr": "Kimlik bilgilerini göster",
|
"tr": "Kimlik bilgilerini göster",
|
||||||
"kk": "Тіркелгі деректерін көрсету"
|
"kk": "Тіркелгі деректерін көрсету"
|
||||||
|
},
|
||||||
|
"TabManager": {
|
||||||
|
"en": "Manager",
|
||||||
|
"ru": "Менеджер",
|
||||||
|
"zh": "管理",
|
||||||
|
"uk": "Менеджер",
|
||||||
|
"fr": "Gestionnaire",
|
||||||
|
"es": "Administrador",
|
||||||
|
"tr": "Yönetici",
|
||||||
|
"kk": "Менеджер"
|
||||||
|
},
|
||||||
|
"TabAssignment": {
|
||||||
|
"en": "Assignment",
|
||||||
|
"ru": "Назначение",
|
||||||
|
"zh": "分配",
|
||||||
|
"uk": "Призначення",
|
||||||
|
"fr": "Attribution",
|
||||||
|
"es": "Asignación",
|
||||||
|
"tr": "Atama",
|
||||||
|
"kk": "Тағайындау"
|
||||||
|
},
|
||||||
|
"TotalAccounts": {
|
||||||
|
"en": "Accounts",
|
||||||
|
"ru": "Аккаунтов",
|
||||||
|
"zh": "账户",
|
||||||
|
"uk": "Акаунтів",
|
||||||
|
"fr": "Comptes",
|
||||||
|
"es": "Cuentas",
|
||||||
|
"tr": "Hesaplar",
|
||||||
|
"kk": "Аккаунттар"
|
||||||
|
},
|
||||||
|
"TotalProxies": {
|
||||||
|
"en": "Proxies",
|
||||||
|
"ru": "Прокси",
|
||||||
|
"zh": "代理",
|
||||||
|
"uk": "Проксі",
|
||||||
|
"fr": "Proxies",
|
||||||
|
"es": "Proxies",
|
||||||
|
"tr": "Proxy'ler",
|
||||||
|
"kk": "Проксилер"
|
||||||
|
},
|
||||||
|
"AssignedProxies": {
|
||||||
|
"en": "Assigned",
|
||||||
|
"ru": "Назначено",
|
||||||
|
"zh": "已分配",
|
||||||
|
"uk": "Призначено",
|
||||||
|
"fr": "Attribués",
|
||||||
|
"es": "Asignados",
|
||||||
|
"tr": "Atandı",
|
||||||
|
"kk": "Тағайындалды"
|
||||||
|
},
|
||||||
|
"UnspecifiedBehavior": {
|
||||||
|
"en": "Unspecified proxy:",
|
||||||
|
"ru": "Не указанный прокси:",
|
||||||
|
"zh": "未指定代理:",
|
||||||
|
"uk": "Не вказаний проксі:",
|
||||||
|
"fr": "Proxy non spécifié:",
|
||||||
|
"es": "Proxy no especificado:",
|
||||||
|
"tr": "Belirtilmemiş proxy:",
|
||||||
|
"kk": "Көрсетілмеген прокси:"
|
||||||
|
},
|
||||||
|
"UnspecifiedRandom": {
|
||||||
|
"en": "Assign random",
|
||||||
|
"ru": "Назначить случайный",
|
||||||
|
"zh": "随机分配",
|
||||||
|
"uk": "Призначити випадковий",
|
||||||
|
"fr": "Attribuer aléatoirement",
|
||||||
|
"es": "Asignar aleatorio",
|
||||||
|
"tr": "Rastgele ata",
|
||||||
|
"kk": "Кездейсоқ тағайындау"
|
||||||
|
},
|
||||||
|
"UnspecifiedRemove": {
|
||||||
|
"en": "Remove proxy",
|
||||||
|
"ru": "Удалить прокси",
|
||||||
|
"zh": "删除代理",
|
||||||
|
"uk": "Видалити проксі",
|
||||||
|
"fr": "Supprimer le proxy",
|
||||||
|
"es": "Eliminar proxy",
|
||||||
|
"tr": "Proxy'yi kaldır",
|
||||||
|
"kk": "Проксиді жою"
|
||||||
|
},
|
||||||
|
"ApplyAssignment": {
|
||||||
|
"en": "Apply",
|
||||||
|
"ru": "Применить",
|
||||||
|
"zh": "应用",
|
||||||
|
"uk": "Застосувати",
|
||||||
|
"fr": "Appliquer",
|
||||||
|
"es": "Aplicar",
|
||||||
|
"tr": "Uygula",
|
||||||
|
"kk": "Қолдану"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -904,6 +904,16 @@
|
|||||||
"es": "Desvincular proxy",
|
"es": "Desvincular proxy",
|
||||||
"tr": "Proxy bağlantısını kaldır",
|
"tr": "Proxy bağlantısını kaldır",
|
||||||
"kk": "Проксиді ажырату"
|
"kk": "Проксиді ажырату"
|
||||||
|
},
|
||||||
|
"AssignFreeProxy": {
|
||||||
|
"en": "Assign free proxy",
|
||||||
|
"ru": "Назначить свободный прокси",
|
||||||
|
"zh": "分配空闲代理",
|
||||||
|
"uk": "Призначити вільний проксі",
|
||||||
|
"fr": "Attribuer un proxy libre",
|
||||||
|
"es": "Asignar proxy libre",
|
||||||
|
"tr": "Serbest proxy ata",
|
||||||
|
"kk": "Бос проксиді тағайындау"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Proxy": {
|
"Proxy": {
|
||||||
|
|||||||
@@ -420,6 +420,9 @@
|
|||||||
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.UnattachProxy}"
|
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.UnattachProxy}"
|
||||||
Command="{Binding RemoveProxyCommand}"
|
Command="{Binding RemoveProxyCommand}"
|
||||||
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||||
|
<MenuItem Header="{Tr MainWindow.ContextMenus.Mafile.AssignFreeProxy}"
|
||||||
|
Command="{Binding AssignFreeProxyCommand}"
|
||||||
|
CommandParameter="{Binding PlacementTarget.SelectedValue, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</FrameworkElement.ContextMenu>
|
</FrameworkElement.ContextMenu>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using AchiesUtilities.Collections;
|
||||||
|
using AchiesUtilities.Web.Proxy;
|
||||||
|
using NebulaAuth.Model.Entities;
|
||||||
|
|
||||||
|
namespace NebulaAuth.Model;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// In-memory cache tracking which proxy IDs are currently assigned to accounts.
|
||||||
|
/// Provides fast lookup for free proxy selection without iterating all mafiles.
|
||||||
|
/// Initialized at startup and kept in sync whenever proxy assignments change.
|
||||||
|
/// </summary>
|
||||||
|
public static class ProxyAssignmentCache
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<int, HashSet<string>> _proxyToAccounts = new();
|
||||||
|
|
||||||
|
public static void Initialize(IEnumerable<Mafile> mafiles)
|
||||||
|
{
|
||||||
|
_proxyToAccounts.Clear();
|
||||||
|
foreach (var maf in mafiles)
|
||||||
|
{
|
||||||
|
if (maf.Proxy != null)
|
||||||
|
AddInternal(maf.Proxy.Id, maf.AccountName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Call whenever a proxy assignment on a mafile changes and is persisted to disk.
|
||||||
|
/// </summary>
|
||||||
|
public static void UpdateAssignment(string? accountName, int? oldProxyId, int? newProxyId)
|
||||||
|
{
|
||||||
|
if (accountName == null) return;
|
||||||
|
if (oldProxyId.HasValue) RemoveInternal(oldProxyId.Value, accountName);
|
||||||
|
if (newProxyId.HasValue) AddInternal(newProxyId.Value, accountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsProxyFree(int proxyId)
|
||||||
|
{
|
||||||
|
return !_proxyToAccounts.TryGetValue(proxyId, out var set) || set.Count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetAccountCount(int proxyId)
|
||||||
|
{
|
||||||
|
return _proxyToAccounts.TryGetValue(proxyId, out var set) ? set.Count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a random free proxy (not the default proxy), or null if none available.
|
||||||
|
/// </summary>
|
||||||
|
public static KeyValuePair<int, ProxyData>? GetRandomFreeProxy(
|
||||||
|
ObservableDictionary<int, ProxyData> proxies,
|
||||||
|
int? excludeDefaultProxyId)
|
||||||
|
{
|
||||||
|
var free = proxies
|
||||||
|
.Where(kvp => kvp.Key != excludeDefaultProxyId && IsProxyFree(kvp.Key))
|
||||||
|
.ToList();
|
||||||
|
if (free.Count == 0) return null;
|
||||||
|
return free[Random.Shared.Next(free.Count)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddInternal(int proxyId, string accountName)
|
||||||
|
{
|
||||||
|
if (!_proxyToAccounts.TryGetValue(proxyId, out var set))
|
||||||
|
{
|
||||||
|
set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
_proxyToAccounts[proxyId] = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
set.Add(accountName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RemoveInternal(int proxyId, string accountName)
|
||||||
|
{
|
||||||
|
if (_proxyToAccounts.TryGetValue(proxyId, out var set))
|
||||||
|
set.Remove(accountName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ public static class Shell
|
|||||||
|
|
||||||
var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1;
|
var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1;
|
||||||
await Storage.Initialize(threads);
|
await Storage.Initialize(threads);
|
||||||
|
ProxyAssignmentCache.Initialize(Storage.MaFiles);
|
||||||
MAACStorage.Initialize();
|
MAACStorage.Initialize();
|
||||||
MafileExporterStorage.Initialize();
|
MafileExporterStorage.Initialize();
|
||||||
|
|
||||||
|
|||||||
@@ -89,4 +89,7 @@
|
|||||||
Color="{StaticResource SecondaryColor}" />
|
Color="{StaticResource SecondaryColor}" />
|
||||||
<SolidColorBrush x:Key="MaterialDesign.Brush.Secondary.Dark.Foreground"
|
<SolidColorBrush x:Key="MaterialDesign.Brush.Secondary.Dark.Foreground"
|
||||||
Color="{StaticResource BaseContentColor}" />
|
Color="{StaticResource BaseContentColor}" />
|
||||||
|
|
||||||
|
<SolidColorBrush x:Key="MaterialDesign.Brush.RadioButton.Outline"
|
||||||
|
Color="{StaticResource BaseContentColor}" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
@@ -20,12 +20,12 @@
|
|||||||
FontSize="16">
|
FontSize="16">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
<Grid Margin="10,10,10,5">
|
<Grid Margin="10,10,10,5">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
@@ -45,7 +45,23 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Separator Grid.Row="1" />
|
<Separator Grid.Row="1" />
|
||||||
<Grid Grid.Row="2">
|
|
||||||
|
<!-- Tabs -->
|
||||||
|
<TabControl Grid.Row="2"
|
||||||
|
md:ColorZoneAssist.Background="{DynamicResource Base100Brush}"
|
||||||
|
Style="{StaticResource MaterialDesignUniformTabControl}">
|
||||||
|
|
||||||
|
<!-- Tab 1: Proxy Manager -->
|
||||||
|
<TabItem Header="{Tr ProxyManagerDialog.TabManager}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Default proxy + display options -->
|
||||||
|
<Grid Grid.Row="0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
@@ -57,7 +73,8 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock HorizontalAlignment="Stretch">
|
<TextBlock HorizontalAlignment="Stretch">
|
||||||
<Run Text="{Tr ProxyManagerDialog.DefaultProxy, IsDynamic=False}" />
|
<Run Text="{Tr ProxyManagerDialog.DefaultProxy, IsDynamic=False}" />
|
||||||
<Run Text="{Binding DefaultProxy.Key, StringFormat='
0:', FallbackValue='-', Mode=OneWay}" />
|
<Run
|
||||||
|
Text="{Binding DefaultProxy.Key, StringFormat='
0:', FallbackValue='-', Mode=OneWay}" />
|
||||||
<Run>
|
<Run>
|
||||||
<Run.Text>
|
<Run.Text>
|
||||||
<MultiBinding Mode="OneWay"
|
<MultiBinding Mode="OneWay"
|
||||||
@@ -71,19 +88,20 @@
|
|||||||
</Run.Text>
|
</Run.Text>
|
||||||
</Run>
|
</Run>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
<Button Grid.Column="1" Command="{Binding RemoveDefaultCommand}">
|
<Button Grid.Column="1" Command="{Binding RemoveDefaultCommand}">
|
||||||
<md:PackIcon Kind="ClearBox" Width="20" Height="20" />
|
<md:PackIcon Kind="ClearBox" Width="20" Height="20" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<StackPanel Grid.Row="1" Margin="15,0,15,15">
|
<StackPanel Grid.Row="1" Margin="15,0,15,15">
|
||||||
<CheckBox Content="{Tr ProxyManagerDialog.DisplayProxyProtocol}" IsChecked="{Binding DisplayProtocol}" />
|
<CheckBox Content="{Tr ProxyManagerDialog.DisplayProxyProtocol}"
|
||||||
|
IsChecked="{Binding DisplayProtocol}" />
|
||||||
<CheckBox Content="{Tr ProxyManagerDialog.DisplayProxyCredentials}"
|
<CheckBox Content="{Tr ProxyManagerDialog.DisplayProxyCredentials}"
|
||||||
IsChecked="{Binding DisplayCredentials}" />
|
IsChecked="{Binding DisplayCredentials}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<md:Card Grid.Row="3" Margin="10">
|
|
||||||
|
<!-- Proxy list -->
|
||||||
|
<md:Card Grid.Row="1" Margin="10">
|
||||||
<ListBox VirtualizingStackPanel.VirtualizationMode="Recycling" FontSize="14"
|
<ListBox VirtualizingStackPanel.VirtualizationMode="Recycling" FontSize="14"
|
||||||
SelectedValue="{Binding SelectedProxy}" ItemsSource="{Binding Proxies}">
|
SelectedValue="{Binding SelectedProxy}" ItemsSource="{Binding Proxies}">
|
||||||
<ListBox.InputBindings>
|
<ListBox.InputBindings>
|
||||||
@@ -110,17 +128,15 @@
|
|||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
|
|
||||||
|
|
||||||
</Style>
|
</Style>
|
||||||
</ListBox.ItemContainerStyle>
|
</ListBox.ItemContainerStyle>
|
||||||
|
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock VerticalAlignment="Center">
|
<TextBlock VerticalAlignment="Center">
|
||||||
<Run Text="{Binding Key, Mode=OneWay}" /><Run Text=": " />
|
<Run Text="{Binding Key, Mode=OneWay}" /><Run Text=": " />
|
||||||
@@ -136,23 +152,28 @@
|
|||||||
</MultiBinding>
|
</MultiBinding>
|
||||||
</Run.Text>
|
</Run.Text>
|
||||||
</Run>
|
</Run>
|
||||||
|
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<Button Style="{StaticResource MaterialDesignIconButton}" Padding="0" Width="24"
|
|
||||||
Height="24" md:RippleAssist.IsDisabled="True" Grid.Column="1"
|
<TextBlock Grid.Column="1" VerticalAlignment="Center" FontSize="10" Margin="0,0,2,0"
|
||||||
|
Foreground="{DynamicResource MaterialDesign.Brush.ForegroundLight}"
|
||||||
|
Text="{Binding Key, Converter={StaticResource ProxyAccountCountConverter}}" />
|
||||||
|
<Button Style="{StaticResource MaterialDesignIconButton}" Padding="0"
|
||||||
|
Width="24"
|
||||||
|
Height="24" md:RippleAssist.IsDisabled="True" Grid.Column="2"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Command="{Binding DataContext.SetDefaultCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
Command="{Binding DataContext.SetDefaultCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||||
CommandParameter="{Binding}">
|
CommandParameter="{Binding}">
|
||||||
<md:PackIcon Height="16" Width="16" Kind="Heart" />
|
<md:PackIcon Height="16" Width="16" Kind="Heart" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
</md:Card>
|
</md:Card>
|
||||||
<Grid Grid.Row="4" Margin="5,0,15,0">
|
|
||||||
|
<!-- Add / Remove area -->
|
||||||
|
<Grid Grid.Row="2" Margin="5,0,15,0">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
@@ -166,14 +187,12 @@
|
|||||||
Text="{Binding ErrorText}"
|
Text="{Binding ErrorText}"
|
||||||
CloseCommand="{Binding ClearErrorCommand}"
|
CloseCommand="{Binding ClearErrorCommand}"
|
||||||
ShowCloseButton="True" />
|
ShowCloseButton="True" />
|
||||||
|
|
||||||
<Grid Grid.Row="1">
|
<Grid Grid.Row="1">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<TextBox Text="{Binding AddProxyField}"
|
<TextBox Text="{Binding AddProxyField}"
|
||||||
KeyDown="ProxyInput_KeyDown"
|
KeyDown="ProxyInput_KeyDown"
|
||||||
FontSize="12"
|
FontSize="12"
|
||||||
@@ -185,17 +204,110 @@
|
|||||||
md:HintAssist.IsFloating="False"
|
md:HintAssist.IsFloating="False"
|
||||||
md:HintAssist.Hint="IP:PORT
IP:PORT:USER:PASS
PROTOCOL://IP:PORT:USER:PASS
IP:PORT:USER:PASS{ID}
|
md:HintAssist.Hint="IP:PORT
IP:PORT:USER:PASS
PROTOCOL://IP:PORT:USER:PASS
IP:PORT:USER:PASS{ID}
|
||||||
" />
|
" />
|
||||||
<Button x:Name="AddProxyBtn" ToolTip="CTRL+ENTER" IsDefault="True" Grid.Column="1" Height="90"
|
<Button x:Name="AddProxyBtn" ToolTip="CTRL+ENTER" IsDefault="True" Grid.Column="1"
|
||||||
Command="{Binding AddProxyCommand}">
|
Height="90" Command="{Binding AddProxyCommand}">
|
||||||
<md:PackIcon Kind="Add" />
|
<md:PackIcon Kind="Add" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button Grid.Column="2" Height="90" Command="{Binding RemoveProxyCommand}" CommandParameter="{x:Null}">
|
<Button Grid.Column="2" Height="90" Command="{Binding RemoveProxyCommand}"
|
||||||
|
CommandParameter="{x:Null}">
|
||||||
<md:PackIcon Kind="Trash" />
|
<md:PackIcon Kind="Trash" />
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<!-- Tab 2: Bulk Assignment -->
|
||||||
|
<TabItem Header="{Tr ProxyManagerDialog.TabAssignment}">
|
||||||
|
<Grid Margin="12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Stats counters -->
|
||||||
|
<UniformGrid Grid.Row="0" Columns="3" Margin="0,8,0,12">
|
||||||
|
<Border Margin="0,0,4,0" Padding="10,8" CornerRadius="8"
|
||||||
|
Background="{DynamicResource MaterialDesign.Brush.Card.Background}">
|
||||||
|
<StackPanel HorizontalAlignment="Center">
|
||||||
|
<TextBlock FontSize="20" FontWeight="Bold" HorizontalAlignment="Center"
|
||||||
|
Text="{Binding TotalAccounts, Mode=OneWay}" />
|
||||||
|
<TextBlock FontSize="11" HorizontalAlignment="Center"
|
||||||
|
Text="{Tr ProxyManagerDialog.TotalAccounts}"
|
||||||
|
Foreground="{DynamicResource MaterialDesign.Brush.ForegroundLight}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Margin="4,0,4,0" Padding="10,8" CornerRadius="8"
|
||||||
|
Background="{DynamicResource MaterialDesign.Brush.Card.Background}">
|
||||||
|
<StackPanel HorizontalAlignment="Center">
|
||||||
|
<TextBlock FontSize="20" FontWeight="Bold" HorizontalAlignment="Center"
|
||||||
|
Text="{Binding TotalProxies, Mode=OneWay}" />
|
||||||
|
<TextBlock FontSize="11" HorizontalAlignment="Center"
|
||||||
|
Text="{Tr ProxyManagerDialog.TotalProxies}"
|
||||||
|
Foreground="{DynamicResource MaterialDesign.Brush.ForegroundLight}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Margin="4,0,0,0" Padding="10,8" CornerRadius="8"
|
||||||
|
Background="{DynamicResource MaterialDesign.Brush.Card.Background}">
|
||||||
|
<StackPanel HorizontalAlignment="Center">
|
||||||
|
<TextBlock FontSize="20" FontWeight="Bold" HorizontalAlignment="Center"
|
||||||
|
Text="{Binding AssignedProxies, Mode=OneWay}" />
|
||||||
|
<TextBlock FontSize="11" HorizontalAlignment="Center"
|
||||||
|
Text="{Tr ProxyManagerDialog.AssignedProxies}"
|
||||||
|
Foreground="{DynamicResource MaterialDesign.Brush.ForegroundLight}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UniformGrid>
|
||||||
|
|
||||||
|
<!-- Toggle: behaviour for unspecified proxy -->
|
||||||
|
<StackPanel Grid.Row="1" Margin="0,0,0,10">
|
||||||
|
<TextBlock Text="{Tr ProxyManagerDialog.UnspecifiedBehavior}" FontSize="13"
|
||||||
|
Margin="0,0,0,4"
|
||||||
|
Foreground="{DynamicResource MaterialDesign.Brush.ForegroundLight}" />
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<RadioButton Content="{Tr ProxyManagerDialog.UnspecifiedRandom}"
|
||||||
|
IsChecked="{Binding UnspecifiedAssignRandom}"
|
||||||
|
Margin="0,0,24,0" />
|
||||||
|
<RadioButton Content="{Tr ProxyManagerDialog.UnspecifiedRemove}"
|
||||||
|
IsChecked="{Binding UnspecifiedAssignRandom, Converter={StaticResource ReverseBooleanConverter}}" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Input -->
|
||||||
|
<TextBox Grid.Row="2"
|
||||||
|
Text="{Binding AssignmentInput, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
FontSize="12"
|
||||||
|
md:TextFieldAssist.HasClearButton="True"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
Style="{StaticResource MaterialDesignOutlinedTextBox}"
|
||||||
|
VerticalScrollBarVisibility="Auto"
|
||||||
|
md:HintAssist.IsFloating="False"
|
||||||
|
VerticalContentAlignment="Top"
|
||||||
|
Cursor="IBeam"
|
||||||
|
|
||||||
|
md:HintAssist.Hint="login:IP:PORT
login:IP:PORT:USER:PASS
login:socks5://USER:PASS@IP:PORT
login:ID
login" />
|
||||||
|
|
||||||
|
<!-- Result HintBox -->
|
||||||
|
<nebulaAuth:HintBox Grid.Row="3"
|
||||||
|
Visibility="{Binding AssignmentTip, Converter={StaticResource NullableToVisibilityConverter}}"
|
||||||
|
Margin="0,8,0,0"
|
||||||
|
Severity="Info"
|
||||||
|
FontSize="14"
|
||||||
|
Text="{Binding AssignmentTip}"
|
||||||
|
CloseCommand="{Binding ClearAssignmentTipCommand}"
|
||||||
|
ShowCloseButton="True" />
|
||||||
|
|
||||||
|
<!-- Apply button -->
|
||||||
|
<Button Grid.Row="4" Margin="0,10,0,0" HorizontalAlignment="Right"
|
||||||
|
Command="{Binding ApplyAssignmentCommand}"
|
||||||
|
Content="{Tr ProxyManagerDialog.ApplyAssignment}" />
|
||||||
|
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -42,7 +42,7 @@ public partial class MainVM : ObservableObject
|
|||||||
new MaProxy(kvp.Key, kvp.Value)));
|
new MaProxy(kvp.Key, kvp.Value)));
|
||||||
Storage.MaFiles.CollectionChanged += MaFilesOnCollectionChanged;
|
Storage.MaFiles.CollectionChanged += MaFilesOnCollectionChanged;
|
||||||
QueryGroups();
|
QueryGroups();
|
||||||
UpdateManager.CheckForUpdates(false);
|
UpdateManager.CheckForUpdates();
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ namespace NebulaAuth.ViewModel;
|
|||||||
|
|
||||||
public partial class MainVM //File //TODO: Refactor
|
public partial class MainVM //File //TODO: Refactor
|
||||||
{
|
{
|
||||||
|
private record MafileReadResult(
|
||||||
|
Mafile? Mafile,
|
||||||
|
bool SdaPasswordPrompted,
|
||||||
|
SDAEncryptionHelper.Context? SdaContext);
|
||||||
|
|
||||||
public Settings Settings => Settings.Instance;
|
public Settings Settings => Settings.Instance;
|
||||||
|
|
||||||
|
|
||||||
@@ -347,9 +352,4 @@ public partial class MainVM //File //TODO: Refactor
|
|||||||
if (mafile is not Mafile maf) return false;
|
if (mafile is not Mafile maf) return false;
|
||||||
return maf.Password != null && PHandler.IsPasswordSet;
|
return maf.Password != null && PHandler.IsPasswordSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private record MafileReadResult(
|
|
||||||
Mafile? Mafile,
|
|
||||||
bool SdaPasswordPrompted,
|
|
||||||
SDAEncryptionHelper.Context? SdaContext);
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
@@ -38,9 +39,10 @@ public partial class MainVM
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!system && SelectedMafile != null)
|
if (!system && SelectedMafile != null)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
var oldProxyId = SelectedMafile.Proxy?.Id;
|
||||||
SelectedMafile.Proxy = SelectedProxy;
|
SelectedMafile.Proxy = SelectedProxy;
|
||||||
|
ProxyAssignmentCache.UpdateAssignment(SelectedMafile.AccountName, oldProxyId, SelectedProxy?.Id);
|
||||||
Storage.UpdateMafile(SelectedMafile);
|
Storage.UpdateMafile(SelectedMafile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +76,9 @@ public partial class MainVM
|
|||||||
{
|
{
|
||||||
mafile ??= SelectedMafile;
|
mafile ??= SelectedMafile;
|
||||||
if (mafile?.Proxy == null) return;
|
if (mafile?.Proxy == null) return;
|
||||||
|
var oldProxyId = mafile.Proxy.Id;
|
||||||
mafile.Proxy = null;
|
mafile.Proxy = null;
|
||||||
|
ProxyAssignmentCache.UpdateAssignment(mafile.AccountName, oldProxyId, null);
|
||||||
if (mafile == SelectedMafile)
|
if (mafile == SelectedMafile)
|
||||||
SetProxy(null, false); //Not system, triggered by user
|
SetProxy(null, false); //Not system, triggered by user
|
||||||
}
|
}
|
||||||
@@ -85,6 +89,38 @@ public partial class MainVM
|
|||||||
return mafile is {Proxy: not null};
|
return mafile is {Proxy: not null};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand(CanExecute = nameof(AssignFreeProxyCanExecute))]
|
||||||
|
private void AssignFreeProxy(Mafile? mafile)
|
||||||
|
{
|
||||||
|
mafile ??= SelectedMafile;
|
||||||
|
if (mafile == null) return;
|
||||||
|
|
||||||
|
int? defaultProxyId = null;
|
||||||
|
if (MaClient.DefaultProxy != null)
|
||||||
|
{
|
||||||
|
var defKvp = ProxyStorage.Proxies.FirstOrDefault(kvp => kvp.Value.Equals(MaClient.DefaultProxy));
|
||||||
|
if (defKvp.Value != null)
|
||||||
|
defaultProxyId = defKvp.Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
var freeProxy = ProxyAssignmentCache.GetRandomFreeProxy(ProxyStorage.Proxies, defaultProxyId);
|
||||||
|
if (freeProxy == null) return;
|
||||||
|
|
||||||
|
var newProxy = new MaProxy(freeProxy.Value.Key, freeProxy.Value.Value);
|
||||||
|
var oldProxyId = mafile.Proxy?.Id;
|
||||||
|
mafile.Proxy = newProxy;
|
||||||
|
ProxyAssignmentCache.UpdateAssignment(mafile.AccountName, oldProxyId, newProxy.Id);
|
||||||
|
Storage.UpdateMafile(mafile);
|
||||||
|
|
||||||
|
if (mafile == SelectedMafile)
|
||||||
|
SetProxy(newProxy, true); // system=true: update UI only, assignment already saved
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AssignFreeProxyCanExecute(Mafile? mafile)
|
||||||
|
{
|
||||||
|
return ProxyStorage.Proxies.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
private void CheckProxyExist()
|
private void CheckProxyExist()
|
||||||
{
|
{
|
||||||
if (SelectedProxy == null)
|
if (SelectedProxy == null)
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AchiesUtilities.Web.Proxy;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using NebulaAuth.Model;
|
||||||
|
using NebulaAuth.Model.Entities;
|
||||||
|
using NebulaAuth.Model.Mafiles;
|
||||||
|
using SteamLibForked.Models.SteamIds;
|
||||||
|
|
||||||
|
namespace NebulaAuth.ViewModel.Other;
|
||||||
|
|
||||||
|
public partial class ProxyManagerVM
|
||||||
|
{
|
||||||
|
public int TotalAccounts => Storage.MaFiles.Count;
|
||||||
|
public int TotalProxies => ProxyStorage.Proxies.Count;
|
||||||
|
public int AssignedProxies => Storage.MaFiles.Count(m => m.Proxy != null);
|
||||||
|
|
||||||
|
[ObservableProperty] private string? _assignmentInput;
|
||||||
|
[ObservableProperty] private string? _assignmentTip;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// true = assign a random free proxy to unspecified accounts; false = remove proxy from unspecified accounts.
|
||||||
|
/// </summary>
|
||||||
|
[ObservableProperty] private bool _unspecifiedAssignRandom = true;
|
||||||
|
|
||||||
|
public void RefreshAssignmentCounters()
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(TotalAccounts));
|
||||||
|
OnPropertyChanged(nameof(TotalProxies));
|
||||||
|
OnPropertyChanged(nameof(AssignedProxies));
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task ApplyAssignment()
|
||||||
|
{
|
||||||
|
AssignmentTip = null;
|
||||||
|
var input = AssignmentInput;
|
||||||
|
if (string.IsNullOrWhiteSpace(input)) return;
|
||||||
|
|
||||||
|
var lines = input.Split(["\r\n", "\n"], StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var mafs = Storage.MaFiles.ToList();
|
||||||
|
var defaultProxyId = GetDefaultProxyId();
|
||||||
|
|
||||||
|
var success = 0;
|
||||||
|
var errors = 0;
|
||||||
|
var notFound = 0;
|
||||||
|
|
||||||
|
foreach (var rawLine in lines)
|
||||||
|
{
|
||||||
|
var line = rawLine.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(line)) continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string login;
|
||||||
|
// resolvedProxy: null means unspecified (apply toggle behaviour)
|
||||||
|
MaProxy? resolvedProxy;
|
||||||
|
|
||||||
|
var colonIdx = line.IndexOf(':');
|
||||||
|
if (colonIdx >= 0)
|
||||||
|
{
|
||||||
|
login = line[..colonIdx].Trim();
|
||||||
|
var proxyPart = line[(colonIdx + 1)..].Trim();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(proxyPart))
|
||||||
|
{
|
||||||
|
resolvedProxy = null; // unspecified
|
||||||
|
}
|
||||||
|
else if (int.TryParse(proxyPart, out var parsedId))
|
||||||
|
{
|
||||||
|
// Mode: login:ID — proxy ID
|
||||||
|
if (!ProxyStorage.Proxies.TryGetValue(parsedId, out var byId))
|
||||||
|
{
|
||||||
|
errors++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedProxy = new MaProxy(parsedId, byId);
|
||||||
|
}
|
||||||
|
else if (ProxyStorage.DefaultScheme.TryParse(proxyPart, out var parsedProxy))
|
||||||
|
{
|
||||||
|
// Mode: login:proxy_string — find existing or add
|
||||||
|
resolvedProxy = FindOrAddProxy(parsedProxy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errors++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
login = line;
|
||||||
|
resolvedProxy = null; // unspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(login))
|
||||||
|
{
|
||||||
|
errors++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var maf = FindMafile(mafs, login);
|
||||||
|
if (maf == null)
|
||||||
|
{
|
||||||
|
notFound++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newProxy = resolvedProxy ?? (UnspecifiedAssignRandom
|
||||||
|
? BuildRandomFreeProxy(defaultProxyId)
|
||||||
|
: null);
|
||||||
|
|
||||||
|
var oldProxyId = maf.Proxy?.Id;
|
||||||
|
maf.Proxy = newProxy;
|
||||||
|
ProxyAssignmentCache.UpdateAssignment(maf.AccountName, oldProxyId, newProxy?.Id);
|
||||||
|
await Storage.UpdateMafileAsync(maf);
|
||||||
|
success++;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
errors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnyChanges = true;
|
||||||
|
RefreshAssignmentCounters();
|
||||||
|
AssignmentTip = BuildAssignmentTip(success, notFound, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void ClearAssignmentTip()
|
||||||
|
{
|
||||||
|
AssignmentTip = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a proxy equal to <paramref name="proxyData" /> in storage.
|
||||||
|
/// If not found, adds it and returns the new entry.
|
||||||
|
/// </summary>
|
||||||
|
private static MaProxy FindOrAddProxy(ProxyData proxyData)
|
||||||
|
{
|
||||||
|
var existing = ProxyStorage.Proxies.FirstOrDefault(kvp => kvp.Value.Equals(proxyData));
|
||||||
|
if (existing.Value != null)
|
||||||
|
return new MaProxy(existing.Key, existing.Value);
|
||||||
|
|
||||||
|
ProxyStorage.SetProxy(null, proxyData);
|
||||||
|
|
||||||
|
// Fetch the entry we just added (SetProxy guarantees it was stored)
|
||||||
|
var added = ProxyStorage.Proxies.First(kvp => kvp.Value.Equals(proxyData));
|
||||||
|
return new MaProxy(added.Key, added.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MaProxy? BuildRandomFreeProxy(int? defaultProxyId)
|
||||||
|
{
|
||||||
|
var free = ProxyAssignmentCache.GetRandomFreeProxy(ProxyStorage.Proxies, defaultProxyId);
|
||||||
|
return free.HasValue ? new MaProxy(free.Value.Key, free.Value.Value) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int? GetDefaultProxyId()
|
||||||
|
{
|
||||||
|
if (MaClient.DefaultProxy == null) return null;
|
||||||
|
var defKvp = ProxyStorage.Proxies.FirstOrDefault(kvp => kvp.Value.Equals(MaClient.DefaultProxy));
|
||||||
|
return defKvp.Value != null ? defKvp.Key : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Mafile? FindMafile(List<Mafile> mafs, string login)
|
||||||
|
{
|
||||||
|
SteamId64? steamId = null;
|
||||||
|
if (SteamId64.TryParse(login, out var id64))
|
||||||
|
steamId = id64;
|
||||||
|
if (steamId != null)
|
||||||
|
return mafs.FirstOrDefault(m => m.SteamId == steamId || m.AccountName == login);
|
||||||
|
return mafs.FirstOrDefault(m =>
|
||||||
|
string.Equals(m.AccountName, login, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildAssignmentTip(int success, int notFound, int errors)
|
||||||
|
{
|
||||||
|
if (success > 0 && errors == 0 && notFound == 0)
|
||||||
|
return string.Format(GetLocalizationOrDefault("AssignmentSuccess"), success);
|
||||||
|
return string.Format(GetLocalizationOrDefault("AssignmentPartialSuccess"), success, notFound, errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using SteamLibForked.Models.Core;
|
using System.Collections.Immutable;
|
||||||
using System.Collections.Immutable;
|
using SteamLibForked.Models.Core;
|
||||||
|
|
||||||
namespace SteamLib.Core;
|
namespace SteamLib.Core;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using SteamLib.Core;
|
using SteamLib.Core;
|
||||||
using SteamLibForked.Abstractions;
|
using SteamLibForked.Abstractions;
|
||||||
using SteamLibForked.Models.Core;
|
using SteamLibForked.Models.Core;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace SteamLibForked.Models.Session;
|
namespace SteamLibForked.Models.Session;
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@ public class MobileSessionData : SessionData, IMobileSessionData
|
|||||||
// See: SteamAuthToken 'TODO' for more details
|
// See: SteamAuthToken 'TODO' for more details
|
||||||
return MobileToken ?? base.GetToken(domain);
|
return MobileToken ?? base.GetToken(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetToken(domain);
|
return base.GetToken(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ public class MobileSessionData : SessionData, IMobileSessionData
|
|||||||
if (token.Type != SteamAccessTokenType.Mobile)
|
if (token.Type != SteamAccessTokenType.Mobile)
|
||||||
throw new ArgumentException("Token must be of type MobileAccess", nameof(token))
|
throw new ArgumentException("Token must be of type MobileAccess", nameof(token))
|
||||||
{
|
{
|
||||||
Data = { { "ActualType", token.Type } }
|
Data = {{"ActualType", token.Type}}
|
||||||
};
|
};
|
||||||
|
|
||||||
MobileToken = token;
|
MobileToken = token;
|
||||||
@@ -65,6 +66,6 @@ public class MobileSessionData : SessionData, IMobileSessionData
|
|||||||
|
|
||||||
public override MobileSessionData Clone()
|
public override MobileSessionData Clone()
|
||||||
{
|
{
|
||||||
return (MobileSessionData)((ISessionData)this).Clone();
|
return (MobileSessionData) ((ISessionData) this).Clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,14 +4,11 @@ namespace SteamLib.Utility.MafileSerialization;
|
|||||||
|
|
||||||
internal class LegacyMafile
|
internal class LegacyMafile
|
||||||
{
|
{
|
||||||
[JsonProperty("shared_secret")]
|
[JsonProperty("shared_secret")] public string SharedSecret { get; set; } = null!;
|
||||||
public string SharedSecret { get; set; } = null!;
|
|
||||||
|
|
||||||
[JsonProperty("identity_secret")]
|
[JsonProperty("identity_secret")] public string IdentitySecret { get; set; } = null!;
|
||||||
public string IdentitySecret { get; set; } = null!;
|
|
||||||
|
|
||||||
[JsonProperty("device_id")]
|
[JsonProperty("device_id")] public string DeviceId { get; set; } = null!;
|
||||||
public string DeviceId { get; set; } = null!;
|
|
||||||
|
|
||||||
[JsonProperty("revocation_code")] public string RevocationCode { get; set; } = null!;
|
[JsonProperty("revocation_code")] public string RevocationCode { get; set; } = null!;
|
||||||
[JsonProperty("account_name")] public string AccountName { get; set; } = null!;
|
[JsonProperty("account_name")] public string AccountName { get; set; } = null!;
|
||||||
|
|||||||
Reference in New Issue
Block a user