refactor
This commit is contained in:
parent
4424f4c3a8
commit
0e7de0a5f3
281 changed files with 3188 additions and 611 deletions
258
src/Nexus.Core/ConfigStore.cs
Normal file
258
src/Nexus.Core/ConfigStore.cs
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Serilog;
|
||||
|
||||
namespace Nexus.Core;
|
||||
|
||||
public class SavedLink
|
||||
{
|
||||
public string Url { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public bool Active { get; set; } = true;
|
||||
public LinkMode Mode { get; set; } = LinkMode.Live;
|
||||
public PostAction PostAction { get; set; } = PostAction.Stash;
|
||||
public string AddedAt { get; set; } = DateTime.UtcNow.ToString("o");
|
||||
}
|
||||
|
||||
public class SavedSettings
|
||||
{
|
||||
public bool Paused { get; set; }
|
||||
public List<SavedLink> Links { get; set; } = [];
|
||||
public string GameLogPath { get; set; } = @"C:\Program Files (x86)\Steam\steamapps\common\Path of Exile 2\logs\Client.txt";
|
||||
public string GameWindowTitle { get; set; } = "Path of Exile 2";
|
||||
public string BrowserUserDataDir { get; set; } = "./browser-data";
|
||||
public int TravelTimeoutMs { get; set; } = 15000;
|
||||
public int StashScanTimeoutMs { get; set; } = 10000;
|
||||
public int WaitForMoreItemsMs { get; set; } = 20000;
|
||||
public int BetweenTradesDelayMs { get; set; } = 5000;
|
||||
public double? WindowX { get; set; }
|
||||
public double? WindowY { get; set; }
|
||||
public double? WindowWidth { get; set; }
|
||||
public double? WindowHeight { get; set; }
|
||||
public bool Headless { get; set; } = true;
|
||||
public BotMode Mode { get; set; } = BotMode.Trading;
|
||||
public MapType MapType { get; set; } = MapType.TrialOfChaos;
|
||||
public StashCalibration? StashCalibration { get; set; }
|
||||
public StashCalibration? ShopCalibration { get; set; }
|
||||
public bool ShowHudDebug { get; set; }
|
||||
public string OcrEngine { get; set; } = "WinOCR";
|
||||
public KulemakSettings Kulemak { get; set; } = new();
|
||||
public DiamondSettings Diamond { get; set; } = new();
|
||||
public List<CraftRecipe> Crafts { get; set; } = [];
|
||||
public List<CurrencyPosition> CurrencyPositions { get; set; } = [];
|
||||
public string SelectedLeague { get; set; } = "";
|
||||
}
|
||||
|
||||
public class DiamondPriceConfig
|
||||
{
|
||||
public string ItemName { get; set; } = "";
|
||||
public string DisplayName { get; set; } = "";
|
||||
public double MaxDivinePrice { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
}
|
||||
|
||||
public class DiamondSettings
|
||||
{
|
||||
public static readonly Dictionary<string, string> KnownDiamonds = new()
|
||||
{
|
||||
["SanctumJewel"] = "Time-Lost Diamond",
|
||||
["SacredFlameJewel"] = "Prism of Belief",
|
||||
["TrialmasterJewel"] = "The Adorned",
|
||||
["DeliriumJewel"] = "Megalomaniac Diamond",
|
||||
["ApostatesHeart"] = "Heart of the Well",
|
||||
};
|
||||
|
||||
public List<DiamondPriceConfig> Prices { get; set; } = DefaultPrices();
|
||||
|
||||
private static List<DiamondPriceConfig> DefaultPrices() =>
|
||||
KnownDiamonds.Select(kv => new DiamondPriceConfig
|
||||
{
|
||||
ItemName = kv.Key,
|
||||
DisplayName = kv.Value,
|
||||
MaxDivinePrice = 0,
|
||||
Enabled = false,
|
||||
}).ToList();
|
||||
|
||||
/// <summary>Ensure all known diamonds exist in the list (adds missing ones as disabled).</summary>
|
||||
public void BackfillKnown()
|
||||
{
|
||||
// Remove blank entries
|
||||
Prices.RemoveAll(p => string.IsNullOrWhiteSpace(p.ItemName));
|
||||
|
||||
var existing = new HashSet<string>(Prices.Select(p => p.ItemName), StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var kv in KnownDiamonds)
|
||||
{
|
||||
if (existing.Contains(kv.Key)) continue;
|
||||
Prices.Add(new DiamondPriceConfig
|
||||
{
|
||||
ItemName = kv.Key,
|
||||
DisplayName = kv.Value,
|
||||
MaxDivinePrice = 0,
|
||||
Enabled = false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class KulemakSettings
|
||||
{
|
||||
public bool Enabled { get; set; }
|
||||
public string InvitationTabPath { get; set; } = "";
|
||||
public string LootTabPath { get; set; } = "";
|
||||
public int InvitationCount { get; set; } = 15;
|
||||
public int RunCount { get; set; } = 15;
|
||||
}
|
||||
|
||||
public class ConfigStore
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
|
||||
};
|
||||
|
||||
private readonly string _filePath;
|
||||
private SavedSettings _data;
|
||||
|
||||
public ConfigStore(string? configPath = null)
|
||||
{
|
||||
_filePath = configPath ?? Path.GetFullPath("config.json");
|
||||
_data = Load();
|
||||
}
|
||||
|
||||
public SavedSettings Settings => _data;
|
||||
public IReadOnlyList<SavedLink> Links => _data.Links;
|
||||
|
||||
public void AddLink(string url, string name = "", LinkMode mode = LinkMode.Live, PostAction? postAction = null)
|
||||
{
|
||||
url = StripLive(url);
|
||||
var existing = _data.Links.FirstOrDefault(l => l.Url == url);
|
||||
if (existing != null)
|
||||
{
|
||||
// Update mode/postAction/name if re-added with different settings
|
||||
existing.Mode = mode;
|
||||
existing.PostAction = postAction ?? (mode == LinkMode.Scrap ? PostAction.Salvage : PostAction.Stash);
|
||||
if (!string.IsNullOrEmpty(name)) existing.Name = name;
|
||||
Save();
|
||||
return;
|
||||
}
|
||||
_data.Links.Add(new SavedLink
|
||||
{
|
||||
Url = url,
|
||||
Name = name,
|
||||
Active = true,
|
||||
Mode = mode,
|
||||
PostAction = postAction ?? (mode == LinkMode.Scrap ? PostAction.Salvage : PostAction.Stash),
|
||||
AddedAt = DateTime.UtcNow.ToString("o")
|
||||
});
|
||||
Save();
|
||||
}
|
||||
|
||||
public void RemoveLink(string url)
|
||||
{
|
||||
_data.Links.RemoveAll(l => l.Url == url);
|
||||
Save();
|
||||
}
|
||||
|
||||
public void RemoveLinkById(string id)
|
||||
{
|
||||
_data.Links.RemoveAll(l => l.Url.Split('/').Last() == id);
|
||||
Save();
|
||||
}
|
||||
|
||||
public SavedLink? UpdateLinkById(string id, Action<SavedLink> update)
|
||||
{
|
||||
var link = _data.Links.FirstOrDefault(l => l.Url.Split('/').Last() == id);
|
||||
if (link == null) return null;
|
||||
update(link);
|
||||
Save();
|
||||
return link;
|
||||
}
|
||||
|
||||
public void SetPaused(bool paused)
|
||||
{
|
||||
_data.Paused = paused;
|
||||
Save();
|
||||
}
|
||||
|
||||
public void UpdateSettings(Action<SavedSettings> update)
|
||||
{
|
||||
update(_data);
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = JsonSerializer.Serialize(_data, JsonOptions);
|
||||
File.WriteAllText(_filePath, json);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to save config.json to {Path}", _filePath);
|
||||
}
|
||||
}
|
||||
|
||||
private SavedSettings Load()
|
||||
{
|
||||
if (!File.Exists(_filePath))
|
||||
{
|
||||
Log.Information("No config.json found at {Path}, using defaults", _filePath);
|
||||
return new SavedSettings();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var raw = File.ReadAllText(_filePath);
|
||||
|
||||
// Migrate: BossRun was removed from BotMode, now it's MapType.Kulemak
|
||||
if (raw.Contains("\"bossRun\"") || raw.Contains("\"BossRun\""))
|
||||
{
|
||||
const System.Text.RegularExpressions.RegexOptions ic =
|
||||
System.Text.RegularExpressions.RegexOptions.IgnoreCase;
|
||||
|
||||
// Mode enum: bossRun → mapping
|
||||
using var doc = JsonDocument.Parse(raw);
|
||||
if (doc.RootElement.TryGetProperty("Mode", out var modeProp) &&
|
||||
modeProp.GetString()?.Equals("bossRun", StringComparison.OrdinalIgnoreCase) == true)
|
||||
{
|
||||
raw = System.Text.RegularExpressions.Regex.Replace(
|
||||
raw, @"""Mode""\s*:\s*""bossRun""", @"""Mode"": ""mapping""", ic);
|
||||
raw = System.Text.RegularExpressions.Regex.Replace(
|
||||
raw, @"""MapType""\s*:\s*""[^""]*""", @"""MapType"": ""kulemak""", ic);
|
||||
Log.Information("Migrated config: Mode bossRun -> mapping + MapType kulemak");
|
||||
}
|
||||
|
||||
// MapType enum value: bossRun → kulemak
|
||||
raw = System.Text.RegularExpressions.Regex.Replace(
|
||||
raw, @"""MapType""\s*:\s*""bossRun""", @"""MapType"": ""kulemak""", ic);
|
||||
|
||||
// Settings property name: BossRun → Kulemak
|
||||
raw = raw.Replace("\"BossRun\":", "\"Kulemak\":");
|
||||
}
|
||||
|
||||
var parsed = JsonSerializer.Deserialize<SavedSettings>(raw, JsonOptions);
|
||||
if (parsed == null) return new SavedSettings();
|
||||
|
||||
// Migrate links: strip /live from URLs
|
||||
foreach (var link in parsed.Links)
|
||||
{
|
||||
link.Url = StripLive(link.Url);
|
||||
}
|
||||
|
||||
// Backfill known diamonds
|
||||
parsed.Diamond.BackfillKnown();
|
||||
|
||||
Log.Information("Loaded config.json from {Path} ({LinkCount} links)", _filePath, parsed.Links.Count);
|
||||
return parsed;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "Failed to read config.json at {Path}, using defaults", _filePath);
|
||||
return new SavedSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private static string StripLive(string url) => System.Text.RegularExpressions.Regex.Replace(url, @"/live/?$", "");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue