Compare commits

..

33 Commits

Author SHA1 Message Date
JustArchi
c4c11cf61f Update Strings.resx 2020-08-11 11:44:32 +02:00
JustArchi
06b1b8deeb Misc trade logging enhancements 2020-08-11 11:34:32 +02:00
GitHub Action
0beb000d4b Automatic translations update 2020-08-11 02:07:38 +00:00
dependabot-preview[bot]
acc871e609 Bump ASF-ui from 6e066d0 to f6e73f9
Bumps [ASF-ui](https://github.com/JustArchiNET/ASF-ui) from `6e066d0` to `f6e73f9`.
- [Release notes](https://github.com/JustArchiNET/ASF-ui/releases)
- [Commits](6e066d0edc...f6e73f9c90)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-11 01:28:55 +00:00
dependabot-preview[bot]
e8545176e7 Bump wiki from 424e013 to c04af74
Bumps [wiki](https://github.com/JustArchiNET/ArchiSteamFarm.wiki) from `424e013` to `c04af74`.
- [Release notes](https://github.com/JustArchiNET/ArchiSteamFarm.wiki/releases)
- [Commits](424e0133fd...c04af74ce5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-11 01:22:20 +00:00
JustArchi
532a47bda5 Update config.yml 2020-08-11 01:10:30 +02:00
JustArchi
ddc2d956ba Misc 2020-08-11 01:07:15 +02:00
JustArchi
563f57e3aa Keep readme in project's root
Even if it's not critical, it's a good practice to have a readme, not to mention that it can be used in other sources, such as docker page
2020-08-11 01:06:27 +02:00
JustArchi
0a973cefd2 Move non-critical md resources into .github directory 2020-08-11 01:05:16 +02:00
JustArchi
2563ade159 Bump 2020-08-10 15:07:37 +02:00
JustArchi
1763a109c4 Move new handle under /Confirmations root 2020-08-10 14:57:04 +02:00
JustArchi
f57b5a2166 Use unified name instead of full name
Handles Class<T> better
2020-08-10 14:42:50 +02:00
JustArchi
7210e6b86c Misc 2020-08-10 14:04:06 +02:00
JustArchi
6b241d7439 Maintain backwards compatibility 2020-08-10 14:02:28 +02:00
JustArchi
833995ca61 Fix swaggergen generation
By avoiding a possibility of multiple conflicting types having the same name.

I'm not sure why this isn't the default behaviour, seems silly to me.
2020-08-10 13:41:07 +02:00
JustArchi
40531e9554 Misc
damn sneaky boi
2020-08-10 13:25:46 +02:00
JustArchi
7a5a9c8a51 Enhance TwoFactorAuthentication/Confirmations IPC API by allowing to specify additional parameters 2020-08-10 13:24:12 +02:00
GitHub Action
daae97b3d4 Automatic translations update 2020-08-10 02:07:36 +00:00
dependabot-preview[bot]
eeeaacb4db Bump wiki from 67094f0 to 5fed7f3
Bumps [wiki](https://github.com/JustArchiNET/ArchiSteamFarm.wiki) from `67094f0` to `5fed7f3`.
- [Release notes](https://github.com/JustArchiNET/ArchiSteamFarm.wiki/releases)
- [Commits](67094f0241...5fed7f38d7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-10 01:28:34 +00:00
dependabot-preview[bot]
5324a9127d Bump ASF-ui from eae9c22 to 6e066d0
Bumps [ASF-ui](https://github.com/JustArchiNET/ASF-ui) from `eae9c22` to `6e066d0`.
- [Release notes](https://github.com/JustArchiNET/ASF-ui/releases)
- [Commits](eae9c22d4d...6e066d0edc)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-10 01:23:22 +00:00
JustArchi
bbd267d4f0 Validate SteamTradeToken against length of 8 2020-08-10 00:49:19 +02:00
GitHub Action
53495e34e5 Automatic translations update 2020-08-09 02:07:29 +00:00
JustArchi
7026d8b0d5 Remove ASF-WebConfigGenerator as git submodule
We still use the project and it's not going anywhere, but it's no longer required to be linked directly in ASF core repo. Previously it was linked for translations process, but since it's standalone now, nothing in ASF core references it anymore.

We still need ASF-ui and wiki though, first one as dependency during ASF build process, and second one as we're aiding translation process for that one.
2020-08-09 02:27:42 +02:00
GitHub Action
a3b57ac50e Automatic translations update 2020-08-08 02:07:19 +00:00
GitHub Action
4910692576 Automatic translations update 2020-08-07 02:07:40 +00:00
dependabot-preview[bot]
5147835d48 Bump ASF-ui from 01e264f to eae9c22
Bumps [ASF-ui](https://github.com/JustArchiNET/ASF-ui) from `01e264f` to `eae9c22`.
- [Release notes](https://github.com/JustArchiNET/ASF-ui/releases)
- [Commits](01e264f173...eae9c22d4d)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-07 01:37:41 +00:00
dependabot-preview[bot]
3a8d79d4da Bump ASF-WebConfigGenerator from 82ebd9a to 146a4ef
Bumps [ASF-WebConfigGenerator](https://github.com/JustArchiNET/ASF-WebConfigGenerator) from `82ebd9a` to `146a4ef`.
- [Release notes](https://github.com/JustArchiNET/ASF-WebConfigGenerator/releases)
- [Commits](82ebd9af65...146a4ef861)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-07 01:28:12 +00:00
dependabot-preview[bot]
f1684c11a3 Bump wiki from 6a123c4 to dccfd6d
Bumps [wiki](https://github.com/JustArchiNET/ArchiSteamFarm.wiki) from `6a123c4` to `dccfd6d`.
- [Release notes](https://github.com/JustArchiNET/ArchiSteamFarm.wiki/releases)
- [Commits](6a123c4dda...dccfd6d509)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-07 01:22:55 +00:00
JustArchi
f38b6ef6dd Misc
No longer needed
2020-08-06 20:07:14 +02:00
Vitaliy
d895f3e4bb Fix trace log line in checking eligibility for matching (#1927) 2020-08-06 19:04:04 +02:00
Vitaliy
e8b7d7d908 Add trade hold logic to MatchActively (#1926) 2020-08-06 19:03:15 +02:00
dependabot-preview[bot]
e7195b0e68 Bump Microsoft.NET.Test.Sdk from 16.6.1 to 16.7.0
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.6.1 to 16.7.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v16.6.1...v16.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-06 12:34:46 +00:00
JustArchi
2983ede83d Update Directory.Build.props 2020-08-06 13:05:38 +02:00
23 changed files with 253 additions and 93 deletions

View File

@@ -15,9 +15,6 @@ ArchiSteamFarm/logs
# Ignore standard out folders for publishing
**/out
# Ignore crowdin CLI secret (if exists)
tools/ArchiCrowdin/crowdin_identity.yml
# _ ____ _____ ____ _
# / \ / ___|| ___| | _ \ ___ ___| | _____ _ __
# / _ \ \___ \| |_ _____| | | |/ _ \ / __| |/ / _ \ '__|

View File

@@ -7,7 +7,7 @@ contact_links:
url: https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Localization
about: Please use our crowdin platform.
- name: Support question or technical issue
url: https://github.com/JustArchiNET/ArchiSteamFarm/blob/master/SUPPORT.md
url: https://github.com/JustArchiNET/ArchiSteamFarm/blob/master/.github/SUPPORT.md
about: Please review our support guidelines.
- name: Negative feedback, complaints and demands
url: https://www.youtube.com/watch?v=dQw4w9WgXcQ

View File

View File

3
.gitignore vendored
View File

@@ -15,9 +15,6 @@ ArchiSteamFarm/logs
# Ignore standard out folders for publishing
**/out
# Ignore crowdin CLI secret (if exists)
tools/ArchiCrowdin/crowdin_identity.yml
# _ _
# | | (_) _ __ _ _ __ __
# | | | || '_ \ | | | |\ \/ /

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "ASF-WebConfigGenerator"]
path = ASF-WebConfigGenerator
url = https://github.com/JustArchiNET/ASF-WebConfigGenerator.git
[submodule "ASF-ui"]
path = ASF-ui
url = https://github.com/JustArchiNET/ASF-ui.git

2
ASF-ui

Submodule ASF-ui updated: 01e264f173...f6e73f9c90

View File

@@ -7,7 +7,7 @@
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="4.0.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
</ItemGroup>

View File

@@ -138,6 +138,28 @@ namespace ArchiSteamFarm.Tests {
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void SingleGameAbrynosWasWrongNeutralAccept() {
HashSet<Steam.Asset> inventory = new HashSet<Steam.Asset> {
CreateItem(1),
CreateItem(2, 2),
CreateItem(3),
CreateItem(4),
CreateItem(5)
};
HashSet<Steam.Asset> itemsToGive = new HashSet<Steam.Asset> {
CreateItem(2)
};
HashSet<Steam.Asset> itemsToReceive = new HashSet<Steam.Asset> {
CreateItem(3)
};
Assert.IsTrue(IsFairExchange(itemsToGive, itemsToReceive));
Assert.IsTrue(IsTradeNeutralOrBetter(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void SingleGameDonationAccept() {
HashSet<Steam.Asset> inventory = new HashSet<Steam.Asset> {

View File

@@ -286,6 +286,8 @@ namespace ArchiSteamFarm {
try {
inventory = await Bot.ArchiWebHandler.GetInventoryAsync(Bot.SteamID, appID, contextID).Where(item => item.Tradable && filterFunction(item)).ToHashSetAsync().ConfigureAwait(false);
} catch (HttpRequestException e) {
Bot.ArchiLogger.LogGenericWarningException(e);
return (false, string.Format(Strings.WarningFailedWithError, e.Message));
} catch (Exception e) {
Bot.ArchiLogger.LogGenericException(e);

View File

@@ -62,6 +62,7 @@ namespace ArchiSteamFarm {
private const string DefaultSteamTradeToken = null;
private const ETradingPreferences DefaultTradingPreferences = ETradingPreferences.None;
private const bool DefaultUseLoginKeys = true;
private const byte SteamTradeTokenLength = 8;
private static readonly ImmutableList<EFarmingOrder> DefaultFarmingOrders = ImmutableList<EFarmingOrder>.Empty;
private static readonly ImmutableHashSet<uint> DefaultGamesPlayedWhileIdle = ImmutableHashSet<uint>.Empty;
@@ -288,6 +289,10 @@ namespace ArchiSteamFarm {
return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamParentalCode), SteamParentalCode));
}
if (!string.IsNullOrEmpty(SteamTradeToken) && (SteamTradeToken.Length != SteamTradeTokenLength)) {
return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamTradeToken), SteamTradeToken));
}
foreach ((ulong steamID, EPermission permission) in SteamUserPermissions) {
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) {
return (false, string.Format(Strings.ErrorConfigPropertyInvalid, nameof(SteamUserPermissions), steamID));

View File

@@ -2864,7 +2864,9 @@ namespace ArchiSteamFarm {
completeSuccess = false;
}
}
} catch (HttpRequestException) {
} catch (HttpRequestException e) {
Bot.ArchiLogger.LogGenericWarningException(e);
completeSuccess = false;
} catch (Exception e) {
Bot.ArchiLogger.LogGenericException(e);

View File

@@ -446,17 +446,62 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
/// Accepts 2FA confirmations of given bots, requires ASF 2FA module to be active on them.
/// </summary>
[HttpPost("{botNames:required}/TwoFactorAuthentication/Confirmations/Accept")]
[Obsolete]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GenericResponse>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationConfirmationsAcceptPost(string botNames) => await TwoFactorAuthenticationConfirmationsPost(botNames, true).ConfigureAwait(false);
public async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationConfirmationsAcceptPost(string botNames) {
ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningDeprecated, nameof(TwoFactorAuthenticationConfirmationsAcceptPost), nameof(TwoFactorAuthenticationConfirmationsPost)));
return await TwoFactorAuthenticationConfirmationsPost(botNames, new TwoFactorAuthenticationConfirmationsRequest(true)).ConfigureAwait(false);
}
/// <summary>
/// Denies 2FA confirmations of given bots, requires ASF 2FA module to be active on them.
/// </summary>
[HttpPost("{botNames:required}/TwoFactorAuthentication/Confirmations/Cancel")]
[Obsolete]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GenericResponse>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationConfirmationsCancelPost(string botNames) => await TwoFactorAuthenticationConfirmationsPost(botNames, false).ConfigureAwait(false);
public async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationConfirmationsCancelPost(string botNames) {
ASF.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningDeprecated, nameof(TwoFactorAuthenticationConfirmationsCancelPost), nameof(TwoFactorAuthenticationConfirmationsPost)));
return await TwoFactorAuthenticationConfirmationsPost(botNames, new TwoFactorAuthenticationConfirmationsRequest(false)).ConfigureAwait(false);
}
/// <summary>
/// Handles 2FA confirmations of given bots, requires ASF 2FA module to be active on them.
/// </summary>
[HttpPost("{botNames:required}/TwoFactorAuthentication/Confirmations")]
[ProducesResponseType(typeof(GenericResponse<IReadOnlyDictionary<string, GenericResponse>>), (int) HttpStatusCode.OK)]
[ProducesResponseType(typeof(GenericResponse), (int) HttpStatusCode.BadRequest)]
public async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationConfirmationsPost(string botNames, [FromBody] TwoFactorAuthenticationConfirmationsRequest request) {
if (string.IsNullOrEmpty(botNames) || (request == null)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, GenericResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames) + " || " + nameof(request))));
}
if (request.AcceptedType.HasValue && ((request.AcceptedType.Value == MobileAuthenticator.Confirmation.EType.Unknown) || !Enum.IsDefined(typeof(MobileAuthenticator.Confirmation.EType), request.AcceptedType.Value))) {
return BadRequest(new GenericResponse(false, string.Format(Strings.ErrorIsInvalid, nameof(request.AcceptedType))));
}
HashSet<Bot> bots = Bot.GetBots(botNames);
if ((bots == null) || (bots.Count == 0)) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, GenericResponse>>(false, string.Format(Strings.BotNotFound, botNames)));
}
IList<(bool Success, string Message)> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.HandleTwoFactorAuthenticationConfirmations(request.Accept, request.AcceptedType, request.AcceptedCreatorIDs?.Count > 0 ? request.AcceptedCreatorIDs : null, request.WaitIfNeeded))).ConfigureAwait(false);
Dictionary<string, GenericResponse> result = new Dictionary<string, GenericResponse>(bots.Count, Bot.BotsComparer);
foreach (Bot bot in bots) {
(bool success, string message) = results[result.Count];
result[bot.BotName] = new GenericResponse(success, message);
}
return Ok(new GenericResponse<IReadOnlyDictionary<string, GenericResponse>>(result));
}
/// <summary>
/// Fetches 2FA tokens of given bots, requires ASF 2FA module to be active on them.
@@ -488,30 +533,5 @@ namespace ArchiSteamFarm.IPC.Controllers.Api {
return Ok(new GenericResponse<IReadOnlyDictionary<string, GenericResponse<string>>>(result));
}
private async Task<ActionResult<GenericResponse>> TwoFactorAuthenticationConfirmationsPost(string botNames, bool accept) {
if (string.IsNullOrEmpty(botNames)) {
ASF.ArchiLogger.LogNullError(nameof(botNames));
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, GenericResponse>>(false, string.Format(Strings.ErrorIsEmpty, nameof(botNames))));
}
HashSet<Bot> bots = Bot.GetBots(botNames);
if ((bots == null) || (bots.Count == 0)) {
return BadRequest(new GenericResponse<IReadOnlyDictionary<string, GenericResponse>>(false, string.Format(Strings.BotNotFound, botNames)));
}
IList<(bool Success, string Message)> results = await Utilities.InParallel(bots.Select(bot => bot.Actions.HandleTwoFactorAuthenticationConfirmations(accept))).ConfigureAwait(false);
Dictionary<string, GenericResponse> result = new Dictionary<string, GenericResponse>(bots.Count, Bot.BotsComparer);
foreach (Bot bot in bots) {
(bool success, string message) = results[result.Count];
result[bot.BotName] = new GenericResponse(success, message);
}
return Ok(new GenericResponse<IReadOnlyDictionary<string, GenericResponse>>(result));
}
}
}

View File

@@ -0,0 +1,90 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using ArchiSteamFarm.Localization;
using Newtonsoft.Json;
namespace ArchiSteamFarm.IPC.Requests {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
public sealed class TwoFactorAuthenticationConfirmationsRequest {
/// <summary>
/// Specifies the target action, whether we should accept the confirmations (true), or decline them (false).
/// </summary>
[JsonProperty(Required = Required.Always)]
public readonly bool Accept;
/// <summary>
/// Specifies the type of confirmations to handle. If not provided, all confirmation types are considered for an action.
/// </summary>
[JsonProperty(Required = Required.DisallowNull)]
public readonly MobileAuthenticator.Confirmation.EType? AcceptedType;
/// <summary>
/// Specifies whether we should wait for the confirmations to arrive, in case they're not available immediately. This option makes sense only if <see cref="AcceptedCreatorIDs" /> is specified as well, and in this case ASF will add a few more tries if needed to ensure that all specified IDs are handled. Useful if confirmations are generated with a delay on Steam network side, which happens fairly often.
/// </summary>
[JsonProperty(Required = Required.DisallowNull)]
public readonly bool WaitIfNeeded;
/// <summary>
/// Specifies IDs of the confirmations that we're supposed to handle. CreatorID of the confirmation is equal to ID of the object that triggered it - e.g. ID of the trade offer, or ID of the market listing. If not provided, or empty array, all confirmation IDs are considered for an action.
/// </summary>
[JsonProperty(Required = Required.DisallowNull)]
public ImmutableHashSet<ulong> AcceptedCreatorIDs { get; private set; }
/// <summary>
/// A helper property which works the same as <see cref="AcceptedCreatorIDs" /> but with values written as strings - for javascript compatibility purposes. Use either this one, or <see cref="AcceptedCreatorIDs" />, not both.
/// </summary>
[JsonProperty(PropertyName = SharedInfo.UlongCompatibilityStringPrefix + nameof(AcceptedCreatorIDs), Required = Required.DisallowNull)]
public ImmutableHashSet<string> SAcceptedCreatorIDs {
set {
if (value == null) {
ASF.ArchiLogger.LogNullError(nameof(value));
return;
}
HashSet<ulong> acceptedCreatorIDs = new HashSet<ulong>(value.Count);
foreach (string creatorIDText in value) {
if (!ulong.TryParse(creatorIDText, out ulong creatorID) || (creatorID == 0)) {
ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, nameof(SAcceptedCreatorIDs)));
return;
}
acceptedCreatorIDs.Add(creatorID);
}
AcceptedCreatorIDs = acceptedCreatorIDs.ToImmutableHashSet();
}
}
[Obsolete]
internal TwoFactorAuthenticationConfirmationsRequest(bool accept) => Accept = accept;
[JsonConstructor]
private TwoFactorAuthenticationConfirmationsRequest() { }
}
}

View File

@@ -170,6 +170,7 @@ namespace ArchiSteamFarm.IPC {
}
);
options.CustomSchemaIds(type => type.GetUnifiedName());
options.EnableAnnotations(true);
options.SchemaFilter<EnumSchemaFilter>();

View File

@@ -691,6 +691,15 @@ namespace ArchiSteamFarm.Localization {
}
}
/// <summary>
/// Wyszukuje zlokalizowany ciąg podobny do ciągu The trade offer {0} is determined to be {1} due to {2}..
/// </summary>
public static string BotTradeOfferResult {
get {
return ResourceManager.GetString("BotTradeOfferResult", resourceCulture);
}
}
/// <summary>
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Unable to login to Steam: {0}/{1}.
/// </summary>

View File

@@ -724,4 +724,8 @@ Process uptime: {1}</value>
<data name="IPCConfigChanged" xml:space="preserve">
<value>IPC config has been changed!</value>
</data>
<data name="BotTradeOfferResult" xml:space="preserve">
<value>The trade offer {0} is determined to be {1} due to {2}.</value>
<comment>{0} will be replaced by trade offer ID (number), {1} will be replaced by internal ASF enum name, {2} will be replaced by technical reason why the trade was determined to be in this state</comment>
</data>
</root>

View File

@@ -180,7 +180,9 @@ namespace ArchiSteamFarm {
try {
inventory = await Bot.ArchiWebHandler.GetInventoryAsync().Where(item => item.Tradable && acceptedMatchableTypes.Contains(item.Type)).ToHashSetAsync().ConfigureAwait(false);
} catch (HttpRequestException) {
} catch (HttpRequestException e) {
Bot.ArchiLogger.LogGenericWarningException(e);
// This is actually inventory failure, so we'll stop sending heartbeats but not record it as valid check
ShouldSendHeartBeats = false;
@@ -282,7 +284,7 @@ namespace ArchiSteamFarm {
// Bot must have at least one accepted matchable type set
if ((Bot.BotConfig.MatchableTypes.Count == 0) || Bot.BotConfig.MatchableTypes.All(type => !AcceptedMatchableTypes.Contains(type))) {
Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.BotConfig.MatchableTypes) + ": " + Bot.BotConfig.MatchableTypes));
Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.WarningFailedWithError, nameof(Bot.BotConfig.MatchableTypes) + ": " + string.Join(", ", Bot.BotConfig.MatchableTypes)));
return false;
}
@@ -364,7 +366,9 @@ namespace ArchiSteamFarm {
try {
ourInventory = await Bot.ArchiWebHandler.GetInventoryAsync().Where(item => acceptedMatchableTypes.Contains(item.Type)).ToHashSetAsync().ConfigureAwait(false);
} catch (HttpRequestException) {
} catch (HttpRequestException e) {
Bot.ArchiLogger.LogGenericWarningException(e);
return (false, false);
} catch (Exception e) {
Bot.ArchiLogger.LogGenericException(e);
@@ -412,11 +416,29 @@ namespace ArchiSteamFarm {
Bot.ArchiLogger.LogGenericTrace(listedUser.SteamID + "...");
byte? holdDuration = await Bot.ArchiWebHandler.GetTradeHoldDurationForUser(listedUser.SteamID, listedUser.TradeToken).ConfigureAwait(false);
if (!holdDuration.HasValue) {
Bot.ArchiLogger.LogGenericTrace(string.Format(Strings.ErrorIsEmpty, nameof(holdDuration)));
continue;
}
if (holdDuration.Value > 0) {
if (holdDuration.Value > ASF.GlobalConfig.MaxTradeHoldDuration) {
Bot.ArchiLogger.LogGenericTrace(holdDuration.Value + " > " + ASF.GlobalConfig.MaxTradeHoldDuration);
continue;
}
}
HashSet<Steam.Asset> theirInventory;
try {
theirInventory = await Bot.ArchiWebHandler.GetInventoryAsync(listedUser.SteamID).Where(item => (!listedUser.MatchEverything || item.Tradable) && wantedSets.Contains((item.RealAppID, item.Type, item.Rarity))).ToHashSetAsync().ConfigureAwait(false);
} catch (HttpRequestException) {
theirInventory = await Bot.ArchiWebHandler.GetInventoryAsync(listedUser.SteamID).Where(item => (!listedUser.MatchEverything || item.Tradable) && wantedSets.Contains((item.RealAppID, item.Type, item.Rarity)) && ((holdDuration.Value == 0) || !(((item.Type == Steam.Asset.EType.FoilTradingCard) || (item.Type == Steam.Asset.EType.TradingCard)) && CardsFarmer.SalesBlacklist.Contains(item.RealAppID)))).ToHashSetAsync().ConfigureAwait(false);
} catch (HttpRequestException e) {
Bot.ArchiLogger.LogGenericWarningException(e);
continue;
} catch (Exception e) {
Bot.ArchiLogger.LogGenericException(e);

View File

@@ -527,11 +527,15 @@ namespace ArchiSteamFarm {
if (tradeOffer.OtherSteamID64 != 0) {
// Always accept trades from SteamMasterID
if (Bot.HasPermission(tradeOffer.OtherSteamID64, BotConfig.EPermission.Master)) {
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Accepted, nameof(tradeOffer.OtherSteamID64) + " " + tradeOffer.OtherSteamID64 + ": " + BotConfig.EPermission.Master));
return ParseTradeResult.EResult.Accepted;
}
// Always deny trades from blacklisted steamIDs
if (Bot.IsBlacklistedFromTrades(tradeOffer.OtherSteamID64)) {
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Blacklisted, nameof(tradeOffer.OtherSteamID64) + " " + tradeOffer.OtherSteamID64));
return ParseTradeResult.EResult.Blacklisted;
}
}
@@ -540,6 +544,8 @@ namespace ArchiSteamFarm {
switch (tradeOffer.ItemsToGive.Count) {
case 0 when tradeOffer.ItemsToReceive.Count == 0:
// If it's steam issue, try again later
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(tradeOffer.ItemsToReceive.Count) + " = 0"));
return ParseTradeResult.EResult.TryAgain;
case 0:
// Otherwise react accordingly, depending on our preference
@@ -548,32 +554,46 @@ namespace ArchiSteamFarm {
// If we accept donations and bot trades, accept it right away
if (acceptDonations && acceptBotTrades) {
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Accepted, nameof(acceptDonations) + " = " + true + " && " + nameof(acceptBotTrades) + " = " + true));
return ParseTradeResult.EResult.Accepted;
}
// If we don't accept donations, neither bot trades, deny it right away
if (!acceptDonations && !acceptBotTrades) {
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(acceptDonations) + " = " + false + " && " + nameof(acceptBotTrades) + " = " + false));
return ParseTradeResult.EResult.Rejected;
}
// Otherwise we either accept donations but not bot trades, or we accept bot trades but not donations
bool isBotTrade = (tradeOffer.OtherSteamID64 != 0) && Bot.Bots.Values.Any(bot => bot.SteamID == tradeOffer.OtherSteamID64);
return (acceptDonations && !isBotTrade) || (acceptBotTrades && isBotTrade) ? ParseTradeResult.EResult.Accepted : ParseTradeResult.EResult.Rejected;
ParseTradeResult.EResult result = (acceptDonations && !isBotTrade) || (acceptBotTrades && isBotTrade) ? ParseTradeResult.EResult.Accepted : ParseTradeResult.EResult.Rejected;
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, result, nameof(acceptDonations) + " = " + acceptDonations + " && " + nameof(acceptBotTrades) + " = " + acceptBotTrades + " && " + nameof(isBotTrade) + " = " + isBotTrade));
return result;
}
// If we don't have SteamTradeMatcher enabled, this is the end for us
if (!Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.SteamTradeMatcher)) {
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(BotConfig.ETradingPreferences.SteamTradeMatcher) + " = " + false));
return ParseTradeResult.EResult.Rejected;
}
// Decline trade if we're giving more count-wise, this is a very naive pre-check, it'll be strengthened in more detailed fair types exchange next
if (tradeOffer.ItemsToGive.Count > tradeOffer.ItemsToReceive.Count) {
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(tradeOffer.ItemsToGive.Count) + ": " + tradeOffer.ItemsToGive.Count + " > " + tradeOffer.ItemsToReceive.Count));
return ParseTradeResult.EResult.Rejected;
}
// Decline trade if we're requested to handle any not-accepted item type or if it's not fair games/types exchange
if (!tradeOffer.IsValidSteamItemsRequest(Bot.BotConfig.MatchableTypes) || !IsFairExchange(tradeOffer.ItemsToGive, tradeOffer.ItemsToReceive)) {
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(tradeOffer.IsValidSteamItemsRequest) + " || " + nameof(IsFairExchange)));
return ParseTradeResult.EResult.Rejected;
}
@@ -584,6 +604,8 @@ namespace ArchiSteamFarm {
if (!holdDuration.HasValue) {
// If we can't get trade hold duration, try again later
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(holdDuration)));
return ParseTradeResult.EResult.TryAgain;
}
@@ -591,12 +613,16 @@ namespace ArchiSteamFarm {
if (holdDuration.Value > 0) {
// If trade hold duration exceeds our max, or user asks for cards with short lifespan, reject the trade
if ((holdDuration.Value > ASF.GlobalConfig.MaxTradeHoldDuration) || tradeOffer.ItemsToGive.Any(item => ((item.Type == Steam.Asset.EType.FoilTradingCard) || (item.Type == Steam.Asset.EType.TradingCard)) && CardsFarmer.SalesBlacklist.Contains(item.RealAppID))) {
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Rejected, nameof(holdDuration) + " > 0: " + holdDuration.Value));
return ParseTradeResult.EResult.Rejected;
}
}
// If we're matching everything, this is enough for us
if (Bot.BotConfig.TradingPreferences.HasFlag(BotConfig.ETradingPreferences.MatchEverything)) {
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.Accepted, BotConfig.ETradingPreferences.MatchEverything));
return ParseTradeResult.EResult.Accepted;
}
@@ -612,12 +638,16 @@ namespace ArchiSteamFarm {
try {
inventory = await Bot.ArchiWebHandler.GetInventoryAsync(Bot.SteamID).Where(item => wantedSets.Contains((item.RealAppID, item.Type, item.Rarity))).ToHashSetAsync().ConfigureAwait(false);
} catch (HttpRequestException) {
} catch (HttpRequestException e) {
// If we can't check our inventory when not using MatchEverything, this is a temporary failure, try again later
Bot.ArchiLogger.LogGenericWarningException(e);
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory)));
return ParseTradeResult.EResult.TryAgain;
} catch (Exception e) {
// If we can't check our inventory when not using MatchEverything, this is a temporary failure, try again later
Bot.ArchiLogger.LogGenericException(e);
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory)));
return ParseTradeResult.EResult.TryAgain;
}
@@ -625,6 +655,7 @@ namespace ArchiSteamFarm {
if (inventory.Count == 0) {
// If we can't check our inventory when not using MatchEverything, this is a temporary failure, try again later
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(inventory)));
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, ParseTradeResult.EResult.TryAgain, nameof(inventory)));
return ParseTradeResult.EResult.TryAgain;
}
@@ -632,7 +663,11 @@ namespace ArchiSteamFarm {
bool accept = IsTradeNeutralOrBetter(inventory, tradeOffer.ItemsToGive.Select(item => item.CreateShallowCopy()).ToHashSet(), tradeOffer.ItemsToReceive.Select(item => item.CreateShallowCopy()).ToHashSet());
// We're now sure whether the trade is neutral+ for us or not
return accept ? ParseTradeResult.EResult.Accepted : ParseTradeResult.EResult.Rejected;
ParseTradeResult.EResult acceptResult = accept ? ParseTradeResult.EResult.Accepted : ParseTradeResult.EResult.Rejected;
Bot.ArchiLogger.LogGenericDebug(string.Format(Strings.BotTradeOfferResult, tradeOffer.TradeOfferID, acceptResult, nameof(IsTradeNeutralOrBetter)));
return acceptResult;
}
public sealed class ParseTradeResult {

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>4.2.3.7</Version>
<Version>4.2.3.9</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -1,42 +0,0 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
$archiCrowdinPath = 'tools\ArchiCrowdin'
$archiCrowdinScriptPath = "$archiCrowdinPath\archi.ps1"
$crowdinConfigFileName = 'crowdin.yml'
$crowdinIdentityFileName = 'crowdin_identity.yml'
$crowdinIdentityPath = "$archiCrowdinPath\$crowdinIdentityFileName"
$archiTargets = @('ASF-ui', 'ASF-WebConfigGenerator')
Push-Location "$PSScriptRoot"
try {
for ($i = 0; ($i -lt 3) -and (!(Test-Path "$crowdinConfigFileName" -PathType Leaf)); $i++) {
Set-Location ..
}
if (!(Test-Path "$crowdinConfigFileName" -PathType Leaf)) {
throw "$crowdinConfigFileName could not be found, aborting."
}
if (!(Test-Path "$archiCrowdinScriptPath" -PathType Leaf)) {
throw "$archiCrowdinScriptPath could not be found, aborting."
}
if (!(Test-Path "$crowdinIdentityPath" -PathType Leaf)) {
throw "$crowdinIdentityPath could not be found, aborting."
}
& "$archiCrowdinScriptPath" -t:$archiTargets -d -c -p # Download and commit all independent submodules that are part of ASF project
& "$archiCrowdinScriptPath" -t:wiki -c -p # Wiki submodule depends on us, we do this one more time before in order to ensure that tree is up-to-date (e.g. branch is master before we start modifying files in the next step)
& "$archiCrowdinScriptPath" -d -p -rs:no # Download translations for the main project, which also includes wiki submodule as of now
& "$archiCrowdinScriptPath" -t:wiki -c -p # Commit the wiki submodule that we updated in the previous step
& "$archiCrowdinScriptPath" -c # Commit the main project and references of all submodules
} finally {
Pop-Location
}
pause

2
wiki

Submodule wiki updated: 6a123c4dda...24dac880e6