mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-04 16:10:08 +00:00
Closes #3327
This commit is contained in:
@@ -325,7 +325,7 @@ public sealed class BotController : ArchiController {
|
||||
[HttpPost("{botNames:required}/RedeemPoints/{definitionID:required}")]
|
||||
[ProducesResponseType<GenericResponse<IReadOnlyDictionary<string, EResult>>>((int) HttpStatusCode.OK)]
|
||||
[ProducesResponseType<GenericResponse>((int) HttpStatusCode.BadRequest)]
|
||||
public async Task<ActionResult<GenericResponse>> RedeemPointsPost(string botNames, uint definitionID) {
|
||||
public async Task<ActionResult<GenericResponse>> RedeemPointsPost(string botNames, uint definitionID, [FromQuery] bool forced = false) {
|
||||
ArgumentException.ThrowIfNullOrEmpty(botNames);
|
||||
ArgumentOutOfRangeException.ThrowIfZero(definitionID);
|
||||
|
||||
@@ -335,7 +335,7 @@ public sealed class BotController : ArchiController {
|
||||
return BadRequest(new GenericResponse(false, Strings.FormatBotNotFound(botNames)));
|
||||
}
|
||||
|
||||
IList<EResult> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.RedeemPoints(definitionID))).ConfigureAwait(false);
|
||||
IList<EResult> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.RedeemPoints(definitionID, forced))).ConfigureAwait(false);
|
||||
|
||||
Dictionary<string, EResult> result = new(bots.Count, Bot.BotsComparer);
|
||||
|
||||
|
||||
@@ -386,6 +386,58 @@ public sealed class ArchiHandler : ClientMsgHandler {
|
||||
return response.Result == EResult.OK ? response.Body.summary?.points : null;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public async Task<Dictionary<uint, LoyaltyRewardDefinition>?> GetRewardItems(IReadOnlyCollection<uint> definitionIDs) {
|
||||
if ((definitionIDs == null) || (definitionIDs.Count == 0)) {
|
||||
throw new ArgumentNullException(nameof(definitionIDs));
|
||||
}
|
||||
|
||||
if (Client == null) {
|
||||
throw new InvalidOperationException(nameof(Client));
|
||||
}
|
||||
|
||||
if (!Client.IsConnected) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CLoyaltyRewards_QueryRewardItems_Request request = new();
|
||||
|
||||
request.definitionids.AddRange(definitionIDs is IReadOnlySet<uint> or ISet<uint> ? definitionIDs : definitionIDs.Distinct());
|
||||
|
||||
Dictionary<uint, LoyaltyRewardDefinition>? result = null;
|
||||
|
||||
while (true) {
|
||||
SteamUnifiedMessages.ServiceMethodResponse<CLoyaltyRewards_QueryRewardItems_Response> response;
|
||||
|
||||
try {
|
||||
response = await UnifiedLoyaltyRewards.QueryRewardItems(request).ToLongRunningTask().ConfigureAwait(false);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericWarningException(e);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.Result != EResult.OK) {
|
||||
return null;
|
||||
}
|
||||
|
||||
result ??= new Dictionary<uint, LoyaltyRewardDefinition>(response.Body.total_count);
|
||||
|
||||
bool added = false;
|
||||
|
||||
foreach (LoyaltyRewardDefinition _ in response.Body.definitions.Where(entry => result.TryAdd(entry.defid, entry))) {
|
||||
added = true;
|
||||
}
|
||||
|
||||
// Normally it should be enough to compare counts exclusively, but we're going to use additional bulletproofing against infinite loops just in case
|
||||
if (!added || (result.Count >= response.Body.total_count) || string.IsNullOrEmpty(response.Body.next_cursor) || (request.cursor == response.Body.next_cursor)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
request.cursor = response.Body.next_cursor;
|
||||
}
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public async Task<CCredentials_GetSteamGuardDetails_Response?> GetSteamGuardStatus() {
|
||||
if (Client == null) {
|
||||
|
||||
@@ -42,6 +42,7 @@ using ArchiSteamFarm.Web;
|
||||
using JetBrains.Annotations;
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
using SteamKit2.WebUI.Internal;
|
||||
|
||||
namespace ArchiSteamFarm.Steam.Interaction;
|
||||
|
||||
@@ -171,6 +172,15 @@ public sealed class Actions : IAsyncDisposable, IDisposable {
|
||||
return (steamOwnerID > 0) && new SteamID(steamOwnerID).IsIndividualAccount ? steamOwnerID : 0;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public async Task<Dictionary<uint, LoyaltyRewardDefinition>?> GetRewardItems(IReadOnlyCollection<uint> definitionIDs) {
|
||||
if ((definitionIDs == null) || (definitionIDs.Count == 0)) {
|
||||
throw new ArgumentNullException(nameof(definitionIDs));
|
||||
}
|
||||
|
||||
return await Bot.ArchiHandler.GetRewardItems(definitionIDs).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[MustDisposeResource]
|
||||
[PublicAPI]
|
||||
public async Task<IDisposable> GetTradingLock() {
|
||||
@@ -314,9 +324,25 @@ public sealed class Actions : IAsyncDisposable, IDisposable {
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public async Task<EResult> RedeemPoints(uint definitionID) {
|
||||
public async Task<EResult> RedeemPoints(uint definitionID, bool forced = false) {
|
||||
ArgumentOutOfRangeException.ThrowIfZero(definitionID);
|
||||
|
||||
if (!forced) {
|
||||
Dictionary<uint, LoyaltyRewardDefinition>? definitions = await Bot.Actions.GetRewardItems(new HashSet<uint>(1) { definitionID }).ConfigureAwait(false);
|
||||
|
||||
if (definitions == null) {
|
||||
return EResult.Timeout;
|
||||
}
|
||||
|
||||
if (!definitions.TryGetValue(definitionID, out LoyaltyRewardDefinition? definition)) {
|
||||
return EResult.InvalidParam;
|
||||
}
|
||||
|
||||
if (definition.point_cost > 0) {
|
||||
return EResult.InvalidState;
|
||||
}
|
||||
}
|
||||
|
||||
return await Bot.ArchiHandler.RedeemPoints(definitionID).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ using ArchiSteamFarm.Storage;
|
||||
using JetBrains.Annotations;
|
||||
using SteamKit2;
|
||||
using SteamKit2.Internal;
|
||||
using SteamKit2.WebUI.Internal;
|
||||
|
||||
namespace ArchiSteamFarm.Steam.Interaction;
|
||||
|
||||
@@ -1028,6 +1029,10 @@ public sealed class Commands {
|
||||
ArgumentOutOfRangeException.ThrowIfZero(contextID);
|
||||
ArgumentNullException.ThrowIfNull(targetBot);
|
||||
|
||||
if ((assetRarities == null) || (assetRarities.Count == 0)) {
|
||||
throw new ArgumentNullException(nameof(assetRarities));
|
||||
}
|
||||
|
||||
if (access < EAccess.Master) {
|
||||
return null;
|
||||
}
|
||||
@@ -2767,7 +2772,7 @@ public sealed class Commands {
|
||||
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
|
||||
}
|
||||
|
||||
private async Task<string?> ResponseRedeemPoints(EAccess access, HashSet<uint> definitionIDs) {
|
||||
private async Task<string?> ResponseRedeemPoints(EAccess access, Dictionary<uint, bool> definitionIDs) {
|
||||
if (!Enum.IsDefined(access)) {
|
||||
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
|
||||
}
|
||||
@@ -2784,13 +2789,34 @@ public sealed class Commands {
|
||||
return FormatBotResponse(Strings.BotNotConnected);
|
||||
}
|
||||
|
||||
IList<EResult> results = await Utilities.InParallel(definitionIDs.Select(Bot.Actions.RedeemPoints)).ConfigureAwait(false);
|
||||
HashSet<uint> definitionIDsToCheck = definitionIDs.Where(static entry => !entry.Value).Select(static entry => entry.Key).ToHashSet();
|
||||
|
||||
if (definitionIDsToCheck.Count > 0) {
|
||||
Dictionary<uint, LoyaltyRewardDefinition>? definitions = await Bot.Actions.GetRewardItems(definitionIDsToCheck).ConfigureAwait(false);
|
||||
|
||||
if (definitions == null) {
|
||||
return FormatBotResponse(Strings.FormatWarningFailedWithError(nameof(Bot.Actions.GetRewardItems)));
|
||||
}
|
||||
|
||||
foreach (uint definitionID in definitionIDsToCheck) {
|
||||
if (!definitions.TryGetValue(definitionID, out LoyaltyRewardDefinition? definition)) {
|
||||
return FormatBotResponse(Strings.FormatWarningFailedWithError(definitionID));
|
||||
}
|
||||
|
||||
if (definition.point_cost > 0) {
|
||||
return FormatBotResponse(Strings.FormatWarningFailedWithError($"{definitionID} {nameof(definition.point_cost)} ({definition.point_cost}) > 0"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We already did more optimized check, therefore we can skip the one in actions
|
||||
IList<EResult> results = await Utilities.InParallel(definitionIDs.Keys.Select(definitionID => Bot.Actions.RedeemPoints(definitionID, true))).ConfigureAwait(false);
|
||||
|
||||
int i = 0;
|
||||
|
||||
StringBuilder response = new();
|
||||
|
||||
foreach (uint definitionID in definitionIDs) {
|
||||
foreach (uint definitionID in definitionIDs.Keys) {
|
||||
response.AppendLine(FormatBotResponse(Strings.FormatBotAddLicense(definitionID, results[i++])));
|
||||
}
|
||||
|
||||
@@ -2818,14 +2844,22 @@ public sealed class Commands {
|
||||
return FormatBotResponse(Strings.FormatErrorIsEmpty(nameof(definitions)));
|
||||
}
|
||||
|
||||
HashSet<uint> definitionIDs = new(definitions.Length);
|
||||
Dictionary<uint, bool> definitionIDs = new(definitions.Length);
|
||||
|
||||
foreach (string definition in definitions) {
|
||||
if (!uint.TryParse(definition, out uint definitionID) || (definitionID == 0)) {
|
||||
bool forced = false;
|
||||
string definitionToParse = definition;
|
||||
|
||||
if (definitionToParse.EndsWith('!')) {
|
||||
forced = true;
|
||||
definitionToParse = definitionToParse[..^1];
|
||||
}
|
||||
|
||||
if (!uint.TryParse(definitionToParse, out uint definitionID) || (definitionID == 0)) {
|
||||
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(definition)));
|
||||
}
|
||||
|
||||
definitionIDs.Add(definitionID);
|
||||
definitionIDs[definitionID] = forced;
|
||||
}
|
||||
|
||||
return await ResponseRedeemPoints(access, definitionIDs).ConfigureAwait(false);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>6.0.8.8</Version>
|
||||
<Version>6.0.9.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
Reference in New Issue
Block a user