Compare commits

..

13 Commits

Author SHA1 Message Date
JustArchi
a5f7e7988c Change default FarmingDelay from 5 to 15
Now that we have event-based mechanism, we don't need to check that often, but still keep fuckups in mind
2016-06-09 01:15:48 +02:00
JustArchi
80ed0e66bb Never restart cards farming module
There is enough logic to handle games added in the meantime already
2016-06-09 00:49:52 +02:00
JustArchi
5529a8e1f0 Fix more regressions 2016-06-09 00:35:54 +02:00
JustArchi
496bea5ac5 Fix regression 2016-06-09 00:01:13 +02:00
JustArchi
52f3a86255 EXPERIMENTAL: Closes #238
Needs further tests
2016-06-08 23:26:37 +02:00
Łukasz Domeradzki
7d205cfa42 Update README.md 2016-06-08 19:02:34 +02:00
JustArchi
c4f47c56da Misc 2016-06-08 13:01:41 +02:00
JustArchi
546440d9dc Bump 2016-06-06 18:09:50 +02:00
JustArchi
ffa6548594 Reset games played also OnFarmingStopped() 2016-06-06 17:38:21 +02:00
JustArchi
53d59ce2a9 Add !version 2016-06-06 05:27:11 +02:00
JustArchi
b966db5845 Misc 2016-06-05 16:40:23 +02:00
JustArchi
aae41d5c1f Add IsBotAccount + misc 2016-06-04 22:02:38 +02:00
JustArchi
8ace0d7782 Bump 2016-06-03 00:57:15 +02:00
15 changed files with 201 additions and 101 deletions

View File

@@ -0,0 +1,17 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=API/@EntryIndexedValue">API</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ASF/@EntryIndexedValue">ASF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FA/@EntryIndexedValue">FA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FS/@EntryIndexedValue">FS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OK/@EntryIndexedValue">OK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PIN/@EntryIndexedValue">PIN</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SC/@EntryIndexedValue">SC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SMS/@EntryIndexedValue">SMS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TTL/@EntryIndexedValue">TTL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=URL/@EntryIndexedValue">URL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WCF/@EntryIndexedValue">WCF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WTF/@EntryIndexedValue">WTF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XML/@EntryIndexedValue">XML</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="AaBb" /&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String></wpf:ResourceDictionary>

View File

@@ -85,9 +85,7 @@ namespace ArchiSteamFarm {
JobID = jobID; JobID = jobID;
if (msg.count_new_items > 0) { if (msg.count_new_items > 0) {
Notifications = new HashSet<ENotification> { Notifications = new HashSet<ENotification> { ENotification.Items };
ENotification.Items
};
} }
} }
} }

View File

@@ -308,7 +308,7 @@ namespace ArchiSteamFarm {
} }
if (response == null) { if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries"); Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return null; return null;
} }
@@ -393,16 +393,14 @@ namespace ArchiSteamFarm {
State = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>() State = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
}; };
foreach (KeyValue item in trade["items_to_give"].Children) { foreach (Steam.Item steamItem in trade["items_to_give"].Children.Select(item => new Steam.Item {
Steam.Item steamItem = new Steam.Item { AppID = (uint) item["appid"].AsUnsignedLong(),
AppID = (uint) item["appid"].AsUnsignedLong(), ContextID = item["contextid"].AsUnsignedLong(),
ContextID = item["contextid"].AsUnsignedLong(), AssetID = item["assetid"].AsUnsignedLong(),
AssetID = item["assetid"].AsUnsignedLong(), ClassID = item["classid"].AsUnsignedLong(),
ClassID = item["classid"].AsUnsignedLong(), InstanceID = item["instanceid"].AsUnsignedLong(),
InstanceID = item["instanceid"].AsUnsignedLong(), Amount = (uint) item["amount"].AsUnsignedLong()
Amount = (uint) item["amount"].AsUnsignedLong() })) {
};
Tuple<uint, Steam.Item.EType> description; Tuple<uint, Steam.Item.EType> description;
if (descriptions.TryGetValue(steamItem.ClassID, out description)) { if (descriptions.TryGetValue(steamItem.ClassID, out description)) {
steamItem.RealAppID = description.Item1; steamItem.RealAppID = description.Item1;
@@ -412,16 +410,14 @@ namespace ArchiSteamFarm {
tradeOffer.ItemsToGive.Add(steamItem); tradeOffer.ItemsToGive.Add(steamItem);
} }
foreach (KeyValue item in trade["items_to_receive"].Children) { foreach (Steam.Item steamItem in trade["items_to_receive"].Children.Select(item => new Steam.Item {
Steam.Item steamItem = new Steam.Item { AppID = (uint) item["appid"].AsUnsignedLong(),
AppID = (uint) item["appid"].AsUnsignedLong(), ContextID = item["contextid"].AsUnsignedLong(),
ContextID = item["contextid"].AsUnsignedLong(), AssetID = item["assetid"].AsUnsignedLong(),
AssetID = item["assetid"].AsUnsignedLong(), ClassID = item["classid"].AsUnsignedLong(),
ClassID = item["classid"].AsUnsignedLong(), InstanceID = item["instanceid"].AsUnsignedLong(),
InstanceID = item["instanceid"].AsUnsignedLong(), Amount = (uint) item["amount"].AsUnsignedLong()
Amount = (uint) item["amount"].AsUnsignedLong() })) {
};
Tuple<uint, Steam.Item.EType> description; Tuple<uint, Steam.Item.EType> description;
if (descriptions.TryGetValue(steamItem.ClassID, out description)) { if (descriptions.TryGetValue(steamItem.ClassID, out description)) {
steamItem.RealAppID = description.Item1; steamItem.RealAppID = description.Item1;
@@ -464,16 +460,49 @@ namespace ArchiSteamFarm {
return await WebBrowser.UrlPostRetry(request, data, referer).ConfigureAwait(false); return await WebBrowser.UrlPostRetry(request, data, referer).ConfigureAwait(false);
} }
internal async Task<HashSet<Steam.Item>> GetMyTradableInventory() { internal bool DeclineTradeOffer(ulong tradeID) {
if ((tradeID == 0) || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
// TODO: Correct this when Mono 4.4+ will be a latest stable one | https://bugzilla.xamarin.com/show_bug.cgi?id=39455
Logging.LogNullError("tradeID || SteamApiKey", Bot.BotName);
//Logging.LogNullError(nameof(tradeID) + " || " + nameof(Bot.BotConfig.SteamApiKey), Bot.BotName);
return false;
}
KeyValue response = null;
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
iEconService.Timeout = Timeout;
for (byte i = 0; (i < WebBrowser.MaxRetries) && (response == null); i++) {
try {
response = iEconService.DeclineTradeOffer(
tradeofferid: tradeID.ToString(),
method: WebRequestMethods.Http.Post,
secure: !Program.GlobalConfig.ForceHttp
);
} catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName);
}
}
}
if (response != null) {
return true;
}
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
internal async Task<HashSet<Steam.Item>> GetMyInventory(bool tradable) {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null; return null;
} }
HashSet<Steam.Item> result = new HashSet<Steam.Item>(); HashSet<Steam.Item> result = new HashSet<Steam.Item>();
ushort nextPage = 0; uint currentPage = 0;
while (true) { while (true) {
string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1&start=" + nextPage; string request = SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=" + (tradable ? "1" : "0") + "&start=" + currentPage;
JObject jObject = await WebBrowser.UrlGetToJObjectRetry(request).ConfigureAwait(false); JObject jObject = await WebBrowser.UrlGetToJObjectRetry(request).ConfigureAwait(false);
if (jObject == null) { if (jObject == null) {
@@ -504,13 +533,14 @@ namespace ArchiSteamFarm {
} }
uint appID = 0; uint appID = 0;
Steam.Item.EType type = Steam.Item.EType.Unknown;
string hashName = description["market_hash_name"].ToString(); string hashName = description["market_hash_name"].ToString();
if (!string.IsNullOrEmpty(hashName)) { if (!string.IsNullOrEmpty(hashName)) {
appID = GetAppIDFromMarketHashName(hashName); appID = GetAppIDFromMarketHashName(hashName);
} }
Steam.Item.EType type = Steam.Item.EType.Unknown;
string descriptionType = description["type"].ToString(); string descriptionType = description["type"].ToString();
if (!string.IsNullOrEmpty(descriptionType)) { if (!string.IsNullOrEmpty(descriptionType)) {
type = GetItemType(descriptionType); type = GetItemType(descriptionType);
@@ -526,11 +556,10 @@ namespace ArchiSteamFarm {
} }
foreach (JToken item in items) { foreach (JToken item in items) {
Steam.Item steamItem; Steam.Item steamItem;
try { try {
steamItem = JsonConvert.DeserializeObject<Steam.Item>(item.ToString()); steamItem = item.ToObject<Steam.Item>();
} catch (JsonException e) { } catch (JsonException e) {
Logging.LogGenericException(e, Bot.BotName); Logging.LogGenericException(e, Bot.BotName);
continue; continue;
@@ -555,12 +584,17 @@ namespace ArchiSteamFarm {
break; // OK, last page break; // OK, last page
} }
if (ushort.TryParse(jObject["more_start"].ToString(), out nextPage)) { uint nextPage;
continue; if (!uint.TryParse(jObject["more_start"].ToString(), out nextPage)) {
Logging.LogNullError(nameof(nextPage), Bot.BotName);
break;
} }
Logging.LogNullError(nameof(nextPage), Bot.BotName); if (nextPage <= currentPage) {
break; break;
}
currentPage = nextPage;
} }
return result; return result;

View File

@@ -315,7 +315,11 @@ namespace ArchiSteamFarm {
return false; return false;
} }
internal void OnFarmingStopped() => ResetGamesPlayed();
internal async Task OnFarmingFinished(bool farmedSomething) { internal async Task OnFarmingFinished(bool farmedSomething) {
OnFarmingStopped();
if ((farmedSomething || !FirstTradeSent) && BotConfig.SendOnFarmingFinished) { if ((farmedSomething || !FirstTradeSent) && BotConfig.SendOnFarmingFinished) {
FirstTradeSent = true; FirstTradeSent = true;
await ResponseSendTrade(BotConfig.SteamMasterID).ConfigureAwait(false); await ResponseSendTrade(BotConfig.SteamMasterID).ConfigureAwait(false);
@@ -326,11 +330,8 @@ namespace ArchiSteamFarm {
SkipFirstShutdown = false; SkipFirstShutdown = false;
} else { } else {
Stop(); Stop();
return;
} }
} }
ResetGamesPlayed();
} }
internal async Task<string> Response(ulong steamID, string message) { internal async Task<string> Response(ulong steamID, string message) {
@@ -375,6 +376,8 @@ namespace ArchiSteamFarm {
return ResponseStop(steamID); return ResponseStop(steamID);
case "!update": case "!update":
return await ResponseUpdate(steamID).ConfigureAwait(false); return await ResponseUpdate(steamID).ConfigureAwait(false);
case "!version":
return ResponseVersion(steamID);
default: default:
return ResponseUnknown(steamID); return ResponseUnknown(steamID);
} }
@@ -655,7 +658,7 @@ namespace ArchiSteamFarm {
} }
await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false); await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false);
HashSet<Steam.Item> inventory = await ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false); HashSet<Steam.Item> inventory = await ArchiWebHandler.GetMyInventory(true).ConfigureAwait(false);
if ((inventory == null) || (inventory.Count == 0)) { if ((inventory == null) || (inventory.Count == 0)) {
return "Nothing to send, inventory seems empty!"; return "Nothing to send, inventory seems empty!";
@@ -835,7 +838,11 @@ namespace ArchiSteamFarm {
return "This bot instance is not connected!"; return "This bot instance is not connected!";
} }
CardsFarmer.RestartFarming().Forget(); if (CardsFarmer.CurrentGamesFarming.Count > 0) {
return "This bot instance is farming already!";
}
CardsFarmer.StartFarming().Forget();
return "Done!"; return "Done!";
} }
@@ -1305,6 +1312,19 @@ namespace ArchiSteamFarm {
return "Done!"; return "Done!";
} }
private string ResponseVersion(ulong steamID) {
if (steamID == 0) {
Logging.LogNullError(nameof(steamID));
return null;
}
if (!IsMaster(steamID)) {
return null;
}
return "ASF V" + Program.Version;
}
private void HandleCallbacks() { private void HandleCallbacks() {
TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep); TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep);
while (KeepRunning || SteamClient.IsConnected) { while (KeepRunning || SteamClient.IsConnected) {
@@ -1646,7 +1666,8 @@ namespace ArchiSteamFarm {
} }
if (acceptedSomething) { if (acceptedSomething) {
CardsFarmer.RestartFarming().Forget(); // Start farming, but only if we're not farming already
CardsFarmer.StartFarming().Forget();
} }
} }
@@ -1704,11 +1725,12 @@ namespace ArchiSteamFarm {
// TODO: Accept clan invites from master? // TODO: Accept clan invites from master?
break; break;
default: default:
if (!IsMaster(friend.SteamID)) { if (IsMaster(friend.SteamID)) {
break; SteamFriends.AddFriend(friend.SteamID);
} else if (BotConfig.IsBotAccount) {
SteamFriends.RemoveFriend(friend.SteamID);
} }
SteamFriends.AddFriend(friend.SteamID);
break; break;
} }
} }
@@ -1945,26 +1967,19 @@ namespace ArchiSteamFarm {
return; return;
} }
bool checkTrades = false;
bool markInventory = false;
foreach (ArchiHandler.NotificationsCallback.ENotification notification in callback.Notifications) { foreach (ArchiHandler.NotificationsCallback.ENotification notification in callback.Notifications) {
switch (notification) { switch (notification) {
case ArchiHandler.NotificationsCallback.ENotification.Items: case ArchiHandler.NotificationsCallback.ENotification.Items:
markInventory = true; CardsFarmer.OnNewItemsNotification();
if (BotConfig.DismissInventoryNotifications) {
await ArchiWebHandler.MarkInventory().ConfigureAwait(false);
}
break; break;
case ArchiHandler.NotificationsCallback.ENotification.Trading: case ArchiHandler.NotificationsCallback.ENotification.Trading:
checkTrades = true; await Trading.CheckTrades().ConfigureAwait(false);
break; break;
} }
} }
if (checkTrades) {
Trading.CheckTrades().Forget();
}
if (markInventory && BotConfig.DismissInventoryNotifications) {
await ArchiWebHandler.MarkInventory().ConfigureAwait(false);
}
} }
private void OnOfflineMessage(ArchiHandler.OfflineMessageCallback callback) { private void OnOfflineMessage(ArchiHandler.OfflineMessageCallback callback) {
@@ -2007,8 +2022,8 @@ namespace ArchiSteamFarm {
} }
if (callback.PurchaseResult == ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK) { if (callback.PurchaseResult == ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK) {
// We will restart CF module to recalculate current status and decide about new optimal approach // Start farming, but only if we're not farming already
CardsFarmer.RestartFarming().Forget(); CardsFarmer.StartFarming().Forget();
} }
} }
} }

View File

@@ -25,11 +25,12 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
// ReSharper disable once ClassCannotBeInstantiated [SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
// ReSharper disable once ClassNeverInstantiated.Global [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
internal sealed class BotConfig { internal sealed class BotConfig {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal bool Enabled { get; private set; } = false; internal bool Enabled { get; private set; } = false;
@@ -70,6 +71,9 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal bool AcceptGifts { get; private set; } = false; internal bool AcceptGifts { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool IsBotAccount { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal bool SteamTradeMatcher { get; private set; } = false; internal bool SteamTradeMatcher { get; private set; } = false;

View File

@@ -44,7 +44,7 @@ namespace ArchiSteamFarm {
internal bool ManualMode { get; private set; } internal bool ManualMode { get; private set; }
private bool NowFarming; private bool KeepFarming, NowFarming;
internal CardsFarmer(Bot bot) { internal CardsFarmer(Bot bot) {
if (bot == null) { if (bot == null) {
@@ -107,7 +107,7 @@ namespace ArchiSteamFarm {
return; return;
} }
NowFarming = true; KeepFarming = NowFarming = true;
FarmingSemaphore.Release(); // From this point we allow other calls to shut us down FarmingSemaphore.Release(); // From this point we allow other calls to shut us down
do { do {
@@ -170,10 +170,11 @@ namespace ArchiSteamFarm {
} }
Logging.LogGenericInfo("Sending signal to stop farming", Bot.BotName); Logging.LogGenericInfo("Sending signal to stop farming", Bot.BotName);
KeepFarming = false;
FarmResetEvent.Set(); FarmResetEvent.Set();
Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName); Logging.LogGenericInfo("Waiting for reaction...", Bot.BotName);
for (byte i = 0; (i < Program.GlobalConfig.HttpTimeout) && NowFarming; i++) { for (byte i = 0; (i < 5) && NowFarming; i++) {
await Utilities.SleepAsync(1000).ConfigureAwait(false); await Utilities.SleepAsync(1000).ConfigureAwait(false);
} }
@@ -181,14 +182,17 @@ namespace ArchiSteamFarm {
Logging.LogGenericWarning("Timed out!", Bot.BotName); Logging.LogGenericWarning("Timed out!", Bot.BotName);
} }
FarmResetEvent.Reset();
Logging.LogGenericInfo("Farming stopped!", Bot.BotName); Logging.LogGenericInfo("Farming stopped!", Bot.BotName);
Bot.OnFarmingStopped();
FarmingSemaphore.Release(); FarmingSemaphore.Release();
} }
internal async Task RestartFarming() { internal void OnNewItemsNotification() {
await StopFarming().ConfigureAwait(false); if (!NowFarming) {
await StartFarming().ConfigureAwait(false); return;
}
FarmResetEvent.Set();
} }
private static HashSet<uint> GetGamesToFarmSolo(ConcurrentDictionary<uint, float> gamesToFarm) { private static HashSet<uint> GetGamesToFarmSolo(ConcurrentDictionary<uint, float> gamesToFarm) {
@@ -217,22 +221,25 @@ namespace ArchiSteamFarm {
} }
byte maxPages = 1; byte maxPages = 1;
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
if ((htmlNodeCollection != null) && (htmlNodeCollection.Count > 0)) { HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("(//a[@class='pagelink'])[last()]");
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1]; if (htmlNode != null) {
string lastPage = htmlNode.InnerText; string lastPage = htmlNode.InnerText;
if (!string.IsNullOrEmpty(lastPage)) { if (string.IsNullOrEmpty(lastPage)) {
if (!byte.TryParse(lastPage, out maxPages)) { Logging.LogNullError(nameof(lastPage), Bot.BotName);
maxPages = 1; // Should never happen return false;
} }
if (!byte.TryParse(lastPage, out maxPages) || (maxPages == 0)) {
Logging.LogNullError(nameof(maxPages), Bot.BotName);
return false;
} }
} }
GamesToFarm.Clear(); GamesToFarm.Clear();
CheckPage(htmlDocument); CheckPage(htmlDocument);
if (maxPages <= 1) { if (maxPages == 1) {
return GamesToFarm.Count > 0; return GamesToFarm.Count > 0;
} }
@@ -255,8 +262,7 @@ namespace ArchiSteamFarm {
} }
HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_title_stats']"); HtmlNodeCollection htmlNodes = htmlDocument.DocumentNode.SelectNodes("//div[@class='badge_title_stats']");
if (htmlNodes == null) { if (htmlNodes == null) { // For example a page full of non-games badges
Logging.LogNullError(nameof(htmlNodes), Bot.BotName);
return; return;
} }
@@ -423,22 +429,28 @@ namespace ArchiSteamFarm {
} }
Bot.ArchiHandler.PlayGames(appID); Bot.ArchiHandler.PlayGames(appID);
DateTime endFarmingDate = DateTime.Now.AddHours(Program.GlobalConfig.MaxFarmingTime);
bool success = true; bool success = true;
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false); bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
for (ushort farmingTime = 0; (farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime) && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
while (keepFarming.GetValueOrDefault(true) && (DateTime.Now < endFarmingDate)) {
Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName);
DateTime startFarmingPeriod = DateTime.Now;
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) { if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
success = false; FarmResetEvent.Reset();
break; success = KeepFarming;
} }
// Don't forget to update our GamesToFarm hours // Don't forget to update our GamesToFarm hours
float timePlayed = Program.GlobalConfig.FarmingDelay / 60.0F; GamesToFarm[appID] += (float) DateTime.Now.Subtract(startFarmingPeriod).TotalHours;
GamesToFarm[appID] += timePlayed;
if (!success) {
break;
}
keepFarming = await ShouldFarm(appID).ConfigureAwait(false); keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName);
} }
Logging.LogGenericInfo("Stopped farming: " + appID, Bot.BotName); Logging.LogGenericInfo("Stopped farming: " + appID, Bot.BotName);
@@ -459,19 +471,25 @@ namespace ArchiSteamFarm {
bool success = true; bool success = true;
while (maxHour < 2) { while (maxHour < 2) {
Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName);
DateTime startFarmingPeriod = DateTime.Now;
if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) { if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
success = false; FarmResetEvent.Reset();
break; success = KeepFarming;
} }
// Don't forget to update our GamesToFarm hours // Don't forget to update our GamesToFarm hours
float timePlayed = Program.GlobalConfig.FarmingDelay / 60.0F; float timePlayed = (float) DateTime.Now.Subtract(startFarmingPeriod).TotalHours;
foreach (uint appID in appIDs) { foreach (uint appID in appIDs) {
GamesToFarm[appID] += timePlayed; GamesToFarm[appID] += timePlayed;
} }
if (!success) {
break;
}
maxHour += timePlayed; maxHour += timePlayed;
Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName);
} }
Logging.LogGenericInfo("Stopped farming: " + string.Join(", ", appIDs), Bot.BotName); Logging.LogGenericInfo("Stopped farming: " + string.Join(", ", appIDs), Bot.BotName);

View File

@@ -30,7 +30,8 @@ using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated"), SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] [SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
internal sealed class GlobalConfig { internal sealed class GlobalConfig {
[SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "UnusedMember.Global")]
internal enum EUpdateChannel : byte { internal enum EUpdateChannel : byte {
@@ -42,7 +43,7 @@ namespace ArchiSteamFarm {
internal const byte DefaultHttpTimeout = 60; internal const byte DefaultHttpTimeout = 60;
private const byte DefaultMaxFarmingTime = 10; private const byte DefaultMaxFarmingTime = 10;
private const byte DefaultFarmingDelay = 5; private const byte DefaultFarmingDelay = 15;
private const ushort DefaultWCFPort = 1242; private const ushort DefaultWCFPort = 1242;
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp; private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;

View File

@@ -433,7 +433,7 @@ namespace ArchiSteamFarm {
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler; AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler; TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;
Logging.LogGenericInfo("Archi's Steam Farm, version " + Version); Logging.LogGenericInfo("ASF V" + Version);
Directory.SetCurrentDirectory(ExecutableDirectory); Directory.SetCurrentDirectory(ExecutableDirectory);
InitServices(); InitServices();

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.5.1")] [assembly: AssemblyVersion("2.0.5.3")]
[assembly: AssemblyFileVersion("2.0.5.1")] [assembly: AssemblyFileVersion("2.0.5.3")]

View File

@@ -111,6 +111,9 @@ namespace ArchiSteamFarm {
if (await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false)) { if (await ShouldAcceptTrade(tradeOffer).ConfigureAwait(false)) {
Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName); Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false); await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
} else if (Bot.BotConfig.IsBotAccount) {
Logging.LogGenericInfo("Rejecting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
Bot.ArchiWebHandler.DeclineTradeOffer(tradeOffer.TradeOfferID);
} else { } else {
Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName); Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
} }
@@ -150,7 +153,7 @@ namespace ArchiSteamFarm {
// At this point we're sure that STM trade is valid // At this point we're sure that STM trade is valid
// Now check if it's worth for us to do the trade // Now check if it's worth for us to do the trade
HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false); HashSet<Steam.Item> inventory = await Bot.ArchiWebHandler.GetMyInventory(false).ConfigureAwait(false);
if ((inventory == null) || (inventory.Count == 0)) { if ((inventory == null) || (inventory.Count == 0)) {
return true; // OK, assume that this trade is valid, we can't check our EQ return true; // OK, assume that this trade is valid, we can't check our EQ
} }

View File

@@ -8,7 +8,7 @@
"SteamOwnerID": 0, "SteamOwnerID": 0,
"MaxFarmingTime": 10, "MaxFarmingTime": 10,
"IdleFarmingPeriod": 3, "IdleFarmingPeriod": 3,
"FarmingDelay": 5, "FarmingDelay": 15,
"LoginLimiterDelay": 7, "LoginLimiterDelay": 7,
"InventoryLimiterDelay": 3, "InventoryLimiterDelay": 3,
"ForceHttp": false, "ForceHttp": false,

View File

@@ -12,6 +12,7 @@
"FarmOffline": false, "FarmOffline": false,
"HandleOfflineMessages": false, "HandleOfflineMessages": false,
"AcceptGifts": false, "AcceptGifts": false,
"IsBotAccount": false,
"SteamTradeMatcher": false, "SteamTradeMatcher": false,
"ForwardKeysToOtherBots": false, "ForwardKeysToOtherBots": false,
"DistributeKeys": false, "DistributeKeys": false,

View File

@@ -30,7 +30,10 @@ using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
namespace ConfigGenerator { namespace ConfigGenerator {
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal sealed class BotConfig : ASFConfig { internal sealed class BotConfig : ASFConfig {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public bool Enabled { get; set; } = false; public bool Enabled { get; set; } = false;
@@ -41,7 +44,8 @@ namespace ConfigGenerator {
[JsonProperty] [JsonProperty]
public string SteamLogin { get; set; } = null; public string SteamLogin { get; set; } = null;
[JsonProperty, PasswordPropertyText(true)] [JsonProperty]
[PasswordPropertyText(true)]
public string SteamPassword { get; set; } = null; public string SteamPassword { get; set; } = null;
[JsonProperty] [JsonProperty]
@@ -71,6 +75,9 @@ namespace ConfigGenerator {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public bool AcceptGifts { get; set; } = false; public bool AcceptGifts { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool IsBotAccount { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public bool SteamTradeMatcher { get; set; } = false; public bool SteamTradeMatcher { get; set; } = false;

View File

@@ -30,9 +30,11 @@ using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
namespace ConfigGenerator { namespace ConfigGenerator {
[SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global"), SuppressMessage("ReSharper", "CollectionNeverQueried.Global"), SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global")] [SuppressMessage("ReSharper", "AutoPropertyCanBeMadeGetOnly.Global")]
[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal sealed class GlobalConfig : ASFConfig { internal sealed class GlobalConfig : ASFConfig {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
internal enum EUpdateChannel : byte { internal enum EUpdateChannel : byte {
Unknown, Unknown,
Stable, Stable,
@@ -40,7 +42,7 @@ namespace ConfigGenerator {
} }
private const byte DefaultMaxFarmingTime = 10; private const byte DefaultMaxFarmingTime = 10;
private const byte DefaultFarmingDelay = 5; private const byte DefaultFarmingDelay = 15;
private const byte DefaultHttpTimeout = 60; private const byte DefaultHttpTimeout = 60;
private const ushort DefaultWCFPort = 1242; private const ushort DefaultWCFPort = 1242;
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp; private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;

View File

@@ -5,7 +5,7 @@ ArchiSteamFarm
[![Build Status (Mono)](https://img.shields.io/travis/JustArchi/ArchiSteamFarm.svg?label=Mono)](https://travis-ci.org/JustArchi/ArchiSteamFarm) [![Build Status (Mono)](https://img.shields.io/travis/JustArchi/ArchiSteamFarm.svg?label=Mono)](https://travis-ci.org/JustArchi/ArchiSteamFarm)
[![GitHub Release](https://img.shields.io/github/release/JustArchi/ArchiSteamFarm.svg?label=Latest)](https://github.com/JustArchi/ArchiSteamFarm/releases/latest) [![GitHub Release](https://img.shields.io/github/release/JustArchi/ArchiSteamFarm.svg?label=Latest)](https://github.com/JustArchi/ArchiSteamFarm/releases/latest)
[![Github All Releases](https://img.shields.io/github/downloads/JustArchi/ArchiSteamFarm/total.svg?label=Downloads)](https://github.com/JustArchi/ArchiSteamFarm/releases) [![Github All Releases](https://img.shields.io/github/downloads/JustArchi/ArchiSteamFarm/total.svg?label=Downloads)](https://github.com/JustArchi/ArchiSteamFarm/releases)
[![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) [![Paypal Donate](https://img.shields.io/badge/Paypal-donate-yellow.svg)](https://www.paypal.me/JustArchi/1usd)
[![Steam Donate](https://img.shields.io/badge/Steam-donate-yellow.svg)](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_) [![Steam Donate](https://img.shields.io/badge/Steam-donate-yellow.svg)](https://steamcommunity.com/tradeoffer/new/?partner=46697991&token=0ix2Ruv_)
--- ---