Skip to content

Commit

Permalink
Merge branch 'main' into net9
Browse files Browse the repository at this point in the history
  • Loading branch information
JustArchi authored Sep 30, 2024
2 parents 0935218 + 1e34619 commit a9c0bfa
Show file tree
Hide file tree
Showing 23 changed files with 279 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/RELEASE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### Notice

**Pre-releases are experimental versions that often contain unpatched bugs, work-in-progress features and rewritten implementations. If you don't consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchiNET/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Release-cycle)** if you'd like to learn more.**
**Pre-releases are test versions that often contain unpatched bugs, work-in-progress features and rewritten implementations. If you don't consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchiNET/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Release-cycle)** if you'd like to learn more.**

---

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ jobs:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}

- name: Report Qodana results to GitHub
uses: github/codeql-action/[email protected].9
uses: github/codeql-action/[email protected].10
with:
sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json
2 changes: 1 addition & 1 deletion .github/workflows/docker-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
uses: docker/[email protected]

- name: Build ${{ matrix.configuration }} Docker image from ${{ matrix.file }}
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.8.0
with:
build-args: CONFIGURATION=${{ matrix.configuration }}
context: .
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish-latest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
echo "DH_REPOSITORY=$(echo ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
- name: Build and publish Docker image from Dockerfile.Service
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.8.0
with:
context: .
file: Dockerfile.Service
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
echo "DH_REPOSITORY=$(echo ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
- name: Build and publish Docker image from Dockerfile
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.8.0
with:
context: .
platforms: ${{ env.PLATFORMS }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish-released.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
echo "DH_REPOSITORY=$(echo ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }} | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV"
- name: Build and publish Docker image from Dockerfile
uses: docker/build-push-action@v6.7.0
uses: docker/build-push-action@v6.8.0
with:
context: .
platforms: ${{ env.PLATFORMS }}
Expand Down
2 changes: 1 addition & 1 deletion ASF-ui
Original file line number Diff line number Diff line change
Expand Up @@ -1255,7 +1255,7 @@ private async Task<bool> MatchActively(ImmutableHashSet<ListedUser> listedUsers,
Dictionary<ulong, uint> fairClassIDsToGive = new();
Dictionary<ulong, uint> fairClassIDsToReceive = new();

foreach (ListedUser listedUser in listedUsers.Where(listedUser => (listedUser.SteamID != Bot.SteamID) && acceptedMatchableTypes.Any(listedUser.MatchableTypes.Contains) && !Bot.IsBlacklistedFromTrades(listedUser.SteamID)).OrderByDescending(listedUser => !deprioritizedSteamIDs.Contains(listedUser.SteamID)).ThenByDescending(static listedUser => listedUser.TotalGamesCount > 1).ThenByDescending(static listedUser => listedUser.MatchEverything).ThenBy(static listedUser => listedUser.TotalInventoryCount)) {
foreach (ListedUser listedUser in listedUsers.Where(listedUser => (listedUser.SteamID != Bot.SteamID) && acceptedMatchableTypes.Overlaps(listedUser.MatchableTypes) && !Bot.IsBlacklistedFromTrades(listedUser.SteamID)).OrderByDescending(listedUser => !deprioritizedSteamIDs.Contains(listedUser.SteamID)).ThenByDescending(static listedUser => listedUser.TotalGamesCount > 1).ThenByDescending(static listedUser => listedUser.MatchEverything).ThenBy(static listedUser => listedUser.TotalInventoryCount)) {
if (failuresInRow >= WebBrowser.MaxTries) {
Bot.ArchiLogger.LogGenericWarning(Strings.FormatWarningFailedWithError($"{nameof(failuresInRow)} >= {WebBrowser.MaxTries}"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@
<value>{0} je v současné době v souladu s vaší konfigurací zakázán. Pokud byste chtěli pomoci SteamDB při odesílání dat, podívejte se na naši wiki.</value>
<comment>{0} will be replaced by the name of the plugin (e.g. "SteamTokenDumperPlugin")</comment>
</data>

<data name="PluginInitializedAndEnabled" xml:space="preserve">
<value>{0} byl úspěšně inicializován, předem děkujeme za vaši pomoc. První zpráva bude vygenerována zhruba za {1}.</value>
<comment>{0} will be replaced by the name of the plugin (e.g. "SteamTokenDumperPlugin"), {1} will be replaced by translated TimeSpan string (such as "53 minutes")</comment>
</data>
<data name="FileCouldNotBeLoadedFreshInit" xml:space="preserve">
<value>{0} nelze načíst, nová instance bude inicializována...</value>
<comment>{0} will be replaced by the name of the file (e.g. "GlobalCache")</comment>
Expand Down
4 changes: 4 additions & 0 deletions ArchiSteamFarm/Helpers/ArchiCacheable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ public ArchiCacheable(Func<CancellationToken, Task<(bool Success, T? Result)>> r
} catch (OperationCanceledException e) {
ASF.ArchiLogger.LogGenericDebuggingException(e);

return GetFailedValueFor(cacheFallback);
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);

return GetFailedValueFor(cacheFallback);
} finally {
InitSemaphore.Release();
Expand Down
28 changes: 28 additions & 0 deletions ArchiSteamFarm/IPC/Controllers/Api/BotController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,34 @@ public async Task<ActionResult<GenericResponse>> AddLicensePost(string botNames,
return Ok(new GenericResponse<IReadOnlyDictionary<string, BotAddLicenseResponse>>(result));
}

/// <summary>
/// Redeems points on given bots.
/// </summary>
[Consumes("application/json")]
[HttpPost("{botNames:required}/RedeemPoints/{definitionID:required}")]
[ProducesResponseType<GenericResponse<IReadOnlyDictionary<string, EResult>>>((int) HttpStatusCode.OK)]
[ProducesResponseType<GenericResponse>((int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> AddLicensePost(string botNames, uint definitionID) {
ArgumentException.ThrowIfNullOrEmpty(botNames);
ArgumentOutOfRangeException.ThrowIfZero(definitionID);

HashSet<Bot>? bots = Bot.GetBots(botNames);

if ((bots == null) || (bots.Count == 0)) {
return BadRequest(new GenericResponse(false, Strings.FormatBotNotFound(botNames)));
}

IList<EResult> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.RedeemPoints(definitionID))).ConfigureAwait(false);

Dictionary<string, EResult> result = new(bots.Count, Bot.BotsComparer);

foreach (Bot bot in bots) {
result[bot.BotName] = results[result.Count];
}

return Ok(new GenericResponse<IReadOnlyDictionary<string, EResult>>(result));
}

/// <summary>
/// Deletes all files related to given bots.
/// </summary>
Expand Down
11 changes: 3 additions & 8 deletions ArchiSteamFarm/Steam/Bot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3562,14 +3562,7 @@ private async void RedeemGamesInBackground(object? state = null) {
(string? key, string? name) = BotDatabase.GetGameToRedeemInBackground();

if (string.IsNullOrEmpty(key)) {
ArchiLogger.LogNullError(key);

break;
}

if (string.IsNullOrEmpty(name)) {
ArchiLogger.LogNullError(name);

// No more games to redeem left, possible due to e.g. queue purge
break;
}

Expand Down Expand Up @@ -3637,6 +3630,8 @@ private async void RedeemGamesInBackground(object? state = null) {
BotDatabase.RemoveGameToRedeemInBackground(key);

// If user omitted the name or intentionally provided the same name as key, replace it with the Steam result
name ??= key;

if (name.Equals(key, StringComparison.OrdinalIgnoreCase) && (items?.Count > 0)) {
name = string.Join(", ", items.Values);
}
Expand Down
2 changes: 1 addition & 1 deletion ArchiSteamFarm/Steam/Exchange/Trading.cs
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ private async Task<ParseTradeResult> ParseTrade(TradeOffer tradeOffer, ISet<(uin
if (accept) {
// Ensure that accepting this trade offer does not create conflicts with other
lock (handledSets) {
if (wantedSets.Any(handledSets.Contains)) {
if (handledSets.Overlaps(wantedSets)) {
Bot.ArchiLogger.LogGenericDebug(Strings.FormatBotTradeOfferResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RetryAfterOthers, nameof(handledSets)));

return ParseTradeResult.EResult.RetryAfterOthers;
Expand Down
72 changes: 72 additions & 0 deletions ArchiSteamFarm/Steam/Integration/ArchiHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public sealed class ArchiHandler : ClientMsgHandler {
private readonly SteamUnifiedMessages.UnifiedService<IEcon> UnifiedEconService;
private readonly SteamUnifiedMessages.UnifiedService<IFamilyGroups> UnifiedFamilyGroups;
private readonly SteamUnifiedMessages.UnifiedService<IFriendMessages> UnifiedFriendMessagesService;
private readonly SteamUnifiedMessages.UnifiedService<ILoyaltyRewards> UnifiedLoyaltyRewards;
private readonly SteamUnifiedMessages.UnifiedService<IPlayer> UnifiedPlayerService;
private readonly SteamUnifiedMessages.UnifiedService<IStore> UnifiedStoreService;
private readonly SteamUnifiedMessages.UnifiedService<ITwoFactor> UnifiedTwoFactorService;
Expand All @@ -80,6 +81,7 @@ internal ArchiHandler(ArchiLogger archiLogger, SteamUnifiedMessages steamUnified
UnifiedEconService = steamUnifiedMessages.CreateService<IEcon>();
UnifiedFamilyGroups = steamUnifiedMessages.CreateService<IFamilyGroups>();
UnifiedFriendMessagesService = steamUnifiedMessages.CreateService<IFriendMessages>();
UnifiedLoyaltyRewards = steamUnifiedMessages.CreateService<ILoyaltyRewards>();
UnifiedPlayerService = steamUnifiedMessages.CreateService<IPlayer>();
UnifiedStoreService = steamUnifiedMessages.CreateService<IStore>();
UnifiedTwoFactorService = steamUnifiedMessages.CreateService<ITwoFactor>();
Expand Down Expand Up @@ -357,6 +359,47 @@ public async IAsyncEnumerable<Asset> GetMyInventoryAsync(uint appID = Asset.Stea
return body.games.ToDictionary(static game => (uint) game.appid, static game => game.name);
}

[PublicAPI]
public async Task<long?> GetPointsBalance() {
if (Client == null) {
throw new InvalidOperationException(nameof(Client));
}

if (!Client.IsConnected) {
return null;
}

if (Client.SteamID == null) {
throw new InvalidOperationException(nameof(Client.SteamID));
}

ulong steamID = Client.SteamID;

if (steamID == 0) {
throw new InvalidOperationException(nameof(Client.SteamID));
}

CLoyaltyRewards_GetSummary_Request request = new() { steamid = steamID };

SteamUnifiedMessages.ServiceMethodResponse response;

try {
response = await UnifiedLoyaltyRewards.SendMessage(x => x.GetSummary(request)).ToLongRunningTask().ConfigureAwait(false);
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);

return null;
}

if (response.Result != EResult.OK) {
return null;
}

CLoyaltyRewards_GetSummary_Response body = response.GetDeserializedResponse<CLoyaltyRewards_GetSummary_Response>();

return body.summary?.points;
}

[PublicAPI]
public async Task<CCredentials_GetSteamGuardDetails_Response?> GetSteamGuardStatus() {
if (Client == null) {
Expand Down Expand Up @@ -500,6 +543,35 @@ public async Task<bool> LeaveChatRoomGroup(ulong chatGroupID) {
return response.Result == EResult.OK;
}

[PublicAPI]
public async Task<EResult> RedeemPoints(uint definitionID) {
ArgumentOutOfRangeException.ThrowIfZero(definitionID);

if (Client == null) {
throw new InvalidOperationException(nameof(Client));
}

if (!Client.IsConnected) {
return EResult.NoConnection;
}

CLoyaltyRewards_RedeemPoints_Request request = new() {
defid = definitionID
};

SteamUnifiedMessages.ServiceMethodResponse response;

try {
response = await UnifiedLoyaltyRewards.SendMessage(x => x.RedeemPoints(request)).ToLongRunningTask().ConfigureAwait(false);
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);

return EResult.Timeout;
}

return response.Result;
}

[PublicAPI]
public async Task<bool> RemoveFriend(ulong steamID) {
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
Expand Down
4 changes: 4 additions & 0 deletions ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ public sealed class ArchiWebHandler : IDisposable {
internal const ushort MaxItemsInSingleInventoryRequest = 5000;

private const string EconService = "IEconService";

[Obsolete]
private const string LoyaltyRewardsService = "ILoyaltyRewardsService";

private const byte MaxTradeOfferMessageLength = 128;
private const byte MinimumSessionValidityInSeconds = 10;
private const byte SessionIDLength = 24; // For maximum compatibility, should be divisible by 2 and match the length of "sessionid" property that Steam uses across their websites
Expand Down Expand Up @@ -394,6 +397,7 @@ public async IAsyncEnumerable<Asset> GetInventoryAsync(ulong steamID = 0, uint a
}
}

[Obsolete($"Use {nameof(ArchiHandler)}.{nameof(ArchiHandler.GetPointsBalance)} instead, this endpoint will be removed in the future version")]
[PublicAPI]
public async Task<uint?> GetPointsBalance() {
string? accessToken = Bot.AccessToken;
Expand Down
7 changes: 7 additions & 0 deletions ArchiSteamFarm/Steam/Interaction/Actions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,13 @@ public static string Hash(ArchiCryptoHelper.EHashingMethod hashingMethod, string
return await Bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
}

[PublicAPI]
public async Task<EResult> RedeemPoints(uint definitionID) {
ArgumentOutOfRangeException.ThrowIfZero(definitionID);

return await Bot.ArchiHandler.RedeemPoints(definitionID).ConfigureAwait(false);
}

[PublicAPI]
public static (bool Success, string Message) Restart() {
if (!Program.RestartAllowed) {
Expand Down
Loading

0 comments on commit a9c0bfa

Please sign in to comment.