Compare commits

..

25 Commits

Author SHA1 Message Date
JustArchi
fa12ffd9d0 Add headlness mode 2016-04-06 16:37:45 +02:00
JustArchi
ecb27adedd Bump 2016-04-06 15:37:55 +02:00
JustArchi
6e9be09944 Misc 2016-04-06 15:30:03 +02:00
JustArchi
6a79a89a10 Allow offline bot to handle keys redeeming as well 2016-04-06 15:28:28 +02:00
JustArchi
d67be4f092 Fix ResponseRedeem() and make it possible to redeem multiple keys 2016-04-06 15:25:52 +02:00
JustArchi
a8e1039e32 Add !2fano 2016-04-05 03:41:59 +02:00
JustArchi
2ad9d9e197 Bump 2016-04-03 17:00:40 +02:00
JustArchi
fd6e2c72d7 Major cleanup & code review of ResponseRedeem() 2016-04-02 19:23:09 +02:00
JustArchi
1eed0f7647 Misc 2016-04-02 13:41:53 +02:00
JustArchi
c018c08260 Misc WCF enhancements 2016-04-02 13:41:08 +02:00
JustArchi
72fa98cb89 Improve exit/restart 2016-04-02 13:08:43 +02:00
JustArchi
71215d695e Bump 2016-04-02 12:33:41 +02:00
JustArchi
75b785d4b6 Misc 2016-04-01 20:19:17 +02:00
JustArchi
12e32692cb Code review 2016-04-01 20:18:21 +02:00
JustArchi
6158a9268d Misc 2016-04-01 17:14:03 +02:00
JustArchi
a48e53585a Pretty sure those are no longer needed 2016-03-30 13:10:00 +02:00
JustArchi
33383633ea Fix ultra rare crash 2016-03-30 01:41:53 +02:00
JustArchi
3f7359c608 Misc 2016-03-29 23:32:12 +02:00
JustArchi
d8b59c6889 Add Linux scripts 2016-03-29 22:58:18 +02:00
JustArchi
0f71b788cb Use /my/ steamcommunity trick 2016-03-29 14:48:31 +02:00
JustArchi
d012255a21 EXPERIMENTAL: Steam session improvements
1. Make sure that every call to steamcommunity has active session
2. Move whole userspace logic for session handling to ArchiWebHandler (and Bot)
3. Implement session caching and TTL so we won't send IsLoggedIn() on each ArchiWebHandler call
4. Instead of restarting whole steam account, just refresh the session via ArchiWebHandler instead
2016-03-29 14:33:05 +02:00
JustArchi
8fc39a44cd Misc 2016-03-28 15:34:59 +02:00
JustArchi
c6fe424fcc Refuse to handle https requests when ForceHttp is true 2016-03-28 15:34:10 +02:00
JustArchi
12488dafd3 Misc 2016-03-28 13:11:02 +02:00
JustArchi
3b53491567 Bump 2016-03-28 00:18:07 +02:00
18 changed files with 484 additions and 301 deletions

View File

@@ -257,6 +257,7 @@ namespace ArchiSteamFarm {
request.Body.key = key;
Client.Send(request);
try {
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID);
} catch (Exception e) {

View File

@@ -32,21 +32,22 @@ using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Threading;
namespace ArchiSteamFarm {
internal sealed class ArchiWebHandler {
private const string SteamCommunity = "steamcommunity.com";
private const byte MinSessionTTL = 15; // Assume session is valid for at least that amount of seconds
private static string SteamCommunityURL = "https://" + SteamCommunity;
private static int Timeout = 30 * 1000;
private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000;
private readonly Bot Bot;
private readonly Dictionary<string, string> Cookie = new Dictionary<string, string>(4);
private readonly SemaphoreSlim SessionSemaphore = new SemaphoreSlim(1);
internal bool IsInitialized { get; private set; }
private ulong SteamID;
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
internal static void Init() {
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
@@ -61,18 +62,14 @@ namespace ArchiSteamFarm {
Bot = bot;
}
internal void OnDisconnected() {
IsInitialized = false;
}
internal async Task<bool> Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce) || IsInitialized) {
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) {
return false;
}
SteamID = steamClient.SteamID;
ulong steamID = steamClient.SteamID;
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(SteamID.ToString()));
string sessionID = Convert.ToBase64String(Encoding.UTF8.GetBytes(steamID.ToString()));
// Generate an AES session key
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
@@ -99,7 +96,7 @@ namespace ArchiSteamFarm {
try {
authResult = iSteamUserAuth.AuthenticateUser(
steamid: SteamID,
steamid: steamID,
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
method: WebRequestMethods.Http.Post,
@@ -127,17 +124,15 @@ namespace ArchiSteamFarm {
// The below is used for display purposes only
Cookie["webTradeEligibility"] = "{\"allowed\":0,\"reason\":0,\"allowed_at_time\":0,\"steamguard_required_days\":0,\"sales_this_year\":0,\"max_sales_per_year\":0,\"forms_requested\":0}";
await UnlockParentalAccount(parentalPin).ConfigureAwait(false);
if (!UnlockParentalAccount(parentalPin).Result) {
return false;
}
IsInitialized = true;
LastSessionRefreshCheck = DateTime.Now;
return true;
}
internal async Task<bool?> IsLoggedIn() {
if (SteamID == 0) {
return false;
}
HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile", Cookie).ConfigureAwait(false);
@@ -152,23 +147,42 @@ namespace ArchiSteamFarm {
return htmlNode != null;
}
internal async Task<bool> ReconnectIfNeeded() {
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
if (isLoggedIn.HasValue && !isLoggedIn.Value) {
Logging.LogGenericInfo("Reconnecting because our sessionID expired!", Bot.BotName);
Bot.RestartIfRunning().Forget();
internal async Task<bool> RefreshSessionIfNeeded() {
DateTime now = DateTime.Now;
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
return true;
}
return false;
await SessionSemaphore.WaitAsync().ConfigureAwait(false);
now = DateTime.Now;
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
SessionSemaphore.Release();
return true;
}
bool result;
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
if (isLoggedIn.GetValueOrDefault(true)) {
result = true;
now = DateTime.Now;
LastSessionRefreshCheck = now;
} else {
Logging.LogGenericInfo("Refreshing our session!", Bot.BotName);
result = await Bot.RefreshSession().ConfigureAwait(false);
}
SessionSemaphore.Release();
return result;
}
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
if (SteamID == 0) {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
}
string request = SteamCommunityURL + "/profiles/" + SteamID + "/games/?xml=1";
string request = SteamCommunityURL + "/my/games/?xml=1";
XmlDocument response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
@@ -273,6 +287,10 @@ namespace ArchiSteamFarm {
return false;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
@@ -303,6 +321,10 @@ namespace ArchiSteamFarm {
return false;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
@@ -330,37 +352,11 @@ namespace ArchiSteamFarm {
return true;
}
internal bool DeclineTradeOffer(ulong tradeID) {
if (tradeID == 0 || string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
return false;
}
KeyValue response = null;
using (dynamic iEconService = WebAPI.GetInterface("IEconService", Bot.BotConfig.SteamApiKey)) {
iEconService.Timeout = Timeout;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
try {
response = iEconService.DeclineTradeOffer(
tradeofferid: tradeID.ToString(),
method: WebRequestMethods.Http.Post,
secure: !Program.GlobalConfig.ForceHttp
);
} catch (Exception e) {
Logging.LogGenericException(e, Bot.BotName);
}
}
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
return true;
}
internal async Task<List<Steam.Item>> GetMyTradableInventory() {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
}
JObject jObject = null;
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/753/6?trading=1", Cookie).ConfigureAwait(false);
@@ -394,6 +390,10 @@ namespace ArchiSteamFarm {
return false;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
@@ -449,13 +449,17 @@ namespace ArchiSteamFarm {
}
internal async Task<HtmlDocument> GetBadgePage(byte page) {
if (page == 0 || SteamID == 0) {
if (page == 0) {
return null;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
}
HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/badges?l=english&p=" + page, Cookie).ConfigureAwait(false);
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/badges?l=english&p=" + page, Cookie).ConfigureAwait(false);
}
if (htmlDocument == null) {
@@ -467,13 +471,17 @@ namespace ArchiSteamFarm {
}
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
if (appID == 0 || SteamID == 0) {
if (appID == 0) {
return null;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null;
}
HtmlDocument htmlDocument = null;
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/profiles/" + SteamID + "/gamecards/" + appID + "?l=english", Cookie).ConfigureAwait(false);
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/gamecards/" + appID + "?l=english", Cookie).ConfigureAwait(false);
}
if (htmlDocument == null) {
@@ -485,13 +493,13 @@ namespace ArchiSteamFarm {
}
internal async Task<bool> MarkInventory() {
if (SteamID == 0) {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlGet(SteamCommunityURL + "/profiles/" + SteamID + "/inventory", Cookie).ConfigureAwait(false);
response = await WebBrowser.UrlGet(SteamCommunityURL + "/my/inventory", Cookie).ConfigureAwait(false);
}
if (response == null) {
@@ -507,6 +515,10 @@ namespace ArchiSteamFarm {
return false;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
@@ -530,9 +542,9 @@ namespace ArchiSteamFarm {
return true;
}
private async Task UnlockParentalAccount(string parentalPin) {
private async Task<bool> UnlockParentalAccount(string parentalPin) {
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
return;
return true;
}
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
@@ -550,13 +562,13 @@ namespace ArchiSteamFarm {
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return;
return false;
}
IEnumerable<string> setCookieValues;
if (!response.Headers.TryGetValues("Set-Cookie", out setCookieValues)) {
Logging.LogNullError("setCookieValues", Bot.BotName);
return;
return false;
}
foreach (string setCookieValue in setCookieValues) {
@@ -573,10 +585,11 @@ namespace ArchiSteamFarm {
Cookie["steamparental"] = setCookie;
Logging.LogGenericInfo("Success!", Bot.BotName);
return;
return true;
}
Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName);
return false;
}
}
}

View File

@@ -187,6 +187,7 @@ namespace ArchiSteamFarm {
CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn);
CallbackManager.Subscribe<SteamUser.LoginKeyCallback>(OnLoginKey);
CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth);
CallbackManager.Subscribe<SteamUser.WebAPIUserNonceCallback>(OnWebAPIUserNonce);
CallbackManager.Subscribe<ArchiHandler.NotificationsCallback>(OnNotifications);
CallbackManager.Subscribe<ArchiHandler.OfflineMessageCallback>(OnOfflineMessage);
@@ -196,16 +197,16 @@ namespace ArchiSteamFarm {
CardsFarmer = new CardsFarmer(this);
Trading = new Trading(this);
if (BotConfig.AcceptConfirmationsPeriod > 0) {
if (AcceptConfirmationsTimer == null && BotConfig.AcceptConfirmationsPeriod > 0) {
AcceptConfirmationsTimer = new Timer(
async e => await AcceptConfirmations().ConfigureAwait(false),
async e => await AcceptConfirmations(true).ConfigureAwait(false),
null,
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod), // Delay
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod) // Period
);
}
if (BotConfig.SendTradePeriod > 0) {
if (SendItemsTimer == null && BotConfig.SendTradePeriod > 0) {
SendItemsTimer = new Timer(
async e => await ResponseSendTrade(BotConfig.SteamMasterID).ConfigureAwait(false),
null,
@@ -222,7 +223,7 @@ namespace ArchiSteamFarm {
Start().Forget();
}
internal async Task AcceptConfirmations(Confirmation.ConfirmationType allowedConfirmationType = Confirmation.ConfirmationType.Unknown) {
internal async Task AcceptConfirmations(bool confirm, Confirmation.ConfirmationType allowedConfirmationType = Confirmation.ConfirmationType.Unknown) {
if (BotDatabase.SteamGuardAccount == null) {
return;
}
@@ -242,10 +243,14 @@ namespace ArchiSteamFarm {
continue;
}
BotDatabase.SteamGuardAccount.AcceptConfirmation(confirmation);
if (confirm) {
BotDatabase.SteamGuardAccount.AcceptConfirmation(confirmation);
} else {
BotDatabase.SteamGuardAccount.DenyConfirmation(confirmation);
}
}
} catch (SteamGuardAccount.WGTokenInvalidException) {
Logging.LogGenericWarning("Accepting confirmation: Failed!", BotName);
Logging.LogGenericWarning("Handling confirmation: Failed!", 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);
} catch (Exception e) {
@@ -261,12 +266,23 @@ namespace ArchiSteamFarm {
}
}
internal async Task RestartIfRunning() {
internal async Task<bool> RefreshSession() {
if (!SteamClient.IsConnected) {
return;
return false;
}
await Start().ConfigureAwait(false);
var userNonce = await SteamUser.RequestWebAPIUserNonce();
if (userNonce == null || userNonce.Result != EResult.OK || string.IsNullOrEmpty(userNonce.Nonce)) {
Start().Forget();
return false;
}
if (!ArchiWebHandler.Init(SteamClient, userNonce.Nonce, BotConfig.SteamParentalPIN)) {
Start().Forget();
return false;
}
return true;
}
internal async Task OnFarmingFinished(bool farmedSomething) {
@@ -285,17 +301,19 @@ namespace ArchiSteamFarm {
}
if (!message.StartsWith("!")) {
return await ResponseRedeem(steamID, BotName, message, true).ConfigureAwait(false);
return await ResponseRedeem(steamID, message.Replace(",", Environment.NewLine), true).ConfigureAwait(false);
}
if (!message.Contains(" ")) {
switch (message) {
case "!2fa":
return Response2FA(steamID);
case "!2fano":
return await Response2FAConfirm(steamID, false).ConfigureAwait(false);
case "!2faoff":
return Response2FAOff(steamID);
case "!2faok":
return await Response2FAOK(steamID).ConfigureAwait(false);
return await Response2FAConfirm(steamID, true).ConfigureAwait(false);
case "!exit":
return ResponseExit(steamID);
case "!farm":
@@ -326,10 +344,12 @@ namespace ArchiSteamFarm {
switch (args[0]) {
case "!2fa":
return Response2FA(steamID, args[1]);
case "!2fano":
return await Response2FAConfirm(steamID, args[1], false).ConfigureAwait(false);
case "!2faoff":
return Response2FAOff(steamID, args[1]);
case "!2faok":
return await Response2FAOK(steamID, args[1]).ConfigureAwait(false);
return await Response2FAConfirm(steamID, args[1], true).ConfigureAwait(false);
case "!addlicense":
if (args.Length > 2) {
return await ResponseAddLicense(steamID, args[1], args[2]).ConfigureAwait(false);
@@ -358,9 +378,9 @@ namespace ArchiSteamFarm {
}
case "!redeem":
if (args.Length > 2) {
return await ResponseRedeem(steamID, args[1], args[2], false).ConfigureAwait(false);
return await ResponseRedeem(steamID, args[1], args[2].Replace(",", Environment.NewLine), false).ConfigureAwait(false);
} else {
return await ResponseRedeem(steamID, BotName, args[1], false).ConfigureAwait(false);
return await ResponseRedeem(steamID, BotName, args[1].Replace(",", Environment.NewLine), false).ConfigureAwait(false);
}
case "!start":
return await ResponseStart(steamID, args[1]).ConfigureAwait(false);
@@ -432,7 +452,9 @@ namespace ArchiSteamFarm {
// But here we're dealing with WinAuth authenticator
Logging.LogGenericInfo("ASF requires a few more steps to complete authenticator import...", BotName);
InitializeLoginAndPassword();
if (!InitializeLoginAndPassword()) {
return;
}
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
LoginResult loginResult;
@@ -440,6 +462,9 @@ namespace ArchiSteamFarm {
switch (loginResult) {
case LoginResult.Need2FA:
userLogin.TwoFactorCode = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
if (string.IsNullOrEmpty(userLogin.TwoFactorCode)) {
return;
}
break;
default:
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
@@ -560,7 +585,7 @@ namespace ArchiSteamFarm {
}
if (await ArchiWebHandler.SendTradeOffer(inventory, BotConfig.SteamMasterID, BotConfig.SteamTradeToken).ConfigureAwait(false)) {
await AcceptConfirmations(Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
await AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
return "Trade offer sent successfully!";
} else {
return "Trade offer failed due to error!";
@@ -635,7 +660,7 @@ namespace ArchiSteamFarm {
return bot.Response2FAOff(steamID);
}
private async Task<string> Response2FAOK(ulong steamID) {
private async Task<string> Response2FAConfirm(ulong steamID, bool confirm) {
if (steamID == 0 || !IsMaster(steamID)) {
return null;
}
@@ -644,11 +669,11 @@ namespace ArchiSteamFarm {
return "That bot doesn't have ASF 2FA enabled!";
}
await AcceptConfirmations().ConfigureAwait(false);
await AcceptConfirmations(confirm).ConfigureAwait(false);
return "Done!";
}
private static async Task<string> Response2FAOK(ulong steamID, string botName) {
private static async Task<string> Response2FAConfirm(ulong steamID, string botName, bool confirm) {
if (steamID == 0 || string.IsNullOrEmpty(botName)) {
return null;
}
@@ -658,7 +683,7 @@ namespace ArchiSteamFarm {
return "Couldn't find any bot named " + botName + "!";
}
return await bot.Response2FAOK(steamID).ConfigureAwait(false);
return await bot.Response2FAConfirm(steamID, confirm).ConfigureAwait(false);
}
private static string ResponseExit(ulong steamID) {
@@ -670,8 +695,13 @@ namespace ArchiSteamFarm {
return null;
}
Environment.Exit(0);
return null;
// Schedule the task after some time so user can receive response
Task.Run(async () => {
await Utilities.SleepAsync(1000).ConfigureAwait(false);
Program.Exit();
}).Forget();
return "Done!";
}
private string ResponseFarm(ulong steamID) {
@@ -727,130 +757,82 @@ namespace ArchiSteamFarm {
}
StringBuilder response = new StringBuilder();
using (StringReader reader = new StringReader(message)) {
using (StringReader reader = new StringReader(message))
using (IEnumerator<Bot> iterator = Bots.Values.GetEnumerator()) {
string key = reader.ReadLine();
IEnumerator<Bot> iterator = Bots.Values.GetEnumerator();
Bot currentBot = this;
while (key != null) {
if (currentBot == null) {
break;
}
while (!string.IsNullOrEmpty(key) && currentBot != null) {
if (validate && !IsValidCdKey(key)) {
key = reader.ReadLine();
continue;
key = reader.ReadLine(); // Next key
continue; // Without changing the bot
}
ArchiHandler.PurchaseResponseCallback result;
try {
result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
} catch (Exception e) {
Logging.LogGenericException(e, currentBot.BotName);
break;
if (currentBot.SteamClient.IsConnected) {
ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
if (result != null) {
switch (result.PurchaseResult) {
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK:
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + result.PurchaseResult + " | Items: " + string.Join("", result.Items));
key = reader.ReadLine(); // Next key
break; // Next bot (if needed)
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.AlreadyOwned:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.BaseGameRequired:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OnCooldown:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.RegionLocked:
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + result.PurchaseResult + " | Items: " + string.Join("", result.Items));
if (!BotConfig.ForwardKeysToOtherBots) {
key = reader.ReadLine(); // Next key
break; // Next bot (if needed)
}
if (BotConfig.DistributeKeys) {
break; // Next bot, without changing key
}
bool alreadyHandled = false;
foreach (Bot bot in Bots.Values) {
if (bot == this || !bot.SteamClient.IsConnected) {
continue;
}
ArchiHandler.PurchaseResponseCallback otherResult = await bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
if (otherResult == null) {
continue;
}
switch (otherResult.PurchaseResult) {
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK:
alreadyHandled = true; // This key is already handled, as we either redeemed it or we're sure it's dupe/invalid
break;
}
response.Append(Environment.NewLine + "<" + bot.BotName + "> Key: " + key + " | Status: " + otherResult.PurchaseResult + " | Items: " + string.Join("", otherResult.Items));
if (alreadyHandled) {
break;
}
}
key = reader.ReadLine(); // Next key
break; // Next bot (if needed)
}
}
}
if (result == null) {
break;
}
var purchaseResult = result.PurchaseResult;
var items = result.Items;
switch (purchaseResult) {
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.AlreadyOwned:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.BaseGameRequired:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OnCooldown:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.RegionLocked:
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + purchaseResult + " | Items: " + string.Join("", items));
if (BotConfig.DistributeKeys) {
do {
if (iterator.MoveNext()) {
currentBot = iterator.Current;
} else {
currentBot = null;
}
} while (currentBot == this);
if (!BotConfig.ForwardKeysToOtherBots) {
key = reader.ReadLine();
}
break;
if (BotConfig.DistributeKeys) {
do {
if (iterator.MoveNext()) {
currentBot = iterator.Current;
} else {
currentBot = null;
}
if (!BotConfig.ForwardKeysToOtherBots) {
key = reader.ReadLine();
break;
}
bool alreadyHandled = false;
foreach (Bot bot in Bots.Values) {
if (alreadyHandled) {
break;
}
if (bot == this) {
continue;
}
ArchiHandler.PurchaseResponseCallback otherResult;
try {
otherResult = await bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
} catch (Exception e) {
Logging.LogGenericException(e, bot.BotName);
break; // We're done with this key
}
if (otherResult == null) {
break; // We're done with this key
}
var otherPurchaseResult = otherResult.PurchaseResult;
var otherItems = otherResult.Items;
switch (otherPurchaseResult) {
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK:
alreadyHandled = true; // We're done with this key
response.Append(Environment.NewLine + "<" + bot.BotName + "> Key: " + key + " | Status: " + otherPurchaseResult + " | Items: " + string.Join("", otherItems));
break;
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
alreadyHandled = true; // This key doesn't work, don't try to redeem it anymore
response.Append(Environment.NewLine + "<" + bot.BotName + "> Key: " + key + " | Status: " + otherPurchaseResult + " | Items: " + string.Join("", otherItems));
break;
default:
response.Append(Environment.NewLine + "<" + bot.BotName + "> Key: " + key + " | Status: " + otherPurchaseResult + " | Items: " + string.Join("", otherItems));
break;
}
}
key = reader.ReadLine();
break;
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK:
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + purchaseResult + " | Items: " + string.Join("", items));
if (BotConfig.DistributeKeys) {
do {
if (iterator.MoveNext()) {
currentBot = iterator.Current;
} else {
currentBot = null;
}
} while (currentBot == this);
}
key = reader.ReadLine();
break;
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + purchaseResult + " | Items: " + string.Join("", items));
if (BotConfig.DistributeKeys && !BotConfig.ForwardKeysToOtherBots) {
do {
if (iterator.MoveNext()) {
currentBot = iterator.Current;
} else {
currentBot = null;
}
} while (currentBot == this);
}
key = reader.ReadLine();
break;
} while (currentBot == this || (currentBot != null && !currentBot.SteamClient.IsConnected));
}
}
}
@@ -900,8 +882,13 @@ namespace ArchiSteamFarm {
return null;
}
Program.Restart();
return null;
// Schedule the task after some time so user can receive response
Task.Run(async () => {
await Utilities.SleepAsync(1000).ConfigureAwait(false);
Program.Restart();
}).Forget();
return "Done!";
}
private async Task<string> ResponseAddLicense(ulong steamID, HashSet<uint> gameIDs) {
@@ -911,11 +898,8 @@ namespace ArchiSteamFarm {
StringBuilder result = new StringBuilder();
foreach (uint gameID in gameIDs) {
SteamApps.FreeLicenseCallback callback;
try {
callback = await SteamApps.RequestFreeLicense(gameID);
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
SteamApps.FreeLicenseCallback callback = await SteamApps.RequestFreeLicense(gameID);
if (callback == null) {
continue;
}
@@ -1129,7 +1113,11 @@ namespace ArchiSteamFarm {
private void HandleCallbacks() {
TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep);
while (KeepRunning || SteamClient.IsConnected) {
CallbackManager.RunWaitCallbacks(timeSpan);
try {
CallbackManager.RunWaitCallbacks(timeSpan);
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
}
}
}
@@ -1160,7 +1148,9 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Linking new ASF MobileAuthenticator...", BotName);
InitializeLoginAndPassword();
if (!InitializeLoginAndPassword()) {
return;
}
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
LoginResult loginResult;
@@ -1168,6 +1158,9 @@ namespace ArchiSteamFarm {
switch (loginResult) {
case LoginResult.NeedEmail:
userLogin.EmailCode = Program.GetUserInput(Program.EUserInputType.SteamGuard, BotName);
if (string.IsNullOrEmpty(userLogin.EmailCode)) {
return;
}
break;
default:
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
@@ -1182,6 +1175,9 @@ namespace ArchiSteamFarm {
switch (linkResult) {
case AuthenticatorLinker.LinkResult.MustProvidePhoneNumber:
authenticatorLinker.PhoneNumber = Program.GetUserInput(Program.EUserInputType.PhoneNumber, BotName);
if (string.IsNullOrEmpty(authenticatorLinker.PhoneNumber)) {
return;
}
break;
default:
Logging.LogGenericError("Unhandled situation: " + linkResult, BotName);
@@ -1191,13 +1187,21 @@ namespace ArchiSteamFarm {
BotDatabase.SteamGuardAccount = authenticatorLinker.LinkedAccount;
AuthenticatorLinker.FinalizeResult finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(Program.GetUserInput(Program.EUserInputType.SMS, BotName));
string sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName);
if (string.IsNullOrEmpty(sms)) {
return;
}
AuthenticatorLinker.FinalizeResult finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(sms);
if (finalizeResult != AuthenticatorLinker.FinalizeResult.Success) {
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
DelinkMobileAuthenticator();
return;
}
// Ensure that we also save changes made by finalization step (if any)
BotDatabase.Save();
Logging.LogGenericInfo("Successfully linked ASF as new mobile authenticator for this account!", BotName);
Program.GetUserInput(Program.EUserInputType.RevocationCode, BotName, BotDatabase.SteamGuardAccount.RevocationCode);
}
@@ -1224,14 +1228,22 @@ namespace ArchiSteamFarm {
SteamFriends.JoinChat(BotConfig.SteamMasterClanID);
}
private void InitializeLoginAndPassword() {
private bool InitializeLoginAndPassword() {
if (string.IsNullOrEmpty(BotConfig.SteamLogin)) {
BotConfig.SteamLogin = Program.GetUserInput(Program.EUserInputType.Login, BotName);
if (string.IsNullOrEmpty(BotConfig.SteamLogin)) {
return false;
}
}
if (string.IsNullOrEmpty(BotConfig.SteamPassword) && string.IsNullOrEmpty(BotDatabase.LoginKey)) {
BotConfig.SteamPassword = Program.GetUserInput(Program.EUserInputType.Password, BotName);
if (string.IsNullOrEmpty(BotConfig.SteamPassword)) {
return false;
}
}
return true;
}
private void OnConnected(SteamClient.ConnectedCallback callback) {
@@ -1262,7 +1274,10 @@ namespace ArchiSteamFarm {
}
}
InitializeLoginAndPassword();
if (!InitializeLoginAndPassword()) {
Stop();
return;
}
Logging.LogGenericInfo("Logging in...", BotName);
@@ -1300,7 +1315,6 @@ namespace ArchiSteamFarm {
}
Logging.LogGenericInfo("Disconnected from Steam!", BotName);
ArchiWebHandler.OnDisconnected();
CardsFarmer.StopFarming().Forget();
// If we initiated disconnect, do not attempt to reconnect
@@ -1354,15 +1368,6 @@ namespace ArchiSteamFarm {
return;
}
for (byte i = 0; i < WebBrowser.MaxRetries && !ArchiWebHandler.IsInitialized; i++) {
await Utilities.SleepAsync(1000).ConfigureAwait(false);
}
if (!ArchiWebHandler.IsInitialized) {
Logging.LogGenericWarning("Reached timeout while waiting for ArchiWebHandler to initialize!");
return;
}
bool acceptedSomething = false;
foreach (KeyValue guestPass in callback.GuestPasses) {
ulong gid = guestPass["gid"].AsUnsignedLong();
@@ -1500,10 +1505,18 @@ namespace ArchiSteamFarm {
switch (callback.Result) {
case EResult.AccountLogonDenied:
AuthCode = Program.GetUserInput(Program.EUserInputType.SteamGuard, BotName);
if (string.IsNullOrEmpty(AuthCode)) {
Stop();
return;
}
break;
case EResult.AccountLoginDeniedNeedTwoFactor:
if (BotDatabase.SteamGuardAccount == null) {
TwoFactorAuth = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
if (string.IsNullOrEmpty(TwoFactorAuth)) {
Stop();
return;
}
} else {
TwoFactorAuth = BotDatabase.SteamGuardAccount.GenerateSteamGuardCode();
}
@@ -1536,11 +1549,16 @@ namespace ArchiSteamFarm {
if (string.IsNullOrEmpty(BotConfig.SteamParentalPIN)) {
BotConfig.SteamParentalPIN = Program.GetUserInput(Program.EUserInputType.SteamParentalPIN, BotName);
if (string.IsNullOrEmpty(BotConfig.SteamParentalPIN)) {
Stop();
return;
}
}
if (!await ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN).ConfigureAwait(false)) {
await RestartIfRunning().ConfigureAwait(false);
return;
if (!ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN)) {
if (!await RefreshSession().ConfigureAwait(false)) {
return;
}
}
if (BotConfig.DismissInventoryNotifications) {
@@ -1592,35 +1610,37 @@ namespace ArchiSteamFarm {
return;
}
try {
int fileSize;
byte[] sentryHash;
int fileSize;
byte[] sentryHash;
using (FileStream fileStream = File.Open(SentryFile, FileMode.OpenOrCreate, FileAccess.ReadWrite)) {
fileStream.Seek(callback.Offset, SeekOrigin.Begin);
fileStream.Write(callback.Data, 0, callback.BytesToWrite);
fileSize = (int) fileStream.Length;
using (FileStream fileStream = File.Open(SentryFile, FileMode.OpenOrCreate, FileAccess.ReadWrite)) {
fileStream.Seek(callback.Offset, SeekOrigin.Begin);
fileStream.Write(callback.Data, 0, callback.BytesToWrite);
fileSize = (int) fileStream.Length;
fileStream.Seek(0, SeekOrigin.Begin);
using (SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider()) {
sentryHash = sha.ComputeHash(fileStream);
}
fileStream.Seek(0, SeekOrigin.Begin);
using (SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider()) {
sentryHash = sha.ComputeHash(fileStream);
}
}
// Inform the steam servers that we're accepting this sentry file
SteamUser.SendMachineAuthResponse(new SteamUser.MachineAuthDetails {
JobID = callback.JobID,
FileName = callback.FileName,
BytesWritten = callback.BytesToWrite,
FileSize = fileSize,
Offset = callback.Offset,
Result = EResult.OK,
LastError = 0,
OneTimePassword = callback.OneTimePassword,
SentryFileHash = sentryHash,
});
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
// Inform the steam servers that we're accepting this sentry file
SteamUser.SendMachineAuthResponse(new SteamUser.MachineAuthDetails {
JobID = callback.JobID,
FileName = callback.FileName,
BytesWritten = callback.BytesToWrite,
FileSize = fileSize,
Offset = callback.Offset,
Result = EResult.OK,
LastError = 0,
OneTimePassword = callback.OneTimePassword,
SentryFileHash = sentryHash
});
}
private void OnWebAPIUserNonce(SteamUser.WebAPIUserNonceCallback callback) {
if (callback == null) {
return;
}
}

View File

@@ -99,14 +99,14 @@ namespace ArchiSteamFarm {
internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint>() { 0 };
internal static BotConfig Load(string path) {
if (string.IsNullOrEmpty(path) || !File.Exists(path)) {
internal static BotConfig Load(string filePath) {
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) {
return null;
}
BotConfig botConfig;
try {
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(path));
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(filePath));
} catch (Exception e) {
Logging.LogGenericException(e);
return null;

View File

@@ -53,7 +53,7 @@ namespace ArchiSteamFarm {
Bot = bot;
if (Program.GlobalConfig.IdleFarmingPeriod > 0) {
if (Timer == null && Program.GlobalConfig.IdleFarmingPeriod > 0) {
Timer = new Timer(
async e => await CheckGamesForFarming().ConfigureAwait(false),
null,
@@ -211,10 +211,6 @@ namespace ArchiSteamFarm {
return true;
}
if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) {
return false;
}
Logging.LogGenericInfo("Checking badges...", Bot.BotName);
// Find the number of badge pages
@@ -229,8 +225,11 @@ namespace ArchiSteamFarm {
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
if (htmlNodeCollection != null && htmlNodeCollection.Count > 0) {
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
if (!byte.TryParse(htmlNode.InnerText, out maxPages)) {
maxPages = 1; // Should never happen
string lastPage = htmlNode.InnerText;
if (!string.IsNullOrEmpty(lastPage)) {
if (!byte.TryParse(lastPage, out maxPages)) {
maxPages = 1; // Should never happen
}
}
}
@@ -242,17 +241,13 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
List<Task> tasks = new List<Task>(maxPages - 1);
for (byte page = 2; page <= maxPages; page++) {
byte currentPage = page; // We need a copy of variable being passed when in for loops
byte currentPage = page; // We need a copy of variable being passed when in for loops, as loop will proceed before task is launched
tasks.Add(CheckPage(currentPage));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
}
if (GamesToFarm.Count == 0) {
return false;
}
return true;
return GamesToFarm.Count > 0;
}
private void CheckPage(HtmlDocument htmlDocument) {
@@ -362,7 +357,6 @@ namespace ArchiSteamFarm {
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
if (htmlNode == null) {
await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false);
return null;
}

View File

@@ -36,9 +36,10 @@ namespace ArchiSteamFarm {
Experimental
}
internal const byte DefaultHttpTimeout = 60;
private const byte DefaultMaxFarmingTime = 10;
private const byte DefaultFarmingDelay = 5;
private const byte DefaultHttpTimeout = 60;
private const ushort DefaultWCFPort = 1242;
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
@@ -48,6 +49,9 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)]
internal bool Debug { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool Headless { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool AutoUpdates { get; private set; } = true;
@@ -117,6 +121,10 @@ namespace ArchiSteamFarm {
return null;
}
if (globalConfig == null) {
return null;
}
// SK2 supports only TCP and UDP steam protocols
// Ensure that user can't screw this up
switch (globalConfig.SteamProtocol) {

View File

@@ -247,10 +247,15 @@ namespace ArchiSteamFarm {
Restart();
}
internal static void Exit(int exitCode = 0) {
WCF.StopServer();
Environment.Exit(exitCode);
}
internal static void Restart() {
try {
Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs().Skip(1)));
Environment.Exit(0);
Exit();
} catch (Exception e) {
Logging.LogGenericException(e);
}
@@ -269,6 +274,11 @@ namespace ArchiSteamFarm {
return null;
}
if (GlobalConfig.Headless) {
Logging.LogGenericWarning("Received a request for user input, but process is running in headless mode!");
return null;
}
string result;
lock (ConsoleLock) {
ConsoleIsBusy = true;
@@ -337,14 +347,14 @@ namespace ArchiSteamFarm {
if (GlobalConfig == null) {
Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.json exists and is valid!");
Thread.Sleep(5000);
Environment.Exit(1);
Exit(1);
}
GlobalDatabase = GlobalDatabase.Load();
if (GlobalDatabase == null) {
Logging.LogGenericError("Global database could not be loaded!");
Thread.Sleep(5000);
Environment.Exit(1);
Exit(1);
}
ArchiWebHandler.Init();
@@ -445,7 +455,7 @@ namespace ArchiSteamFarm {
// If we ran ASF as a client, we're done by now
if (Mode == EMode.Client) {
Environment.Exit(0);
Exit();
}
// From now on it's server mode
@@ -454,7 +464,7 @@ namespace ArchiSteamFarm {
if (!Directory.Exists(ConfigDirectory)) {
Logging.LogGenericError("Config directory doesn't exist!");
Thread.Sleep(5000);
Environment.Exit(1);
Exit(1);
}
CheckForUpdate().Wait();
@@ -494,7 +504,7 @@ namespace ArchiSteamFarm {
ShutdownResetEvent.WaitOne();
// We got a signal to shutdown
Environment.Exit(0);
Exit();
}
}
}

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
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.2.1")]
[assembly: AssemblyFileVersion("2.0.2.1")]
[assembly: AssemblyVersion("2.0.2.5")]
[assembly: AssemblyFileVersion("2.0.2.5")]

View File

@@ -84,19 +84,12 @@ namespace ArchiSteamFarm {
return;
}
List<Task> tasks = new List<Task>();
foreach (Steam.TradeOffer tradeOffer in tradeOffers) {
if (tradeOffer.trade_offer_state == Steam.TradeOffer.ETradeOfferState.Active) {
tasks.Add(Task.Run(async () => await ParseTrade(tradeOffer).ConfigureAwait(false)));
}
}
await Task.WhenAll(tasks).ConfigureAwait(false);
await Bot.AcceptConfirmations(Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
}
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
if (tradeOffer == null) {
if (tradeOffer == null || tradeOffer.trade_offer_state != Steam.TradeOffer.ETradeOfferState.Active) {
return;
}
@@ -105,12 +98,33 @@ namespace ArchiSteamFarm {
return;
}
if (tradeOffer.items_to_give.Count == 0 || tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID) {
if (ShouldAcceptTrade(tradeOffer)) {
Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
} else {
Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName);
}
}
private bool ShouldAcceptTrade(Steam.TradeOffer tradeOffer) {
if (tradeOffer == null) {
return false;
}
// Always accept trades when we're not losing anything
if (tradeOffer.items_to_give.Count == 0) {
return true;
}
// Always accept trades from SteamMasterID
if (tradeOffer.OtherSteamID64 != 0 && tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID) {
return true;
}
// TODO: Add optional SteamTradeMatcher integration here
// If no rule above matched this trade, reject it
return false;
}
}
}

View File

@@ -22,14 +22,25 @@
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ArchiSteamFarm {
internal static class Utilities {
internal static void Forget(this Task task) { }
internal static async Task SleepAsync(int miliseconds) {
await Task.Delay(miliseconds).ConfigureAwait(false);
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
return Task.WhenAll(sequence.Select(action));
}
internal static Task SleepAsync(int miliseconds) {
if (miliseconds < 0) {
return Task.FromResult(true);
}
return Task.Delay(miliseconds);
}
internal static uint GetCharCountInString(string s, char c) {

View File

@@ -44,6 +44,9 @@ namespace ArchiSteamFarm {
internal static void Init() {
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
Program.GlobalConfig.WCFHostname = Program.GetUserInput(Program.EUserInputType.WCFHostname);
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
return;
}
}
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";

View File

@@ -184,6 +184,10 @@ namespace ArchiSteamFarm {
return null;
}
if (request.StartsWith("https://") && Program.GlobalConfig.ForceHttp) {
return null;
}
HttpResponseMessage responseMessage;
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
if (data != null && data.Count > 0) {
@@ -220,7 +224,7 @@ namespace ArchiSteamFarm {
if (!responseMessage.IsSuccessStatusCode) {
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
Logging.LogGenericError("Request: " + request + "failed!");
Logging.LogGenericError("Request: " + request + " failed!");
Logging.LogGenericError("Status code: " + responseMessage.StatusCode);
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false));
}

View File

@@ -1,5 +1,6 @@
{
"Debug": false,
"Headless": false,
"AutoUpdates": true,
"UpdateChannel": 1,
"SteamProtocol": 6,

View File

@@ -54,9 +54,9 @@ namespace ConfigGenerator {
internal virtual void Remove() {
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
lock (FilePath) {
foreach (var configFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
try {
File.Delete(configFile);
File.Delete(botFile);
} catch (Exception e) {
Logging.LogGenericException(e);
}
@@ -72,9 +72,9 @@ namespace ConfigGenerator {
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
lock (FilePath) {
foreach (var file in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
try {
File.Move(file, Path.Combine(Program.ConfigDirectory, botName + Path.GetExtension(file)));
File.Move(botFile, Path.Combine(Program.ConfigDirectory, botName + Path.GetExtension(botFile)));
} catch (Exception e) {
Logging.LogGenericException(e);
}

View File

@@ -98,7 +98,6 @@ namespace ConfigGenerator {
[JsonProperty(Required = Required.DisallowNull)]
public List<uint> GamesPlayedWhileIdle { get; set; } = new List<uint>();
internal static BotConfig Load(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
return null;
@@ -116,6 +115,10 @@ namespace ConfigGenerator {
return new BotConfig(filePath);
}
if (botConfig == null) {
return new BotConfig(filePath);
}
botConfig.FilePath = filePath;
return botConfig;

View File

@@ -48,6 +48,9 @@ namespace ConfigGenerator {
[JsonProperty(Required = Required.DisallowNull)]
public bool Debug { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool Headless { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool AutoUpdates { get; set; } = true;
@@ -120,6 +123,10 @@ namespace ConfigGenerator {
return new GlobalConfig(filePath);
}
if (globalConfig == null) {
return new GlobalConfig(filePath);
}
globalConfig.FilePath = filePath;
// SK2 supports only TCP and UDP steam protocols

66
cc.sh Executable file
View File

@@ -0,0 +1,66 @@
#!/bin/bash
set -eu
BUILD="Release"
CLEAN=0
MONO_ARGS=("--aot" "--llvm" "--server" "-O=all")
XBUILD_ARGS=("/nologo")
BINARIES=("ArchiSteamFarm/bin/Release/ArchiSteamFarm.exe")
SOLUTION="ArchiSteamFarm.sln"
PRINT_USAGE() {
echo "Usage: $0 [--clean] [debug/release]"
exit 1
}
for ARG in "$@"; do
case "$ARG" in
release|Release) BUILD="Release" ;;
debug|Debug) BUILD="Debug" ;;
--clean) CLEAN=1 ;;
*) PRINT_USAGE
esac
done
XBUILD_ARGS+=("/p:Configuration=$BUILD")
cd "$(dirname "$(readlink -f "$0")")"
if [[ -d ".git" ]] && hash git &>/dev/null; then
git pull
fi
if [[ ! -f "$SOLUTION" ]]; then
echo "ERROR: $SOLUTION could not be found!"
exit 1
fi
if hash nuget &>/dev/null; then
nuget restore "$SOLUTION"
fi
if [[ "$CLEAN" -eq 1 ]]; then
rm -rf out
xbuild "${XBUILD_ARGS[@]}" "/t:Clean" "$SOLUTION"
fi
xbuild "${XBUILD_ARGS[@]}" "$SOLUTION"
if [[ ! -f "${BINARIES[0]}" ]]; then
echo "ERROR: ${BINARIES[0]} binary could not be found!"
fi
# If it's release build, use Mono AOT for output binaries
if [[ "$BUILD" = "Release" ]]; then
for BINARY in "${BINARIES[@]}"; do
if [[ ! -f "$BINARY" ]]; then
continue
fi
mono "${MONO_ARGS[@]}" "$BINARY"
done
fi
echo
echo "Compilation finished successfully! :)"

28
run.sh Executable file
View File

@@ -0,0 +1,28 @@
#!/bin/bash
set -eu
BUILD="Release"
MONO_ARGS=("--llvm" "--server" "-O=all")
PRINT_USAGE() {
echo "Usage: $0 [debug/release]"
exit 1
}
for ARG in "$@"; do
case "$ARG" in
release|Release) BUILD="Release" ;;
debug|Debug) BUILD="Debug" ;;
*) PRINT_USAGE
esac
done
BINARY="ArchiSteamFarm/bin/$BUILD/ArchiSteamFarm.exe"
if [[ ! -f "$BINARY" ]]; then
echo "ERROR: $BINARY could not be found!"
exit 1
fi
mono "${MONO_ARGS[@]}" "$BINARY"