mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-25 02:36:48 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4c11cf61f | ||
|
|
06b1b8deeb | ||
|
|
0beb000d4b | ||
|
|
acc871e609 | ||
|
|
e8545176e7 | ||
|
|
532a47bda5 | ||
|
|
ddc2d956ba | ||
|
|
563f57e3aa | ||
|
|
0a973cefd2 | ||
|
|
2563ade159 | ||
|
|
1763a109c4 | ||
|
|
f57b5a2166 | ||
|
|
7210e6b86c | ||
|
|
6b241d7439 | ||
|
|
833995ca61 | ||
|
|
40531e9554 | ||
|
|
7a5a9c8a51 | ||
|
|
daae97b3d4 | ||
|
|
eeeaacb4db | ||
|
|
5324a9127d | ||
|
|
bbd267d4f0 | ||
|
|
53495e34e5 | ||
|
|
7026d8b0d5 | ||
|
|
a3b57ac50e | ||
|
|
4910692576 | ||
|
|
5147835d48 | ||
|
|
3a8d79d4da | ||
|
|
f1684c11a3 | ||
|
|
f38b6ef6dd | ||
|
|
d895f3e4bb | ||
|
|
e8b7d7d908 | ||
|
|
e7195b0e68 | ||
|
|
2983ede83d |
@@ -15,9 +15,6 @@ ArchiSteamFarm/logs
|
||||
# Ignore standard out folders for publishing
|
||||
**/out
|
||||
|
||||
# Ignore crowdin CLI secret (if exists)
|
||||
tools/ArchiCrowdin/crowdin_identity.yml
|
||||
|
||||
# _ ____ _____ ____ _
|
||||
# / \ / ___|| ___| | _ \ ___ ___| | _____ _ __
|
||||
# / _ \ \___ \| |_ _____| | | |/ _ \ / __| |/ / _ \ '__|
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -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
|
||||
|
||||
0
SECURITY.md → .github/SECURITY.md
vendored
0
SECURITY.md → .github/SECURITY.md
vendored
0
SUPPORT.md → .github/SUPPORT.md
vendored
0
SUPPORT.md → .github/SUPPORT.md
vendored
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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
3
.gitmodules
vendored
@@ -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
|
||||
|
||||
Submodule ASF-WebConfigGenerator deleted from 82ebd9af65
2
ASF-ui
2
ASF-ui
Submodule ASF-ui updated: 01e264f173...f6e73f9c90
@@ -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>
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() { }
|
||||
}
|
||||
}
|
||||
@@ -170,6 +170,7 @@ namespace ArchiSteamFarm.IPC {
|
||||
}
|
||||
);
|
||||
|
||||
options.CustomSchemaIds(type => type.GetUnifiedName());
|
||||
options.EnableAnnotations(true);
|
||||
options.SchemaFilter<EnumSchemaFilter>();
|
||||
|
||||
|
||||
9
ArchiSteamFarm/Localization/Strings.Designer.cs
generated
9
ArchiSteamFarm/Localization/Strings.Designer.cs
generated
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>4.2.3.7</Version>
|
||||
<Version>4.2.3.9</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -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
2
wiki
Submodule wiki updated: 6a123c4dda...24dac880e6
Reference in New Issue
Block a user