Skip to content

Commit

Permalink
Closes #2434
Browse files Browse the repository at this point in the history
It'll be a miracle if I didn't make any mistake while refactoring this
  • Loading branch information
JustArchi committed Nov 18, 2021
1 parent efd5079 commit 666a04a
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 132 deletions.
12 changes: 6 additions & 6 deletions ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@
using ArchiSteamFarm.IPC.Responses;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Steam.Integration.Callbacks;
using ArchiSteamFarm.Steam.Storage;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using SteamKit2;

namespace ArchiSteamFarm.IPC.Controllers.Api;

Expand Down Expand Up @@ -318,7 +318,7 @@ public async Task<ActionResult<GenericResponse>> PausePost(string botNames, [Fro
/// </remarks>
[Consumes("application/json")]
[HttpPost("{botNames:required}/Redeem")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, PurchaseResponseCallback>>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, SteamApps.PurchaseResponseCallback>>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> RedeemPost(string botNames, [FromBody] BotRedeemRequest request) {
if (string.IsNullOrEmpty(botNames)) {
Expand All @@ -339,22 +339,22 @@ public async Task<ActionResult<GenericResponse>> RedeemPost(string botNames, [Fr
return BadRequest(new GenericResponse(false, string.Format(CultureInfo.CurrentCulture, Strings.BotNotFound, botNames)));
}

IList<PurchaseResponseCallback?> results = await Utilities.InParallel(bots.Select(bot => request.KeysToRedeem.Select(key => bot.Actions.RedeemKey(key))).SelectMany(static task => task)).ConfigureAwait(false);
IList<SteamApps.PurchaseResponseCallback?> results = await Utilities.InParallel(bots.Select(bot => request.KeysToRedeem.Select(key => bot.Actions.RedeemKey(key))).SelectMany(static task => task)).ConfigureAwait(false);

Dictionary<string, IReadOnlyDictionary<string, PurchaseResponseCallback?>> result = new(bots.Count, Bot.BotsComparer);
Dictionary<string, IReadOnlyDictionary<string, SteamApps.PurchaseResponseCallback?>> result = new(bots.Count, Bot.BotsComparer);

int count = 0;

foreach (Bot bot in bots) {
Dictionary<string, PurchaseResponseCallback?> responses = new(request.KeysToRedeem.Count, StringComparer.Ordinal);
Dictionary<string, SteamApps.PurchaseResponseCallback?> responses = new(request.KeysToRedeem.Count, StringComparer.Ordinal);
result[bot.BotName] = responses;

foreach (string key in request.KeysToRedeem) {
responses[key] = results[count++];
}
}

return Ok(new GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, PurchaseResponseCallback?>>>(result.Values.SelectMany(static responses => responses.Values).All(static value => value != null), result));
return Ok(new GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, SteamApps.PurchaseResponseCallback?>>>(result.Values.SelectMany(static responses => responses.Values).All(static value => value != null), result));
}

/// <summary>
Expand Down
12 changes: 7 additions & 5 deletions ArchiSteamFarm/Steam/Bot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3181,7 +3181,7 @@ private async void RedeemGamesInBackground(object? state = null) {
}

// ReSharper disable once RedundantSuppressNullableWarningExpression - required for .NET Framework
PurchaseResponseCallback? result = await Actions.RedeemKey(key!).ConfigureAwait(false);
SteamApps.PurchaseResponseCallback? result = await Actions.RedeemKey(key!).ConfigureAwait(false);

if (result == null) {
continue;
Expand All @@ -3201,7 +3201,9 @@ private async void RedeemGamesInBackground(object? state = null) {
}
}

ArchiLogger.LogGenericDebug(result.Items?.Count > 0 ? string.Format(CultureInfo.CurrentCulture, Strings.BotRedeemWithItems, key, $"{result.Result}/{result.PurchaseResultDetail}", string.Join(", ", result.Items)) : string.Format(CultureInfo.CurrentCulture, Strings.BotRedeem, key, $"{result.Result}/{result.PurchaseResultDetail}"));
Dictionary<uint, string>? items = result.ParseItems();

ArchiLogger.LogGenericDebug(items?.Count > 0 ? string.Format(CultureInfo.CurrentCulture, Strings.BotRedeemWithItems, key, $"{result.Result}/{result.PurchaseResultDetail}", string.Join(", ", items)) : string.Format(CultureInfo.CurrentCulture, Strings.BotRedeem, key, $"{result.Result}/{result.PurchaseResultDetail}"));

bool rateLimited = false;
bool redeemed = false;
Expand Down Expand Up @@ -3239,11 +3241,11 @@ private async void RedeemGamesInBackground(object? state = null) {

// If user omitted the name or intentionally provided the same name as key, replace it with the Steam result
// ReSharper disable once RedundantSuppressNullableWarningExpression - required for .NET Framework
if (name!.Equals(key, StringComparison.OrdinalIgnoreCase) && (result.Items?.Count > 0)) {
name = string.Join(", ", result.Items.Values);
if (name!.Equals(key, StringComparison.OrdinalIgnoreCase) && (items?.Count > 0)) {
name = string.Join(", ", items.Values);
}

string logEntry = $"{name}{DefaultBackgroundKeysRedeemerSeparator}[{result.PurchaseResultDetail}]{(result.Items?.Count > 0 ? $"{DefaultBackgroundKeysRedeemerSeparator}{string.Join(", ", result.Items)}" : "")}{DefaultBackgroundKeysRedeemerSeparator}{key}";
string logEntry = $"{name}{DefaultBackgroundKeysRedeemerSeparator}[{result.PurchaseResultDetail}]{(items?.Count > 0 ? $"{DefaultBackgroundKeysRedeemerSeparator}{string.Join(", ", items)}" : "")}{DefaultBackgroundKeysRedeemerSeparator}{key}";

string filePath = GetFilePath(redeemed ? EFileType.KeysToRedeemUsed : EFileType.KeysToRedeemUnused);

Expand Down
35 changes: 4 additions & 31 deletions ArchiSteamFarm/Steam/Integration/ArchiHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,6 @@ public override void HandleMsg(IPacketMsg packetMsg) {
ClientMsgProtobuf<CMsgClientPlayingSessionState> playingSessionState = new(packetMsg);
Client.PostCallback(new PlayingSessionStateCallback(packetMsg.TargetJobID, playingSessionState.Body));

break;
case EMsg.ClientPurchaseResponse:
ClientMsgProtobuf<CMsgClientPurchaseResponse> purchaseResponse = new(packetMsg);
Client.PostCallback(new PurchaseResponseCallback(packetMsg.TargetJobID, purchaseResponse.Body));

break;
case EMsg.ClientRedeemGuestPassResponse:
ClientMsgProtobuf<CMsgClientRedeemGuestPassResponse> redeemGuestPassResponse = new(packetMsg);
Client.PostCallback(new RedeemGuestPassResponseCallback(packetMsg.TargetJobID, redeemGuestPassResponse.Body));

break;
case EMsg.ClientSharedLibraryLockStatus:
ClientMsgProtobuf<CMsgClientSharedLibraryLockStatus> sharedLibraryLockStatus = new(packetMsg);
Expand Down Expand Up @@ -566,7 +556,7 @@ internal async Task PlayGames(IReadOnlyCollection<uint> gameIDs, string? gameNam
Client.Send(request);
}

internal async Task<RedeemGuestPassResponseCallback?> RedeemGuestPass(ulong guestPassID) {
internal async Task<SteamApps.RedeemGuestPassResponseCallback?> RedeemGuestPass(ulong guestPassID) {
if (guestPassID == 0) {
throw new ArgumentOutOfRangeException(nameof(guestPassID));
}
Expand All @@ -587,15 +577,15 @@ internal async Task PlayGames(IReadOnlyCollection<uint> gameIDs, string? gameNam
Client.Send(request);

try {
return await new AsyncJob<RedeemGuestPassResponseCallback>(Client, request.SourceJobID).ToLongRunningTask().ConfigureAwait(false);
return await new AsyncJob<SteamApps.RedeemGuestPassResponseCallback>(Client, request.SourceJobID).ToLongRunningTask().ConfigureAwait(false);
} catch (Exception e) {
ArchiLogger.LogGenericException(e);

return null;
}
}

internal async Task<PurchaseResponseCallback?> RedeemKey(string key) {
internal async Task<SteamApps.PurchaseResponseCallback?> RedeemKey(string key) {
if (string.IsNullOrEmpty(key)) {
throw new ArgumentNullException(nameof(key));
}
Expand All @@ -616,7 +606,7 @@ internal async Task PlayGames(IReadOnlyCollection<uint> gameIDs, string? gameNam
Client.Send(request);

try {
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID).ToLongRunningTask().ConfigureAwait(false);
return await new AsyncJob<SteamApps.PurchaseResponseCallback>(Client, request.SourceJobID).ToLongRunningTask().ConfigureAwait(false);
} catch (Exception e) {
ArchiLogger.LogGenericException(e);

Expand Down Expand Up @@ -796,23 +786,6 @@ internal PlayingSessionStateCallback(JobID jobID, CMsgClientPlayingSessionState
}
}

internal sealed class RedeemGuestPassResponseCallback : CallbackMsg {
internal readonly EResult Result;

internal RedeemGuestPassResponseCallback(JobID jobID, CMsgClientRedeemGuestPassResponse msg) {
if (jobID == null) {
throw new ArgumentNullException(nameof(jobID));
}

if (msg == null) {
throw new ArgumentNullException(nameof(msg));
}

JobID = jobID;
Result = (EResult) msg.eresult;
}
}

internal sealed class SharedLibraryLockStatusCallback : CallbackMsg {
internal readonly ulong LibraryLockedBySteamID;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,74 +21,25 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using ArchiSteamFarm.Core;
using JetBrains.Annotations;
using SteamKit2;
using SteamKit2.Internal;

namespace ArchiSteamFarm.Steam.Integration.Callbacks;
namespace ArchiSteamFarm.Steam.Integration;

public sealed class PurchaseResponseCallback : CallbackMsg {
[PublicAPI]
public Dictionary<uint, string>? Items { get; }

public EPurchaseResultDetail PurchaseResultDetail { get; internal set; }

[PublicAPI]
public EResult Result { get; internal set; }

internal PurchaseResponseCallback(EResult result, EPurchaseResultDetail purchaseResult) {
if (!Enum.IsDefined(typeof(EResult), result)) {
throw new InvalidEnumArgumentException(nameof(result), (int) result, typeof(EResult));
}

if (!Enum.IsDefined(typeof(EPurchaseResultDetail), purchaseResult)) {
throw new InvalidEnumArgumentException(nameof(purchaseResult), (int) purchaseResult, typeof(EPurchaseResultDetail));
}

Result = result;
PurchaseResultDetail = purchaseResult;
}

internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
if (jobID == null) {
throw new ArgumentNullException(nameof(jobID));
}

if (msg == null) {
throw new ArgumentNullException(nameof(msg));
internal static class SteamUtilities {
internal static Dictionary<uint, string>? ParseItems(this SteamApps.PurchaseResponseCallback callback) {
if (callback == null) {
throw new ArgumentNullException(nameof(callback));
}

JobID = jobID;
PurchaseResultDetail = (EPurchaseResultDetail) msg.purchase_result_details;
Result = (EResult) msg.eresult;

if (msg.purchase_receipt_info == null) {
ASF.ArchiLogger.LogNullError(nameof(msg.purchase_receipt_info));

return;
}

KeyValue receiptInfo = new();

using (MemoryStream ms = new(msg.purchase_receipt_info)) {
if (!receiptInfo.TryReadAsBinary(ms)) {
ASF.ArchiLogger.LogNullError(nameof(ms));

return;
}
}

List<KeyValue> lineItems = receiptInfo["lineitems"].Children;
List<KeyValue> lineItems = callback.PurchaseReceiptInfo["lineitems"].Children;

if (lineItems.Count == 0) {
return;
return null;
}

Items = new Dictionary<uint, string>(lineItems.Count);
Dictionary<uint, string> result = new(lineItems.Count);

foreach (KeyValue lineItem in lineItems) {
uint packageID = lineItem["PackageID"].AsUnsignedInteger();
Expand All @@ -101,7 +52,7 @@ internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
if (packageID == 0) {
ASF.ArchiLogger.LogNullError(nameof(packageID));

return;
return null;
}
}

Expand All @@ -110,12 +61,14 @@ internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
if (string.IsNullOrEmpty(gameName)) {
ASF.ArchiLogger.LogNullError(nameof(gameName));

return;
return null;
}

// Apparently steam expects client to decode sent HTML
gameName = WebUtility.HtmlDecode(gameName);
Items[packageID] = gameName;
result[packageID] = gameName;
}

return result;
}
}
6 changes: 2 additions & 4 deletions ArchiSteamFarm/Steam/Interaction/Actions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam.Data;
using ArchiSteamFarm.Steam.Exchange;
using ArchiSteamFarm.Steam.Integration;
using ArchiSteamFarm.Steam.Integration.Callbacks;
using ArchiSteamFarm.Steam.Security;
using ArchiSteamFarm.Steam.Storage;
using ArchiSteamFarm.Storage;
Expand Down Expand Up @@ -243,7 +241,7 @@ public static string Hash(ArchiCryptoHelper.EHashingMethod hashingMethod, string
}

[PublicAPI]
public async Task<PurchaseResponseCallback?> RedeemKey(string key) {
public async Task<SteamApps.PurchaseResponseCallback?> RedeemKey(string key) {
await LimitGiftsRequestsAsync().ConfigureAwait(false);

return await Bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
Expand Down Expand Up @@ -494,7 +492,7 @@ internal async Task AcceptGuestPasses(IReadOnlyCollection<ulong> guestPassIDs) {
Bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotAcceptingGift, guestPassID));
await LimitGiftsRequestsAsync().ConfigureAwait(false);

ArchiHandler.RedeemGuestPassResponseCallback? response = await Bot.ArchiHandler.RedeemGuestPass(guestPassID).ConfigureAwait(false);
SteamApps.RedeemGuestPassResponseCallback? response = await Bot.ArchiHandler.RedeemGuestPass(guestPassID).ConfigureAwait(false);

if (response != null) {
if (response.Result == EResult.OK) {
Expand Down
Loading

0 comments on commit 666a04a

Please sign in to comment.