switched to new way
This commit is contained in:
parent
f22d182c8f
commit
4a65c8e17b
96 changed files with 4991 additions and 10025 deletions
29
src/Poe2Trade.Core/AppConfig.cs
Normal file
29
src/Poe2Trade.Core/AppConfig.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Poe2Trade.Core;
|
||||
|
||||
public class AppConfig
|
||||
{
|
||||
public List<string> TradeUrls { 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 static AppConfig Load(string? configPath = null)
|
||||
{
|
||||
var builder = new ConfigurationBuilder();
|
||||
var path = configPath ?? "appsettings.json";
|
||||
if (File.Exists(path))
|
||||
{
|
||||
builder.AddJsonFile(path, optional: true);
|
||||
}
|
||||
var configuration = builder.Build();
|
||||
var config = new AppConfig();
|
||||
configuration.Bind(config);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
147
src/Poe2Trade.Core/ConfigStore.cs
Normal file
147
src/Poe2Trade.Core/ConfigStore.cs
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
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 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);
|
||||
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/?$", "");
|
||||
}
|
||||
14
src/Poe2Trade.Core/Helpers.cs
Normal file
14
src/Poe2Trade.Core/Helpers.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
namespace Poe2Trade.Core;
|
||||
|
||||
public static class Helpers
|
||||
{
|
||||
private static readonly Random Rng = new();
|
||||
|
||||
public static Task Sleep(int ms) => Task.Delay(ms);
|
||||
|
||||
public static Task RandomDelay(int minMs, int maxMs)
|
||||
{
|
||||
var delay = Rng.Next(minMs, maxMs + 1);
|
||||
return Task.Delay(delay);
|
||||
}
|
||||
}
|
||||
129
src/Poe2Trade.Core/LinkManager.cs
Normal file
129
src/Poe2Trade.Core/LinkManager.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using Serilog;
|
||||
|
||||
namespace Poe2Trade.Core;
|
||||
|
||||
public class TradeLink
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string Url { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public string Label { 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 LinkManager
|
||||
{
|
||||
private readonly Dictionary<string, TradeLink> _links = new();
|
||||
private readonly ConfigStore _store;
|
||||
|
||||
public LinkManager(ConfigStore store)
|
||||
{
|
||||
_store = store;
|
||||
}
|
||||
|
||||
public TradeLink AddLink(string url, string name = "", LinkMode? mode = null, PostAction? postAction = null)
|
||||
{
|
||||
url = StripLive(url);
|
||||
var id = ExtractId(url);
|
||||
var label = ExtractLabel(url);
|
||||
var savedLink = _store.Links.FirstOrDefault(l => l.Url == url);
|
||||
var resolvedMode = mode ?? savedLink?.Mode ?? LinkMode.Live;
|
||||
var link = new TradeLink
|
||||
{
|
||||
Id = id,
|
||||
Url = url,
|
||||
Name = name != "" ? name : savedLink?.Name ?? "",
|
||||
Label = label,
|
||||
Active = savedLink?.Active ?? true,
|
||||
Mode = resolvedMode,
|
||||
PostAction = postAction ?? savedLink?.PostAction ?? (resolvedMode == LinkMode.Scrap ? PostAction.Salvage : PostAction.Stash),
|
||||
AddedAt = DateTime.UtcNow.ToString("o")
|
||||
};
|
||||
_links[id] = link;
|
||||
_store.AddLink(url, link.Name, link.Mode, link.PostAction);
|
||||
Log.Information("Trade link added: {Id} {Url} mode={Mode}", id, url, link.Mode);
|
||||
return link;
|
||||
}
|
||||
|
||||
public void RemoveLink(string id)
|
||||
{
|
||||
if (_links.TryGetValue(id, out var link))
|
||||
{
|
||||
_links.Remove(id);
|
||||
_store.RemoveLink(link.Url);
|
||||
}
|
||||
else
|
||||
{
|
||||
_store.RemoveLinkById(id);
|
||||
}
|
||||
Log.Information("Trade link removed: {Id}", id);
|
||||
}
|
||||
|
||||
public TradeLink? ToggleLink(string id, bool active)
|
||||
{
|
||||
if (!_links.TryGetValue(id, out var link)) return null;
|
||||
link.Active = active;
|
||||
_store.UpdateLinkById(id, l => l.Active = active);
|
||||
Log.Information("Trade link {Action}: {Id}", active ? "activated" : "deactivated", id);
|
||||
return link;
|
||||
}
|
||||
|
||||
public void UpdateName(string id, string name)
|
||||
{
|
||||
if (!_links.TryGetValue(id, out var link)) return;
|
||||
link.Name = name;
|
||||
_store.UpdateLinkById(id, l => l.Name = name);
|
||||
}
|
||||
|
||||
public TradeLink? UpdateMode(string id, LinkMode mode)
|
||||
{
|
||||
if (!_links.TryGetValue(id, out var link)) return null;
|
||||
link.Mode = mode;
|
||||
_store.UpdateLinkById(id, l => l.Mode = mode);
|
||||
return link;
|
||||
}
|
||||
|
||||
public TradeLink? UpdatePostAction(string id, PostAction postAction)
|
||||
{
|
||||
if (!_links.TryGetValue(id, out var link)) return null;
|
||||
link.PostAction = postAction;
|
||||
_store.UpdateLinkById(id, l => l.PostAction = postAction);
|
||||
return link;
|
||||
}
|
||||
|
||||
public bool IsActive(string id) => _links.TryGetValue(id, out var link) && link.Active;
|
||||
|
||||
public List<TradeLink> GetLinks() => _links.Values.ToList();
|
||||
|
||||
public TradeLink? GetLink(string id) => _links.GetValueOrDefault(id);
|
||||
|
||||
private static string StripLive(string url) =>
|
||||
System.Text.RegularExpressions.Regex.Replace(url, @"/live/?$", "");
|
||||
|
||||
private static string ExtractId(string url)
|
||||
{
|
||||
var parts = url.Split('/');
|
||||
return parts.Length > 0 ? parts[^1] : url;
|
||||
}
|
||||
|
||||
private static string ExtractLabel(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = new Uri(url);
|
||||
var parts = uri.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
var poe2Idx = Array.IndexOf(parts, "poe2");
|
||||
if (poe2Idx >= 0 && parts.Length > poe2Idx + 2)
|
||||
{
|
||||
var league = Uri.UnescapeDataString(parts[poe2Idx + 1]);
|
||||
var searchId = parts[poe2Idx + 2];
|
||||
return $"{league} / {searchId}";
|
||||
}
|
||||
}
|
||||
catch { /* fallback */ }
|
||||
return url.Length > 60 ? url[..60] : url;
|
||||
}
|
||||
}
|
||||
19
src/Poe2Trade.Core/Logging.cs
Normal file
19
src/Poe2Trade.Core/Logging.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using Serilog;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Poe2Trade.Core;
|
||||
|
||||
public static class Logging
|
||||
{
|
||||
public static void Setup()
|
||||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Information()
|
||||
.WriteTo.Console(
|
||||
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
|
||||
.WriteTo.File("logs/poe2trade-.log",
|
||||
rollingInterval: RollingInterval.Day,
|
||||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
|
||||
.CreateLogger();
|
||||
}
|
||||
}
|
||||
16
src/Poe2Trade.Core/Poe2Trade.Core.csproj
Normal file
16
src/Poe2Trade.Core/Poe2Trade.Core.csproj
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
70
src/Poe2Trade.Core/Types.cs
Normal file
70
src/Poe2Trade.Core/Types.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
namespace Poe2Trade.Core;
|
||||
|
||||
public record Region(int X, int Y, int Width, int Height);
|
||||
|
||||
public record TradeInfo(
|
||||
string SearchId,
|
||||
List<string> ItemIds,
|
||||
string WhisperText,
|
||||
long Timestamp,
|
||||
string TradeUrl,
|
||||
object? Page // Playwright Page reference
|
||||
);
|
||||
|
||||
public record TradeItem(
|
||||
string Id,
|
||||
int W,
|
||||
int H,
|
||||
int StashX,
|
||||
int StashY,
|
||||
string Account
|
||||
);
|
||||
|
||||
public record LogEvent(
|
||||
DateTime Timestamp,
|
||||
LogEventType Type,
|
||||
Dictionary<string, string> Data
|
||||
);
|
||||
|
||||
public enum LogEventType
|
||||
{
|
||||
AreaEntered,
|
||||
WhisperReceived,
|
||||
TradeAccepted,
|
||||
Unknown
|
||||
}
|
||||
|
||||
public enum TradeState
|
||||
{
|
||||
Idle,
|
||||
Traveling,
|
||||
InSellersHideout,
|
||||
ScanningStash,
|
||||
Buying,
|
||||
WaitingForMore,
|
||||
GoingHome,
|
||||
InHideout,
|
||||
Failed
|
||||
}
|
||||
|
||||
public enum ScrapState
|
||||
{
|
||||
Idle,
|
||||
Traveling,
|
||||
Buying,
|
||||
Salvaging,
|
||||
Storing,
|
||||
Failed
|
||||
}
|
||||
|
||||
public enum LinkMode
|
||||
{
|
||||
Live,
|
||||
Scrap
|
||||
}
|
||||
|
||||
public enum PostAction
|
||||
{
|
||||
Stash,
|
||||
Salvage
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue