poe2-bot/src/Poe2Trade.Core/ConfigStore.cs

189 lines
6.4 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
using Serilog;
namespace Poe2Trade.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 Poe2LogPath { get; set; } = @"C:\Program Files (x86)\Steam\steamapps\common\Path of Exile 2\logs\Client.txt";
public string Poe2WindowTitle { 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 KulemakSettings Kulemak { get; set; } = new();
}
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 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);
if (_data.Links.Any(l => l.Url == url)) 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);
}
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/?$", "");
}