Compare commits

..

39 Commits

Author SHA1 Message Date
JustArchi
6804f5b5f7 Fix for multiple licenses for the same package 2020-06-13 21:45:37 +02:00
JustArchi
574e8739c1 Bump 2020-06-13 20:23:55 +02:00
JustArchi
89e3c9f76d Remove debugging leftover 2020-06-13 20:22:38 +02:00
JustArchi
2f45e28bae Bump 2020-06-13 19:05:54 +02:00
JustArchi
dd8a0a18c2 Translations update 2020-06-13 19:03:41 +02:00
JustArchi
a2edbe42ed Misc 2020-06-13 18:47:09 +02:00
JustArchi
b4f73263e6 Misc fix 2020-06-13 17:23:41 +02:00
JustArchi
513397ca0f Correct HasCustomPluginsLoaded check 2020-06-13 17:14:59 +02:00
JustArchi
abb702ffa3 Misc 2020-06-13 17:06:50 +02:00
JustArchi
a13569e5fb Add STEAM_TOKEN_DUMPER_TOKEN logic to docker images 2020-06-13 17:05:26 +02:00
JustArchi
84cd99468f Add compatibility with OS X 2020-06-13 16:25:13 +02:00
JustArchi
723824ee7d Set up STEAM_TOKEN_DUMPER_TOKEN 2020-06-13 16:14:37 +02:00
JustArchi
f6c1e217f0 STD: Fail on invalid tokens 2020-06-13 15:35:56 +02:00
JustArchi
4d1bca5e51 Introduce concept of official plugins 2020-06-13 15:09:12 +02:00
JustArchi
70bb1e865a Thanks .NET Framework 2020-06-13 15:06:07 +02:00
JustArchi
88b4856fe1 CIs: Start bundling the plugin with ASF 2020-06-13 14:45:41 +02:00
JustArchi
460689841a Allow update operation to handle official plugins 2020-06-13 14:44:52 +02:00
JustArchi
73014c71a1 Always schedule package for refresh on token change 2020-06-13 13:03:13 +02:00
JustArchi
0d8753f44e Fix possibility of package having no appIDs 2020-06-13 12:49:12 +02:00
JustArchi
2a43a87e08 Initial SteamTokenDumper upload 2020-06-13 12:08:21 +02:00
JustArchi
32b376d85a Open RNG for plugins 2020-06-12 13:00:32 +02:00
dependabot-preview[bot]
d40dbb2676 Bump wiki from ac4a50b to ce5a224
Bumps [wiki](https://github.com/JustArchiNET/ArchiSteamFarm.wiki) from `ac4a50b` to `ce5a224`.
- [Release notes](https://github.com/JustArchiNET/ArchiSteamFarm.wiki/releases)
- [Commits](ac4a50ba9d...ce5a2247a6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-12 01:36:15 +00:00
dependabot-preview[bot]
4cf4bae2e9 Bump ASF-ui from 198fe2f to 2a3c6a4
Bumps [ASF-ui](https://github.com/JustArchiNET/ASF-ui) from `198fe2f` to `2a3c6a4`.
- [Release notes](https://github.com/JustArchiNET/ASF-ui/releases)
- [Commits](198fe2ff77...2a3c6a4df8)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-12 01:28:13 +00:00
dependabot-preview[bot]
162ab3f693 Bump ASF-WebConfigGenerator from bb455ee to b0dee46
Bumps [ASF-WebConfigGenerator](https://github.com/JustArchiNET/ASF-WebConfigGenerator) from `bb455ee` to `b0dee46`.
- [Release notes](https://github.com/JustArchiNET/ASF-WebConfigGenerator/releases)
- [Commits](bb455ee233...b0dee46cab)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-12 01:27:43 +00:00
JustArchi
5f7978f21e Open OwnedPackageIDsReadOnly for plugin usage 2020-06-11 14:23:28 +02:00
dependabot-preview[bot]
3d95ba63e3 Bump wiki from 508359a to ac4a50b
Bumps [wiki](https://github.com/JustArchiNET/ArchiSteamFarm.wiki) from `508359a` to `ac4a50b`.
- [Release notes](https://github.com/JustArchiNET/ArchiSteamFarm.wiki/releases)
- [Commits](508359a397...ac4a50ba9d)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-11 01:27:48 +00:00
dependabot-preview[bot]
55df09e04b Bump ASF-ui from 46007c0 to 198fe2f
Bumps [ASF-ui](https://github.com/JustArchiNET/ASF-ui) from `46007c0` to `198fe2f`.
- [Release notes](https://github.com/JustArchiNET/ASF-ui/releases)
- [Commits](46007c0f12...198fe2ff77)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-11 01:22:33 +00:00
JustArchi
bdc3beb15a Do not report warning over having empty list of packagesData
It can be empty if we're not able to fetch any of given packageIDs, e.g. because of missing token.
2020-06-10 22:43:06 +02:00
dependabot-preview[bot]
fa7bb801db Bump Microsoft.Extensions.Logging.Configuration from 3.1.4 to 3.1.5
Bumps [Microsoft.Extensions.Logging.Configuration](https://github.com/aspnet/Extensions) from 3.1.4 to 3.1.5.
- [Release notes](https://github.com/aspnet/Extensions/releases)
- [Commits](https://github.com/aspnet/Extensions/compare/v3.1.4...v3.1.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-10 01:29:15 +00:00
dependabot-preview[bot]
e68d6acf94 Bump ASF-WebConfigGenerator from 024a6ef to bb455ee
Bumps [ASF-WebConfigGenerator](https://github.com/JustArchiNET/ASF-WebConfigGenerator) from `024a6ef` to `bb455ee`.
- [Release notes](https://github.com/JustArchiNET/ASF-WebConfigGenerator/releases)
- [Commits](024a6ef31b...bb455ee233)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-10 01:23:08 +00:00
JustArchi
a9c1d58ddc Misc 2020-06-09 23:09:13 +02:00
JustArchi
0fa4d5e2a6 Refactor package info and their access tokens, add ISteamPICSChanges plugin interface 2020-06-09 22:56:04 +02:00
JustArchi
3203daf787 Remove PICSSemaphore
It should be no longer needed
2020-06-09 19:45:12 +02:00
dependabot-preview[bot]
b576a97db0 Bump Microsoft.AspNetCore.Mvc.NewtonsoftJson from 3.1.4 to 3.1.5
Bumps [Microsoft.AspNetCore.Mvc.NewtonsoftJson](https://github.com/aspnet/AspNetCore) from 3.1.4 to 3.1.5.
- [Release notes](https://github.com/aspnet/AspNetCore/releases)
- [Changelog](https://github.com/dotnet/aspnetcore/blob/master/docs/CrossRepoBreakingChanges.md)
- [Commits](https://github.com/aspnet/AspNetCore/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-09 15:18:40 +00:00
dependabot-preview[bot]
a461ad6835 Bump Microsoft.Extensions.Configuration.Json from 3.1.4 to 3.1.5
Bumps [Microsoft.Extensions.Configuration.Json](https://github.com/aspnet/Extensions) from 3.1.4 to 3.1.5.
- [Release notes](https://github.com/aspnet/Extensions/releases)
- [Commits](https://github.com/aspnet/Extensions/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-09 15:11:39 +00:00
dependabot-preview[bot]
33ab56dc03 Bump ASF-ui from 810095a to 46007c0
Bumps [ASF-ui](https://github.com/JustArchiNET/ASF-ui) from `810095a` to `46007c0`.
- [Release notes](https://github.com/JustArchiNET/ASF-ui/releases)
- [Commits](810095a335...46007c0f12)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-09 01:22:16 +00:00
JustArchi
641766a828 Update config.yml 2020-06-08 18:04:31 +02:00
JustArchi
452b551e88 Create config.yml 2020-06-08 18:03:21 +02:00
JustArchi
d31fb4ff2b Bump 2020-06-08 16:11:14 +02:00
44 changed files with 1808 additions and 947 deletions

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: ASF-ui issue
url: https://github.com/JustArchiNET/ASF-ui/issues/new/choose
about: Please open issue in ASF-ui repo.
- name: Localization improvement
url: https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Localization
about: Please use our crowdin platform.
- name: Questions and technical issues
url: https://github.com/JustArchiNET/ArchiSteamFarm/blob/master/SUPPORT.md
about: Please review our support guidelines.
- name: Negative feedback, complaints and demands
url: https://www.youtube.com/watch?v=dQw4w9WgXcQ
about: We're taking those very seriously.

View File

@@ -10,6 +10,8 @@ env:
NET_CORE_VERSION: netcoreapp3.1
NET_FRAMEWORK_VERSION: net48
NODE_JS_VERSION: 12
STEAM_TOKEN_DUMPER_NAME: ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
STEAM_TOKEN_DUMPER_TOKEN: ${{ secrets.STEAM_TOKEN_DUMPER_TOKEN }}
jobs:
build:
@@ -51,21 +53,50 @@ jobs:
run: npm run-script build:ci --no-progress --prefix ASF-ui
- name: Build ArchiSteamFarm
run: dotnet build ArchiSteamFarm -c "${{ env.CONFIGURATION }}" -f "${{ env.NET_CORE_VERSION }}" -p:UseAppHost=false --nologo
run: dotnet build ArchiSteamFarm -c "${{ env.CONFIGURATION }}" -p:UseAppHost=false --nologo
- name: Build ArchiSteamFarm.CustomPlugins.ExamplePlugin
run: dotnet build ArchiSteamFarm.CustomPlugins.ExamplePlugin -c "${{ env.CONFIGURATION }}" -f "${{ env.NET_CORE_VERSION }}" -p:UseAppHost=false --nologo
run: dotnet build ArchiSteamFarm.CustomPlugins.ExamplePlugin -c "${{ env.CONFIGURATION }}" -p:UseAppHost=false --nologo
- name: Build ArchiSteamFarm.CustomPlugins.PeriodicGC
run: dotnet build ArchiSteamFarm.CustomPlugins.PeriodicGC -c "${{ env.CONFIGURATION }}" -f "${{ env.NET_CORE_VERSION }}" -p:UseAppHost=false --nologo
run: dotnet build ArchiSteamFarm.CustomPlugins.PeriodicGC -c "${{ env.CONFIGURATION }}" -p:UseAppHost=false --nologo
- name: Build ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
run: dotnet build ArchiSteamFarm.OfficialPlugins.SteamTokenDumper -c "${{ env.CONFIGURATION }}" -p:UseAppHost=false --nologo
- name: Run ArchiSteamFarm.Tests
run: dotnet test ArchiSteamFarm.Tests -c "${{ env.CONFIGURATION }}" -f "${{ env.NET_CORE_VERSION }}" -p:UseAppHost=false --nologo
run: dotnet test ArchiSteamFarm.Tests -c "${{ env.CONFIGURATION }}" -p:UseAppHost=false --nologo
- name: Perform cleanup in preparation for publishing
run: dotnet clean ArchiSteamFarm -c "${{ env.CONFIGURATION }}" -f "${{ env.NET_CORE_VERSION }}" -p:UseAppHost=false --nologo
- name: Prepare ArchiSteamFarm.OfficialPlugins.SteamTokenDumper on Unix
if: startsWith(matrix.os, 'macos-') || startsWith(matrix.os, 'ubuntu-')
shell: sh
run: |
if [ -n "${STEAM_TOKEN_DUMPER_TOKEN-}" ] && [ -f "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs" ]; then
sed "s/STEAM_TOKEN_DUMPER_TOKEN/${STEAM_TOKEN_DUMPER_TOKEN}/g" "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs" > "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs.new";
mv "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs.new" "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs"
fi
- name: Prepare ArchiSteamFarm.OfficialPlugins.SteamTokenDumper on Windows
if: startsWith(matrix.os, 'windows-')
shell: pwsh
run: |
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
- name: Restore packages in preparation for publishing
if ((Test-Path env:STEAM_TOKEN_DUMPER_TOKEN) -and (Test-Path 'ArchiSteamFarm.OfficialPlugins.SteamTokenDumper\SharedInfo.cs' -PathType Leaf)) {
(Get-Content 'ArchiSteamFarm.OfficialPlugins.SteamTokenDumper\SharedInfo.cs').Replace('STEAM_TOKEN_DUMPER_TOKEN', "$env:STEAM_TOKEN_DUMPER_TOKEN") | Set-Content 'ArchiSteamFarm.OfficialPlugins.SteamTokenDumper\SharedInfo.cs'
}
- name: Publish ArchiSteamFarm.OfficialPlugins.SteamTokenDumper for .NET Core
run: dotnet publish "${{ env.STEAM_TOKEN_DUMPER_NAME }}" -c "${{ env.CONFIGURATION }}" -f "${{ env.NET_CORE_VERSION }}" -o "out/${{ env.STEAM_TOKEN_DUMPER_NAME }}/${{ env.NET_CORE_VERSION }}" -p:UseAppHost=false --nologo
- name: Publish ArchiSteamFarm.OfficialPlugins.SteamTokenDumper for .NET Framework
if: startsWith(matrix.os, 'windows-')
run: dotnet publish "${{ env.STEAM_TOKEN_DUMPER_NAME }}" -c "${{ env.CONFIGURATION }}" -f "${{ env.NET_FRAMEWORK_VERSION }}" -o "out/${{ env.STEAM_TOKEN_DUMPER_NAME }}/${{ env.NET_FRAMEWORK_VERSION }}" -p:UseAppHost=false --nologo
- name: Perform cleanup of ArchiSteamFarm in preparation for publishing
run: dotnet clean ArchiSteamFarm -c "${{ env.CONFIGURATION }}" -p:UseAppHost=false --nologo
- name: Restore packages in preparation for ArchiSteamFarm publishing
run: dotnet restore ArchiSteamFarm
- name: Publish ArchiSteamFarm on Unix
@@ -85,11 +116,17 @@ jobs:
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/${1}" "-p:ASFVariant=$1" --no-restore --nologo $variantArgs
# If we include any overlay for this variant, copy it to output directory
# If we're including any overlay for this variant, copy it to output directory
if [ -d "ArchiSteamFarm/overlay/${1}" ]; then
cp "ArchiSteamFarm/overlay/${1}/"* "out/${1}"
fi
# If we're including SteamTokenDumper plugin for this framework, copy it to output directory
if [ -f "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" ]; then
mkdir -p "out/${1}/plugins/${STEAM_TOKEN_DUMPER_NAME}"
cp "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" "out/${1}/plugins/${STEAM_TOKEN_DUMPER_NAME}"
fi
# Include .ico file for all platforms, since only Windows script can bundle it inside the exe
cp "resources/ASF.ico" "out/${1}/ArchiSteamFarm.ico"
@@ -116,7 +153,6 @@ jobs:
for job in $jobs; do
wait "$job"
done
- name: Publish ArchiSteamFarm on Windows
if: startsWith(matrix.os, 'windows-')
env:
@@ -154,11 +190,20 @@ jobs:
throw "Last command failed."
}
# If we include any overlay for this variant, copy it output directory
# If we're including any overlay for this variant, copy it to output directory
if (Test-Path "ArchiSteamFarm\overlay\$variant" -PathType Container) {
Copy-Item "ArchiSteamFarm\overlay\$variant\*" "out\$variant"
}
# If we're including SteamTokenDumper plugin for this framework, copy it to output directory
if (Test-Path "out\$env:STEAM_TOKEN_DUMPER_NAME\$targetFramework\$env:STEAM_TOKEN_DUMPER_NAME.dll" -PathType Leaf) {
if (!(Test-Path "out\$variant\plugins\$env:STEAM_TOKEN_DUMPER_NAME" -PathType Container)) {
New-Item -ItemType Directory -Path "out\$variant\plugins\$env:STEAM_TOKEN_DUMPER_NAME" > $null
}
Copy-Item "out\$env:STEAM_TOKEN_DUMPER_NAME\$targetFramework\$env:STEAM_TOKEN_DUMPER_NAME.dll" "out\$variant\plugins\$env:STEAM_TOKEN_DUMPER_NAME"
}
# Until https://github.com/dotnet/cli/issues/3267 happens, we'll hack dotnet binary icon on Windows and include .ico file on other platforms
if ($targetFramework -ne "$env:NET_FRAMEWORK_VERSION") {
if (!(Test-Path "out\$variant\ArchiSteamFarm.exe" -PathType Leaf)) {
@@ -178,7 +223,6 @@ jobs:
}
Get-Job | Receive-Job -Wait -AutoRemoveJob
- name: Upload ASF-generic
continue-on-error: true
uses: actions/upload-artifact@v2

View File

@@ -16,6 +16,7 @@ env:
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
- NET_CORE_VERSION: netcoreapp3.1
- STEAM_TOKEN_DUMPER_NAME: ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
- VARIANTS="generic linux-arm linux-arm64 linux-x64 osx-x64 win-x64" # NOTE: When modifying variants, don't forget to update ASF_VARIANT definitions in SharedInfo.cs!
addons:
homebrew:
@@ -34,12 +35,20 @@ script:
npm ci --no-progress --prefix ASF-ui
npm run-script deploy --no-progress --prefix ASF-ui
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:UseAppHost=false --nologo
dotnet build ArchiSteamFarm.CustomPlugins.ExamplePlugin -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:UseAppHost=false --nologo
dotnet build ArchiSteamFarm.CustomPlugins.PeriodicGC -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:UseAppHost=false --nologo
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:UseAppHost=false --nologo
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -p:UseAppHost=false --nologo
dotnet build ArchiSteamFarm.CustomPlugins.ExamplePlugin -c "$CONFIGURATION" -p:UseAppHost=false --nologo
dotnet build ArchiSteamFarm.CustomPlugins.PeriodicGC -c "$CONFIGURATION" -p:UseAppHost=false --nologo
dotnet build ArchiSteamFarm.OfficialPlugins.SteamTokenDumper -c "$CONFIGURATION" -p:UseAppHost=false --nologo
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -p:UseAppHost=false --nologo
dotnet clean ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:UseAppHost=false --nologo
if [ -n "${STEAM_TOKEN_DUMPER_TOKEN-}" ] && [ -f "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs" ]; then
sed "s/STEAM_TOKEN_DUMPER_TOKEN/${STEAM_TOKEN_DUMPER_TOKEN}/g" "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs" > "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs.new";
mv "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs.new" "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper/SharedInfo.cs"
fi
dotnet publish "$STEAM_TOKEN_DUMPER_NAME" -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}" -p:UseAppHost=false --nologo
dotnet clean ArchiSteamFarm -c "$CONFIGURATION" -p:UseAppHost=false --nologo
dotnet restore ArchiSteamFarm
publish() {
@@ -51,11 +60,17 @@ script:
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/${1}" "-p:ASFVariant=$1" --no-restore --nologo $variantArgs
# If we include any overlay for this variant, copy it to output directory
# If we're including any overlay for this variant, copy it to output directory
if [ -d "ArchiSteamFarm/overlay/${1}" ]; then
cp "ArchiSteamFarm/overlay/${1}/"* "out/${1}"
fi
# If we're including SteamTokenDumper plugin for this framework, copy it to output directory
if [ -f "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" ]; then
mkdir -p "out/${1}/plugins/${STEAM_TOKEN_DUMPER_NAME}"
cp "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" "out/${1}/plugins/${STEAM_TOKEN_DUMPER_NAME}"
fi
# Include .ico file for all platforms, since only Windows script can bundle it inside the exe
cp "resources/ASF.ico" "out/${1}/ArchiSteamFarm.ico"

2
ASF-ui

Submodule ASF-ui updated: 810095a335...17c5a777b9

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="4.0.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ArchiSteamFarm\ArchiSteamFarm.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,294 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ArchiSteamFarm.Collections;
using ArchiSteamFarm.Helpers;
using JetBrains.Annotations;
using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper {
internal sealed class GlobalCache : SerializableFile {
[NotNull]
private static string SharedFilePath => Path.Combine(ArchiSteamFarm.SharedInfo.ConfigDirectory, nameof(SteamTokenDumper) + ".cache");
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, uint> AppChangeNumbers = new ConcurrentDictionary<uint, uint>();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, ulong> AppTokens = new ConcurrentDictionary<uint, ulong>();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, string> DepotKeys = new ConcurrentDictionary<uint, string>();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, ulong> PackageTokens = new ConcurrentDictionary<uint, ulong>();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentHashSet<uint> SubmittedAppIDs = new ConcurrentHashSet<uint>();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentHashSet<uint> SubmittedDepotIDs = new ConcurrentHashSet<uint>();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentHashSet<uint> SubmittedPackageIDs = new ConcurrentHashSet<uint>();
[JsonProperty(Required = Required.DisallowNull)]
internal uint LastChangeNumber { get; private set; }
internal GlobalCache() => FilePath = SharedFilePath;
internal ulong GetAppToken(uint appID) => AppTokens[appID];
[NotNull]
internal Dictionary<uint, ulong> GetAppTokensForSubmission() => AppTokens.Where(appToken => !SubmittedAppIDs.Contains(appToken.Key)).ToDictionary(appToken => appToken.Key, appToken => appToken.Value);
[NotNull]
internal Dictionary<uint, string> GetDepotKeysForSubmission() => DepotKeys.Where(depotKey => !SubmittedDepotIDs.Contains(depotKey.Key)).ToDictionary(depotKey => depotKey.Key, depotKey => depotKey.Value);
[NotNull]
internal Dictionary<uint, ulong> GetPackageTokensForSubmission() => PackageTokens.Where(packageToken => !SubmittedPackageIDs.Contains(packageToken.Key)).ToDictionary(packageToken => packageToken.Key, packageToken => packageToken.Value);
[ItemNotNull]
internal static async Task<GlobalCache> Load() {
if (!File.Exists(SharedFilePath)) {
return new GlobalCache();
}
GlobalCache globalCache = null;
try {
string json = await RuntimeCompatibility.File.ReadAllTextAsync(SharedFilePath).ConfigureAwait(false);
if (!string.IsNullOrEmpty(json)) {
globalCache = JsonConvert.DeserializeObject<GlobalCache>(json);
}
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
}
if (globalCache == null) {
ASF.ArchiLogger.LogGenericError($"{nameof(GlobalCache)} could not be loaded, a fresh instance will be initialized.");
globalCache = new GlobalCache();
}
return globalCache;
}
internal async Task OnPICSChanges(uint currentChangeNumber, [NotNull] IReadOnlyCollection<KeyValuePair<uint, SteamApps.PICSChangesCallback.PICSChangeData>> appChanges) {
if ((currentChangeNumber == 0) || (appChanges == null)) {
throw new ArgumentNullException(nameof(appChanges));
}
if (currentChangeNumber <= LastChangeNumber) {
return;
}
ASF.ArchiLogger.LogGenericTrace($"{LastChangeNumber} => {currentChangeNumber}");
LastChangeNumber = currentChangeNumber;
foreach ((uint appID, SteamApps.PICSChangesCallback.PICSChangeData appData) in appChanges) {
if (!AppChangeNumbers.TryGetValue(appID, out uint previousChangeNumber) || (appData.ChangeNumber <= previousChangeNumber)) {
continue;
}
AppChangeNumbers.TryRemove(appID, out _);
ASF.ArchiLogger.LogGenericTrace($"App needs refresh: {appID}");
}
await Save().ConfigureAwait(false);
}
internal async Task OnPICSChangesRestart(uint currentChangeNumber) {
if (currentChangeNumber == 0) {
throw new ArgumentNullException();
}
if (currentChangeNumber <= LastChangeNumber) {
return;
}
ASF.ArchiLogger.LogGenericDebug($"RESET {LastChangeNumber} => {currentChangeNumber}");
LastChangeNumber = currentChangeNumber;
AppChangeNumbers.Clear();
await Save().ConfigureAwait(false);
}
internal bool ShouldRefreshAppInfo(uint appID) => !AppChangeNumbers.ContainsKey(appID);
internal bool ShouldRefreshDepotKey(uint depotID) => !DepotKeys.ContainsKey(depotID);
internal async Task UpdateAppChangeNumbers([NotNull] IReadOnlyCollection<KeyValuePair<uint, uint>> appChangeNumbers) {
if (appChangeNumbers == null) {
throw new ArgumentNullException(nameof(appChangeNumbers));
}
bool save = false;
foreach ((uint appID, uint changeNumber) in appChangeNumbers) {
if (AppChangeNumbers.TryGetValue(appID, out uint previousChangeNumber) && (previousChangeNumber == changeNumber)) {
continue;
}
AppChangeNumbers[appID] = changeNumber;
save = true;
}
if (save) {
await Save().ConfigureAwait(false);
}
}
internal async Task UpdateAppTokens([NotNull] IReadOnlyCollection<KeyValuePair<uint, ulong>> appTokens, [NotNull] IReadOnlyCollection<uint> publicAppIDs) {
if ((appTokens == null) || (publicAppIDs == null)) {
throw new ArgumentNullException(nameof(appTokens) + " || " + nameof(publicAppIDs));
}
bool save = false;
foreach ((uint appID, ulong appToken) in appTokens) {
if (AppTokens.TryGetValue(appID, out ulong previousAppToken) && (previousAppToken == appToken)) {
continue;
}
AppTokens[appID] = appToken;
if (appToken == 0) {
// Backend is not interested in zero access tokens
SubmittedAppIDs.Add(appID);
}
save = true;
}
foreach (uint appID in publicAppIDs) {
if (AppTokens.TryGetValue(appID, out ulong previousAppToken) && (previousAppToken == 0)) {
continue;
}
AppTokens[appID] = 0;
// Backend is not interested in zero access tokens
SubmittedAppIDs.Add(appID);
save = true;
}
if (save) {
await Save().ConfigureAwait(false);
}
}
internal async Task UpdateDepotKeys([NotNull] IReadOnlyCollection<SteamApps.DepotKeyCallback> depotKeyResults) {
if (depotKeyResults == null) {
throw new ArgumentNullException(nameof(depotKeyResults));
}
bool save = false;
foreach (SteamApps.DepotKeyCallback depotKeyResult in depotKeyResults) {
if ((depotKeyResult == null) || (depotKeyResult.Result != EResult.OK)) {
continue;
}
string depotKey = BitConverter.ToString(depotKeyResult.DepotKey).Replace("-", "");
if (DepotKeys.TryGetValue(depotKeyResult.DepotID, out string previousDepotKey) && (previousDepotKey == depotKey)) {
continue;
}
DepotKeys[depotKeyResult.DepotID] = depotKey;
if (string.IsNullOrEmpty(depotKey)) {
// Backend is not interested in zero depot keys
SubmittedDepotIDs.Add(depotKeyResult.DepotID);
}
save = true;
}
if (save) {
await Save().ConfigureAwait(false);
}
}
internal async Task UpdatePackageTokens([NotNull] IReadOnlyCollection<KeyValuePair<uint, ulong>> packageTokens) {
if (packageTokens == null) {
throw new ArgumentNullException(nameof(packageTokens));
}
bool save = false;
foreach ((uint packageID, ulong packageToken) in packageTokens) {
if (PackageTokens.TryGetValue(packageID, out ulong previousPackageToken) && (previousPackageToken == packageToken)) {
continue;
}
PackageTokens[packageID] = packageToken;
if (packageToken == 0) {
// Backend is not interested in zero access tokens
SubmittedPackageIDs.Add(packageID);
}
save = true;
}
if (save) {
await Save().ConfigureAwait(false);
}
}
internal async Task UpdateSubmittedData([NotNull] IReadOnlyCollection<uint> appIDs, [NotNull] IReadOnlyCollection<uint> packageIDs, [NotNull] IReadOnlyCollection<uint> depotIDs) {
if ((appIDs == null) || (packageIDs == null) || (depotIDs == null)) {
throw new ArgumentNullException(nameof(appIDs) + " || " + nameof(packageIDs) + " || " + nameof(depotIDs));
}
bool save = false;
foreach (uint _ in appIDs.Where(appID => SubmittedAppIDs.Add(appID))) {
save = true;
}
foreach (uint _ in packageIDs.Where(packageID => SubmittedPackageIDs.Add(packageID))) {
save = true;
}
foreach (uint _ in depotIDs.Where(depotID => SubmittedDepotIDs.Add(depotID))) {
save = true;
}
if (save) {
await Save().ConfigureAwait(false);
}
}
}
}

View File

@@ -0,0 +1,81 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using JetBrains.Annotations;
using Newtonsoft.Json;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper {
internal sealed class RequestData {
#pragma warning disable IDE0052
[JsonProperty(PropertyName = "apps", Required = Required.Always)]
private readonly ImmutableDictionary<string, string> Apps;
#pragma warning restore IDE0052
#pragma warning disable IDE0052
[JsonProperty(PropertyName = "depots", Required = Required.Always)]
private readonly ImmutableDictionary<string, string> Depots;
#pragma warning restore IDE0052
#pragma warning disable IDE0052
[JsonProperty(PropertyName = "guid", Required = Required.Always)]
private readonly string Guid = ASF.GlobalDatabase.Guid.ToString("N");
#pragma warning restore IDE0052
private readonly ulong SteamID;
#pragma warning disable IDE0052
[JsonProperty(PropertyName = "subs", Required = Required.Always)]
private readonly ImmutableDictionary<string, string> Subs;
#pragma warning restore IDE0052
#pragma warning disable IDE0051, 414
[JsonProperty(PropertyName = "token", Required = Required.Always)]
private readonly string Token = SharedInfo.Token;
#pragma warning restore IDE0051, 414
#pragma warning disable IDE0051, 414
[JsonProperty(PropertyName = "v", Required = Required.Always)]
private readonly byte Version = SharedInfo.ApiVersion;
#pragma warning restore IDE0051, 414
#pragma warning disable IDE0051
[JsonProperty(PropertyName = "steamid", Required = Required.Always)]
[NotNull]
private string SteamIDText => new SteamID(SteamID).Render();
#pragma warning restore IDE0051
internal RequestData(ulong steamID, [NotNull] IEnumerable<KeyValuePair<uint, ulong>> apps, [NotNull] IEnumerable<KeyValuePair<uint, ulong>> accessTokens, [NotNull] IEnumerable<KeyValuePair<uint, string>> depots) {
if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount || (apps == null) || (accessTokens == null) || (depots == null)) {
throw new ArgumentNullException(nameof(steamID) + " || " + nameof(apps) + " || " + nameof(accessTokens) + " || " + nameof(depots));
}
SteamID = steamID;
Apps = apps.ToImmutableDictionary(app => app.Key.ToString(), app => app.Value.ToString());
Subs = accessTokens.ToImmutableDictionary(package => package.Key.ToString(), package => package.Value.ToString());
Depots = depots.ToImmutableDictionary(depot => depot.Key.ToString(), depot => depot.Value);
}
}
}

View File

@@ -0,0 +1,61 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Diagnostics.CodeAnalysis;
using Newtonsoft.Json;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
internal sealed class ResponseData {
#pragma warning disable 649
[JsonProperty(PropertyName = "data", Required = Required.Always)]
internal readonly InternalData Data;
#pragma warning restore 649
#pragma warning disable 649
[JsonProperty(PropertyName = "success", Required = Required.Always)]
internal readonly bool Success;
#pragma warning restore 649
[JsonConstructor]
private ResponseData() { }
internal sealed class InternalData {
#pragma warning disable 649
[JsonProperty(PropertyName = "new_apps", Required = Required.Always)]
internal readonly uint NewAppsCount;
#pragma warning restore 649
#pragma warning disable 649
[JsonProperty(PropertyName = "new_depots", Required = Required.Always)]
internal readonly uint NewDepotsCount;
#pragma warning restore 649
#pragma warning disable 649
[JsonProperty(PropertyName = "new_subs", Required = Required.Always)]
internal readonly uint NewSubsCount;
#pragma warning restore 649
[JsonConstructor]
private InternalData() { }
}
}
}

View File

@@ -0,0 +1,37 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper {
internal static class SharedInfo {
internal const byte ApiVersion = 1;
internal const string ConfigurationPropertyEnabled = nameof(SteamTokenDumperPlugin) + "Enabled";
internal const ushort ItemsPerSingleRequest = 2048; // Should be synchronized with TimeoutForLongRunningTasksInSeconds
internal const byte MaximumHoursBetweenRefresh = 8; // Per single bot account, makes sense to be 2 or 3 times less than MinimumHoursBetweenUploads
internal const byte MaximumMinutesBeforeFirstUpload = 60; // Must be greater or equal to MinimumMinutesBeforeFirstUpload
internal const byte MinimumHoursBetweenUploads = 24;
internal const byte MinimumMinutesBeforeFirstUpload = 10; // Must be less or equal to MaximumMinutesBeforeFirstUpload
internal const string ServerURL = "https://asf-token-dumper.xpaw.me";
internal const byte TimeoutForLongRunningTasksInSeconds = 60; // Should be synchronized with ItemsPerSingleRequest
internal const string Token = "STEAM_TOKEN_DUMPER_TOKEN";
internal static bool HasValidToken => Token.Length == 128;
}
}

View File

@@ -0,0 +1,40 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper {
internal static class StaticHelpers {
[NotNull]
internal static Task<T> ToLongRunningTask<T>([NotNull] this AsyncJob<T> job) where T : CallbackMsg {
if (job == null) {
throw new ArgumentNullException(nameof(job));
}
job.Timeout = TimeSpan.FromSeconds(SharedInfo.TimeoutForLongRunningTasksInSeconds);
return job.ToTask();
}
}
}

View File

@@ -0,0 +1,432 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Plugins;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using SteamKit2;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper {
[Export(typeof(IPlugin))]
[UsedImplicitly]
internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotSteamClient, ISteamPICSChanges {
private static readonly ConcurrentDictionary<Bot, IDisposable> BotSubscriptions = new ConcurrentDictionary<Bot, IDisposable>();
private static readonly ConcurrentDictionary<Bot, (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer)> BotSynchronizations = new ConcurrentDictionary<Bot, (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer)>();
private static readonly SemaphoreSlim SubmissionSemaphore = new SemaphoreSlim(1, 1);
private static readonly Timer SubmissionTimer = new Timer(async e => await SubmitData().ConfigureAwait(false));
private static GlobalCache GlobalCache;
private static bool IsEnabled;
public override string Name => nameof(SteamTokenDumperPlugin);
public override Version Version => typeof(SteamTokenDumperPlugin).Assembly.GetName().Version ?? throw new ArgumentNullException(nameof(Version));
public Task<uint> GetPreferredChangeNumberToStartFrom() => Task.FromResult(IsEnabled ? GlobalCache?.LastChangeNumber ?? 0 : 0);
public void OnASFInit(IReadOnlyDictionary<string, JToken> additionalConfigProperties = null) {
if (!SharedInfo.HasValidToken) {
ASF.ArchiLogger.LogGenericError($"{Name} has been disabled due to missing build token.");
return;
}
bool enabled = false;
if (additionalConfigProperties != null) {
foreach ((string configProperty, JToken configValue) in additionalConfigProperties) {
try {
if (configProperty == SharedInfo.ConfigurationPropertyEnabled) {
enabled = configValue.Value<bool>();
}
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
break;
}
}
}
IsEnabled = enabled;
if (!enabled) {
ASF.ArchiLogger.LogGenericInfo($"{Name} is currently disabled. If you'd like to help SteamDB in data submission, check out our wiki for {nameof(SteamTokenDumperPlugin)}.");
return;
}
GlobalCache ??= GlobalCache.Load().Result;
TimeSpan startIn = TimeSpan.FromMinutes(Utilities.RandomNext(SharedInfo.MinimumMinutesBeforeFirstUpload, SharedInfo.MaximumMinutesBeforeFirstUpload));
lock (SubmissionTimer) {
SubmissionTimer.Change(startIn, TimeSpan.FromHours(SharedInfo.MinimumHoursBetweenUploads));
}
ASF.ArchiLogger.LogGenericInfo($"{Name} has been initialized successfully, thank you for your help. The first submission will happen in approximately {startIn.ToHumanReadable()} from now.");
}
public async void OnBotDestroy(Bot bot) {
if (bot == null) {
throw new ArgumentNullException(nameof(bot));
}
if (BotSubscriptions.TryRemove(bot, out IDisposable subscription)) {
subscription.Dispose();
}
if (BotSynchronizations.TryRemove(bot, out (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer) synchronization)) {
synchronization.RefreshSemaphore.Dispose();
await synchronization.RefreshTimer.DisposeAsync().ConfigureAwait(false);
}
}
public async void OnBotInit(Bot bot) {
if (bot == null) {
throw new ArgumentNullException(nameof(bot));
}
if (!IsEnabled) {
return;
}
SemaphoreSlim refreshSemaphore = new SemaphoreSlim(1, 1);
Timer refreshTimer = new Timer(async e => await Refresh(bot).ConfigureAwait(false));
if (!BotSynchronizations.TryAdd(bot, (refreshSemaphore, refreshTimer))) {
refreshSemaphore.Dispose();
await refreshTimer.DisposeAsync().ConfigureAwait(false);
}
}
public void OnBotSteamCallbacksInit(Bot bot, CallbackManager callbackManager) {
if ((bot == null) || (callbackManager == null)) {
throw new ArgumentNullException(nameof(bot) + " || " + nameof(callbackManager));
}
if (BotSubscriptions.TryRemove(bot, out IDisposable subscription)) {
subscription.Dispose();
}
if (!IsEnabled) {
return;
}
subscription = callbackManager.Subscribe<SteamApps.LicenseListCallback>(callback => OnLicenseList(bot, callback));
if (!BotSubscriptions.TryAdd(bot, subscription)) {
subscription.Dispose();
}
}
public IReadOnlyCollection<ClientMsgHandler> OnBotSteamHandlersInit(Bot bot) => null;
public override void OnLoaded() { }
public async void OnPICSChanges(uint currentChangeNumber, IReadOnlyDictionary<uint, SteamApps.PICSChangesCallback.PICSChangeData> appChanges, IReadOnlyDictionary<uint, SteamApps.PICSChangesCallback.PICSChangeData> packageChanges) {
if ((currentChangeNumber == 0) || (appChanges == null) || (packageChanges == null)) {
throw new ArgumentNullException(nameof(currentChangeNumber) + " || " + nameof(appChanges) + " || " + nameof(packageChanges));
}
if (!IsEnabled) {
return;
}
if (GlobalCache == null) {
throw new ArgumentNullException(nameof(GlobalCache));
}
await GlobalCache.OnPICSChanges(currentChangeNumber, appChanges).ConfigureAwait(false);
}
public async void OnPICSChangesRestart(uint currentChangeNumber) {
if (currentChangeNumber == 0) {
throw new ArgumentNullException(nameof(currentChangeNumber));
}
if (!IsEnabled) {
return;
}
if (GlobalCache == null) {
throw new ArgumentNullException(nameof(GlobalCache));
}
await GlobalCache.OnPICSChangesRestart(currentChangeNumber).ConfigureAwait(false);
}
private static async void OnLicenseList([NotNull] Bot bot, [NotNull] SteamApps.LicenseListCallback callback) {
if ((bot == null) || (callback == null)) {
throw new ArgumentNullException(nameof(callback));
}
if (!IsEnabled) {
return;
}
if (GlobalCache == null) {
throw new ArgumentNullException(nameof(GlobalCache));
}
Dictionary<uint, ulong> packageTokens = callback.LicenseList.GroupBy(license => license.PackageID).ToDictionary(group => group.Key, group => group.OrderByDescending(license => license.TimeCreated).First().AccessToken);
await GlobalCache.UpdatePackageTokens(packageTokens).ConfigureAwait(false);
await Refresh(bot, packageTokens.Keys).ConfigureAwait(false);
}
private static async Task Refresh([NotNull] Bot bot, IReadOnlyCollection<uint> packageIDs = null) {
if (bot == null) {
throw new ArgumentNullException(nameof(bot));
}
if (!IsEnabled) {
return;
}
if (GlobalCache == null) {
throw new ArgumentNullException(nameof(GlobalCache));
}
if (!BotSynchronizations.TryGetValue(bot, out (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer) synchronization)) {
throw new ArgumentNullException(nameof(synchronization));
}
if (!await synchronization.RefreshSemaphore.WaitAsync(0).ConfigureAwait(false)) {
return;
}
try {
if (!bot.IsConnectedAndLoggedOn) {
return;
}
packageIDs ??= bot.OwnedPackageIDsReadOnly;
HashSet<uint> appIDsToRefresh = new HashSet<uint>();
foreach (uint packageID in packageIDs) {
if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(packageID, out (uint ChangeNumber, HashSet<uint> AppIDs) packageData) || (packageData.AppIDs == null)) {
// ASF might not have the package info for us at the moment, we'll retry later
continue;
}
appIDsToRefresh.UnionWith(packageData.AppIDs.Where(appID => GlobalCache.ShouldRefreshAppInfo(appID)));
}
if (appIDsToRefresh.Count == 0) {
bot.ArchiLogger.LogGenericDebug($"There are no apps to refresh for {bot.BotName}.");
return;
}
bot.ArchiLogger.LogGenericInfo($"Retrieving a total of {appIDsToRefresh.Count} app access tokens...");
HashSet<uint> appIDsThisRound = new HashSet<uint>(Math.Min(appIDsToRefresh.Count, SharedInfo.ItemsPerSingleRequest));
using (HashSet<uint>.Enumerator enumerator = appIDsToRefresh.GetEnumerator()) {
while (true) {
while ((appIDsThisRound.Count < SharedInfo.ItemsPerSingleRequest) && enumerator.MoveNext()) {
appIDsThisRound.Add(enumerator.Current);
}
if (appIDsThisRound.Count == 0) {
break;
}
bot.ArchiLogger.LogGenericInfo($"Retrieving {appIDsThisRound.Count} app access tokens...");
SteamApps.PICSTokensCallback response;
try {
response = await bot.SteamApps.PICSGetAccessTokens(appIDsThisRound, Enumerable.Empty<uint>());
} catch (Exception e) {
bot.ArchiLogger.LogGenericWarningException(e);
return;
}
bot.ArchiLogger.LogGenericInfo($"Finished retrieving {appIDsThisRound.Count} app access tokens.");
appIDsThisRound.Clear();
await GlobalCache.UpdateAppTokens(response.AppTokens, response.AppTokensDenied).ConfigureAwait(false);
}
}
bot.ArchiLogger.LogGenericInfo($"Finished retrieving a total of {appIDsToRefresh.Count} app access tokens.");
bot.ArchiLogger.LogGenericInfo($"Retrieving all depots for a total of {appIDsToRefresh.Count} apps...");
appIDsThisRound.Clear();
using (HashSet<uint>.Enumerator enumerator = appIDsToRefresh.GetEnumerator()) {
while (true) {
while ((appIDsThisRound.Count < SharedInfo.ItemsPerSingleRequest) && enumerator.MoveNext()) {
appIDsThisRound.Add(enumerator.Current);
}
if (appIDsThisRound.Count == 0) {
break;
}
bot.ArchiLogger.LogGenericInfo($"Retrieving {appIDsThisRound.Count} app infos...");
AsyncJobMultiple<SteamApps.PICSProductInfoCallback>.ResultSet response;
try {
response = await bot.SteamApps.PICSGetProductInfo(appIDsThisRound.Select(appID => new SteamApps.PICSRequest { ID = appID, AccessToken = GlobalCache.GetAppToken(appID), Public = false }), Enumerable.Empty<SteamApps.PICSRequest>());
} catch (Exception e) {
bot.ArchiLogger.LogGenericWarningException(e);
return;
}
if (response.Results == null) {
bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningFailedWithError, nameof(response.Results)));
return;
}
bot.ArchiLogger.LogGenericInfo($"Finished retrieving {appIDsThisRound.Count} app infos.");
appIDsThisRound.Clear();
Dictionary<uint, uint> appChangeNumbers = new Dictionary<uint, uint>();
HashSet<Task<SteamApps.DepotKeyCallback>> depotTasks = new HashSet<Task<SteamApps.DepotKeyCallback>>();
foreach (SteamApps.PICSProductInfoCallback.PICSProductInfo app in response.Results.SelectMany(result => result.Apps.Values)) {
appChangeNumbers[app.ID] = app.ChangeNumber;
if (GlobalCache.ShouldRefreshDepotKey(app.ID)) {
depotTasks.Add(bot.SteamApps.GetDepotDecryptionKey(app.ID, app.ID).ToLongRunningTask());
}
foreach (KeyValue depot in app.KeyValues["depots"].Children) {
if (uint.TryParse(depot.Name, out uint depotID) && GlobalCache.ShouldRefreshDepotKey(depotID)) {
depotTasks.Add(bot.SteamApps.GetDepotDecryptionKey(depotID, app.ID).ToLongRunningTask());
}
}
}
await GlobalCache.UpdateAppChangeNumbers(appChangeNumbers).ConfigureAwait(false);
if (depotTasks.Count > 0) {
bot.ArchiLogger.LogGenericInfo($"Retrieving {depotTasks.Count} depot keys...");
SteamApps.DepotKeyCallback[] results = await Task.WhenAll(depotTasks).ConfigureAwait(false);
bot.ArchiLogger.LogGenericInfo($"Finished retrieving {depotTasks.Count} depot keys.");
await GlobalCache.UpdateDepotKeys(results).ConfigureAwait(false);
}
}
}
bot.ArchiLogger.LogGenericInfo($"Finished retrieving all depot keys for a total of {appIDsToRefresh.Count} apps.");
} finally {
TimeSpan timeSpan = TimeSpan.FromHours(SharedInfo.MaximumHoursBetweenRefresh);
synchronization.RefreshTimer.Change(timeSpan, timeSpan);
synchronization.RefreshSemaphore.Release();
}
}
private static async Task SubmitData() {
const string request = SharedInfo.ServerURL + "/submit";
if (!IsEnabled) {
return;
}
if (GlobalCache == null) {
throw new ArgumentNullException(nameof(GlobalCache));
}
if (!await SubmissionSemaphore.WaitAsync(0).ConfigureAwait(false)) {
ASF.ArchiLogger.LogGenericDebug($"Skipped {nameof(SubmitData)} trigger because there is already one in progress.");
return;
}
try {
Dictionary<uint, ulong> appTokens = GlobalCache.GetAppTokensForSubmission();
Dictionary<uint, ulong> packageTokens = GlobalCache.GetPackageTokensForSubmission();
Dictionary<uint, string> depotKeys = GlobalCache.GetDepotKeysForSubmission();
if ((appTokens.Count == 0) && (packageTokens.Count == 0) && (depotKeys.Count == 0)) {
ASF.ArchiLogger.LogGenericInfo("There is no new data to submit, everything up-to-date.");
return;
}
ulong contributorSteamID = (ASF.GlobalConfig.SteamOwnerID > 0) && new SteamID(ASF.GlobalConfig.SteamOwnerID).IsIndividualAccount ? ASF.GlobalConfig.SteamOwnerID : Bot.BotsReadOnly.Values.Where(bot => bot.SteamID > 0).OrderByDescending(bot => bot.OwnedPackageIDsReadOnly.Count).FirstOrDefault()?.SteamID ?? 0;
if (contributorSteamID == 0) {
ASF.ArchiLogger.LogGenericError($"Skipped {nameof(SubmitData)} trigger because there is no valid steamID we could classify as a contributor. Consider setting up {nameof(ASF.GlobalConfig.SteamOwnerID)} property.");
return;
}
RequestData requestData = new RequestData(contributorSteamID, appTokens, packageTokens, depotKeys);
ASF.ArchiLogger.LogGenericInfo($"Submitting registered apps/subs/depots: {appTokens.Count}/{packageTokens.Count}/{depotKeys.Count}...");
WebBrowser.ObjectResponse<ResponseData> response = await ASF.WebBrowser.UrlPostToJsonObject<ResponseData, RequestData>(request, requestData, requestOptions: WebBrowser.ERequestOptions.ReturnClientErrors).ConfigureAwait(false);
if ((response?.Content == null) || response.StatusCode.IsClientErrorCode()) {
ASF.ArchiLogger.LogGenericWarning(Strings.WarningFailed);
#if NETFRAMEWORK
if (response?.StatusCode == (HttpStatusCode) 429) {
#else
if (response?.StatusCode == HttpStatusCode.TooManyRequests) {
#endif
TimeSpan startIn = TimeSpan.FromMinutes(Utilities.RandomNext(SharedInfo.MinimumMinutesBeforeFirstUpload, SharedInfo.MaximumMinutesBeforeFirstUpload));
lock (SubmissionTimer) {
SubmissionTimer.Change(startIn, TimeSpan.FromHours(SharedInfo.MinimumHoursBetweenUploads));
}
ASF.ArchiLogger.LogGenericInfo($"The submission will happen in approximately {startIn.ToHumanReadable()} from now.");
}
return;
}
ASF.ArchiLogger.LogGenericInfo($"Data successfully submitted. Newly registered apps/subs/depots: {response.Content.Data.NewAppsCount}/{response.Content.Data.NewSubsCount}/{response.Content.Data.NewDepotsCount}.");
await GlobalCache.UpdateSubmittedData(appTokens.Keys, packageTokens.Keys, depotKeys.Keys).ConfigureAwait(false);
} finally {
SubmissionSemaphore.Release();
}
}
}
}

View File

@@ -9,7 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArchiSteamFarm.Tests", "Arc
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArchiSteamFarm.CustomPlugins.ExamplePlugin", "ArchiSteamFarm.CustomPlugins.ExamplePlugin\ArchiSteamFarm.CustomPlugins.ExamplePlugin.csproj", "{2E2C26B6-7C1D-4BAF-BCF9-79286DA08F82}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchiSteamFarm.CustomPlugins.PeriodicGC", "ArchiSteamFarm.CustomPlugins.PeriodicGC\ArchiSteamFarm.CustomPlugins.PeriodicGC.csproj", "{2C935C25-1B03-4C55-81C9-DF1D472D72F4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArchiSteamFarm.CustomPlugins.PeriodicGC", "ArchiSteamFarm.CustomPlugins.PeriodicGC\ArchiSteamFarm.CustomPlugins.PeriodicGC.csproj", "{2C935C25-1B03-4C55-81C9-DF1D472D72F4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper", "ArchiSteamFarm.OfficialPlugins.SteamTokenDumper\ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.csproj", "{A9299EE5-AF67-4FCB-8D03-263B41819504}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -33,12 +35,16 @@ Global
{2C935C25-1B03-4C55-81C9-DF1D472D72F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2C935C25-1B03-4C55-81C9-DF1D472D72F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2C935C25-1B03-4C55-81C9-DF1D472D72F4}.Release|Any CPU.Build.0 = Release|Any CPU
{A9299EE5-AF67-4FCB-8D03-263B41819504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9299EE5-AF67-4FCB-8D03-263B41819504}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9299EE5-AF67-4FCB-8D03-263B41819504}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9299EE5-AF67-4FCB-8D03-263B41819504}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D7D54143-C857-4B76-A219-0E98C5BC4895}
RESX_ShowErrorsInErrorList = False
SolutionGuid = {D7D54143-C857-4B76-A219-0E98C5BC4895}
EndGlobalSection
EndGlobal

View File

@@ -125,6 +125,10 @@ namespace ArchiSteamFarm {
await ArchiKestrel.Start().ConfigureAwait(false);
}
uint changeNumberToStartFrom = await PluginsCore.GetChangeNumberToStartFrom().ConfigureAwait(false);
SteamPICSChanges.Init(changeNumberToStartFrom);
await RegisterBots().ConfigureAwait(false);
InitEvents();
@@ -828,7 +832,7 @@ namespace ArchiSteamFarm {
Directory.CreateDirectory(targetBackupDirectory);
string targetBackupFile = Path.Combine(targetBackupDirectory, fileName);
File.Move(file, targetBackupFile);
RuntimeCompatibility.File.Move(file, targetBackupFile, true);
}
// We can now get rid of directories that are empty
@@ -844,8 +848,8 @@ namespace ArchiSteamFarm {
if (File.Exists(file)) {
// This is possible only with files that we decided to leave in place during our backup function
// Those files should never be overwritten with anything, ignore
continue;
string targetBackupFile = file + ".bak";
RuntimeCompatibility.File.Move(file, targetBackupFile, true);
}
// Check if this file requires its own folder

View File

@@ -35,7 +35,7 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.4" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.5" />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.7.0" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.7.0" />
</ItemGroup>
@@ -48,8 +48,8 @@
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.4" />
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="3.1.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="3.1.5" />
<Reference Include="System.Net.Http">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.8\System.Net.Http.dll</HintPath>
</Reference>

View File

@@ -22,3 +22,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("ArchiSteamFarm.Tests")]
[assembly: InternalsVisibleTo("ArchiSteamFarm.OfficialPlugins.SteamTokenDumper")]

View File

@@ -114,10 +114,15 @@ namespace ArchiSteamFarm {
[PublicAPI]
public bool IsPlayingPossible => !PlayingBlocked && !LibraryLocked;
[NotNull]
[JsonIgnore]
[PublicAPI]
public IReadOnlyCollection<uint> OwnedPackageIDsReadOnly => OwnedPackageIDs.Keys.ToHashSet();
internal readonly ArchiHandler ArchiHandler;
internal readonly BotDatabase BotDatabase;
internal readonly ConcurrentDictionary<uint, (EPaymentMethod PaymentMethod, DateTime TimeCreated, ulong AccessToken)> OwnedPackageIDs = new ConcurrentDictionary<uint, (EPaymentMethod PaymentMethod, DateTime TimeCreated, ulong AccessToken)>();
internal readonly ConcurrentDictionary<uint, (EPaymentMethod PaymentMethod, DateTime TimeCreated)> OwnedPackageIDs = new ConcurrentDictionary<uint, (EPaymentMethod PaymentMethod, DateTime TimeCreated)>();
internal bool CanReceiveSteamCards => !IsAccountLimited && !IsAccountLocked;
internal bool IsAccountLimited => AccountFlags.HasFlag(EAccountFlags.LimitedUser) || AccountFlags.HasFlag(EAccountFlags.LimitedUserForce);
@@ -130,7 +135,6 @@ namespace ArchiSteamFarm {
private readonly SemaphoreSlim InitializationSemaphore = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim MessagingSemaphore = new SemaphoreSlim(1, 1);
private readonly ConcurrentDictionary<ArchiHandler.UserNotificationsCallback.EUserNotification, uint> PastNotifications = new ConcurrentDictionary<ArchiHandler.UserNotificationsCallback.EUserNotification, uint>();
private readonly SemaphoreSlim PICSSemaphore = new SemaphoreSlim(1, 1);
private readonly Statistics Statistics;
private readonly SteamClient SteamClient;
private readonly ConcurrentHashSet<ulong> SteamFamilySharingIDs = new ConcurrentHashSet<ulong>();
@@ -306,7 +310,6 @@ namespace ArchiSteamFarm {
GamesRedeemerInBackgroundSemaphore.Dispose();
InitializationSemaphore.Dispose();
MessagingSemaphore.Dispose();
PICSSemaphore.Dispose();
Trading.Dispose();
await Actions.DisposeAsync().ConfigureAwait(false);
@@ -650,7 +653,7 @@ namespace ArchiSteamFarm {
DateTime mostRecent = DateTime.MinValue;
foreach (uint packageID in packageIDs) {
if (!OwnedPackageIDs.TryGetValue(packageID, out (EPaymentMethod PaymentMethod, DateTime TimeCreated, ulong AccessToken) packageData)) {
if (!OwnedPackageIDs.TryGetValue(packageID, out (EPaymentMethod PaymentMethod, DateTime TimeCreated) packageData)) {
continue;
}
@@ -671,14 +674,10 @@ namespace ArchiSteamFarm {
AsyncJobMultiple<SteamApps.PICSProductInfoCallback>.ResultSet productInfoResultSet = null;
for (byte i = 0; (i < WebBrowser.MaxTries) && (productInfoResultSet == null) && IsConnectedAndLoggedOn; i++) {
await PICSSemaphore.WaitAsync().ConfigureAwait(false);
try {
productInfoResultSet = await SteamApps.PICSGetProductInfo(appID, null, false);
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);
} finally {
PICSSemaphore.Release();
}
}
@@ -828,17 +827,27 @@ namespace ArchiSteamFarm {
return null;
}
HashSet<SteamApps.PICSRequest> packageRequests = new HashSet<SteamApps.PICSRequest>();
foreach (uint packageID in packageIDs) {
if (!ASF.GlobalDatabase.PackageAccessTokensReadOnly.TryGetValue(packageID, out ulong packageAccessToken)) {
continue;
}
packageRequests.Add(new SteamApps.PICSRequest(packageID, packageAccessToken, false));
}
if (packageRequests.Count == 0) {
return new Dictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)>(0);
}
AsyncJobMultiple<SteamApps.PICSProductInfoCallback>.ResultSet productInfoResultSet = null;
for (byte i = 0; (i < WebBrowser.MaxTries) && (productInfoResultSet == null) && IsConnectedAndLoggedOn; i++) {
await PICSSemaphore.WaitAsync().ConfigureAwait(false);
try {
productInfoResultSet = await SteamApps.PICSGetProductInfo(Enumerable.Empty<SteamApps.PICSRequest>(), packageIDs.Select(packageID => new SteamApps.PICSRequest(packageID, OwnedPackageIDs.TryGetValue(packageID, out (EPaymentMethod PaymentMethod, DateTime TimeCreated, ulong AccessToken) value) ? value.AccessToken : 0, false)));
productInfoResultSet = await SteamApps.PICSGetProductInfo(Enumerable.Empty<SteamApps.PICSRequest>(), packageRequests);
} catch (Exception e) {
ArchiLogger.LogGenericWarningException(e);
} finally {
PICSSemaphore.Release();
}
}
@@ -2364,16 +2373,26 @@ namespace ArchiSteamFarm {
Commands.OnNewLicenseList();
OwnedPackageIDs.Clear();
Dictionary<uint, ulong> packageAccessTokens = new Dictionary<uint, ulong>();
Dictionary<uint, uint> packagesToRefresh = new Dictionary<uint, uint>();
foreach (SteamApps.LicenseListCallback.License license in callback.LicenseList.Where(license => license.PackageID != 0)) {
OwnedPackageIDs[license.PackageID] = (license.PaymentMethod, license.TimeCreated, license.AccessToken);
foreach (SteamApps.LicenseListCallback.License license in callback.LicenseList.GroupBy(license => license.PackageID).Select(group => group.OrderByDescending(license => license.TimeCreated).First())) {
OwnedPackageIDs[license.PackageID] = (license.PaymentMethod, license.TimeCreated);
if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(license.PackageID, out (uint ChangeNumber, HashSet<uint> AppIDs) packageData) || (packageData.ChangeNumber < license.LastChangeNumber) || (packageData.AppIDs == null)) {
if (!ASF.GlobalDatabase.PackageAccessTokensReadOnly.TryGetValue(license.PackageID, out ulong packageAccessToken) || (packageAccessToken != license.AccessToken)) {
packageAccessTokens[license.PackageID] = license.AccessToken;
// Package is always due to refresh with access token change
packagesToRefresh[license.PackageID] = (uint) license.LastChangeNumber;
} else if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(license.PackageID, out (uint ChangeNumber, HashSet<uint> AppIDs) packageData) || (packageData.ChangeNumber < license.LastChangeNumber)) {
packagesToRefresh[license.PackageID] = (uint) license.LastChangeNumber;
}
}
if (packageAccessTokens.Count > 0) {
ASF.GlobalDatabase.RefreshPackageAccessTokens(packageAccessTokens);
}
if (packagesToRefresh.Count > 0) {
ArchiLogger.LogGenericTrace(Strings.BotRefreshingPackagesData);
await ASF.GlobalDatabase.RefreshPackages(this, packagesToRefresh).ConfigureAwait(false);
@@ -2588,6 +2607,8 @@ namespace ArchiSteamFarm {
);
}
SteamPICSChanges.OnBotLoggedOn();
await PluginsCore.OnBotLoggedOn(this).ConfigureAwait(false);
break;

View File

@@ -1181,7 +1181,7 @@ namespace ArchiSteamFarm {
if (packageIDs != null) {
foreach (uint packageID in packageIDs) {
if (!Bot.OwnedPackageIDs.TryGetValue(packageID, out (EPaymentMethod PaymentMethod, DateTime TimeCreated, ulong AccessToken) packageData)) {
if (!Bot.OwnedPackageIDs.TryGetValue(packageID, out (EPaymentMethod PaymentMethod, DateTime TimeCreated) packageData)) {
Bot.ArchiLogger.LogNullError(nameof(packageData));
return;

View File

@@ -38,6 +38,10 @@ namespace ArchiSteamFarm {
[PublicAPI]
public readonly Guid Guid = Guid.NewGuid();
[JsonIgnore]
[PublicAPI]
public IReadOnlyDictionary<uint, ulong> PackageAccessTokensReadOnly => PackagesAccessTokens;
[JsonIgnore]
[PublicAPI]
public IReadOnlyDictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)> PackagesDataReadOnly => PackagesData;
@@ -45,6 +49,9 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)]
internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, ulong> PackagesAccessTokens = new ConcurrentDictionary<uint, ulong>();
[JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentDictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)> PackagesData = new ConcurrentDictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)>();
@@ -139,7 +146,7 @@ namespace ArchiSteamFarm {
HashSet<uint> result = new HashSet<uint>();
foreach (uint packageID in packageIDs.Where(packageID => packageID != 0)) {
if (!PackagesData.TryGetValue(packageID, out (uint _, HashSet<uint> AppIDs) packagesData) || (packagesData.AppIDs?.Contains(appID) != true)) {
if (!PackagesData.TryGetValue(packageID, out (uint ChangeNumber, HashSet<uint> AppIDs) packagesData) || (packagesData.AppIDs?.Contains(appID) != true)) {
continue;
}
@@ -149,6 +156,27 @@ namespace ArchiSteamFarm {
return result;
}
internal void RefreshPackageAccessTokens(IReadOnlyDictionary<uint, ulong> packageAccessTokens) {
if ((packageAccessTokens == null) || (packageAccessTokens.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(packageAccessTokens));
return;
}
bool save = false;
foreach ((uint packageID, ulong currentAccessToken) in packageAccessTokens) {
if (!PackagesAccessTokens.TryGetValue(packageID, out ulong previousAccessToken) || (previousAccessToken != currentAccessToken)) {
PackagesAccessTokens[packageID] = currentAccessToken;
save = true;
}
}
if (save) {
Utilities.InBackground(Save);
}
}
internal async Task RefreshPackages(Bot bot, IReadOnlyDictionary<uint, uint> packages) {
if ((bot == null) || (packages == null) || (packages.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(bot) + " || " + nameof(packages));
@@ -159,7 +187,7 @@ namespace ArchiSteamFarm {
await PackagesRefreshSemaphore.WaitAsync().ConfigureAwait(false);
try {
HashSet<uint> packageIDs = packages.Where(package => (package.Key != 0) && (!PackagesData.TryGetValue(package.Key, out (uint ChangeNumber, HashSet<uint> AppIDs) packageData) || (packageData.ChangeNumber < package.Value) || (packageData.AppIDs == null))).Select(package => package.Key).ToHashSet();
HashSet<uint> packageIDs = packages.Where(package => (package.Key != 0) && (!PackagesData.TryGetValue(package.Key, out (uint ChangeNumber, HashSet<uint> AppIDs) packageData) || (packageData.ChangeNumber < package.Value))).Select(package => package.Key).ToHashSet();
if (packageIDs.Count == 0) {
return;
@@ -167,17 +195,26 @@ namespace ArchiSteamFarm {
Dictionary<uint, (uint ChangeNumber, HashSet<uint> AppIDs)> packagesData = await bot.GetPackagesData(packageIDs).ConfigureAwait(false);
if ((packagesData == null) || (packagesData.Count == 0)) {
if (packagesData == null) {
bot.ArchiLogger.LogGenericWarning(Strings.WarningFailed);
return;
}
foreach ((uint packageID, (uint ChangeNumber, HashSet<uint> AppIDs) package) in packagesData) {
PackagesData[packageID] = package;
bool save = false;
foreach ((uint packageID, (uint ChangeNumber, HashSet<uint> AppIDs) packageData) in packagesData) {
if (PackagesData.TryGetValue(packageID, out (uint ChangeNumber, HashSet<uint> AppIDs) previousData) && (packageData.ChangeNumber < previousData.ChangeNumber)) {
continue;
}
PackagesData[packageID] = packageData;
save = true;
}
Utilities.InBackground(Save);
if (save) {
Utilities.InBackground(Save);
}
} finally {
PackagesRefreshSemaphore.Release();
}

View File

@@ -634,47 +634,22 @@ StackTrace:
<value>Η χρήση του {0} είναι υπό απόσυρση και θα αφαιρεθεί σε μελλοντικές εκδόσεις του προγράμματος. Παρακαλώ χρησιμοποιήστε το {1}.</value>
<comment>{0} will be replaced by the name of deprecated property (such as argument, config property or likewise), {1} will be replaced by the name of valid replacement (such as another argument or config property)</comment>
</data>
<data name="BotAcceptedDonationTrade" xml:space="preserve">
<value>Accepted donation trade: {0}</value>
<comment>{0} will be replaced by trade's ID (number)</comment>
</data>
<data name="WarningWorkaroundTriggered" xml:space="preserve">
<value>Workaround for {0} bug has been triggered.</value>
<comment>{0} will be replaced by the bug's name provided by ASF</comment>
</data>
<data name="TargetBotNotConnected" xml:space="preserve">
<value>Target bot instance is not connected!</value>
</data>
<data name="BotWalletBalance" xml:space="preserve">
<value>Wallet balance: {0} {1}</value>
<comment>{0} will be replaced by wallet balance value, {1} will be replaced by currency name</comment>
</data>
<data name="BotHasNoWallet" xml:space="preserve">
<value>Bot has no wallet.</value>
</data>
<data name="BotLevel" xml:space="preserve">
<value>Το bot βρίσκεται στο επίπεδο {0}.</value>
<comment>{0} will be replaced by bot's level</comment>
</data>
<data name="ActivelyMatchingItems" xml:space="preserve">
<value>Matching Steam items, round #{0}...</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="DoneActivelyMatchingItems" xml:space="preserve">
<value>Done matching Steam items, round #{0}.</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="ErrorAborted" xml:space="preserve">
<value>Ακυρώθηκε!</value>
</data>
<data name="ActivelyMatchingItemsRound" xml:space="preserve">
<value>Matched a total of {0} sets this round.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="WarningExcessiveBotsCount" xml:space="preserve">
<value>You're running more personal bot accounts than our upper recommended limit ({0}). Be advised that this setup is not supported and might cause various Steam-related issues, including account suspensions. Check out the FAQ for more details.</value>
<comment>{0} will be replaced by our maximum recommended bots count (number)</comment>
</data>
<data name="PluginLoaded" xml:space="preserve">
<value>{0} φορτώθηκε με επιτυχία!</value>
<comment>{0} will be replaced by the name of the custom ASF plugin</comment>
@@ -686,9 +661,7 @@ StackTrace:
<data name="NothingFound" xml:space="preserve">
<value>Δεν βρέθηκε τίποτα!</value>
</data>
<data name="PluginsWarning" xml:space="preserve">
<value>You've loaded one or more of custom plugins into the ASF. Since we're unable to offer a support for modded setups, please reach the appropriate developers of the plugins that you decided to use in case of any issues.</value>
</data>
<data name="PleaseWait" xml:space="preserve">
<value>Παρακαλώ περιμένετε...</value>
</data>
@@ -727,10 +700,6 @@ StackTrace:
<data name="UpdateCleanup" xml:space="preserve">
<value>Εκκαθάριση παλιών αρχείων μετά την ενημέρωση...</value>
</data>
<data name="BotGeneratingSteamParentalCode" xml:space="preserve">
<value>Generating Steam parental code, this can take a while, consider putting it in the config instead...</value>
</data>
<data name="IPCConfigChanged" xml:space="preserve">
<value>IPC config has been changed!</value>
</data>
</root>

View File

@@ -716,14 +716,8 @@ StackTrace:
<data name="ErrorSingleInstanceRequired" xml:space="preserve">
<value>ASF prosessi on jo käynnissä tässä työhakemistossa. Keskeytetään!</value>
</data>
<data name="BotHandledConfirmations" xml:space="preserve">
<value>Successfully handled {0} confirmations!</value>
<comment>{0} will be replaced by number of confirmations</comment>
</data>
<data name="BotExtraIdlingCooldown" xml:space="preserve">
<value>Waiting up to {0} to ensure that we're free to start idling...</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "1 minute")</comment>
</data>
<data name="UpdateCleanup" xml:space="preserve">
<value>Siivotaan vanhat tiedostot päivityksen jälkeen...</value>
</data>

View File

@@ -264,10 +264,7 @@ StackTrace:
<value>נא הזינו את משתמש ה- Steam שלכם: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamParentalCode" xml:space="preserve">
<value>Please enter Steam parental code: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamPassword" xml:space="preserve">
<value>אנא הקישו את הסיסמה הנוכחית שלכם: </value>
<comment>Please note that this translation should end with space</comment>
@@ -279,9 +276,7 @@ StackTrace:
<data name="IPCReady" xml:space="preserve">
<value>שרת IPC מוכן!</value>
</data>
<data name="IPCStarting" xml:space="preserve">
<value>Starting IPC server...</value>
</data>
<data name="BotAlreadyStopped" xml:space="preserve">
<value>בוט זה כבר הפסיק!</value>
</data>
@@ -336,9 +331,7 @@ StackTrace:
<data name="IdlingStopped" xml:space="preserve">
<value>הרצה נעצרה!</value>
</data>
<data name="IgnoredPermanentPauseEnabled" xml:space="preserve">
<value>Ignoring this request, as permanent pause is enabled!</value>
</data>
<data name="NothingToIdle" xml:space="preserve">
<value>אין שום דבר להריץ בחשבון זה!</value>
</data>
@@ -438,18 +431,12 @@ StackTrace:
<data name="BotInstanceNotStartingBecauseDisabled" xml:space="preserve">
<value>לא מריץ את הבוט הזה כי הוא אינו מופעל בקובץ ההגדרה!</value>
</data>
<data name="BotInvalidAuthenticatorDuringLogin" xml:space="preserve">
<value>Received TwoFactorCodeMismatch error code {0} times in a row. Either your 2FA credentials are no longer valid, or your clock is out of sync, aborting!</value>
<comment>{0} will be replaced by maximum allowed number of failed 2FA attempts</comment>
</data>
<data name="BotLoggedOff" xml:space="preserve">
<value>ניתוק מסטים: {0}</value>
<comment>{0} will be replaced by logging off reason (string)</comment>
</data>
<data name="BotLoggedOn" xml:space="preserve">
<value>Successfully logged on as {0}.</value>
<comment>{0} will be replaced by steam ID (number)</comment>
</data>
<data name="BotLoggingIn" xml:space="preserve">
<value>מתחבר...</value>
</data>
@@ -642,95 +629,30 @@ StackTrace:
<value>הדרך לעקיפת הבעיה עבור {0} באגים הופעלה.</value>
<comment>{0} will be replaced by the bug's name provided by ASF</comment>
</data>
<data name="TargetBotNotConnected" xml:space="preserve">
<value>Target bot instance is not connected!</value>
</data>
<data name="BotWalletBalance" xml:space="preserve">
<value>Wallet balance: {0} {1}</value>
<comment>{0} will be replaced by wallet balance value, {1} will be replaced by currency name</comment>
</data>
<data name="BotHasNoWallet" xml:space="preserve">
<value>Bot has no wallet.</value>
</data>
<data name="BotLevel" xml:space="preserve">
<value>Bot has level {0}.</value>
<comment>{0} will be replaced by bot's level</comment>
</data>
<data name="ActivelyMatchingItems" xml:space="preserve">
<value>Matching Steam items, round #{0}...</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="DoneActivelyMatchingItems" xml:space="preserve">
<value>Done matching Steam items, round #{0}.</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="ErrorAborted" xml:space="preserve">
<value>Aborted!</value>
</data>
<data name="ActivelyMatchingItemsRound" xml:space="preserve">
<value>Matched a total of {0} sets this round.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="WarningExcessiveBotsCount" xml:space="preserve">
<value>You're running more personal bot accounts than our upper recommended limit ({0}). Be advised that this setup is not supported and might cause various Steam-related issues, including account suspensions. Check out the FAQ for more details.</value>
<comment>{0} will be replaced by our maximum recommended bots count (number)</comment>
</data>
<data name="PluginLoaded" xml:space="preserve">
<value>{0} has been loaded successfully!</value>
<comment>{0} will be replaced by the name of the custom ASF plugin</comment>
</data>
<data name="PluginLoading" xml:space="preserve">
<value>Loading {0} V{1}...</value>
<comment>{0} will be replaced by the name of the custom ASF plugin, {1} will be replaced by its version</comment>
</data>
<data name="NothingFound" xml:space="preserve">
<value>Nothing found!</value>
</data>
<data name="PluginsWarning" xml:space="preserve">
<value>You've loaded one or more of custom plugins into the ASF. Since we're unable to offer a support for modded setups, please reach the appropriate developers of the plugins that you decided to use in case of any issues.</value>
</data>
<data name="PleaseWait" xml:space="preserve">
<value>Please wait...</value>
</data>
<data name="EnterCommand" xml:space="preserve">
<value>Enter command: </value>
</data>
<data name="Executing" xml:space="preserve">
<value>Executing...</value>
</data>
<data name="InteractiveConsoleEnabled" xml:space="preserve">
<value>Interactive console is now active, type 'c' in order to enter command mode.</value>
</data>
<data name="InteractiveConsoleNotAvailable" xml:space="preserve">
<value>Interactive console is not available due to missing {0} config property.</value>
<comment>{0} will be replaced by the name of the missing config property (string)</comment>
</data>
<data name="Response" xml:space="preserve">
<value>Response: {0}</value>
<comment>{0} will be replaced by the generated response (string)</comment>
</data>
<data name="BotGamesToRedeemInBackgroundCount" xml:space="preserve">
<value>Bot has {0} games remaining in its background queue.</value>
<comment>{0} will be replaced by remaining number of games in BGR's queue</comment>
</data>
<data name="ErrorSingleInstanceRequired" xml:space="preserve">
<value>ASF process is already running for this working directory, aborting!</value>
</data>
<data name="BotHandledConfirmations" xml:space="preserve">
<value>Successfully handled {0} confirmations!</value>
<comment>{0} will be replaced by number of confirmations</comment>
</data>
<data name="BotExtraIdlingCooldown" xml:space="preserve">
<value>Waiting up to {0} to ensure that we're free to start idling...</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "1 minute")</comment>
</data>
<data name="UpdateCleanup" xml:space="preserve">
<value>Cleaning up old files after update...</value>
</data>
<data name="BotGeneratingSteamParentalCode" xml:space="preserve">
<value>Generating Steam parental code, this can take a while, consider putting it in the config instead...</value>
</data>
<data name="IPCConfigChanged" xml:space="preserve">
<value>IPC config has been changed!</value>
</data>
</root>

View File

@@ -435,10 +435,7 @@
<data name="BotInstanceNotStartingBecauseDisabled" xml:space="preserve">
<value>Tidak memulai instansi bot ini karena dinonaktifkan di file konfigurasi!</value>
</data>
<data name="BotInvalidAuthenticatorDuringLogin" xml:space="preserve">
<value>Received TwoFactorCodeMismatch error code {0} times in a row. Either your 2FA credentials are no longer valid, or your clock is out of sync, aborting!</value>
<comment>{0} will be replaced by maximum allowed number of failed 2FA attempts</comment>
</data>
<data name="BotLoggedOff" xml:space="preserve">
<value>Ter-log off dari Steam: {0}</value>
<comment>{0} will be replaced by logging off reason (string)</comment>
@@ -479,10 +476,7 @@
<value>Yang sudah dimiliki: {0} | {1}</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="BotRateLimitExceeded" xml:space="preserve">
<value>Rate limit exceeded, we will retry after {0} of cooldown...</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "25 minutes")</comment>
</data>
<data name="BotReconnecting" xml:space="preserve">
<value>Menyambungkan kembali...</value>
</data>
@@ -627,21 +621,13 @@
<data name="BotRefreshingPackagesData" xml:space="preserve">
<value>Menyegarkan data paket...</value>
</data>
<data name="WarningDeprecated" xml:space="preserve">
<value>Usage of {0} is deprecated and will be removed in future versions of the program. Please use {1} instead.</value>
<comment>{0} will be replaced by the name of deprecated property (such as argument, config property or likewise), {1} will be replaced by the name of valid replacement (such as another argument or config property)</comment>
</data>
<data name="BotAcceptedDonationTrade" xml:space="preserve">
<value>Menerima trade donasi: {0}</value>
<comment>{0} will be replaced by trade's ID (number)</comment>
</data>
<data name="WarningWorkaroundTriggered" xml:space="preserve">
<value>Workaround for {0} bug has been triggered.</value>
<comment>{0} will be replaced by the bug's name provided by ASF</comment>
</data>
<data name="TargetBotNotConnected" xml:space="preserve">
<value>Target bot instance is not connected!</value>
</data>
<data name="BotWalletBalance" xml:space="preserve">
<value>Saldo wallet: {0} {1}</value>
<comment>{0} will be replaced by wallet balance value, {1} will be replaced by currency name</comment>
@@ -653,25 +639,13 @@
<value>Bot mempunyai level {0}.</value>
<comment>{0} will be replaced by bot's level</comment>
</data>
<data name="ActivelyMatchingItems" xml:space="preserve">
<value>Matching Steam items, round #{0}...</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="DoneActivelyMatchingItems" xml:space="preserve">
<value>Done matching Steam items, round #{0}.</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="ErrorAborted" xml:space="preserve">
<value>Dibatalkan!</value>
</data>
<data name="ActivelyMatchingItemsRound" xml:space="preserve">
<value>Matched a total of {0} sets this round.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="WarningExcessiveBotsCount" xml:space="preserve">
<value>You're running more personal bot accounts than our upper recommended limit ({0}). Be advised that this setup is not supported and might cause various Steam-related issues, including account suspensions. Check out the FAQ for more details.</value>
<comment>{0} will be replaced by our maximum recommended bots count (number)</comment>
</data>
<data name="PluginLoaded" xml:space="preserve">
<value>{0} telah berhasil memuat!</value>
<comment>{0} will be replaced by the name of the custom ASF plugin</comment>
@@ -683,9 +657,7 @@
<data name="NothingFound" xml:space="preserve">
<value>Tidak ditemukan!</value>
</data>
<data name="PluginsWarning" xml:space="preserve">
<value>You've loaded one or more of custom plugins into the ASF. Since we're unable to offer a support for modded setups, please reach the appropriate developers of the plugins that you decided to use in case of any issues.</value>
</data>
<data name="PleaseWait" xml:space="preserve">
<value>Harap tunggu...</value>
</data>
@@ -695,13 +667,8 @@
<data name="Executing" xml:space="preserve">
<value>Mengeksekusi...</value>
</data>
<data name="InteractiveConsoleEnabled" xml:space="preserve">
<value>Interactive console is now active, type 'c' in order to enter command mode.</value>
</data>
<data name="InteractiveConsoleNotAvailable" xml:space="preserve">
<value>Interactive console is not available due to missing {0} config property.</value>
<comment>{0} will be replaced by the name of the missing config property (string)</comment>
</data>
<data name="Response" xml:space="preserve">
<value>Respon: {0}</value>
<comment>{0} will be replaced by the generated response (string)</comment>
@@ -710,24 +677,10 @@
<value>Bot mempunyai {0} game tersisa di dalam antrian.</value>
<comment>{0} will be replaced by remaining number of games in BGR's queue</comment>
</data>
<data name="ErrorSingleInstanceRequired" xml:space="preserve">
<value>ASF process is already running for this working directory, aborting!</value>
</data>
<data name="BotHandledConfirmations" xml:space="preserve">
<value>Successfully handled {0} confirmations!</value>
<comment>{0} will be replaced by number of confirmations</comment>
</data>
<data name="BotExtraIdlingCooldown" xml:space="preserve">
<value>Waiting up to {0} to ensure that we're free to start idling...</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "1 minute")</comment>
</data>
<data name="UpdateCleanup" xml:space="preserve">
<value>Cleaning up old files after update...</value>
</data>
<data name="BotGeneratingSteamParentalCode" xml:space="preserve">
<value>Generating Steam parental code, this can take a while, consider putting it in the config instead...</value>
</data>
<data name="IPCConfigChanged" xml:space="preserve">
<value>IPC config has been changed!</value>
</data>
</root>

View File

@@ -724,10 +724,6 @@
<data name="UpdateCleanup" xml:space="preserve">
<value>更新後に古いファイルをクリーンアップしています…</value>
</data>
<data name="BotGeneratingSteamParentalCode" xml:space="preserve">
<value>Generating Steam parental code, this can take a while, consider putting it in the config instead...</value>
</data>
<data name="IPCConfigChanged" xml:space="preserve">
<value>IPC config has been changed!</value>
</data>
</root>

View File

@@ -130,49 +130,28 @@
{0}</value>
<comment>{0} will be replaced by content string. Please note that this string should include newline for formatting.</comment>
</data>
<data name="ErrorConfigPropertyInvalid" xml:space="preserve">
<value>Configured {0} property is invalid: {1}</value>
<comment>{0} will be replaced by name of the configuration property, {1} will be replaced by invalid value</comment>
</data>
<data name="ErrorEarlyFatalExceptionInfo" xml:space="preserve">
<value>ASF V{0} has run into fatal exception before core logging module was even able to initialize!</value>
<comment>{0} will be replaced by version number</comment>
</data>
<data name="ErrorEarlyFatalExceptionPrint" xml:space="preserve">
<value>Exceção: {0}() {1}
StackTrace:
{2}</value>
<comment>{0} will be replaced by function name, {1} will be replaced by exception message, {2} will be replaced by entire stack trace. Please note that this string should include newlines for formatting.</comment>
</data>
<data name="ErrorExitingWithNonZeroErrorCode" xml:space="preserve">
<value>Exiting with nonzero error code!</value>
</data>
<data name="ErrorFailingRequest" xml:space="preserve">
<value>Request failing: {0}</value>
<comment>{0} will be replaced by URL of the request</comment>
</data>
<data name="ErrorGlobalConfigNotLoaded" xml:space="preserve">
<value>Global config could not be loaded. Make sure that {0} exists and is valid! Follow 'setting up' guide on the wiki if you're confused.</value>
<comment>{0} will be replaced by file's path</comment>
</data>
<data name="ErrorIsInvalid" xml:space="preserve">
<value>{0} é inválido!</value>
<comment>{0} will be replaced by object's name</comment>
</data>
<data name="ErrorMobileAuthenticatorInvalidDeviceID" xml:space="preserve">
<value>Refusing to execute this function due to invalid DeviceID in ASF 2FA!</value>
</data>
<data name="ErrorNoBotsDefined" xml:space="preserve">
<value>No bots are defined. Did you forget to configure your ASF?</value>
</data>
<data name="ErrorObjectIsNull" xml:space="preserve">
<value>{0} é nulo!</value>
<comment>{0} will be replaced by object's name</comment>
</data>
<data name="ErrorParsingObject" xml:space="preserve">
<value>Parsing {0} failed!</value>
<comment>{0} will be replaced by object's name</comment>
</data>
<data name="ErrorRequestFailedTooManyTimes" xml:space="preserve">
<value>O pedido falhou após {0} tentativas!</value>
<comment>{0} will be replaced by maximum number of tries</comment>
@@ -180,15 +159,9 @@ StackTrace:
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
<value>Não foi possível verificar a versão mais recente!</value>
</data>
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
<value>Could not proceed with update because there is no asset that relates to currently running version! Automatic update to that version is not possible.</value>
</data>
<data name="ErrorUpdateNoAssets" xml:space="preserve">
<value>Could not proceed with an update because that version doesn't include any assets!</value>
</data>
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
<value>Received a request for user input, but process is running in headless mode!</value>
</data>
<data name="Exiting" xml:space="preserve">
<value>A sair...</value>
</data>
@@ -212,13 +185,8 @@ StackTrace:
<data name="NoBotsAreRunning" xml:space="preserve">
<value>Não existe bots em execução, a sair...</value>
</data>
<data name="RefreshingOurSession" xml:space="preserve">
<value>Refreshing our session!</value>
</data>
<data name="RejectingTrade" xml:space="preserve">
<value>Rejecting trade: {0}</value>
<comment>{0} will be replaced by trade number</comment>
</data>
<data name="Restarting" xml:space="preserve">
<value>A reiniciar...</value>
</data>
@@ -228,22 +196,15 @@ StackTrace:
<data name="Success" xml:space="preserve">
<value>Sucesso!</value>
</data>
<data name="UnlockingParentalAccount" xml:space="preserve">
<value>Unlocking parental account...</value>
</data>
<data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>A verificar por novas versões...</value>
</data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<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 name="UpdateFinished" xml:space="preserve">
<value>Processo de atualização terminado!</value>
</data>
<data name="UpdateNewVersionAvailable" xml:space="preserve">
<value>New ASF version is available! Consider updating yourself!</value>
</data>
<data name="UpdateVersionInfo" xml:space="preserve">
<value>Versão local: {0} | Versão remota: {1}</value>
<comment>{0} will be replaced by current version, {1} will be replaced by remote version</comment>
@@ -256,35 +217,22 @@ StackTrace:
<value>Por favor, digite o seu código de autenticação de dois fatores da app de autenticador da Steam: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamGuard" xml:space="preserve">
<value>Please enter SteamGuard auth code that was sent on your e-mail: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamLogin" xml:space="preserve">
<value>Por favor escreva o seu Steam login: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamParentalCode" xml:space="preserve">
<value>Please enter Steam parental code: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamPassword" xml:space="preserve">
<value>Por favor escreva a sua palavra-chave da Steam: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
<value>Received unknown value for {0}, please report this: {1}</value>
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
</data>
<data name="IPCReady" xml:space="preserve">
<value>Servidor IPC pronto!</value>
</data>
<data name="IPCStarting" xml:space="preserve">
<value>Starting IPC server...</value>
</data>
<data name="BotAlreadyStopped" xml:space="preserve">
<value>This bot has already stopped!</value>
</data>
<data name="BotNotFound" xml:space="preserve">
<value>Não foi possível encontrar qualquer bot chamado {0}!</value>
<comment>{0} will be replaced by bot's name query (string)</comment>
@@ -293,24 +241,11 @@ StackTrace:
<value>Existe {0}/{1} bots ligados, com um total de {2} jogos ({3} cartas) restantes para coletar.</value>
<comment>{0} will be replaced by number of active bots, {1} will be replaced by total number of bots, {2} will be replaced by total number of games left to idle, {3} will be replaced by total number of cards left to idle</comment>
</data>
<data name="BotStatusIdling" xml:space="preserve">
<value>Bot is idling game: {0} ({1}, {2} card drops remaining) from a total of {3} games ({4} cards) left to idle (~{5} remaining).</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by number of cards left to idle, {3} will be replaced by total number of games to idle, {4} will be replaced by total number of cards to idle, {5} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="BotStatusIdlingList" xml:space="preserve">
<value>Bot is idling games: {0} from a total of {1} games ({2} cards) left to idle (~{3} remaining).</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), {1} will be replaced by total number of games to idle, {2} will be replaced by total number of cards to idle, {3} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="CheckingFirstBadgePage" xml:space="preserve">
<value>Checking first badge page...</value>
</data>
<data name="CheckingOtherBadgePages" xml:space="preserve">
<value>Checking other badge pages...</value>
</data>
<data name="ChosenFarmingAlgorithm" xml:space="preserve">
<value>Chosen idling algorithm: {0}</value>
<comment>{0} will be replaced by the name of chosen idling algorithm</comment>
</data>
<data name="Done" xml:space="preserve">
<value>Feito!</value>
</data>
@@ -318,53 +253,25 @@ StackTrace:
<value>Nós temos um total de {0} jogos ({1} cartas) restantes para obter as cartas (~{2} cartas restantes)...</value>
<comment>{0} will be replaced by number of games, {1} will be replaced by number of cards, {2} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="IdlingFinished" xml:space="preserve">
<value>Idling finished!</value>
</data>
<data name="IdlingFinishedForGame" xml:space="preserve">
<value>Finished idling: {0} ({1}) after {2} of playtime!</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="IdlingFinishedForGames" xml:space="preserve">
<value>Finished idling games: {0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
</data>
<data name="IdlingStatusForGame" xml:space="preserve">
<value>Idling status for {0} ({1}): {2} cards remaining</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by number of cards left to idle</comment>
</data>
<data name="IdlingStopped" xml:space="preserve">
<value>Idling stopped!</value>
</data>
<data name="IgnoredPermanentPauseEnabled" xml:space="preserve">
<value>Ignoring this request, as permanent pause is enabled!</value>
</data>
<data name="NothingToIdle" xml:space="preserve">
<value>We don't have anything to idle on this account!</value>
</data>
<data name="NowIdling" xml:space="preserve">
<value>Now idling: {0} ({1})</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="NowIdlingList" xml:space="preserve">
<value>Agora a farmar: {0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
</data>
<data name="PlayingNotAvailable" xml:space="preserve">
<value>Playing is currently unavailable, we'll try again later!</value>
</data>
<data name="StillIdling" xml:space="preserve">
<value>Still idling: {0} ({1})</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="StillIdlingList" xml:space="preserve">
<value>Ainda está a coletar: {0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
</data>
<data name="StoppedIdling" xml:space="preserve">
<value>Stopped idling: {0} ({1})</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="StoppedIdlingList" xml:space="preserve">
<value>Parou de coletar: {0}</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), separated by a comma</comment>
@@ -372,20 +279,13 @@ StackTrace:
<data name="UnknownCommand" xml:space="preserve">
<value>Comando desconhecido!</value>
</data>
<data name="WarningCouldNotCheckBadges" xml:space="preserve">
<value>Could not get badges' information, we will try again later!</value>
</data>
<data name="WarningCouldNotCheckCardsStatus" xml:space="preserve">
<value>Could not check cards status for: {0} ({1}), we will try again later!</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="BotAcceptingGift" xml:space="preserve">
<value>A aceitar presente: {0}...</value>
<comment>{0} will be replaced by giftID (number)</comment>
</data>
<data name="BotAccountLimited" xml:space="preserve">
<value>This account is limited, idling process is unavailable until the restriction is removed!</value>
</data>
<data name="BotAddLicense" xml:space="preserve">
<value>ID: {0} | Estado: {1}</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by status string</comment>
@@ -400,9 +300,7 @@ StackTrace:
<data name="BotAuthenticatorConverting" xml:space="preserve">
<value>A converter ficheiro .maFile em formato ASF...</value>
</data>
<data name="BotAuthenticatorImportFinished" xml:space="preserve">
<value>Successfully finished importing mobile authenticator!</value>
</data>
<data name="BotAuthenticatorInvalidDeviceID" xml:space="preserve">
<value>O DeviceID está incorreto ou não existe!</value>
</data>
@@ -410,24 +308,14 @@ StackTrace:
<value>Token 2FA: {0}</value>
<comment>{0} will be replaced by generated 2FA token (string)</comment>
</data>
<data name="BotAutomaticIdlingNowPaused" xml:space="preserve">
<value>Automatic idling has paused!</value>
</data>
<data name="BotAutomaticIdlingNowResumed" xml:space="preserve">
<value>Automatic idling has resumed!</value>
</data>
<data name="BotAutomaticIdlingPausedAlready" xml:space="preserve">
<value>Automatic idling is paused already!</value>
</data>
<data name="BotAutomaticIdlingResumedAlready" xml:space="preserve">
<value>Automatic idling is resumed already!</value>
</data>
<data name="BotConnected" xml:space="preserve">
<value>Conectado ao Steam!</value>
</data>
<data name="BotDisconnected" xml:space="preserve">
<value>Disconnected from Steam!</value>
</data>
<data name="BotDisconnecting" xml:space="preserve">
<value>Desconectando...</value>
</data>
@@ -438,10 +326,7 @@ StackTrace:
<data name="BotInstanceNotStartingBecauseDisabled" xml:space="preserve">
<value>Não é possível iniciar este bot porque está desativado no ficheiro de configuração!</value>
</data>
<data name="BotInvalidAuthenticatorDuringLogin" xml:space="preserve">
<value>Received TwoFactorCodeMismatch error code {0} times in a row. Either your 2FA credentials are no longer valid, or your clock is out of sync, aborting!</value>
<comment>{0} will be replaced by maximum allowed number of failed 2FA attempts</comment>
</data>
<data name="BotLoggedOff" xml:space="preserve">
<value>Desconectado da Steam: {0}</value>
<comment>{0} will be replaced by logging off reason (string)</comment>
@@ -465,9 +350,7 @@ StackTrace:
<data name="BotLootingSuccess" xml:space="preserve">
<value>Proposta enviada com sucesso!</value>
</data>
<data name="BotSendingTradeToYourself" xml:space="preserve">
<value>You can't send trade to yourself!</value>
</data>
<data name="BotNoASFAuthenticator" xml:space="preserve">
<value>Esse bot não tem a autenticação de dois fatores ligada! Esqueceu-se de importar seu autenticador como autenticação de dois fatores?</value>
</data>
@@ -497,9 +380,7 @@ StackTrace:
<value>Chave: {0} | Estado: {1} | Itens: {2}</value>
<comment>{0} will be replaced by cd-key (string), {1} will be replaced by status string, {2} will be replaced by list of key-value pairs, separated by a comma</comment>
</data>
<data name="BotRemovedExpiredLoginKey" xml:space="preserve">
<value>Removed expired login key!</value>
</data>
<data name="BotStatusNotIdling" xml:space="preserve">
<value>O Bot não está a farmar nada.</value>
</data>
@@ -592,12 +473,8 @@ StackTrace:
<value>{0} V{1}</value>
<comment>{0} will be replaced by program's name (e.g. "ASF"), {1} will be replaced by program's version (e.g. "1.0.0.0"). This string typically has nothing to translate and you should leave it as it is, unless you need to change the format, e.g. in RTL languages.</comment>
</data>
<data name="BotAccountLocked" xml:space="preserve">
<value>This account is locked, idling process is permanently unavailable!</value>
</data>
<data name="BotStatusLocked" xml:space="preserve">
<value>Bot is locked and can't drop any cards through idling.</value>
</data>
<data name="ErrorFunctionOnlyInHeadlessMode" xml:space="preserve">
<value>Esta função só está disponível no modo headless!</value>
</data>
@@ -608,9 +485,7 @@ StackTrace:
<data name="ErrorAccessDenied" xml:space="preserve">
<value>Acesso negado!</value>
</data>
<data name="WarningPreReleaseVersion" xml:space="preserve">
<value>You're using a version that is newer than latest released version for your update channel. Please note that 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.</value>
</data>
<data name="BotStats" xml:space="preserve">
<value>Uso de memória atual: {0} MB.</value>
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
@@ -619,10 +494,7 @@ StackTrace:
<value>A limpar a fila de descoberta da Steam #{0}...</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
<value>Done clearing Steam discovery queue #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverviewPerGame" xml:space="preserve">
<value>{0}/{1} bots já possuem o jogo {2}.</value>
<comment>{0} will be replaced by number of bots that already own particular game being checked, {1} will be replaced by total number of bots that were checked during the process, {2} will be replaced by game's ID (number)</comment>
@@ -630,18 +502,12 @@ StackTrace:
<data name="BotRefreshingPackagesData" xml:space="preserve">
<value>Atualizando dados de pacotes...</value>
</data>
<data name="WarningDeprecated" xml:space="preserve">
<value>Usage of {0} is deprecated and will be removed in future versions of the program. Please use {1} instead.</value>
<comment>{0} will be replaced by the name of deprecated property (such as argument, config property or likewise), {1} will be replaced by the name of valid replacement (such as another argument or config property)</comment>
</data>
<data name="BotAcceptedDonationTrade" xml:space="preserve">
<value>Doação aceite: {0}</value>
<comment>{0} will be replaced by trade's ID (number)</comment>
</data>
<data name="WarningWorkaroundTriggered" xml:space="preserve">
<value>Workaround for {0} bug has been triggered.</value>
<comment>{0} will be replaced by the bug's name provided by ASF</comment>
</data>
<data name="TargetBotNotConnected" xml:space="preserve">
<value>A instância do bot alvo não está conectada!</value>
</data>
@@ -656,14 +522,8 @@ StackTrace:
<value>O bot está a nível {0}.</value>
<comment>{0} will be replaced by bot's level</comment>
</data>
<data name="ActivelyMatchingItems" xml:space="preserve">
<value>Matching Steam items, round #{0}...</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="DoneActivelyMatchingItems" xml:space="preserve">
<value>Done matching Steam items, round #{0}.</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="ErrorAborted" xml:space="preserve">
<value>Cancelado!</value>
</data>
@@ -671,10 +531,7 @@ StackTrace:
<value>Corresponde a um total de {0} conjuntos nesta rodada.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="WarningExcessiveBotsCount" xml:space="preserve">
<value>You're running more personal bot accounts than our upper recommended limit ({0}). Be advised that this setup is not supported and might cause various Steam-related issues, including account suspensions. Check out the FAQ for more details.</value>
<comment>{0} will be replaced by our maximum recommended bots count (number)</comment>
</data>
<data name="PluginLoaded" xml:space="preserve">
<value>{0} foi carregado com sucesso!</value>
<comment>{0} will be replaced by the name of the custom ASF plugin</comment>
@@ -686,9 +543,7 @@ StackTrace:
<data name="NothingFound" xml:space="preserve">
<value>Nada encontrado!</value>
</data>
<data name="PluginsWarning" xml:space="preserve">
<value>You've loaded one or more of custom plugins into the ASF. Since we're unable to offer a support for modded setups, please reach the appropriate developers of the plugins that you decided to use in case of any issues.</value>
</data>
<data name="PleaseWait" xml:space="preserve">
<value>Por favor, aguarde...</value>
</data>
@@ -701,36 +556,13 @@ StackTrace:
<data name="InteractiveConsoleEnabled" xml:space="preserve">
<value>A consola interativa está agora ativa, digite 'c' para entrar no modo de comando.</value>
</data>
<data name="InteractiveConsoleNotAvailable" xml:space="preserve">
<value>Interactive console is not available due to missing {0} config property.</value>
<comment>{0} will be replaced by the name of the missing config property (string)</comment>
</data>
<data name="Response" xml:space="preserve">
<value>Response: {0}</value>
<comment>{0} will be replaced by the generated response (string)</comment>
</data>
<data name="BotGamesToRedeemInBackgroundCount" xml:space="preserve">
<value>Bot has {0} games remaining in its background queue.</value>
<comment>{0} will be replaced by remaining number of games in BGR's queue</comment>
</data>
<data name="ErrorSingleInstanceRequired" xml:space="preserve">
<value>ASF process is already running for this working directory, aborting!</value>
</data>
<data name="BotHandledConfirmations" xml:space="preserve">
<value>Successfully handled {0} confirmations!</value>
<comment>{0} will be replaced by number of confirmations</comment>
</data>
<data name="BotExtraIdlingCooldown" xml:space="preserve">
<value>Waiting up to {0} to ensure that we're free to start idling...</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "1 minute")</comment>
</data>
<data name="UpdateCleanup" xml:space="preserve">
<value>Cleaning up old files after update...</value>
</data>
<data name="BotGeneratingSteamParentalCode" xml:space="preserve">
<value>Generating Steam parental code, this can take a while, consider putting it in the config instead...</value>
</data>
<data name="IPCConfigChanged" xml:space="preserve">
<value>IPC config has been changed!</value>
</data>
</root>

View File

@@ -642,9 +642,7 @@ StackTrace:
<value>Riešenie pre obídenie chyby {0} bolo spustené.</value>
<comment>{0} will be replaced by the bug's name provided by ASF</comment>
</data>
<data name="TargetBotNotConnected" xml:space="preserve">
<value>Target bot instance is not connected!</value>
</data>
<data name="BotWalletBalance" xml:space="preserve">
<value>Stav peňaženky: {0} {1}</value>
<comment>{0} will be replaced by wallet balance value, {1} will be replaced by currency name</comment>
@@ -656,29 +654,12 @@ StackTrace:
<value>Bot má level {0}.</value>
<comment>{0} will be replaced by bot's level</comment>
</data>
<data name="ActivelyMatchingItems" xml:space="preserve">
<value>Matching Steam items, round #{0}...</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="DoneActivelyMatchingItems" xml:space="preserve">
<value>Done matching Steam items, round #{0}.</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="ErrorAborted" xml:space="preserve">
<value>Aborted!</value>
</data>
<data name="ActivelyMatchingItemsRound" xml:space="preserve">
<value>Matched a total of {0} sets this round.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="WarningExcessiveBotsCount" xml:space="preserve">
<value>You're running more personal bot accounts than our upper recommended limit ({0}). Be advised that this setup is not supported and might cause various Steam-related issues, including account suspensions. Check out the FAQ for more details.</value>
<comment>{0} will be replaced by our maximum recommended bots count (number)</comment>
</data>
<data name="PluginLoaded" xml:space="preserve">
<value>{0} has been loaded successfully!</value>
<comment>{0} will be replaced by the name of the custom ASF plugin</comment>
</data>
<data name="PluginLoading" xml:space="preserve">
<value>Načítám {0} V{1}...</value>
<comment>{0} will be replaced by the name of the custom ASF plugin, {1} will be replaced by its version</comment>

View File

@@ -180,9 +180,7 @@ StackTrace:
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
<value>Neuspešno traženje nove verzije!</value>
</data>
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
<value>Could not proceed with update because there is no asset that relates to currently running version! Automatic update to that version is not possible.</value>
</data>
<data name="ErrorUpdateNoAssets" xml:space="preserve">
<value>Nije moguće nastaviti sa ažuriranjem zato što ta verzija ne uključuje potrebne podatke!</value>
</data>
@@ -234,10 +232,7 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>Traženje nove verzije...</value>
</data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<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 name="UpdateFinished" xml:space="preserve">
<value>Proces ažuriranja je završen!</value>
</data>
@@ -264,10 +259,7 @@ StackTrace:
<value>Molimo Vas unestie vašu Steam prijavu: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamParentalCode" xml:space="preserve">
<value>Please enter Steam parental code: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamPassword" xml:space="preserve">
<value>Molimo Vas da uneste vašu Steam lozinku: </value>
<comment>Please note that this translation should end with space</comment>
@@ -293,14 +285,8 @@ StackTrace:
<value>{0}/{1} botova je pokrenuto, sa ukupno {2} igrica ({3} karata) preostalih da se idle-uje.</value>
<comment>{0} will be replaced by number of active bots, {1} will be replaced by total number of bots, {2} will be replaced by total number of games left to idle, {3} will be replaced by total number of cards left to idle</comment>
</data>
<data name="BotStatusIdling" xml:space="preserve">
<value>Bot is idling game: {0} ({1}, {2} card drops remaining) from a total of {3} games ({4} cards) left to idle (~{5} remaining).</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by number of cards left to idle, {3} will be replaced by total number of games to idle, {4} will be replaced by total number of cards to idle, {5} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="BotStatusIdlingList" xml:space="preserve">
<value>Bot is idling games: {0} from a total of {1} games ({2} cards) left to idle (~{3} remaining).</value>
<comment>{0} will be replaced by list of the games (IDs, numbers), {1} will be replaced by total number of games to idle, {2} will be replaced by total number of cards to idle, {3} will be replaced by translated TimeSpan string (such as "1 day, 5 hours and 30 minutes")</comment>
</data>
<data name="CheckingFirstBadgePage" xml:space="preserve">
<value>Proveravanje prve stranje bedževa...</value>
</data>
@@ -336,9 +322,7 @@ StackTrace:
<data name="IdlingStopped" xml:space="preserve">
<value>Idle-ovanje zaustavljeno!</value>
</data>
<data name="IgnoredPermanentPauseEnabled" xml:space="preserve">
<value>Ignoring this request, as permanent pause is enabled!</value>
</data>
<data name="NothingToIdle" xml:space="preserve">
<value>Ništa nije preostalo da se idle-uje na ovom nalogu!</value>
</data>
@@ -372,9 +356,7 @@ StackTrace:
<data name="UnknownCommand" xml:space="preserve">
<value>Nepoznata komanda!</value>
</data>
<data name="WarningCouldNotCheckBadges" xml:space="preserve">
<value>Could not get badges' information, we will try again later!</value>
</data>
<data name="WarningCouldNotCheckCardsStatus" xml:space="preserve">
<value>Nije uspela provera status za: {0} ({1}), probaćemo kasnije!</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
@@ -383,9 +365,7 @@ StackTrace:
<value>Prihvatanje poklona: {0}...</value>
<comment>{0} will be replaced by giftID (number)</comment>
</data>
<data name="BotAccountLimited" xml:space="preserve">
<value>This account is limited, idling process is unavailable until the restriction is removed!</value>
</data>
<data name="BotAddLicense" xml:space="preserve">
<value>ID: {0} | Status: {1}</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by status string</comment>
@@ -394,9 +374,7 @@ StackTrace:
<value>ID: {0} | Status: {1} | Stavke: {2}</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by status string, {2} will be replaced by list of granted IDs (numbers), separated by a comma</comment>
</data>
<data name="BotAlreadyRunning" xml:space="preserve">
<value>This bot is already running!</value>
</data>
<data name="BotAuthenticatorConverting" xml:space="preserve">
<value>Konvertovanje .maFile u ASF format...</value>
</data>
@@ -438,10 +416,7 @@ StackTrace:
<data name="BotInstanceNotStartingBecauseDisabled" xml:space="preserve">
<value>Ovaj bot se ne započinje zato što je onemogućen u konfiguracionom fajlu!</value>
</data>
<data name="BotInvalidAuthenticatorDuringLogin" xml:space="preserve">
<value>Received TwoFactorCodeMismatch error code {0} times in a row. Either your 2FA credentials are no longer valid, or your clock is out of sync, aborting!</value>
<comment>{0} will be replaced by maximum allowed number of failed 2FA attempts</comment>
</data>
<data name="BotLoggedOff" xml:space="preserve">
<value>Odjavljen sa Steam-a: {0}</value>
<comment>{0} will be replaced by logging off reason (string)</comment>
@@ -459,9 +434,7 @@ StackTrace:
<data name="BotLootingFailed" xml:space="preserve">
<value>Ponuda za ramenu nije uspela!</value>
</data>
<data name="BotLootingMasterNotDefined" xml:space="preserve">
<value>Trade couldn't be sent because there is no user with master permission defined!</value>
</data>
<data name="BotLootingSuccess" xml:space="preserve">
<value>Ponuda za razmenu je uspešno poslata!</value>
</data>
@@ -474,18 +447,9 @@ StackTrace:
<data name="BotNotConnected" xml:space="preserve">
<value>Ova instanca bot-a nije povezana!</value>
</data>
<data name="BotNotOwnedYet" xml:space="preserve">
<value>Not owned yet: {0}</value>
<comment>{0} will be replaced by query (string)</comment>
</data>
<data name="BotOwnedAlreadyWithName" xml:space="preserve">
<value>Owned already: {0} | {1}</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="BotRateLimitExceeded" xml:space="preserve">
<value>Rate limit exceeded, we will retry after {0} of cooldown...</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "25 minutes")</comment>
</data>
<data name="BotReconnecting" xml:space="preserve">
<value>Ponovo povezivanje...</value>
</data>
@@ -546,19 +510,12 @@ StackTrace:
<data name="BotConnecting" xml:space="preserve">
<value>Povezivanje...</value>
</data>
<data name="BotHeartBeatFailed" xml:space="preserve">
<value>Failed to disconnect the client. Abandoning this bot instance!</value>
</data>
<data name="BotSteamDirectoryInitializationFailed" xml:space="preserve">
<value>Could not initialize SteamDirectory: connecting with Steam Network might take much longer than usual!</value>
</data>
<data name="BotStopping" xml:space="preserve">
<value>Zaustavljanje...</value>
</data>
<data name="ErrorBotConfigInvalid" xml:space="preserve">
<value>Your bot config is invalid. Please verify content of {0} and try again!</value>
<comment>{0} will be replaced by file's path</comment>
</data>
<data name="ErrorDatabaseInvalid" xml:space="preserve">
<value>Trajna baza podataka ne može biti učitana, ako se problem nastavi, molimo Vas da uklonite {0} da bi se baza podataka ponovo stvorila!</value>
<comment>{0} will be replaced by file's path</comment>
@@ -573,164 +530,55 @@ StackTrace:
<data name="Welcome" xml:space="preserve">
<value>Izgleda da prvi put pokrećete Asf, dobrodošli!</value>
</data>
<data name="ErrorInvalidCurrentCulture" xml:space="preserve">
<value>Your provided CurrentCulture is invalid, ASF will keep running with the default one!</value>
</data>
<data name="TranslationIncomplete" xml:space="preserve">
<value>ASF će pokušati da koristi vašu preferiranu {0} "kulturu", ali prevod u taj jezik je samo {1} gotov. Možda bi ste mogli da nam pomogne u prevodu ASF na vaš jezik?</value>
<comment>{0} will be replaced by culture code, such as "en-US", {1} will be replaced by completeness percentage, such as "78.5%"</comment>
</data>
<data name="IdlingGameNotPossible" xml:space="preserve">
<value>Idling {0} ({1}) is temporarily disabled, as ASF is not able to play that game at the moment.</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="WarningIdlingGameMismatch" xml:space="preserve">
<value>ASF detected ID mismatch for {0} ({1}) and will use ID of {2} instead.</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name, {2} will be replaced by game's ID (number)</comment>
</data>
<data name="BotVersion" xml:space="preserve">
<value>{0} V{1}</value>
<comment>{0} will be replaced by program's name (e.g. "ASF"), {1} will be replaced by program's version (e.g. "1.0.0.0"). This string typically has nothing to translate and you should leave it as it is, unless you need to change the format, e.g. in RTL languages.</comment>
</data>
<data name="BotAccountLocked" xml:space="preserve">
<value>This account is locked, idling process is permanently unavailable!</value>
</data>
<data name="BotStatusLocked" xml:space="preserve">
<value>Bot is locked and can't drop any cards through idling.</value>
</data>
<data name="ErrorFunctionOnlyInHeadlessMode" xml:space="preserve">
<value>This function is available only in headless mode!</value>
</data>
<data name="BotOwnedAlready" xml:space="preserve">
<value>Owned already: {0}</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="ErrorAccessDenied" xml:space="preserve">
<value>Access denied!</value>
</data>
<data name="WarningPreReleaseVersion" xml:space="preserve">
<value>You're using a version that is newer than latest released version for your update channel. Please note that 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.</value>
</data>
<data name="BotStats" xml:space="preserve">
<value>Current memory usage: {0} MB.</value>
<comment>{0} will be replaced by number (in megabytes) of memory being used</comment>
</data>
<data name="ClearingDiscoveryQueue" xml:space="preserve">
<value>Clearing Steam discovery queue #{0}...</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="DoneClearingDiscoveryQueue" xml:space="preserve">
<value>Done clearing Steam discovery queue #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverviewPerGame" xml:space="preserve">
<value>{0}/{1} bots already own game {2}.</value>
<comment>{0} will be replaced by number of bots that already own particular game being checked, {1} will be replaced by total number of bots that were checked during the process, {2} will be replaced by game's ID (number)</comment>
</data>
<data name="BotRefreshingPackagesData" xml:space="preserve">
<value>Refreshing packages data...</value>
</data>
<data name="WarningDeprecated" xml:space="preserve">
<value>Usage of {0} is deprecated and will be removed in future versions of the program. Please use {1} instead.</value>
<comment>{0} will be replaced by the name of deprecated property (such as argument, config property or likewise), {1} will be replaced by the name of valid replacement (such as another argument or config property)</comment>
</data>
<data name="BotAcceptedDonationTrade" xml:space="preserve">
<value>Accepted donation trade: {0}</value>
<comment>{0} will be replaced by trade's ID (number)</comment>
</data>
<data name="WarningWorkaroundTriggered" xml:space="preserve">
<value>Workaround for {0} bug has been triggered.</value>
<comment>{0} will be replaced by the bug's name provided by ASF</comment>
</data>
<data name="TargetBotNotConnected" xml:space="preserve">
<value>Target bot instance is not connected!</value>
</data>
<data name="BotWalletBalance" xml:space="preserve">
<value>Wallet balance: {0} {1}</value>
<comment>{0} will be replaced by wallet balance value, {1} will be replaced by currency name</comment>
</data>
<data name="BotHasNoWallet" xml:space="preserve">
<value>Bot has no wallet.</value>
</data>
<data name="BotLevel" xml:space="preserve">
<value>Bot has level {0}.</value>
<comment>{0} will be replaced by bot's level</comment>
</data>
<data name="ActivelyMatchingItems" xml:space="preserve">
<value>Matching Steam items, round #{0}...</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="DoneActivelyMatchingItems" xml:space="preserve">
<value>Done matching Steam items, round #{0}.</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="ErrorAborted" xml:space="preserve">
<value>Aborted!</value>
</data>
<data name="ActivelyMatchingItemsRound" xml:space="preserve">
<value>Matched a total of {0} sets this round.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="WarningExcessiveBotsCount" xml:space="preserve">
<value>You're running more personal bot accounts than our upper recommended limit ({0}). Be advised that this setup is not supported and might cause various Steam-related issues, including account suspensions. Check out the FAQ for more details.</value>
<comment>{0} will be replaced by our maximum recommended bots count (number)</comment>
</data>
<data name="PluginLoaded" xml:space="preserve">
<value>{0} has been loaded successfully!</value>
<comment>{0} will be replaced by the name of the custom ASF plugin</comment>
</data>
<data name="PluginLoading" xml:space="preserve">
<value>Loading {0} V{1}...</value>
<comment>{0} will be replaced by the name of the custom ASF plugin, {1} will be replaced by its version</comment>
</data>
<data name="NothingFound" xml:space="preserve">
<value>Nothing found!</value>
</data>
<data name="PluginsWarning" xml:space="preserve">
<value>You've loaded one or more of custom plugins into the ASF. Since we're unable to offer a support for modded setups, please reach the appropriate developers of the plugins that you decided to use in case of any issues.</value>
</data>
<data name="PleaseWait" xml:space="preserve">
<value>Please wait...</value>
</data>
<data name="EnterCommand" xml:space="preserve">
<value>Enter command: </value>
</data>
<data name="Executing" xml:space="preserve">
<value>Executing...</value>
</data>
<data name="InteractiveConsoleEnabled" xml:space="preserve">
<value>Interactive console is now active, type 'c' in order to enter command mode.</value>
</data>
<data name="InteractiveConsoleNotAvailable" xml:space="preserve">
<value>Interactive console is not available due to missing {0} config property.</value>
<comment>{0} will be replaced by the name of the missing config property (string)</comment>
</data>
<data name="Response" xml:space="preserve">
<value>Response: {0}</value>
<comment>{0} will be replaced by the generated response (string)</comment>
</data>
<data name="BotGamesToRedeemInBackgroundCount" xml:space="preserve">
<value>Bot has {0} games remaining in its background queue.</value>
<comment>{0} will be replaced by remaining number of games in BGR's queue</comment>
</data>
<data name="ErrorSingleInstanceRequired" xml:space="preserve">
<value>ASF process is already running for this working directory, aborting!</value>
</data>
<data name="BotHandledConfirmations" xml:space="preserve">
<value>Successfully handled {0} confirmations!</value>
<comment>{0} will be replaced by number of confirmations</comment>
</data>
<data name="BotExtraIdlingCooldown" xml:space="preserve">
<value>Waiting up to {0} to ensure that we're free to start idling...</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "1 minute")</comment>
</data>
<data name="UpdateCleanup" xml:space="preserve">
<value>Cleaning up old files after update...</value>
</data>
<data name="BotGeneratingSteamParentalCode" xml:space="preserve">
<value>Generating Steam parental code, this can take a while, consider putting it in the config instead...</value>
</data>
<data name="IPCConfigChanged" xml:space="preserve">
<value>IPC config has been changed!</value>
</data>
</root>

View File

@@ -265,10 +265,7 @@ StackTrace:
<value>Vänligen ange din Steam-inloggning: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamParentalCode" xml:space="preserve">
<value>Please enter Steam parental code: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="UserInputSteamPassword" xml:space="preserve">
<value>Vänligen ange ditt Steam lösenord: </value>
<comment>Please note that this translation should end with space</comment>
@@ -280,9 +277,7 @@ StackTrace:
<data name="IPCReady" xml:space="preserve">
<value>IPC servern är redo!</value>
</data>
<data name="IPCStarting" xml:space="preserve">
<value>Starting IPC server...</value>
</data>
<data name="BotAlreadyStopped" xml:space="preserve">
<value>Bot-instansen har redan stoppats!</value>
</data>
@@ -337,9 +332,7 @@ StackTrace:
<data name="IdlingStopped" xml:space="preserve">
<value>Farmandet stoppad!</value>
</data>
<data name="IgnoredPermanentPauseEnabled" xml:space="preserve">
<value>Ignoring this request, as permanent pause is enabled!</value>
</data>
<data name="NothingToIdle" xml:space="preserve">
<value>Vi har inte något att farma på detta konto!</value>
</data>
@@ -439,18 +432,12 @@ StackTrace:
<data name="BotInstanceNotStartingBecauseDisabled" xml:space="preserve">
<value>Startar inte denna bot-instansen för att den är inaktiverad i config filen!</value>
</data>
<data name="BotInvalidAuthenticatorDuringLogin" xml:space="preserve">
<value>Received TwoFactorCodeMismatch error code {0} times in a row. Either your 2FA credentials are no longer valid, or your clock is out of sync, aborting!</value>
<comment>{0} will be replaced by maximum allowed number of failed 2FA attempts</comment>
</data>
<data name="BotLoggedOff" xml:space="preserve">
<value>Utloggad från Steam: {0}</value>
<comment>{0} will be replaced by logging off reason (string)</comment>
</data>
<data name="BotLoggedOn" xml:space="preserve">
<value>Successfully logged on as {0}.</value>
<comment>{0} will be replaced by steam ID (number)</comment>
</data>
<data name="BotLoggingIn" xml:space="preserve">
<value>Loggar in...</value>
</data>
@@ -466,9 +453,7 @@ StackTrace:
<data name="BotLootingSuccess" xml:space="preserve">
<value>Bytesförslag skickat!</value>
</data>
<data name="BotSendingTradeToYourself" xml:space="preserve">
<value>You can't send trade to yourself!</value>
</data>
<data name="BotNoASFAuthenticator" xml:space="preserve">
<value>Denna botten har inte ASF 2FA aktiverat! Glömde du att importera din authenticator som ASF 2FA?</value>
</data>
@@ -483,10 +468,7 @@ StackTrace:
<value>Ägs redan: {0} | {1}</value>
<comment>{0} will be replaced by game's ID (number), {1} will be replaced by game's name</comment>
</data>
<data name="BotRateLimitExceeded" xml:space="preserve">
<value>Rate limit exceeded, we will retry after {0} of cooldown...</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "25 minutes")</comment>
</data>
<data name="BotReconnecting" xml:space="preserve">
<value>Återansluter...</value>
</data>
@@ -624,114 +606,35 @@ StackTrace:
<value>Genomgått Steam discovery-kön #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverviewPerGame" xml:space="preserve">
<value>{0}/{1} bots already own game {2}.</value>
<comment>{0} will be replaced by number of bots that already own particular game being checked, {1} will be replaced by total number of bots that were checked during the process, {2} will be replaced by game's ID (number)</comment>
</data>
<data name="BotRefreshingPackagesData" xml:space="preserve">
<value>Refreshing packages data...</value>
</data>
<data name="WarningDeprecated" xml:space="preserve">
<value>Usage of {0} is deprecated and will be removed in future versions of the program. Please use {1} instead.</value>
<comment>{0} will be replaced by the name of deprecated property (such as argument, config property or likewise), {1} will be replaced by the name of valid replacement (such as another argument or config property)</comment>
</data>
<data name="BotAcceptedDonationTrade" xml:space="preserve">
<value>Accepted donation trade: {0}</value>
<comment>{0} will be replaced by trade's ID (number)</comment>
</data>
<data name="WarningWorkaroundTriggered" xml:space="preserve">
<value>Workaround for {0} bug has been triggered.</value>
<comment>{0} will be replaced by the bug's name provided by ASF</comment>
</data>
<data name="TargetBotNotConnected" xml:space="preserve">
<value>Target bot instance is not connected!</value>
</data>
<data name="BotWalletBalance" xml:space="preserve">
<value>Wallet balance: {0} {1}</value>
<comment>{0} will be replaced by wallet balance value, {1} will be replaced by currency name</comment>
</data>
<data name="BotHasNoWallet" xml:space="preserve">
<value>Bot has no wallet.</value>
</data>
<data name="BotLevel" xml:space="preserve">
<value>Bot has level {0}.</value>
<comment>{0} will be replaced by bot's level</comment>
</data>
<data name="ActivelyMatchingItems" xml:space="preserve">
<value>Matching Steam items, round #{0}...</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="DoneActivelyMatchingItems" xml:space="preserve">
<value>Done matching Steam items, round #{0}.</value>
<comment>{0} will be replaced by round number</comment>
</data>
<data name="ErrorAborted" xml:space="preserve">
<value>Aborted!</value>
</data>
<data name="ActivelyMatchingItemsRound" xml:space="preserve">
<value>Matched a total of {0} sets this round.</value>
<comment>{0} will be replaced by number of sets traded</comment>
</data>
<data name="WarningExcessiveBotsCount" xml:space="preserve">
<value>You're running more personal bot accounts than our upper recommended limit ({0}). Be advised that this setup is not supported and might cause various Steam-related issues, including account suspensions. Check out the FAQ for more details.</value>
<comment>{0} will be replaced by our maximum recommended bots count (number)</comment>
</data>
<data name="PluginLoaded" xml:space="preserve">
<value>{0} has been loaded successfully!</value>
<comment>{0} will be replaced by the name of the custom ASF plugin</comment>
</data>
<data name="PluginLoading" xml:space="preserve">
<value>Loading {0} V{1}...</value>
<comment>{0} will be replaced by the name of the custom ASF plugin, {1} will be replaced by its version</comment>
</data>
<data name="NothingFound" xml:space="preserve">
<value>Nothing found!</value>
</data>
<data name="PluginsWarning" xml:space="preserve">
<value>You've loaded one or more of custom plugins into the ASF. Since we're unable to offer a support for modded setups, please reach the appropriate developers of the plugins that you decided to use in case of any issues.</value>
</data>
<data name="PleaseWait" xml:space="preserve">
<value>Please wait...</value>
</data>
<data name="EnterCommand" xml:space="preserve">
<value>Enter command: </value>
</data>
<data name="Executing" xml:space="preserve">
<value>Executing...</value>
</data>
<data name="InteractiveConsoleEnabled" xml:space="preserve">
<value>Interactive console is now active, type 'c' in order to enter command mode.</value>
</data>
<data name="InteractiveConsoleNotAvailable" xml:space="preserve">
<value>Interactive console is not available due to missing {0} config property.</value>
<comment>{0} will be replaced by the name of the missing config property (string)</comment>
</data>
<data name="Response" xml:space="preserve">
<value>Response: {0}</value>
<comment>{0} will be replaced by the generated response (string)</comment>
</data>
<data name="BotGamesToRedeemInBackgroundCount" xml:space="preserve">
<value>Bot has {0} games remaining in its background queue.</value>
<comment>{0} will be replaced by remaining number of games in BGR's queue</comment>
</data>
<data name="ErrorSingleInstanceRequired" xml:space="preserve">
<value>ASF process is already running for this working directory, aborting!</value>
</data>
<data name="BotHandledConfirmations" xml:space="preserve">
<value>Successfully handled {0} confirmations!</value>
<comment>{0} will be replaced by number of confirmations</comment>
</data>
<data name="BotExtraIdlingCooldown" xml:space="preserve">
<value>Waiting up to {0} to ensure that we're free to start idling...</value>
<comment>{0} will be replaced by translated TimeSpan string (such as "1 minute")</comment>
</data>
<data name="UpdateCleanup" xml:space="preserve">
<value>Cleaning up old files after update...</value>
</data>
<data name="BotGeneratingSteamParentalCode" xml:space="preserve">
<value>Generating Steam parental code, this can take a while, consider putting it in the config instead...</value>
</data>
<data name="IPCConfigChanged" xml:space="preserve">
<value>IPC config has been changed!</value>
</data>
</root>

View File

@@ -0,0 +1,51 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Collections.Generic;
using System.Threading.Tasks;
using JetBrains.Annotations;
using SteamKit2;
namespace ArchiSteamFarm.Plugins {
[PublicAPI]
public interface ISteamPICSChanges : IPlugin {
/// <summary>
/// ASF uses this method for determining the point in time from which it should keep history going upon a restart. The actual point in time that will be used is calculated as the lowest change number from all loaded plugins, to guarantee that no plugin will miss any changes, while allowing possible duplicates for those plugins that were already synchronized with newer changes. If you don't care about persistent state and just want to receive the ongoing history, you should return 0 (which is equal to "I'm fine with any"). If there won't be any plugin asking for a specific point in time, ASF will start returning entries since the start of the program.
/// </summary>
/// <returns>The most recent change number from which you're fine to receive <see cref="OnPICSChanges" /></returns>
[NotNull]
Task<uint> GetPreferredChangeNumberToStartFrom();
/// <summary>
/// ASF will call this method upon receiving any app/package PICS changes. The history is guaranteed to be precise and continuous starting from <see cref="GetPreferredChangeNumberToStartFrom" /> until <see cref="OnPICSChangesRestart" /> is called. It's possible for this method to have duplicated calls across different runs, in particular when some other plugin asks for lower <see cref="GetPreferredChangeNumberToStartFrom" />, therefore you should keep that in mind (and refer to change number of standalone apps/packages).
/// </summary>
/// <param name="currentChangeNumber">The change number of current callback.</param>
/// <param name="appChanges">App changes that happened since the previous call of this method. Can be empty.</param>
/// <param name="packageChanges">Package changes that happened since the previous call of this method. Can be empty.</param>
void OnPICSChanges(uint currentChangeNumber, [NotNull] IReadOnlyDictionary<uint, SteamApps.PICSChangesCallback.PICSChangeData> appChanges, [NotNull] IReadOnlyDictionary<uint, SteamApps.PICSChangesCallback.PICSChangeData> packageChanges);
/// <summary>
/// ASF will call this method when it'll be necessary to restart the history of PICS changes. This can happen due to Steam limitation in which we're unable to keep history going if we're too far behind (approx 5k changeNumbers). If you're relying on continuous history of app/package PICS changes sent by <see cref="OnPICSChanges" />, ASF can no longer guarantee that upon calling this method, therefore you should start clean.
/// </summary>
/// <param name="currentChangeNumber">The change number from which we're restarting the PICS history.</param>
void OnPICSChangesRestart(uint currentChangeNumber);
}
}

View File

@@ -0,0 +1,32 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
namespace ArchiSteamFarm.Plugins {
internal abstract class OfficialPlugin : IPlugin {
public abstract string Name { get; }
public abstract Version Version { get; }
public abstract void OnLoaded();
internal bool HasSameVersion() => Version == SharedInfo.Version;
}
}

View File

@@ -37,7 +37,9 @@ using SteamKit2;
namespace ArchiSteamFarm.Plugins {
internal static class PluginsCore {
internal static bool HasActivePluginsLoaded => ActivePlugins?.Count > 0;
internal static bool HasCustomPluginsLoaded => HasActivePluginsLoaded && ActivePlugins.Any(plugin => !(plugin is OfficialPlugin officialPlugin) || !officialPlugin.HasSameVersion());
private static bool HasActivePluginsLoaded => ActivePlugins?.Count > 0;
[ImportMany]
private static ImmutableHashSet<IPlugin> ActivePlugins { get; set; }
@@ -63,6 +65,30 @@ namespace ArchiSteamFarm.Plugins {
return result ?? StringComparer.Ordinal;
}
internal static async Task<uint> GetChangeNumberToStartFrom() {
if (!HasActivePluginsLoaded) {
return 0;
}
IList<uint> results;
try {
results = await Utilities.InParallel(ActivePlugins.OfType<ISteamPICSChanges>().Select(plugin => plugin.GetPreferredChangeNumberToStartFrom())).ConfigureAwait(false);
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
return 0;
}
uint changeNumberToStartFrom = uint.MaxValue;
foreach (uint result in results.Where(result => (result > 0) && (result < changeNumberToStartFrom))) {
changeNumberToStartFrom = result;
}
return changeNumberToStartFrom == uint.MaxValue ? 0 : changeNumberToStartFrom;
}
internal static bool InitPlugins() {
if (HasActivePluginsLoaded) {
return false;
@@ -123,10 +149,13 @@ namespace ArchiSteamFarm.Plugins {
}
ActivePlugins = activePlugins.ToImmutableHashSet();
ASF.ArchiLogger.LogGenericInfo(Strings.PluginsWarning);
// Loading plugins changes the program identifier, refresh the console title
Console.Title = SharedInfo.ProgramIdentifier;
if (HasCustomPluginsLoaded) {
ASF.ArchiLogger.LogGenericInfo(Strings.PluginsWarning);
// Loading plugins changes the program identifier, refresh the console title
Console.Title = SharedInfo.ProgramIdentifier;
}
return invalidPlugins.Count == 0;
}
@@ -493,6 +522,42 @@ namespace ArchiSteamFarm.Plugins {
}
}
internal static async Task OnPICSChanges(uint currentChangeNumber, IReadOnlyDictionary<uint, SteamApps.PICSChangesCallback.PICSChangeData> appChanges, IReadOnlyDictionary<uint, SteamApps.PICSChangesCallback.PICSChangeData> packageChanges) {
if ((currentChangeNumber == 0) || (appChanges == null) || (packageChanges == null)) {
ASF.ArchiLogger.LogNullError(nameof(currentChangeNumber) + " || " + nameof(appChanges) + " || " + nameof(packageChanges));
return;
}
if (!HasActivePluginsLoaded) {
return;
}
try {
await Utilities.InParallel(ActivePlugins.OfType<ISteamPICSChanges>().Select(plugin => Task.Run(() => plugin.OnPICSChanges(currentChangeNumber, appChanges, packageChanges)))).ConfigureAwait(false);
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
}
}
internal static async Task OnPICSChangesRestart(uint currentChangeNumber) {
if (currentChangeNumber == 0) {
ASF.ArchiLogger.LogNullError(nameof(currentChangeNumber));
return;
}
if (!HasActivePluginsLoaded) {
return;
}
try {
await Utilities.InParallel(ActivePlugins.OfType<ISteamPICSChanges>().Select(plugin => Task.Run(() => plugin.OnPICSChangesRestart(currentChangeNumber)))).ConfigureAwait(false);
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
}
}
private static HashSet<Assembly> LoadAssembliesFrom(string path) {
if (string.IsNullOrEmpty(path)) {
ASF.ArchiLogger.LogNullError(nameof(path));

View File

@@ -64,6 +64,20 @@ namespace ArchiSteamFarm {
await System.IO.File.AppendAllTextAsync(path, contents).ConfigureAwait(false);
#endif
#pragma warning disable IDE0022
public static void Move([NotNull] string sourceFileName, [NotNull] string destFileName, bool overwrite) {
#if NETFRAMEWORK
if (overwrite && System.IO.File.Exists(destFileName)) {
System.IO.File.Delete(destFileName);
}
System.IO.File.Move(sourceFileName, destFileName);
#else
System.IO.File.Move(sourceFileName, destFileName, overwrite);
#endif
}
#pragma warning restore IDE0022
[ItemNotNull]
public static async Task<byte[]> ReadAllBytesAsync([NotNull] string path) =>
#if NETFRAMEWORK

View File

@@ -26,7 +26,10 @@ using ArchiSteamFarm.Plugins;
using JetBrains.Annotations;
namespace ArchiSteamFarm {
internal static class SharedInfo {
public static class SharedInfo {
[PublicAPI]
public const string ConfigDirectory = "config";
internal const ulong ArchiSteamID = 76561198006963719;
internal const string ArchivalLogFile = "log.{#}.txt";
internal const string ArchivalLogsDirectory = "logs";
@@ -34,7 +37,6 @@ namespace ArchiSteamFarm {
internal const ulong ASFGroupSteamID = 103582791440160998;
internal const string AssemblyDocumentation = AssemblyName + ".xml";
internal const string AssemblyName = nameof(ArchiSteamFarm);
internal const string ConfigDirectory = "config";
internal const string DatabaseExtension = ".db";
internal const string DebugDirectory = "debug";
internal const string EnvironmentVariableCryptKey = ASF + "_CRYPTKEY";
@@ -83,7 +85,7 @@ namespace ArchiSteamFarm {
internal static string ProgramIdentifier => PublicIdentifier + " V" + Version + " (" + BuildInfo.Variant + "/" + ModuleVersion + " | " + OS.Variant + ")";
[NotNull]
internal static string PublicIdentifier => AssemblyName + (BuildInfo.IsCustomBuild ? "-custom" : PluginsCore.HasActivePluginsLoaded ? "-modded" : "");
internal static string PublicIdentifier => AssemblyName + (BuildInfo.IsCustomBuild ? "-custom" : PluginsCore.HasCustomPluginsLoaded ? "-modded" : "");
[NotNull]
internal static Version Version => Assembly.GetEntryAssembly()?.GetName().Version ?? throw new ArgumentNullException(nameof(Version));

View File

@@ -0,0 +1,108 @@
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
// Copyright 2015-2020 Łukasz "JustArchi" Domeradzki
// Contact: JustArchi@JustArchi.net
// |
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// |
// http://www.apache.org/licenses/LICENSE-2.0
// |
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Plugins;
using SteamKit2;
namespace ArchiSteamFarm {
internal static class SteamPICSChanges {
private const byte RefreshTimerInMinutes = 5;
private static readonly SemaphoreSlim RefreshSemaphore = new SemaphoreSlim(1, 1);
private static readonly Timer RefreshTimer = new Timer(async e => await RefreshChanges().ConfigureAwait(false));
private static uint LastChangeNumber;
private static bool TimerAlreadySet;
internal static void Init(uint changeNumberToStartFrom) => LastChangeNumber = changeNumberToStartFrom;
internal static void OnBotLoggedOn() {
if (TimerAlreadySet) {
return;
}
lock (RefreshTimer) {
if (TimerAlreadySet) {
return;
}
TimerAlreadySet = true;
RefreshTimer.Change(TimeSpan.Zero, TimeSpan.FromMinutes(RefreshTimerInMinutes));
}
}
private static async Task RefreshChanges() {
if (!await RefreshSemaphore.WaitAsync(0).ConfigureAwait(false)) {
return;
}
try {
Bot refreshBot = null;
SteamApps.PICSChangesCallback picsChanges = null;
for (byte i = 0; (i < WebBrowser.MaxTries) && (picsChanges == null); i++) {
refreshBot = Bot.Bots.Values.FirstOrDefault(bot => bot.IsConnectedAndLoggedOn);
if (refreshBot == null) {
return;
}
try {
picsChanges = await refreshBot.SteamApps.PICSGetChangesSince(LastChangeNumber, true, true);
} catch (Exception e) {
refreshBot.ArchiLogger.LogGenericWarningException(e);
}
}
if ((refreshBot == null) || (picsChanges == null)) {
ASF.ArchiLogger.LogGenericWarning(Strings.WarningFailed);
return;
}
if (picsChanges.CurrentChangeNumber == picsChanges.LastChangeNumber) {
return;
}
LastChangeNumber = picsChanges.CurrentChangeNumber;
if ((picsChanges.AppChanges.Count == 0) && (picsChanges.PackageChanges.Count == 0)) {
await PluginsCore.OnPICSChangesRestart(picsChanges.CurrentChangeNumber).ConfigureAwait(false);
return;
}
if (picsChanges.PackageChanges.Count > 0) {
await ASF.GlobalDatabase.RefreshPackages(refreshBot, picsChanges.PackageChanges.ToDictionary(package => package.Key, package => package.Value.ChangeNumber)).ConfigureAwait(false);
}
await PluginsCore.OnPICSChanges(picsChanges.CurrentChangeNumber, picsChanges.AppChanges, picsChanges.PackageChanges).ConfigureAwait(false);
} finally {
RefreshSemaphore.Release();
}
}
}
}

View File

@@ -196,6 +196,43 @@ namespace ArchiSteamFarm {
return (text.Length % 2 == 0) && text.All(Uri.IsHexDigit);
}
[PublicAPI]
public static int RandomNext() {
lock (Random) {
return Random.Next();
}
}
[PublicAPI]
public static int RandomNext(int maxValue) {
if (maxValue < 0) {
throw new ArgumentOutOfRangeException(nameof(maxValue));
}
if (maxValue <= 1) {
return maxValue;
}
lock (Random) {
return Random.Next(maxValue);
}
}
[PublicAPI]
public static int RandomNext(int minValue, int maxValue) {
if (minValue > maxValue) {
throw new ArgumentOutOfRangeException(nameof(minValue) + " && " + nameof(maxValue));
}
if (minValue >= maxValue - 1) {
return minValue;
}
lock (Random) {
return Random.Next(minValue, maxValue);
}
}
[ItemNotNull]
[NotNull]
[PublicAPI]
@@ -268,12 +305,6 @@ namespace ArchiSteamFarm {
return cookies.Count > 0 ? (from Cookie cookie in cookies where cookie.Name.Equals(name) select cookie.Value).FirstOrDefault() : null;
}
internal static int RandomNext() {
lock (Random) {
return Random.Next();
}
}
internal static bool RelativeDirectoryStartsWith(string directory, params string[] prefixes) {
if (string.IsNullOrEmpty(directory) || (prefixes == null) || (prefixes.Length == 0)) {
ASF.ArchiLogger.LogNullError(nameof(directory) + " || " + nameof(prefixes));

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>4.2.2.1</Version>
<Version>4.2.2.4</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -7,25 +7,28 @@ RUN echo "node: $(node --version)" && \
npm run deploy
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-dotnet
ARG STEAM_TOKEN_DUMPER_TOKEN
ENV CONFIGURATION Release
ENV DOTNET_CLI_TELEMETRY_OPTOUT 1
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
ENV NET_CORE_VERSION netcoreapp3.1
ENV STEAM_TOKEN_DUMPER_NAME ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
WORKDIR /app
COPY --from=build-node /app/dist ASF-ui/dist
COPY ArchiSteamFarm ArchiSteamFarm
COPY ArchiSteamFarm.Tests ArchiSteamFarm.Tests
COPY ArchiSteamFarm.OfficialPlugins.SteamTokenDumper ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
COPY resources resources
COPY Directory.Build.props Directory.Build.props
RUN dotnet --info && \
# TODO: Remove workaround for https://github.com/microsoft/msbuild/issues/3897 when it's no longer needed
if [ -f "ArchiSteamFarm/Localization/Strings.zh-CN.resx" ]; then ln -s "Strings.zh-CN.resx" "ArchiSteamFarm/Localization/Strings.zh-Hans.resx"; fi && \
if [ -f "ArchiSteamFarm/Localization/Strings.zh-TW.resx" ]; then ln -s "Strings.zh-TW.resx" "ArchiSteamFarm/Localization/Strings.zh-Hant.resx"; fi && \
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
if [ -n "${STEAM_TOKEN_DUMPER_TOKEN-}" ] && [ -f "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs" ]; then sed -i "s/STEAM_TOKEN_DUMPER_TOKEN/${STEAM_TOKEN_DUMPER_TOKEN}/g" "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs"; fi && \
dotnet publish "${STEAM_TOKEN_DUMPER_NAME}" -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}" -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
dotnet clean ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o out -p:ASFVariant=linux-arm -p:PublishSingleFile=true -p:PublishTrimmed=true -r linux-arm --nologo && \
if [ -d "ArchiSteamFarm/overlay/linux-arm" ]; then cp "ArchiSteamFarm/overlay/linux-arm/"* "out"; fi
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/result" -p:ASFVariant=linux-arm -p:PublishSingleFile=true -p:PublishTrimmed=true -r linux-arm --nologo && \
if [ -d "ArchiSteamFarm/overlay/linux-arm" ]; then cp "ArchiSteamFarm/overlay/linux-arm/"* "out/result"; fi && \
if [ -f "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" ]; then mkdir -p "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; cp "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; fi
FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-buster-slim-arm32v7 AS runtime
ENV ASPNETCORE_URLS=
@@ -34,7 +37,7 @@ ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
LABEL maintainer="JustArchi <JustArchi@JustArchi.net>"
EXPOSE 1242
WORKDIR /app
COPY --from=build-dotnet /app/out .
COPY --from=build-dotnet /app/out/result .
VOLUME ["/app/config", "/app/logs", "/app/plugins"]
HEALTHCHECK CMD ["pidof", "-q", "ArchiSteamFarm"]
ENTRYPOINT ["./ArchiSteamFarm-Service.sh", "--no-restart", "--process-required", "--system-required"]

View File

@@ -7,25 +7,28 @@ RUN echo "node: $(node --version)" && \
npm run deploy
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-dotnet
ARG STEAM_TOKEN_DUMPER_TOKEN
ENV CONFIGURATION Release
ENV DOTNET_CLI_TELEMETRY_OPTOUT 1
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
ENV NET_CORE_VERSION netcoreapp3.1
ENV STEAM_TOKEN_DUMPER_NAME ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
WORKDIR /app
COPY --from=build-node /app/dist ASF-ui/dist
COPY ArchiSteamFarm ArchiSteamFarm
COPY ArchiSteamFarm.Tests ArchiSteamFarm.Tests
COPY ArchiSteamFarm.OfficialPlugins.SteamTokenDumper ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
COPY resources resources
COPY Directory.Build.props Directory.Build.props
RUN dotnet --info && \
# TODO: Remove workaround for https://github.com/microsoft/msbuild/issues/3897 when it's no longer needed
if [ -f "ArchiSteamFarm/Localization/Strings.zh-CN.resx" ]; then ln -s "Strings.zh-CN.resx" "ArchiSteamFarm/Localization/Strings.zh-Hans.resx"; fi && \
if [ -f "ArchiSteamFarm/Localization/Strings.zh-TW.resx" ]; then ln -s "Strings.zh-TW.resx" "ArchiSteamFarm/Localization/Strings.zh-Hant.resx"; fi && \
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
if [ -n "${STEAM_TOKEN_DUMPER_TOKEN-}" ] && [ -f "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs" ]; then sed -i "s/STEAM_TOKEN_DUMPER_TOKEN/${STEAM_TOKEN_DUMPER_TOKEN}/g" "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs"; fi && \
dotnet publish "${STEAM_TOKEN_DUMPER_NAME}" -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}" -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
dotnet clean ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o out -p:ASFVariant=linux-arm64 -p:PublishSingleFile=true -p:PublishTrimmed=true -r linux-arm64 --nologo && \
if [ -d "ArchiSteamFarm/overlay/linux-arm64" ]; then cp "ArchiSteamFarm/overlay/linux-arm64/"* "out"; fi
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/result" -p:ASFVariant=linux-arm64 -p:PublishSingleFile=true -p:PublishTrimmed=true -r linux-arm64 --nologo && \
if [ -d "ArchiSteamFarm/overlay/linux-arm64" ]; then cp "ArchiSteamFarm/overlay/linux-arm64/"* "out/result"; fi && \
if [ -f "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" ]; then mkdir -p "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; cp "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; fi
FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-buster-slim-arm64v8 AS runtime
ENV ASPNETCORE_URLS=
@@ -34,7 +37,7 @@ ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
LABEL maintainer="JustArchi <JustArchi@JustArchi.net>"
EXPOSE 1242
WORKDIR /app
COPY --from=build-dotnet /app/out .
COPY --from=build-dotnet /app/out/result .
VOLUME ["/app/config", "/app/logs", "/app/plugins"]
HEALTHCHECK CMD ["pidof", "-q", "ArchiSteamFarm"]
ENTRYPOINT ["./ArchiSteamFarm-Service.sh", "--no-restart", "--process-required", "--system-required"]

View File

@@ -7,25 +7,28 @@ RUN echo "node: $(node --version)" && \
npm run deploy
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-dotnet
ARG STEAM_TOKEN_DUMPER_TOKEN
ENV CONFIGURATION Release
ENV DOTNET_CLI_TELEMETRY_OPTOUT 1
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
ENV NET_CORE_VERSION netcoreapp3.1
ENV STEAM_TOKEN_DUMPER_NAME ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
WORKDIR /app
COPY --from=build-node /app/dist ASF-ui/dist
COPY ArchiSteamFarm ArchiSteamFarm
COPY ArchiSteamFarm.Tests ArchiSteamFarm.Tests
COPY ArchiSteamFarm.OfficialPlugins.SteamTokenDumper ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
COPY resources resources
COPY Directory.Build.props Directory.Build.props
RUN dotnet --info && \
# TODO: Remove workaround for https://github.com/microsoft/msbuild/issues/3897 when it's no longer needed
if [ -f "ArchiSteamFarm/Localization/Strings.zh-CN.resx" ]; then ln -s "Strings.zh-CN.resx" "ArchiSteamFarm/Localization/Strings.zh-Hans.resx"; fi && \
if [ -f "ArchiSteamFarm/Localization/Strings.zh-TW.resx" ]; then ln -s "Strings.zh-TW.resx" "ArchiSteamFarm/Localization/Strings.zh-Hant.resx"; fi && \
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
if [ -n "${STEAM_TOKEN_DUMPER_TOKEN-}" ] && [ -f "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs" ]; then sed -i "s/STEAM_TOKEN_DUMPER_TOKEN/${STEAM_TOKEN_DUMPER_TOKEN}/g" "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs"; fi && \
dotnet publish "${STEAM_TOKEN_DUMPER_NAME}" -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}" -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
dotnet clean ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o out -p:ASFVariant=linux-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true -r linux-x64 --nologo && \
if [ -d "ArchiSteamFarm/overlay/linux-x64" ]; then cp "ArchiSteamFarm/overlay/linux-x64/"* "out"; fi
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/result" -p:ASFVariant=linux-x64 -p:PublishSingleFile=true -p:PublishTrimmed=true -r linux-x64 --nologo && \
if [ -d "ArchiSteamFarm/overlay/linux-x64" ]; then cp "ArchiSteamFarm/overlay/linux-x64/"* "out/result"; fi && \
if [ -f "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" ]; then mkdir -p "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; cp "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; fi
FROM mcr.microsoft.com/dotnet/core/runtime-deps:3.1-buster-slim AS runtime
ENV ASPNETCORE_URLS=
@@ -34,7 +37,7 @@ ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
LABEL maintainer="JustArchi <JustArchi@JustArchi.net>"
EXPOSE 1242
WORKDIR /app
COPY --from=build-dotnet /app/out .
COPY --from=build-dotnet /app/out/result .
VOLUME ["/app/config", "/app/logs", "/app/plugins"]
HEALTHCHECK CMD ["pidof", "-q", "ArchiSteamFarm"]
ENTRYPOINT ["./ArchiSteamFarm-Service.sh", "--no-restart", "--process-required", "--system-required"]

View File

@@ -7,25 +7,28 @@ RUN echo "node: $(node --version)" && \
npm run deploy
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-dotnet
ARG STEAM_TOKEN_DUMPER_TOKEN
ENV CONFIGURATION Release
ENV DOTNET_CLI_TELEMETRY_OPTOUT 1
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
ENV NET_CORE_VERSION netcoreapp3.1
ENV STEAM_TOKEN_DUMPER_NAME ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
WORKDIR /app
COPY --from=build-node /app/dist ASF-ui/dist
COPY ArchiSteamFarm ArchiSteamFarm
COPY ArchiSteamFarm.Tests ArchiSteamFarm.Tests
COPY ArchiSteamFarm.OfficialPlugins.SteamTokenDumper ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
COPY resources resources
COPY Directory.Build.props Directory.Build.props
RUN dotnet --info && \
# TODO: Remove workaround for https://github.com/microsoft/msbuild/issues/3897 when it's no longer needed
if [ -f "ArchiSteamFarm/Localization/Strings.zh-CN.resx" ]; then ln -s "Strings.zh-CN.resx" "ArchiSteamFarm/Localization/Strings.zh-Hans.resx"; fi && \
if [ -f "ArchiSteamFarm/Localization/Strings.zh-TW.resx" ]; then ln -s "Strings.zh-TW.resx" "ArchiSteamFarm/Localization/Strings.zh-Hant.resx"; fi && \
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
if [ -n "${STEAM_TOKEN_DUMPER_TOKEN-}" ] && [ -f "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs" ]; then sed -i "s/STEAM_TOKEN_DUMPER_TOKEN/${STEAM_TOKEN_DUMPER_TOKEN}/g" "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs"; fi && \
dotnet publish "${STEAM_TOKEN_DUMPER_NAME}" -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}" -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
dotnet clean ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o out -p:ASFVariant=docker -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
if [ -d "ArchiSteamFarm/overlay/generic" ]; then cp "ArchiSteamFarm/overlay/generic/"* "out"; fi
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/result" -p:ASFVariant=docker -p:SelfContained=false -p:UseAppHost=false -r linux-arm --nologo && \
if [ -d "ArchiSteamFarm/overlay/generic" ]; then cp "ArchiSteamFarm/overlay/generic/"* "out/result"; fi && \
if [ -f "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" ]; then mkdir -p "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; cp "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; fi
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim-arm32v7 AS runtime
ENV ASPNETCORE_URLS=
@@ -34,7 +37,7 @@ ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
LABEL maintainer="JustArchi <JustArchi@JustArchi.net>"
EXPOSE 1242
WORKDIR /app
COPY --from=build-dotnet /app/out .
COPY --from=build-dotnet /app/out/result .
VOLUME ["/app/config", "/app/logs", "/app/plugins"]
HEALTHCHECK CMD ["pidof", "-q", "dotnet"]
ENTRYPOINT ["./ArchiSteamFarm.sh", "--no-restart", "--process-required", "--system-required"]

View File

@@ -7,25 +7,28 @@ RUN echo "node: $(node --version)" && \
npm run deploy
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-dotnet
ARG STEAM_TOKEN_DUMPER_TOKEN
ENV CONFIGURATION Release
ENV DOTNET_CLI_TELEMETRY_OPTOUT 1
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
ENV NET_CORE_VERSION netcoreapp3.1
ENV STEAM_TOKEN_DUMPER_NAME ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
WORKDIR /app
COPY --from=build-node /app/dist ASF-ui/dist
COPY ArchiSteamFarm ArchiSteamFarm
COPY ArchiSteamFarm.Tests ArchiSteamFarm.Tests
COPY ArchiSteamFarm.OfficialPlugins.SteamTokenDumper ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
COPY resources resources
COPY Directory.Build.props Directory.Build.props
RUN dotnet --info && \
# TODO: Remove workaround for https://github.com/microsoft/msbuild/issues/3897 when it's no longer needed
if [ -f "ArchiSteamFarm/Localization/Strings.zh-CN.resx" ]; then ln -s "Strings.zh-CN.resx" "ArchiSteamFarm/Localization/Strings.zh-Hans.resx"; fi && \
if [ -f "ArchiSteamFarm/Localization/Strings.zh-TW.resx" ]; then ln -s "Strings.zh-TW.resx" "ArchiSteamFarm/Localization/Strings.zh-Hant.resx"; fi && \
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
if [ -n "${STEAM_TOKEN_DUMPER_TOKEN-}" ] && [ -f "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs" ]; then sed -i "s/STEAM_TOKEN_DUMPER_TOKEN/${STEAM_TOKEN_DUMPER_TOKEN}/g" "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs"; fi && \
dotnet publish "${STEAM_TOKEN_DUMPER_NAME}" -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}" -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
dotnet clean ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o out -p:ASFVariant=docker -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
if [ -d "ArchiSteamFarm/overlay/generic" ]; then cp "ArchiSteamFarm/overlay/generic/"* "out"; fi
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/result" -p:ASFVariant=docker -p:SelfContained=false -p:UseAppHost=false -r linux-arm64 --nologo && \
if [ -d "ArchiSteamFarm/overlay/generic" ]; then cp "ArchiSteamFarm/overlay/generic/"* "out/result"; fi && \
if [ -f "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" ]; then mkdir -p "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; cp "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; fi
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim-arm64v8 AS runtime
ENV ASPNETCORE_URLS=
@@ -34,7 +37,7 @@ ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
LABEL maintainer="JustArchi <JustArchi@JustArchi.net>"
EXPOSE 1242
WORKDIR /app
COPY --from=build-dotnet /app/out .
COPY --from=build-dotnet /app/out/result .
VOLUME ["/app/config", "/app/logs", "/app/plugins"]
HEALTHCHECK CMD ["pidof", "-q", "dotnet"]
ENTRYPOINT ["./ArchiSteamFarm.sh", "--no-restart", "--process-required", "--system-required"]

View File

@@ -7,25 +7,28 @@ RUN echo "node: $(node --version)" && \
npm run deploy
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-dotnet
ARG STEAM_TOKEN_DUMPER_TOKEN
ENV CONFIGURATION Release
ENV DOTNET_CLI_TELEMETRY_OPTOUT 1
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
ENV NET_CORE_VERSION netcoreapp3.1
ENV STEAM_TOKEN_DUMPER_NAME ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
WORKDIR /app
COPY --from=build-node /app/dist ASF-ui/dist
COPY ArchiSteamFarm ArchiSteamFarm
COPY ArchiSteamFarm.Tests ArchiSteamFarm.Tests
COPY ArchiSteamFarm.OfficialPlugins.SteamTokenDumper ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
COPY resources resources
COPY Directory.Build.props Directory.Build.props
RUN dotnet --info && \
# TODO: Remove workaround for https://github.com/microsoft/msbuild/issues/3897 when it's no longer needed
if [ -f "ArchiSteamFarm/Localization/Strings.zh-CN.resx" ]; then ln -s "Strings.zh-CN.resx" "ArchiSteamFarm/Localization/Strings.zh-Hans.resx"; fi && \
if [ -f "ArchiSteamFarm/Localization/Strings.zh-TW.resx" ]; then ln -s "Strings.zh-TW.resx" "ArchiSteamFarm/Localization/Strings.zh-Hant.resx"; fi && \
dotnet build ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
dotnet test ArchiSteamFarm.Tests -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
if [ -n "${STEAM_TOKEN_DUMPER_TOKEN-}" ] && [ -f "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs" ]; then sed -i "s/STEAM_TOKEN_DUMPER_TOKEN/${STEAM_TOKEN_DUMPER_TOKEN}/g" "${STEAM_TOKEN_DUMPER_NAME}/SharedInfo.cs"; fi && \
dotnet publish "${STEAM_TOKEN_DUMPER_NAME}" -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}" -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
dotnet clean ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o out -p:ASFVariant=docker -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
if [ -d "ArchiSteamFarm/overlay/generic" ]; then cp "ArchiSteamFarm/overlay/generic/"* "out"; fi
dotnet publish ArchiSteamFarm -c "$CONFIGURATION" -f "$NET_CORE_VERSION" -o "out/result" -p:ASFVariant=docker -p:SelfContained=false -p:UseAppHost=false -r linux-x64 --nologo && \
if [ -d "ArchiSteamFarm/overlay/generic" ]; then cp "ArchiSteamFarm/overlay/generic/"* "out/result"; fi && \
if [ -f "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" ]; then mkdir -p "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; cp "out/${STEAM_TOKEN_DUMPER_NAME}/${NET_CORE_VERSION}/${STEAM_TOKEN_DUMPER_NAME}.dll" "out/result/plugins/${STEAM_TOKEN_DUMPER_NAME}"; fi
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS runtime
ENV ASPNETCORE_URLS=
@@ -34,7 +37,7 @@ ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
LABEL maintainer="JustArchi <JustArchi@JustArchi.net>"
EXPOSE 1242
WORKDIR /app
COPY --from=build-dotnet /app/out .
COPY --from=build-dotnet /app/out/result .
VOLUME ["/app/config", "/app/logs", "/app/plugins"]
HEALTHCHECK CMD ["pidof", "-q", "dotnet"]
ENTRYPOINT ["./ArchiSteamFarm.sh", "--no-restart", "--process-required", "--system-required"]

View File

@@ -16,6 +16,9 @@ environment:
NET_CORE_VERSION: netcoreapp3.1
NET_FRAMEWORK_VERSION: net48
NODE_JS_VERSION: lts
STEAM_TOKEN_DUMPER_NAME: ArchiSteamFarm.OfficialPlugins.SteamTokenDumper
STEAM_TOKEN_DUMPER_TOKEN:
secure: uttQUE9ZK7BIa9SIbDkpUTMx7Slnl3zAPkRNzE465YgwxLdLEwv6yYR5QXCSZolb5Qq23Z/LmZNGd3M6B0+hbx3waWOeW2AiWvfCcnUmuT+3wfLJsgLbf1g4agFS7zsDgeRPfnNMzOxD8etelnA5YOOUMNB3RLw3fIdznNd+Fs6R0Ou3/1UavDuHKkbh1+A5
VARIANTS: generic generic-netf linux-arm linux-arm64 linux-x64 osx-x64 win-x64 # NOTE: When modifying variants, don't forget to update ASF_VARIANT definitions in SharedInfo.cs!
matrix:
allow_failures:
@@ -100,7 +103,7 @@ build_script:
}
dotnet build ArchiSteamFarm -c "$env:CONFIGURATION" -f "$env:NET_CORE_VERSION" -p:UseAppHost=false --nologo
dotnet build ArchiSteamFarm -c "$env:CONFIGURATION" -p:UseAppHost=false --nologo
if ($LastExitCode -ne 0) {
@@ -108,7 +111,7 @@ build_script:
}
dotnet build ArchiSteamFarm.CustomPlugins.ExamplePlugin -c "$env:CONFIGURATION" -f "$env:NET_CORE_VERSION" -p:UseAppHost=false --nologo
dotnet build ArchiSteamFarm.CustomPlugins.ExamplePlugin -c "$env:CONFIGURATION" -p:UseAppHost=false --nologo
if ($LastExitCode -ne 0) {
@@ -116,7 +119,15 @@ build_script:
}
dotnet build ArchiSteamFarm.CustomPlugins.PeriodicGC -c "$env:CONFIGURATION" -f "$env:NET_CORE_VERSION" -p:UseAppHost=false --nologo
dotnet build ArchiSteamFarm.CustomPlugins.PeriodicGC -c "$env:CONFIGURATION" -p:UseAppHost=false --nologo
if ($LastExitCode -ne 0) {
throw "Last command failed."
}
dotnet build ArchiSteamFarm.OfficialPlugins.SteamTokenDumper -c "$env:CONFIGURATION" -p:UseAppHost=false --nologo
if ($LastExitCode -ne 0) {
@@ -131,7 +142,7 @@ test_script:
$ProgressPreference = 'SilentlyContinue'
dotnet test ArchiSteamFarm.Tests -c "$env:CONFIGURATION" -f "$env:NET_CORE_VERSION" -p:UseAppHost=false --nologo
dotnet test ArchiSteamFarm.Tests -c "$env:CONFIGURATION" -p:UseAppHost=false --nologo
if ($LastExitCode -ne 0) {
@@ -146,7 +157,27 @@ after_test:
$ProgressPreference = 'SilentlyContinue'
dotnet clean ArchiSteamFarm -c "$env:CONFIGURATION" -f "$env:NET_CORE_VERSION" -p:UseAppHost=false --nologo
if ((Test-Path env:STEAM_TOKEN_DUMPER_TOKEN) -and (Test-Path 'ArchiSteamFarm.OfficialPlugins.SteamTokenDumper\SharedInfo.cs' -PathType Leaf)) {
(Get-Content 'ArchiSteamFarm.OfficialPlugins.SteamTokenDumper\SharedInfo.cs').Replace('STEAM_TOKEN_DUMPER_TOKEN', "$env:STEAM_TOKEN_DUMPER_TOKEN") | Set-Content 'ArchiSteamFarm.OfficialPlugins.SteamTokenDumper\SharedInfo.cs'
}
dotnet publish "$env:STEAM_TOKEN_DUMPER_NAME" -c "$env:CONFIGURATION" -f "$env:NET_CORE_VERSION" -o "out/$env:STEAM_TOKEN_DUMPER_NAME/$env:NET_CORE_VERSION" -p:UseAppHost=false --nologo
if ($LastExitCode -ne 0) {
throw "Last command failed."
}
dotnet publish "$env:STEAM_TOKEN_DUMPER_NAME" -c "$env:CONFIGURATION" -f "$env:NET_FRAMEWORK_VERSION" -o "out/$env:STEAM_TOKEN_DUMPER_NAME/$env:NET_FRAMEWORK_VERSION" -p:UseAppHost=false --nologo
if ($LastExitCode -ne 0) {
throw "Last command failed."
}
dotnet clean ArchiSteamFarm -c "$env:CONFIGURATION" -p:UseAppHost=false --nologo
if ($LastExitCode -ne 0) {
@@ -189,11 +220,20 @@ after_test:
throw "Last command failed."
}
# If we include any overlay for this variant, copy it output directory
# If we're including any overlay for this variant, copy it to output directory
if (Test-Path "ArchiSteamFarm\overlay\$variant" -PathType Container) {
Copy-Item "ArchiSteamFarm\overlay\$variant\*" "out\$variant"
}
# If we're including SteamTokenDumper plugin for this framework, copy it to output directory
if (Test-Path "out\$env:STEAM_TOKEN_DUMPER_NAME\$targetFramework\$env:STEAM_TOKEN_DUMPER_NAME.dll" -PathType Leaf) {
if (!(Test-Path "out\$variant\plugins\$env:STEAM_TOKEN_DUMPER_NAME" -PathType Container)) {
New-Item -ItemType Directory -Path "out\$variant\plugins\$env:STEAM_TOKEN_DUMPER_NAME" > $null
}
Copy-Item "out\$env:STEAM_TOKEN_DUMPER_NAME\$targetFramework\$env:STEAM_TOKEN_DUMPER_NAME.dll" "out\$variant\plugins\$env:STEAM_TOKEN_DUMPER_NAME"
}
# Until https://github.com/dotnet/cli/issues/3267 happens, we'll hack dotnet binary icon on Windows and include .ico file on other platforms
if ($targetFramework -ne "$env:NET_FRAMEWORK_VERSION") {
if (!(Test-Path "out\$variant\ArchiSteamFarm.exe" -PathType Leaf)) {

2
wiki

Submodule wiki updated: 508359a397...b825f6ddbb