Compare commits

...

23 Commits

Author SHA1 Message Date
JustArchi
f4187e194e Various bugfixes 2017-07-10 17:07:48 +02:00
JustArchi
32b2ea9fb7 Add double fine adventure to global blacklist 2017-07-10 09:15:48 +02:00
JustArchi
6e87ee3718 Travis: final fixes 2017-07-10 09:02:57 +02:00
JustArchi
a718d1cefe Travis: Unit tests fixes 2017-07-10 08:52:59 +02:00
JustArchi
019f347be4 AppVeyor: Deploy after tests 2017-07-10 08:33:38 +02:00
JustArchi
5b2b00a9f0 Add MatchableTypes
As well as unit testing so I'm sure that part is not screwed up.
2017-07-10 08:20:15 +02:00
JustArchi
e6e1b6f061 Add pre-2.0 HttpClient max connections limit 2017-07-09 09:53:26 +02:00
JustArchi
88a7d2e7e9 Code cleanup 2017-07-09 09:09:46 +02:00
JustArchi
b223fc2d97 Misc 2017-07-08 00:11:58 +02:00
JustArchi
589f4f5746 Improve I/O database code
This should lead to less file corruptions.
2017-07-07 07:33:31 +02:00
JustArchi
c4e7bbbf0c Add missing linux-arm version for update 2017-07-07 07:06:25 +02:00
JustArchi
bbb43600e5 Misc 2017-07-07 07:02:06 +02:00
JustArchi
c602654800 Misc 2017-07-07 06:54:54 +02:00
JustArchi
7a49dc5188 Misc 2017-07-05 08:00:30 +02:00
JustArchi
f1b84e57e4 Bump 2017-07-05 07:59:04 +02:00
JustArchi
3db5830f7b Misc 2017-07-05 07:58:20 +02:00
JustArchi
4acc71485e Misc 2017-07-05 07:57:40 +02:00
JustArchi
7822210cc0 Misc + translations update 2017-07-05 07:49:40 +02:00
JustArchi
c46eee4499 Various auto-update improvements 2017-07-05 07:30:08 +02:00
JustArchi
ad09b1dec9 Fix possible early crash 2017-07-05 07:07:03 +02:00
JustArchi
e640f82712 Bump 2017-07-05 06:33:33 +02:00
JustArchi
ae03a64bf6 Bump 2017-07-05 06:20:15 +02:00
JustArchi
b49bd2537e AppVeyor: Always use fastest compression
The misc savings of 3 MB are not worth extra load on CPU, even during release builds.
2017-07-05 06:18:15 +02:00
62 changed files with 605 additions and 346 deletions

View File

@@ -17,6 +17,8 @@ mono: none
# ASF requires .NET Core 2.0+ # ASF requires .NET Core 2.0+
# TODO: We should target stable 2.0.0 once it's released # TODO: We should target stable 2.0.0 once it's released
# TODO: Update at least to preview2 once https://github.com/travis-ci/travis-ci/issues/8037 is fixed
# TODO: Add --no-restore to dotnet test when we get preview2+
dotnet: 2.0.0-preview1-005977 dotnet: 2.0.0-preview1-005977
env: env:
@@ -28,6 +30,7 @@ script:
- set -e - set -e
- dotnet restore - dotnet restore
- dotnet build -c Release - dotnet build -c Release
- dotnet test -c Release --no-build ArchiSteamFarm.Tests
- dotnet publish -c Release -o out/generic - dotnet publish -c Release -o out/generic
- echo "generic" > "ArchiSteamFarm/out/generic/ArchiSteamFarm.version" - echo "generic" > "ArchiSteamFarm/out/generic/ArchiSteamFarm.version"
- dotnet publish -c Release -r win-x64 -o out/win-x64 - dotnet publish -c Release -r win-x64 -o out/win-x64
@@ -39,16 +42,17 @@ script:
- dotnet publish -c Release -r osx-x64 -o out/osx-x64 - dotnet publish -c Release -r osx-x64 -o out/osx-x64
- echo "osx-x64" > "ArchiSteamFarm/out/osx-x64/ArchiSteamFarm.version" - echo "osx-x64" > "ArchiSteamFarm/out/osx-x64/ArchiSteamFarm.version"
# This is our main build matrix
matrix: matrix:
# We can use fast finish, as we don't need to wait for all builds to mark it as failed/passed # We can use fast finish, as we don't need to wait for allow_failures builds to mark build as success
fast_finish: true fast_finish: true
allow_failures: allow_failures:
# We allow OS X to fail until https://github.com/travis-ci/travis-ci/issues/7757 is fixed # TODO: We allow OS X to fail until https://github.com/travis-ci/travis-ci/issues/7757 is fixed
- os: osx - os: osx
include: include:
# We're building ASF with dotnet on latest versions of Linux and OS X # We're building ASF with dotnet on latest versions of Linux and OS X
# Ref: https://docs.travis-ci.com/user/ci-environment/#Virtualization-environments # Ref: https://docs.travis-ci.com/user/ci-environment/#Virtualization-environments
# Ref: https://docs.travis-ci.com/user/trusty-ci-environment/
# Ref: https://docs.travis-ci.com/user/osx-ci-environment/
- os: linux - os: linux
dist: trusty dist: trusty
sudo: false sudo: false

View File

@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ErrorReport>none</ErrorReport>
<RuntimeIdentifiers>win-x64;linux-x64;linux-arm;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
<DebugSymbols>false</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170628-02" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0-beta" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.0-beta" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,175 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2017 Ł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.Reflection;
using ArchiSteamFarm.JSON;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ArchiSteamFarm.Tests {
[TestClass]
public sealed class Trading {
[TestMethod]
public void TradingMultiGameBadReject() {
Steam.Item item1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item1Game1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
Steam.Item item2Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item1Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 730, Steam.Item.EType.TradingCard);
Steam.Item item2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 730, Steam.Item.EType.TradingCard);
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Game1X9, item1Game2, item2Game2 };
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Game1, item1Game2 };
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Game1, item2Game2 };
Assert.IsFalse(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void TradingMultiGameMultiTypeBadReject() {
Steam.Item item1Type1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item1Type1Game1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
Steam.Item item2Type1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item3Type2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 730, Steam.Item.EType.Emoticon);
Steam.Item item3Type2Game2X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 9, 730, Steam.Item.EType.Emoticon);
Steam.Item item4Type2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 730, Steam.Item.EType.Emoticon);
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Type1Game1X9, item3Type2Game2X9, item4Type2Game2 };
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Type1Game1, item4Type2Game2 };
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Type1Game1, item3Type2Game2 };
Assert.IsFalse(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void TradingMultiGameMultiTypeNeutralAccept() {
Steam.Item item1Type1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item1Type1Game1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
Steam.Item item2Type1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item3Type2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 730, Steam.Item.EType.Emoticon);
Steam.Item item4Type2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 730, Steam.Item.EType.Emoticon);
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Type1Game1X9, item3Type2Game2 };
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Type1Game1, item3Type2Game2 };
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Type1Game1, item4Type2Game2 };
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void TradingMultiGameNeutralAccept() {
Steam.Item item1Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item1Game1X2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 2, 570, Steam.Item.EType.TradingCard);
Steam.Item item2Game1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item1Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 730, Steam.Item.EType.TradingCard);
Steam.Item item2Game2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 730, Steam.Item.EType.TradingCard);
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Game1X2, item1Game2 };
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Game1, item1Game2 };
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Game1, item2Game2 };
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void TradingSingleGameBadReject() {
Steam.Item item1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1, item2 };
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1 };
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2 };
Assert.IsFalse(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void TradingSingleGameGoodAccept() {
Steam.Item item1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item1X2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 2, 570, Steam.Item.EType.TradingCard);
Steam.Item item2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1X2 };
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1 };
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2 };
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void TradingSingleGameMultiTypeBadReject() {
Steam.Item item1Type1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item1Type1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
Steam.Item item2Type1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item3Type2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 570, Steam.Item.EType.Emoticon);
Steam.Item item3Type2X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 9, 570, Steam.Item.EType.Emoticon);
Steam.Item item4Type2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 570, Steam.Item.EType.Emoticon);
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Type1X9, item3Type2X9, item4Type2 };
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Type1, item4Type2 };
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Type1, item3Type2 };
Assert.IsFalse(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void TradingSingleGameMultiTypeNeutralAccept() {
Steam.Item item1Type1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item1Type1X9 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 9, 570, Steam.Item.EType.TradingCard);
Steam.Item item2Type1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item3Type2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 3, 1, 570, Steam.Item.EType.Emoticon);
Steam.Item item4Type2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 4, 1, 570, Steam.Item.EType.Emoticon);
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1Type1X9, item3Type2 };
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1Type1, item3Type2 };
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2Type1, item4Type2 };
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
}
[TestMethod]
public void TradingSingleGameNeutralAccept() {
Steam.Item item1 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 1, 1, 570, Steam.Item.EType.TradingCard);
Steam.Item item2 = new Steam.Item(Steam.Item.SteamAppID, Steam.Item.SteamCommunityContextID, 2, 1, 570, Steam.Item.EType.TradingCard);
HashSet<Steam.Item> inventory = new HashSet<Steam.Item> { item1 };
HashSet<Steam.Item> itemsToGive = new HashSet<Steam.Item> { item1 };
HashSet<Steam.Item> itemsToReceive = new HashSet<Steam.Item> { item2 };
Assert.IsTrue(AcceptsTrade(inventory, itemsToGive, itemsToReceive));
}
private static bool AcceptsTrade(HashSet<Steam.Item> inventory, HashSet<Steam.Item> itemsToGive, HashSet<Steam.Item> itemsToReceive) {
Type trading = typeof(ArchiSteamFarm.Trading);
MethodInfo method = trading.GetMethod("IsTradeNeutralOrBetter", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
return (bool) method.Invoke(null, new object[] { inventory, itemsToGive, itemsToReceive });
}
}
}

View File

@@ -1,10 +1,12 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26608.5 VisualStudioVersion = 15.0.26621.2
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArchiSteamFarm", "ArchiSteamFarm\ArchiSteamFarm.csproj", "{CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArchiSteamFarm", "ArchiSteamFarm\ArchiSteamFarm.csproj", "{CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchiSteamFarm.Tests", "ArchiSteamFarm.Tests\ArchiSteamFarm.Tests.csproj", "{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -15,8 +17,15 @@ Global
{CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}.Debug|Any CPU.Build.0 = Debug|Any CPU {CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}.Release|Any CPU.ActiveCfg = Release|Any CPU {CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}.Release|Any CPU.Build.0 = Release|Any CPU {CF84911C-2C4C-4195-8AF3-ABBB6D3DE9AA}.Release|Any CPU.Build.0 = Release|Any CPU
{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91DC4C4F-3C23-4716-8CB2-BC7EAB65D759}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D7D54143-C857-4B76-A219-0E98C5BC4895}
EndGlobalSection
EndGlobal EndGlobal

View File

@@ -117,7 +117,7 @@
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALLOW_COMMENT_AFTER_LBRACE/@EntryValue">False</s:Boolean> <s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALLOW_COMMENT_AFTER_LBRACE/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">END_OF_LINE</s:String> <s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">END_OF_LINE</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AFTER_CONTROL_TRANSFER_STATEMENTS/@EntryValue">1</s:Int64> <s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AFTER_CONTROL_TRANSFER_STATEMENTS/@EntryValue">1</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_FIELD/@EntryValue">0</s:Int64> <s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_FIELD/@EntryValue">1</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/CASE_BLOCK_BRACES/@EntryValue">END_OF_LINE</s:String> <s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/CASE_BLOCK_BRACES/@EntryValue">END_OF_LINE</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/EMPTY_BLOCK_STYLE/@EntryValue">TOGETHER_SAME_LINE</s:String> <s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/EMPTY_BLOCK_STYLE/@EntryValue">TOGETHER_SAME_LINE</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_STYLE/@EntryValue">Tab</s:String> <s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_STYLE/@EntryValue">Tab</s:String>
@@ -384,4 +384,5 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EXml_002ECodeStyle_002EFormatSettingsUpgrade_002EXmlMoveToCommonFormatterSettingsUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Int64 x:Key="/Default/Environment/UnitTesting/ParallelProcessesCount/@EntryValue">8</s:Int64></wpf:ResourceDictionary>

View File

@@ -46,32 +46,6 @@ namespace ArchiSteamFarm {
private static FileSystemWatcher FileSystemWatcher; private static FileSystemWatcher FileSystemWatcher;
internal static async Task CheckForUpdate(bool updateOverride = false) { internal static async Task CheckForUpdate(bool updateOverride = false) {
string targetDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
// Cleanup from previous update - update directory for old in-use runtime files
string backupDirectory = Path.Combine(targetDirectory, SharedInfo.UpdateDirectory);
if (Directory.Exists(backupDirectory)) {
// It's entirely possible that old process is still running, wait at least a second for eventual cleanup
await Task.Delay(1000).ConfigureAwait(false);
try {
Directory.Delete(backupDirectory, true);
} catch (Exception e) {
ArchiLogger.LogGenericException(e);
return;
}
}
// Cleanup from previous update - old non-runtime in-use files
foreach (string file in Directory.GetFiles(targetDirectory, "*.old", SearchOption.AllDirectories)) {
try {
File.Delete(file);
} catch (Exception e) {
ArchiLogger.LogGenericException(e);
return;
}
}
if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.None) { if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.None) {
return; return;
} }
@@ -102,7 +76,7 @@ namespace ArchiSteamFarm {
} }
version = version.TrimEnd(); version = version.TrimEnd();
if (string.IsNullOrEmpty(version) || !IsVersionValid(version)) { if (string.IsNullOrEmpty(version) || !IsValidVersion(version)) {
ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, SharedInfo.VersionFile)); ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsInvalid, SharedInfo.VersionFile));
return; return;
} }
@@ -120,13 +94,39 @@ namespace ArchiSteamFarm {
ArchiLogger.LogGenericInfo(string.Format(Strings.AutoUpdateCheckInfo, autoUpdatePeriod.ToHumanReadable())); ArchiLogger.LogGenericInfo(string.Format(Strings.AutoUpdateCheckInfo, autoUpdatePeriod.ToHumanReadable()));
} }
ArchiLogger.LogGenericInfo(Strings.UpdateCheckingNewVersion);
string targetDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
// Cleanup from previous update - update directory for old in-use runtime files
string backupDirectory = Path.Combine(targetDirectory, SharedInfo.UpdateDirectory);
if (Directory.Exists(backupDirectory)) {
// It's entirely possible that old process is still running, wait a short moment for eventual cleanup
await Task.Delay(5000).ConfigureAwait(false);
try {
Directory.Delete(backupDirectory, true);
} catch (Exception e) {
ArchiLogger.LogGenericException(e);
return;
}
}
// Cleanup from previous update - old non-runtime in-use files
foreach (string file in Directory.EnumerateFiles(targetDirectory, "*.old", SearchOption.AllDirectories)) {
try {
File.Delete(file);
} catch (Exception e) {
ArchiLogger.LogGenericException(e);
return;
}
}
string releaseURL = SharedInfo.GithubReleaseURL; string releaseURL = SharedInfo.GithubReleaseURL;
if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) { if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
releaseURL += "/latest"; releaseURL += "/latest";
} }
ArchiLogger.LogGenericInfo(Strings.UpdateCheckingNewVersion);
GitHub.ReleaseResponse releaseResponse; GitHub.ReleaseResponse releaseResponse;
if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) { if (Program.GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
@@ -189,7 +189,7 @@ namespace ArchiSteamFarm {
return; return;
} }
ArchiLogger.LogGenericInfo(Strings.UpdateDownloadingNewVersion); ArchiLogger.LogGenericInfo(string.Format(Strings.UpdateDownloadingNewVersion, newVersion, binaryAsset.Size / 1024 / 1024));
byte[] result = await Program.WebBrowser.UrlGetToBytesRetry(binaryAsset.DownloadURL).ConfigureAwait(false); byte[] result = await Program.WebBrowser.UrlGetToBytesRetry(binaryAsset.DownloadURL).ConfigureAwait(false);
if (result == null) { if (result == null) {
@@ -217,14 +217,7 @@ namespace ArchiSteamFarm {
// Before attempting to connect, initialize our list of CMs // Before attempting to connect, initialize our list of CMs
await Bot.InitializeCMs(Program.GlobalDatabase.CellID, Program.GlobalDatabase.ServerListProvider).ConfigureAwait(false); await Bot.InitializeCMs(Program.GlobalDatabase.CellID, Program.GlobalDatabase.ServerListProvider).ConfigureAwait(false);
foreach (string botName in Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension).Where(botName => !string.IsNullOrEmpty(botName) && (botName[0] != '.'))) { foreach (string botName in Directory.EnumerateFiles(SharedInfo.ConfigDirectory, "*.json").Select(Path.GetFileNameWithoutExtension).Where(botName => !string.IsNullOrEmpty(botName) && IsValidBotName(botName)).OrderBy(botName => botName)) {
switch (botName) {
case SharedInfo.ASF:
case "example":
case "minimal":
continue;
}
Bot.RegisterBot(botName); Bot.RegisterBot(botName);
} }
@@ -270,7 +263,27 @@ namespace ArchiSteamFarm {
Bot.RegisterBot(botName); Bot.RegisterBot(botName);
} }
private static bool IsVersionValid(string version) { private static bool IsValidBotName(string botName) {
if (string.IsNullOrEmpty(botName)) {
ArchiLogger.LogNullError(nameof(botName));
return false;
}
if (botName[0] == '.') {
return false;
}
switch (botName) {
case SharedInfo.ASF:
case "example":
case "minimal":
return false;
default:
return true;
}
}
private static bool IsValidVersion(string version) {
if (string.IsNullOrEmpty(version)) { if (string.IsNullOrEmpty(version)) {
ArchiLogger.LogNullError(nameof(version)); ArchiLogger.LogNullError(nameof(version));
return false; return false;
@@ -278,9 +291,10 @@ namespace ArchiSteamFarm {
switch (version) { switch (version) {
case "generic": case "generic":
case "win-x64": case "linux-arm":
case "linux-x64": case "linux-x64":
case "osx-x64": case "osx-x64":
case "win-x64":
return true; return true;
default: default:
return false; return false;
@@ -438,7 +452,7 @@ namespace ArchiSteamFarm {
// Move top-level runtime in-use files to other directory // Move top-level runtime in-use files to other directory
// We must do it in order to not crash at later stage - all libraries/executables must keep original names // We must do it in order to not crash at later stage - all libraries/executables must keep original names
foreach (string file in Directory.GetFiles(targetDirectory)) { foreach (string file in Directory.EnumerateFiles(targetDirectory)) {
string target = Path.Combine(backupDirectory, Path.GetFileName(file)); string target = Path.Combine(backupDirectory, Path.GetFileName(file));
File.Move(file, target); File.Move(file, target);
} }
@@ -446,7 +460,7 @@ namespace ArchiSteamFarm {
// In generic ASF variant there can also be "runtimes" directory in need of same approach // In generic ASF variant there can also be "runtimes" directory in need of same approach
string runtimesDirectory = Path.Combine(targetDirectory, "runtimes"); string runtimesDirectory = Path.Combine(targetDirectory, "runtimes");
if (Directory.Exists(runtimesDirectory)) { if (Directory.Exists(runtimesDirectory)) {
foreach (string file in Directory.GetFiles(runtimesDirectory, "*", SearchOption.AllDirectories)) { foreach (string file in Directory.EnumerateFiles(runtimesDirectory, "*", SearchOption.AllDirectories)) {
string directory = Path.Combine(backupDirectory, Path.GetDirectoryName(Path.GetRelativePath(targetDirectory, file))); string directory = Path.Combine(backupDirectory, Path.GetDirectoryName(Path.GetRelativePath(targetDirectory, file)));
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);

View File

@@ -336,6 +336,7 @@ namespace ArchiSteamFarm {
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
Unknown = 0, Unknown = 0,
Trading = 1, Trading = 1,
// Only custom below, different than ones available as user_notification_type // Only custom below, different than ones available as user_notification_type
Items = 254 Items = 254
} }

View File

@@ -3,8 +3,8 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework> <TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyVersion>3.0.0.0</AssemblyVersion> <AssemblyVersion>3.0.0.3</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion> <FileVersion>3.0.0.3</FileVersion>
<LangVersion>7</LangVersion> <LangVersion>7</LangVersion>
<ErrorReport>none</ErrorReport> <ErrorReport>none</ErrorReport>
<ApplicationIcon>ASF.ico</ApplicationIcon> <ApplicationIcon>ASF.ico</ApplicationIcon>
@@ -19,7 +19,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.5.0" /> <PackageReference Include="HtmlAgilityPack" Version="1.5.2-beta1" />
<PackageReference Include="Humanizer" Version="2.2.0" /> <PackageReference Include="Humanizer" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Nito.AsyncEx.Coordination" Version="5.0.0-pre-02" /> <PackageReference Include="Nito.AsyncEx.Coordination" Version="5.0.0-pre-02" />

View File

@@ -50,10 +50,12 @@ namespace ArchiSteamFarm {
// We must use HTTPS for SteamCommunity, as http would make certain POST requests failing (trades) // We must use HTTPS for SteamCommunity, as http would make certain POST requests failing (trades)
private const string SteamCommunityHost = "steamcommunity.com"; private const string SteamCommunityHost = "steamcommunity.com";
private const string SteamCommunityURL = "https://" + SteamCommunityHost; private const string SteamCommunityURL = "https://" + SteamCommunityHost;
// We could (and should) use HTTPS for SteamStore, but that would make certain POST requests failing // We could (and should) use HTTPS for SteamStore, but that would make certain POST requests failing
private const string SteamStoreHost = "store.steampowered.com"; private const string SteamStoreHost = "store.steampowered.com";
private const string SteamStoreURL = "http://" + SteamStoreHost; private const string SteamStoreURL = "http://" + SteamStoreHost;
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1); private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1);
@@ -177,7 +179,7 @@ namespace ArchiSteamFarm {
} }
KeyValue response = null; KeyValue response = null;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) { for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) {
await Task.Run(() => { await Task.Run(() => {
using (dynamic iEconService = WebAPI.GetInterface(IEconService, steamApiKey)) { using (dynamic iEconService = WebAPI.GetInterface(IEconService, steamApiKey)) {
iEconService.Timeout = Timeout; iEconService.Timeout = Timeout;
@@ -196,7 +198,7 @@ namespace ArchiSteamFarm {
} }
if (response == null) { if (response == null) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxRetries)); Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries));
} }
} }
@@ -228,7 +230,7 @@ namespace ArchiSteamFarm {
} }
KeyValue response = null; KeyValue response = null;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) { for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) {
await Task.Run(() => { await Task.Run(() => {
using (dynamic iEconService = WebAPI.GetInterface(IEconService, steamApiKey)) { using (dynamic iEconService = WebAPI.GetInterface(IEconService, steamApiKey)) {
iEconService.Timeout = Timeout; iEconService.Timeout = Timeout;
@@ -249,7 +251,7 @@ namespace ArchiSteamFarm {
} }
if (response == null) { if (response == null) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxRetries)); Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries));
return null; return null;
} }
@@ -625,7 +627,7 @@ namespace ArchiSteamFarm {
} }
KeyValue response = null; KeyValue response = null;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) { for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) {
await Task.Run(() => { await Task.Run(() => {
using (dynamic iPlayerService = WebAPI.GetInterface(IPlayerService, steamApiKey)) { using (dynamic iPlayerService = WebAPI.GetInterface(IPlayerService, steamApiKey)) {
iPlayerService.Timeout = Timeout; iPlayerService.Timeout = Timeout;
@@ -644,7 +646,7 @@ namespace ArchiSteamFarm {
} }
if (response == null) { if (response == null) {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxRetries)); Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries));
return null; return null;
} }
@@ -664,7 +666,7 @@ namespace ArchiSteamFarm {
internal async Task<uint> GetServerTime() { internal async Task<uint> GetServerTime() {
KeyValue response = null; KeyValue response = null;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) { for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) {
await Task.Run(() => { await Task.Run(() => {
using (dynamic iTwoFactorService = WebAPI.GetInterface(ITwoFactorService)) { using (dynamic iTwoFactorService = WebAPI.GetInterface(ITwoFactorService)) {
iTwoFactorService.Timeout = Timeout; iTwoFactorService.Timeout = Timeout;
@@ -685,7 +687,7 @@ namespace ArchiSteamFarm {
return response["server_time"].AsUnsignedInteger(); return response["server_time"].AsUnsignedInteger();
} }
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxRetries)); Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries));
return 0; return 0;
} }

View File

@@ -0,0 +1,27 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2017 Ł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.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("ArchiSteamFarm.Tests")]

View File

@@ -1276,7 +1276,7 @@ namespace ArchiSteamFarm {
// This is pretty rare, but SK2 SteamFriends handler and this handler could execute at the same time // This is pretty rare, but SK2 SteamFriends handler and this handler could execute at the same time
// So we wait for nickname to be registered (with timeout of 5 tries/seconds) // So we wait for nickname to be registered (with timeout of 5 tries/seconds)
string nickname = SteamFriends.GetPersonaName(); string nickname = SteamFriends.GetPersonaName();
for (byte i = 0; (i < WebBrowser.MaxRetries) && (string.IsNullOrEmpty(nickname) || nickname.Equals("[unassigned]")); i++) { for (byte i = 0; (i < WebBrowser.MaxTries) && (string.IsNullOrEmpty(nickname) || nickname.Equals("[unassigned]")); i++) {
await Task.Delay(1000).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false);
nickname = SteamFriends.GetPersonaName(); nickname = SteamFriends.GetPersonaName();
} }
@@ -2699,6 +2699,7 @@ namespace ArchiSteamFarm {
response.Append(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, ownedGame.Key, ownedGame.Value))); response.Append(FormatBotResponse(string.Format(Strings.BotOwnedAlreadyWithName, ownedGame.Key, ownedGame.Value)));
} }
} else { } else {
query = query.Replace('_', ' ');
string[] games = query.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] games = query.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string game in games) { foreach (string game in games) {

View File

@@ -92,6 +92,12 @@ namespace ArchiSteamFarm {
Steam.Item.EType.TradingCard Steam.Item.EType.TradingCard
}; };
[JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace, Required = Required.DisallowNull)]
internal readonly HashSet<Steam.Item.EType> MatchableTypes = new HashSet<Steam.Item.EType> {
Steam.Item.EType.FoilTradingCard,
Steam.Item.EType.TradingCard
};
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText; internal readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;

View File

@@ -26,7 +26,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Threading;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
@@ -152,21 +151,19 @@ namespace ArchiSteamFarm {
return; return;
} }
// This call verifies if JSON is alright
// We don't wrap it in try catch as it should always be the case
// And if it's not, we want to know about it (in a crash) and correct it in future version
JsonConvert.DeserializeObject<BotDatabase>(json);
lock (FileLock) { lock (FileLock) {
for (byte i = 0; i < 5; i++) { string newFilePath = FilePath + ".new";
try {
File.WriteAllText(FilePath, json);
break;
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
}
Thread.Sleep(1000); try {
File.WriteAllText(newFilePath, json);
if (File.Exists(FilePath)) {
File.Replace(newFilePath, FilePath, null);
} else {
File.Move(newFilePath, FilePath);
}
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
} }
} }
} }

View File

@@ -41,7 +41,7 @@ namespace ArchiSteamFarm {
internal const string UlongStringPrefix = "s_"; internal const string UlongStringPrefix = "s_";
// This is hardcoded blacklist which should not be possible to change // This is hardcoded blacklist which should not be possible to change
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280, 480730, 566020, 639900 }; internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 402590, 425280, 480730, 566020, 639900 };
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal readonly bool AutoRestart = true; internal readonly bool AutoRestart = true;

View File

@@ -25,7 +25,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
@@ -114,21 +113,19 @@ namespace ArchiSteamFarm {
return; return;
} }
// This call verifies if JSON is alright
// We don't wrap it in try catch as it should always be the case
// And if it's not, we want to know about it (in a crash) and correct it in future version
JsonConvert.DeserializeObject<GlobalDatabase>(json, CustomSerializerSettings);
lock (FileLock) { lock (FileLock) {
for (byte i = 0; i < 5; i++) { string newFilePath = FilePath + ".new";
try {
File.WriteAllText(FilePath, json);
break;
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
}
Thread.Sleep(1000); try {
File.WriteAllText(newFilePath, json);
if (File.Exists(FilePath)) {
File.Replace(newFilePath, FilePath, null);
} else {
File.Move(newFilePath, FilePath);
}
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
} }
} }
} }

View File

@@ -50,11 +50,11 @@ namespace ArchiSteamFarm {
} }
internal static void Start() { internal static void Start() {
if (KeepRunning) { if (KeepRunning || (HttpListener.Prefixes.Count == 0)) {
return; return;
} }
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCStarting, HttpListener.Prefixes.FirstOrDefault())); ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCStarting, HttpListener.Prefixes.First()));
try { try {
HttpListener.Start(); HttpListener.Start();
@@ -84,56 +84,60 @@ namespace ArchiSteamFarm {
return; return;
} }
if (Program.GlobalConfig.SteamOwnerID == 0) { try {
ASF.ArchiLogger.LogGenericInfo(Strings.ErrorIPCAccessDenied); if (Program.GlobalConfig.SteamOwnerID == 0) {
await context.Response.WriteAsync(HttpStatusCode.Forbidden, Strings.ErrorIPCAccessDenied).ConfigureAwait(false); ASF.ArchiLogger.LogGenericWarning(Strings.ErrorIPCAccessDenied);
return; await context.Response.WriteAsync(HttpStatusCode.Forbidden, Strings.ErrorIPCAccessDenied).ConfigureAwait(false);
} return;
}
if (!context.Request.RawUrl.StartsWith("/" + nameof(IPC), StringComparison.Ordinal)) { if (!context.Request.RawUrl.StartsWith("/" + nameof(IPC), StringComparison.Ordinal)) {
await context.Response.WriteAsync(HttpStatusCode.BadRequest, nameof(HttpStatusCode.BadRequest)).ConfigureAwait(false);
return;
}
switch (context.Request.HttpMethod) {
case WebRequestMethods.Http.Get:
for (int i = 0; i < context.Request.QueryString.Count; i++) {
string key = context.Request.QueryString.GetKey(i);
switch (key) {
case "command":
string command = context.Request.QueryString.Get(i);
if (string.IsNullOrEmpty(command)) {
break;
}
Bot bot = Bot.Bots.Values.FirstOrDefault();
if (bot == null) {
await context.Response.WriteAsync(HttpStatusCode.NotAcceptable, Strings.ErrorNoBotsDefined).ConfigureAwait(false);
return;
}
if (command[0] != '!') {
command = "!" + command;
}
string response = await bot.Response(Program.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false);
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCAnswered, command, response));
await context.Response.WriteAsync(HttpStatusCode.OK, response).ConfigureAwait(false);
break;
}
}
break;
default:
await context.Response.WriteAsync(HttpStatusCode.BadRequest, nameof(HttpStatusCode.BadRequest)).ConfigureAwait(false); await context.Response.WriteAsync(HttpStatusCode.BadRequest, nameof(HttpStatusCode.BadRequest)).ConfigureAwait(false);
return; return;
} }
if (context.Response.ContentLength64 == 0) { switch (context.Request.HttpMethod) {
await context.Response.WriteAsync(HttpStatusCode.MethodNotAllowed, nameof(HttpStatusCode.MethodNotAllowed)).ConfigureAwait(false); case WebRequestMethods.Http.Get:
for (int i = 0; i < context.Request.QueryString.Count; i++) {
string key = context.Request.QueryString.GetKey(i);
switch (key) {
case "command":
string command = context.Request.QueryString.Get(i);
if (string.IsNullOrEmpty(command)) {
break;
}
Bot bot = Bot.Bots.Values.FirstOrDefault();
if (bot == null) {
await context.Response.WriteAsync(HttpStatusCode.NotAcceptable, Strings.ErrorNoBotsDefined).ConfigureAwait(false);
return;
}
if (command[0] != '!') {
command = "!" + command;
}
string response = await bot.Response(Program.GlobalConfig.SteamOwnerID, command).ConfigureAwait(false);
ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCAnswered, command, response));
await context.Response.WriteAsync(HttpStatusCode.OK, response).ConfigureAwait(false);
break;
}
}
break;
default:
await context.Response.WriteAsync(HttpStatusCode.BadRequest, nameof(HttpStatusCode.BadRequest)).ConfigureAwait(false);
return;
}
if (context.Response.ContentLength64 == 0) {
await context.Response.WriteAsync(HttpStatusCode.MethodNotAllowed, nameof(HttpStatusCode.MethodNotAllowed)).ConfigureAwait(false);
}
} finally {
context.Response.Close();
} }
} }
@@ -148,8 +152,7 @@ namespace ArchiSteamFarm {
continue; continue;
} }
await HandleRequest(context).ConfigureAwait(false); Utilities.StartBackgroundFunction(() => HandleRequest(context));
context.Response.Close();
} }
} }
@@ -164,8 +167,14 @@ namespace ArchiSteamFarm {
response.StatusCode = (ushort) statusCode; response.StatusCode = (ushort) statusCode;
} }
byte[] buffer = Encoding.UTF8.GetBytes(message); Encoding encoding = Encoding.UTF8;
response.ContentEncoding = encoding;
string html = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><p>" + message + "</p></body></html>";
byte[] buffer = encoding.GetBytes(html);
response.ContentLength64 = buffer.Length; response.ContentLength64 = buffer.Length;
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); await response.OutputStream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
} catch (Exception e) { } catch (Exception e) {
response.StatusCode = (ushort) HttpStatusCode.InternalServerError; response.StatusCode = (ushort) HttpStatusCode.InternalServerError;

View File

@@ -55,6 +55,11 @@ namespace ArchiSteamFarm.JSON {
internal readonly string Name; internal readonly string Name;
#pragma warning restore 649 #pragma warning restore 649
#pragma warning disable 649
[JsonProperty(PropertyName = "size", Required = Required.Always)]
internal readonly uint Size;
#pragma warning restore 649
// Deserialized from JSON // Deserialized from JSON
private Asset() { } private Asset() { }
} }

View File

@@ -486,7 +486,15 @@ namespace ArchiSteamFarm.JSON {
return true; return true;
} }
internal bool IsSteamCardsRequest() => ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamCommunityContextID) && (item.Type == Item.EType.TradingCard)); internal bool IsValidSteamItemsRequest(HashSet<Item.EType> acceptedTypes) {
if ((acceptedTypes == null) || (acceptedTypes.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(acceptedTypes));
return false;
}
bool result = ItemsToGive.All(item => (item.AppID == Item.SteamAppID) && (item.ContextID == Item.SteamCommunityContextID) && acceptedTypes.Contains(item.Type));
return result;
}
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
internal enum ETradeOfferState : byte { internal enum ETradeOfferState : byte {

View File

@@ -1306,7 +1306,7 @@ namespace ArchiSteamFarm.Localization {
} }
/// <summary> /// <summary>
/// Wyszukuje zlokalizowany ciąg podobny do ciągu Downloading new version... While waiting, consider donating if you appreciate the work being done! :). /// Wyszukuje zlokalizowany ciąg podobny do ciągu Downloading new version: {0} ({1} MB)... While waiting, consider donating if you appreciate the work being done! :).
/// </summary> /// </summary>
internal static string UpdateDownloadingNewVersion { internal static string UpdateDownloadingNewVersion {
get { get {

View File

@@ -191,9 +191,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>جاري التحقيق من وجود إصدار جديد...</value> <value>جاري التحقيق من وجود إصدار جديد...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>جاري تحميل الإصدار الجديد... بينما تنتظر، تبرع إذا اعجبك عملنا! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>تم الانتهاء من عملية التحديث!</value> <value>تم الانتهاء من عملية التحديث!</value>
</data> </data>

View File

@@ -235,9 +235,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Проверяване за нова версия...</value> <value>Проверяване за нова версия...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>В момента се сваля новата версия... Докато чакате, помислете за дарение, ако оценявате свършената от нас работа! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Обновлението приключи!</value> <value>Обновлението приключи!</value>
</data> </data>

View File

@@ -241,9 +241,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Kontrola nové verze...</value> <value>Kontrola nové verze...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Probíhá stahování nové verze... Během čekání zvažte podpotu tohoto projektu! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Úspěšně aktualizováno.</value> <value>Úspěšně aktualizováno.</value>
</data> </data>

View File

@@ -240,9 +240,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Undersøger for ny version...</value> <value>Undersøger for ny version...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Downloader ny version... Mens du venter, overvej at donere hvis du sætter pris på det arbejde der bliver gjort! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Opdateringsprocessen er færdig!</value> <value>Opdateringsprocessen er færdig!</value>
</data> </data>

View File

@@ -240,9 +240,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Prüfe auf neue Version...</value> <value>Prüfe auf neue Version...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Lade neue Version herunter... Während du wartest, denk darüber nach zu spenden, wenn du die geleistete Arbeit zu schätzen weißt! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Aktualisierung abgeschlossen!</value> <value>Aktualisierung abgeschlossen!</value>
</data> </data>

View File

@@ -241,9 +241,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Prüfe auf neue Version...</value> <value>Prüfe auf neue Version...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Lade neue Version herunter... Während du wartest, denk darüber nach zu spenden, wenn du die geleistete Arbeit zu schätzen weißt! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Aktualisierung abgeschlossen!</value> <value>Aktualisierung abgeschlossen!</value>
</data> </data>

View File

@@ -235,9 +235,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Έλεγχος για νέα έκδοση...</value> <value>Έλεγχος για νέα έκδοση...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Λήψη νέας έκδοσης... Όσο περιμένετε, σκεφτείτε να κάνετε μια δωρεά εάν εκτιμάτε τη δουλειά που γίνεται! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Η διαδικασία ενημέρωσης ολοκληρώθηκε!</value> <value>Η διαδικασία ενημέρωσης ολοκληρώθηκε!</value>
</data> </data>

View File

@@ -240,9 +240,7 @@ Trazo de pila:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Comprobando si existe una nueva versión...</value> <value>Comprobando si existe una nueva versión...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Descargando la nueva versión... ¡Mientras espera, considere donar si aprecia el trabajo que se está realizando! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>¡Proceso de actualización finalizado!</value> <value>¡Proceso de actualización finalizado!</value>
</data> </data>

View File

@@ -201,9 +201,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Tarkistetaan päivityksiä...</value> <value>Tarkistetaan päivityksiä...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Ladataan päivityksiä... Odottaessa, harkitse lahjoittamista kiittääksesi työhön käytetystä ajasta! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Päivitys valmis!</value> <value>Päivitys valmis!</value>
</data> </data>

View File

@@ -241,9 +241,7 @@ StackTrace :
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Recherche d'une nouvelle version...</value> <value>Recherche d'une nouvelle version...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Téléchargement de la nouvelle version en cours... En attendant, vous pouvez faire un don si vous appréciez le travail effectué ! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Mise à jour terminée !</value> <value>Mise à jour terminée !</value>
</data> </data>

View File

@@ -241,9 +241,7 @@ StackTrace :
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Recherche d'une nouvelle version...</value> <value>Recherche d'une nouvelle version...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Téléchargement de la nouvelle version en cours... En attendant, vous pouvez faire un don si vous appréciez le travail effectué ! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Mise à jour terminée !</value> <value>Mise à jour terminée !</value>
</data> </data>

View File

@@ -228,9 +228,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>מחפש גירסה חדשה...</value> <value>מחפש גירסה חדשה...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>מוריד גירסה חדשה... בזמן ההמתנה, אנא שקלו לתרום אם אתם מעריכים את העבודה שאנו עושים :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>תהליך העדכון הסתיים!</value> <value>תהליך העדכון הסתיים!</value>
</data> </data>

View File

@@ -239,9 +239,7 @@ StackTrace: {2}</value>
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Új verzió keresése...</value> <value>Új verzió keresése...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Új verzió letöltése folyamatban... Mialatt várakozol, fontold meg, hogy pénzzel támogatod a munkámat, ha tetszik, amit csinálok! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Frissítés kész!</value> <value>Frissítés kész!</value>
</data> </data>

View File

@@ -239,9 +239,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Sedang mengecek versi terbaru...</value> <value>Sedang mengecek versi terbaru...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Mengunduh versi baru... Sambil menunggu, pertimbangkan untuk mengapresiasi seluruh kerja keras dengan mendonasi! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Proses update selesai!</value> <value>Proses update selesai!</value>
</data> </data>

View File

@@ -239,9 +239,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Verifica della nuova versione...</value> <value>Verifica della nuova versione...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Scaricando la nuova versione... Durante l'attesa, considera una donazione se apprezzi il lavoro svolto! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Aggiornamento completato!</value> <value>Aggiornamento completato!</value>
</data> </data>

View File

@@ -238,9 +238,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>新しいバージョンをチェックしています...</value> <value>新しいバージョンをチェックしています...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>新しいバージョンをダウンロードしています... 待っている間、作者への寄付をご検討ください! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>アップデート完了!</value> <value>アップデート完了!</value>
</data> </data>

View File

@@ -241,9 +241,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>새로운 버전 확인 중...</value> <value>새로운 버전 확인 중...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>새로운 버전을 다운로드 중... 기다리는 동안, 완성된 작업이 고맙다면 기부를 고려해 보세요! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>업데이트 작업 완료!</value> <value>업데이트 작업 완료!</value>
</data> </data>

View File

@@ -238,9 +238,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Ieškoma naujos versijos...</value> <value>Ieškoma naujos versijos...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Atsiunčiama nauja versija... Kol laukiate, apsvarstykite, gal norite paaukoti jei vertinate daromą darbą! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Naujinimo procesas baigtas!</value> <value>Naujinimo procesas baigtas!</value>
</data> </data>

View File

@@ -240,9 +240,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Controleren op nieuwe versie...</value> <value>Controleren op nieuwe versie...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Nieuwe versie wordt gedownload... Als je het gedane werk waardeert, overweeg dan tijdens het wachten om te doneren! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Update is afgerond!</value> <value>Update is afgerond!</value>
</data> </data>

View File

@@ -240,9 +240,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Controleren op nieuwe versie...</value> <value>Controleren op nieuwe versie...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Nieuwe versie wordt gedownload... Als je het gedane werk waardeert, overweeg dan tijdens het wachten om te doneren! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Update is afgerond!</value> <value>Update is afgerond!</value>
</data> </data>

View File

@@ -247,7 +247,8 @@ StackTrace:
<value>Wyszukiwanie nowej wersji...</value> <value>Wyszukiwanie nowej wersji...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve"> <data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Pobieranie nowej wersji... Podczas czekania rozważ dotację, jeśli doceniasz naszą pracę! :)</value> <value>Pobieranie nowej wersji: {0} ({1} MB)... Podczas czekania rozważ dotację, jeśli doceniasz naszą pracę! :)</value>
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
</data> </data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Aktualizacja została zakończona!</value> <value>Aktualizacja została zakończona!</value>

View File

@@ -241,9 +241,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Verificando se há atualizações...</value> <value>Verificando se há atualizações...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Baixando nova versão... Enquanto aguarda, considere fazer uma doação caso aprecie o trabalho que está sendo feito! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Processo de atualização finalizado!</value> <value>Processo de atualização finalizado!</value>
</data> </data>

View File

@@ -238,9 +238,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>A verificar por novas versões...</value> <value>A verificar por novas versões...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>A obter a nova versão... Enquanto espera, considere doar algo se aprecias o trabalho feito! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Processo de actualização terminada!</value> <value>Processo de actualização terminada!</value>
</data> </data>

View File

@@ -247,7 +247,8 @@ StackTrace:
<value>Checking for new version...</value> <value>Checking for new version...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve"> <data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Downloading new version... While waiting, consider donating if you appreciate the work being done! :)</value> <value>Downloading new version: {0} ({1} MB)... While waiting, consider donating if you appreciate the work being done! :)</value>
<comment>{0} will be replaced by version string, {1} will be replaced by update size (in megabytes)</comment>
</data> </data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Update process finished!</value> <value>Update process finished!</value>

View File

@@ -241,9 +241,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Se caută versiune nouă...</value> <value>Se caută versiune nouă...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Se descarcă o versiune nouă... Cât timp aștepți, dacă consideri, poți dona dacă apreciezi toată munca depusă! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Proces de actualizare finalizat!</value> <value>Proces de actualizare finalizat!</value>
</data> </data>

View File

@@ -241,9 +241,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Проверка новой версии...</value> <value>Проверка новой версии...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Загрузка новой версии... Во время ожидания подумайте о пожертвовании в пользу разработчиков, если вы цените проделанную работу! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Процесс обновления завершён!</value> <value>Процесс обновления завершён!</value>
</data> </data>

View File

@@ -241,9 +241,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Kontrola najnovšej verzie...</value> <value>Kontrola najnovšej verzie...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Sťahovanie najnovšej verzie... Zváž podporu tohto projektu počas čakania! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Aktualizácia úspešne dokončená!</value> <value>Aktualizácia úspešne dokončená!</value>
</data> </data>

View File

@@ -225,9 +225,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Traženje nove verzije...</value> <value>Traženje nove verzije...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Skidanje nove verzije... Dok čekate, razmislite o podržavanju ovoh programa putem donacija ako cenite uložen rad! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Proces ažuriranja je završen!</value> <value>Proces ažuriranja je završen!</value>
</data> </data>

View File

@@ -241,10 +241,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Söker efter senaste versionen...</value> <value>Söker efter senaste versionen...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Laddar ner senaste versionen... Medan du väntar, fundera på att donera om du uppskattar arbetet som görs!
:)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Uppdateringsprocessen klar!</value> <value>Uppdateringsprocessen klar!</value>
</data> </data>

View File

@@ -241,9 +241,7 @@ Yığın izleme:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Yeni sürüm kontrol ediliyor...</value> <value>Yeni sürüm kontrol ediliyor...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Yeni sürüm indiriliyor... Beklerken, yaptığımız çalışmayı takdir ediyorsanız bağış yapmayı düşünün! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Güncelleme işlemi tamamlandı!</value> <value>Güncelleme işlemi tamamlandı!</value>
</data> </data>

View File

@@ -241,9 +241,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Перевірка наявності нової версії...</value> <value>Перевірка наявності нової версії...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Завантаження нової версії... Під час очікування подумайте про пожертвування на користь розробників, якщо ви цінуєте виконану роботу! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Процесс оновлення закінчено!</value> <value>Процесс оновлення закінчено!</value>
</data> </data>

View File

@@ -236,9 +236,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Kiểm tra phiên bản mới...</value> <value>Kiểm tra phiên bản mới...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>Tải xuống phiên bản mới... Trong khi chờ đợi, hãy xem xét quyên góp nếu bạn đánh giá cao công việc được thực hiện! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>Quá trình cập nhật hoàn tất!</value> <value>Quá trình cập nhật hoàn tất!</value>
</data> </data>

View File

@@ -238,9 +238,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>正在检查新版本...</value> <value>正在检查新版本...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>更新中……如果你喜欢这个项目,可以考虑趁现在进行捐赠:)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>更新完毕</value> <value>更新完毕</value>
</data> </data>

View File

@@ -238,9 +238,7 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve"> <data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>正在檢查新版本...</value> <value>正在檢查新版本...</value>
</data> </data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>正在下載新版本... 等待期間如果喜歡這個軟體請考慮捐助ASF! :)</value>
</data>
<data name="UpdateFinished" xml:space="preserve"> <data name="UpdateFinished" xml:space="preserve">
<value>更新完成 </value> <value>更新完成 </value>
</data> </data>

View File

@@ -94,8 +94,6 @@ namespace ArchiSteamFarm {
internal enum EExecutionState : uint { internal enum EExecutionState : uint {
Error = 0, Error = 0,
SystemRequired = 0x00000001, SystemRequired = 0x00000001,
//DisplayRequired = 0x00000002,
//UserPresent = 0x00000004,
AwayModeRequired = 0x00000040, AwayModeRequired = 0x00000040,
Continuous = 0x80000000 Continuous = 0x80000000
} }

View File

@@ -135,13 +135,17 @@ namespace ArchiSteamFarm {
ASF.ArchiLogger.LogGenericException(e); ASF.ArchiLogger.LogGenericException(e);
} }
// Give new process some time to take over the window (if needed)
await Task.Delay(2000).ConfigureAwait(false);
ShutdownResetEvent.Set(); ShutdownResetEvent.Set();
Environment.Exit(0); Environment.Exit(0);
} }
private static async Task Init(string[] args) { private static async Task Init(string[] args) {
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler; AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
// We must register our logging target as soon as possible // We must register our logging target as soon as possible
Target.Register<SteamTarget>(SteamTarget.TargetName); Target.Register<SteamTarget>(SteamTarget.TargetName);
@@ -180,6 +184,8 @@ namespace ArchiSteamFarm {
ParsePostInitArgs(args); ParsePostInitArgs(args);
} }
IPC.Start();
if (!Debugging.IsDebugBuild) { if (!Debugging.IsDebugBuild) {
await ASF.CheckForUpdate().ConfigureAwait(false); await ASF.CheckForUpdate().ConfigureAwait(false);
} }
@@ -306,7 +312,7 @@ namespace ArchiSteamFarm {
OS.Init(GlobalConfig.Headless); OS.Init(GlobalConfig.Headless);
WebBrowser.Init(); WebBrowser.Init();
WebBrowser = new WebBrowser(ASF.ArchiLogger); WebBrowser = new WebBrowser(ASF.ArchiLogger, true);
} }
private static async Task<bool> InitShutdownSequence() { private static async Task<bool> InitShutdownSequence() {
@@ -316,8 +322,6 @@ namespace ArchiSteamFarm {
ShutdownSequenceInitialized = true; ShutdownSequenceInitialized = true;
IPC.Stop();
if (Bot.Bots.Count == 0) { if (Bot.Bots.Count == 0) {
return true; return true;
} }
@@ -327,12 +331,12 @@ namespace ArchiSteamFarm {
switch (GlobalConfig.OptimizationMode) { switch (GlobalConfig.OptimizationMode) {
case GlobalConfig.EOptimizationMode.MinMemoryUsage: case GlobalConfig.EOptimizationMode.MinMemoryUsage:
foreach (Task task in tasks) { foreach (Task task in tasks) {
await Task.WhenAny(task, Task.Delay(WebBrowser.MaxRetries * 1000)).ConfigureAwait(false); await Task.WhenAny(task, Task.Delay(WebBrowser.MaxTries * 1000)).ConfigureAwait(false);
} }
break; break;
default: default:
await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(Bot.Bots.Count * WebBrowser.MaxRetries * 1000)).ConfigureAwait(false); await Task.WhenAny(Task.WhenAll(tasks), Task.Delay(Bot.Bots.Count * WebBrowser.MaxTries * 1000)).ConfigureAwait(false);
break; break;
} }
@@ -350,6 +354,31 @@ namespace ArchiSteamFarm {
Exit().Wait(); Exit().Wait();
} }
private static void OnProcessExit(object sender, EventArgs e) => IPC.Stop();
private static async void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) {
if (e?.ExceptionObject == null) {
ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.ExceptionObject));
return;
}
ASF.ArchiLogger.LogFatalException((Exception) e.ExceptionObject);
await Exit(1).ConfigureAwait(false);
}
private static void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) {
if (e?.Exception == null) {
ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.Exception));
return;
}
ASF.ArchiLogger.LogFatalException(e.Exception);
// Normally we should abort the application here, but many tasks are in fact failing in SK2 code which we can't easily fix
// Thanks Valve.
e.SetObserved();
}
private static void ParsePostInitArgs(IEnumerable<string> args) { private static void ParsePostInitArgs(IEnumerable<string> args) {
if (args == null) { if (args == null) {
ASF.ArchiLogger.LogNullError(nameof(args)); ASF.ArchiLogger.LogNullError(nameof(args));
@@ -404,28 +433,5 @@ namespace ArchiSteamFarm {
ShutdownResetEvent.Set(); ShutdownResetEvent.Set();
} }
private static async void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e) {
if (e?.ExceptionObject == null) {
ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.ExceptionObject));
return;
}
ASF.ArchiLogger.LogFatalException((Exception) e.ExceptionObject);
await Exit(1).ConfigureAwait(false);
}
private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs e) {
if (e?.Exception == null) {
ASF.ArchiLogger.LogNullError(nameof(e) + " || " + nameof(e.Exception));
return;
}
ASF.ArchiLogger.LogFatalException(e.Exception);
// Normally we should abort the application here, but many tasks are in fact failing in SK2 code which we can't easily fix
// Thanks Valve.
e.SetObserved();
}
} }
} }

View File

@@ -32,7 +32,7 @@ using ArchiSteamFarm.Localization;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal sealed class Trading : IDisposable { internal sealed class Trading : IDisposable {
internal const byte MaxItemsPerTrade = 150; // This is due to limit on POST size in WebBrowser internal const byte MaxItemsPerTrade = byte.MaxValue; // This is due to limit on POST size in WebBrowser
internal const byte MaxTradesPerAccount = 5; // This is limit introduced by Valve internal const byte MaxTradesPerAccount = 5; // This is limit introduced by Valve
private readonly Bot Bot; private readonly Bot Bot;
@@ -173,7 +173,7 @@ namespace ArchiSteamFarm {
return new ParseTradeResult(tradeOffer.TradeOfferID, tradeOffer.ItemsToGive.Count > 0 ? ParseTradeResult.EResult.AcceptedWithItemLose : ParseTradeResult.EResult.AcceptedWithoutItemLose); return new ParseTradeResult(tradeOffer.TradeOfferID, tradeOffer.ItemsToGive.Count > 0 ? ParseTradeResult.EResult.AcceptedWithItemLose : ParseTradeResult.EResult.AcceptedWithoutItemLose);
} }
// Always deny trades from blacklistem steamIDs // Always deny trades from blacklisted steamIDs
if (Bot.IsBlacklistedFromTrades(tradeOffer.OtherSteamID64)) { if (Bot.IsBlacklistedFromTrades(tradeOffer.OtherSteamID64)) {
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently); return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently);
} }
@@ -214,8 +214,8 @@ namespace ArchiSteamFarm {
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently); return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently);
} }
// Decline trade if we're losing anything but steam cards, or if it's non-dupes trade // Decline trade if it's not fair games/types exchange or if we're requested to handle any not-accepted item type
if (!tradeOffer.IsSteamCardsRequest() || !tradeOffer.IsFairTypesExchange()) { if (!tradeOffer.IsFairTypesExchange() || !tradeOffer.IsValidSteamItemsRequest(Bot.BotConfig.MatchableTypes)) {
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently); return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedPermanently);
} }
@@ -241,17 +241,35 @@ namespace ArchiSteamFarm {
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.AcceptedWithItemLose); return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.AcceptedWithItemLose);
} }
// Get appIDs we're interested in // Get appIDs/types we're interested in
HashSet<uint> appIDs = new HashSet<uint>(tradeOffer.ItemsToGive.Select(item => item.RealAppID)); HashSet<uint> appIDs = new HashSet<uint>();
HashSet<Steam.Item.EType> types = new HashSet<Steam.Item.EType>();
foreach (Steam.Item item in tradeOffer.ItemsToGive) {
appIDs.Add(item.RealAppID);
types.Add(item.Type);
}
// Now check if it's worth for us to do the trade // Now check if it's worth for us to do the trade
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(false, new HashSet<Steam.Item.EType> { Steam.Item.EType.TradingCard }, appIDs).ConfigureAwait(false); HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMySteamInventory(false, types, appIDs).ConfigureAwait(false);
if ((inventory == null) || (inventory.Count == 0)) { if ((inventory == null) || (inventory.Count == 0)) {
// If we can't check our inventory when not using MatchEverything, this is a temporary failure // If we can't check our inventory when not using MatchEverything, this is a temporary failure
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(inventory))); Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorIsEmpty, nameof(inventory)));
return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedTemporarily); return new ParseTradeResult(tradeOffer.TradeOfferID, ParseTradeResult.EResult.RejectedTemporarily);
} }
bool accept = IsTradeNeutralOrBetter(inventory, tradeOffer.ItemsToGive, tradeOffer.ItemsToReceive);
// Even if trade is not neutral+ for us right now, it might be in the future, unless we're bot account where we assume that inventory doesn't change
return new ParseTradeResult(tradeOffer.TradeOfferID, accept ? ParseTradeResult.EResult.AcceptedWithItemLose : (Bot.BotConfig.IsBotAccount ? ParseTradeResult.EResult.RejectedPermanently : ParseTradeResult.EResult.RejectedTemporarily));
}
private static bool IsTradeNeutralOrBetter(HashSet<Steam.Item> inventory, HashSet<Steam.Item> itemsToGive, HashSet<Steam.Item> itemsToReceive) {
if ((inventory == null) || (inventory.Count == 0) || (itemsToGive == null) || (itemsToGive.Count == 0) || (itemsToReceive == null) || (itemsToReceive.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(inventory) + " || " + nameof(itemsToGive) + " || " + nameof(itemsToReceive));
return false;
}
// Now let's create a map which maps items to their amount in our EQ // Now let's create a map which maps items to their amount in our EQ
// This has to be done as we might have multiple items of given ClassID with multiple amounts // This has to be done as we might have multiple items of given ClassID with multiple amounts
Dictionary<ulong, uint> itemAmounts = new Dictionary<ulong, uint>(); Dictionary<ulong, uint> itemAmounts = new Dictionary<ulong, uint>();
@@ -264,12 +282,12 @@ namespace ArchiSteamFarm {
} }
// Calculate our value of items to give on per-game basis // Calculate our value of items to give on per-game basis
Dictionary<uint, List<uint>> itemAmountToGivePerGame = new Dictionary<uint, List<uint>>(appIDs.Count); Dictionary<(Steam.Item.EType Type, uint AppID), List<uint>> itemAmountToGivePerGame = new Dictionary<(Steam.Item.EType Type, uint AppID), List<uint>>();
Dictionary<ulong, uint> itemAmountsToGive = new Dictionary<ulong, uint>(itemAmounts); Dictionary<ulong, uint> itemAmountsToGive = new Dictionary<ulong, uint>(itemAmounts);
foreach (Steam.Item item in tradeOffer.ItemsToGive) { foreach (Steam.Item item in itemsToGive) {
if (!itemAmountToGivePerGame.TryGetValue(item.RealAppID, out List<uint> amountsToGive)) { if (!itemAmountToGivePerGame.TryGetValue((item.Type, item.RealAppID), out List<uint> amountsToGive)) {
amountsToGive = new List<uint>(); amountsToGive = new List<uint>();
itemAmountToGivePerGame[item.RealAppID] = amountsToGive; itemAmountToGivePerGame[(item.Type, item.RealAppID)] = amountsToGive;
} }
if (!itemAmountsToGive.TryGetValue(item.ClassID, out uint amount)) { if (!itemAmountsToGive.TryGetValue(item.ClassID, out uint amount)) {
@@ -287,12 +305,12 @@ namespace ArchiSteamFarm {
} }
// Calculate our value of items to receive on per-game basis // Calculate our value of items to receive on per-game basis
Dictionary<uint, List<uint>> itemAmountToReceivePerGame = new Dictionary<uint, List<uint>>(appIDs.Count); Dictionary<(Steam.Item.EType Type, uint AppID), List<uint>> itemAmountToReceivePerGame = new Dictionary<(Steam.Item.EType Type, uint AppID), List<uint>>();
Dictionary<ulong, uint> itemAmountsToReceive = new Dictionary<ulong, uint>(itemAmounts); Dictionary<ulong, uint> itemAmountsToReceive = new Dictionary<ulong, uint>(itemAmounts);
foreach (Steam.Item item in tradeOffer.ItemsToReceive) { foreach (Steam.Item item in itemsToReceive) {
if (!itemAmountToReceivePerGame.TryGetValue(item.RealAppID, out List<uint> amountsToReceive)) { if (!itemAmountToReceivePerGame.TryGetValue((item.Type, item.RealAppID), out List<uint> amountsToReceive)) {
amountsToReceive = new List<uint>(); amountsToReceive = new List<uint>();
itemAmountToReceivePerGame[item.RealAppID] = amountsToReceive; itemAmountToReceivePerGame[(item.Type, item.RealAppID)] = amountsToReceive;
} }
if (!itemAmountsToReceive.TryGetValue(item.ClassID, out uint amount)) { if (!itemAmountsToReceive.TryGetValue(item.ClassID, out uint amount)) {
@@ -313,10 +331,7 @@ namespace ArchiSteamFarm {
// This is quite complex operation of taking minimum difference from all differences on per-game basis // This is quite complex operation of taking minimum difference from all differences on per-game basis
// When calculating per-game difference, we sum only amounts at proper indexes, because user might be overpaying // When calculating per-game difference, we sum only amounts at proper indexes, because user might be overpaying
int difference = itemAmountToGivePerGame.Min(kv => kv.Value.Select((t, i) => (int) (t - itemAmountToReceivePerGame[kv.Key][i])).Sum()); int difference = itemAmountToGivePerGame.Min(kv => kv.Value.Select((t, i) => (int) (t - itemAmountToReceivePerGame[kv.Key][i])).Sum());
return difference > 0;
// Trade is neutral+ for us if the difference is greater than 0
// If not, we assume that the trade might be good for us in the future, unless we're bot account where we assume that inventory doesn't change
return new ParseTradeResult(tradeOffer.TradeOfferID, difference > 0 ? ParseTradeResult.EResult.AcceptedWithItemLose : (Bot.BotConfig.IsBotAccount ? ParseTradeResult.EResult.RejectedPermanently : ParseTradeResult.EResult.RejectedTemporarily));
} }
private sealed class ParseTradeResult { private sealed class ParseTradeResult {

View File

@@ -95,22 +95,34 @@ namespace ArchiSteamFarm {
} }
} }
internal static void StartBackgroundAction(Action action) { internal static void StartBackgroundAction(Action action, bool longRunning = true) {
if (action == null) { if (action == null) {
ASF.ArchiLogger.LogNullError(nameof(action)); ASF.ArchiLogger.LogNullError(nameof(action));
return; return;
} }
Task.Factory.StartNew(action, TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning).Forget(); TaskCreationOptions options = TaskCreationOptions.DenyChildAttach;
if (longRunning) {
options |= TaskCreationOptions.LongRunning;
}
Task.Factory.StartNew(action, options).Forget();
} }
internal static void StartBackgroundFunction(Func<Task> function) { internal static void StartBackgroundFunction(Func<Task> function, bool longRunning = true) {
if (function == null) { if (function == null) {
ASF.ArchiLogger.LogNullError(nameof(function)); ASF.ArchiLogger.LogNullError(nameof(function));
return; return;
} }
Task.Factory.StartNew(function, TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning).Forget(); TaskCreationOptions options = TaskCreationOptions.DenyChildAttach;
if (longRunning) {
options |= TaskCreationOptions.LongRunning;
}
Task.Factory.StartNew(function, options).Forget();
} }
internal static IEnumerable<T> ToEnumerable<T>(this T item) where T : struct { internal static IEnumerable<T> ToEnumerable<T>(this T item) where T : struct {

View File

@@ -35,26 +35,28 @@ using Newtonsoft.Json.Linq;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal sealed class WebBrowser { internal sealed class WebBrowser {
internal const byte MaxRetries = 5; // Defines maximum number of retries, UrlRequest() does not handle retry by itself (it's app responsibility) internal const byte MaxTries = 5; // Defines maximum number of recommended tries for a single request
private const byte MaxConnections = ServicePointManager.DefaultNonPersistentConnectionLimit; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state private const byte ExtendedTimeoutMultiplier = 10; // Defines multiplier of timeout for WebBrowsers dealing with huge data (ASF update)
private const byte MaxIdleTime = 15; // In seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it private const byte MaxConnections = 10; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
private const byte MaxIdleTime = 15; // Defines in seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
internal readonly CookieContainer CookieContainer = new CookieContainer(); internal readonly CookieContainer CookieContainer = new CookieContainer();
private readonly ArchiLogger ArchiLogger; private readonly ArchiLogger ArchiLogger;
private readonly HttpClient HttpClient; private readonly HttpClient HttpClient;
internal WebBrowser(ArchiLogger archiLogger) { internal WebBrowser(ArchiLogger archiLogger, bool extendedTimeout = false) {
ArchiLogger = archiLogger ?? throw new ArgumentNullException(nameof(archiLogger)); ArchiLogger = archiLogger ?? throw new ArgumentNullException(nameof(archiLogger));
HttpClientHandler httpClientHandler = new HttpClientHandler { HttpClientHandler httpClientHandler = new HttpClientHandler {
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip, AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
CookieContainer = CookieContainer CookieContainer = CookieContainer,
MaxConnectionsPerServer = MaxConnections
}; };
HttpClient = new HttpClient(httpClientHandler) { HttpClient = new HttpClient(httpClientHandler) {
Timeout = TimeSpan.FromSeconds(Program.GlobalConfig.ConnectionTimeout) Timeout = TimeSpan.FromSeconds(extendedTimeout ? ExtendedTimeoutMultiplier * Program.GlobalConfig.ConnectionTimeout : Program.GlobalConfig.ConnectionTimeout)
}; };
// Most web services expect that UserAgent is set, so we declare it globally // Most web services expect that UserAgent is set, so we declare it globally
@@ -94,7 +96,7 @@ namespace ArchiSteamFarm {
} }
byte[] result = null; byte[] result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) { for (byte i = 0; (i < MaxTries) && (result == null); i++) {
result = await UrlGetToBytes(request, referer).ConfigureAwait(false); result = await UrlGetToBytes(request, referer).ConfigureAwait(false);
} }
@@ -102,7 +104,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries)); ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
return null; return null;
} }
@@ -114,7 +116,7 @@ namespace ArchiSteamFarm {
} }
HtmlDocument result = null; HtmlDocument result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) { for (byte i = 0; (i < MaxTries) && (result == null); i++) {
result = await UrlGetToHtmlDocument(request, referer).ConfigureAwait(false); result = await UrlGetToHtmlDocument(request, referer).ConfigureAwait(false);
} }
@@ -122,7 +124,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries)); ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
return null; return null;
} }
@@ -134,7 +136,7 @@ namespace ArchiSteamFarm {
} }
JObject result = null; JObject result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) { for (byte i = 0; (i < MaxTries) && (result == null); i++) {
result = await UrlGetToJObject(request, referer).ConfigureAwait(false); result = await UrlGetToJObject(request, referer).ConfigureAwait(false);
} }
@@ -142,7 +144,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries)); ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
return null; return null;
} }
@@ -178,7 +180,7 @@ namespace ArchiSteamFarm {
} }
XmlDocument result = null; XmlDocument result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) { for (byte i = 0; (i < MaxTries) && (result == null); i++) {
result = await UrlGetToXML(request, referer).ConfigureAwait(false); result = await UrlGetToXML(request, referer).ConfigureAwait(false);
} }
@@ -186,7 +188,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries)); ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
return null; return null;
} }
@@ -198,7 +200,7 @@ namespace ArchiSteamFarm {
} }
bool result = false; bool result = false;
for (byte i = 0; (i < MaxRetries) && !result; i++) { for (byte i = 0; (i < MaxTries) && !result; i++) {
result = await UrlHead(request, referer).ConfigureAwait(false); result = await UrlHead(request, referer).ConfigureAwait(false);
} }
@@ -206,7 +208,7 @@ namespace ArchiSteamFarm {
return true; return true;
} }
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries)); ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
return false; return false;
} }
@@ -218,7 +220,7 @@ namespace ArchiSteamFarm {
} }
Uri result = null; Uri result = null;
for (byte i = 0; (i < MaxRetries) && (result == null); i++) { for (byte i = 0; (i < MaxTries) && (result == null); i++) {
result = await UrlHeadToUri(request, referer).ConfigureAwait(false); result = await UrlHeadToUri(request, referer).ConfigureAwait(false);
} }
@@ -226,7 +228,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries)); ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
return null; return null;
} }
@@ -249,7 +251,7 @@ namespace ArchiSteamFarm {
} }
bool result = false; bool result = false;
for (byte i = 0; (i < MaxRetries) && !result; i++) { for (byte i = 0; (i < MaxTries) && !result; i++) {
result = await UrlPost(request, data, referer).ConfigureAwait(false); result = await UrlPost(request, data, referer).ConfigureAwait(false);
} }
@@ -257,7 +259,7 @@ namespace ArchiSteamFarm {
return true; return true;
} }
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries)); ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
return false; return false;
} }
@@ -333,7 +335,7 @@ namespace ArchiSteamFarm {
} }
string result = null; string result = null;
for (byte i = 0; (i < MaxRetries) && string.IsNullOrEmpty(result); i++) { for (byte i = 0; (i < MaxTries) && string.IsNullOrEmpty(result); i++) {
result = await UrlGetToContent(request, referer).ConfigureAwait(false); result = await UrlGetToContent(request, referer).ConfigureAwait(false);
} }
@@ -341,7 +343,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries)); ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
return null; return null;
} }
@@ -380,12 +382,13 @@ namespace ArchiSteamFarm {
} }
private async Task<HttpResponseMessage> UrlGetToResponse(string request, string referer = null) { private async Task<HttpResponseMessage> UrlGetToResponse(string request, string referer = null) {
if (!string.IsNullOrEmpty(request)) { if (string.IsNullOrEmpty(request)) {
return await UrlRequest(new Uri(request), HttpMethod.Get, null, referer).ConfigureAwait(false); ArchiLogger.LogNullError(nameof(request));
return null;
} }
ArchiLogger.LogNullError(nameof(request)); HttpResponseMessage result = await UrlRequest(new Uri(request), HttpMethod.Get, null, referer).ConfigureAwait(false);
return null; return result;
} }
private async Task<XmlDocument> UrlGetToXML(string request, string referer = null) { private async Task<XmlDocument> UrlGetToXML(string request, string referer = null) {
@@ -464,7 +467,7 @@ namespace ArchiSteamFarm {
} }
string result = null; string result = null;
for (byte i = 0; (i < MaxRetries) && string.IsNullOrEmpty(result); i++) { for (byte i = 0; (i < MaxTries) && string.IsNullOrEmpty(result); i++) {
result = await UrlPostToContent(request, data, referer).ConfigureAwait(false); result = await UrlPostToContent(request, data, referer).ConfigureAwait(false);
} }
@@ -472,7 +475,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxRetries)); ArchiLogger.LogGenericWarning(string.Format(Strings.ErrorRequestFailedTooManyTimes, MaxTries));
ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request)); ArchiLogger.LogGenericDebug(string.Format(Strings.ErrorFailingRequest, request));
return null; return null;
} }
@@ -486,7 +489,7 @@ namespace ArchiSteamFarm {
return null; return null;
} }
private async Task<HttpResponseMessage> UrlRequest(Uri requestUri, HttpMethod httpMethod, ICollection<KeyValuePair<string, string>> data = null, string referer = null, byte maxRedirections = MaxRetries) { private async Task<HttpResponseMessage> UrlRequest(Uri requestUri, HttpMethod httpMethod, ICollection<KeyValuePair<string, string>> data = null, string referer = null, byte maxRedirections = MaxTries) {
if ((requestUri == null) || (httpMethod == null)) { if ((requestUri == null) || (httpMethod == null)) {
ArchiLogger.LogNullError(nameof(requestUri) + " || " + nameof(httpMethod)); ArchiLogger.LogNullError(nameof(requestUri) + " || " + nameof(httpMethod));
return null; return null;

View File

@@ -15,6 +15,10 @@
3, 3,
5 5
], ],
"MatchableTypes": [
3,
5
],
"PasswordFormat": 0, "PasswordFormat": 0,
"Paused": false, "Paused": false,
"RedeemingPreferences": 0, "RedeemingPreferences": 0,

View File

@@ -18,21 +18,12 @@ build:
project: ArchiSteamFarm.sln project: ArchiSteamFarm.sln
parallel: true parallel: true
verbosity: minimal verbosity: minimal
after_build: after_test:
- ps: >- - ps: >-
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
$RUNTIMES = 'generic', 'win-x64', 'linux-x64', "linux-arm", 'osx-x64' $RUNTIMES = 'generic', 'win-x64', 'linux-x64', 'linux-arm', 'osx-x64'
if ($env:APPVEYOR_REPO_TAG -eq 'true') {
$ZIP_ARGS = '-mx=9', '-mfb=257', '-mpass=15'
} else {
$ZIP_ARGS = '-mx=1'
}
foreach ($RUNTIME in $RUNTIMES) { foreach ($RUNTIME in $RUNTIMES) {
@@ -44,13 +35,13 @@ after_build:
Add-Content "ArchiSteamFarm\out\$RUNTIME\ArchiSteamFarm.version" "$RUNTIME" Add-Content "ArchiSteamFarm\out\$RUNTIME\ArchiSteamFarm.version" "$RUNTIME"
7z a -bd -tzip -mm=Deflate64 $ZIP_ARGS "ArchiSteamFarm\out\ASF-$RUNTIME.zip" "$env:APPVEYOR_BUILD_FOLDER\ArchiSteamFarm\out\$RUNTIME\*" 7z a -bd -tzip -mm=Deflate64 -mx=5 "ArchiSteamFarm\out\ASF-$RUNTIME.zip" "$env:APPVEYOR_BUILD_FOLDER\ArchiSteamFarm\out\$RUNTIME\*"
Push-AppveyorArtifact "ArchiSteamFarm\out\ASF-$RUNTIME.zip" -FileName "ASF-$RUNTIME.zip" -DeploymentName "ASF-$RUNTIME.zip" Push-AppveyorArtifact "ArchiSteamFarm\out\ASF-$RUNTIME.zip" -FileName "ASF-$RUNTIME.zip" -DeploymentName "ASF-$RUNTIME.zip"
} }
deploy: deploy:
- provider: GitHub - provider: GitHub
tag: $(appveyor_repo_tag_name) tag: $(appveyor_repo_tag_name)
release: ASF V$(appveyor_repo_tag_name) release: ArchiSteamFarm V$(appveyor_repo_tag_name)
description: '**NOTICE:** Pre-releases are experimental versions that often contain unpatched bugs, work-in-progress features or rewritten implementations. If you don''t consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchi/ArchiSteamFarm/wiki/Release-cycle)** if you''d like to learn more.\n\n---\n\nThis is automated AppVeyor GitHub deployment, human-readable changelog should be available soon. In the meantime please refer to **[GitHub commits](https://github.com/JustArchi/ArchiSteamFarm/commits/$(appveyor_repo_tag_name))**.\n\n---\n\nASF is available for free. If you''re grateful for what we''re doing, please consider donating. Developing ASF requires massive amount of time and knowledge, especially when it comes to Steam (and its problems). Even 1$ is highly appreciated and shows that you care!\n\n [![Patreon support](https://img.shields.io/badge/Patreon-support-yellow.svg)](https://www.patreon.com/JustArchi) [![Paypal.me donate](https://img.shields.io/badge/Paypal.me-donate-yellow.svg)](https://www.paypal.me/JustArchi/1usd) [![Paypal donate](https://img.shields.io/badge/Paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4) [![Bitcoin donate](https://img.shields.io/badge/Bitcoin-donate-yellow.svg)](https://blockchain.info/payment_request?address=1Archi6M1r5b41Rvn1SY2FfJAzsrEUT7aT) [![Steam donate](https://img.shields.io/badge/Steam-donate-yellow.svg)](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)' description: '**NOTICE:** Pre-releases are experimental versions that often contain unpatched bugs, work-in-progress features or rewritten implementations. If you don''t consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchi/ArchiSteamFarm/wiki/Release-cycle)** if you''d like to learn more.\n\n---\n\nThis is automated AppVeyor GitHub deployment, human-readable changelog should be available soon. In the meantime please refer to **[GitHub commits](https://github.com/JustArchi/ArchiSteamFarm/commits/$(appveyor_repo_tag_name))**.\n\n---\n\nASF is available for free. If you''re grateful for what we''re doing, please consider donating. Developing ASF requires massive amount of time and knowledge, especially when it comes to Steam (and its problems). Even 1$ is highly appreciated and shows that you care!\n\n [![Patreon support](https://img.shields.io/badge/Patreon-support-yellow.svg)](https://www.patreon.com/JustArchi) [![Paypal.me donate](https://img.shields.io/badge/Paypal.me-donate-yellow.svg)](https://www.paypal.me/JustArchi/1usd) [![Paypal donate](https://img.shields.io/badge/Paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4) [![Bitcoin donate](https://img.shields.io/badge/Bitcoin-donate-yellow.svg)](https://blockchain.info/payment_request?address=1Archi6M1r5b41Rvn1SY2FfJAzsrEUT7aT) [![Steam donate](https://img.shields.io/badge/Steam-donate-yellow.svg)](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)'
auth_token: auth_token:
secure: QC5gIDMvSpd43EG6qW8d1E3ZHiVU4aR7pbKQonXstjj/JtAABf5S1IbtoY4OsnOR secure: QC5gIDMvSpd43EG6qW8d1E3ZHiVU4aR7pbKQonXstjj/JtAABf5S1IbtoY4OsnOR

View File

@@ -15,9 +15,9 @@ This tool is being used by ASF developers for synchronization of strings/transla
## Before you begin ## Before you begin
- Make sure that your ```crowdin_identity.yaml``` file exists - this is the file with login credentials that is not being committed to GitHub. If it doesn't exist yet (e.g. because you've just cloned the repo), create it from ```crowdin_identity_example.yaml``` and fill ```api_key``` that can be found **[here](http://l10n.asf.justarchi.net/project/archisteamfarm/settings#api)**. - Make sure that your `crowdin_identity.yaml` file exists - this is the file with login credentials that is not being committed to GitHub. If it doesn't exist yet (e.g. because you've just cloned the repo), create it from `crowdin_identity_example.yaml` and fill `api_key` that can be found **[here](http://l10n.asf.justarchi.net/project/archisteamfarm/settings#api)**.
- Ensure that ```crowdin``` command is recognized by your OS. - Ensure that `crowdin` command is recognized by your OS.
--- ---
@@ -25,14 +25,16 @@ This tool is being used by ASF developers for synchronization of strings/transla
- Install **[Java JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html)**. - Install **[Java JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html)**.
- **[Set JAVA_HOME properly](https://confluence.atlassian.com/doc/setting-the-java_home-variable-in-windows-8895.html)**. - **[Set JAVA_HOME properly](https://confluence.atlassian.com/doc/setting-the-java_home-variable-in-windows-8895.html)**.
- Launch ```setup_crowdin.bat``` as administrator. - Launch `setup_crowdin.bat` as administrator.
- Open new ```cmd``` prompt and verify that ```crowdin help``` indeed works. - Open new `cmd` prompt and verify that `crowdin help` indeed works.
--- ---
## Usage ## Usage
- ```archi_download.bat``` for downloading translations from Crowdin (typically last commit before release). - `archi_upload.bat` for pushing strings to Crowdin (when any `*Strings.resx` file gets modified).
- ```archi_upload.bat``` for pushing strings to Crowdin (when any ```*Strings.resx``` file gets modified). - `archi_download.bat` for downloading translations from Crowdin (typically last commit before release).
- `archi_sync.bat` for upload + download (tree sync).

View File

@@ -0,0 +1,5 @@
@echo off
cd ..\\..
call crowdin -b master --identity tools\\crowdin-cli\\crowdin_identity.yaml upload sources
call crowdin -b master --identity tools\\crowdin-cli\\crowdin_identity.yaml download
pause