Big code review

This commit is contained in:
JustArchi
2016-02-22 18:34:45 +01:00
parent a06af3402a
commit af61ad66c6
12 changed files with 507 additions and 703 deletions

View File

@@ -26,6 +26,7 @@ using SteamKit2;
using SteamKit2.Internal;
using System.Collections.Generic;
using System.IO;
using System.Net;
namespace ArchiSteamFarm {
internal sealed class ArchiHandler : ClientMsgHandler {
@@ -39,7 +40,7 @@ namespace ArchiSteamFarm {
*/
internal sealed class NotificationsCallback : CallbackMsg {
internal class Notification {
internal sealed class Notification {
internal enum ENotificationType {
Unknown = 0,
Trading = 1,
@@ -48,7 +49,7 @@ namespace ArchiSteamFarm {
internal ENotificationType NotificationType { get; set; }
}
internal List<Notification> Notifications { get; private set; }
internal readonly List<Notification> Notifications;
internal NotificationsCallback(JobID jobID, CMsgClientUserNotifications msg) {
JobID = jobID;
@@ -67,8 +68,8 @@ namespace ArchiSteamFarm {
}
internal sealed class OfflineMessageCallback : CallbackMsg {
internal uint OfflineMessages { get; private set; }
internal List<uint> Users { get; private set; }
internal readonly uint OfflineMessages;
internal readonly List<uint> Users;
internal OfflineMessageCallback(JobID jobID, CMsgClientOfflineMessageNotification msg) {
JobID = jobID;
@@ -94,10 +95,10 @@ namespace ArchiSteamFarm {
OnCooldown = 53
}
internal EResult Result { get; private set; }
internal EPurchaseResult PurchaseResult { get; private set; }
internal KeyValue ReceiptInfo { get; private set; }
internal Dictionary<uint, string> Items { get; private set; }
internal readonly EResult Result;
internal readonly EPurchaseResult PurchaseResult;
internal readonly KeyValue ReceiptInfo;
internal readonly Dictionary<uint, string> Items;
internal PurchaseResponseCallback(JobID jobID, CMsgClientPurchaseResponse msg) {
JobID = jobID;
@@ -106,21 +107,22 @@ namespace ArchiSteamFarm {
return;
}
ReceiptInfo = new KeyValue();
Items = new Dictionary<uint, string>();
Result = (EResult) msg.eresult;
PurchaseResult = (EPurchaseResult) msg.purchase_result_details;
ReceiptInfo = new KeyValue();
using (MemoryStream ms = new MemoryStream(msg.purchase_receipt_info)) {
if (!ReceiptInfo.TryReadAsBinary(ms)) {
return;
}
foreach (KeyValue lineItem in ReceiptInfo["lineitems"].Children) {
List<KeyValue> lineItems = ReceiptInfo["lineitems"].Children;
Items = new Dictionary<uint, string>(lineItems.Count);
foreach (KeyValue lineItem in lineItems) {
uint appID = (uint) lineItem["PackageID"].AsUnsignedLong();
string gameName = lineItem["ItemDescription"].AsString();
gameName = Utilities.UrlDecode(gameName); // Apparently steam expects client to decode sent HTML
gameName = WebUtility.UrlDecode(gameName); // Apparently steam expects client to decode sent HTML
Items.Add(appID, gameName);
}
}
@@ -180,7 +182,7 @@ namespace ArchiSteamFarm {
}
internal void PlayGames(ICollection<uint> gameIDs) {
if (!Client.IsConnected) {
if (gameIDs == null || gameIDs.Count == 0 || !Client.IsConnected) {
return;
}

View File

@@ -27,7 +27,6 @@ using HtmlAgilityPack;
using SteamKit2;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net;
using System.Net.Http;
using System.Text;
@@ -39,60 +38,9 @@ namespace ArchiSteamFarm {
private readonly Bot Bot;
private readonly string ApiKey;
private readonly Dictionary<string, string> Cookie = new Dictionary<string, string>();
private readonly Dictionary<string, string> Cookie = new Dictionary<string, string>(3);
private ulong SteamID;
private string VanityURL;
// This is required because home_process request must be done on final URL
private string GetHomeProcess() {
if (!string.IsNullOrEmpty(VanityURL)) {
return "https://steamcommunity.com/id/" + VanityURL + "/home_process";
} else if (SteamID != 0) {
return "https://steamcommunity.com/profiles/" + SteamID + "/home_process";
} else {
return null;
}
}
private async Task UnlockParentalAccount(string parentalPin) {
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
return;
}
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
Dictionary<string, string> data = new Dictionary<string, string>() {
{ "pin", parentalPin }
};
HttpResponseMessage response = null;
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);
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return;
}
IEnumerable<string> setCookieValues;
if (!response.Headers.TryGetValues("Set-Cookie", out setCookieValues)) {
Logging.LogNullError("setCookieValues", Bot.BotName);
return;
}
foreach (string setCookieValue in setCookieValues) {
if (setCookieValue.Contains("steamparental=")) {
string setCookie = setCookieValue.Substring(setCookieValue.IndexOf("steamparental=") + 14);
setCookie = setCookie.Substring(0, setCookie.IndexOf(';'));
Cookie["steamparental"] = setCookie;
Logging.LogGenericInfo("Success!", Bot.BotName);
return;
}
}
Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName);
}
internal ArchiWebHandler(Bot bot, string apiKey) {
Bot = bot;
@@ -102,13 +50,12 @@ namespace ArchiSteamFarm {
}
}
internal async Task<bool> Init(SteamClient steamClient, string webAPIUserNonce, string vanityURL, string parentalPin) {
internal async Task<bool> Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) {
return false;
}
SteamID = steamClient.SteamID;
VanityURL = vanityURL;
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString()));
@@ -161,7 +108,6 @@ namespace ArchiSteamFarm {
Cookie["sessionid"] = sessionID;
Cookie["steamLogin"] = steamLogin;
Cookie["steamLoginSecure"] = steamLoginSecure;
Cookie["birthtime"] = "-473356799";
await UnlockParentalAccount(parentalPin).ConfigureAwait(false);
return true;
@@ -229,28 +175,16 @@ namespace ArchiSteamFarm {
SteamTradeOffer tradeOffer = new SteamTradeOffer {
tradeofferid = trade["tradeofferid"].AsString(),
accountid_other = trade["accountid_other"].AsInteger(),
message = trade["message"].AsString(),
expiration_time = trade["expiration_time"].AsInteger(),
trade_offer_state = trade["trade_offer_state"].AsEnum<SteamTradeOffer.ETradeOfferState>(),
items_to_give = new List<SteamItem>(),
items_to_receive = new List<SteamItem>(),
is_our_offer = trade["is_our_offer"].AsBoolean(),
time_created = trade["time_created"].AsInteger(),
time_updated = trade["time_updated"].AsInteger(),
from_real_time_trade = trade["from_real_time_trade"].AsBoolean(),
escrow_end_date = trade["escrow_end_date"].AsInteger(),
confirmation_method = trade["confirmation_method"].AsEnum<SteamTradeOffer.ETradeOfferConfirmationMethod>()
trade_offer_state = trade["trade_offer_state"].AsEnum<SteamTradeOffer.ETradeOfferState>()
};
foreach (KeyValue item in trade["items_to_give"].Children) {
tradeOffer.items_to_give.Add(new SteamItem {
appid = item["appid"].AsString(),
contextid = item["contextid"].AsString(),
assetid = item["assetid"].AsString(),
currencyid = item["currencyid"].AsString(),
classid = item["classid"].AsString(),
instanceid = item["instanceid"].AsString(),
amount = item["amount"].AsString(),
missing = item["missing"].AsBoolean()
});
}
foreach (KeyValue item in trade["items_to_receive"].Children) {
@@ -258,11 +192,9 @@ namespace ArchiSteamFarm {
appid = item["appid"].AsString(),
contextid = item["contextid"].AsString(),
assetid = item["assetid"].AsString(),
currencyid = item["currencyid"].AsString(),
classid = item["classid"].AsString(),
instanceid = item["instanceid"].AsString(),
amount = item["amount"].AsString(),
missing = item["missing"].AsBoolean()
});
}
result.Add(tradeOffer);
@@ -283,7 +215,7 @@ namespace ArchiSteamFarm {
string request = "https://steamcommunity.com/gid/" + clanID;
Dictionary<string, string> data = new Dictionary<string, string>() {
Dictionary<string, string> data = new Dictionary<string, string>(2) {
{"sessionID", sessionID},
{"action", "join"}
};
@@ -301,36 +233,6 @@ namespace ArchiSteamFarm {
return true;
}
internal async Task<bool> LeaveClan(ulong clanID) {
if (clanID == 0) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
}
string request = GetHomeProcess();
Dictionary<string, string> data = new Dictionary<string, string>() {
{"sessionID", sessionID},
{"action", "leaveGroup"},
{"groupId", clanID.ToString()}
};
HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlPost(request, data, Cookie).ConfigureAwait(false);
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
return true;
}
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
if (tradeID == 0) {
return false;
@@ -344,7 +246,7 @@ namespace ArchiSteamFarm {
string referer = "https://steamcommunity.com/tradeoffer/" + tradeID;
string request = referer + "/accept";
Dictionary<string, string> data = new Dictionary<string, string>() {
Dictionary<string, string> data = new Dictionary<string, string>(3) {
{"sessionid", sessionID},
{"serverid", "1"},
{"tradeofferid", tradeID.ToString()}
@@ -404,11 +306,19 @@ namespace ArchiSteamFarm {
return null;
}
List<SteamItem> result = new List<SteamItem>();
IEnumerable<JToken> jTokens = jObject.SelectTokens("$.rgInventory.*");
if (jTokens == null) {
Logging.LogNullError("jTokens", Bot.BotName);
return null;
}
List<SteamItem> result = new List<SteamItem>();
foreach (JToken jToken in jTokens) {
result.Add(JsonConvert.DeserializeObject<SteamItem>(jToken.ToString()));
try {
result.Add(JsonConvert.DeserializeObject<SteamItem>(jToken.ToString()));
} catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName);
}
}
return result;
@@ -424,7 +334,7 @@ namespace ArchiSteamFarm {
return false;
}
List<SteamTradeOfferRequest> trades = new List<SteamTradeOfferRequest>();
List<SteamTradeOfferRequest> trades = new List<SteamTradeOfferRequest>(1 + inventory.Count / Trading.MaxItemsPerTrade);
SteamTradeOfferRequest singleTrade = null;
for (ushort i = 0; i < inventory.Count; i++) {
@@ -450,7 +360,7 @@ namespace ArchiSteamFarm {
string request = referer + "/send";
foreach (SteamTradeOfferRequest trade in trades) {
Dictionary<string, string> data = new Dictionary<string, string>() {
Dictionary<string, string> data = new Dictionary<string, string>(6) {
{"sessionid", sessionID},
{"serverid", "1"},
{"partner", partnerID.ToString()},
@@ -508,5 +418,44 @@ namespace ArchiSteamFarm {
return htmlDocument;
}
private async Task UnlockParentalAccount(string parentalPin) {
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
return;
}
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
Dictionary<string, string> data = new Dictionary<string, string>(1) {
{ "pin", parentalPin }
};
HttpResponseMessage response = null;
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);
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return;
}
IEnumerable<string> setCookieValues;
if (!response.Headers.TryGetValues("Set-Cookie", out setCookieValues)) {
Logging.LogNullError("setCookieValues", Bot.BotName);
return;
}
foreach (string setCookieValue in setCookieValues) {
if (setCookieValue.Contains("steamparental=")) {
string setCookie = setCookieValue.Substring(setCookieValue.IndexOf("steamparental=") + 14);
setCookie = setCookie.Substring(0, setCookie.IndexOf(';'));
Cookie["steamparental"] = setCookie;
Logging.LogGenericInfo("Success!", Bot.BotName);
return;
}
}
Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName);
}
}
}

View File

@@ -41,14 +41,19 @@ namespace ArchiSteamFarm {
private const ulong ArchiSCFarmGroup = 103582791440160998;
private const ushort CallbackSleep = 500; // In miliseconds
private static readonly uint LoginID = MsgClientLogon.ObfuscationMask; // This must be the same for all ASF bots and all ASF processes
internal static readonly ConcurrentDictionary<string, Bot> Bots = new ConcurrentDictionary<string, Bot>();
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 303700, 335590, 368020, 425280 };
private static readonly uint LoginID = MsgClientLogon.ObfuscationMask; // This must be the same for all ASF bots and all ASF processes
private readonly string ConfigFile, LoginKeyFile, MobileAuthenticatorFile, SentryFile;
private readonly Timer SendItemsTimer;
internal readonly string BotName;
internal readonly ArchiHandler ArchiHandler;
internal readonly ArchiWebHandler ArchiWebHandler;
internal readonly SteamClient SteamClient;
private readonly CallbackManager CallbackManager;
private readonly CardsFarmer CardsFarmer;
private readonly SteamApps SteamApps;
@@ -56,15 +61,6 @@ namespace ArchiSteamFarm {
private readonly SteamUser SteamUser;
private readonly Trading Trading;
internal readonly string BotName;
internal readonly ArchiHandler ArchiHandler;
internal readonly ArchiWebHandler ArchiWebHandler;
internal readonly SteamClient SteamClient;
private bool InvalidPassword = false;
private bool LoggedInElsewhere = false;
private string AuthCode, LoginKey, TwoFactorAuth;
internal bool KeepRunning { get; private set; } = false;
internal SteamGuardAccount SteamGuardAccount { get; private set; }
@@ -88,19 +84,12 @@ namespace ArchiSteamFarm {
internal bool SendOnFarmingFinished { get; private set; } = false;
internal string SteamTradeToken { get; private set; } = "null";
internal byte SendTradePeriod { get; private set; } = 0;
internal HashSet<uint> Blacklist { get; private set; } = new HashSet<uint>();
internal HashSet<uint> Blacklist { get; } = new HashSet<uint>();
internal bool Statistics { get; private set; } = true;
private static bool IsValidCdKey(string key) {
if (string.IsNullOrEmpty(key)) {
return false;
}
// Steam keys are offered in many formats: https://support.steampowered.com/kb_article.php?ref=7480-WUSF-3601
// It's pointless to implement them all, so we'll just do a simple check if key is supposed to be valid
// Every valid key, apart from Prey one has at least two dashes
return Utilities.GetCharCountInString(key, '-') >= 2;
}
private bool InvalidPassword = false;
private bool LoggedInElsewhere = false;
private string AuthCode, LoginKey, TwoFactorAuth;
internal static string GetAnyBotName() {
foreach (string botName in Bots.Keys) {
@@ -130,6 +119,17 @@ namespace ArchiSteamFarm {
}
}
private static bool IsValidCdKey(string key) {
if (string.IsNullOrEmpty(key)) {
return false;
}
// Steam keys are offered in many formats: https://support.steampowered.com/kb_article.php?ref=7480-WUSF-3601
// It's pointless to implement them all, so we'll just do a simple check if key is supposed to be valid
// Every valid key, apart from Prey one has at least two dashes
return Utilities.GetCharCountInString(key, '-') >= 2;
}
internal Bot(string botName) {
if (Bots.ContainsKey(botName)) {
return;
@@ -137,10 +137,11 @@ namespace ArchiSteamFarm {
BotName = botName;
ConfigFile = Path.Combine(Program.ConfigDirectory, botName + ".xml");
LoginKeyFile = Path.Combine(Program.ConfigDirectory, botName + ".key");
MobileAuthenticatorFile = Path.Combine(Program.ConfigDirectory, botName + ".auth");
SentryFile = Path.Combine(Program.ConfigDirectory, botName + ".bin");
string botPath = Path.Combine(Program.ConfigDirectory, botName);
ConfigFile = botPath + ".xml";
LoginKeyFile = botPath + ".key";
MobileAuthenticatorFile = botPath + ".auth";
SentryFile = botPath + ".bin";
if (!ReadConfig()) {
return;
@@ -234,181 +235,6 @@ namespace ArchiSteamFarm {
}
}
private bool LinkMobileAuthenticator() {
if (SteamGuardAccount != null) {
return false;
}
Logging.LogGenericInfo("Linking new ASF MobileAuthenticator...", BotName);
UserLogin userLogin = new UserLogin(SteamLogin, SteamPassword);
LoginResult loginResult;
while ((loginResult = userLogin.DoLogin()) != LoginResult.LoginOkay) {
switch (loginResult) {
case LoginResult.NeedEmail:
userLogin.EmailCode = Program.GetUserInput(BotName, Program.EUserInputType.SteamGuard);
break;
default:
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
return false;
}
}
AuthenticatorLinker authenticatorLinker = new AuthenticatorLinker(userLogin.Session);
AuthenticatorLinker.LinkResult linkResult;
while ((linkResult = authenticatorLinker.AddAuthenticator()) != AuthenticatorLinker.LinkResult.AwaitingFinalization) {
switch (linkResult) {
case AuthenticatorLinker.LinkResult.MustProvidePhoneNumber:
authenticatorLinker.PhoneNumber = Program.GetUserInput(BotName, Program.EUserInputType.PhoneNumber);
break;
default:
Logging.LogGenericError("Unhandled situation: " + linkResult, BotName);
return false;
}
}
SteamGuardAccount = authenticatorLinker.LinkedAccount;
try {
File.WriteAllText(MobileAuthenticatorFile, JsonConvert.SerializeObject(SteamGuardAccount));
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
return false;
}
AuthenticatorLinker.FinalizeResult finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(Program.GetUserInput(BotName, Program.EUserInputType.SMS));
if (finalizeResult != AuthenticatorLinker.FinalizeResult.Success) {
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
DelinkMobileAuthenticator();
return false;
}
Logging.LogGenericInfo("Successfully linked ASF as new mobile authenticator for this account!", BotName);
Program.GetUserInput(BotName, Program.EUserInputType.RevocationCode, SteamGuardAccount.RevocationCode);
return true;
}
private bool DelinkMobileAuthenticator() {
if (SteamGuardAccount == null) {
return false;
}
bool result = SteamGuardAccount.DeactivateAuthenticator();
SteamGuardAccount = null;
try {
File.Delete(MobileAuthenticatorFile);
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
}
return result;
}
private bool ReadConfig() {
if (!File.Exists(ConfigFile)) {
return false;
}
try {
using (XmlReader reader = XmlReader.Create(ConfigFile)) {
while (reader.Read()) {
if (reader.NodeType != XmlNodeType.Element) {
continue;
}
string key = reader.Name;
if (string.IsNullOrEmpty(key)) {
continue;
}
string value = reader.GetAttribute("value");
if (string.IsNullOrEmpty(value)) {
continue;
}
switch (key) {
case "Enabled":
Enabled = bool.Parse(value);
break;
case "SteamLogin":
SteamLogin = value;
break;
case "SteamPassword":
SteamPassword = value;
break;
case "SteamNickname":
SteamNickname = value;
break;
case "SteamApiKey":
SteamApiKey = value;
break;
case "SteamTradeToken":
SteamTradeToken = value;
break;
case "SteamParentalPIN":
SteamParentalPIN = value;
break;
case "SteamMasterID":
SteamMasterID = ulong.Parse(value);
break;
case "SteamMasterClanID":
SteamMasterClanID = ulong.Parse(value);
break;
case "StartOnLaunch":
StartOnLaunch = bool.Parse(value);
break;
case "UseAsfAsMobileAuthenticator":
UseAsfAsMobileAuthenticator = bool.Parse(value);
break;
case "CardDropsRestricted":
CardDropsRestricted = bool.Parse(value);
break;
case "FarmOffline":
FarmOffline = bool.Parse(value);
break;
case "HandleOfflineMessages":
HandleOfflineMessages = bool.Parse(value);
break;
case "ForwardKeysToOtherBots":
ForwardKeysToOtherBots = bool.Parse(value);
break;
case "DistributeKeys":
DistributeKeys = bool.Parse(value);
break;
case "ShutdownOnFarmingFinished":
ShutdownOnFarmingFinished = bool.Parse(value);
break;
case "SendOnFarmingFinished":
SendOnFarmingFinished = bool.Parse(value);
break;
case "SendTradePeriod":
SendTradePeriod = byte.Parse(value);
break;
case "Blacklist":
Blacklist.Clear();
foreach (string appID in value.Split(',')) {
Blacklist.Add(uint.Parse(appID));
}
break;
case "Statistics":
Statistics = bool.Parse(value);
break;
default:
Logging.LogGenericWarning("Unrecognized config value: " + key + "=" + value, BotName);
break;
}
}
}
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
Logging.LogGenericError("Your config for this bot instance is invalid, it won't run!", BotName);
return false;
}
return true;
}
internal async Task Restart() {
Stop();
await Utilities.SleepAsync(500).ConfigureAwait(false);
@@ -460,26 +286,6 @@ namespace ArchiSteamFarm {
}
}
private void HandleCallbacks() {
TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep);
while (KeepRunning) {
CallbackManager.RunWaitCallbacks(timeSpan);
}
}
private void SendMessage(ulong steamID, string message) {
if (steamID == 0 || string.IsNullOrEmpty(message)) {
return;
}
// TODO: I really need something better
if (steamID < 110300000000000000) {
SteamFriends.SendChatMessage(steamID, EChatEntryType.ChatMsg, message);
} else {
SteamFriends.SendChatRoomMessage(steamID, EChatEntryType.ChatMsg, message);
}
}
internal string ResponseStatus() {
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.";
@@ -966,6 +772,13 @@ namespace ArchiSteamFarm {
}
}
private void HandleCallbacks() {
TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep);
while (KeepRunning) {
CallbackManager.RunWaitCallbacks(timeSpan);
}
}
private async Task HandleMessage(ulong steamID, string message) {
if (steamID == 0 || string.IsNullOrEmpty(message)) {
return;
@@ -974,6 +787,90 @@ namespace ArchiSteamFarm {
SendMessage(steamID, await HandleMessage(message).ConfigureAwait(false));
}
private void SendMessage(ulong steamID, string message) {
if (steamID == 0 || string.IsNullOrEmpty(message)) {
return;
}
// TODO: I really need something better
if (steamID < 110300000000000000) {
SteamFriends.SendChatMessage(steamID, EChatEntryType.ChatMsg, message);
} else {
SteamFriends.SendChatRoomMessage(steamID, EChatEntryType.ChatMsg, message);
}
}
private bool LinkMobileAuthenticator() {
if (SteamGuardAccount != null) {
return false;
}
Logging.LogGenericInfo("Linking new ASF MobileAuthenticator...", BotName);
UserLogin userLogin = new UserLogin(SteamLogin, SteamPassword);
LoginResult loginResult;
while ((loginResult = userLogin.DoLogin()) != LoginResult.LoginOkay) {
switch (loginResult) {
case LoginResult.NeedEmail:
userLogin.EmailCode = Program.GetUserInput(BotName, Program.EUserInputType.SteamGuard);
break;
default:
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
return false;
}
}
AuthenticatorLinker authenticatorLinker = new AuthenticatorLinker(userLogin.Session);
AuthenticatorLinker.LinkResult linkResult;
while ((linkResult = authenticatorLinker.AddAuthenticator()) != AuthenticatorLinker.LinkResult.AwaitingFinalization) {
switch (linkResult) {
case AuthenticatorLinker.LinkResult.MustProvidePhoneNumber:
authenticatorLinker.PhoneNumber = Program.GetUserInput(BotName, Program.EUserInputType.PhoneNumber);
break;
default:
Logging.LogGenericError("Unhandled situation: " + linkResult, BotName);
return false;
}
}
SteamGuardAccount = authenticatorLinker.LinkedAccount;
try {
File.WriteAllText(MobileAuthenticatorFile, JsonConvert.SerializeObject(SteamGuardAccount));
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
return false;
}
AuthenticatorLinker.FinalizeResult finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(Program.GetUserInput(BotName, Program.EUserInputType.SMS));
if (finalizeResult != AuthenticatorLinker.FinalizeResult.Success) {
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
DelinkMobileAuthenticator();
return false;
}
Logging.LogGenericInfo("Successfully linked ASF as new mobile authenticator for this account!", BotName);
Program.GetUserInput(BotName, Program.EUserInputType.RevocationCode, SteamGuardAccount.RevocationCode);
return true;
}
private bool DelinkMobileAuthenticator() {
if (SteamGuardAccount == null) {
return false;
}
bool result = SteamGuardAccount.DeactivateAuthenticator();
SteamGuardAccount = null;
try {
File.Delete(MobileAuthenticatorFile);
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
}
return result;
}
private void OnConnected(SteamClient.ConnectedCallback callback) {
if (callback == null) {
return;
@@ -1252,7 +1149,7 @@ namespace ArchiSteamFarm {
SteamParentalPIN = Program.GetUserInput(BotName, Program.EUserInputType.SteamParentalPIN);
}
if (!await ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, callback.VanityURL, SteamParentalPIN).ConfigureAwait(false)) {
if (!await ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, SteamParentalPIN).ConfigureAwait(false)) {
await Restart().ConfigureAwait(false);
return;
}
@@ -1376,5 +1273,109 @@ namespace ArchiSteamFarm {
await CardsFarmer.RestartFarming().ConfigureAwait(false);
}
}
private bool ReadConfig() {
if (!File.Exists(ConfigFile)) {
return false;
}
try {
using (XmlReader reader = XmlReader.Create(ConfigFile)) {
while (reader.Read()) {
if (reader.NodeType != XmlNodeType.Element) {
continue;
}
string key = reader.Name;
if (string.IsNullOrEmpty(key)) {
continue;
}
string value = reader.GetAttribute("value");
if (string.IsNullOrEmpty(value)) {
continue;
}
switch (key) {
case "Enabled":
Enabled = bool.Parse(value);
break;
case "SteamLogin":
SteamLogin = value;
break;
case "SteamPassword":
SteamPassword = value;
break;
case "SteamNickname":
SteamNickname = value;
break;
case "SteamApiKey":
SteamApiKey = value;
break;
case "SteamTradeToken":
SteamTradeToken = value;
break;
case "SteamParentalPIN":
SteamParentalPIN = value;
break;
case "SteamMasterID":
SteamMasterID = ulong.Parse(value);
break;
case "SteamMasterClanID":
SteamMasterClanID = ulong.Parse(value);
break;
case "StartOnLaunch":
StartOnLaunch = bool.Parse(value);
break;
case "UseAsfAsMobileAuthenticator":
UseAsfAsMobileAuthenticator = bool.Parse(value);
break;
case "CardDropsRestricted":
CardDropsRestricted = bool.Parse(value);
break;
case "FarmOffline":
FarmOffline = bool.Parse(value);
break;
case "HandleOfflineMessages":
HandleOfflineMessages = bool.Parse(value);
break;
case "ForwardKeysToOtherBots":
ForwardKeysToOtherBots = bool.Parse(value);
break;
case "DistributeKeys":
DistributeKeys = bool.Parse(value);
break;
case "ShutdownOnFarmingFinished":
ShutdownOnFarmingFinished = bool.Parse(value);
break;
case "SendOnFarmingFinished":
SendOnFarmingFinished = bool.Parse(value);
break;
case "SendTradePeriod":
SendTradePeriod = byte.Parse(value);
break;
case "Blacklist":
Blacklist.Clear();
foreach (string appID in value.Split(',')) {
Blacklist.Add(uint.Parse(appID));
}
break;
case "Statistics":
Statistics = bool.Parse(value);
break;
default:
Logging.LogGenericWarning("Unrecognized config value: " + key + "=" + value, BotName);
break;
}
}
}
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
Logging.LogGenericError("Your config for this bot instance is invalid, it won't run!", BotName);
return false;
}
return true;
}
}
}

View File

@@ -24,12 +24,13 @@
using SteamKit2;
using SteamKit2.Internal;
using System;
using System.IO;
namespace ArchiSteamFarm {
internal sealed class CMsgClientClanInviteAction : ISteamSerializableMessage, ISteamSerializable {
internal ulong GroupID = 0;
internal bool AcceptInvite = true;
internal ulong GroupID { get; set; } = 0;
internal bool AcceptInvite { get; set; } = true;
EMsg ISteamSerializableMessage.GetEMsg() {
return EMsg.ClientAcknowledgeClanInvite;
@@ -44,8 +45,8 @@ namespace ArchiSteamFarm {
BinaryWriter binaryWriter = new BinaryWriter(stream);
binaryWriter.Write(GroupID);
binaryWriter.Write(AcceptInvite);
} catch {
throw new IOException();
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
@@ -58,8 +59,8 @@ namespace ArchiSteamFarm {
BinaryReader binaryReader = new BinaryReader(stream);
GroupID = binaryReader.ReadUInt64();
AcceptInvite = binaryReader.ReadBoolean();
} catch {
throw new IOException();
} catch (Exception e) {
Logging.LogGenericException(e);
}
}
}

View File

@@ -36,15 +36,15 @@ namespace ArchiSteamFarm {
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 List<uint> CurrentGamesFarming = new List<uint>();
private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false);
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
private readonly Bot Bot;
private readonly Timer Timer;
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
internal readonly List<uint> CurrentGamesFarming = new List<uint>();
private bool ManualMode = false;
private bool NowFarming = false;
@@ -118,6 +118,7 @@ namespace ArchiSteamFarm {
}
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
foreach (uint appID in appIDs.Keys) {
CurrentGamesFarming.Add(appID);
}
@@ -138,6 +139,7 @@ namespace ArchiSteamFarm {
}
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
CurrentGamesFarming.Add(appID);
Logging.LogGenericInfo("Now farming: " + appID, Bot.BotName);
@@ -184,6 +186,7 @@ namespace ArchiSteamFarm {
if (await FarmSolo(appID).ConfigureAwait(false)) {
Logging.LogGenericInfo("Done farming: " + appID, Bot.BotName);
gamesToFarmSolo.Remove(appID);
gamesToFarmSolo.TrimExcess();
} else {
NowFarming = false;
return;
@@ -212,6 +215,7 @@ namespace ArchiSteamFarm {
}
CurrentGamesFarming.Clear();
CurrentGamesFarming.TrimExcess();
NowFarming = false;
Logging.LogGenericInfo("Farming finished!", Bot.BotName);
await Bot.OnFarmingFinished().ConfigureAwait(false);
@@ -269,16 +273,16 @@ namespace ArchiSteamFarm {
// Find APPIDs we need to farm
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
List<Task> checkPagesTasks = new List<Task>();
List<Task> tasks = new List<Task>(maxPages - 1);
for (byte page = 1; page <= maxPages; page++) {
if (page == 1) {
CheckPage(htmlDocument); // Because we fetched page number 1 already
} else {
byte currentPage = page; // We need a copy of variable being passed when in for loops
checkPagesTasks.Add(Task.Run(async () => await CheckPage(currentPage).ConfigureAwait(false)));
tasks.Add(Task.Run(async () => await CheckPage(currentPage).ConfigureAwait(false)));
}
}
await Task.WhenAll(checkPagesTasks).ConfigureAwait(false);
await Task.WhenAll(tasks).ConfigureAwait(false);
if (GamesToFarm.Count == 0) {
return true;
@@ -286,12 +290,12 @@ namespace ArchiSteamFarm {
// If we have restricted card drops, actually do check hours of all games that are left to farm
if (Bot.CardDropsRestricted) {
List<Task> checkHoursTasks = new List<Task>();
tasks = new List<Task>(GamesToFarm.Keys.Count);
Logging.LogGenericInfo("Checking hours...", Bot.BotName);
foreach (uint appID in GamesToFarm.Keys) {
checkHoursTasks.Add(Task.Run(async () => await CheckHours(appID).ConfigureAwait(false)));
tasks.Add(Task.Run(async () => await CheckHours(appID).ConfigureAwait(false)));
}
await Task.WhenAll(checkHoursTasks).ConfigureAwait(false);
await Task.WhenAll(tasks).ConfigureAwait(false);
}
return true;

View File

@@ -34,7 +34,79 @@ namespace ArchiSteamFarm {
internal static bool LogToFile { get; set; } = false;
internal static void Init() {
File.Delete(Program.LogFile);
lock (FileLock) {
try {
File.Delete(Program.LogFile);
} catch (Exception e) {
bool logToFile = LogToFile;
LogToFile = false;
LogGenericException(e);
LogToFile = logToFile;
}
}
}
internal static void LogGenericWTF(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[!!] WTF: " + previousMethodName + "() <" + botName + "> " + message + ", WTF?");
}
internal static void LogGenericError(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (exception == null) {
return;
}
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
Log("[!] StackTrace: " + exception.StackTrace);
Exception innerException = exception.InnerException;
if (innerException != null) {
LogGenericException(innerException, botName, previousMethodName);
}
}
internal static void LogGenericWarning(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[!] WARNING: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericInfo(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[*] INFO: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogNullError(string nullObjectName, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(nullObjectName)) {
return;
}
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
}
[Conditional("DEBUG")]
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
if (string.IsNullOrEmpty(message)) {
return;
}
Log("[#] DEBUG: " + previousMethodName + "() <" + botName + "> " + message);
}
private static void Log(string message) {
@@ -51,44 +123,16 @@ namespace ArchiSteamFarm {
if (LogToFile) {
lock (FileLock) {
File.AppendAllText(Program.LogFile, loggedMessage);
try {
File.AppendAllText(Program.LogFile, loggedMessage);
} catch (Exception e) {
bool logToFile = LogToFile;
LogToFile = false;
LogGenericException(e);
LogToFile = logToFile;
}
}
}
}
internal static void LogGenericWTF(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
Log("[!!] WTF: " + previousMethodName + "() <" + botName + "> " + message + ", WTF?");
}
internal static void LogGenericError(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
Log("[!!] ERROR: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericException(Exception exception, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
Log("[!] EXCEPTION: " + previousMethodName + "() <" + botName + "> " + exception.Message);
Log("[!] StackTrace: " + exception.StackTrace);
Exception innerException = exception.InnerException;
if (innerException != null) {
LogGenericException(innerException, botName, previousMethodName);
}
}
internal static void LogGenericWarning(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
Log("[!] WARNING: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogGenericInfo(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
Log("[*] INFO: " + previousMethodName + "() <" + botName + "> " + message);
}
internal static void LogNullError(string nullObjectName, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
LogGenericError(nullObjectName + " is null!", botName, previousMethodName);
}
[Conditional("DEBUG")]
internal static void LogGenericDebug(string message, string botName = "Main", [CallerMemberName] string previousMethodName = "") {
Log("[#] DEBUG: " + previousMethodName + "() <" + botName + "> " + message);
}
}
}

View File

@@ -28,13 +28,13 @@ namespace ArchiSteamFarm {
internal sealed class SteamItem {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
[JsonProperty]
[JsonProperty(Required = Required.Always)]
internal string appid { get; set; }
[JsonProperty]
[JsonProperty(Required = Required.Always)]
internal string contextid { get; set; }
[JsonProperty]
[JsonProperty(Required = Required.Always)]
internal string assetid { get; set; }
[JsonProperty]
@@ -43,22 +43,13 @@ namespace ArchiSteamFarm {
set { assetid = value; }
}
[JsonProperty]
internal string currencyid { get; set; }
[JsonProperty]
[JsonProperty(Required = Required.Always)]
internal string classid { get; set; }
[JsonProperty]
[JsonProperty(Required = Required.Always)]
internal string instanceid { get; set; }
[JsonProperty]
[JsonProperty(Required = Required.Always)]
internal string amount { get; set; }
[JsonProperty]
internal bool missing { get; set; }
[JsonProperty]
internal int pos { get; set; }
}
}

View File

@@ -26,14 +26,8 @@ using Newtonsoft.Json;
using System.Collections.Generic;
namespace ArchiSteamFarm {
internal class SteamItemList {
[JsonProperty]
internal List<SteamItem> assets { get; set; } = new List<SteamItem>();
[JsonProperty]
internal List<string> currency { get; set; } = new List<string>();
[JsonProperty]
internal bool ready { get; set; } = false;
internal sealed class SteamItemList {
[JsonProperty(Required = Required.Always)]
internal List<SteamItem> assets { get; } = new List<SteamItem>();
}
}

View File

@@ -29,7 +29,7 @@ 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 {
internal enum ETradeOfferState : byte {
Unknown,
Invalid,
Active,
@@ -44,50 +44,20 @@ namespace ArchiSteamFarm {
OnHold
}
internal enum ETradeOfferConfirmationMethod {
Invalid,
Email,
MobileApp
}
[JsonProperty]
[JsonProperty(Required = Required.Always)]
internal string tradeofferid { get; set; }
[JsonProperty]
[JsonProperty(Required = Required.Always)]
internal int accountid_other { get; set; }
[JsonProperty]
internal string message { get; set; }
[JsonProperty]
internal int expiration_time { get; set; }
[JsonProperty]
[JsonProperty(Required = Required.Always)]
internal ETradeOfferState trade_offer_state { get; set; }
[JsonProperty]
internal List<SteamItem> items_to_give { get; set; } = new List<SteamItem>();
[JsonProperty(Required = Required.Always)]
internal List<SteamItem> items_to_give { get; } = new List<SteamItem>();
[JsonProperty]
internal List<SteamItem> items_to_receive { get; set; } = new List<SteamItem>();
[JsonProperty]
internal bool is_our_offer { get; set; }
[JsonProperty]
internal int time_created { get; set; }
[JsonProperty]
internal int time_updated { get; set; }
[JsonProperty]
internal bool from_real_time_trade { get; set; }
[JsonProperty]
internal int escrow_end_date { get; set; }
[JsonProperty]
internal ETradeOfferConfirmationMethod confirmation_method { get; set; }
[JsonProperty(Required = Required.Always)]
internal List<SteamItem> items_to_receive { get; } = new List<SteamItem>();
// Extra
private ulong _OtherSteamID64 = 0;

View File

@@ -25,17 +25,17 @@
using Newtonsoft.Json;
namespace ArchiSteamFarm {
internal class SteamTradeOfferRequest {
[JsonProperty]
internal bool newversion { get; set; } = true;
internal sealed class SteamTradeOfferRequest {
[JsonProperty(Required = Required.Always)]
internal bool newversion { get; } = true;
[JsonProperty]
internal int version { get; set; } = 2;
[JsonProperty(Required = Required.Always)]
internal int version { get; } = 2;
[JsonProperty]
internal SteamItemList me { get; set; } = new SteamItemList();
[JsonProperty(Required = Required.Always)]
internal SteamItemList me { get; } = new SteamItemList();
[JsonProperty]
internal SteamItemList them { get; set; } = new SteamItemList();
[JsonProperty(Required = Required.Always)]
internal SteamItemList them { get; } = new SteamItemList();
}
}

View File

@@ -72,13 +72,5 @@ namespace ArchiSteamFarm {
return count;
}
internal static string UrlDecode(string message) {
if (string.IsNullOrEmpty(message)) {
return null;
}
return WebUtility.UrlDecode(message);
}
}
}

View File

@@ -30,27 +30,20 @@ using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace ArchiSteamFarm {
internal static class WebBrowser {
[Flags]
internal enum RequestOptions : byte {
None = 0,
FakeUserAgent = 1 << 0,
XMLHttpRequest = 1 << 1
}
private const string FakeUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36";
internal const byte HttpTimeout = 180; // In seconds, how long we can wait for server's response
internal const byte MaxConnections = 20; // Defines maximum number of connections. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
internal const byte MaxConnections = 10; // Defines maximum number of connections per ServicePoint. Be careful, as it also defines maximum number of sockets in CLOSE_WAIT state
internal const byte MaxIdleTime = 15; // In seconds, how long socket is allowed to stay in CLOSE_WAIT state after there are no connections to it
internal const byte MaxRetries = 5; // Defines maximum number of retries, UrlRequest() does not handle retry by itself (it's app responsibility)
private static readonly string DefaultUserAgent = "ArchiSteamFarm/" + Program.Version;
private static readonly HttpClientHandler HttpClientHandler = new HttpClientHandler { UseCookies = false };
private static readonly HttpClient HttpClient = new HttpClient(HttpClientHandler) { Timeout = TimeSpan.FromSeconds(HttpTimeout) };
private static readonly HttpClient HttpClient = new HttpClient(new HttpClientHandler {
UseCookies = false
}) {
Timeout = TimeSpan.FromSeconds(HttpTimeout)
};
internal static void Init() {
// Most web services expect that UserAgent is set, so we declare it globally
@@ -65,9 +58,86 @@ namespace ArchiSteamFarm {
// Don't use Expect100Continue, we're sure about our POSTs, save some TCP packets
ServicePointManager.Expect100Continue = false;
// Reuse ports if possible
// TODO: Mono doesn't support that feature yet
//ServicePointManager.ReusePort = true;
}
private static async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
internal static async Task<HttpResponseMessage> UrlGet(string request, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
return await UrlRequest(request, HttpMethod.Get, null, cookies, referer).ConfigureAwait(false);
}
internal static async Task<HttpResponseMessage> UrlPost(string request, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
return await UrlRequest(request, HttpMethod.Post, data, cookies, referer).ConfigureAwait(false);
}
internal static async Task<string> UrlGetToContent(string request, Dictionary<string, string> cookies, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HttpResponseMessage httpResponse = await UrlGet(request, cookies, referer).ConfigureAwait(false);
if (httpResponse == null) {
return null;
}
HttpContent httpContent = httpResponse.Content;
if (httpContent == null) {
return null;
}
return await httpContent.ReadAsStringAsync().ConfigureAwait(false);
}
internal static async Task<HtmlDocument> UrlGetToHtmlDocument(string request, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
string content = await UrlGetToContent(request, cookies, referer).ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
content = WebUtility.HtmlDecode(content);
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(content);
return htmlDocument;
}
internal static async Task<JObject> UrlGetToJObject(string request, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request)) {
return null;
}
string content = await UrlGetToContent(request, cookies, referer).ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
JObject jObject;
try {
jObject = JObject.Parse(content);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
return jObject;
}
private static async Task<HttpResponseMessage> UrlRequest(string request, HttpMethod httpMethod, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null) {
if (string.IsNullOrEmpty(request) || httpMethod == null) {
return null;
}
@@ -95,14 +165,6 @@ namespace ArchiSteamFarm {
requestMessage.Headers.Referrer = new Uri(referer);
}
if (requestOptions.HasFlag(RequestOptions.FakeUserAgent)) {
requestMessage.Headers.UserAgent.ParseAdd(FakeUserAgent);
}
if (requestOptions.HasFlag(RequestOptions.XMLHttpRequest)) {
requestMessage.Headers.Add("X-Requested-With", "XMLHttpRequest");
}
try {
responseMessage = await HttpClient.SendAsync(requestMessage).ConfigureAwait(false);
} catch { // Request failed, we don't need to know the exact reason, swallow exception
@@ -116,211 +178,5 @@ namespace ArchiSteamFarm {
return responseMessage;
}
internal static async Task<HttpResponseMessage> UrlGet(string request, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
return await UrlRequest(request, HttpMethod.Get, null, cookies, referer, requestOptions).ConfigureAwait(false);
}
internal static async Task<HttpResponseMessage> UrlPost(string request, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
return await UrlRequest(request, HttpMethod.Post, data, cookies, referer, requestOptions).ConfigureAwait(false);
}
internal static async Task<HtmlDocument> HttpResponseToHtmlDocument(HttpResponseMessage httpResponse) {
if (httpResponse == null) {
return null;
}
HttpContent httpContent = httpResponse.Content;
if (httpContent == null) {
return null;
}
string content = await httpContent.ReadAsStringAsync().ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
content = WebUtility.HtmlDecode(content);
HtmlDocument htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(content);
return htmlDocument;
}
internal static async Task<string> UrlGetToContent(string request, Dictionary<string, string> cookies, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HttpResponseMessage httpResponse = await UrlGet(request, cookies, referer, requestOptions).ConfigureAwait(false);
if (httpResponse == null) {
return null;
}
HttpContent httpContent = httpResponse.Content;
if (httpContent == null) {
return null;
}
return await httpContent.ReadAsStringAsync().ConfigureAwait(false);
}
internal static async Task<string> UrlPostToContent(string request, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HttpResponseMessage httpResponse = await UrlPost(request, data, cookies, referer, requestOptions).ConfigureAwait(false);
if (httpResponse == null) {
return null;
}
HttpContent httpContent = httpResponse.Content;
if (httpContent == null) {
return null;
}
return await httpContent.ReadAsStringAsync().ConfigureAwait(false);
}
internal static async Task<JObject> UrlPostToJObject(string request, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
string content = await UrlPostToContent(request, data, cookies, referer, requestOptions).ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
JObject jObject;
try {
jObject = JObject.Parse(content);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
return jObject;
}
internal static async Task<HtmlDocument> UrlGetToHtmlDocument(string request, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HttpResponseMessage httpResponse = await UrlGet(request, cookies, referer, requestOptions).ConfigureAwait(false);
if (httpResponse == null) {
return null;
}
return await HttpResponseToHtmlDocument(httpResponse).ConfigureAwait(false);
}
internal static async Task<HtmlDocument> UrlPostToHtmlDocument(string request, Dictionary<string, string> data = null, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HttpResponseMessage httpResponse = await UrlPost(request, data, cookies, referer, requestOptions).ConfigureAwait(false);
if (httpResponse == null) {
return null;
}
return await HttpResponseToHtmlDocument(httpResponse).ConfigureAwait(false);
}
internal static async Task<string> UrlGetToTitle(string request, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
HtmlDocument htmlDocument = await UrlGetToHtmlDocument(request, cookies, referer, requestOptions).ConfigureAwait(false);
if (htmlDocument == null) {
return null;
}
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//head/title");
if (htmlNode == null) {
return null;
}
return htmlNode.InnerText;
}
internal static async Task<JArray> UrlGetToJArray(string request, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
string content = await UrlGetToContent(request, cookies, referer, requestOptions).ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
JArray jArray;
try {
jArray = JArray.Parse(content);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
return jArray;
}
internal static async Task<JObject> UrlGetToJObject(string request, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
string content = await UrlGetToContent(request, cookies, referer, requestOptions).ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
JObject jObject;
try {
jObject = JObject.Parse(content);
} catch (Exception e) {
Logging.LogGenericException(e);
return null;
}
return jObject;
}
internal static async Task<XmlDocument> UrlGetToXML(string request, Dictionary<string, string> cookies = null, string referer = null, RequestOptions requestOptions = RequestOptions.None) {
if (string.IsNullOrEmpty(request)) {
return null;
}
string content = await UrlGetToContent(request, cookies, referer, requestOptions).ConfigureAwait(false);
if (string.IsNullOrEmpty(content)) {
return null;
}
XmlDocument xmlDocument = new XmlDocument();
try {
xmlDocument.LoadXml(content);
} catch (XmlException e) {
Logging.LogGenericException(e);
return null;
}
return xmlDocument;
}
}
}