Compare commits

...

38 Commits

Author SHA1 Message Date
JustArchi
7587ff024c Add more debugging for SK2 2016-03-13 21:22:52 +01:00
JustArchi
06778c9bb0 Misc 2016-03-13 20:56:23 +01:00
JustArchi
7a97045412 Add ForceHttp 2016-03-13 20:19:52 +01:00
JustArchi
5d9bedfd28 Misc 2016-03-13 18:16:43 +01:00
JustArchi
575992c25d Increase HttpTimeout to 60 2016-03-13 17:03:15 +01:00
JustArchi
72db5bc9f3 We don't need that delay anymore 2016-03-12 17:03:53 +01:00
JustArchi
b015720a3e Add workaround for Volvo fuckup, #135 2016-03-12 17:02:02 +01:00
JustArchi
84a6d4501b Bump 2016-03-12 06:39:08 +01:00
JustArchi
07049e71c0 Misc 2016-03-12 06:01:55 +01:00
JustArchi
4710d9c1eb Add support for WinAuth, #144 2016-03-12 05:58:51 +01:00
JustArchi
d08462745a I swear no more bumps tonight 2016-03-11 21:42:16 +01:00
JustArchi
eb4e9ee077 Fix remaining crash 2016-03-11 21:40:25 +01:00
JustArchi
601a486b13 Bump 2016-03-11 19:58:03 +01:00
JustArchi
14867f470d Derp 2016-03-11 19:49:52 +01:00
JustArchi
187f0800b2 Add !update 2016-03-11 19:48:14 +01:00
JustArchi
3afa202d0b Misc 2016-03-11 19:42:15 +01:00
JustArchi
d33e76c8b0 Add !farm, closes #151 2016-03-11 19:39:25 +01:00
JustArchi
3061c55eaf Bump 2016-03-11 19:15:56 +01:00
JustArchi
621a1dc2cb Do not crash on bad configs 2016-03-11 18:49:29 +01:00
JustArchi
1a832780a2 Bump 2016-03-11 18:35:42 +01:00
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
25 changed files with 570 additions and 414 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

@@ -34,6 +34,10 @@ using System.Threading.Tasks;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal sealed class ArchiWebHandler { internal sealed class ArchiWebHandler {
private const string SteamCommunity = "steamcommunity.com";
private static string SteamCommunityURL = "https://" + SteamCommunity;
private static int Timeout = 30 * 1000; private static int Timeout = 30 * 1000;
private readonly Bot Bot; private readonly Bot Bot;
@@ -43,9 +47,14 @@ namespace ArchiSteamFarm {
internal static void Init() { internal static void Init() {
Timeout = Program.GlobalConfig.HttpTimeout * 1000; Timeout = Program.GlobalConfig.HttpTimeout * 1000;
SteamCommunityURL = (Program.GlobalConfig.ForceHttp ? "http://" : "https://") + SteamCommunity;
} }
internal ArchiWebHandler(Bot bot) { internal ArchiWebHandler(Bot bot) {
if (bot == null) {
return;
}
Bot = bot; Bot = bot;
} }
@@ -62,7 +71,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);
} }
@@ -87,7 +96,7 @@ namespace ArchiSteamFarm {
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)), sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)), encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
method: WebRequestMethods.Http.Post, method: WebRequestMethods.Http.Post,
secure: true secure: !Program.GlobalConfig.ForceHttp
); );
} catch (Exception e) { } catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName); Logging.LogGenericException(e, Bot.BotName);
@@ -122,7 +131,7 @@ namespace ArchiSteamFarm {
HtmlDocument htmlDocument = null; HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) { for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument("https://steamcommunity.com/my/profile", Cookie).ConfigureAwait(false); htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile", Cookie).ConfigureAwait(false);
} }
if (htmlDocument == null) { if (htmlDocument == null) {
@@ -145,7 +154,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;
} }
@@ -159,7 +168,7 @@ namespace ArchiSteamFarm {
response = iEconService.GetTradeOffers( response = iEconService.GetTradeOffers(
get_received_offers: 1, get_received_offers: 1,
active_only: 1, active_only: 1,
secure: true secure: !Program.GlobalConfig.ForceHttp
); );
} catch (Exception e) { } catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName); Logging.LogGenericException(e, Bot.BotName);
@@ -172,15 +181,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 = (uint) trade["accountid_other"].AsUnsignedLong(), // TODO: Correct this when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released
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 +199,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(),
@@ -215,7 +224,7 @@ namespace ArchiSteamFarm {
return false; return false;
} }
string request = "https://steamcommunity.com/gid/" + clanID; string request = SteamCommunityURL + "/gid/" + clanID;
Dictionary<string, string> data = new Dictionary<string, string>(2) { Dictionary<string, string> data = new Dictionary<string, string>(2) {
{"sessionID", sessionID}, {"sessionID", sessionID},
@@ -245,7 +254,7 @@ namespace ArchiSteamFarm {
return false; return false;
} }
string referer = "https://steamcommunity.com/tradeoffer/" + tradeID; string referer = SteamCommunityURL + "/tradeoffer/" + tradeID;
string request = referer + "/accept"; string request = referer + "/accept";
Dictionary<string, string> data = new Dictionary<string, string>(3) { Dictionary<string, string> data = new Dictionary<string, string>(3) {
@@ -281,7 +290,7 @@ namespace ArchiSteamFarm {
response = iEconService.DeclineTradeOffer( response = iEconService.DeclineTradeOffer(
tradeofferid: tradeID.ToString(), tradeofferid: tradeID.ToString(),
method: WebRequestMethods.Http.Post, method: WebRequestMethods.Http.Post,
secure: true secure: !Program.GlobalConfig.ForceHttp
); );
} catch (Exception e) { } catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName); Logging.LogGenericException(e, Bot.BotName);
@@ -297,10 +306,10 @@ 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(SteamCommunityURL + "/my/inventory/json/753/6?trading=1", Cookie).ConfigureAwait(false);
} }
if (jObject == null) { if (jObject == null) {
@@ -314,10 +323,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 +335,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 +345,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,
@@ -358,17 +367,17 @@ namespace ArchiSteamFarm {
}); });
} }
string referer = "https://steamcommunity.com/tradeoffer/new"; string referer = SteamCommunityURL + "/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;
@@ -392,7 +401,7 @@ namespace ArchiSteamFarm {
HtmlDocument htmlDocument = null; HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) { for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument("https://steamcommunity.com/profiles/" + SteamID + "/badges?l=english&p=" + page, Cookie).ConfigureAwait(false); htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/badges?l=english&p=" + page, Cookie).ConfigureAwait(false);
} }
if (htmlDocument == null) { if (htmlDocument == null) {
@@ -410,7 +419,7 @@ namespace ArchiSteamFarm {
HtmlDocument htmlDocument = null; HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) { for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument("https://steamcommunity.com/profiles/" + SteamID + "/gamecards/" + appID + "?l=english", Cookie).ConfigureAwait(false); htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/gamecards/" + appID + "?l=english", Cookie).ConfigureAwait(false);
} }
if (htmlDocument == null) { if (htmlDocument == null) {
@@ -428,7 +437,7 @@ namespace ArchiSteamFarm {
HttpResponseMessage response = null; HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) { for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlGet("https://steamcommunity.com/profiles/" + SteamID + "/inventory", Cookie).ConfigureAwait(false); response = await WebBrowser.UrlGet(SteamCommunityURL + "/profiles/" + SteamID + "/inventory", Cookie).ConfigureAwait(false);
} }
if (response == null) { if (response == null) {
@@ -449,9 +458,12 @@ namespace ArchiSteamFarm {
{ "pin", parentalPin } { "pin", parentalPin }
}; };
string referer = SteamCommunityURL;
string request = referer + "/parental/ajaxunlock";
HttpResponseMessage response = null; HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) { for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlPost("https://steamcommunity.com/parental/ajaxunlock", data, Cookie, "https://steamcommunity.com/").ConfigureAwait(false); response = await WebBrowser.UrlPost(request, data, Cookie, referer).ConfigureAwait(false);
} }
if (response == null) { if (response == null) {
@@ -466,14 +478,21 @@ 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(';')); }
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; Cookie["steamparental"] = setCookie;
Logging.LogGenericInfo("Success!", Bot.BotName); Logging.LogGenericInfo("Success!", Bot.BotName);
return; 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++) {
@@ -179,26 +172,16 @@ namespace ArchiSteamFarm {
SentryFile = botPath + ".bin"; SentryFile = botPath + ".bin";
// Support and convert SDA files // Support and convert SDA files
if (BotDatabase.SteamGuardAccount == null && File.Exists(botPath + ".maFile")) { string maFilePath = botPath + ".maFile";
Logging.LogGenericInfo("Converting SDA .maFile into ASF format...", botName); if (BotDatabase.SteamGuardAccount == null && File.Exists(maFilePath)) {
try { ImportAuthenticator(maFilePath);
BotDatabase.SteamGuardAccount = JsonConvert.DeserializeObject<SteamGuardAccount>(File.ReadAllText(botPath + ".maFile"));
File.Delete(botPath + ".maFile");
Logging.LogGenericInfo("Success!", botName);
} catch (Exception e) {
Logging.LogGenericException(e, botName);
}
} }
// Initialize // Initialize
SteamClient = new SteamClient(); SteamClient = new SteamClient();
if (Program.GlobalConfig.Debug && !Debugging.NetHookAlreadyInitialized) { if (Program.GlobalConfig.Debug && !Debugging.NetHookAlreadyInitialized && Directory.Exists(Program.DebugDirectory)) {
try { try {
if (Directory.Exists(Program.DebugDirectory)) {
Directory.Delete(Program.DebugDirectory, true);
}
Directory.CreateDirectory(Program.DebugDirectory);
SteamClient.DebugNetworkListener = new NetHookNetworkListener(Program.DebugDirectory); SteamClient.DebugNetworkListener = new NetHookNetworkListener(Program.DebugDirectory);
Debugging.NetHookAlreadyInitialized = true; Debugging.NetHookAlreadyInitialized = true;
} catch (Exception e) { } catch (Exception e) {
@@ -238,6 +221,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 +252,17 @@ namespace ArchiSteamFarm {
return; return;
} }
await BotDatabase.SteamGuardAccount.RefreshSessionAsync().ConfigureAwait(false);
try { try {
foreach (Confirmation confirmation in await BotDatabase.SteamGuardAccount.FetchConfirmationsAsync().ConfigureAwait(false)) { if (!await BotDatabase.SteamGuardAccount.RefreshSessionAsync().ConfigureAwait(false)) {
return;
}
Confirmation[] confirmations = await BotDatabase.SteamGuardAccount.FetchConfirmationsAsync().ConfigureAwait(false);
if (confirmations == null) {
return;
}
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 {
@@ -274,6 +273,9 @@ namespace ArchiSteamFarm {
Logging.LogGenericWarning("Accepting confirmation: Failed!", BotName); Logging.LogGenericWarning("Accepting confirmation: Failed!", BotName);
Logging.LogGenericWarning("Confirmation could not be accepted because of invalid token exception", BotName); Logging.LogGenericWarning("Confirmation could not be accepted because of invalid token exception", BotName);
Logging.LogGenericWarning("If issue persists, consider removing and readding ASF 2FA", BotName); Logging.LogGenericWarning("If issue persists, consider removing and readding ASF 2FA", BotName);
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
return;
} }
} }
@@ -286,8 +288,7 @@ namespace ArchiSteamFarm {
} }
internal async Task Restart() { internal async Task Restart() {
Stop(); await Stop().ConfigureAwait(false);
await Utilities.SleepAsync(500).ConfigureAwait(false);
await Start().ConfigureAwait(false); await Start().ConfigureAwait(false);
} }
@@ -296,7 +297,7 @@ namespace ArchiSteamFarm {
await ResponseSendTrade().ConfigureAwait(false); await ResponseSendTrade().ConfigureAwait(false);
} }
if (BotConfig.ShutdownOnFarmingFinished) { if (BotConfig.ShutdownOnFarmingFinished) {
Shutdown(); await Shutdown().ConfigureAwait(false);
} }
} }
@@ -315,22 +316,29 @@ 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;
case "!farm":
return await ResponseFarm().ConfigureAwait(false);
case "!loot":
return await ResponseSendTrade().ConfigureAwait(false);
case "!rejoinchat": case "!rejoinchat":
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":
return ResponseStatusAll(); return ResponseStatusAll();
case "!stop": case "!stop":
return ResponseStop(); return await ResponseStop().ConfigureAwait(false);
case "!loot": case "!update":
return await ResponseSendTrade().ConfigureAwait(false); await Program.CheckForUpdate().ConfigureAwait(false);
return "Done!";
default: default:
return "Unrecognized command: " + message; return "Unrecognized command: " + message;
} }
@@ -341,12 +349,18 @@ 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);
} else { } else {
return await ResponseAddLicense(BotName, args[1]).ConfigureAwait(false); return await ResponseAddLicense(BotName, args[1]).ConfigureAwait(false);
} }
case "!farm":
return await ResponseFarm(args[1]).ConfigureAwait(false);
case "!loot":
return await ResponseSendTrade(args[1]).ConfigureAwait(false);
case "!play": case "!play":
if (args.Length > 2) { if (args.Length > 2) {
return await ResponsePlay(args[1], args[2]).ConfigureAwait(false); return await ResponsePlay(args[1], args[2]).ConfigureAwait(false);
@@ -361,12 +375,10 @@ namespace ArchiSteamFarm {
} }
case "!start": case "!start":
return await ResponseStart(args[1]).ConfigureAwait(false); return await ResponseStart(args[1]).ConfigureAwait(false);
case "!stop":
return ResponseStop(args[1]);
case "!status": case "!status":
return ResponseStatus(args[1]); return ResponseStatus(args[1]);
case "!loot": case "!stop":
return await ResponseSendTrade(args[1]).ConfigureAwait(false); return await ResponseStop(args[1]).ConfigureAwait(false);
default: default:
return "Unrecognized command: " + args[0]; return "Unrecognized command: " + args[0];
} }
@@ -393,22 +405,87 @@ namespace ArchiSteamFarm {
SteamClient.Connect(); SteamClient.Connect();
} }
private void Stop() { private async Task Stop() {
if (!SteamClient.IsConnected) { if (!SteamClient.IsConnected) {
return; return;
} }
Logging.LogGenericInfo("Stopping...", BotName); Logging.LogGenericInfo("Stopping...", BotName);
for (byte i = 0; i < WebBrowser.MaxRetries && SteamClient.IsConnected; i++) {
SteamClient.Disconnect(); SteamClient.Disconnect();
await Utilities.SleepAsync(1000).ConfigureAwait(false);
} }
private void Shutdown() { if (SteamClient.IsConnected) {
Logging.LogGenericWarning("Could not stop this bot instance!", BotName);
} else {
Logging.LogGenericInfo("Stopped!", BotName);
}
}
private async Task Shutdown() {
KeepRunning = false; KeepRunning = false;
Stop(); await Stop().ConfigureAwait(false);
Program.OnBotShutdown(); Program.OnBotShutdown();
} }
private void ImportAuthenticator(string maFilePath) {
if (BotDatabase.SteamGuardAccount != null || !File.Exists(maFilePath)) {
return;
}
Logging.LogGenericInfo("Converting SDA .maFile into ASF format...", BotName);
try {
BotDatabase.SteamGuardAccount = JsonConvert.DeserializeObject<SteamGuardAccount>(File.ReadAllText(maFilePath));
File.Delete(maFilePath);
Logging.LogGenericInfo("Success!", BotName);
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
return;
}
// If this is SDA file, then we should already have everything ready
if (BotDatabase.SteamGuardAccount.Session != null) {
Logging.LogGenericInfo("Successfully finished importing mobile authenticator!", BotName);
return;
}
// But here we're dealing with WinAuth authenticator
Logging.LogGenericInfo("ASF requires a few more steps to complete authenticator import...", BotName);
InitializeLoginAndPassword();
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
LoginResult loginResult;
while ((loginResult = userLogin.DoLogin()) != LoginResult.LoginOkay) {
switch (loginResult) {
case LoginResult.Need2FA:
userLogin.TwoFactorCode = Program.GetUserInput(BotName, Program.EUserInputType.TwoFactorAuthentication);
break;
default:
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
return;
}
}
if (userLogin.Session == null) {
BotDatabase.SteamGuardAccount = null;
Logging.LogGenericError("Session is invalid, linking can't be completed!", BotName);
return;
}
BotDatabase.SteamGuardAccount.FullyEnrolled = true;
BotDatabase.SteamGuardAccount.Session = userLogin.Session;
if (string.IsNullOrEmpty(BotDatabase.SteamGuardAccount.DeviceID)) {
BotDatabase.SteamGuardAccount.DeviceID = Program.GetUserInput(BotName, Program.EUserInputType.DeviceID);
}
BotDatabase.Save();
Logging.LogGenericInfo("Successfully finished importing mobile authenticator!", BotName);
}
private string ResponseStatus() { private string ResponseStatus() {
if (CardsFarmer.CurrentGamesFarming.Count > 0) { if (CardsFarmer.CurrentGamesFarming.Count > 0) {
return "Bot " + BotName + " is currently farming appIDs: " + string.Join(", ", CardsFarmer.CurrentGamesFarming) + " and has a total of " + CardsFarmer.GamesToFarm.Count + " games left to farm."; return "Bot " + BotName + " is currently farming appIDs: " + string.Join(", ", CardsFarmer.CurrentGamesFarming) + " and has a total of " + CardsFarmer.GamesToFarm.Count + " games left to farm.";
@@ -453,7 +530,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 +604,46 @@ 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> ResponseFarm() {
await CardsFarmer.RestartFarming().ConfigureAwait(false);
return "Done!";
}
private static async Task<string> ResponseFarm(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.ResponseFarm().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;
@@ -805,16 +922,16 @@ namespace ArchiSteamFarm {
return await bot.ResponseStart().ConfigureAwait(false); return await bot.ResponseStart().ConfigureAwait(false);
} }
private string ResponseStop() { private async Task<string> ResponseStop() {
if (!KeepRunning) { if (!KeepRunning) {
return "That bot instance is already inactive!"; return "That bot instance is already inactive!";
} }
Shutdown(); await Shutdown().ConfigureAwait(false);
return "Done!"; return "Done!";
} }
private static string ResponseStop(string botName) { private static async Task<string> ResponseStop(string botName) {
if (string.IsNullOrEmpty(botName)) { if (string.IsNullOrEmpty(botName)) {
return null; return null;
} }
@@ -824,7 +941,7 @@ namespace ArchiSteamFarm {
return "Couldn't find any bot named " + botName + "!"; return "Couldn't find any bot named " + botName + "!";
} }
return bot.ResponseStop(); return await bot.ResponseStop().ConfigureAwait(false);
} }
private void HandleCallbacks() { private void HandleCallbacks() {
@@ -847,20 +964,22 @@ 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);
InitializeLoginAndPassword();
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword); UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
LoginResult loginResult; LoginResult loginResult;
while ((loginResult = userLogin.DoLogin()) != LoginResult.LoginOkay) { while ((loginResult = userLogin.DoLogin()) != LoginResult.LoginOkay) {
@@ -870,7 +989,7 @@ namespace ArchiSteamFarm {
break; break;
default: default:
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName); Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
return false; return;
} }
} }
@@ -884,7 +1003,7 @@ namespace ArchiSteamFarm {
break; break;
default: default:
Logging.LogGenericError("Unhandled situation: " + linkResult, BotName); Logging.LogGenericError("Unhandled situation: " + linkResult, BotName);
return false; return;
} }
} }
@@ -894,12 +1013,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() {
@@ -924,6 +1042,16 @@ namespace ArchiSteamFarm {
SteamFriends.JoinChat(BotConfig.SteamMasterClanID); SteamFriends.JoinChat(BotConfig.SteamMasterClanID);
} }
private void InitializeLoginAndPassword() {
if (string.IsNullOrEmpty(BotConfig.SteamLogin)) {
BotConfig.SteamLogin = Program.GetUserInput(BotName, Program.EUserInputType.Login);
}
if (string.IsNullOrEmpty(BotConfig.SteamPassword) && string.IsNullOrEmpty(BotDatabase.LoginKey)) {
BotConfig.SteamPassword = Program.GetUserInput(BotName, Program.EUserInputType.Password);
}
}
private void OnConnected(SteamClient.ConnectedCallback callback) { private void OnConnected(SteamClient.ConnectedCallback callback) {
if (callback == null) { if (callback == null) {
return; return;
@@ -952,13 +1080,7 @@ namespace ArchiSteamFarm {
} }
} }
if (string.IsNullOrEmpty(BotConfig.SteamLogin)) { InitializeLoginAndPassword();
BotConfig.SteamLogin = Program.GetUserInput(BotName, Program.EUserInputType.Login);
}
if (string.IsNullOrEmpty(BotConfig.SteamPassword) && string.IsNullOrEmpty(BotDatabase.LoginKey)) {
BotConfig.SteamPassword = Program.GetUserInput(BotName, Program.EUserInputType.Password);
}
SteamUser.LogOn(new SteamUser.LogOnDetails { SteamUser.LogOn(new SteamUser.LogOnDetails {
Username = BotConfig.SteamLogin, Username = BotConfig.SteamLogin,
@@ -1000,7 +1122,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);
} }
@@ -1184,14 +1306,7 @@ namespace ArchiSteamFarm {
// Support and convert SDA files // Support and convert SDA files
string maFilePath = Path.Combine(Program.ConfigDirectory, callback.ClientSteamID.ConvertToUInt64() + ".maFile"); string maFilePath = Path.Combine(Program.ConfigDirectory, callback.ClientSteamID.ConvertToUInt64() + ".maFile");
if (BotDatabase.SteamGuardAccount == null && File.Exists(maFilePath)) { if (BotDatabase.SteamGuardAccount == null && File.Exists(maFilePath)) {
Logging.LogGenericInfo("Converting SDA .maFile into ASF format...", BotName); ImportAuthenticator(maFilePath);
try {
BotDatabase.SteamGuardAccount = JsonConvert.DeserializeObject<SteamGuardAccount>(File.ReadAllText(maFilePath));
File.Delete(maFilePath);
Logging.LogGenericInfo("Success!", BotName);
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
}
} }
if (BotConfig.UseAsfAsMobileAuthenticator && TwoFactorAuth == null && BotDatabase.SteamGuardAccount == null) { if (BotConfig.UseAsfAsMobileAuthenticator && TwoFactorAuth == null && BotDatabase.SteamGuardAccount == null) {
@@ -1213,12 +1328,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);
} }
@@ -1235,7 +1354,7 @@ namespace ArchiSteamFarm {
break; break;
default: // Unexpected result, shutdown immediately default: // Unexpected result, shutdown immediately
Logging.LogGenericWarning("Unable to login to Steam: " + callback.Result, BotName); Logging.LogGenericWarning("Unable to login to Steam: " + callback.Result, BotName);
Shutdown(); await Shutdown().ConfigureAwait(false);
break; break;
} }
} }
@@ -1325,14 +1444,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

@@ -91,7 +91,7 @@ namespace ArchiSteamFarm {
// This constructor is used only by deserializer // This constructor is used only by deserializer
private BotDatabase() { } private BotDatabase() { }
private void Save() { internal void Save() {
lock (FilePath) { lock (FilePath) {
try { try {
File.WriteAllText(FilePath, JsonConvert.SerializeObject(this)); File.WriteAllText(FilePath, JsonConvert.SerializeObject(this));

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,14 +392,14 @@ 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;
} }
await StartFarming().ConfigureAwait(false); await StartFarming().ConfigureAwait(false);
} }
private async Task<bool?> ShouldFarm(ulong appID) { private async Task<bool?> ShouldFarm(uint appID) {
if (appID == 0) { if (appID == 0) {
return false; return false;
} }
@@ -419,13 +424,13 @@ 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); if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) {
success = false; success = false;
break; break;
} }
keepFarming = await ShouldFarm(appID).ConfigureAwait(false); keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
Logging.LogGenericInfo("Still farming: " + appID, Bot.BotName);
} }
Bot.ResetGamesPlayed(); Bot.ResetGamesPlayed();
@@ -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

@@ -22,6 +22,10 @@
*/ */
using SteamKit2;
using System;
using System.IO;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal static class Debugging { internal static class Debugging {
#if DEBUG #if DEBUG
@@ -33,5 +37,27 @@ namespace ArchiSteamFarm {
internal static bool IsReleaseBuild => !IsDebugBuild; internal static bool IsReleaseBuild => !IsDebugBuild;
internal static bool NetHookAlreadyInitialized { get; set; } = false; internal static bool NetHookAlreadyInitialized { get; set; } = false;
internal sealed class DebugListener : IDebugListener {
private readonly string FilePath;
internal DebugListener(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
return;
}
FilePath = filePath;
}
public void WriteLine(string category, string msg) {
lock (FilePath) {
try {
File.AppendAllText(FilePath, category + " | " + msg + Environment.NewLine);
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}
}
} }
} }

View File

@@ -45,16 +45,31 @@ 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 bool ForceHttp { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal byte HttpTimeout { get; private set; } = 60;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal string WCFHostname { get; private set; } = "localhost"; internal string WCFHostname { get; private set; } = "localhost";
@@ -62,6 +77,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 uint 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(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

@@ -71,9 +71,8 @@ namespace ArchiSteamFarm {
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message); Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
Log("[!] StackTrace: " + exception.StackTrace); Log("[!] StackTrace: " + exception.StackTrace);
Exception innerException = exception.InnerException; if (exception.InnerException != null) {
if (innerException != null) { LogGenericException(exception.InnerException, botName, previousMethodName);
LogGenericException(innerException, botName, previousMethodName);
} }
} }
@@ -119,7 +118,9 @@ namespace ArchiSteamFarm {
// Write on console only when not awaiting response from user // Write on console only when not awaiting response from user
if (!Program.ConsoleIsBusy) { if (!Program.ConsoleIsBusy) {
try {
Console.Write(loggedMessage); Console.Write(loggedMessage);
} catch { }
} }
if (LogToFile.GetValueOrDefault()) { if (LogToFile.GetValueOrDefault()) {
@@ -129,7 +130,6 @@ namespace ArchiSteamFarm {
} catch (Exception e) { } catch (Exception e) {
LogToFile = false; LogToFile = false;
LogGenericException(e); LogGenericException(e);
LogToFile = true;
} }
} }
} }

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;
@@ -34,6 +35,8 @@ using System.Threading.Tasks;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal static class Program { internal static class Program {
internal enum EUserInputType : byte { internal enum EUserInputType : byte {
Unknown,
DeviceID,
Login, Login,
Password, Password,
PhoneNumber, PhoneNumber,
@@ -45,12 +48,13 @@ namespace ArchiSteamFarm {
} }
internal enum EMode : byte { internal enum EMode : byte {
Unknown,
Normal, // Standard most common usage Normal, // Standard most common usage
Client, // WCF client only Client, // WCF client only
Server // Normal + WCF server Server // Normal + WCF server
} }
private const string GithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases"; private const string GithubReleaseURL = "https://api.github.com/repos/JustArchi/ArchiSteamFarm/releases"; // GitHub API is HTTPS only
internal const string ASF = "ASF"; internal const string ASF = "ASF";
internal const string ConfigDirectory = "config"; internal const string ConfigDirectory = "config";
@@ -77,7 +81,7 @@ namespace ArchiSteamFarm {
private static Timer AutoUpdatesTimer; private static Timer AutoUpdatesTimer;
private static EMode Mode = EMode.Normal; private static EMode Mode = EMode.Normal;
private static async Task CheckForUpdate() { internal static async Task CheckForUpdate() {
string oldExeFile = ExecutableFile + ".old"; string oldExeFile = ExecutableFile + ".old";
// We booted successfully so we can now remove old exe file // We booted successfully so we can now remove old exe file
@@ -110,7 +114,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 +146,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 +259,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,16 +274,23 @@ 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();
} }
internal static string GetUserInput(string botLogin, EUserInputType userInputType, string extraInformation = null) { internal static string GetUserInput(string botLogin, EUserInputType userInputType, string extraInformation = null) {
if (userInputType == EUserInputType.Unknown) {
return null;
}
string result; string result;
lock (ConsoleLock) { lock (ConsoleLock) {
ConsoleIsBusy = true; ConsoleIsBusy = true;
switch (userInputType) { switch (userInputType) {
case EUserInputType.DeviceID:
Console.Write("<" + botLogin + "> Please enter your Device ID (including \"android:\"): ");
break;
case EUserInputType.Login: case EUserInputType.Login:
Console.Write("<" + botLogin + "> Please enter your login: "); Console.Write("<" + botLogin + "> Please enter your login: ");
break; break;
@@ -308,13 +317,16 @@ namespace ArchiSteamFarm {
case EUserInputType.TwoFactorAuthentication: case EUserInputType.TwoFactorAuthentication:
Console.Write("<" + botLogin + "> Please enter your 2 factor auth code from your authenticator app: "); Console.Write("<" + botLogin + "> Please enter your 2 factor auth code from your authenticator app: ");
break; break;
default:
Console.Write("<" + botLogin + "> Please enter not documented yet value of \"" + userInputType + "\": ");
break;
} }
result = Console.ReadLine(); result = Console.ReadLine();
Console.Clear(); // For security purposes Console.Clear(); // For security purposes
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() {
@@ -335,7 +347,7 @@ namespace ArchiSteamFarm {
private static void InitServices() { private static void InitServices() {
GlobalConfig = GlobalConfig.Load(); GlobalConfig = GlobalConfig.Load();
if (GlobalConfig == null) { if (GlobalConfig == null) {
Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.db exists and is valid!"); Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.json exists and is valid!");
Thread.Sleep(5000); Thread.Sleep(5000);
Exit(1); Exit(1);
} }
@@ -426,6 +438,18 @@ namespace ArchiSteamFarm {
} }
} }
// If debugging is on, we prepare debug directory prior to running
if (GlobalConfig.Debug) {
if (Directory.Exists(Program.DebugDirectory)) {
Directory.Delete(Program.DebugDirectory, true);
Thread.Sleep(1000); // Dirty workaround giving Windows some time to sync
}
Directory.CreateDirectory(Program.DebugDirectory);
SteamKit2.DebugLog.AddListener(new Debugging.DebugListener(Path.Combine(Program.DebugDirectory, "debug.txt")));
SteamKit2.DebugLog.Enabled = true;
}
// Parse args // Parse args
ParseArgs(args); ParseArgs(args);
@@ -455,7 +479,7 @@ namespace ArchiSteamFarm {
} }
Bot bot = new Bot(botName); Bot bot = new Bot(botName);
if (!bot.BotConfig.Enabled) { if (bot.BotConfig == null || !bot.BotConfig.Enabled) {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName); Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
} }
} }
@@ -465,7 +489,7 @@ namespace ArchiSteamFarm {
string botName = Path.GetFileNameWithoutExtension(configFile); string botName = Path.GetFileNameWithoutExtension(configFile);
Logging.LogGenericWarning("Found legacy " + botName + ".xml config file, it will now be converted to new ASF V2.0 format!"); Logging.LogGenericWarning("Found legacy " + botName + ".xml config file, it will now be converted to new ASF V2.0 format!");
Bot bot = new Bot(botName); Bot bot = new Bot(botName);
if (!bot.BotConfig.Enabled) { if (bot.BotConfig == null || !bot.BotConfig.Enabled) {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName); Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
} }
} }

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.9")]
[assembly: AssemblyFileVersion("2.0.0.2")] [assembly: AssemblyFileVersion("2.0.0.9")]

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,17 +40,24 @@ 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) {
return;
}
ParsingTasks++; ParsingTasks++;
await Semaphore.WaitAsync().ConfigureAwait(false); await Semaphore.WaitAsync().ConfigureAwait(false);
@@ -59,17 +66,16 @@ namespace ArchiSteamFarm {
ParsingTasks--; 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,17 @@
"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,
"ForceHttp": false,
"HttpTimeout": 60,
"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