diff --git a/src/NebulaAuth/Converters/Converters.xaml b/src/NebulaAuth/Converters/Converters.xaml index 97888f1..8ef5bd7 100644 --- a/src/NebulaAuth/Converters/Converters.xaml +++ b/src/NebulaAuth/Converters/Converters.xaml @@ -15,6 +15,7 @@ + diff --git a/src/NebulaAuth/Converters/ProxyAccountCountConverter.cs b/src/NebulaAuth/Converters/ProxyAccountCountConverter.cs new file mode 100644 index 0000000..f39d5b4 --- /dev/null +++ b/src/NebulaAuth/Converters/ProxyAccountCountConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using NebulaAuth.Model; + +namespace NebulaAuth.Converters; + +/// +/// 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. +/// +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(); + } +} \ No newline at end of file diff --git a/src/NebulaAuth/Localization/dialogs.proxymanager.loc.json b/src/NebulaAuth/Localization/dialogs.proxymanager.loc.json index 07193f8..04a7baf 100644 --- a/src/NebulaAuth/Localization/dialogs.proxymanager.loc.json +++ b/src/NebulaAuth/Localization/dialogs.proxymanager.loc.json @@ -40,6 +40,26 @@ "es": "El proxy tiene un formato incorrecto", "tr": "Proxy yanlış formatta", "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", "tr": "Kimlik bilgilerini göster", "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": "Қолдану" } } } \ No newline at end of file diff --git a/src/NebulaAuth/Localization/mainwindow.loc.json b/src/NebulaAuth/Localization/mainwindow.loc.json index c8b57e8..ca22364 100644 --- a/src/NebulaAuth/Localization/mainwindow.loc.json +++ b/src/NebulaAuth/Localization/mainwindow.loc.json @@ -904,6 +904,16 @@ "es": "Desvincular proxy", "tr": "Proxy bağlantısını kaldır", "kk": "Проксиді ажырату" + }, + "AssignFreeProxy": { + "en": "Assign free proxy", + "ru": "Назначить свободный прокси", + "zh": "分配空闲代理", + "uk": "Призначити вільний проксі", + "fr": "Attribuer un proxy libre", + "es": "Asignar proxy libre", + "tr": "Serbest proxy ata", + "kk": "Бос проксиді тағайындау" } }, "Proxy": { diff --git a/src/NebulaAuth/MainWindow.xaml b/src/NebulaAuth/MainWindow.xaml index 6eb2983..47654ed 100644 --- a/src/NebulaAuth/MainWindow.xaml +++ b/src/NebulaAuth/MainWindow.xaml @@ -420,6 +420,9 @@ + diff --git a/src/NebulaAuth/Model/ProxyAssignmentCache.cs b/src/NebulaAuth/Model/ProxyAssignmentCache.cs new file mode 100644 index 0000000..0a02f32 --- /dev/null +++ b/src/NebulaAuth/Model/ProxyAssignmentCache.cs @@ -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; + +/// +/// 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. +/// +public static class ProxyAssignmentCache +{ + private static readonly Dictionary> _proxyToAccounts = new(); + + public static void Initialize(IEnumerable mafiles) + { + _proxyToAccounts.Clear(); + foreach (var maf in mafiles) + { + if (maf.Proxy != null) + AddInternal(maf.Proxy.Id, maf.AccountName); + } + } + + /// + /// Call whenever a proxy assignment on a mafile changes and is persisted to disk. + /// + 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; + } + + /// + /// Returns a random free proxy (not the default proxy), or null if none available. + /// + public static KeyValuePair? GetRandomFreeProxy( + ObservableDictionary 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(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); + } +} \ No newline at end of file diff --git a/src/NebulaAuth/Model/Shell.cs b/src/NebulaAuth/Model/Shell.cs index 7a74a5d..f2ce3e5 100644 --- a/src/NebulaAuth/Model/Shell.cs +++ b/src/NebulaAuth/Model/Shell.cs @@ -45,6 +45,7 @@ public static class Shell var threads = Environment.ProcessorCount > 0 ? Environment.ProcessorCount : 1; await Storage.Initialize(threads); + ProxyAssignmentCache.Initialize(Storage.MaFiles); MAACStorage.Initialize(); MafileExporterStorage.Initialize(); diff --git a/src/NebulaAuth/Theme/Brushes.xaml b/src/NebulaAuth/Theme/Brushes.xaml index a75f635..5c13e96 100644 --- a/src/NebulaAuth/Theme/Brushes.xaml +++ b/src/NebulaAuth/Theme/Brushes.xaml @@ -89,4 +89,7 @@ Color="{StaticResource SecondaryColor}" /> + + \ No newline at end of file diff --git a/src/NebulaAuth/View/ProxyManagerView.xaml b/src/NebulaAuth/View/ProxyManagerView.xaml index 6eea83f..6dfadf9 100644 --- a/src/NebulaAuth/View/ProxyManagerView.xaml +++ b/src/NebulaAuth/View/ProxyManagerView.xaml @@ -20,12 +20,12 @@ FontSize="16"> - - + + @@ -45,90 +45,41 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - + + + + + + + - - + + + - + - - - + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + - + + + + + + + + + + + + - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +