Skip to content

Commit

Permalink
Closes #3304
Browse files Browse the repository at this point in the history
  • Loading branch information
JustArchi committed Oct 6, 2024
1 parent 9b40ab3 commit 33c9aed
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 23 deletions.
46 changes: 45 additions & 1 deletion ArchiSteamFarm/Steam/Bot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
private readonly ConcurrentHashSet<ulong> SteamFamilySharingIDs = [];
private readonly SteamUser SteamUser;
private readonly Trading Trading;
private readonly SemaphoreSlim UnpackBoosterPacksSemaphore = new(1, 1);

private IEnumerable<(string FilePath, EFileType FileType)> RelatedFiles {
get {
Expand Down Expand Up @@ -315,6 +316,7 @@ private set {
private SteamSaleEvent? SteamSaleEvent;
private Timer? TradeCheckTimer;
private string? TwoFactorCode;
private bool UnpackBoosterPacksScheduled;

private Bot(string botName, BotConfig botConfig, BotDatabase botDatabase) {
ArgumentException.ThrowIfNullOrEmpty(botName);
Expand Down Expand Up @@ -415,6 +417,7 @@ public void Dispose() {
RefreshWebSessionSemaphore.Dispose();
SendCompleteTypesSemaphore.Dispose();
Trading.Dispose();
UnpackBoosterPacksSemaphore.Dispose();

Actions.Dispose();
CardsFarmer.Dispose();
Expand Down Expand Up @@ -442,6 +445,7 @@ public async ValueTask DisposeAsync() {
RefreshWebSessionSemaphore.Dispose();
SendCompleteTypesSemaphore.Dispose();
Trading.Dispose();
UnpackBoosterPacksSemaphore.Dispose();

await Actions.DisposeAsync().ConfigureAwait(false);
await CardsFarmer.DisposeAsync().ConfigureAwait(false);
Expand Down Expand Up @@ -3121,7 +3125,21 @@ private void OnInventoryChanged() {
Utilities.InBackground(ArchiWebHandler.MarkInventory);
}

if (BotConfig.CompleteTypesToSend.Count > 0) {
// The following actions should be synchronized, as they modify the state of the inventory
if (BotConfig.FarmingPreferences.HasFlag(BotConfig.EFarmingPreferences.AutoUnpackBoosterPacks)) {
Utilities.InBackground(
async () => {
if (!await UnpackBoosterPacks().ConfigureAwait(false)) {
// Another task is already in progress, so it'll handle the actions below as well
return;
}
if (BotConfig.CompleteTypesToSend.Count > 0) {
await SendCompletedSets().ConfigureAwait(false);
}
}
);
} else if (BotConfig.CompleteTypesToSend.Count > 0) {
Utilities.InBackground(SendCompletedSets);
}
}
Expand Down Expand Up @@ -3923,6 +3941,32 @@ private void StopRefreshTokensTimer() {
RefreshTokensTimer = null;
}

private async Task<bool> UnpackBoosterPacks() {
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
lock (UnpackBoosterPacksSemaphore) {
if (UnpackBoosterPacksScheduled) {
return false;
}

UnpackBoosterPacksScheduled = true;
}

await UnpackBoosterPacksSemaphore.WaitAsync().ConfigureAwait(false);

try {
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
lock (UnpackBoosterPacksSemaphore) {
UnpackBoosterPacksScheduled = false;
}

await Actions.UnpackBoosterPacks().ConfigureAwait(false);
} finally {
UnpackBoosterPacksSemaphore.Release();
}

return true;
}

private void UpdateTokens(string accessToken, string? refreshToken = null) {
ArgumentException.ThrowIfNullOrEmpty(accessToken);

Expand Down
29 changes: 29 additions & 0 deletions ArchiSteamFarm/Steam/Interaction/Actions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,35 @@ static async () => {
return (true, Strings.Done);
}

[PublicAPI]
public async Task<bool> UnpackBoosterPacks() {
if (!Bot.IsConnectedAndLoggedOn) {
return false;
}

// It'd make sense here to actually check return code of ArchiWebHandler.UnpackBooster(), but it lies most of the time | https://github.com/JustArchi/ArchiSteamFarm/issues/704
bool result = true;

// It'd also make sense to run all of this in parallel, but it seems that Steam has a lot of problems with inventory-related parallel requests | https://steamcommunity.com/groups/archiasf/discussions/1/3559414588264550284/
try {
await foreach (Asset item in Bot.ArchiHandler.GetMyInventoryAsync().Where(static item => item.Type == EAssetType.BoosterPack).ConfigureAwait(false)) {
if (!await Bot.ArchiWebHandler.UnpackBooster(item.RealAppID, item.AssetID).ConfigureAwait(false)) {
result = false;
}
}
} catch (TimeoutException e) {
Bot.ArchiLogger.LogGenericWarningException(e);

return false;
} catch (Exception e) {
Bot.ArchiLogger.LogGenericException(e);

return false;
}

return result;
}

[PublicAPI]
public static async Task<(bool Success, string? Message, Version? Version)> Update(GlobalConfig.EUpdateChannel? channel = null, bool forced = false) {
if (channel.HasValue && !Enum.IsDefined(channel.Value)) {
Expand Down
22 changes: 2 additions & 20 deletions ArchiSteamFarm/Steam/Interaction/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3418,27 +3418,9 @@ internal void OnNewLicenseList() {
return FormatBotResponse(Strings.BotNotConnected);
}

// It'd make sense here to actually check return code of ArchiWebHandler.UnpackBooster(), but it lies most of the time | https://github.com/JustArchi/ArchiSteamFarm/issues/704
bool completeSuccess = true;

// It'd also make sense to run all of this in parallel, but it seems that Steam has a lot of problems with inventory-related parallel requests | https://steamcommunity.com/groups/archiasf/discussions/1/3559414588264550284/
try {
await foreach (Asset item in Bot.ArchiHandler.GetMyInventoryAsync().Where(static item => item.Type == EAssetType.BoosterPack).ConfigureAwait(false)) {
if (!await Bot.ArchiWebHandler.UnpackBooster(item.RealAppID, item.AssetID).ConfigureAwait(false)) {
completeSuccess = false;
}
}
} catch (TimeoutException e) {
Bot.ArchiLogger.LogGenericWarningException(e);

completeSuccess = false;
} catch (Exception e) {
Bot.ArchiLogger.LogGenericException(e);

completeSuccess = false;
}
bool result = await Bot.Actions.UnpackBoosterPacks().ConfigureAwait(false);

return FormatBotResponse(completeSuccess ? Strings.Success : Strings.Done);
return FormatBotResponse(result ? Strings.Success : Strings.Done);
}

private static async Task<string?> ResponseUnpackBoosters(EAccess access, string botNames, ulong steamID = 0) {
Expand Down
5 changes: 3 additions & 2 deletions ArchiSteamFarm/Steam/Storage/BotConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ public enum EFarmingOrder : byte {

[Flags]
[PublicAPI]
public enum EFarmingPreferences : byte {
public enum EFarmingPreferences : ushort {
None = 0,
FarmingPausedByDefault = 1,
ShutdownOnFarmingFinished = 2,
Expand All @@ -651,7 +651,8 @@ public enum EFarmingPreferences : byte {
SkipUnplayedGames = 32,
EnableRiskyCardsDiscovery = 64,
AutoSteamSaleEvent = 128,
All = FarmingPausedByDefault | ShutdownOnFarmingFinished | SendOnFarmingFinished | FarmPriorityQueueOnly | SkipRefundableGames | SkipUnplayedGames | EnableRiskyCardsDiscovery | AutoSteamSaleEvent
AutoUnpackBoosterPacks = 256,
All = FarmingPausedByDefault | ShutdownOnFarmingFinished | SendOnFarmingFinished | FarmPriorityQueueOnly | SkipRefundableGames | SkipUnplayedGames | EnableRiskyCardsDiscovery | AutoSteamSaleEvent | AutoUnpackBoosterPacks
}

[Flags]
Expand Down

0 comments on commit 33c9aed

Please sign in to comment.