Compare commits

...

19 Commits

Author SHA1 Message Date
JustArchi
4ada8595bd Translations update 2017-08-01 19:14:20 +02:00
JustArchi
bc64c43748 Misc 2017-08-01 19:04:55 +02:00
JustArchi
f4f7935d4c Add a workaround for invalid scheme redirections
https://github.com/JustArchi/ArchiSteamFarm/issues/586#issuecomment-319422864
2017-08-01 18:59:10 +02:00
JustArchi
aacc0a1720 Bump 2017-08-01 13:23:57 +02:00
JustArchi
e5ee909b96 Remove debug leftovers 2017-08-01 13:23:19 +02:00
JustArchi
f3f444d0bd Test fix #605 2017-08-01 13:00:54 +02:00
JustArchi
43d18b6d49 Move debug to appropriate place 2017-08-01 12:44:22 +02:00
JustArchi
f24be67c8c Bunch of stuff for #605 2017-08-01 12:41:57 +02:00
JustArchi
6ce4f2941b Bump 2017-07-31 17:51:04 +02:00
JustArchi
ef5398fae8 Translations update 2017-07-31 17:39:40 +02:00
JustArchi
ce600fdf42 Packages update 2017-07-31 17:38:38 +02:00
JustArchi
7a6485775d Misc 2017-07-31 17:35:17 +02:00
JustArchi
d39e7a32d6 Add debug for #586 2017-07-31 17:22:18 +02:00
JustArchi
b4cbe85b36 Closes #605 2017-07-31 16:57:10 +02:00
JustArchi
4d83417c9d Misc 2017-07-24 12:18:40 +02:00
JustArchi
25bfbf4862 Misc optimization
I like how R# spots multiple enumerations.
2017-07-24 11:13:55 +02:00
JustArchi
e539e1bdc7 Fixes #537
Apparently Steam web is too stupid to handle redirections properly.
2017-07-24 10:39:51 +02:00
JustArchi
b0f83fcd6d AppVeyor: Misc 2017-07-24 10:08:51 +02:00
JustArchi
4880a4e60b Bump 2017-07-23 13:42:03 +02:00
28 changed files with 249 additions and 135 deletions

View File

@@ -50,11 +50,10 @@ matrix:
fast_finish: true
include:
# We're building ASF with dotnet on latest versions of Linux and OS X
# Ref: https://docs.travis-ci.com/user/ci-environment/#Virtualization-environments
- os: linux
# Ref: https://docs.travis-ci.com/user/trusty-ci-environment/
# Ref: https://docs.travis-ci.com/user/reference/trusty/
dist: trusty
sudo: false
- os: osx
# Ref: https://docs.travis-ci.com/user/osx-ci-environment/
# Ref: https://docs.travis-ci.com/user/reference/osx/
osx_image: xcode9

View File

@@ -24,7 +24,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170628-02" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0-preview-20170727-01" />
<PackageReference Include="MSTest.TestAdapter" Version="1.2.0-beta" />
<PackageReference Include="MSTest.TestFramework" Version="1.2.0-beta" />
</ItemGroup>

View File

@@ -3,8 +3,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyVersion>3.0.0.6</AssemblyVersion>
<FileVersion>3.0.0.6</FileVersion>
<AssemblyVersion>3.0.0.9</AssemblyVersion>
<FileVersion>3.0.0.9</FileVersion>
<LangVersion>latest</LangVersion>
<ErrorReport>none</ErrorReport>
<ApplicationIcon>ASF.ico</ApplicationIcon>

View File

@@ -58,15 +58,15 @@ namespace ArchiSteamFarm {
private const string SteamStoreURL = "http://" + SteamStoreHost;
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1);
private static readonly SemaphoreSlim InventorySemaphore = new SemaphoreSlim(1, 1);
private static int Timeout = GlobalConfig.DefaultConnectionTimeout * 1000; // This must be int type
private readonly SemaphoreSlim ApiKeySemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim ApiKeySemaphore = new SemaphoreSlim(1, 1);
private readonly Bot Bot;
private readonly SemaphoreSlim PublicInventorySemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim TradeTokenSemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim PublicInventorySemaphore = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim TradeTokenSemaphore = new SemaphoreSlim(1, 1);
private readonly WebBrowser WebBrowser;
private string CachedApiKey;
@@ -74,6 +74,7 @@ namespace ArchiSteamFarm {
private string CachedTradeToken;
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
private ulong SteamID;
private string VanityURL;
internal ArchiWebHandler(Bot bot) {
Bot = bot ?? throw new ArgumentNullException(nameof(bot));
@@ -867,12 +868,16 @@ namespace ArchiSteamFarm {
internal static void Init() => Timeout = Program.GlobalConfig.ConnectionTimeout * 1000;
internal async Task<bool> Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalPin) {
internal async Task<bool> Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalPin, string vanityURL = null) {
if ((steamID == 0) || (universe == EUniverse.Invalid) || string.IsNullOrEmpty(webAPIUserNonce) || string.IsNullOrEmpty(parentalPin)) {
Bot.ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(universe) + " || " + nameof(webAPIUserNonce) + " || " + nameof(parentalPin));
return false;
}
if (!string.IsNullOrEmpty(vanityURL)) {
VanityURL = vanityURL;
}
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
// Generate an AES session key
@@ -1104,7 +1109,7 @@ namespace ArchiSteamFarm {
return false;
}
string request = SteamCommunityURL + "/profiles/" + SteamID + "/ajaxunpackbooster";
string request = GetAbsoluteProfileURL() + "/ajaxunpackbooster";
Dictionary<string, string> data = new Dictionary<string, string>(3) {
{ "sessionid", sessionID },
{ "appid", appID.ToString() },
@@ -1115,6 +1120,14 @@ namespace ArchiSteamFarm {
return response?.Result == EResult.OK;
}
private string GetAbsoluteProfileURL() {
if (!string.IsNullOrEmpty(VanityURL)) {
return SteamCommunityURL + "/id/" + VanityURL;
}
return SteamCommunityURL + "/profiles/" + SteamID;
}
private async Task<string> GetApiKey() {
if (CachedApiKey != null) {
// We fetched API key already, and either got valid one, or permanent AccessDenied

View File

@@ -53,8 +53,8 @@ namespace ArchiSteamFarm {
internal static readonly ConcurrentDictionary<string, Bot> Bots = new ConcurrentDictionary<string, Bot>();
private static readonly SemaphoreSlim GiftsSemaphore = new SemaphoreSlim(1);
private static readonly SemaphoreSlim LoginSemaphore = new SemaphoreSlim(1);
private static readonly SemaphoreSlim GiftsSemaphore = new SemaphoreSlim(1, 1);
private static readonly SemaphoreSlim LoginSemaphore = new SemaphoreSlim(1, 1);
private static readonly SteamConfiguration SteamConfiguration = new SteamConfiguration();
internal readonly ArchiLogger ArchiLogger;
@@ -73,15 +73,16 @@ namespace ArchiSteamFarm {
private readonly BotDatabase BotDatabase;
private readonly string BotName;
private readonly CallbackManager CallbackManager;
private readonly SemaphoreSlim CallbackSemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim CallbackSemaphore = new SemaphoreSlim(1, 1);
[JsonProperty]
private readonly CardsFarmer CardsFarmer;
private readonly ConcurrentHashSet<ulong> HandledGifts = new ConcurrentHashSet<ulong>();
private readonly Timer HeartBeatTimer;
private readonly SemaphoreSlim InitializationSemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim LootingSemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim InitializationSemaphore = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim LootingSemaphore = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim PICSSemaphore = new SemaphoreSlim(1, 1);
private readonly Statistics Statistics;
private readonly SteamApps SteamApps;
private readonly SteamClient SteamClient;
@@ -235,6 +236,8 @@ namespace ArchiSteamFarm {
CardsFarmer.Dispose();
HeartBeatTimer.Dispose();
InitializationSemaphore.Dispose();
LootingSemaphore.Dispose();
PICSSemaphore.Dispose();
Trading.Dispose();
// Those are objects that might be null and the check should be in-place
@@ -349,11 +352,15 @@ namespace ArchiSteamFarm {
AsyncJobMultiple<SteamApps.PICSProductInfoCallback>.ResultSet productInfoResultSet;
await PICSSemaphore.WaitAsync().ConfigureAwait(false);
try {
productInfoResultSet = await SteamApps.PICSGetProductInfo(appID, null, false);
} catch (Exception e) {
ArchiLogger.LogGenericException(e);
return (0, DateTime.MinValue);
} finally {
PICSSemaphore.Release();
}
// ReSharper disable once LoopCanBePartlyConvertedToQuery - C# 7.0 out can't be used within LINQ query yet | https://github.com/dotnet/roslyn/issues/15619
@@ -452,11 +459,15 @@ namespace ArchiSteamFarm {
internal async Task<Dictionary<uint, HashSet<uint>>> GetAppIDsToPackageIDs(IEnumerable<uint> packageIDs) {
AsyncJobMultiple<SteamApps.PICSProductInfoCallback>.ResultSet productInfoResultSet;
await PICSSemaphore.WaitAsync().ConfigureAwait(false);
try {
productInfoResultSet = await SteamApps.PICSGetProductInfo(Enumerable.Empty<uint>(), packageIDs);
} catch (Exception e) {
ArchiLogger.LogGenericException(e);
return null;
} finally {
PICSSemaphore.Release();
}
Dictionary<uint, HashSet<uint>> result = new Dictionary<uint, HashSet<uint>>();
@@ -510,6 +521,17 @@ namespace ArchiSteamFarm {
return result;
}
internal void IdleGame(uint gameID) => IdleGames(gameID.ToEnumerable());
internal void IdleGames(IEnumerable<uint> gameIDs) {
if (gameIDs == null) {
ArchiLogger.LogNullError(nameof(gameIDs));
return;
}
ArchiHandler.PlayGames(gameIDs, BotConfig.CustomGamePlayedWhileFarming);
}
internal static async Task InitializeSteamConfiguration(ProtocolTypes protocolTypes, uint cellID, InMemoryServerListProvider serverListProvider) {
if (serverListProvider == null) {
ASF.ArchiLogger.LogNullError(nameof(serverListProvider));
@@ -635,17 +657,6 @@ namespace ArchiSteamFarm {
}
}
internal void PlayGame(uint gameID, string gameName = null) => PlayGames(gameID.ToEnumerable(), gameName);
internal void PlayGames(IEnumerable<uint> gameIDs, string gameName = null) {
if (gameIDs == null) {
ArchiLogger.LogNullError(nameof(gameIDs));
return;
}
ArchiHandler.PlayGames(gameIDs, gameName);
}
internal async Task<bool> RefreshSession() {
if (!IsConnectedAndLoggedOn) {
return false;
@@ -1123,7 +1134,7 @@ namespace ArchiSteamFarm {
try {
if (DateTime.UtcNow.Subtract(ArchiHandler.LastPacketReceived).TotalSeconds > MinHeartBeatTTL) {
await SteamApps.PICSGetProductInfo(0, null);
await SteamFriends.RequestProfileInfo(SteamClient.SteamID);
}
HeartBeatFailures = 0;
@@ -1810,7 +1821,7 @@ namespace ArchiSteamFarm {
ArchiLogger.LogGenericWarning(Strings.BotAccountLocked);
}
if ((callback.CellID != 0) && (Program.GlobalDatabase.CellID != callback.CellID)) {
if (callback.CellID != 0) {
Program.GlobalDatabase.CellID = callback.CellID;
}
@@ -1832,7 +1843,7 @@ namespace ArchiSteamFarm {
SetUserInput(ASF.EUserInputType.SteamParentalPIN, steamParentalPIN);
}
if (!await ArchiWebHandler.Init(callback.ClientSteamID, SteamClient.Universe, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN).ConfigureAwait(false)) {
if (!await ArchiWebHandler.Init(callback.ClientSteamID, SteamClient.Universe, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN, callback.VanityURL).ConfigureAwait(false)) {
if (!await RefreshSession().ConfigureAwait(false)) {
break;
}

View File

@@ -58,9 +58,9 @@ namespace ArchiSteamFarm {
);
private readonly Bot Bot;
private readonly SemaphoreSlim EventSemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim FarmingSemaphore = new SemaphoreSlim(1);
private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
private readonly SemaphoreSlim EventSemaphore = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim FarmingInitializationSemaphore = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim FarmingResetSemaphore = new SemaphoreSlim(0, 1);
private readonly Timer IdleFarmingTimer;
[JsonProperty]
@@ -87,8 +87,8 @@ namespace ArchiSteamFarm {
public void Dispose() {
// Those are objects that are always being created if constructor doesn't throw exception
EventSemaphore.Dispose();
FarmingSemaphore.Dispose();
FarmResetEvent.Dispose();
FarmingInitializationSemaphore.Dispose();
FarmingResetSemaphore.Dispose();
// Those are objects that might be null and the check should be in-place
IdleFarmingTimer?.Dispose();
@@ -140,7 +140,7 @@ namespace ArchiSteamFarm {
internal async Task OnNewItemsNotification() {
if (NowFarming) {
FarmResetEvent.Set();
FarmingResetSemaphore.Release();
return;
}
@@ -188,7 +188,7 @@ namespace ArchiSteamFarm {
return;
}
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
await FarmingInitializationSemaphore.WaitAsync().ConfigureAwait(false);
try {
if (NowFarming || Paused || !Bot.IsPlayingPossible) {
@@ -231,7 +231,7 @@ namespace ArchiSteamFarm {
KeepFarming = NowFarming = true;
Utilities.StartBackgroundFunction(Farm);
} finally {
FarmingSemaphore.Release();
FarmingInitializationSemaphore.Release();
}
}
@@ -240,7 +240,7 @@ namespace ArchiSteamFarm {
return;
}
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
await FarmingInitializationSemaphore.WaitAsync().ConfigureAwait(false);
try {
if (!NowFarming) {
@@ -248,7 +248,7 @@ namespace ArchiSteamFarm {
}
KeepFarming = false;
FarmResetEvent.Set();
FarmingResetSemaphore.Release();
for (byte i = 0; (i < 5) && NowFarming; i++) {
await Task.Delay(1000).ConfigureAwait(false);
@@ -261,7 +261,7 @@ namespace ArchiSteamFarm {
Bot.ArchiLogger.LogGenericInfo(Strings.IdlingStopped);
Bot.OnFarmingStopped();
} finally {
FarmingSemaphore.Release();
FarmingInitializationSemaphore.Release();
}
}
@@ -556,65 +556,68 @@ namespace ArchiSteamFarm {
// If we have restricted card drops, we use complex algorithm
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ChosenFarmingAlgorithm, "Complex"));
while (GamesToFarm.Count > 0) {
HashSet<Game> playableGamesToFarmSolo = new HashSet<Game>();
foreach (Game game in GamesToFarm.Where(game => game.HoursPlayed >= HoursToBump)) {
if (await IsPlayableGame(game).ConfigureAwait(false)) {
playableGamesToFarmSolo.Add(game);
HashSet<Game> gamesToCheck = new HashSet<Game>(GamesToFarm.Where(game => game.HoursPlayed >= HoursToBump));
foreach (Game game in gamesToCheck) {
if (!await IsPlayableGame(game).ConfigureAwait(false)) {
GamesToFarm.Remove(game);
continue;
}
if (await FarmSolo(game).ConfigureAwait(false)) {
continue;
}
NowFarming = false;
return;
}
gamesToCheck = new HashSet<Game>(GamesToFarm.OrderByDescending(game => game.HoursPlayed));
HashSet<Game> playableGamesToFarmMultiple = new HashSet<Game>();
foreach (Game game in gamesToCheck) {
if (!await IsPlayableGame(game).ConfigureAwait(false)) {
GamesToFarm.Remove(game);
continue;
}
playableGamesToFarmMultiple.Add(game);
if (playableGamesToFarmMultiple.Count >= ArchiHandler.MaxGamesPlayedConcurrently) {
break;
}
}
if (playableGamesToFarmSolo.Count > 0) {
while (playableGamesToFarmSolo.Count > 0) {
Game playableGame = playableGamesToFarmSolo.First();
if (await FarmSolo(playableGame).ConfigureAwait(false)) {
playableGamesToFarmSolo.Remove(playableGame);
} else {
NowFarming = false;
return;
}
}
if (playableGamesToFarmMultiple.Count == 0) {
break;
}
if (await FarmMultiple(playableGamesToFarmMultiple).ConfigureAwait(false)) {
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingFinishedForGames, string.Join(", ", playableGamesToFarmMultiple.Select(game => game.AppID))));
} else {
HashSet<Game> playableGamesToFarmMultiple = new HashSet<Game>();
foreach (Game game in GamesToFarm.Where(game => game.HoursPlayed < HoursToBump).OrderByDescending(game => game.HoursPlayed)) {
if (await IsPlayableGame(game).ConfigureAwait(false)) {
playableGamesToFarmMultiple.Add(game);
}
if (playableGamesToFarmMultiple.Count >= ArchiHandler.MaxGamesPlayedConcurrently) {
break;
}
}
if (FarmMultiple(playableGamesToFarmMultiple)) {
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.IdlingFinishedForGames, string.Join(", ", playableGamesToFarmMultiple.Select(game => game.AppID))));
} else {
NowFarming = false;
return;
}
NowFarming = false;
return;
}
}
} else {
// If we have unrestricted card drops, we use simple algorithm
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.ChosenFarmingAlgorithm, "Simple"));
while (GamesToFarm.Count > 0) {
Game playableGame = null;
foreach (Game game in GamesToFarm) {
HashSet<Game> gamesToCheck = new HashSet<Game>(GamesToFarm);
foreach (Game game in gamesToCheck) {
if (!await IsPlayableGame(game).ConfigureAwait(false)) {
GamesToFarm.Remove(game);
continue;
}
playableGame = game;
break;
}
if (playableGame != null) {
if (await FarmSolo(playableGame).ConfigureAwait(false)) {
if (await FarmSolo(game).ConfigureAwait(false)) {
continue;
}
}
NowFarming = false;
return;
NowFarming = false;
return;
}
}
}
} while ((await IsAnythingToFarm().ConfigureAwait(false)).GetValueOrDefault());
@@ -638,7 +641,7 @@ namespace ArchiSteamFarm {
Bot.ArchiLogger.LogGenericWarning(string.Format(Strings.WarningIdlingGameMismatch, game.AppID, game.GameName, game.PlayableAppID));
}
Bot.PlayGame(game.PlayableAppID, Bot.BotConfig.CustomGamePlayedWhileFarming);
Bot.IdleGame(game.PlayableAppID);
DateTime endFarmingDate = DateTime.UtcNow.AddHours(Program.GlobalConfig.MaxFarmingTime);
bool? keepFarming = await ShouldFarm(game).ConfigureAwait(false);
@@ -646,8 +649,7 @@ namespace ArchiSteamFarm {
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StillIdling, game.AppID, game.GameName));
DateTime startFarmingPeriod = DateTime.UtcNow;
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
FarmResetEvent.Reset();
if (await FarmingResetSemaphore.WaitAsync(60 * 1000 * Program.GlobalConfig.FarmingDelay).ConfigureAwait(false)) {
success = KeepFarming;
}
@@ -665,7 +667,7 @@ namespace ArchiSteamFarm {
return success;
}
private bool FarmHours(ConcurrentHashSet<Game> games) {
private async Task<bool> FarmHours(ConcurrentHashSet<Game> games) {
if ((games == null) || (games.Count == 0)) {
Bot.ArchiLogger.LogNullError(nameof(games));
return false;
@@ -682,15 +684,14 @@ namespace ArchiSteamFarm {
return true;
}
Bot.PlayGames(games.Select(game => game.AppID), Bot.BotConfig.CustomGamePlayedWhileFarming);
Bot.IdleGames(games.Select(game => game.PlayableAppID));
bool success = true;
while (maxHour < 2) {
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.StillIdlingList, string.Join(", ", games.Select(game => game.AppID))));
DateTime startFarmingPeriod = DateTime.UtcNow;
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
FarmResetEvent.Reset();
if (await FarmingResetSemaphore.WaitAsync(60 * 1000 * Program.GlobalConfig.FarmingDelay).ConfigureAwait(false)) {
success = KeepFarming;
}
@@ -711,7 +712,7 @@ namespace ArchiSteamFarm {
return success;
}
private bool FarmMultiple(IEnumerable<Game> games) {
private async Task<bool> FarmMultiple(IEnumerable<Game> games) {
if (games == null) {
Bot.ArchiLogger.LogNullError(nameof(games));
return false;
@@ -721,7 +722,7 @@ namespace ArchiSteamFarm {
Bot.ArchiLogger.LogGenericInfo(string.Format(Strings.NowIdlingList, string.Join(", ", CurrentGamesFarming.Select(game => game.AppID))));
bool result = FarmHours(CurrentGamesFarming);
bool result = await FarmHours(CurrentGamesFarming).ConfigureAwait(false);
CurrentGamesFarming.Clear();
return result;
}
@@ -926,7 +927,7 @@ namespace ArchiSteamFarm {
case BotConfig.EFarmingOrder.RedeemDateTimesDescending:
Dictionary<uint, DateTime> redeemDates = new Dictionary<uint, DateTime>(GamesToFarm.Count);
foreach (Game game in gamesToFarm) {
foreach (Game game in GamesToFarm) {
DateTime redeemDate = DateTime.MinValue;
if (Program.GlobalDatabase.AppIDsToPackageIDs.TryGetValue(game.AppID, out ConcurrentHashSet<uint> packageIDs)) {
// ReSharper disable once LoopCanBePartlyConvertedToQuery - C# 7.0 out can't be used within LINQ query yet | https://github.com/dotnet/roslyn/issues/15619

View File

@@ -44,7 +44,7 @@ namespace ArchiSteamFarm {
public bool IsReadOnly => false;
private readonly HashSet<T> BackingCollection = new HashSet<T>();
private readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1);
private readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1);
public bool Add(T item) {
SemaphoreSlim.Wait();

View File

@@ -102,7 +102,7 @@ namespace ArchiSteamFarm {
internal readonly bool Statistics = true;
[JsonProperty(Required = Required.DisallowNull)]
internal readonly ProtocolTypes SteamProtocols = ProtocolTypes.All;
internal readonly ProtocolTypes SteamProtocols = ProtocolTypes.Tcp;
[JsonProperty(Required = Required.DisallowNull)]
internal readonly EUpdateChannel UpdateChannel = EUpdateChannel.Stable;

View File

@@ -44,7 +44,7 @@ namespace ArchiSteamFarm {
private readonly object FileLock = new object();
private readonly SemaphoreSlim PackagesRefreshSemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim PackagesRefreshSemaphore = new SemaphoreSlim(1, 1);
internal uint CellID {
get => _CellID;

View File

@@ -673,5 +673,8 @@ StackTrace:
<value>Procházení fronty doporučení #{0} dokončeno.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>{0}/{1} botů již vlastní všechny uvedené hry.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -673,5 +673,8 @@ StackTrace:
<value>Fertig mit Löschung der Steam Entdeckungsliste #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>Es gibt {0}/{1}-Bots, die bereits alle geprüften Spiele besitzen.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -673,5 +673,8 @@ StackTrace:
<value>Ολοκληρώθηκε η εκκαθάριση σειράς ανακαλύψεων Steam #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>Υπάρχουν {0}/{1} bot που κατέχουν ήδη όλα τα παιχνίδια για τα οποία γίνεται έλεγχος.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -289,7 +289,9 @@
</data>
<data name="IPCReady" xml:space="preserve">
<value>Server IPC siap!</value>
</data>
<data name="BotAlreadyStopped" xml:space="preserve">
<value>Bot ini sudah berhenti!</value>

View File

@@ -184,14 +184,19 @@ StackTrace:
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
<value>최신 버전을 확인할 수 없습니다!</value>
</data>
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
<value>현재 실행 중인 버전과 관련된 자산이 없으므로 업데이트를 진행할 수 없습니다! 해당 버전으로 자동 업데이트가 불가능합니다.</value>
</data>
<data name="ErrorUpdateNoAssets" xml:space="preserve">
<value>해당 버전이 아무 내용도 포함되어 있지 않아 업데이트를 진행할 수 없습니다!</value>
</data>
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
<value>사용자 입력 요청을 받았지만, 프로세스는 Headless 모드로 실행 중입니다.</value>
</data>
<data name="ErrorIPCAccessDenied" xml:space="preserve">
<value>SteamOwnerID가 설정되지 않았기 때문에 요청을 거절합니다!</value>
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
</data>
<data name="Exiting" xml:space="preserve">
<value>종료 중...</value>
</data>
@@ -241,7 +246,10 @@ StackTrace:
<data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>새로운 버전 확인 중...</value>
</data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>새로운 버전을 내려받는 중: {0} ({1} MB).... 기다리는 동안, 이 프로그램이 고맙다면, 기부하는 것을 고려해보세요! :)</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>업데이트 작업 완료!</value>
</data>
@@ -280,7 +288,10 @@ StackTrace:
<value>등록되지 않은 {0}의 값을 입력하세요: </value>
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
</data>
<data name="UserInputIPCHost" xml:space="preserve">
<value>IPC 호스트를 입력하세요: </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
<value>{0}의 알 수 없는 값을 받았습니다. 이것을 보고 바랍니다: {1}</value>
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
@@ -289,10 +300,20 @@ StackTrace:
<value>동시에 {0}개 이상의 게임들을 플레이하는 것은 불가능합니다. {1}에 의해 단지 {0}개의 항목만 사용될 것입니다!</value>
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
</data>
<data name="ErrorIPCAddressAccessDeniedException" xml:space="preserve">
<value>AddressAccessDeniedException로 인해 IPC 서비스가 시작될 수 없습니다! ASF에서 제공하는 IPC 서비스를 사용하고 싶다면, ASF를 관리자 모드로 실행하거나 적절한 권한을 줘야 합니다!</value>
</data>
<data name="IPCAnswered" xml:space="preserve">
<value>{0} IPC 명령에 대한 응답: {1}</value>
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
</data>
<data name="IPCReady" xml:space="preserve">
<value>IPC 서버 준비 완료!</value>
</data>
<data name="IPCStarting" xml:space="preserve">
<value>{0} 에서 IPC 서버 시작...</value>
<comment>{0} will be replaced by IPC hostname</comment>
</data>
<data name="BotAlreadyStopped" xml:space="preserve">
<value>이 봇은 이미 중지되어 있습니다!</value>
</data>
@@ -652,5 +673,8 @@ StackTrace:
<value>스팀 맞춤 대기열 #{0}을 지웠습니다.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>게임을 확인 중인 {0}/{1} 개의 봇이 있습니다.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -670,5 +670,8 @@
<value>Baigta peržiūrėti Steam atradimo eilė #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>Šiuo metu {0}/{1} botai turi visus tikrinamus žaidimus.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -673,5 +673,8 @@ StackTrace:
<value>Steam-ontdekkingswachtrij voltooid #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>Er zijn {0}/{1} bots die de gecontroleerde spellen al in bezit hebben.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -673,5 +673,8 @@ StackTrace:
<value>Steam-ontdekkingswachtrij voltooid #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>Er zijn {0}/{1} bots die de gecontroleerde spellen al in bezit hebben.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -673,5 +673,8 @@ StackTrace:
<value>Limpeza da lista de descoberta da Steam #{0} concluída.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>Os bots {0}/{1} já possuem todos os jogos que estão sendo verificados.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -674,5 +674,8 @@ inválidas, abortando!</value>
<value>Fila de exploração da Steam feita #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>Os bots {0}/{1} já possuem todos os jogos que está sendo verificados.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -673,5 +673,8 @@ StackTrace:
<value>S-a terminat ștergerea cozii pentru lista de descoperiri Steam #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>Există {0}/{1} boți care dețin deja toate jocurile verificate.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -673,5 +673,8 @@ Yığın izleme:
<value>Steam keşif kuyruğu temizlenmesi bitti #{0}.</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>{0}/{1} bot zaten sahip olunan tüm oyunları denetliyor.</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -181,14 +181,19 @@
<data name="ErrorUpdateCheckFailed" xml:space="preserve">
<value>无法检查最新版本 </value>
</data>
<data name="ErrorUpdateNoAssetForThisVersion" xml:space="preserve">
<value>无法继续更新,因为没有与当前正在运行的版本相关的版本! 无法自动更新到该版本。</value>
</data>
<data name="ErrorUpdateNoAssets" xml:space="preserve">
<value>不能进行更新,因为此版本没有任何资源!</value>
</data>
<data name="ErrorUserInputRunningInHeadlessMode" xml:space="preserve">
<value>收到一个用户输入请求,但进程目前正在以无显示模式运行 </value>
</data>
<data name="ErrorIPCAccessDenied" xml:space="preserve">
<value>拒绝处理该请求,因为未设置 SteamOwnerID </value>
<comment>SteamOwnerID is name of bot config property, it should not be translated</comment>
</data>
<data name="Exiting" xml:space="preserve">
<value>正在退出...</value>
</data>
@@ -238,7 +243,10 @@
<data name="UpdateCheckingNewVersion" xml:space="preserve">
<value>正在检查新版本...</value>
</data>
<data name="UpdateDownloadingNewVersion" xml:space="preserve">
<value>更新中: {0} ({1} MB)...... 在你等待的时候,可以考虑趁现在进行捐赠!:)</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>更新完毕</value>
</data>
@@ -277,7 +285,10 @@
<value>请输入非正式的值 {0}: </value>
<comment>{0} will be replaced by property name. Please note that this translation should end with space</comment>
</data>
<data name="UserInputIPCHost" xml:space="preserve">
<value>请输入你的IPC主机 </value>
<comment>Please note that this translation should end with space</comment>
</data>
<data name="WarningUnknownValuePleaseReport" xml:space="preserve">
<value>收到的{0} 为未知值,请报告此值:{1}</value>
<comment>{0} will be replaced by object's name, {1} will be replaced by value for that object</comment>
@@ -286,10 +297,20 @@
<value>目前无法同时挂 {0} 个以上的游戏,只有 {1} 里面的前 {0} 个游戏可用!</value>
<comment>{0} will be replaced by max number of games, {1} will be replaced by name of the configuration property</comment>
</data>
<data name="ErrorIPCAddressAccessDeniedException" xml:space="preserve">
<value>由于目标地址访问受拒绝,无法启动 IPC 服务 如果你想要使用ASF提供的 IPC 服务,请用管理员身份运行,或者给予更高的权限。</value>
</data>
<data name="IPCAnswered" xml:space="preserve">
<value>IPC 命令响应︰ {0} 及 {1}</value>
<comment>{0} will be replaced by IPC command, {1} will be replaced by IPC answer</comment>
</data>
<data name="IPCReady" xml:space="preserve">
<value>IPC 服务已就绪 </value>
</data>
<data name="IPCStarting" xml:space="preserve">
<value>在 {0} 上的启动 IPC 服务...</value>
<comment>{0} will be replaced by IPC hostname</comment>
</data>
<data name="BotAlreadyStopped" xml:space="preserve">
<value>这个帐号已停止运行!</value>
</data>
@@ -649,5 +670,8 @@
<value>已完成Steam探索队列 #{0}。</value>
<comment>{0} will be replaced by queue number</comment>
</data>
<data name="BotOwnsOverview" xml:space="preserve">
<value>{0}/{1} 个BOT已经拥有所有正在被检查的游戏。</value>
<comment>{0} will be replaced by number of bots that already own games being checked, {1} will be replaced by total number of bots that were checked during the process</comment>
</data>
</root>

View File

@@ -42,14 +42,14 @@ namespace ArchiSteamFarm {
private const byte CodeInterval = 30;
private static readonly char[] CodeCharacters = { '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'T', 'V', 'W', 'X', 'Y' };
private static readonly SemaphoreSlim TimeSemaphore = new SemaphoreSlim(1);
private static readonly SemaphoreSlim TimeSemaphore = new SemaphoreSlim(1, 1);
private static int? SteamTimeDifference;
// "ERROR" is being used by SteamDesktopAuthenticator
internal bool HasCorrectDeviceID => !string.IsNullOrEmpty(DeviceID) && !DeviceID.Equals("ERROR");
private readonly SemaphoreSlim ConfirmationsSemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim ConfirmationsSemaphore = new SemaphoreSlim(1, 1);
#pragma warning disable 649
[JsonProperty(PropertyName = "identity_secret", Required = Required.Always)]

View File

@@ -40,7 +40,7 @@ namespace ArchiSteamFarm {
private const string URL = "https://" + SharedInfo.StatisticsServer;
private readonly Bot Bot;
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1);
private DateTime LastAnnouncementCheck = DateTime.MinValue;
private DateTime LastHeartBeat = DateTime.MinValue;

View File

@@ -37,7 +37,7 @@ namespace ArchiSteamFarm {
private readonly Bot Bot;
private readonly ConcurrentHashSet<ulong> IgnoredTrades = new ConcurrentHashSet<ulong>();
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1);
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1, 1);
private bool ParsingScheduled;

View File

@@ -535,7 +535,17 @@ namespace ArchiSteamFarm {
ushort status = (ushort) responseMessage.StatusCode;
if ((status >= 300) && (status <= 399) && (maxRedirections > 0)) {
redirectUri = responseMessage.Headers.Location;
if (!redirectUri.IsAbsoluteUri) {
if (redirectUri.IsAbsoluteUri) {
switch (redirectUri.Scheme) {
case "http":
case "https":
break;
default:
// Invalid ones such as "steammobile"
return null;
}
} else {
redirectUri = new Uri(requestUri.GetLeftPart(UriPartial.Authority) + redirectUri);
}
} else {

View File

@@ -18,6 +18,6 @@
"OptimizationMode": 0,
"Statistics": true,
"SteamOwnerID": 0,
"SteamProtocols": 7,
"SteamProtocols": 1,
"UpdateChannel": 1
}

View File

@@ -42,7 +42,7 @@ deploy:
- provider: GitHub
tag: $(appveyor_repo_tag_name)
release: ArchiSteamFarm V$(appveyor_repo_tag_name)
description: '**NOTICE:** Pre-releases are experimental versions that often contain unpatched bugs, work-in-progress features or rewritten implementations. If you don''t consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchi/ArchiSteamFarm/wiki/Release-cycle)** if you''d like to learn more.\n\n---\n\nThis is automated AppVeyor GitHub deployment, human-readable changelog should be available soon. In the meantime please refer to **[GitHub commits](https://github.com/JustArchi/ArchiSteamFarm/commits/$(appveyor_repo_tag_name))**.\n\n---\n\nASF is available for free. If you''re grateful for what we''re doing, please consider donating. Developing ASF requires massive amount of time and knowledge, especially when it comes to Steam (and its problems). Even 1$ is highly appreciated and shows that you care!\n\n [![Patreon support](https://img.shields.io/badge/Patreon-support-yellow.svg)](https://www.patreon.com/JustArchi) [![Paypal.me donate](https://img.shields.io/badge/Paypal.me-donate-yellow.svg)](https://www.paypal.me/JustArchi/1usd) [![Paypal donate](https://img.shields.io/badge/Paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4) [![Bitcoin donate](https://img.shields.io/badge/Bitcoin-donate-yellow.svg)](https://blockchain.info/payment_request?address=1Archi6M1r5b41Rvn1SY2FfJAzsrEUT7aT) [![Steam donate](https://img.shields.io/badge/Steam-donate-yellow.svg)](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)'
description: '### **NOTICE:** Pre-releases are experimental versions that often contain unpatched bugs, work-in-progress features or rewritten implementations. If you don''t consider yourself advanced user, please download **[latest stable release](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)** instead. Pre-release versions are dedicated to users who know how to report bugs, deal with issues and give feedback - no technical support will be given. Check out ASF **[release cycle](https://github.com/JustArchi/ArchiSteamFarm/wiki/Release-cycle)** if you''d like to learn more.\n\n---\n\nThis is automated AppVeyor GitHub deployment, human-readable changelog should be available soon. In the meantime please refer to **[GitHub commits](https://github.com/JustArchi/ArchiSteamFarm/commits/$(appveyor_repo_tag_name))**.\n\n---\n\nASF is available for free. If you''re grateful for what we''re doing, please consider donating. Developing ASF requires massive amount of time and knowledge, especially when it comes to Steam (and its problems). Even 1$ is highly appreciated and shows that you care!\n\n [![Patreon support](https://img.shields.io/badge/Patreon-support-yellow.svg)](https://www.patreon.com/JustArchi) [![Paypal.me donate](https://img.shields.io/badge/Paypal.me-donate-yellow.svg)](https://www.paypal.me/JustArchi/1usd) [![Paypal donate](https://img.shields.io/badge/Paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=HD2P2P3WGS5Y4) [![Bitcoin donate](https://img.shields.io/badge/Bitcoin-donate-yellow.svg)](https://blockchain.info/payment_request?address=1Archi6M1r5b41Rvn1SY2FfJAzsrEUT7aT) [![Steam donate](https://img.shields.io/badge/Steam-donate-yellow.svg)](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)'
auth_token:
secure: QC5gIDMvSpd43EG6qW8d1E3ZHiVU4aR7pbKQonXstjj/JtAABf5S1IbtoY4OsnOR
artifact: /.*/
@@ -62,7 +62,7 @@ notifications:
{
"avatar_url": "https://www.appveyor.com/assets/img/appveyor-logo-256.png",
"username": "AppVeyor",
"content": "[{{projectName}}:{{branch}}] {{commitMessage}} - {{committerName}} ({{commitId}}) | **{{status}}** | {{buildUrl}}"
"content": "[{{projectName}}:{{branch}}] {{commitMessage}} - {{committerName}} ({{commitId}}) | {{buildUrl}}/artifacts | **{{status}}**"
}
on_build_success: true
on_build_failure: true