2024-03-01 11:11:57 +01:00

199 lines
7.1 KiB
C#

using GlaxLogger;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace SteamGameTimeTrack;
public class Tracker : IDisposable
{
private bool _running = true;
// ReSharper disable once MemberInitializerValueIgnored
private readonly ILogger _logger = null!;
internal readonly HashSet<Player> _players = new();
private readonly Net net;
private readonly Config config;
private readonly API api;
private readonly TimeSpan _updateFriendsInterval = TimeSpan.FromMinutes(30);
private readonly TimeSpan _updateGamesInterval = TimeSpan.FromMinutes(60);
private readonly TimeSpan _updateInfoInterval = TimeSpan.FromSeconds(60);
private readonly Thread _updateFriendsThread, _updateGamesThread, _updateInfoThread;
public Tracker(Config config)
{
this.config = config;
this._logger = new Logger(config.LogLevel, consoleOut: Console.Out);
this.net = new(this._logger, config.ApiToken);
this._logger.LogInformation(config.ToString());
LoadKnownInformation();
if(!_players.Any(player => player.IsMe))
_players.Add(new Player(config.SteamId, net, true));
if (config.TrackFriends)
UpdateFriends();
UpdatePlayerGames();
UpdatePlayerInfo();
this._updateFriendsThread = new (() =>
{
lock(_updateFriendsThread!)
{
while (_running)
{
_logger.LogInformation($"Waiting {_updateFriendsInterval:g} for next Friends-List-Update. {DateTime.Now.Add(_updateFriendsInterval):HH:mm:ss zz}");
Monitor.Wait(_updateFriendsThread, _updateFriendsInterval);
if (!_running)
break;
UpdateFriends();
}
}
});
this._updateFriendsThread.Start();
this._updateGamesThread = new(() =>
{
lock(_updateGamesThread!)
{
while (_running)
{
_logger.LogInformation($"Waiting {_updateGamesInterval:g} for next Player-Games-Update. {DateTime.Now.Add(_updateGamesInterval):HH:mm:ss zz}");
Monitor.Wait(_updateGamesThread, _updateGamesInterval);
if (!_running)
break;
UpdatePlayerGames();
}
}
});
this._updateGamesThread.Start();
this._updateInfoThread = new(() =>
{
lock(_updateInfoThread!)
{
while (_running)
{
_logger.LogInformation($"Waiting {_updateInfoInterval:g} for next Player-Info-Update. {DateTime.Now.Add(_updateInfoInterval):HH:mm:ss zz}");
Monitor.Wait(_updateInfoThread, _updateInfoInterval);
if (!_running)
break;
UpdatePlayerInfo();
}
}
});
this._updateInfoThread.Start();
this.api = new(this, config.ApiPort, this._logger);
}
public Tracker(string steamId, string apiToken, bool trackFriends = true, int apiPort = 8888, LogLevel logLevel = LogLevel.Information) : this(new Config()
{
ApiToken = apiToken,
LogLevel = logLevel,
SteamId = steamId,
TrackFriends = trackFriends,
ApiPort = apiPort
})
{
File.WriteAllText("config.json", JsonConvert.SerializeObject(config, Formatting.Indented));
}
private void UpdateFriends()
{
this._logger.LogInformation("Getting Friends-List...");
JObject? friendsResult = net.MakeRequestGetJObject($"https://api.steampowered.com/ISteamUser/GetFriendList/v0001/?steamid={config.SteamId}&relationship=friend");
if (friendsResult is not null && friendsResult.TryGetValue("friendslist", out JToken? value) && value.SelectToken("friends") is not null)
{
this._logger.LogDebug("Got Friends-List.");
foreach (JToken friend in value["friends"]!)
{
string steamid = friend["steamid"]!.Value<string>()!;
if(!_players.Any(player => player.steamid.Equals(steamid)))
this._players.Add(new(steamid, net)
{
friend_since = friend["friend_since"]!.Value<long>()
});
}
this._logger.LogInformation($"Got {this._players.Count - 1} friends.");
}
}
private void UpdatePlayerGames()
{
int i = 1;
foreach (Player player in this._players)
{
_logger.LogInformation($"Getting GameInfo {i++}/{_players.Count} {player.personaname} {player.steamid}");
player.GetGames();
}
}
private void UpdatePlayerInfo()
{
this._logger.LogInformation("Getting Player-info...");
string steamIds = string.Join(',', _players.Select(player => player.steamid));
JObject? result = net.MakeRequestGetJObject($"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?steamids={steamIds}");
if (result is not null && result.TryGetValue("response", out JToken? response))
{
foreach (JToken player in response["players"]!)
{
Player p = player.ToObject<Player>();
UpdatePlayer(p);
}
}
this._logger.LogInformation("Done getting Player-info...");
}
private void LoadKnownInformation()
{
if (!Directory.Exists("states"))
return;
foreach (DirectoryInfo subDir in new DirectoryInfo("states").GetDirectories())
{
FileInfo? latest = subDir.GetFiles().MaxBy(file => file.CreationTimeUtc);
if (latest is not null)
{
Player player = JsonConvert.DeserializeObject<Player>(File.ReadAllText(latest.FullName), new Player.PlayerJsonConverter(net));
this._players.Add(player);
}
}
}
private void UpdatePlayer(Player player)
{
this._logger.LogDebug($"Updating Player {player.personaname} {player.steamid}");
Player p = this._players.First(pp => pp.steamid.Equals(player.steamid));
this._players.Remove(p);
Player updated = p.WithInfo(player);
this._players.Add(updated);
if (p.Diffs(updated, out string diffStr))
{
updated.Export();
this._logger.LogDebug($"Diffs: {diffStr}");
}
}
public void Dispose()
{
this._logger.LogInformation("Stopping...");
_running = false;
lock (_updateInfoThread)
{
Monitor.Pulse(_updateInfoThread);
}
lock (_updateGamesThread)
{
Monitor.Pulse(_updateGamesThread);
}
lock (_updateFriendsThread)
{
Monitor.Pulse(_updateFriendsThread);
}
this._logger.LogInformation("Exporting Player-States...");
foreach (Player player in this._players)
player.ShuttingDownSaveSessions();
}
}