Skip to content

Commit 55d49f8

Browse files
committed
Closes #3299
1 parent c2abbf0 commit 55d49f8

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed

ArchiSteamFarm/IPC/Controllers/Api/BotController.cs

+28
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,34 @@ public async Task<ActionResult<GenericResponse>> AddLicensePost(string botNames,
7474
return Ok(new GenericResponse<IReadOnlyDictionary<string, BotAddLicenseResponse>>(result));
7575
}
7676

77+
/// <summary>
78+
/// Redeems points on given bots.
79+
/// </summary>
80+
[Consumes("application/json")]
81+
[HttpPost("{botNames:required}/RedeemPoints/{definitionID:required}")]
82+
[ProducesResponseType<GenericResponse<IReadOnlyDictionary<string, EResult>>>((int) HttpStatusCode.OK)]
83+
[ProducesResponseType<GenericResponse>((int) HttpStatusCode.BadRequest)]
84+
public async Task<ActionResult<GenericResponse>> AddLicensePost(string botNames, uint definitionID) {
85+
ArgumentException.ThrowIfNullOrEmpty(botNames);
86+
ArgumentOutOfRangeException.ThrowIfZero(definitionID);
87+
88+
HashSet<Bot>? bots = Bot.GetBots(botNames);
89+
90+
if ((bots == null) || (bots.Count == 0)) {
91+
return BadRequest(new GenericResponse(false, Strings.FormatBotNotFound(botNames)));
92+
}
93+
94+
IList<EResult> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.RedeemPoints(definitionID))).ConfigureAwait(false);
95+
96+
Dictionary<string, EResult> result = new(bots.Count, Bot.BotsComparer);
97+
98+
foreach (Bot bot in bots) {
99+
result[bot.BotName] = results[result.Count];
100+
}
101+
102+
return Ok(new GenericResponse<IReadOnlyDictionary<string, EResult>>(result));
103+
}
104+
77105
/// <summary>
78106
/// Deletes all files related to given bots.
79107
/// </summary>

ArchiSteamFarm/Steam/Integration/ArchiHandler.cs

+31
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public sealed class ArchiHandler : ClientMsgHandler {
6161
private readonly SteamUnifiedMessages.UnifiedService<IEcon> UnifiedEconService;
6262
private readonly SteamUnifiedMessages.UnifiedService<IFamilyGroups> UnifiedFamilyGroups;
6363
private readonly SteamUnifiedMessages.UnifiedService<IFriendMessages> UnifiedFriendMessagesService;
64+
private readonly SteamUnifiedMessages.UnifiedService<ILoyaltyRewards> UnifiedLoyaltyRewards;
6465
private readonly SteamUnifiedMessages.UnifiedService<IPlayer> UnifiedPlayerService;
6566
private readonly SteamUnifiedMessages.UnifiedService<IStore> UnifiedStoreService;
6667
private readonly SteamUnifiedMessages.UnifiedService<ITwoFactor> UnifiedTwoFactorService;
@@ -80,6 +81,7 @@ internal ArchiHandler(ArchiLogger archiLogger, SteamUnifiedMessages steamUnified
8081
UnifiedEconService = steamUnifiedMessages.CreateService<IEcon>();
8182
UnifiedFamilyGroups = steamUnifiedMessages.CreateService<IFamilyGroups>();
8283
UnifiedFriendMessagesService = steamUnifiedMessages.CreateService<IFriendMessages>();
84+
UnifiedLoyaltyRewards = steamUnifiedMessages.CreateService<ILoyaltyRewards>();
8385
UnifiedPlayerService = steamUnifiedMessages.CreateService<IPlayer>();
8486
UnifiedStoreService = steamUnifiedMessages.CreateService<IStore>();
8587
UnifiedTwoFactorService = steamUnifiedMessages.CreateService<ITwoFactor>();
@@ -500,6 +502,35 @@ public async Task<bool> LeaveChatRoomGroup(ulong chatGroupID) {
500502
return response.Result == EResult.OK;
501503
}
502504

505+
[PublicAPI]
506+
public async Task<EResult> RedeemPoints(uint definitionID) {
507+
ArgumentOutOfRangeException.ThrowIfZero(definitionID);
508+
509+
if (Client == null) {
510+
throw new InvalidOperationException(nameof(Client));
511+
}
512+
513+
if (!Client.IsConnected) {
514+
return EResult.NoConnection;
515+
}
516+
517+
CLoyaltyRewards_RedeemPoints_Request request = new() {
518+
defid = definitionID
519+
};
520+
521+
SteamUnifiedMessages.ServiceMethodResponse response;
522+
523+
try {
524+
response = await UnifiedLoyaltyRewards.SendMessage(x => x.RedeemPoints(request)).ToLongRunningTask().ConfigureAwait(false);
525+
} catch (Exception e) {
526+
ArchiLogger.LogGenericWarningException(e);
527+
528+
return EResult.Timeout;
529+
}
530+
531+
return response.Result;
532+
}
533+
503534
[PublicAPI]
504535
public async Task<bool> RemoveFriend(ulong steamID) {
505536
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {

ArchiSteamFarm/Steam/Interaction/Actions.cs

+7
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,13 @@ public static string Hash(ArchiCryptoHelper.EHashingMethod hashingMethod, string
313313
return await Bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
314314
}
315315

316+
[PublicAPI]
317+
public async Task<EResult> RedeemPoints(uint definitionID) {
318+
ArgumentOutOfRangeException.ThrowIfZero(definitionID);
319+
320+
return await Bot.ArchiHandler.RedeemPoints(definitionID).ConfigureAwait(false);
321+
}
322+
316323
[PublicAPI]
317324
public static (bool Success, string Message) Restart() {
318325
if (!Program.RestartAllowed) {

ArchiSteamFarm/Steam/Interaction/Commands.cs

+87
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ public static EAccess GetProxyAccess(Bot bot, EAccess access, ulong steamID = 0)
298298
return await ResponseAdvancedRedeem(access, args[1], args[2], Utilities.GetArgsAsText(args, 3, ","), steamID).ConfigureAwait(false);
299299
case "R^" or "REDEEM^" when args.Length > 2:
300300
return await ResponseAdvancedRedeem(access, args[1], args[2], steamID).ConfigureAwait(false);
301+
case "RP" or "REDEEMPOINTS" when args.Length > 2:
302+
return await ResponseRedeemPoints(access, args[1], Utilities.GetArgsAsText(args, 2, ","), steamID).ConfigureAwait(false);
303+
case "RP" or "REDEEMPOINTS":
304+
return await ResponseRedeemPoints(access, args[1]).ConfigureAwait(false);
301305
case "RESET":
302306
return await ResponseReset(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
303307
case "RESUME":
@@ -2763,6 +2767,89 @@ internal void OnNewLicenseList() {
27632767
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
27642768
}
27652769

2770+
private async Task<string?> ResponseRedeemPoints(EAccess access, HashSet<uint> definitionIDs) {
2771+
if (!Enum.IsDefined(access)) {
2772+
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
2773+
}
2774+
2775+
if ((definitionIDs == null) || (definitionIDs.Count == 0)) {
2776+
throw new ArgumentNullException(nameof(definitionIDs));
2777+
}
2778+
2779+
if (access < EAccess.Operator) {
2780+
return null;
2781+
}
2782+
2783+
if (!Bot.IsConnectedAndLoggedOn) {
2784+
return FormatBotResponse(Strings.BotNotConnected);
2785+
}
2786+
2787+
StringBuilder response = new();
2788+
2789+
foreach (uint definitionID in definitionIDs) {
2790+
EResult result = await Bot.Actions.RedeemPoints(definitionID).ConfigureAwait(false);
2791+
2792+
response.AppendLine(FormatBotResponse(Strings.FormatBotAddLicense(definitionID, result)));
2793+
}
2794+
2795+
return response.Length > 0 ? response.ToString() : null;
2796+
}
2797+
2798+
private async Task<string?> ResponseRedeemPoints(EAccess access, string targetDefinitionIDs) {
2799+
if (!Enum.IsDefined(access)) {
2800+
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
2801+
}
2802+
2803+
ArgumentException.ThrowIfNullOrEmpty(targetDefinitionIDs);
2804+
2805+
if (access < EAccess.Operator) {
2806+
return null;
2807+
}
2808+
2809+
if (!Bot.IsConnectedAndLoggedOn) {
2810+
return FormatBotResponse(Strings.BotNotConnected);
2811+
}
2812+
2813+
string[] definitions = targetDefinitionIDs.Split(SharedInfo.ListElementSeparators, StringSplitOptions.RemoveEmptyEntries);
2814+
2815+
if (definitions.Length == 0) {
2816+
return FormatBotResponse(Strings.FormatErrorIsEmpty(nameof(definitions)));
2817+
}
2818+
2819+
HashSet<uint> definitionIDs = new(definitions.Length);
2820+
2821+
foreach (string definition in definitions) {
2822+
if (!uint.TryParse(definition, out uint definitionID) || (definitionID == 0)) {
2823+
return FormatBotResponse(Strings.FormatErrorIsInvalid(nameof(definition)));
2824+
}
2825+
2826+
definitionIDs.Add(definitionID);
2827+
}
2828+
2829+
return await ResponseRedeemPoints(access, definitionIDs).ConfigureAwait(false);
2830+
}
2831+
2832+
private static async Task<string?> ResponseRedeemPoints(EAccess access, string botNames, string targetDefinitionIDs, ulong steamID = 0) {
2833+
if (!Enum.IsDefined(access)) {
2834+
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
2835+
}
2836+
2837+
ArgumentException.ThrowIfNullOrEmpty(botNames);
2838+
ArgumentException.ThrowIfNullOrEmpty(targetDefinitionIDs);
2839+
2840+
HashSet<Bot>? bots = Bot.GetBots(botNames);
2841+
2842+
if ((bots == null) || (bots.Count == 0)) {
2843+
return access >= EAccess.Owner ? FormatStaticResponse(Strings.FormatBotNotFound(botNames)) : null;
2844+
}
2845+
2846+
IList<string?> results = await Utilities.InParallel(bots.Select(bot => bot.Commands.ResponseRedeemPoints(GetProxyAccess(bot, access, steamID), targetDefinitionIDs))).ConfigureAwait(false);
2847+
2848+
List<string> responses = [..results.Where(static result => !string.IsNullOrEmpty(result))!];
2849+
2850+
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
2851+
}
2852+
27662853
private async Task<string?> ResponseReset(EAccess access) {
27672854
if (!Enum.IsDefined(access)) {
27682855
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));

0 commit comments

Comments
 (0)