From 7154f4dec55901ef463857b764f46db1dd3f18c4 Mon Sep 17 00:00:00 2001 From: chr_ Date: Fri, 8 Mar 2024 13:59:40 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E4=BF=AE=E6=94=B9IPC=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E9=80=82=E9=85=8D=E6=96=B0=E7=9A=84=E8=B4=AD=E7=89=A9?= =?UTF-8?q?=E8=BD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ASFEnhance/ASFEnhance.csproj | 3 +- ASFEnhance/Account/WebRequest.cs | 2 +- ASFEnhance/Cart/Command.cs | 12 +- ASFEnhance/Cart/WebRequest.cs | 111 ++--- .../AddItemsToCartRequest.cs | 2 +- .../IAccountCartService/GetCartResponse.cs | 4 +- ASFEnhance/Event/Command.cs | 4 +- .../IPC/Controllers/PurchaseController.cs | 461 +++++++++--------- ASFEnhance/IPC/Requests/AddCartRequest.cs | 72 +++ ASFEnhance/IPC/Responses/BotCartResponse.cs | 24 + ASFEnhance/Store/WebRequest.cs | 2 +- ASFEnhance/Utils.cs | 6 + Directory.Build.props | 2 +- 13 files changed, 415 insertions(+), 290 deletions(-) create mode 100644 ASFEnhance/IPC/Requests/AddCartRequest.cs create mode 100644 ASFEnhance/IPC/Responses/BotCartResponse.cs diff --git a/ASFEnhance/ASFEnhance.csproj b/ASFEnhance/ASFEnhance.csproj index 26cfbfa..f6eae38 100644 --- a/ASFEnhance/ASFEnhance.csproj +++ b/ASFEnhance/ASFEnhance.csproj @@ -1,4 +1,4 @@ - + Library @@ -53,7 +53,6 @@ - \ No newline at end of file diff --git a/ASFEnhance/Account/WebRequest.cs b/ASFEnhance/Account/WebRequest.cs index 3029208..5ae6f65 100644 --- a/ASFEnhance/Account/WebRequest.cs +++ b/ASFEnhance/Account/WebRequest.cs @@ -299,7 +299,7 @@ internal static async Task RemoveLicense(Bot bot, uint subId) new(NotificationType.SteamTurnNotification,option.SteamTurnNotification), }; - var json = JsonConvert.SerializeObject(optionList); + var json = JsonConvert.SerializeObject(optionList, JsonOptions); var data = new Dictionary(11) { { "notificationpreferences", json }, diff --git a/ASFEnhance/Cart/Command.cs b/ASFEnhance/Cart/Command.cs index 6531001..ca4e49a 100644 --- a/ASFEnhance/Cart/Command.cs +++ b/ASFEnhance/Cart/Command.cs @@ -37,11 +37,11 @@ internal static class Command { if (item.PackageId > 0) { - gameIds.Add(new MyGameId { Id = item.PackageId, Type = EGameIdType.PackageId }); + gameIds.Add(new MyGameId { Id = item.PackageId.Value, Type = EGameIdType.PackageId }); } else if (item.BundleId > 0) { - gameIds.Add(new MyGameId { Id = item.BundleId, Type = EGameIdType.BundleId }); + gameIds.Add(new MyGameId { Id = item.BundleId.Value, Type = EGameIdType.BundleId }); } } @@ -85,7 +85,7 @@ internal static class Command } var price = item.PriceWhenAdded?.FormattedAmount ?? "??"; - if (!gameNameDict.TryGetValue(item.PackageId + item.BundleId, out var gameName)) + if (!gameNameDict.TryGetValue(item.PackageId ?? item.BundleId ?? 0, out var gameName)) { gameName = Langs.KeyNotFound; } @@ -212,11 +212,11 @@ internal static class Command { if (item.PackageId > 0) { - ids.Add(new MyGameId { Id = item.PackageId, Type = EGameIdType.PackageId }); + ids.Add(new MyGameId { Id = item.PackageId.Value, Type = EGameIdType.PackageId }); } else if (item.BundleId > 0) { - ids.Add(new MyGameId { Id = item.BundleId, Type = EGameIdType.BundleId }); + ids.Add(new MyGameId { Id = item.BundleId.Value, Type = EGameIdType.BundleId }); } } @@ -243,7 +243,7 @@ internal static class Command } var price = item.PriceWhenAdded?.FormattedAmount ?? "??"; - if (!gameNameDict.TryGetValue(item.PackageId + item.BundleId, out var gameName)) + if (!gameNameDict.TryGetValue(item.PackageId ?? item.BundleId ?? 0, out var gameName)) { gameName = Langs.KeyNotFound; } diff --git a/ASFEnhance/Cart/WebRequest.cs b/ASFEnhance/Cart/WebRequest.cs index 4981843..a8b0c04 100644 --- a/ASFEnhance/Cart/WebRequest.cs +++ b/ASFEnhance/Cart/WebRequest.cs @@ -24,23 +24,15 @@ internal static class WebRequest return response?.Content?.Response; } - internal static Task AddItemToAccountCart(this Bot bot, SteamGameId gameId, bool isPrivate, AddItemsToCartRequest.GiftInfoData? giftInfo) - { - var myGameId = new MyGameId - { - Id = gameId.GameId, - Type = gameId.Type switch - { - ESteamGameIdType.App => EGameIdType.AppId, - ESteamGameIdType.Sub => EGameIdType.PackageId, - ESteamGameIdType.Bundle => EGameIdType.BundleId, - _ => throw new ArgumentOutOfRangeException(nameof(gameId.Type), gameId.Type, null) - } - }; - - return AddItemToAccountCart(bot, myGameId, isPrivate, giftInfo); - } - + /// + /// 添加购物车项目 + /// + /// + /// + /// + /// + /// + /// internal static async Task AddItemToAccountCart(this Bot bot, MyGameId gameId, bool isPrivate, AddItemsToCartRequest.GiftInfoData? giftInfo) { var payload = new AddItemsToCartRequest @@ -80,7 +72,7 @@ internal static class WebRequest }, }; - var json = JsonConvert.SerializeObject(payload); + var json = JsonConvert.SerializeObject(payload, JsonOptions); var token = bot.AccessToken ?? throw new AccessTokenNullException(); var request = new Uri(SteamApiURL, $"/IAccountCartService/AddItemsToCart/v1/?access_token={token}"); var data = new Dictionary @@ -92,6 +84,15 @@ internal static class WebRequest return response?.Content?.Response; } + /// + /// 添加购物车项目 + /// + /// + /// + /// + /// + /// + /// internal static async Task AddItemToAccountsCart(this Bot bot, List gameIds, bool isPrivate, AddItemsToCartRequest.GiftInfoData? giftInfo) { var items = gameIds.Select(x => new AddItemsToCartRequest.ItemData @@ -130,61 +131,61 @@ internal static class WebRequest }, }; - var json = JsonConvert.SerializeObject(payload); + var json = JsonConvert.SerializeObject(payload, JsonOptions); var token = bot.AccessToken ?? throw new AccessTokenNullException(); var request = new Uri(SteamApiURL, $"/IAccountCartService/AddItemsToCart/v1/?access_token={token}"); var data = new Dictionary - { - { "input_json", json }, - }; + { + { "input_json", json }, + }; var response = await bot.ArchiWebHandler.UrlPostToJsonObject>(request, data, SteamStoreURL).ConfigureAwait(false); return response?.Content?.Response; } /// - /// 添加到购物车 + /// 添加购物车项目 /// /// - /// + /// /// - internal static async Task AddCart(this Bot bot, SteamGameId gameId) + /// + internal static async Task AddItemsToAccountCart(this Bot bot, List items) { - if (gameId.Type == ESteamGameIdType.Sub || gameId.Type == ESteamGameIdType.Bundle) - { - return await AddCart(bot, gameId.GameId, gameId.Type == ESteamGameIdType.Bundle).ConfigureAwait(false); - } - else + var payload = new AddItemsToCartRequest { - return null; - } - } - - - /// - /// 添加到购物车 - /// - /// - /// - /// - /// - internal static async Task AddCart(this Bot bot, uint subId, bool isBundle = false) - { - string type = isBundle ? "bundle" : "sub"; - - var request = new Uri(SteamStoreURL, "/cart/"); - var referer = new Uri(SteamStoreURL, $"/{type}/{subId}"); + Items = items, + Navdata = new AddItemsToCartRequest.NavdataData + { + Domain = "store.steampowered.com", + Controller = "default", + Method = "default", + SubMethod = "", + Feature = "spotlight", + Depth = 1, + CountryCode = Langs.CountryCode, + WebKey = 0, + IsClient = false, + CuratorData = new AddItemsToCartRequest.CuratorData + { + ClanId = null, + ListId = null, + }, + IsLikelyBot = false, + IsUtm = false, + }, + }; - var data = new Dictionary(5, StringComparer.Ordinal) + var json = JsonConvert.SerializeObject(payload, JsonOptions); + var token = bot.AccessToken ?? throw new AccessTokenNullException(); + var request = new Uri(SteamApiURL, $"/IAccountCartService/AddItemsToCart/v1/?access_token={token}"); + var data = new Dictionary { - { "action", "add_to_cart" }, - { type + "id", subId.ToString() }, - { "originating_snr", "1_direct-navigation__" }, + { "input_json", json }, }; - var response = await bot.ArchiWebHandler.UrlPostToHtmlDocumentWithSession(request, data: data, referer: referer).ConfigureAwait(false); - - return response != null; + var response = await bot.ArchiWebHandler.UrlPostToJsonObject>(request, data, SteamStoreURL).ConfigureAwait(false); + return response?.Content?.Response; } /// diff --git a/ASFEnhance/Data/IAccountCartService/AddItemsToCartRequest.cs b/ASFEnhance/Data/IAccountCartService/AddItemsToCartRequest.cs index 3045994..6d20ba0 100644 --- a/ASFEnhance/Data/IAccountCartService/AddItemsToCartRequest.cs +++ b/ASFEnhance/Data/IAccountCartService/AddItemsToCartRequest.cs @@ -126,7 +126,7 @@ public class NavdataData /// /// [JsonProperty("controller")] - public string ?Controller { get; set; } + public string? Controller { get; set; } /// /// /// diff --git a/ASFEnhance/Data/IAccountCartService/GetCartResponse.cs b/ASFEnhance/Data/IAccountCartService/GetCartResponse.cs index 892a4eb..518264d 100644 --- a/ASFEnhance/Data/IAccountCartService/GetCartResponse.cs +++ b/ASFEnhance/Data/IAccountCartService/GetCartResponse.cs @@ -48,12 +48,12 @@ public sealed record CartLineItemData /// /// [JsonProperty("packageid")] - public uint PackageId { get; set; } + public uint ?PackageId { get; set; } /// /// /// [JsonProperty("bundleid")] - public uint BundleId { get; set; } + public uint ?BundleId { get; set; } /// /// /// diff --git a/ASFEnhance/Event/Command.cs b/ASFEnhance/Event/Command.cs index 01765f2..555ca5e 100644 --- a/ASFEnhance/Event/Command.cs +++ b/ASFEnhance/Event/Command.cs @@ -400,7 +400,7 @@ internal static class Command if (intGamsIDs.Count < categories) //不足11个游戏自动补齐 { - var defaultGames = new int[] { 1086940, 1922010, 1374480, 990080, 2344520, 2254740, 2411910, 1817230, 2242710, 1868140, 2194530 }; + List defaultGames = [1086940, 1922010, 1374480, 990080, 2344520, 2254740, 2411910, 1817230, 2242710, 1868140, 2194530]; while (intGamsIDs.Count < categories) { intGamsIDs.Add(defaultGames[intGamsIDs.Count]); @@ -533,7 +533,7 @@ internal static class Command if (intGamsIDs.Count < categories) //不足11个游戏自动补齐 { - var defaultGames = new int[] { 1086940, 1957780, 548430, 990080, 1260320, 668580, 1716740, 2138710, 1817230, 2322560, 1868140 }; + List defaultGames = [1086940, 1957780, 548430, 990080, 1260320, 668580, 1716740, 2138710, 1817230, 2322560, 1868140]; while (intGamsIDs.Count < categories) { intGamsIDs.Add(defaultGames[intGamsIDs.Count]); diff --git a/ASFEnhance/IPC/Controllers/PurchaseController.cs b/ASFEnhance/IPC/Controllers/PurchaseController.cs index 304c470..3bea095 100644 --- a/ASFEnhance/IPC/Controllers/PurchaseController.cs +++ b/ASFEnhance/IPC/Controllers/PurchaseController.cs @@ -2,10 +2,13 @@ using ArchiSteamFarm.IPC.Responses; using ArchiSteamFarm.Localization; using ArchiSteamFarm.Steam; +using ASFEnhance.Cart; +using ASFEnhance.Data.IAccountCartService; using ASFEnhance.IPC.Requests; using ASFEnhance.IPC.Responses; using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; +using System.Data; using System.Globalization; using System.Net; @@ -41,7 +44,7 @@ public async Task> GetAppDetail(string botNames, [ return BadRequest(new GenericResponse(false, Langs.EulaFeatureUnavilable)); } - HashSet? bots = Bot.GetBots(botNames); + var bots = Bot.GetBots(botNames); if (bots == null || bots.Count == 0) { @@ -110,17 +113,143 @@ public async Task> GetAppDetail(string botNames, [ } /// - /// 购买指定游戏 + /// 清空购物车 + /// + /// + /// + /// + [HttpPost("{botNames:required}")] + [SwaggerOperation(Summary = "清空购物车", Description = "清除购物车所有内容")] + [ProducesResponseType(typeof(GenericResponse), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(GenericResponse), (int)HttpStatusCode.BadRequest)] + public async Task> ClearCart(string botNames) + { + if (string.IsNullOrEmpty(botNames)) + { + throw new ArgumentNullException(nameof(botNames)); + } + + if (!Config.EULA) + { + return BadRequest(new GenericResponse(false, Langs.EulaFeatureUnavilable)); + } + + var bots = Bot.GetBots(botNames); + + if (bots == null || bots.Count == 0) + { + return BadRequest(new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames))); + } + + var results = await Utilities.InParallel(bots.Select(x => x.ClearAccountCart())).ConfigureAwait(false); + + var response = new BoolDictResponse(); + int i = 0; + foreach (var result in results) + { + if (i >= bots.Count) + { + break; + } + + response.Add(bots.ElementAt(i++).BotName, result ?? false); + } + + return Ok(new GenericResponse(true, "Ok", response)); + } + + /// + /// 清空购物车 + /// + /// + /// + /// + [HttpPost("{botNames:required}")] + [SwaggerOperation(Summary = "读取机器人购物车", Description = "读取机器人购物车内容")] + [ProducesResponseType(typeof(GenericResponse>), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(GenericResponse), (int)HttpStatusCode.BadRequest)] + public async Task> GetCart(string botNames) + { + if (string.IsNullOrEmpty(botNames)) + { + throw new ArgumentNullException(nameof(botNames)); + } + + if (!Config.EULA) + { + return BadRequest(new GenericResponse(false, Langs.EulaFeatureUnavilable)); + } + + var bots = Bot.GetBots(botNames); + + if (bots == null || bots.Count == 0) + { + return BadRequest(new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames))); + } + + var results = await Utilities.InParallel(bots.Select(x => x.GetAccountCart())).ConfigureAwait(false); + + var response = new Dictionary(); + int i = 0; + foreach (var result in results) + { + if (i >= bots.Count) + { + break; + } + + var cart = result?.Cart; + if (cart == null) + { + response.Add(bots.ElementAt(i++).BotName, null); + } + else + { + var data = new List(); + if (cart.LineItems?.Count > 0) + { + foreach (var item in cart.LineItems) + { + data.Add(new BotCartResponse.CartItemData + { + PackageId = item.PackageId, + BundleId = item.BundleId, + LineItemId = item.LineItemId, + IsValid = item.IsValid, + TimeAdded = item.TimeAdded, + PriceCents = item.PriceWhenAdded?.AmountInCents, + CurrencyCode = item.PriceWhenAdded?.CurrencytCode ?? SteamKit2.ECurrencyCode.Invalid, + PriceFormatted = item.PriceWhenAdded?.FormattedAmount, + IsGift = item.Flags?.IsGift ?? false, + IsPrivate = item.Flags?.IsPrivate ?? false, + GiftInfo = item.GiftInfo, + }); + } + } + response.Add(bots.ElementAt(i++).BotName, new BotCartResponse + { + Items = data, + IsValid = cart.IsValid, + }); + } + } + + return Ok(new GenericResponse>(true, "Ok", response)); + } + + + /// + /// 向购物车添加内容 /// /// /// /// /// [HttpPost("{botNames:required}")] - [SwaggerOperation(Summary = "购买指定游戏", Description = "SubIds和BundleIds可省略,但是必须指定一种,也可以都指定")] - [ProducesResponseType(typeof(GenericResponse>), (int)HttpStatusCode.OK)] + [SwaggerOperation(Summary = "购物车添加项目", Description = "IsGift为True时需要定义GiftInfo")] + [ProducesResponseType(typeof(GenericResponse>), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(GenericResponse), (int)HttpStatusCode.BadRequest)] - public async Task> PurchaseGame(string botNames, [FromBody] PurchaseRequest request) + public async Task> AddCart(string botNames, [FromBody] AddCartRequest request) { if (string.IsNullOrEmpty(botNames)) { @@ -128,223 +257,117 @@ public async Task> PurchaseGame(string botNames, [ } ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(request.Items); if (!Config.EULA) { return BadRequest(new GenericResponse(false, Langs.EulaFeatureUnavilable)); } - //HashSet? bots = Bot.GetBots(botNames); - - //if (bots == null || bots.Count == 0) - //{ - // return BadRequest(new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames))); - //} - - //if ((request.SubIds == null && request.BundleIds == null) || request.SubIds?.Count + request.BundleIds?.Count <= 0) - //{ - // return BadRequest(new GenericResponse(false, "SubIds 和 BundleIds 不能同时为 null")); - //} - - //var response = bots.ToDictionary(x => x.BotName, x => new PurchaseResultResponse()); - - ////清空购物车 - //await Utilities.InParallel(bots.Select(bot => Cart.WebRequest.ClearCart(bot))).ConfigureAwait(false); - - //if (request.BundleIds?.Count > 0) - //{ - // foreach (uint bundleid in request.BundleIds) - // { - // var results = await Utilities.InParallel(bots.Select( - // async bot => - // { - // if (!bot.IsConnectedAndLoggedOn) { return (bot.BotName, false); } - - // bool? result = await Cart.WebRequest.AddCart(bot, bundleid, true).ConfigureAwait(false); - // return (bot.BotName, result ?? false); - // } - // )).ConfigureAwait(false); - - // foreach (var result in results) - // { - // response[result.BotName].AddCartResult.BundleIds.Add(bundleid.ToString(), result.Item2); - // } - // } - //} - - //if (request.SubIds?.Count > 0) - //{ - // foreach (uint subid in request.SubIds) - // { - // var results = await Utilities.InParallel(bots.Select( - // async bot => - // { - // if (!bot.IsConnectedAndLoggedOn) { return (bot.BotName, false); } - - // if (request.SkipOwned) - // { - // if (bot.OwnedPackageIDs.ContainsKey(subid)) - // { - // return (bot.BotName, true); - // } - // } - - // bool? result = await Cart.WebRequest.AddCart(bot, subid, false).ConfigureAwait(false); - // return (bot.BotName, result ?? false); - // } - // )).ConfigureAwait(false); - - // foreach (var result in results) - // { - // response[result.BotName].AddCartResult.SubIds.Add(subid.ToString(), result.Item2); - // } - // } - //} - - ////记录购物车 - //{ - // var results = await Utilities.InParallel(bots.Select( - // async bot => - // { - // if (!bot.IsConnectedAndLoggedOn) { return (bot.BotName, new()); } - - // var cartData = await Cart.WebRequest.GetCartGames(bot).ConfigureAwait(false); - - // PurchaseResult result = new() - // { - // Currency = bot.WalletCurrency.ToString(), - // }; - - // if (cartData == null) - // { - // result.CartItems.Add(new CartItem - // { - // Type = "Error", - // Id = 0, - // Name = Langs.NetworkError, - // }); - // } - // else - // { - // result.PurchaseAsGift = cartData.PurchaseAsGift; - // result.PurchaseForSelf = cartData.PurchaseAsGift; - - // foreach (var c in cartData.CartItems) - // { - // result.CartItems.Add(new() - // { - // Type = c.GameId.Type.ToString(), - // Id = c.GameId.GameId, - // Name = c.Name, - // }); - // } - // } - - // return (bot.BotName, result); - // } - // )).ConfigureAwait(false); - - // foreach (var result in results) - // { - // response[result.BotName].PurchaseResult = result.result; - // } - //} - - ////下单 - //{ - // var results = await Utilities.InParallel(bots.Select( - // async bot => - // { - // if (!bot.IsConnectedAndLoggedOn) { return (bot.BotName, new()); } - - // long balancePrev = bot.WalletBalance; - - // PurchaseResult result = new() - // { - // Success = false, - // BalanceNow = balancePrev, - // BalancePrev = balancePrev, - // }; - - // if (balancePrev < 1) - // { - // return (bot.BotName, result); - // } - - // var response1 = await Cart.WebRequest.CheckOut(bot, false).ConfigureAwait(false); - - // if (response1 == null) - // { - // return (bot.BotName, result); - // } - - // var response2 = await Cart.WebRequest.InitTransaction(bot).ConfigureAwait(false); - - // if (response2 == null) - // { - // return (bot.BotName, result); - // } - - // string? transId = response2.TransId ?? response2.TransActionId; - - // if (string.IsNullOrEmpty(transId)) - // { - // return (bot.BotName, result); - // } - - // var response3 = await Cart.WebRequest.GetFinalPrice(bot, transId, false).ConfigureAwait(false); - - // if (response3 == null || response2.TransId == null) - // { - // return (bot.BotName, result); - // } - - // if (!request.FakePurchase) - // { - // var response4 = await Cart.WebRequest.FinalizeTransaction(bot, transId).ConfigureAwait(false); - - // if (response4 == null) - // { - // return (bot.BotName, result); - // } - - // await Task.Delay(2000).ConfigureAwait(false); - // } - // else - // { - // var response4 = await Cart.WebRequest.CancelTransaction(bot, transId).ConfigureAwait(false); - - // if (response4 == null) - // { - // return (bot.BotName, result); - // } - // result.Success = true; - // } - - // long balanceNow = bot.WalletBalance; - // result.BalanceNow = balanceNow; - // result.Cost = balancePrev - balanceNow; - - // //自动清空购物车 - // await Cart.WebRequest.ClearCart(bot).ConfigureAwait(false); - - // return (bot.BotName, result); - // } - // )).ConfigureAwait(false); - - // foreach (var (botName, result) in results) - // { - // var purchaseResponse = response[botName].PurchaseResult; - // purchaseResponse.Success = result.Success; - // purchaseResponse.BalancePrev = result.BalancePrev; - // purchaseResponse.BalanceNow = result.BalanceNow; - // purchaseResponse.Cost = result.Cost; - // } - //} - - return Ok(new GenericResponse(false, "未完成")); - - //return Ok(new GenericResponse>(response)); + var bots = Bot.GetBots(botNames); + if (bots == null || bots.Count == 0) + { + return BadRequest(new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames))); + } + + var payloads = new List(); + foreach (var item in request.Items) + { + if (item.BundleId == 0 && item.PackageId == 0) + { + continue; + } + + var payload = new AddItemsToCartRequest.ItemData + { + PackageId = item.PackageId == 0 ? null : item.PackageId, + BundleId = item.BundleId == 0 ? null : item.BundleId, + Flags = new AddItemsToCartRequest.FlagsData + { + IsPrivate = item.IsPrivate, + IsGift = item.IsGift, + } + }; + + var giftInfo = item.GiftInfo; + if (item.IsGift) + { + if (giftInfo != null && giftInfo.AccountIdGiftee == 0) + { + payload.GIftInfo = new AddItemsToCartRequest.GiftInfoData + { + AccountIdGiftee = giftInfo.AccountIdGiftee, + GiftMessage = new AddItemsToCartRequest.GiftMessageData + { + GifteeName = giftInfo.GifteeName ?? "", + Message = giftInfo.Message ?? "Send via ASFEnhance", + Sentiment = giftInfo.Sentiment ?? "", + Signature = giftInfo.Signature ?? "ASFEnhance", + }, + TimeScheduledSend = giftInfo.TimeScheduledSend, + }; + } + else + { + return BadRequest(new GenericResponse(false, "IsGift为true时, GiftInfo不能为空")); + } + } + payloads.Add(payload); + } + + if (payloads.Count <= 0) + { + return BadRequest(new GenericResponse(false, "SubIds 和 BundleIds 不能同时为 null")); + } + + var results = await Utilities.InParallel(bots.Select(bot => Cart.WebRequest.AddItemsToAccountCart(bot, payloads))).ConfigureAwait(false); + + var response = new Dictionary(); + int i = 0; + foreach (var result in results) + { + if (i >= bots.Count) + { + break; + } + + var cart = result?.Cart; + if (cart == null) + { + response.Add(bots.ElementAt(i++).BotName, null); + } + else + { + var data = new List(); + if (cart.LineItems?.Count > 0) + { + foreach (var item in cart.LineItems) + { + data.Add(new BotCartResponse.CartItemData + { + PackageId = item.PackageId, + BundleId = item.BundleId, + LineItemId = item.LineItemId, + IsValid = item.IsValid, + TimeAdded = item.TimeAdded, + PriceCents = item.PriceWhenAdded?.AmountInCents, + CurrencyCode = item.PriceWhenAdded?.CurrencytCode ?? SteamKit2.ECurrencyCode.Invalid, + PriceFormatted = item.PriceWhenAdded?.FormattedAmount, + IsGift = item.Flags?.IsGift ?? false, + IsPrivate = item.Flags?.IsPrivate ?? false, + GiftInfo = item.GiftInfo, + }); + } + } + response.Add(bots.ElementAt(i++).BotName, new BotCartResponse + { + Items = data, + IsValid = cart.IsValid, + }); + } + } + + return Ok(new GenericResponse>(true, "Ok", response)); } /// @@ -355,10 +378,10 @@ public async Task> PurchaseGame(string botNames, [ /// /// [HttpPost("{botNames:required}")] - [SwaggerOperation(Summary = "购买指定游戏", Description = "SubIds和BundleIds可省略,但是必须指定一种,也可以都指定")] + [SwaggerOperation(Summary = "购物车下单", Description = "结算当前购物车")] [ProducesResponseType(typeof(GenericResponse>), (int)HttpStatusCode.OK)] [ProducesResponseType(typeof(GenericResponse), (int)HttpStatusCode.BadRequest)] - public async Task> OnlyPurchase(string botNames, [FromBody] OnlyPurchaseRequest request) + public async Task> Purchase(string botNames, [FromBody] OnlyPurchaseRequest request) { if (string.IsNullOrEmpty(botNames)) { @@ -460,7 +483,7 @@ public async Task> OnlyPurchase(string botNames, [ result.Cost = balancePrev - balanceNow; //自动清空购物车 - await Cart.WebRequest.ClearCart(bot).ConfigureAwait(false); + await Cart.WebRequest.ClearAccountCart(bot).ConfigureAwait(false); return result; } diff --git a/ASFEnhance/IPC/Requests/AddCartRequest.cs b/ASFEnhance/IPC/Requests/AddCartRequest.cs new file mode 100644 index 0000000..93bce02 --- /dev/null +++ b/ASFEnhance/IPC/Requests/AddCartRequest.cs @@ -0,0 +1,72 @@ +using Newtonsoft.Json; + +namespace ASFEnhance.IPC.Requests; + +/// +/// 下单请求 +/// +public sealed record AddCartRequest +{ + /// + /// SubID列表 + /// + public List? Items { get; set; } + + /// + /// + /// + public sealed record ItemData + { + /// + /// + /// + public uint PackageId { get; set; } + /// + /// + /// + public uint BundleId { get; set; } + /// + /// + /// + public GiftInfoData? GiftInfo { get; set; } + /// + /// + /// + public bool IsPrivate { get; set; } + /// + /// + /// + public bool IsGift { get; set; } + + /// + /// + /// + public sealed record GiftInfoData + { + /// + /// + /// + public ulong AccountIdGiftee { get; set; } + /// + /// + /// + public string? GifteeName { get; set; } + /// + /// + /// + public string? Message { get; set; } + /// + /// + /// + public string? Sentiment { get; set; } + /// + /// + /// + public string? Signature { get; set; } + /// + /// + /// + public ulong TimeScheduledSend { get; set; } + } + } +} diff --git a/ASFEnhance/IPC/Responses/BotCartResponse.cs b/ASFEnhance/IPC/Responses/BotCartResponse.cs new file mode 100644 index 0000000..5b84525 --- /dev/null +++ b/ASFEnhance/IPC/Responses/BotCartResponse.cs @@ -0,0 +1,24 @@ +using ASFEnhance.Data.IAccountCartService; +using SteamKit2; + +namespace ASFEnhance.IPC.Responses; +public sealed record BotCartResponse +{ + public List? Items { get; set; } + public bool IsValid { get; set; } + + public sealed record CartItemData + { + public uint? PackageId { get; set; } + public uint? BundleId { get; set; } + public string? LineItemId { get; set; } + public bool IsValid { get; set; } + public ulong TimeAdded { get; set; } + public string? PriceCents { get; set; } + public ECurrencyCode CurrencyCode { get; set; } + public string? PriceFormatted { get; set; } + public bool IsGift { get; set; } + public bool IsPrivate { get; set; } + public AddItemsToCartRequest.GiftInfoData? GiftInfo { get; set; } + } +} diff --git a/ASFEnhance/Store/WebRequest.cs b/ASFEnhance/Store/WebRequest.cs index b897c42..b7ed59a 100644 --- a/ASFEnhance/Store/WebRequest.cs +++ b/ASFEnhance/Store/WebRequest.cs @@ -241,7 +241,7 @@ internal static async Task RedeemPoints(this Bot bot, uint defId) } }; - var json = JsonConvert.SerializeObject(payload, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + var json = JsonConvert.SerializeObject(payload, JsonOptions); var encJson = UrlEncode(json); var token = bot.AccessToken ?? throw new AccessTokenNullException(); var request = new Uri(SteamApiURL, $"/IStoreBrowseService/GetItems/v1/?access_token={token}&input_json={encJson}"); diff --git a/ASFEnhance/Utils.cs b/ASFEnhance/Utils.cs index c721bbd..aaf538c 100644 --- a/ASFEnhance/Utils.cs +++ b/ASFEnhance/Utils.cs @@ -5,6 +5,7 @@ using ArchiSteamFarm.Web; using ArchiSteamFarm.Web.Responses; using ASFEnhance.Data; +using Newtonsoft.Json; using ProtoBuf; using System.Collections.ObjectModel; using System.Reflection; @@ -345,4 +346,9 @@ internal static string UrlEncode(string url) int rateLimitingDelay = 0, bool allowSessionRefresh = true, CancellationToken cancellationToken = default) => handler.UrlPostWithSession(request, null, data, referer, requestOptions, ESession.None, checkSessionPreemptively, maxTries, rateLimitingDelay, allowSessionRefresh, cancellationToken); + + /// + /// Json序列化设置 + /// + public static JsonSerializerSettings JsonOptions = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; } diff --git a/Directory.Build.props b/Directory.Build.props index f4fa163..a75d86e 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,6 @@ - 2.0.14.2 + 2.0.15.0