mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2026-01-14 07:30:39 +00:00
Misc improvements to ASF API
In bot controller, 3 endpoints:
POST {bot}
POST {bot}/GamesToRedeemInBackground
POST {bot}/Redeem
Now accept botNames (like all other endpoints), GenericResponse included in those responses now include Dictionary that maps previous single response to botName-response.
@JustArchiNET/asf-ui if you're not doing anything with the inner result of generic response, you don't need to do anything. If you do, please map your previous single inner result to dictionary of botName-single result. Functionality remains the same, thanks.
This commit is contained in:
@@ -265,6 +265,19 @@ namespace ArchiSteamFarm {
|
||||
Trading?.Dispose();
|
||||
}
|
||||
|
||||
internal async Task AddGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) {
|
||||
if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) {
|
||||
ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground));
|
||||
return;
|
||||
}
|
||||
|
||||
await BotDatabase.AddGamesToRedeemInBackground(gamesToRedeemInBackground).ConfigureAwait(false);
|
||||
|
||||
if ((GamesRedeemerInBackgroundTimer == null) && BotDatabase.HasGamesToRedeemInBackground && IsConnectedAndLoggedOn) {
|
||||
Utilities.InBackground(RedeemGamesInBackground);
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<bool> DeleteAllRelatedFiles() {
|
||||
await BotDatabase.MakeReadOnly().ConfigureAwait(false);
|
||||
|
||||
@@ -675,7 +688,11 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
|
||||
if (gamesToRedeemInBackground.Count > 0) {
|
||||
await ValidateAndAddGamesToRedeemInBackground(gamesToRedeemInBackground).ConfigureAwait(false);
|
||||
IOrderedDictionary validGamesToRedeemInBackground = ValidateGamesToRedeemInBackground(gamesToRedeemInBackground);
|
||||
|
||||
if ((validGamesToRedeemInBackground != null) && (validGamesToRedeemInBackground.Count > 0)) {
|
||||
await AddGamesToRedeemInBackground(validGamesToRedeemInBackground).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
File.Delete(filePath);
|
||||
@@ -1103,10 +1120,10 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<bool> ValidateAndAddGamesToRedeemInBackground(OrderedDictionary gamesToRedeemInBackground) {
|
||||
internal static IOrderedDictionary ValidateGamesToRedeemInBackground(IOrderedDictionary gamesToRedeemInBackground) {
|
||||
if ((gamesToRedeemInBackground == null) || (gamesToRedeemInBackground.Count == 0)) {
|
||||
ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground));
|
||||
return false;
|
||||
ASF.ArchiLogger.LogNullError(nameof(gamesToRedeemInBackground));
|
||||
return gamesToRedeemInBackground;
|
||||
}
|
||||
|
||||
HashSet<object> invalidKeys = new HashSet<object>();
|
||||
@@ -1117,16 +1134,16 @@ namespace ArchiSteamFarm {
|
||||
string key = game.Key as string;
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
invalid = true;
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(key)));
|
||||
ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(key)));
|
||||
} else if (!Utilities.IsValidCdKey(key)) {
|
||||
invalid = true;
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, key));
|
||||
ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, key));
|
||||
}
|
||||
|
||||
string name = game.Value as string;
|
||||
if (string.IsNullOrEmpty(name)) {
|
||||
invalid = true;
|
||||
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(name)));
|
||||
ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsInvalid, nameof(name)));
|
||||
}
|
||||
|
||||
if (invalid) {
|
||||
@@ -1138,19 +1155,9 @@ namespace ArchiSteamFarm {
|
||||
foreach (string invalidKey in invalidKeys) {
|
||||
gamesToRedeemInBackground.Remove(invalidKey);
|
||||
}
|
||||
|
||||
if (gamesToRedeemInBackground.Count == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
await BotDatabase.AddGamesToRedeemInBackground(gamesToRedeemInBackground).ConfigureAwait(false);
|
||||
|
||||
if ((GamesRedeemerInBackgroundTimer == null) && BotDatabase.HasGamesToRedeemInBackground && IsConnectedAndLoggedOn) {
|
||||
Utilities.InBackground(RedeemGamesInBackground);
|
||||
}
|
||||
|
||||
return true;
|
||||
return gamesToRedeemInBackground;
|
||||
}
|
||||
|
||||
private async Task CheckOccupationStatus() {
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace ArchiSteamFarm {
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task AddGamesToRedeemInBackground(OrderedDictionary games) {
|
||||
internal async Task AddGamesToRedeemInBackground(IOrderedDictionary games) {
|
||||
if ((games == null) || (games.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(games));
|
||||
return;
|
||||
|
||||
@@ -76,40 +76,58 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
/// Updates bot config of given bot.
|
||||
/// </summary>
|
||||
[Consumes("application/json")]
|
||||
[HttpPost("{botName:required}")]
|
||||
[ProducesResponseType(typeof(GenericResponse), 200)]
|
||||
public async Task<ActionResult<GenericResponse>> BotPost(string botName, [FromBody] BotRequest request) {
|
||||
if (string.IsNullOrEmpty(botName) || (request == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request));
|
||||
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsEmpty, nameof(botName) + " || " + nameof(request))));
|
||||
[HttpPost("{botNames:required}")]
|
||||
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, bool>>), 200)]
|
||||
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, bool>>>> BotPost(string botNames, [FromBody] BotRequest request) {
|
||||
if (string.IsNullOrEmpty(botNames) || (request == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request));
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, bool>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
|
||||
}
|
||||
|
||||
(bool valid, string errorMessage) = request.BotConfig.CheckValidation();
|
||||
if (!valid) {
|
||||
return BadRequest(new GenericResponse(false, errorMessage));
|
||||
}
|
||||
|
||||
if (Bot.Bots.TryGetValue(botName, out Bot bot)) {
|
||||
if (!request.BotConfig.IsSteamLoginSet && bot.BotConfig.IsSteamLoginSet) {
|
||||
request.BotConfig.SteamLogin = bot.BotConfig.SteamLogin;
|
||||
}
|
||||
|
||||
if (!request.BotConfig.IsSteamPasswordSet && bot.BotConfig.IsSteamPasswordSet) {
|
||||
request.BotConfig.DecryptedSteamPassword = bot.BotConfig.DecryptedSteamPassword;
|
||||
}
|
||||
|
||||
if (!request.BotConfig.IsSteamParentalCodeSet && bot.BotConfig.IsSteamParentalCodeSet) {
|
||||
request.BotConfig.SteamParentalCode = bot.BotConfig.SteamParentalCode;
|
||||
}
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, bool>>(false, errorMessage));
|
||||
}
|
||||
|
||||
request.BotConfig.ShouldSerializeEverything = false;
|
||||
request.BotConfig.ShouldSerializeHelperProperties = false;
|
||||
|
||||
string filePath = Path.Combine(SharedInfo.ConfigDirectory, botName + SharedInfo.ConfigExtension);
|
||||
string originalSteamLogin = request.BotConfig.SteamLogin;
|
||||
string originalDecryptedSteamPassword = request.BotConfig.DecryptedSteamPassword;
|
||||
string originalSteamParentalCode = request.BotConfig.SteamParentalCode;
|
||||
|
||||
bool result = await BotConfig.Write(filePath, request.BotConfig).ConfigureAwait(false);
|
||||
return Ok(new GenericResponse(result));
|
||||
HashSet<string> bots = botNames.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Where(botName => botName != SharedInfo.ASF).ToHashSet();
|
||||
|
||||
Dictionary<string, bool> result = new Dictionary<string, bool>(bots.Count);
|
||||
|
||||
foreach (string botName in bots) {
|
||||
bool botExists = Bot.Bots.TryGetValue(botName, out Bot bot);
|
||||
|
||||
if (botExists) {
|
||||
if (!request.BotConfig.IsSteamLoginSet && bot.BotConfig.IsSteamLoginSet) {
|
||||
request.BotConfig.SteamLogin = bot.BotConfig.SteamLogin;
|
||||
}
|
||||
|
||||
if (!request.BotConfig.IsSteamPasswordSet && bot.BotConfig.IsSteamPasswordSet) {
|
||||
request.BotConfig.DecryptedSteamPassword = bot.BotConfig.DecryptedSteamPassword;
|
||||
}
|
||||
|
||||
if (!request.BotConfig.IsSteamParentalCodeSet && bot.BotConfig.IsSteamParentalCodeSet) {
|
||||
request.BotConfig.SteamParentalCode = bot.BotConfig.SteamParentalCode;
|
||||
}
|
||||
}
|
||||
|
||||
string filePath = Path.Combine(SharedInfo.ConfigDirectory, botName + SharedInfo.ConfigExtension);
|
||||
result[botName] = await BotConfig.Write(filePath, request.BotConfig).ConfigureAwait(false);
|
||||
|
||||
if (botExists) {
|
||||
request.BotConfig.SteamLogin = originalSteamLogin;
|
||||
request.BotConfig.DecryptedSteamPassword = originalDecryptedSteamPassword;
|
||||
request.BotConfig.SteamParentalCode = originalSteamParentalCode;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse<IReadOnlyDictionary<string, bool>>(result.Values.All(value => value), result));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -164,24 +182,37 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
/// Adds keys to redeem using BGR to given bot.
|
||||
/// </summary>
|
||||
[Consumes("application/json")]
|
||||
[HttpPost("{botName:required}/GamesToRedeemInBackground")]
|
||||
[ProducesResponseType(typeof(GenericResponse<IOrderedDictionary>), 200)]
|
||||
public async Task<ActionResult<GenericResponse<IOrderedDictionary>>> GamesToRedeemInBackgroundPost(string botName, [FromBody] BotGamesToRedeemInBackgroundRequest request) {
|
||||
if (string.IsNullOrEmpty(botName) || (request == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request));
|
||||
return BadRequest(new GenericResponse<IOrderedDictionary>(false, string.Format(Strings.ErrorIsEmpty, nameof(botName) + " || " + nameof(request))));
|
||||
[HttpPost("{botNames:required}/GamesToRedeemInBackground")]
|
||||
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>), 200)]
|
||||
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>>> GamesToRedeemInBackgroundPost(string botNames, [FromBody] BotGamesToRedeemInBackgroundRequest request) {
|
||||
if (string.IsNullOrEmpty(botNames) || (request == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request));
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
|
||||
}
|
||||
|
||||
if (request.GamesToRedeemInBackground.Count == 0) {
|
||||
return BadRequest(new GenericResponse<IOrderedDictionary>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.GamesToRedeemInBackground))));
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.GamesToRedeemInBackground))));
|
||||
}
|
||||
|
||||
if (!Bot.Bots.TryGetValue(botName, out Bot bot)) {
|
||||
return BadRequest(new GenericResponse<IOrderedDictionary>(false, string.Format(Strings.BotNotFound, botName)));
|
||||
HashSet<Bot> bots = Bot.GetBots(botNames);
|
||||
if ((bots == null) || (bots.Count == 0)) {
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>(false, string.Format(Strings.BotNotFound, botNames)));
|
||||
}
|
||||
|
||||
bool result = await bot.ValidateAndAddGamesToRedeemInBackground(request.GamesToRedeemInBackground).ConfigureAwait(false);
|
||||
return Ok(new GenericResponse<IOrderedDictionary>(result, request.GamesToRedeemInBackground));
|
||||
IOrderedDictionary validGamesToRedeemInBackground = Bot.ValidateGamesToRedeemInBackground(request.GamesToRedeemInBackground);
|
||||
if ((validGamesToRedeemInBackground == null) || (validGamesToRedeemInBackground.Count == 0)) {
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>(false, string.Format(Strings.ErrorIsEmpty, nameof(validGamesToRedeemInBackground))));
|
||||
}
|
||||
|
||||
await Utilities.InParallel(bots.Select(bot => bot.AddGamesToRedeemInBackground(validGamesToRedeemInBackground))).ConfigureAwait(false);
|
||||
|
||||
Dictionary<string, IOrderedDictionary> result = new Dictionary<string, IOrderedDictionary>(bots.Count);
|
||||
|
||||
foreach (Bot bot in bots) {
|
||||
result[bot.BotName] = validGamesToRedeemInBackground;
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse<IReadOnlyDictionary<string, IOrderedDictionary>>(result));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -213,31 +244,39 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
|
||||
/// Redeem result can be a null value, this means that ASF didn't even attempt to send a request (e.g. because of bot not being connected to Steam network).
|
||||
/// </remarks>
|
||||
[Consumes("application/json")]
|
||||
[HttpPost("{botName:required}/Redeem")]
|
||||
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>), 200)]
|
||||
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>> RedeemPost(string botName, [FromBody] BotRedeemRequest request) {
|
||||
if (string.IsNullOrEmpty(botName) || (request == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botName) + " || " + nameof(request));
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botName) + " || " + nameof(request))));
|
||||
[HttpPost("{botNames:required}/Redeem")]
|
||||
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>), 200)]
|
||||
public async Task<ActionResult<GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>>> RedeemPost(string botNames, [FromBody] BotRedeemRequest request) {
|
||||
if (string.IsNullOrEmpty(botNames) || (request == null)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(botNames) + " || " + nameof(request));
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
|
||||
}
|
||||
|
||||
if (request.KeysToRedeem.Count == 0) {
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.KeysToRedeem))));
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>(false, string.Format(Strings.ErrorIsEmpty, nameof(request.KeysToRedeem))));
|
||||
}
|
||||
|
||||
if (!Bot.Bots.TryGetValue(botName, out Bot bot)) {
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>(false, string.Format(Strings.BotNotFound, botName)));
|
||||
HashSet<Bot> bots = Bot.GetBots(botNames);
|
||||
if ((bots == null) || (bots.Count == 0)) {
|
||||
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>(false, string.Format(Strings.BotNotFound, botNames)));
|
||||
}
|
||||
|
||||
IList<ArchiHandler.PurchaseResponseCallback> results = await Utilities.InParallel(request.KeysToRedeem.Select(key => bot.Actions.RedeemKey(key))).ConfigureAwait(false);
|
||||
IList<ArchiHandler.PurchaseResponseCallback> results = await Utilities.InParallel(bots.Select(bot => request.KeysToRedeem.Select(key => bot.Actions.RedeemKey(key))).SelectMany(task => task)).ConfigureAwait(false);
|
||||
|
||||
Dictionary<string, ArchiHandler.PurchaseResponseCallback> result = new Dictionary<string, ArchiHandler.PurchaseResponseCallback>(request.KeysToRedeem.Count);
|
||||
Dictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>> result = new Dictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>(bots.Count);
|
||||
|
||||
foreach (string key in request.KeysToRedeem) {
|
||||
result[key] = results[result.Count];
|
||||
int count = 0;
|
||||
|
||||
foreach (Bot bot in bots) {
|
||||
Dictionary<string, ArchiHandler.PurchaseResponseCallback> responses = new Dictionary<string, ArchiHandler.PurchaseResponseCallback>(request.KeysToRedeem.Count);
|
||||
result[bot.BotName] = responses;
|
||||
|
||||
foreach (string key in request.KeysToRedeem) {
|
||||
responses[key] = results[count++];
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(new GenericResponse<IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>(result.Values.All(value => value != null), result));
|
||||
return Ok(new GenericResponse<IReadOnlyDictionary<string, IReadOnlyDictionary<string, ArchiHandler.PurchaseResponseCallback>>>(result.Values.SelectMany(responses => responses.Values).All(value => value != null), result));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user