Compare commits

..

18 Commits

Author SHA1 Message Date
JustArchi
ab531c80df Misc 2016-03-11 02:15:25 +01:00
JustArchi
f20ea0a87f Add FarmingPeriod 2016-03-11 02:07:20 +01:00
JustArchi
3e7f726afb Bump 2016-03-11 00:52:15 +01:00
JustArchi
d247515a03 Confirmations should be minutes-based 2016-03-10 22:54:59 +01:00
JustArchi
f084a3f219 Mark inventory on login if needed 2016-03-10 21:44:45 +01:00
JustArchi
4d3673c305 Forgot to add 2016-03-10 21:19:21 +01:00
JustArchi
0253c3bf7b Add AcceptConfirmationsPeriod 2016-03-10 21:17:48 +01:00
JustArchi
0eae895676 SteamAuth update 2016-03-10 21:11:54 +01:00
JustArchi
045acb362d Code review 2016-03-10 02:21:28 +01:00
JustArchi
ebd65da3ff Misc 2016-03-10 01:23:32 +01:00
JustArchi
1de3c5bec0 Code review 2016-03-10 01:20:17 +01:00
JustArchi
b334c939df Add !2faok 2016-03-10 00:40:30 +01:00
JustArchi
8e6100e236 Add MaxFarmingTime, FarmingDelay 2016-03-09 19:25:45 +01:00
JustArchi
8cb512b6e5 Add InventoryLimiterDelay, rename RequestLimiterDelay to LoginLimiterDelay 2016-03-09 18:58:14 +01:00
JustArchi
6a28946205 Refresh farming queue when farming is finished 2016-03-09 18:50:54 +01:00
JustArchi
c632c025cb Move Statistics to global config 2016-03-09 05:02:07 +01:00
JustArchi
1403ffe190 This actually DOES work on Mono, hooray! 2016-03-09 03:52:43 +01:00
JustArchi
71ae9a84da Fix misc bug, #48 2016-03-09 03:52:04 +01:00
22 changed files with 350 additions and 339 deletions

View File

@@ -105,17 +105,14 @@
<Compile Include="GlobalDatabase.cs" /> <Compile Include="GlobalDatabase.cs" />
<Compile Include="BotDatabase.cs" /> <Compile Include="BotDatabase.cs" />
<Compile Include="CardsFarmer.cs" /> <Compile Include="CardsFarmer.cs" />
<Compile Include="CMsgClientClanInviteAction.cs" /> <Compile Include="CMsgs\CMsgClientClanInviteAction.cs" />
<Compile Include="Debugging.cs" /> <Compile Include="Debugging.cs" />
<Compile Include="GlobalConfig.cs" /> <Compile Include="GlobalConfig.cs" />
<Compile Include="JSON\GitHub.cs" /> <Compile Include="JSON\GitHub.cs" />
<Compile Include="JSON\Steam.cs" />
<Compile Include="Logging.cs" /> <Compile Include="Logging.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SteamItem.cs" />
<Compile Include="SteamItemList.cs" />
<Compile Include="SteamTradeOffer.cs" />
<Compile Include="SteamTradeOfferRequest.cs" />
<Compile Include="Trading.cs" /> <Compile Include="Trading.cs" />
<Compile Include="Utilities.cs" /> <Compile Include="Utilities.cs" />
<Compile Include="WCF.cs" /> <Compile Include="WCF.cs" />

View File

@@ -46,6 +46,10 @@ namespace ArchiSteamFarm {
} }
internal ArchiWebHandler(Bot bot) { internal ArchiWebHandler(Bot bot) {
if (bot == null) {
return;
}
Bot = bot; Bot = bot;
} }
@@ -62,7 +66,7 @@ namespace ArchiSteamFarm {
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32); byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
// RSA encrypt it with the public key for the universe we're on // RSA encrypt it with the public key for the universe we're on
byte[] cryptedSessionKey = null; byte[] cryptedSessionKey;
using (RSACrypto rsa = new RSACrypto(KeyDictionary.GetPublicKey(steamClient.ConnectedUniverse))) { using (RSACrypto rsa = new RSACrypto(KeyDictionary.GetPublicKey(steamClient.ConnectedUniverse))) {
cryptedSessionKey = rsa.Encrypt(sessionKey); cryptedSessionKey = rsa.Encrypt(sessionKey);
} }
@@ -145,7 +149,7 @@ namespace ArchiSteamFarm {
return false; return false;
} }
internal List<SteamTradeOffer> GetTradeOffers() { internal List<Steam.TradeOffer> GetTradeOffers() {
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) { if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
return null; return null;
} }
@@ -172,15 +176,15 @@ namespace ArchiSteamFarm {
return null; return null;
} }
List<SteamTradeOffer> result = new List<SteamTradeOffer>(); List<Steam.TradeOffer> result = new List<Steam.TradeOffer>();
foreach (KeyValue trade in response["trade_offers_received"].Children) { foreach (KeyValue trade in response["trade_offers_received"].Children) {
SteamTradeOffer tradeOffer = new SteamTradeOffer { Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
tradeofferid = trade["tradeofferid"].AsString(), tradeofferid = trade["tradeofferid"].AsString(),
accountid_other = trade["accountid_other"].AsInteger(), accountid_other = trade["accountid_other"].AsInteger(),
trade_offer_state = trade["trade_offer_state"].AsEnum<SteamTradeOffer.ETradeOfferState>() trade_offer_state = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
}; };
foreach (KeyValue item in trade["items_to_give"].Children) { foreach (KeyValue item in trade["items_to_give"].Children) {
tradeOffer.items_to_give.Add(new SteamItem { tradeOffer.items_to_give.Add(new Steam.Item {
appid = item["appid"].AsString(), appid = item["appid"].AsString(),
contextid = item["contextid"].AsString(), contextid = item["contextid"].AsString(),
assetid = item["assetid"].AsString(), assetid = item["assetid"].AsString(),
@@ -190,7 +194,7 @@ namespace ArchiSteamFarm {
}); });
} }
foreach (KeyValue item in trade["items_to_receive"].Children) { foreach (KeyValue item in trade["items_to_receive"].Children) {
tradeOffer.items_to_receive.Add(new SteamItem { tradeOffer.items_to_receive.Add(new Steam.Item {
appid = item["appid"].AsString(), appid = item["appid"].AsString(),
contextid = item["contextid"].AsString(), contextid = item["contextid"].AsString(),
assetid = item["assetid"].AsString(), assetid = item["assetid"].AsString(),
@@ -297,7 +301,7 @@ namespace ArchiSteamFarm {
return true; return true;
} }
internal async Task<List<SteamItem>> GetMyTradableInventory() { internal async Task<List<Steam.Item>> GetMyTradableInventory() {
JObject jObject = null; JObject jObject = null;
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) { for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
jObject = await WebBrowser.UrlGetToJObject("https://steamcommunity.com/my/inventory/json/753/6?trading=1", Cookie).ConfigureAwait(false); jObject = await WebBrowser.UrlGetToJObject("https://steamcommunity.com/my/inventory/json/753/6?trading=1", Cookie).ConfigureAwait(false);
@@ -314,10 +318,10 @@ namespace ArchiSteamFarm {
return null; return null;
} }
List<SteamItem> result = new List<SteamItem>(); List<Steam.Item> result = new List<Steam.Item>();
foreach (JToken jToken in jTokens) { foreach (JToken jToken in jTokens) {
try { try {
result.Add(JsonConvert.DeserializeObject<SteamItem>(jToken.ToString())); result.Add(JsonConvert.DeserializeObject<Steam.Item>(jToken.ToString()));
} catch (Exception e) { } catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName); Logging.LogGenericException(e, Bot.BotName);
} }
@@ -326,7 +330,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
internal async Task<bool> SendTradeOffer(List<SteamItem> inventory, ulong partnerID, string token = null) { internal async Task<bool> SendTradeOffer(List<Steam.Item> inventory, ulong partnerID, string token = null) {
if (inventory == null || inventory.Count == 0 || partnerID == 0) { if (inventory == null || inventory.Count == 0 || partnerID == 0) {
return false; return false;
} }
@@ -336,21 +340,21 @@ namespace ArchiSteamFarm {
return false; return false;
} }
List<SteamTradeOfferRequest> trades = new List<SteamTradeOfferRequest>(1 + inventory.Count / Trading.MaxItemsPerTrade); List<Steam.TradeOfferRequest> trades = new List<Steam.TradeOfferRequest>(1 + inventory.Count / Trading.MaxItemsPerTrade);
SteamTradeOfferRequest singleTrade = null; Steam.TradeOfferRequest singleTrade = null;
for (ushort i = 0; i < inventory.Count; i++) { for (ushort i = 0; i < inventory.Count; i++) {
if (i % Trading.MaxItemsPerTrade == 0) { if (i % Trading.MaxItemsPerTrade == 0) {
if (trades.Count >= Trading.MaxTradesPerAccount) { if (trades.Count >= Trading.MaxTradesPerAccount) {
break; break;
} }
singleTrade = new SteamTradeOfferRequest(); singleTrade = new Steam.TradeOfferRequest();
trades.Add(singleTrade); trades.Add(singleTrade);
} }
SteamItem item = inventory[i]; Steam.Item item = inventory[i];
singleTrade.me.assets.Add(new SteamItem() { singleTrade.me.assets.Add(new Steam.Item() {
appid = "753", appid = "753",
contextid = "6", contextid = "6",
amount = item.amount, amount = item.amount,
@@ -361,14 +365,14 @@ namespace ArchiSteamFarm {
string referer = "https://steamcommunity.com/tradeoffer/new"; string referer = "https://steamcommunity.com/tradeoffer/new";
string request = referer + "/send"; string request = referer + "/send";
foreach (SteamTradeOfferRequest trade in trades) { foreach (Steam.TradeOfferRequest trade in trades) {
Dictionary<string, string> data = new Dictionary<string, string>(6) { Dictionary<string, string> data = new Dictionary<string, string>(6) {
{"sessionid", sessionID}, {"sessionid", sessionID},
{"serverid", "1"}, {"serverid", "1"},
{"partner", partnerID.ToString()}, {"partner", partnerID.ToString()},
{"tradeoffermessage", "Sent by ASF"}, {"tradeoffermessage", "Sent by ASF"},
{"json_tradeoffer", JsonConvert.SerializeObject(trade)}, {"json_tradeoffer", JsonConvert.SerializeObject(trade)},
{"trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : string.Format("{{ \"trade_offer_access_token\":\"{0}\" }}", token)} // TODO: This should be rewrote {"trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : $"{{\"trade_offer_access_token\":\"{token}\"}}"}
}; };
HttpResponseMessage response = null; HttpResponseMessage response = null;
@@ -466,13 +470,20 @@ namespace ArchiSteamFarm {
} }
foreach (string setCookieValue in setCookieValues) { foreach (string setCookieValue in setCookieValues) {
if (setCookieValue.Contains("steamparental=")) { if (!setCookieValue.Contains("steamparental=")) {
string setCookie = setCookieValue.Substring(setCookieValue.IndexOf("steamparental=") + 14); continue;
setCookie = setCookie.Substring(0, setCookie.IndexOf(';'));
Cookie["steamparental"] = setCookie;
Logging.LogGenericInfo("Success!", Bot.BotName);
return;
} }
string setCookie = setCookieValue.Substring(setCookieValue.IndexOf("steamparental=", StringComparison.Ordinal) + 14);
int index = setCookie.IndexOf(';');
if (index > 0) {
setCookie = setCookie.Substring(0, index);
}
Cookie["steamparental"] = setCookie;
Logging.LogGenericInfo("Success!", Bot.BotName);
return;
} }
Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName); Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName);

View File

@@ -44,6 +44,7 @@ namespace ArchiSteamFarm {
private static readonly uint LoginID = MsgClientLogon.ObfuscationMask; // This must be the same for all ASF bots and all ASF processes private static readonly uint LoginID = MsgClientLogon.ObfuscationMask; // This must be the same for all ASF bots and all ASF processes
private readonly string SentryFile; private readonly string SentryFile;
private readonly Timer AcceptConfirmationsTimer;
private readonly Timer SendItemsTimer; private readonly Timer SendItemsTimer;
internal readonly string BotName; internal readonly string BotName;
@@ -66,14 +67,6 @@ namespace ArchiSteamFarm {
private bool LoggedInElsewhere = false; private bool LoggedInElsewhere = false;
private string AuthCode, TwoFactorAuth; private string AuthCode, TwoFactorAuth;
internal static string GetAnyBotName() {
foreach (string botName in Bots.Keys) {
return botName;
}
return null;
}
internal static async Task RefreshCMs(uint cellID) { internal static async Task RefreshCMs(uint cellID) {
bool initialized = false; bool initialized = false;
for (byte i = 0; i < 3 && !initialized; i++) { for (byte i = 0; i < 3 && !initialized; i++) {
@@ -238,6 +231,15 @@ namespace ArchiSteamFarm {
CardsFarmer = new CardsFarmer(this); CardsFarmer = new CardsFarmer(this);
Trading = new Trading(this); Trading = new Trading(this);
if (BotConfig.AcceptConfirmationsPeriod > 0 && AcceptConfirmationsTimer == null) {
AcceptConfirmationsTimer = new Timer(
async e => await AcceptAllConfirmations().ConfigureAwait(false),
null,
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod), // Delay
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod) // Period
);
}
if (BotConfig.SendTradePeriod > 0 && SendItemsTimer == null) { if (BotConfig.SendTradePeriod > 0 && SendItemsTimer == null) {
SendItemsTimer = new Timer( SendItemsTimer = new Timer(
async e => await ResponseSendTrade().ConfigureAwait(false), async e => await ResponseSendTrade().ConfigureAwait(false),
@@ -260,10 +262,17 @@ namespace ArchiSteamFarm {
return; return;
} }
await BotDatabase.SteamGuardAccount.RefreshSessionAsync().ConfigureAwait(false); if (!await BotDatabase.SteamGuardAccount.RefreshSessionAsync().ConfigureAwait(false)) {
return;
}
Confirmation[] confirmations = await BotDatabase.SteamGuardAccount.FetchConfirmationsAsync().ConfigureAwait(false);
if (confirmations == null) {
return;
}
try { try {
foreach (Confirmation confirmation in await BotDatabase.SteamGuardAccount.FetchConfirmationsAsync().ConfigureAwait(false)) { foreach (Confirmation confirmation in confirmations) {
if (BotDatabase.SteamGuardAccount.AcceptConfirmation(confirmation)) { if (BotDatabase.SteamGuardAccount.AcceptConfirmation(confirmation)) {
Logging.LogGenericInfo("Accepting confirmation: Success!", BotName); Logging.LogGenericInfo("Accepting confirmation: Success!", BotName);
} else { } else {
@@ -315,6 +324,8 @@ namespace ArchiSteamFarm {
return Response2FA(); return Response2FA();
case "!2faoff": case "!2faoff":
return Response2FAOff(); return Response2FAOff();
case "!2faok":
return await Response2FAOK().ConfigureAwait(false);
case "!exit": case "!exit":
Program.Exit(); Program.Exit();
return null; return null;
@@ -322,7 +333,7 @@ namespace ArchiSteamFarm {
return ResponseRejoinChat(); return ResponseRejoinChat();
case "!restart": case "!restart":
Program.Restart(); Program.Restart();
return "Done"; return null;
case "!status": case "!status":
return ResponseStatus(); return ResponseStatus();
case "!statusall": case "!statusall":
@@ -341,6 +352,8 @@ namespace ArchiSteamFarm {
return Response2FA(args[1]); return Response2FA(args[1]);
case "!2faoff": case "!2faoff":
return Response2FAOff(args[1]); return Response2FAOff(args[1]);
case "!2faok":
return await Response2FAOK(args[1]).ConfigureAwait(false);
case "!addlicense": case "!addlicense":
if (args.Length > 2) { if (args.Length > 2) {
return await ResponseAddLicense(args[1], args[2]).ConfigureAwait(false); return await ResponseAddLicense(args[1], args[2]).ConfigureAwait(false);
@@ -453,7 +466,7 @@ namespace ArchiSteamFarm {
} }
await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false); await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false);
List<SteamItem> inventory = await ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false); List<Steam.Item> inventory = await ArchiWebHandler.GetMyTradableInventory().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!";
@@ -527,6 +540,28 @@ namespace ArchiSteamFarm {
return bot.Response2FAOff(); return bot.Response2FAOff();
} }
private async Task<string> Response2FAOK() {
if (BotDatabase.SteamGuardAccount == null) {
return "That bot doesn't have ASF 2FA enabled!";
}
await AcceptAllConfirmations().ConfigureAwait(false);
return "Done!";
}
private static async Task<string> Response2FAOK(string botName) {
if (string.IsNullOrEmpty(botName)) {
return null;
}
Bot bot;
if (!Bots.TryGetValue(botName, out bot)) {
return "Couldn't find any bot named " + botName + "!";
}
return await bot.Response2FAOK().ConfigureAwait(false);
}
private async Task<string> ResponseRedeem(string message, bool validate) { private async Task<string> ResponseRedeem(string message, bool validate) {
if (string.IsNullOrEmpty(message)) { if (string.IsNullOrEmpty(message)) {
return null; return null;
@@ -847,17 +882,16 @@ namespace ArchiSteamFarm {
return; return;
} }
// TODO: I really need something better if (new SteamID(steamID).IsChatAccount) {
if (steamID < 110300000000000000) {
SteamFriends.SendChatMessage(steamID, EChatEntryType.ChatMsg, message);
} else {
SteamFriends.SendChatRoomMessage(steamID, EChatEntryType.ChatMsg, message); SteamFriends.SendChatRoomMessage(steamID, EChatEntryType.ChatMsg, message);
} else {
SteamFriends.SendChatMessage(steamID, EChatEntryType.ChatMsg, message);
} }
} }
private bool LinkMobileAuthenticator() { private void LinkMobileAuthenticator() {
if (BotDatabase.SteamGuardAccount != null) { if (BotDatabase.SteamGuardAccount != null) {
return false; return;
} }
Logging.LogGenericInfo("Linking new ASF MobileAuthenticator...", BotName); Logging.LogGenericInfo("Linking new ASF MobileAuthenticator...", BotName);
@@ -870,7 +904,7 @@ namespace ArchiSteamFarm {
break; break;
default: default:
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName); Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
return false; return;
} }
} }
@@ -884,7 +918,7 @@ namespace ArchiSteamFarm {
break; break;
default: default:
Logging.LogGenericError("Unhandled situation: " + linkResult, BotName); Logging.LogGenericError("Unhandled situation: " + linkResult, BotName);
return false; return;
} }
} }
@@ -894,12 +928,11 @@ namespace ArchiSteamFarm {
if (finalizeResult != AuthenticatorLinker.FinalizeResult.Success) { if (finalizeResult != AuthenticatorLinker.FinalizeResult.Success) {
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName); Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
DelinkMobileAuthenticator(); DelinkMobileAuthenticator();
return false; return;
} }
Logging.LogGenericInfo("Successfully linked ASF as new mobile authenticator for this account!", BotName); Logging.LogGenericInfo("Successfully linked ASF as new mobile authenticator for this account!", BotName);
Program.GetUserInput(BotName, Program.EUserInputType.RevocationCode, BotDatabase.SteamGuardAccount.RevocationCode); Program.GetUserInput(BotName, Program.EUserInputType.RevocationCode, BotDatabase.SteamGuardAccount.RevocationCode);
return true;
} }
private bool DelinkMobileAuthenticator() { private bool DelinkMobileAuthenticator() {
@@ -1000,7 +1033,7 @@ namespace ArchiSteamFarm {
} }
} else if (LoggedInElsewhere) { } else if (LoggedInElsewhere) {
LoggedInElsewhere = false; LoggedInElsewhere = false;
Logging.LogGenericWarning("Account is being used elsewhere, ASF will try to resume farming in " + Program.GlobalConfig.AccountPlayingDelay + " minutes...", BotName); Logging.LogGenericInfo("Account is being used elsewhere, ASF will try to resume farming in " + Program.GlobalConfig.AccountPlayingDelay + " minutes...", BotName);
await Utilities.SleepAsync(Program.GlobalConfig.AccountPlayingDelay * 60 * 1000).ConfigureAwait(false); await Utilities.SleepAsync(Program.GlobalConfig.AccountPlayingDelay * 60 * 1000).ConfigureAwait(false);
} }
@@ -1213,12 +1246,16 @@ namespace ArchiSteamFarm {
return; return;
} }
if (BotConfig.DismissInventoryNotifications) {
await ArchiWebHandler.MarkInventory().ConfigureAwait(false);
}
if (BotConfig.SteamMasterClanID != 0) { if (BotConfig.SteamMasterClanID != 0) {
await ArchiWebHandler.JoinClan(BotConfig.SteamMasterClanID).ConfigureAwait(false); await ArchiWebHandler.JoinClan(BotConfig.SteamMasterClanID).ConfigureAwait(false);
JoinMasterChat(); JoinMasterChat();
} }
if (BotConfig.Statistics) { if (Program.GlobalConfig.Statistics) {
await ArchiWebHandler.JoinClan(ArchiSCFarmGroup).ConfigureAwait(false); await ArchiWebHandler.JoinClan(ArchiSCFarmGroup).ConfigureAwait(false);
SteamFriends.JoinChat(ArchiSCFarmGroup); SteamFriends.JoinChat(ArchiSCFarmGroup);
} }
@@ -1325,14 +1362,14 @@ namespace ArchiSteamFarm {
SteamFriends.RequestOfflineMessages(); SteamFriends.RequestOfflineMessages();
} }
private async void OnPurchaseResponse(ArchiHandler.PurchaseResponseCallback callback) { private void OnPurchaseResponse(ArchiHandler.PurchaseResponseCallback callback) {
if (callback == null) { if (callback == null) {
return; return;
} }
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 // We will restart CF module to recalculate current status and decide about new optimal approach
await CardsFarmer.RestartFarming().ConfigureAwait(false); Task.Run(async () => await CardsFarmer.RestartFarming().ConfigureAwait(false)).Forget();
} }
} }
} }

View File

@@ -87,15 +87,15 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal byte SendTradePeriod { get; private set; } = 0; internal byte SendTradePeriod { get; private set; } = 0;
[JsonProperty(Required = Required.DisallowNull)]
internal byte AcceptConfirmationsPeriod { get; private set; } = 0;
[JsonProperty] [JsonProperty]
internal string CustomGamePlayedWhileIdle { get; private set; } = null; internal string CustomGamePlayedWhileIdle { get; private set; } = null;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint>() { 0 }; internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint>() { 0 };
[JsonProperty(Required = Required.DisallowNull)]
internal bool Statistics { get; private set; } = true;
internal static BotConfig Load(string path) { internal static BotConfig Load(string path) {
if (!File.Exists(path)) { if (!File.Exists(path)) {
@@ -200,8 +200,6 @@ namespace ArchiSteamFarm {
} }
break; break;
case "Statistics": case "Statistics":
botConfig.Statistics = bool.Parse(value);
break;
case "Blacklist": case "Blacklist":
case "SteamNickname": case "SteamNickname":
break; break;

View File

@@ -27,17 +27,15 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal sealed class CardsFarmer { internal sealed class CardsFarmer {
private const byte StatusCheckSleep = 5; // In minutes, how long to wait before checking the appID again
private const ushort MaxFarmingTime = 600; // In minutes, how long ASF is allowed to farm one game in solo mode
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>(); internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
internal readonly List<uint> CurrentGamesFarming = new List<uint>(); internal readonly HashSet<uint> CurrentGamesFarming = new HashSet<uint>();
private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false); private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false);
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1); private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
@@ -49,24 +47,28 @@ namespace ArchiSteamFarm {
private bool NowFarming = false; private bool NowFarming = false;
internal CardsFarmer(Bot bot) { internal CardsFarmer(Bot bot) {
if (bot == null) {
return;
}
Bot = bot; Bot = bot;
if (Timer == null) { if (Program.GlobalConfig.IdleFarmingPeriod > 0 && Timer == null) {
Timer = new Timer( Timer = new Timer(
async e => await CheckGamesForFarming().ConfigureAwait(false), async e => await CheckGamesForFarming().ConfigureAwait(false),
null, null,
TimeSpan.FromMinutes(15), // Delay TimeSpan.FromHours(Program.GlobalConfig.IdleFarmingPeriod), // Delay
TimeSpan.FromMinutes(60) // Period TimeSpan.FromHours(Program.GlobalConfig.IdleFarmingPeriod) // Period
); );
} }
} }
internal static List<uint> GetGamesToFarmSolo(ConcurrentDictionary<uint, float> gamesToFarm) { internal static HashSet<uint> GetGamesToFarmSolo(ConcurrentDictionary<uint, float> gamesToFarm) {
if (gamesToFarm == null) { if (gamesToFarm == null) {
return null; return null;
} }
List<uint> result = new List<uint>(); HashSet<uint> result = new HashSet<uint>();
foreach (KeyValuePair<uint, float> keyValue in gamesToFarm) { foreach (KeyValuePair<uint, float> keyValue in gamesToFarm) {
if (keyValue.Value >= 2) { if (keyValue.Value >= 2) {
result.Add(keyValue.Key); result.Add(keyValue.Key);
@@ -81,11 +83,7 @@ namespace ArchiSteamFarm {
return 0; return 0;
} }
foreach (uint appID in gamesToFarm.Keys) { return gamesToFarm.Keys.FirstOrDefault();
return appID;
}
return 0;
} }
internal async Task<bool> SwitchToManualMode(bool manualMode) { internal async Task<bool> SwitchToManualMode(bool manualMode) {
@@ -112,7 +110,6 @@ namespace ArchiSteamFarm {
} }
float maxHour = 0; float maxHour = 0;
foreach (float hour in appIDs.Values) { foreach (float hour in appIDs.Values) {
if (hour > maxHour) { if (hour > maxHour) {
maxHour = hour; maxHour = hour;
@@ -170,6 +167,7 @@ namespace ArchiSteamFarm {
if (!await IsAnythingToFarm().ConfigureAwait(false)) { if (!await IsAnythingToFarm().ConfigureAwait(false)) {
Semaphore.Release(); // We have nothing to do, don't forget to release semaphore Semaphore.Release(); // We have nothing to do, don't forget to release semaphore
Logging.LogGenericInfo("We don't have anything to farm on this account!", Bot.BotName);
return; return;
} }
@@ -183,10 +181,10 @@ namespace ArchiSteamFarm {
if (Bot.BotConfig.CardDropsRestricted) { // If we have restricted card drops, we use complex algorithm if (Bot.BotConfig.CardDropsRestricted) { // If we have restricted card drops, we use complex algorithm
Logging.LogGenericInfo("Chosen farming algorithm: Complex", Bot.BotName); Logging.LogGenericInfo("Chosen farming algorithm: Complex", Bot.BotName);
while (GamesToFarm.Count > 0) { while (GamesToFarm.Count > 0) {
List<uint> gamesToFarmSolo = GetGamesToFarmSolo(GamesToFarm); HashSet<uint> gamesToFarmSolo = GetGamesToFarmSolo(GamesToFarm);
if (gamesToFarmSolo.Count > 0) { if (gamesToFarmSolo.Count > 0) {
while (gamesToFarmSolo.Count > 0) { while (gamesToFarmSolo.Count > 0) {
uint appID = gamesToFarmSolo[0]; uint appID = gamesToFarmSolo.First();
if (await FarmSolo(appID).ConfigureAwait(false)) { if (await FarmSolo(appID).ConfigureAwait(false)) {
farmedSomething = true; farmedSomething = true;
Logging.LogGenericInfo("Done farming: " + appID, Bot.BotName); Logging.LogGenericInfo("Done farming: " + appID, Bot.BotName);
@@ -199,7 +197,6 @@ namespace ArchiSteamFarm {
} }
} else { } else {
if (FarmMultiple(GamesToFarm)) { if (FarmMultiple(GamesToFarm)) {
farmedSomething = true;
Logging.LogGenericInfo("Done farming: " + string.Join(", ", GamesToFarm.Keys), Bot.BotName); Logging.LogGenericInfo("Done farming: " + string.Join(", ", GamesToFarm.Keys), Bot.BotName);
} else { } else {
NowFarming = false; NowFarming = false;
@@ -224,6 +221,14 @@ namespace ArchiSteamFarm {
CurrentGamesFarming.Clear(); CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess(); CurrentGamesFarming.TrimExcess();
NowFarming = false; NowFarming = false;
// We finished our queue for now, make sure that everything is indeed farmed before proceeding further
// Some games could be added in the meantime
if (await IsAnythingToFarm().ConfigureAwait(false)) {
Task.Run(async () => await StartFarming().ConfigureAwait(false)).Forget();
return;
}
Logging.LogGenericInfo("Farming finished!", Bot.BotName); Logging.LogGenericInfo("Farming finished!", Bot.BotName);
await Bot.OnFarmingFinished(farmedSomething).ConfigureAwait(false); await Bot.OnFarmingFinished(farmedSomething).ConfigureAwait(false);
} }
@@ -249,7 +254,7 @@ namespace ArchiSteamFarm {
private async Task<bool> IsAnythingToFarm() { private async Task<bool> IsAnythingToFarm() {
if (NowFarming) { if (NowFarming) {
return false; return true;
} }
if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) { if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) {
@@ -292,12 +297,12 @@ namespace ArchiSteamFarm {
await Task.WhenAll(tasks).ConfigureAwait(false); await Task.WhenAll(tasks).ConfigureAwait(false);
if (GamesToFarm.Count == 0) { if (GamesToFarm.Count == 0) {
return true; return false;
} }
// If we have restricted card drops, actually do check hours of all games that are left to farm // If we have restricted card drops, actually do check hours of all games that are left to farm
if (Bot.BotConfig.CardDropsRestricted) { if (Bot.BotConfig.CardDropsRestricted) {
tasks = new List<Task>(GamesToFarm.Keys.Count); tasks = new List<Task>(GamesToFarm.Count);
Logging.LogGenericInfo("Checking hours...", Bot.BotName); Logging.LogGenericInfo("Checking hours...", Bot.BotName);
foreach (uint appID in GamesToFarm.Keys) { foreach (uint appID in GamesToFarm.Keys) {
tasks.Add(Task.Run(async () => await CheckHours(appID).ConfigureAwait(false))); tasks.Add(Task.Run(async () => await CheckHours(appID).ConfigureAwait(false)));
@@ -320,7 +325,7 @@ namespace ArchiSteamFarm {
foreach (HtmlNode htmlNode in htmlNodeCollection) { foreach (HtmlNode htmlNode in htmlNodeCollection) {
string steamLink = htmlNode.GetAttributeValue("href", null); string steamLink = htmlNode.GetAttributeValue("href", null);
if (steamLink == null) { if (string.IsNullOrEmpty(steamLink)) {
continue; continue;
} }
@@ -387,7 +392,7 @@ namespace ArchiSteamFarm {
} }
private async Task CheckGamesForFarming() { private async Task CheckGamesForFarming() {
if (NowFarming || ManualMode || GamesToFarm.Count > 0 || !Bot.SteamClient.IsConnected) { if (NowFarming || ManualMode || !Bot.SteamClient.IsConnected) {
return; return;
} }
@@ -419,9 +424,9 @@ namespace ArchiSteamFarm {
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 <= MaxFarmingTime && (!keepFarming.HasValue || keepFarming.Value); farmingTime += StatusCheckSleep) { for (ushort farmingTime = 0; farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName); Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName);
if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) { if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
success = false; success = false;
break; break;
} }
@@ -443,13 +448,13 @@ namespace ArchiSteamFarm {
bool success = true; bool success = true;
while (maxHour < 2) { while (maxHour < 2) {
Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName); Logging.LogGenericInfo("Still farming: " + string.Join(", ", appIDs), Bot.BotName);
if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) { if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
success = false; success = false;
break; break;
} }
// Don't forget to update our GamesToFarm hours // Don't forget to update our GamesToFarm hours
float timePlayed = StatusCheckSleep / 60.0F; float timePlayed = Program.GlobalConfig.FarmingDelay / 60.0F;
foreach (KeyValuePair<uint, float> gameToFarm in GamesToFarm) { foreach (KeyValuePair<uint, float> gameToFarm in GamesToFarm) {
if (!appIDs.Contains(gameToFarm.Key)) { if (!appIDs.Contains(gameToFarm.Key)) {
continue; continue;

View File

@@ -45,16 +45,28 @@ namespace ArchiSteamFarm {
internal bool AutoUpdates { get; private set; } = true; internal bool AutoUpdates { get; private set; } = true;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal EUpdateChannel UpdateChannel { get; private set; } = GlobalConfig.EUpdateChannel.Stable; internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal byte HttpTimeout { get; private set; } = 30; internal byte MaxFarmingTime { get; private set; } = 10;
[JsonProperty(Required = Required.DisallowNull)]
internal byte IdleFarmingPeriod { get; private set; } = 3;
[JsonProperty(Required = Required.DisallowNull)]
internal byte FarmingDelay { get; private set; } = 5;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal byte AccountPlayingDelay { get; private set; } = 5; internal byte AccountPlayingDelay { get; private set; } = 5;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal byte RequestLimiterDelay { get; private set; } = 7; internal byte LoginLimiterDelay { get; private set; } = 7;
[JsonProperty(Required = Required.DisallowNull)]
internal byte InventoryLimiterDelay { get; private set; } = 3;
[JsonProperty(Required = Required.DisallowNull)]
internal byte HttpTimeout { get; private set; } = 30;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal string WCFHostname { get; private set; } = "localhost"; internal string WCFHostname { get; private set; } = "localhost";
@@ -62,6 +74,9 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal ushort WCFPort { get; private set; } = 1242; internal ushort WCFPort { get; private set; } = 1242;
[JsonProperty(Required = Required.DisallowNull)]
internal bool Statistics { get; private set; } = true;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint>(GlobalBlacklist); internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint>(GlobalBlacklist);

View File

@@ -23,7 +23,6 @@
*/ */
using Newtonsoft.Json; using Newtonsoft.Json;
using SteamAuth;
using System; using System;
using System.IO; using System.IO;

View File

@@ -0,0 +1,122 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Ł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 Newtonsoft.Json;
using SteamKit2;
using System.Collections.Generic;
namespace ArchiSteamFarm {
internal static class Steam {
internal sealed class Item {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
[JsonProperty(Required = Required.DisallowNull)]
internal string appid { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
internal string contextid { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
internal string assetid { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
internal string id {
get { return assetid; }
set { assetid = value; }
}
[JsonProperty(Required = Required.AllowNull)]
internal string classid { get; set; }
[JsonProperty(Required = Required.AllowNull)]
internal string instanceid { get; set; }
[JsonProperty(Required = Required.Always)]
internal string amount { get; set; }
}
internal sealed class ItemList {
[JsonProperty(Required = Required.Always)]
internal List<Steam.Item> assets { get; } = new List<Steam.Item>();
}
internal sealed class TradeOffer {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
internal enum ETradeOfferState : byte {
Unknown,
Invalid,
Active,
Accepted,
Countered,
Expired,
Canceled,
Declined,
InvalidItems,
EmailPending,
EmailCanceled,
OnHold
}
[JsonProperty(Required = Required.Always)]
internal string tradeofferid { get; set; }
[JsonProperty(Required = Required.Always)]
internal int accountid_other { get; set; }
[JsonProperty(Required = Required.Always)]
internal ETradeOfferState trade_offer_state { get; set; }
[JsonProperty(Required = Required.Always)]
internal List<Steam.Item> items_to_give { get; } = new List<Steam.Item>();
[JsonProperty(Required = Required.Always)]
internal List<Steam.Item> items_to_receive { get; } = new List<Steam.Item>();
// Extra
private ulong _OtherSteamID64 = 0;
internal ulong OtherSteamID64 {
get {
if (_OtherSteamID64 == 0 && accountid_other != 0) {
_OtherSteamID64 = new SteamID((uint) accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64();
}
return _OtherSteamID64;
}
}
}
internal sealed class TradeOfferRequest {
[JsonProperty(Required = Required.Always)]
internal bool newversion { get; } = true;
[JsonProperty(Required = Required.Always)]
internal int version { get; } = 2;
[JsonProperty(Required = Required.Always)]
internal Steam.ItemList me { get; } = new Steam.ItemList();
[JsonProperty(Required = Required.Always)]
internal Steam.ItemList them { get; } = new Steam.ItemList();
}
}
}

View File

@@ -27,6 +27,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -110,7 +111,7 @@ namespace ArchiSteamFarm {
return; return;
} }
GitHub.ReleaseResponse releaseResponse = null; GitHub.ReleaseResponse releaseResponse;
if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) { if (GlobalConfig.UpdateChannel == GlobalConfig.EUpdateChannel.Stable) {
try { try {
releaseResponse = JsonConvert.DeserializeObject<GitHub.ReleaseResponse>(response); releaseResponse = JsonConvert.DeserializeObject<GitHub.ReleaseResponse>(response);
@@ -142,8 +143,7 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Local version: " + Version + " | Remote version: " + releaseResponse.Tag); Logging.LogGenericInfo("Local version: " + Version + " | Remote version: " + releaseResponse.Tag);
if (Version.CompareTo(releaseResponse.Tag) >= 0) { // If local version is the same or newer than remote version if (string.Compare(Version, releaseResponse.Tag, StringComparison.Ordinal) >= 0) { // If local version is the same or newer than remote version
// Set up a timer that will automatically update ASF on as-needed basis
if (GlobalConfig.AutoUpdates && AutoUpdatesTimer == null) { if (GlobalConfig.AutoUpdates && AutoUpdatesTimer == null) {
Logging.LogGenericInfo("ASF will automatically check for new versions every 24 hours"); Logging.LogGenericInfo("ASF will automatically check for new versions every 24 hours");
AutoUpdatesTimer = new Timer( AutoUpdatesTimer = new Timer(
@@ -256,8 +256,7 @@ namespace ArchiSteamFarm {
internal static bool Restart() { internal static bool Restart() {
try { try {
// TODO: This probably won't work on Mono, I wonder if I can make it work at some point if (Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs().Skip(1))) != null) {
if (Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs())) != null) {
Exit(); Exit();
return true; return true;
} else { } else {
@@ -272,7 +271,7 @@ namespace ArchiSteamFarm {
internal static async Task LimitSteamRequestsAsync() { internal static async Task LimitSteamRequestsAsync() {
await SteamSemaphore.WaitAsync().ConfigureAwait(false); await SteamSemaphore.WaitAsync().ConfigureAwait(false);
Task.Run(async () => { Task.Run(async () => {
await Utilities.SleepAsync(GlobalConfig.RequestLimiterDelay * 1000).ConfigureAwait(false); await Utilities.SleepAsync(GlobalConfig.LoginLimiterDelay * 1000).ConfigureAwait(false);
SteamSemaphore.Release(); SteamSemaphore.Release();
}).Forget(); }).Forget();
} }
@@ -314,7 +313,7 @@ namespace ArchiSteamFarm {
ConsoleIsBusy = false; ConsoleIsBusy = false;
} }
return result.Trim(); // Get rid of all whitespace characters return string.IsNullOrEmpty(result) ? null : result.Trim();
} }
internal static void OnBotShutdown() { internal static void OnBotShutdown() {

View File

@@ -32,5 +32,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.0.2")] [assembly: AssemblyVersion("2.0.0.4")]
[assembly: AssemblyFileVersion("2.0.0.2")] [assembly: AssemblyFileVersion("2.0.0.4")]

View File

@@ -1,55 +0,0 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Ł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 Newtonsoft.Json;
namespace ArchiSteamFarm {
internal sealed class SteamItem {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
[JsonProperty(Required = Required.DisallowNull)]
internal string appid { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
internal string contextid { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
internal string assetid { get; set; }
[JsonProperty(Required = Required.DisallowNull)]
internal string id {
get { return assetid; }
set { assetid = value; }
}
[JsonProperty(Required = Required.AllowNull)]
internal string classid { get; set; }
[JsonProperty(Required = Required.AllowNull)]
internal string instanceid { get; set; }
[JsonProperty(Required = Required.Always)]
internal string amount { get; set; }
}
}

View File

@@ -1,33 +0,0 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Ł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 Newtonsoft.Json;
using System.Collections.Generic;
namespace ArchiSteamFarm {
internal sealed class SteamItemList {
[JsonProperty(Required = Required.Always)]
internal List<SteamItem> assets { get; } = new List<SteamItem>();
}
}

View File

@@ -1,74 +0,0 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Ł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 Newtonsoft.Json;
using SteamKit2;
using System.Collections.Generic;
namespace ArchiSteamFarm {
internal sealed class SteamTradeOffer {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
internal enum ETradeOfferState : byte {
Unknown,
Invalid,
Active,
Accepted,
Countered,
Expired,
Canceled,
Declined,
InvalidItems,
EmailPending,
EmailCanceled,
OnHold
}
[JsonProperty(Required = Required.Always)]
internal string tradeofferid { get; set; }
[JsonProperty(Required = Required.Always)]
internal int accountid_other { get; set; }
[JsonProperty(Required = Required.Always)]
internal ETradeOfferState trade_offer_state { get; set; }
[JsonProperty(Required = Required.Always)]
internal List<SteamItem> items_to_give { get; } = new List<SteamItem>();
[JsonProperty(Required = Required.Always)]
internal List<SteamItem> items_to_receive { get; } = new List<SteamItem>();
// Extra
private ulong _OtherSteamID64 = 0;
internal ulong OtherSteamID64 {
get {
if (_OtherSteamID64 == 0 && accountid_other != 0) {
_OtherSteamID64 = new SteamID((uint) accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64();
}
return _OtherSteamID64;
}
}
}
}

View File

@@ -1,41 +0,0 @@
/*
_ _ _ ____ _ _____
/ \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
/ _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
/ ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
Copyright 2015-2016 Ł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 Newtonsoft.Json;
namespace ArchiSteamFarm {
internal sealed class SteamTradeOfferRequest {
[JsonProperty(Required = Required.Always)]
internal bool newversion { get; } = true;
[JsonProperty(Required = Required.Always)]
internal int version { get; } = 2;
[JsonProperty(Required = Required.Always)]
internal SteamItemList me { get; } = new SteamItemList();
[JsonProperty(Required = Required.Always)]
internal SteamItemList them { get; } = new SteamItemList();
}
}

View File

@@ -40,36 +40,42 @@ namespace ArchiSteamFarm {
internal static async Task LimitInventoryRequestsAsync() { internal static async Task LimitInventoryRequestsAsync() {
await InventorySemaphore.WaitAsync().ConfigureAwait(false); await InventorySemaphore.WaitAsync().ConfigureAwait(false);
Task.Run(async () => { Task.Run(async () => {
await Utilities.SleepAsync(Program.GlobalConfig.RequestLimiterDelay * 1000).ConfigureAwait(false); await Utilities.SleepAsync(Program.GlobalConfig.InventoryLimiterDelay * 1000).ConfigureAwait(false);
InventorySemaphore.Release(); InventorySemaphore.Release();
}).Forget(); }).Forget();
} }
internal Trading(Bot bot) { internal Trading(Bot bot) {
if (bot == null) {
return;
}
Bot = bot; Bot = bot;
} }
internal async void CheckTrades() { internal async void CheckTrades() {
if (ParsingTasks < 2) { if (ParsingTasks >= 2) {
ParsingTasks++; return;
await Semaphore.WaitAsync().ConfigureAwait(false);
await ParseActiveTrades().ConfigureAwait(false);
Semaphore.Release();
ParsingTasks--;
} }
ParsingTasks++;
await Semaphore.WaitAsync().ConfigureAwait(false);
await ParseActiveTrades().ConfigureAwait(false);
Semaphore.Release();
ParsingTasks--;
} }
private async Task ParseActiveTrades() { private async Task ParseActiveTrades() {
List<SteamTradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers(); List<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
if (tradeOffers == null) { if (tradeOffers == null) {
return; return;
} }
List<Task> tasks = new List<Task>(); List<Task> tasks = new List<Task>();
foreach (SteamTradeOffer tradeOffer in tradeOffers) { foreach (Steam.TradeOffer tradeOffer in tradeOffers) {
if (tradeOffer.trade_offer_state == SteamTradeOffer.ETradeOfferState.Active) { if (tradeOffer.trade_offer_state == Steam.TradeOffer.ETradeOfferState.Active) {
tasks.Add(Task.Run(async () => await ParseTrade(tradeOffer).ConfigureAwait(false))); tasks.Add(Task.Run(async () => await ParseTrade(tradeOffer).ConfigureAwait(false)));
} }
} }
@@ -78,7 +84,7 @@ namespace ArchiSteamFarm {
await Bot.AcceptAllConfirmations().ConfigureAwait(false); await Bot.AcceptAllConfirmations().ConfigureAwait(false);
} }
private async Task ParseTrade(SteamTradeOffer tradeOffer) { private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
if (tradeOffer == null) { if (tradeOffer == null) {
return; return;
} }

View File

@@ -23,6 +23,7 @@
*/ */
using System; using System;
using System.Linq;
using System.ServiceModel; using System.ServiceModel;
using System.ServiceModel.Channels; using System.ServiceModel.Channels;
@@ -100,7 +101,7 @@ namespace ArchiSteamFarm {
if (args.Length > 1) { // If we have args[1] provided, use given botName if (args.Length > 1) { // If we have args[1] provided, use given botName
botName = args[1]; botName = args[1];
} else { // If not, just pick first one } else { // If not, just pick first one
botName = Bot.GetAnyBotName(); botName = Bot.Bots.Keys.FirstOrDefault();
} }
if (string.IsNullOrEmpty(botName)) { if (string.IsNullOrEmpty(botName)) {

View File

@@ -2,11 +2,16 @@
"Debug": false, "Debug": false,
"AutoUpdates": true, "AutoUpdates": true,
"UpdateChannel": 1, "UpdateChannel": 1,
"HttpTimeout": 30, "MaxFarmingTime": 10,
"IdleFarmingPeriod": 3,
"FarmingDelay": 5,
"AccountPlayingDelay": 5, "AccountPlayingDelay": 5,
"RequestLimiterDelay": 7, "LoginLimiterDelay": 7,
"InventoryLimiterDelay": 3,
"HttpTimeout": 30,
"WCFHostname": "localhost", "WCFHostname": "localhost",
"WCFPort": 1242, "WCFPort": 1242,
"Statistics": true,
"Blacklist": [ "Blacklist": [
267420, 267420,
303700, 303700,

View File

@@ -18,9 +18,9 @@
"SendOnFarmingFinished": false, "SendOnFarmingFinished": false,
"SteamTradeToken": null, "SteamTradeToken": null,
"SendTradePeriod": 0, "SendTradePeriod": 0,
"AcceptConfirmationsPeriod": 0,
"CustomGamePlayedWhileIdle": null, "CustomGamePlayedWhileIdle": null,
"GamesPlayedWhileIdle": [ "GamesPlayedWhileIdle": [
0 0
], ]
"Statistics": true
} }

View File

@@ -8,8 +8,29 @@ namespace SteamAuth
{ {
public class Confirmation public class Confirmation
{ {
public string ConfirmationID; public string ID;
public string ConfirmationKey; public string Key;
public string ConfirmationDescription; public string Description;
public ConfirmationType ConfType
{
get
{
if (String.IsNullOrEmpty(Description)) return ConfirmationType.Unknown;
if (Description.StartsWith("Confirm ")) return ConfirmationType.GenericConfirmation;
if (Description.StartsWith("Trade with ")) return ConfirmationType.Trade;
if (Description.StartsWith("Sell -")) return ConfirmationType.MarketSellTransaction;
return ConfirmationType.Unknown;
}
}
public enum ConfirmationType
{
GenericConfirmation,
Trade,
MarketSellTransaction,
Unknown
}
} }
} }

View File

@@ -71,7 +71,7 @@ namespace SteamAuth
if (removeResponse == null || removeResponse.Response == null || !removeResponse.Response.Success) return false; if (removeResponse == null || removeResponse.Response == null || !removeResponse.Response.Success) return false;
return true; return true;
} }
catch (Exception e) catch (Exception)
{ {
return false; return false;
} }
@@ -115,7 +115,7 @@ namespace SteamAuth
codePoint /= steamGuardCodeTranslations.Length; codePoint /= steamGuardCodeTranslations.Length;
} }
} }
catch (Exception e) catch (Exception)
{ {
return null; //Change later, catch-alls are bad! return null; //Change later, catch-alls are bad!
} }
@@ -162,9 +162,9 @@ namespace SteamAuth
string confDesc = confDescs[i].Groups[1].Value; string confDesc = confDescs[i].Groups[1].Value;
Confirmation conf = new Confirmation() Confirmation conf = new Confirmation()
{ {
ConfirmationDescription = confDesc, Description = confDesc,
ConfirmationID = confID, ID = confID,
ConfirmationKey = confKey Key = confKey
}; };
ret.Add(conf); ret.Add(conf);
} }
@@ -212,9 +212,9 @@ namespace SteamAuth
string confDesc = confDescs[i].Groups[1].Value; string confDesc = confDescs[i].Groups[1].Value;
Confirmation conf = new Confirmation() Confirmation conf = new Confirmation()
{ {
ConfirmationDescription = confDesc, Description = confDesc,
ConfirmationID = confID, ID = confID,
ConfirmationKey = confKey Key = confKey
}; };
ret.Add(conf); ret.Add(conf);
} }
@@ -268,7 +268,7 @@ namespace SteamAuth
this.Session.SteamLoginSecure = tokenSecure; this.Session.SteamLoginSecure = tokenSecure;
return true; return true;
} }
catch (Exception e) catch (Exception)
{ {
return false; return false;
} }
@@ -300,7 +300,7 @@ namespace SteamAuth
this.Session.SteamLoginSecure = tokenSecure; this.Session.SteamLoginSecure = tokenSecure;
return true; return true;
} }
catch (Exception e) catch (Exception)
{ {
return false; return false;
} }
@@ -308,7 +308,7 @@ namespace SteamAuth
private ConfirmationDetailsResponse _getConfirmationDetails(Confirmation conf) private ConfirmationDetailsResponse _getConfirmationDetails(Confirmation conf)
{ {
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/details/" + conf.ConfirmationID + "?"; string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/details/" + conf.ID + "?";
string queryString = GenerateConfirmationQueryParams("details"); string queryString = GenerateConfirmationQueryParams("details");
url += queryString; url += queryString;
@@ -329,7 +329,7 @@ namespace SteamAuth
string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/ajaxop"; string url = APIEndpoints.COMMUNITY_BASE + "/mobileconf/ajaxop";
string queryString = "?op=" + op + "&"; string queryString = "?op=" + op + "&";
queryString += GenerateConfirmationQueryParams(op); queryString += GenerateConfirmationQueryParams(op);
queryString += "&cid=" + conf.ConfirmationID + "&ck=" + conf.ConfirmationKey; queryString += "&cid=" + conf.ID + "&ck=" + conf.Key;
url += queryString; url += queryString;
CookieContainer cookies = new CookieContainer(); CookieContainer cookies = new CookieContainer();
@@ -401,7 +401,7 @@ namespace SteamAuth
string hash = WebUtility.UrlEncode(encodedData); string hash = WebUtility.UrlEncode(encodedData);
return hash; return hash;
} }
catch (Exception e) catch (Exception)
{ {
return null; //Fix soon: catch-all is BAD! return null; //Fix soon: catch-all is BAD!
} }

View File

@@ -161,8 +161,6 @@ namespace SteamAuth
this.LoggedIn = true; this.LoggedIn = true;
return LoginResult.LoginOkay; return LoginResult.LoginOkay;
} }
return LoginResult.GeneralFailure;
} }
private class LoginResponse private class LoginResponse