diff --git a/API/Api.cs b/API/Api.cs new file mode 100644 index 0000000..809fc9e --- /dev/null +++ b/API/Api.cs @@ -0,0 +1,41 @@ +using SQLiteEF; + +namespace API; + +public class Api +{ + public Api(string[] args) + { + WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + + builder.Services.AddControllers(); + // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi + builder.Services.AddOpenApi(); + + builder.Services.AddDbContext<Context>(); + + WebApplication app = builder.Build(); + + // Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) + { + app.MapOpenApi(); + } + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + + + app.MapControllers(); + + app.Run(); + } + + public static void Main(string[] args) + { + Api _ = new (args); + } +} \ No newline at end of file diff --git a/API/Properties/launchSettings.json b/API/Properties/launchSettings.json new file mode 100644 index 0000000..e25a4ca --- /dev/null +++ b/API/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5239", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://localhost:7103;http://localhost:5239", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/API/appsettings.Development.json b/API/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/API/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/API/appsettings.json b/API/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/API/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Run/Program.cs b/Run/Program.cs new file mode 100644 index 0000000..546589b --- /dev/null +++ b/Run/Program.cs @@ -0,0 +1,12 @@ +using API; + +namespace Run; + +class Program +{ + static void Main(string[] args) + { + Api a = new (args); + Tracker.Tracker t = new (); + } +} \ No newline at end of file diff --git a/Run/Run.csproj b/Run/Run.csproj new file mode 100644 index 0000000..1c8f52c --- /dev/null +++ b/Run/Run.csproj @@ -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> diff --git a/SQLiteEF/Player.cs b/SQLiteEF/Player.cs new file mode 100644 index 0000000..db048f3 --- /dev/null +++ b/SQLiteEF/Player.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore; + +namespace SQLiteEF; + +[PrimaryKey("SteamId")] +public class Player : IUpdateable +{ + public ulong SteamId { get; init; } + public string Name { get; set; } + public string ProfileUrl { get; set; } + public string AvatarUrl { get; set; } + + public ICollection<Game> Games { get; init; } = null!; + public ICollection<TrackedTime> TrackedTimes { get; init; } = null!; + public DateTime UpdatedAt { get; set; } = DateTime.Now; + + public Player(ulong steamid, string name, string profileUrl, string avatarUrl) + { + this.SteamId = steamid; + this.Name = name; + this.ProfileUrl = profileUrl; + this.AvatarUrl = avatarUrl; + this.Games = []; + this.TrackedTimes = []; + } + + /// <summary> + /// EF CORE + /// </summary> + internal Player(ulong steamid, string name, string profileUrl, string avatarUrl, DateTime updatedAt) + { + this.SteamId = steamid; + this.Name = name; + this.ProfileUrl = profileUrl; + this.AvatarUrl = avatarUrl; + this.UpdatedAt = updatedAt; + } +} \ No newline at end of file diff --git a/SQLiteEF/SQLiteEF.csproj b/SQLiteEF/SQLiteEF.csproj new file mode 100644 index 0000000..1211f9b --- /dev/null +++ b/SQLiteEF/SQLiteEF.csproj @@ -0,0 +1,14 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net9.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" /> + <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.5" /> + </ItemGroup> + +</Project> diff --git a/Tracker/Tracker.cs b/Tracker/Tracker.cs new file mode 100644 index 0000000..b399ad3 --- /dev/null +++ b/Tracker/Tracker.cs @@ -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(); + } +} \ No newline at end of file diff --git a/Tracker/Tracker.csproj b/Tracker/Tracker.csproj new file mode 100644 index 0000000..c46201b --- /dev/null +++ b/Tracker/Tracker.csproj @@ -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>