Compare commits
No commits in common. "1a08f932afd9fb9d23674be28ffd66e14b795b6a" and "01310020ce0937200a0ce422d4b849f3b2575e4d" have entirely different histories.
1a08f932af
...
01310020ce
4
.gitignore
vendored
4
.gitignore
vendored
@ -3,6 +3,4 @@ obj/
|
|||||||
/packages/
|
/packages/
|
||||||
riderModule.iml
|
riderModule.iml
|
||||||
/_ReSharper.Caches/
|
/_ReSharper.Caches/
|
||||||
.idea/
|
.idea/
|
||||||
db/
|
|
||||||
SteamGameTime.db*
|
|
@ -8,26 +8,15 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Asp.Versioning.WebApi" Version="7.1.0" />
|
|
||||||
<PackageReference Include="log4net" Version="3.1.0" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.5" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4"/>
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.5">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="8.1.2" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="8.1.2" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.2" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Migrations\" />
|
<Folder Include="Controllers\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\SQLiteEF\SQLiteEF.csproj" />
|
<ProjectReference Include="..\SQLiteEF\SQLiteEF.csproj" />
|
||||||
<ProjectReference Include="..\SteamApiWrapper\SteamApiWrapper.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
26
API/Api.cs
26
API/Api.cs
@ -1,33 +1,20 @@
|
|||||||
using System.Reflection;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.OpenApi;
|
|
||||||
using SQLiteEF;
|
using SQLiteEF;
|
||||||
|
|
||||||
namespace API;
|
namespace API;
|
||||||
|
|
||||||
public class Api
|
public class Api
|
||||||
{
|
{
|
||||||
private Tracker _tracker;
|
|
||||||
public Api(string[] args)
|
public Api(string[] args)
|
||||||
{
|
{
|
||||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
|
||||||
builder.Services.AddControllers().AddNewtonsoftJson();
|
builder.Services.AddControllers();
|
||||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||||
builder.Services.AddOpenApi(options =>
|
builder.Services.AddOpenApi();
|
||||||
{
|
|
||||||
options.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0;
|
|
||||||
});
|
|
||||||
builder.Services.AddCors(options => options.AddPolicy("AllowAll", p => p.AllowAnyOrigin().AllowAnyMethod()));
|
|
||||||
|
|
||||||
builder.Services.AddSwaggerGen();
|
|
||||||
|
|
||||||
builder.Services.AddDbContext<Context>(options => options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection"), optionsBuilder => optionsBuilder.MigrationsAssembly(Assembly.GetAssembly(GetType())!)));
|
builder.Services.AddDbContext<Context>();
|
||||||
|
|
||||||
builder.Services.AddSingleton<Tracker>();
|
|
||||||
SteamApiWrapper.SteamApiWrapper.SetApiKey(builder.Configuration.GetValue<string>("SteamWebApiKey")!);
|
|
||||||
|
|
||||||
WebApplication app = builder.Build();
|
WebApplication app = builder.Build();
|
||||||
|
|
||||||
@ -35,17 +22,14 @@ public class Api
|
|||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
app.UseSwagger();
|
|
||||||
app.UseSwaggerUI();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.Services.CreateScope().ServiceProvider.GetRequiredService<Context>().Database.Migrate();
|
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using SQLiteEF;
|
|
||||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
|
||||||
|
|
||||||
namespace API.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("[controller]")]
|
|
||||||
public class ActionsController(Context databaseContext) : ApiController(typeof(ActionsController))
|
|
||||||
{
|
|
||||||
[HttpPut("Player/{steamId}")]
|
|
||||||
[ProducesResponseType<Player>(Status202Accepted)]
|
|
||||||
[ProducesResponseType<string>(Status500InternalServerError)]
|
|
||||||
public IActionResult AddPlayerToTrack(Tracker tracker, ulong steamId)
|
|
||||||
{
|
|
||||||
Player nPlayer = new (steamId, "", "", "");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
databaseContext.Players.Add(nPlayer);
|
|
||||||
databaseContext.SaveChanges();
|
|
||||||
tracker.ForceLoop();
|
|
||||||
return Accepted(nPlayer);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
return StatusCode(Status500InternalServerError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("Update/Player/{steamId}/All")]
|
|
||||||
[ProducesResponseType(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult UpdatePlayer(Tracker tracker, ulong steamId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Players.Find(steamId) is not { } player)
|
|
||||||
return NotFound();
|
|
||||||
tracker.UpdatePlayer(databaseContext, player);
|
|
||||||
tracker.UpdateOwnedGamesPlayer(databaseContext, player);
|
|
||||||
tracker.UpdateGameTimesPlayer(databaseContext, player);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("Update/Player/{steamId}/Info")]
|
|
||||||
[ProducesResponseType(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult UpdatePlayerInfo(Tracker tracker, ulong steamId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Players.Find(steamId) is not { } player)
|
|
||||||
return NotFound();
|
|
||||||
tracker.UpdatePlayer(databaseContext, player);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("Update/Player/{steamId}/OwnedGames")]
|
|
||||||
[ProducesResponseType(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult UpdatePlayerOwnedGames(Tracker tracker, ulong steamId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Players.Find(steamId) is not { } player)
|
|
||||||
return NotFound();
|
|
||||||
tracker.UpdateOwnedGamesPlayer(databaseContext, player);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("Update/Player/{steamId}/TimeTracked")]
|
|
||||||
[ProducesResponseType(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult UpdatePlayerTimeTracked(Tracker tracker, ulong steamId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Players.Find(steamId) is not { } player)
|
|
||||||
return NotFound();
|
|
||||||
tracker.UpdateGameTimesPlayer(databaseContext, player);
|
|
||||||
return Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("Update/All")]
|
|
||||||
[ProducesResponseType(Status202Accepted)]
|
|
||||||
public IActionResult Update(Tracker tracker)
|
|
||||||
{
|
|
||||||
tracker.ForceLoop();
|
|
||||||
return Accepted();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
using log4net;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace API.Controllers;
|
|
||||||
|
|
||||||
public abstract class ApiController(Type type) : Controller
|
|
||||||
{
|
|
||||||
protected ILog Log { get; init; } = LogManager.GetLogger(type);
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using SQLiteEF;
|
|
||||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
|
||||||
|
|
||||||
namespace API.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("[controller]")]
|
|
||||||
public class DataController(Context databaseContext) : ApiController(typeof(DataController))
|
|
||||||
{
|
|
||||||
[HttpGet("Players")]
|
|
||||||
[ProducesResponseType<Player[]>(Status200OK)]
|
|
||||||
public IActionResult GetPlayers()
|
|
||||||
{
|
|
||||||
return Ok(databaseContext.Players.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("Player/{steamId}")]
|
|
||||||
[ProducesResponseType<Player>(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult GetPlayer(ulong steamId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Players.Find(steamId) is not { } player)
|
|
||||||
return NotFound();
|
|
||||||
return Ok(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("Player/{steamId}/Games")]
|
|
||||||
[ProducesResponseType<Game[]>(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult GetGamesPlayedByPlayer(ulong steamId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Players.Find(steamId) is not { } player)
|
|
||||||
return NotFound();
|
|
||||||
databaseContext.Entry(player).Collection(p => p.Games).Load();
|
|
||||||
return Ok(player.Games.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("Games")]
|
|
||||||
[ProducesResponseType<Game[]>(Status200OK)]
|
|
||||||
public IActionResult GetGames()
|
|
||||||
{
|
|
||||||
return Ok(databaseContext.Games.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("Game/{appId}")]
|
|
||||||
[ProducesResponseType<Game>(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult GetGame(ulong appId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Games.Find(appId) is not { } game)
|
|
||||||
return NotFound();
|
|
||||||
return Ok(game);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("Game/{appId}/Players")]
|
|
||||||
[ProducesResponseType<Player[]>(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult GetPlayersPlayingGame(ulong appId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Games.Find(appId) is not { } game)
|
|
||||||
return NotFound();
|
|
||||||
databaseContext.Entry(game).Collection(g => g.PlayedBy).Load();
|
|
||||||
return Ok(game.PlayedBy.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using SQLiteEF;
|
|
||||||
using static Microsoft.AspNetCore.Http.StatusCodes;
|
|
||||||
|
|
||||||
namespace API.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("[controller]")]
|
|
||||||
public class TimeTrackController(Context databaseContext) : ApiController(typeof(TimeTrackController))
|
|
||||||
{
|
|
||||||
[HttpGet("{steamId}/{appId}")]
|
|
||||||
[ProducesResponseType<TrackedTime[]>(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult GetTrackedTimeForApp(ulong steamId, ulong appId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Players.Find(steamId) is not { } player)
|
|
||||||
return NotFound();
|
|
||||||
if (databaseContext.Games.Find(appId) is not { } game)
|
|
||||||
return NotFound();
|
|
||||||
databaseContext.Entry(player).Collection(p => p.TrackedTimes).Load();
|
|
||||||
return Ok(player.TrackedTimes.Where(t => t.Game == game).ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{steamId}/Total")]
|
|
||||||
[ProducesResponseType<ulong>(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult GetTrackedTimeTotal(ulong steamId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Players.Find(steamId) is not { } player)
|
|
||||||
return NotFound();
|
|
||||||
databaseContext.Entry(player).Collection(p => p.TrackedTimes).Load();
|
|
||||||
ulong[] times = player.TrackedTimes.GroupBy(t => t.Game)
|
|
||||||
.Select(t => t.MaxBy(time => time.TimePlayed)?.TimePlayed ?? 0)
|
|
||||||
.ToArray();
|
|
||||||
ulong sum = 0;
|
|
||||||
foreach (ulong time in times)
|
|
||||||
sum += time;
|
|
||||||
return Ok(sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{steamId}/PerGame")]
|
|
||||||
[ProducesResponseType<Dictionary<ulong, ulong>>(Status200OK)]
|
|
||||||
[ProducesResponseType(Status404NotFound)]
|
|
||||||
public IActionResult GetTrackedTimeAll(ulong steamId)
|
|
||||||
{
|
|
||||||
if (databaseContext.Players.Find(steamId) is not { } player)
|
|
||||||
return NotFound();
|
|
||||||
databaseContext.Entry(player).Collection(p => p.TrackedTimes).Load();
|
|
||||||
Dictionary<ulong, ulong> trackedTimes = player.TrackedTimes
|
|
||||||
.GroupBy(t => t.Game)
|
|
||||||
.ToDictionary(t => t.Key.AppId, t => t.MaxBy(time => time.TimePlayed)?.TimePlayed??0);
|
|
||||||
|
|
||||||
return Ok(trackedTimes);
|
|
||||||
}
|
|
||||||
}
|
|
202
API/Tracker.cs
202
API/Tracker.cs
@ -1,202 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using log4net;
|
|
||||||
using SQLiteEF;
|
|
||||||
using SteamApiWrapper.ReturnTypes;
|
|
||||||
using SteamGame = SteamApiWrapper.ReturnTypes.Game;
|
|
||||||
using Player = SQLiteEF.Player;
|
|
||||||
using SteamPlayer = SteamApiWrapper.ReturnTypes.Player;
|
|
||||||
using Steam = SteamApiWrapper.SteamApiWrapper;
|
|
||||||
|
|
||||||
namespace API;
|
|
||||||
|
|
||||||
public class Tracker : IDisposable
|
|
||||||
{
|
|
||||||
private readonly Thread _trackerThread;
|
|
||||||
private bool _running = true;
|
|
||||||
private ILog Log { get; } = LogManager.GetLogger(typeof(Tracker));
|
|
||||||
private TimeSpan Interval { get; init; }
|
|
||||||
|
|
||||||
public Tracker(IServiceProvider serviceProvider, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
Interval = TimeSpan.Parse(configuration.GetValue<string>("UpdateInterval")!, CultureInfo.InvariantCulture);
|
|
||||||
_trackerThread = new (TrackerLoop);
|
|
||||||
_trackerThread.Start(serviceProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdatePlayers(Context context)
|
|
||||||
{
|
|
||||||
Log.Info("Updating Players");
|
|
||||||
Player[] dbPlayers = context.Players.ToArray();
|
|
||||||
SteamPlayer[] summaries = Steam.GetPlayerSummaries(dbPlayers.Select(p => p.SteamId).ToArray());
|
|
||||||
foreach (Player dbPlayer in dbPlayers)
|
|
||||||
{
|
|
||||||
if(summaries.All(summaryPlayer => summaryPlayer.steamid != dbPlayer.SteamId))
|
|
||||||
continue;
|
|
||||||
SteamPlayer summaryPlayer = summaries.First(summaryPlayer => summaryPlayer.steamid == dbPlayer.SteamId);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
dbPlayer.Name = summaryPlayer.personaname;
|
|
||||||
dbPlayer.AvatarUrl = summaryPlayer.avatar;
|
|
||||||
dbPlayer.ProfileUrl = summaryPlayer.profileurl;
|
|
||||||
dbPlayer.UpdatedAt = DateTime.UtcNow;
|
|
||||||
context.SaveChanges();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdatePlayer(Context context, Player player)
|
|
||||||
{
|
|
||||||
SteamPlayer[] summaries = Steam.GetPlayerSummaries([player.SteamId]);
|
|
||||||
if(summaries.All(summaryPlayer => summaryPlayer.steamid != player.SteamId))
|
|
||||||
return;
|
|
||||||
SteamPlayer summaryPlayer = summaries.First(summaryPlayer => summaryPlayer.steamid == player.SteamId);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
player.Name = summaryPlayer.personaname;
|
|
||||||
player.AvatarUrl = summaryPlayer.avatar;
|
|
||||||
player.ProfileUrl = summaryPlayer.profileurl;
|
|
||||||
player.UpdatedAt = DateTime.UtcNow;
|
|
||||||
context.SaveChanges();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateOwnedGames(Context context)
|
|
||||||
{
|
|
||||||
Log.Info("Updating Owned Games");
|
|
||||||
Player[] players = context.Players.ToArray();
|
|
||||||
foreach (Player player in players)
|
|
||||||
{
|
|
||||||
UpdateOwnedGamesPlayer(context, player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateOwnedGamesPlayer(Context context, Player player)
|
|
||||||
{
|
|
||||||
Log.Debug($"Updating owned games for player {player}");
|
|
||||||
SteamGame[] ownedGames = Steam.GetOwnedGames(player.SteamId);
|
|
||||||
foreach (SteamGame ownedGame in ownedGames)
|
|
||||||
{
|
|
||||||
if (context.Games.Find(ownedGame.appid) is not { } game)
|
|
||||||
{
|
|
||||||
game = new(ownedGame.appid, ownedGame.name);
|
|
||||||
context.Games.Add(game);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!player.Games.Contains(game))
|
|
||||||
{
|
|
||||||
context.Entry(player).Collection(p => p.Games).Load();
|
|
||||||
player.Games.Add(game);
|
|
||||||
context.Entry(game).Collection(g => g.PlayedBy).Load();
|
|
||||||
game.PlayedBy.Add(player);
|
|
||||||
context.Entry(player).Collection(p => p.TrackedTimes).Load();
|
|
||||||
player.TrackedTimes.Add(new (game, player, ownedGame.playtime_forever));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
context.SaveChanges();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateGameTimes(Context context)
|
|
||||||
{
|
|
||||||
Log.Info("Updating Game Times");
|
|
||||||
Player[] players = context.Players.ToArray();
|
|
||||||
foreach (Player player in players)
|
|
||||||
{
|
|
||||||
UpdateGameTimesPlayer(context, player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateGameTimesPlayer(Context context, Player player)
|
|
||||||
{
|
|
||||||
Log.Debug($"Updating game times for player {player}");
|
|
||||||
GetRecentlyPlayedGames recentlyPlayed = Steam.GetRecentlyPlayedGames(player.SteamId);
|
|
||||||
foreach (SteamGame recentlyPlayedGame in recentlyPlayed.games)
|
|
||||||
{
|
|
||||||
if (context.Games.Find(recentlyPlayedGame.appid) is not { } game)
|
|
||||||
{
|
|
||||||
game = new(recentlyPlayedGame.appid, recentlyPlayedGame.name);
|
|
||||||
context.Games.Add(game);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!player.Games.Contains(game))
|
|
||||||
{
|
|
||||||
context.Entry(player).Collection(p => p.Games).Load();
|
|
||||||
player.Games.Add(game);
|
|
||||||
context.Entry(game).Collection(g => g.PlayedBy).Load();
|
|
||||||
game.PlayedBy.Add(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Entry(player).Collection(p => p.TrackedTimes).Load();
|
|
||||||
player.TrackedTimes.Add(new (game, player, recentlyPlayedGame.playtime_forever));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
context.SaveChanges();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TrackerLoop(object? obj)
|
|
||||||
{
|
|
||||||
if (obj is not IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
Log.Fatal("Tracker loop wasn't started.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (_running)
|
|
||||||
{
|
|
||||||
IServiceScope scope = serviceProvider.CreateScope();
|
|
||||||
Context context = scope.ServiceProvider.GetRequiredService<Context>();
|
|
||||||
UpdatePlayers(context);
|
|
||||||
UpdateOwnedGames(context);
|
|
||||||
UpdateGameTimes(context);
|
|
||||||
scope.Dispose();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Thread.Sleep(Interval);
|
|
||||||
}
|
|
||||||
catch (ThreadInterruptedException)
|
|
||||||
{
|
|
||||||
Log.Debug("Thread interrupted");
|
|
||||||
}
|
|
||||||
catch (ThreadAbortException)
|
|
||||||
{
|
|
||||||
_running = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.Info("Thread exited");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ForceLoop()
|
|
||||||
{
|
|
||||||
_trackerThread.Interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_running = false;
|
|
||||||
_trackerThread.Interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"Logging": {
|
||||||
"DefaultConnection": "Data Source=SteamGameTime.db;"
|
"LogLevel": {
|
||||||
},
|
"Default": "Information",
|
||||||
"SteamWebApiKey": ""
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"Logging": {
|
||||||
"DefaultConnection": "Data Source=SteamGameTime.db;"
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"UpdateInterval": "00:05:00",
|
"AllowedHosts": "*"
|
||||||
"SteamWebApiKey": ""
|
|
||||||
}
|
}
|
||||||
|
12
Run/Program.cs
Normal file
12
Run/Program.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using API;
|
||||||
|
|
||||||
|
namespace Run;
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Api a = new (args);
|
||||||
|
Tracker.Tracker t = new ();
|
||||||
|
}
|
||||||
|
}
|
16
Run/Run.csproj
Normal file
16
Run/Run.csproj
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\API\API.csproj" />
|
||||||
|
<ProjectReference Include="..\Tracker\Tracker.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -1,23 +1,16 @@
|
|||||||
using log4net;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace SQLiteEF;
|
namespace SQLiteEF;
|
||||||
|
|
||||||
public class Context(DbContextOptions<Context> contextOptions) : DbContext(contextOptions)
|
public class Context(IConfiguration configuration) : DbContext
|
||||||
{
|
{
|
||||||
public DbSet<Player> Players { get; set; }
|
public DbSet<Player> Players { get; set; }
|
||||||
public DbSet<Game> Games { get; set; }
|
public DbSet<Game> Games { get; set; }
|
||||||
|
|
||||||
private ILog Log { get; } = LogManager.GetLogger(typeof(Context));
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
optionsBuilder.EnableDetailedErrors().EnableSensitiveDataLogging();
|
optionsBuilder.UseSqlite(configuration.GetConnectionString("DefaultConnection"));
|
||||||
optionsBuilder.LogTo(s => Log.Debug(s), (_, level) => level <= LogLevel.Debug);
|
|
||||||
optionsBuilder.LogTo(s => Log.Info(s), (_, level) => level == LogLevel.Information);
|
|
||||||
optionsBuilder.LogTo(s => Log.Warn(s), (_, level) => level == LogLevel.Warning);
|
|
||||||
optionsBuilder.LogTo(s => Log.Error(s), (_, level) => level == LogLevel.Error);
|
|
||||||
optionsBuilder.LogTo(s => Log.Fatal(s), (_, level) => level >= LogLevel.Critical);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
@ -27,29 +20,14 @@ public class Context(DbContextOptions<Context> contextOptions) : DbContext(conte
|
|||||||
.WithMany(g => g.PlayedBy);
|
.WithMany(g => g.PlayedBy);
|
||||||
modelBuilder.Entity<Player>()
|
modelBuilder.Entity<Player>()
|
||||||
.Navigation(p => p.Games)
|
.Navigation(p => p.Games)
|
||||||
.AutoInclude(false);
|
.AutoInclude();
|
||||||
modelBuilder.Entity<Player>()
|
|
||||||
.Navigation(p => p.TrackedTimes)
|
|
||||||
.AutoInclude(false);
|
|
||||||
modelBuilder.Entity<Game>()
|
|
||||||
.Navigation(g => g.PlayedBy)
|
|
||||||
.AutoInclude(false);
|
|
||||||
modelBuilder.Entity<Game>()
|
|
||||||
.Navigation(g => g.TrackedTimes)
|
|
||||||
.AutoInclude(false);
|
|
||||||
modelBuilder.Entity<TrackedTime>()
|
modelBuilder.Entity<TrackedTime>()
|
||||||
.HasOne<Player>(p => p.Player)
|
.HasOne<Player>(p => p.Player)
|
||||||
.WithMany(p => p.TrackedTimes)
|
.WithMany(p => p.TrackedTimes)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
modelBuilder.Entity<TrackedTime>()
|
|
||||||
.Navigation(t => t.Player)
|
|
||||||
.AutoInclude();
|
|
||||||
modelBuilder.Entity<TrackedTime>()
|
modelBuilder.Entity<TrackedTime>()
|
||||||
.HasOne<Game>(p => p.Game)
|
.HasOne<Game>(p => p.Game)
|
||||||
.WithMany(g => g.TrackedTimes)
|
.WithMany(g => g.TrackedTimes)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
modelBuilder.Entity<TrackedTime>()
|
|
||||||
.Navigation(t => t.Game)
|
|
||||||
.AutoInclude();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace SQLiteEF;
|
namespace SQLiteEF;
|
||||||
|
|
||||||
@ -8,6 +7,6 @@ public class Game(ulong appId, string name)
|
|||||||
{
|
{
|
||||||
public ulong AppId { get; init; } = appId;
|
public ulong AppId { get; init; } = appId;
|
||||||
public string Name { get; init; } = name;
|
public string Name { get; init; } = name;
|
||||||
[JsonIgnore] public ICollection<Player> PlayedBy { get; init; } = null!;
|
public ICollection<Player> PlayedBy { get; init; } = null!;
|
||||||
[JsonIgnore] public ICollection<TrackedTime> TrackedTimes { get; init; } = null!;
|
public ICollection<TrackedTime> TrackedTimes { get; init; } = null!;
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace SQLiteEF;
|
namespace SQLiteEF;
|
||||||
|
|
||||||
@ -11,13 +10,13 @@ public class Player : IUpdateable
|
|||||||
public string ProfileUrl { get; set; }
|
public string ProfileUrl { get; set; }
|
||||||
public string AvatarUrl { get; set; }
|
public string AvatarUrl { get; set; }
|
||||||
|
|
||||||
[JsonIgnore] public ICollection<Game> Games { get; init; } = null!;
|
public ICollection<Game> Games { get; init; } = null!;
|
||||||
[JsonIgnore] public ICollection<TrackedTime> TrackedTimes { get; init; } = null!;
|
public ICollection<TrackedTime> TrackedTimes { get; init; } = null!;
|
||||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime UpdatedAt { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
public Player(ulong steamId, string name, string profileUrl, string avatarUrl)
|
public Player(ulong steamid, string name, string profileUrl, string avatarUrl)
|
||||||
{
|
{
|
||||||
this.SteamId = steamId;
|
this.SteamId = steamid;
|
||||||
this.Name = name;
|
this.Name = name;
|
||||||
this.ProfileUrl = profileUrl;
|
this.ProfileUrl = profileUrl;
|
||||||
this.AvatarUrl = avatarUrl;
|
this.AvatarUrl = avatarUrl;
|
||||||
@ -28,9 +27,9 @@ public class Player : IUpdateable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// EF CORE
|
/// EF CORE
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal Player(ulong steamId, string name, string profileUrl, string avatarUrl, DateTime updatedAt)
|
internal Player(ulong steamid, string name, string profileUrl, string avatarUrl, DateTime updatedAt)
|
||||||
{
|
{
|
||||||
this.SteamId = steamId;
|
this.SteamId = steamid;
|
||||||
this.Name = name;
|
this.Name = name;
|
||||||
this.ProfileUrl = profileUrl;
|
this.ProfileUrl = profileUrl;
|
||||||
this.AvatarUrl = avatarUrl;
|
this.AvatarUrl = avatarUrl;
|
||||||
|
@ -7,14 +7,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="log4net" Version="3.1.0" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.5">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.5" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.5" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace SQLiteEF;
|
namespace SQLiteEF;
|
||||||
|
|
||||||
[PrimaryKey("TimeStamp")]
|
[PrimaryKey("TimeStamp")]
|
||||||
public class TrackedTime
|
public class TrackedTime
|
||||||
{
|
{
|
||||||
[JsonIgnore] public Game Game { get; init; }
|
public Game Game { get; init; }
|
||||||
[JsonIgnore] public Player Player { get; init; }
|
public Player Player { get; init; }
|
||||||
public DateTime TimeStamp { get; init; }
|
public DateTime TimeStamp { get; init; }
|
||||||
public ulong TimePlayed { get; init; }
|
public ulong TimePlayed { get; init; }
|
||||||
|
|
||||||
@ -15,7 +14,7 @@ public class TrackedTime
|
|||||||
{
|
{
|
||||||
this.Game = game;
|
this.Game = game;
|
||||||
this.Player = player;
|
this.Player = player;
|
||||||
this.TimeStamp = timeStamp??DateTime.UtcNow;
|
this.TimeStamp = timeStamp??DateTime.Now;
|
||||||
this.TimePlayed = timePlayed;
|
this.TimePlayed = timePlayed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
3
SteamApiWrapper/ReturnTypes/GetOwnedGames.cs
Normal file
3
SteamApiWrapper/ReturnTypes/GetOwnedGames.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace SteamApiWrapper.ReturnTypes;
|
||||||
|
|
||||||
|
public record GetOwnedGames(uint game_count, Game[] games) : IReturnType;
|
3
SteamApiWrapper/ReturnTypes/GetPlayerSummaries.cs
Normal file
3
SteamApiWrapper/ReturnTypes/GetPlayerSummaries.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace SteamApiWrapper.ReturnTypes;
|
||||||
|
|
||||||
|
public record GetPlayerSummaries(Player[] players) : IReturnType;
|
@ -1,4 +1,3 @@
|
|||||||
using log4net;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using SteamApiWrapper.ReturnTypes;
|
using SteamApiWrapper.ReturnTypes;
|
||||||
|
|
||||||
@ -6,10 +5,9 @@ namespace SteamApiWrapper;
|
|||||||
|
|
||||||
public static class SteamApiWrapper
|
public static class SteamApiWrapper
|
||||||
{
|
{
|
||||||
public const string ApiUrl = "http://api.steampowered.com";
|
public const string ApiUrl = "http://api.steampowered.com/";
|
||||||
private static string _apiKey = string.Empty;
|
private static string _apiKey = string.Empty;
|
||||||
private static HttpClient client = new ();
|
private static HttpClient client = new ();
|
||||||
private static ILog Log { get; } = LogManager.GetLogger(typeof(SteamApiWrapper));
|
|
||||||
|
|
||||||
public static void SetApiKey(string apiKey)
|
public static void SetApiKey(string apiKey)
|
||||||
{
|
{
|
||||||
@ -27,93 +25,81 @@ public static class SteamApiWrapper
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Game[] GetOwnedGames(ulong steamid)
|
public static GetOwnedGames GetOwnedGames(ulong steamid)
|
||||||
{
|
{
|
||||||
Log.Debug("GetOwnedGames");
|
|
||||||
HttpRequestMessage request = BuildRequest(SupportedAPIInterface.IPlayerService, "GetOwnedGames", opts: new()
|
HttpRequestMessage request = BuildRequest(SupportedAPIInterface.IPlayerService, "GetOwnedGames", opts: new()
|
||||||
{
|
{
|
||||||
{"steamid", steamid.ToString()},
|
{"steamid", steamid.ToString()},
|
||||||
{"include_appinfo", "true"},
|
{"include_appinfo", "true"},
|
||||||
{"include_played_free_games", "true"}
|
{"include_played_free_games", "true"}
|
||||||
});
|
});
|
||||||
Log.Debug(request.ToString());
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = client.Send(request);
|
HttpResponseMessage response = client.Send(request);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
JObject jObj = JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
JObject jObj = JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
||||||
Log.Debug(jObj);
|
return jObj["response"]?.ToObject<GetOwnedGames>()??new(0, []);
|
||||||
return jObj["response"]?["games"]?.ToObject<Game[]>()??[];
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error(e);
|
Console.WriteLine(e.Message);
|
||||||
return [];
|
return new(0, []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Player[] GetPlayerSummaries(ulong[] steamids)
|
public static GetPlayerSummaries GetPlayerSummaries(ulong[] steamids)
|
||||||
{
|
{
|
||||||
Log.Debug("GetPlayerSummaries");
|
|
||||||
HttpRequestMessage request = BuildRequest(SupportedAPIInterface.ISteamUser, "GetPlayerSummaries", "v0002", opts: new()
|
HttpRequestMessage request = BuildRequest(SupportedAPIInterface.ISteamUser, "GetPlayerSummaries", "v0002", opts: new()
|
||||||
{
|
{
|
||||||
{"steamids", string.Join(',', steamids)}
|
{"steamids", string.Join(',', steamids)}
|
||||||
});
|
});
|
||||||
Log.Debug(request.ToString());
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = client.Send(request);
|
HttpResponseMessage response = client.Send(request);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
JObject jObj = JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
JObject jObj = JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
||||||
Log.Debug(jObj);
|
return jObj["response"]?["players"]?.ToObject<GetPlayerSummaries>()??new([]);
|
||||||
return jObj["response"]?["players"]?.ToObject<Player[]>()??[];
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error(e);
|
Console.WriteLine(e.Message);
|
||||||
return [];
|
return new([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GetRecentlyPlayedGames GetRecentlyPlayedGames(ulong steamid)
|
public static GetRecentlyPlayedGames GetRecentlyPlayedGames(ulong steamid)
|
||||||
{
|
{
|
||||||
Log.Debug("GetRecentlyPlayedGames");
|
|
||||||
HttpRequestMessage request = BuildRequest(SupportedAPIInterface.IPlayerService, "GetRecentlyPlayedGames", opts: new()
|
HttpRequestMessage request = BuildRequest(SupportedAPIInterface.IPlayerService, "GetRecentlyPlayedGames", opts: new()
|
||||||
{
|
{
|
||||||
{"steamid", steamid.ToString()}
|
{"steamid", steamid.ToString()}
|
||||||
});
|
});
|
||||||
Log.Debug(request.ToString());
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = client.Send(request);
|
HttpResponseMessage response = client.Send(request);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
JObject jObj = JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
JObject jObj = JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
||||||
Log.Debug(jObj);
|
|
||||||
return jObj["response"]?.ToObject<GetRecentlyPlayedGames>()??new(0, []);
|
return jObj["response"]?.ToObject<GetRecentlyPlayedGames>()??new(0, []);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error(e);
|
Console.WriteLine(e.Message);
|
||||||
return new(0, []);
|
return new(0, []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GetSupportedAPIList GetSupportedAPIList()
|
public static GetSupportedAPIList GetSupportedAPIList()
|
||||||
{
|
{
|
||||||
Log.Debug("GetSupportedAPIList");
|
|
||||||
HttpRequestMessage request = BuildRequest(SupportedAPIInterface.ISteamWebAPIUtil, "GetSupportedAPIList");
|
HttpRequestMessage request = BuildRequest(SupportedAPIInterface.ISteamWebAPIUtil, "GetSupportedAPIList");
|
||||||
Log.Debug(request.ToString());
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = client.Send(request);
|
HttpResponseMessage response = client.Send(request);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
JObject jObj = JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
JObject jObj = JObject.Parse(response.Content.ReadAsStringAsync().Result);
|
||||||
Log.Debug(jObj);
|
|
||||||
return jObj["apilist"]?.ToObject<GetSupportedAPIList>()??new([]);
|
return jObj["apilist"]?.ToObject<GetSupportedAPIList>()??new([]);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Log.Error(e);
|
Console.WriteLine(e.Message);
|
||||||
return new([]);
|
return new([]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="log4net" Version="3.1.0" />
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -4,6 +4,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "API\API.csproj", "{9
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamApiWrapper", "SteamApiWrapper\SteamApiWrapper.csproj", "{53B416A7-E3B9-4F19-9A71-16CA115BB127}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamApiWrapper", "SteamApiWrapper\SteamApiWrapper.csproj", "{53B416A7-E3B9-4F19-9A71-16CA115BB127}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Run", "Run\Run.csproj", "{959773AD-4671-4FC7-9729-0EE4A971918E}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tracker", "Tracker\Tracker.csproj", "{59876ADC-5DF9-4672-8AA2-F6943966ABF5}"
|
||||||
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLiteEF", "SQLiteEF\SQLiteEF.csproj", "{A269EE55-A8E1-4421-9A70-D6F36D162C4D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLiteEF", "SQLiteEF\SQLiteEF.csproj", "{A269EE55-A8E1-4421-9A70-D6F36D162C4D}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
@ -20,6 +24,14 @@ Global
|
|||||||
{53B416A7-E3B9-4F19-9A71-16CA115BB127}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{53B416A7-E3B9-4F19-9A71-16CA115BB127}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{53B416A7-E3B9-4F19-9A71-16CA115BB127}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{53B416A7-E3B9-4F19-9A71-16CA115BB127}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{53B416A7-E3B9-4F19-9A71-16CA115BB127}.Release|Any CPU.Build.0 = Release|Any CPU
|
{53B416A7-E3B9-4F19-9A71-16CA115BB127}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{959773AD-4671-4FC7-9729-0EE4A971918E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{959773AD-4671-4FC7-9729-0EE4A971918E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{959773AD-4671-4FC7-9729-0EE4A971918E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{959773AD-4671-4FC7-9729-0EE4A971918E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{59876ADC-5DF9-4672-8AA2-F6943966ABF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{59876ADC-5DF9-4672-8AA2-F6943966ABF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{59876ADC-5DF9-4672-8AA2-F6943966ABF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{59876ADC-5DF9-4672-8AA2-F6943966ABF5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{A269EE55-A8E1-4421-9A70-D6F36D162C4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{A269EE55-A8E1-4421-9A70-D6F36D162C4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{A269EE55-A8E1-4421-9A70-D6F36D162C4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A269EE55-A8E1-4421-9A70-D6F36D162C4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A269EE55-A8E1-4421-9A70-D6F36D162C4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{A269EE55-A8E1-4421-9A70-D6F36D162C4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
155
Tracker/Tracker.cs
Normal file
155
Tracker/Tracker.cs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using SQLiteEF;
|
||||||
|
using SteamApiWrapper.ReturnTypes;
|
||||||
|
using SteamGame = SteamApiWrapper.ReturnTypes.Game;
|
||||||
|
using Player = SQLiteEF.Player;
|
||||||
|
using SteamPlayer = SteamApiWrapper.ReturnTypes.Player;
|
||||||
|
using Steam = SteamApiWrapper.SteamApiWrapper;
|
||||||
|
|
||||||
|
namespace Tracker;
|
||||||
|
|
||||||
|
public class Tracker : IDisposable
|
||||||
|
{
|
||||||
|
private IConfiguration Configuration { get; init; }
|
||||||
|
private readonly Thread _trackerThread;
|
||||||
|
private bool _running = true;
|
||||||
|
|
||||||
|
public Tracker()
|
||||||
|
{
|
||||||
|
Configuration = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("appsettings.json")
|
||||||
|
.Build();
|
||||||
|
_trackerThread = new (TrackerLoop);
|
||||||
|
_trackerThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePlayers()
|
||||||
|
{
|
||||||
|
Context context = new (Configuration);
|
||||||
|
IQueryable<ulong> players = context.Players.Select(p => p.SteamId);
|
||||||
|
GetPlayerSummaries summaries = Steam.GetPlayerSummaries(players.ToArray());
|
||||||
|
foreach (SteamPlayer summariesPlayer in summaries.players)
|
||||||
|
{
|
||||||
|
if (context.Players.Find(summariesPlayer.steamid) is not { } player)
|
||||||
|
{
|
||||||
|
context.Players.Add(new (summariesPlayer.steamid, summariesPlayer.personaname,
|
||||||
|
summariesPlayer.profileurl, summariesPlayer.avatar));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
player.Name = summariesPlayer.personaname;
|
||||||
|
player.ProfileUrl = summariesPlayer.profileurl;
|
||||||
|
player.AvatarUrl = summariesPlayer.avatar;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
context.SaveChanges();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateOwnedGames()
|
||||||
|
{
|
||||||
|
Context context = new (Configuration);
|
||||||
|
IQueryable<Player> players = context.Players;
|
||||||
|
foreach (Player player in players)
|
||||||
|
{
|
||||||
|
GetOwnedGames ownedGames = Steam.GetOwnedGames(player.SteamId);
|
||||||
|
foreach (SteamGame ownedGame in ownedGames.games)
|
||||||
|
{
|
||||||
|
if (context.Games.Find(ownedGame.appid) is not { } game)
|
||||||
|
{
|
||||||
|
game = new(ownedGame.appid, ownedGame.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player.Games.Contains(game))
|
||||||
|
{
|
||||||
|
player.Games.Add(game);
|
||||||
|
game.PlayedBy.Add(player);
|
||||||
|
player.TrackedTimes.Add(new (game, player, ownedGame.playtime_forever));
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
context.SaveChanges();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateGametimes()
|
||||||
|
{
|
||||||
|
Context context = new (Configuration);
|
||||||
|
IQueryable<Player> players = context.Players;
|
||||||
|
foreach (Player player in players)
|
||||||
|
{
|
||||||
|
GetRecentlyPlayedGames recentlyPlayed = Steam.GetRecentlyPlayedGames(player.SteamId);
|
||||||
|
foreach (SteamGame recentlyPlayedGame in recentlyPlayed.games)
|
||||||
|
{
|
||||||
|
if (context.Games.Find(recentlyPlayedGame.appid) is not { } game)
|
||||||
|
{
|
||||||
|
game = new(recentlyPlayedGame.appid, recentlyPlayedGame.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player.Games.Contains(game))
|
||||||
|
{
|
||||||
|
player.Games.Add(game);
|
||||||
|
game.PlayedBy.Add(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.TrackedTimes.Add(new (game, player, recentlyPlayedGame.playtime_forever));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
context.SaveChanges();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TrackerLoop()
|
||||||
|
{
|
||||||
|
while (_running)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Thread.Sleep(TimeSpan.FromHours(1));
|
||||||
|
}
|
||||||
|
catch (ThreadInterruptedException)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Thread interrupted");
|
||||||
|
}
|
||||||
|
catch (ThreadAbortException)
|
||||||
|
{
|
||||||
|
_running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Console.WriteLine("Thread exited");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ForceLoop()
|
||||||
|
{
|
||||||
|
_trackerThread.Interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Tracker _ = new ();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_running = false;
|
||||||
|
_trackerThread.Interrupt();
|
||||||
|
}
|
||||||
|
}
|
21
Tracker/Tracker.csproj
Normal file
21
Tracker/Tracker.csproj
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\SQLiteEF\SQLiteEF.csproj" />
|
||||||
|
<ProjectReference Include="..\SteamApiWrapper\SteamApiWrapper.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.5" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
Loading…
x
Reference in New Issue
Block a user