mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-09 13:14:26 +00:00
Closes #3304
This commit is contained in:
@@ -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 {
|
||||
@@ -315,6 +316,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
||||
private SteamSaleEvent? SteamSaleEvent;
|
||||
private Timer? TradeCheckTimer;
|
||||
private string? TwoFactorCode;
|
||||
private bool UnpackBoosterPacksScheduled;
|
||||
|
||||
private Bot(string botName, BotConfig botConfig, BotDatabase botDatabase) {
|
||||
ArgumentException.ThrowIfNullOrEmpty(botName);
|
||||
@@ -415,6 +417,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
||||
RefreshWebSessionSemaphore.Dispose();
|
||||
SendCompleteTypesSemaphore.Dispose();
|
||||
Trading.Dispose();
|
||||
UnpackBoosterPacksSemaphore.Dispose();
|
||||
|
||||
Actions.Dispose();
|
||||
CardsFarmer.Dispose();
|
||||
@@ -442,6 +445,7 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
||||
RefreshWebSessionSemaphore.Dispose();
|
||||
SendCompleteTypesSemaphore.Dispose();
|
||||
Trading.Dispose();
|
||||
UnpackBoosterPacksSemaphore.Dispose();
|
||||
|
||||
await Actions.DisposeAsync().ConfigureAwait(false);
|
||||
await CardsFarmer.DisposeAsync().ConfigureAwait(false);
|
||||
@@ -3121,7 +3125,21 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -3923,6 +3941,32 @@ public sealed class Bot : IAsyncDisposable, IDisposable {
|
||||
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);
|
||||
|
||||
|
||||
@@ -476,6 +476,35 @@ public sealed class Actions : IAsyncDisposable, IDisposable {
|
||||
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)) {
|
||||
|
||||
@@ -3418,27 +3418,9 @@ public sealed class Commands {
|
||||
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;
|
||||
bool result = await Bot.Actions.UnpackBoosterPacks().ConfigureAwait(false);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -641,7 +641,7 @@ public sealed class BotConfig {
|
||||
|
||||
[Flags]
|
||||
[PublicAPI]
|
||||
public enum EFarmingPreferences : byte {
|
||||
public enum EFarmingPreferences : ushort {
|
||||
None = 0,
|
||||
FarmingPausedByDefault = 1,
|
||||
ShutdownOnFarmingFinished = 2,
|
||||
@@ -651,7 +651,8 @@ public sealed class BotConfig {
|
||||
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]
|
||||
|
||||
Reference in New Issue
Block a user