Skip to content

Commit

Permalink
Add SnipeSkins market
Browse files Browse the repository at this point in the history
  • Loading branch information
rhyskoedijk committed Jul 23, 2024
1 parent 4223143 commit df64614
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 0 deletions.
12 changes: 12 additions & 0 deletions SCMM.Market.SnipeSkins.Client/SCMM.Market.SnipeSkins.Client.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>true</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\SCMM.Shared.Web.Client\SCMM.Shared.Web.Client.csproj" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions SCMM.Market.SnipeSkins.Client/SnipeSkinsPricesResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace SCMM.Market.SnipeSkins.Client
{
public class SnipeSkinsPricesResponse : Dictionary<string, int>
{
}
}
32 changes: 32 additions & 0 deletions SCMM.Market.SnipeSkins.Client/SnipeSkinsWebClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.Extensions.Logging;
using System.Text.Json;

namespace SCMM.Market.SnipeSkins.Client
{
public class SnipeSkinsWebClient : Shared.Web.Client.WebClientBase
{
private const string WebsiteBaseUri = "https://snipeskins.com";

public SnipeSkinsWebClient(ILogger<SnipeSkinsWebClient> logger) : base(logger)
{
}

public async Task<SnipeSkinsPricesResponse> GetPricesAsync(string appId)
{
using (var client = BuildWebApiHttpClient(host: new Uri(WebsiteBaseUri)))
{
var url = $"{WebsiteBaseUri}/api/market/{appId}/prices";
var response = await RetryPolicy.ExecuteAsync(() => client.GetAsync(url));
response.EnsureSuccessStatusCode();

var textJson = await response.Content.ReadAsStringAsync();
if (string.IsNullOrEmpty(textJson))
{
return default;
}

return JsonSerializer.Deserialize<SnipeSkinsPricesResponse>(textJson);
}
}
}
}
5 changes: 5 additions & 0 deletions SCMM.Steam.Data.Models/Enums/MarketType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ public enum MarketType : byte
HouseCurrencyName = "Coin", HouseCurrencyScale = 2, HouseCurrencyToUsdExchangeRate = 0.64516129032258064516129032258065)]
Rustyloot = 36,

[Display(Name = "SnipeSkins")]
[Market(Constants.RustAppId, Color = "#f8546f")]
[BuyFrom(Url = "https://snipeskins.com/market?search={3}&sortBy=price&sortDir=asc", AcceptedPayments = PriceFlags.Cash | PriceFlags.Crypto)]
SnipeSkins = 37,

/*
MARKETS TO BE INVESTIGATED
Expand Down
2 changes: 2 additions & 0 deletions SCMM.Steam.Functions/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
using SCMM.Market.SkinsMonkey.Client.Extensions;
using SCMM.Market.SkinSwap.Client;
using SCMM.Market.SkinSwap.Client.Extensions;
using SCMM.Market.SnipeSkins.Client;
using SCMM.Market.SwapGG.Client;
using SCMM.Market.TradeitGG.Client;
using SCMM.Market.Waxpeer.Client;
Expand Down Expand Up @@ -236,6 +237,7 @@ public static IHostBuilder ConfigureServices(this IHostBuilder builder)
var configuration = services.GetService<IConfiguration>();
return configuration.GetSkinsMonkeyConfiguration();
});
services.AddSingleton<SnipeSkinsWebClient>();
services.AddSingleton<SwapGGWebClient>();
services.AddSingleton<TradeitGGWebClient>();
services.AddSingleton<WaxpeerWebClient>();
Expand Down
1 change: 1 addition & 0 deletions SCMM.Steam.Functions/SCMM.Steam.Functions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<ProjectReference Include="..\SCMM.Market.SkinSerpent.Client\SCMM.Market.SkinSerpent.Client.csproj" />
<ProjectReference Include="..\SCMM.Market.SkinsMonkey.Client\SCMM.Market.SkinsMonkey.Client.csproj" />
<ProjectReference Include="..\SCMM.Market.SkinSwap.Client\SCMM.Market.SkinSwap.Client.csproj" />
<ProjectReference Include="..\SCMM.Market.SnipeSkins.Client\SCMM.Market.SnipeSkins.Client.csproj" />
<ProjectReference Include="..\SCMM.Market.SwapGG.Client\SCMM.Market.SwapGG.Client.csproj" />
<ProjectReference Include="..\SCMM.Market.TradeitGG.Client\SCMM.Market.TradeitGG.Client.csproj" />
<ProjectReference Include="..\SCMM.Market.Waxpeer.Client\SCMM.Market.Waxpeer.Client.csproj" />
Expand Down
132 changes: 132 additions & 0 deletions SCMM.Steam.Functions/Timer/UpdateMarketItemPricesFromSnipeSkins.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using Microsoft.Azure.Functions.Worker;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SCMM.Market.SnipeSkins.Client;
using SCMM.Shared.Abstractions.Statistics;
using SCMM.Shared.Data.Models.Extensions;
using SCMM.Shared.Data.Models.Statistics;
using SCMM.Steam.Data.Models;
using SCMM.Steam.Data.Models.Enums;
using SCMM.Steam.Data.Models.Extensions;
using SCMM.Steam.Data.Store;
using SCMM.Steam.Data.Store.Types;
using System.Diagnostics;

namespace SCMM.Steam.Functions.Timer;

public class UpdateMarketItemPricesFromSnipeSkins
{
private const MarketType SnipeSkins = MarketType.SnipeSkins;

private readonly SteamDbContext _db;
private readonly SnipeSkinsWebClient _snipeSkinsWebClient;
private readonly IStatisticsService _statisticsService;

public UpdateMarketItemPricesFromSnipeSkins(SteamDbContext db, SnipeSkinsWebClient snipeSkinsWebClient, IStatisticsService statisticsService)
{
_db = db;
_snipeSkinsWebClient = snipeSkinsWebClient;
_statisticsService = statisticsService;
}

[Function("Update-Market-Item-Prices-From-SnipeSkins")]
public async Task Run([TimerTrigger("0 24/30 * * * *")] /* every 30mins */ TimerInfo timerInfo, FunctionContext context)
{
if (!SnipeSkins.IsEnabled())
{
return;
}

var logger = context.GetLogger("Update-Market-Item-Prices-From-SnipeSkins");

var appIds = SnipeSkins.GetSupportedAppIds().Select(x => x.ToString()).ToArray();
var supportedSteamApps = await _db.SteamApps
.Where(x => appIds.Contains(x.SteamId))
.ToListAsync();
if (!supportedSteamApps.Any())
{
return;
}

// Prices are returned in USD by default
var usdCurrency = _db.SteamCurrencies.FirstOrDefault(x => x.Name == Constants.SteamCurrencyUSD);
if (usdCurrency == null)
{
return;
}

foreach (var app in supportedSteamApps)
{
logger.LogTrace($"Updating market item price information from SnipeSkins (appId: {app.SteamId})");
await UpdateSnipeSkinsMarketPricesForApp(logger, app, usdCurrency);
}
}

private async Task UpdateSnipeSkinsMarketPricesForApp(ILogger logger, SteamApp app, SteamCurrency cnyCurrency)
{
var statisticsKey = String.Format(StatisticKeys.MarketStatusByAppId, app.SteamId);
var stopwatch = new Stopwatch();
try
{
stopwatch.Start();

var snipeSkinsPrices = await _snipeSkinsWebClient.GetPricesAsync(app.SteamId);
var dbItems = await _db.SteamMarketItems
.Where(x => x.AppId == app.Id)
.Select(x => new
{
Name = x.Description.NameHash,
Currency = x.Currency,
Item = x,
})
.ToListAsync();

foreach (var snipeSkinsPrice in snipeSkinsPrices)
{
var item = dbItems.FirstOrDefault(x => x.Name == snipeSkinsPrice.Key)?.Item;
if (item != null)
{
item.UpdateBuyPrices(SnipeSkins, new PriceWithSupply
{
Price = item.Currency.CalculateExchange(snipeSkinsPrice.Value, cnyCurrency),
Supply = null
});
}
}

var missingItems = dbItems.Where(x => !snipeSkinsPrices.Any(y => x.Name == y.Key) && x.Item.BuyPrices.ContainsKey(SnipeSkins));
foreach (var missingItem in missingItems)
{
missingItem.Item.UpdateBuyPrices(SnipeSkins, null);
}

await _db.SaveChangesAsync();

await _statisticsService.PatchDictionaryValueAsync<MarketType, MarketStatusStatistic>(statisticsKey, SnipeSkins, x =>
{
x.TotalItems = snipeSkinsPrices.Count();
x.TotalListings = null;
x.LastUpdatedItemsOn = DateTimeOffset.Now;
x.LastUpdatedItemsDuration = stopwatch.Elapsed;
x.LastUpdateErrorOn = null;
x.LastUpdateError = null;
});
}
catch (Exception ex)
{
try
{
logger.LogError(ex, $"Failed to update market item price information from SnipeSkins (appId: {app.SteamId}). {ex.Message}");
await _statisticsService.PatchDictionaryValueAsync<MarketType, MarketStatusStatistic>(statisticsKey, SnipeSkins, x =>
{
x.LastUpdateErrorOn = DateTimeOffset.Now;
x.LastUpdateError = ex.Message;
});
}
catch (Exception)
{
logger.LogError(ex, $"Failed to update market item price statistics for SnipeSkins (appId: {app.SteamId}). {ex.Message}");
}
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions SCMM.sln
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SCMM.Market.SkinSerpent.Cli
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SCMM.Market.Rustyloot.Client", "SCMM.Market.Rustyloot.Client\SCMM.Market.Rustyloot.Client.csproj", "{4D2911DA-FC67-4E32-8FEF-94B99E53B6A5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SCMM.Market.SnipeSkins.Client", "SCMM.Market.SnipeSkins.Client\SCMM.Market.SnipeSkins.Client.csproj", "{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -744,6 +746,18 @@ Global
{4D2911DA-FC67-4E32-8FEF-94B99E53B6A5}.Release|x64.Build.0 = Release|Any CPU
{4D2911DA-FC67-4E32-8FEF-94B99E53B6A5}.Release|x86.ActiveCfg = Release|Any CPU
{4D2911DA-FC67-4E32-8FEF-94B99E53B6A5}.Release|x86.Build.0 = Release|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Debug|x64.ActiveCfg = Debug|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Debug|x64.Build.0 = Debug|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Debug|x86.ActiveCfg = Debug|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Debug|x86.Build.0 = Debug|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Release|Any CPU.Build.0 = Release|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Release|x64.ActiveCfg = Release|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Release|x64.Build.0 = Release|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Release|x86.ActiveCfg = Release|Any CPU
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -800,6 +814,7 @@ Global
{D1212479-D353-47E8-8F48-A6D2A3E5FD7E} = {E0FE58D1-3036-485C-9289-38E68D937B94}
{1F2D2069-FD6A-475A-8ECB-852E19E48F77} = {8AE530FB-A593-4733-AF49-D1AFE5E5C01E}
{4D2911DA-FC67-4E32-8FEF-94B99E53B6A5} = {8AE530FB-A593-4733-AF49-D1AFE5E5C01E}
{8CB365F6-D035-4D07-ACDA-BB828AAABDDB} = {8AE530FB-A593-4733-AF49-D1AFE5E5C01E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {53C48CAC-8050-44A2-B930-9273F442173C}
Expand Down

0 comments on commit df64614

Please sign in to comment.