mirror of
https://github.com/JustArchiNET/ArchiSteamFarm.git
synced 2025-12-20 16:28:38 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa12ffd9d0 | ||
|
|
ecb27adedd | ||
|
|
6e9be09944 | ||
|
|
6a79a89a10 | ||
|
|
d67be4f092 | ||
|
|
a8e1039e32 | ||
|
|
2ad9d9e197 | ||
|
|
fd6e2c72d7 | ||
|
|
1eed0f7647 | ||
|
|
c018c08260 | ||
|
|
72fa98cb89 | ||
|
|
71215d695e | ||
|
|
75b785d4b6 | ||
|
|
12e32692cb | ||
|
|
6158a9268d | ||
|
|
a48e53585a | ||
|
|
33383633ea | ||
|
|
3f7359c608 | ||
|
|
d8b59c6889 | ||
|
|
0f71b788cb | ||
|
|
d012255a21 | ||
|
|
8fc39a44cd | ||
|
|
c6fe424fcc | ||
|
|
12488dafd3 | ||
|
|
3b53491567 |
@@ -257,6 +257,7 @@ namespace ArchiSteamFarm {
|
|||||||
request.Body.key = key;
|
request.Body.key = key;
|
||||||
|
|
||||||
Client.Send(request);
|
Client.Send(request);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID);
|
return await new AsyncJob<PurchaseResponseCallback>(Client, request.SourceJobID);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -32,21 +32,22 @@ using System.Net.Http;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal sealed class ArchiWebHandler {
|
internal sealed class ArchiWebHandler {
|
||||||
private const string SteamCommunity = "steamcommunity.com";
|
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 string SteamCommunityURL = "https://" + SteamCommunity;
|
||||||
|
|
||||||
private static int Timeout = 30 * 1000;
|
private static int Timeout = GlobalConfig.DefaultHttpTimeout * 1000;
|
||||||
|
|
||||||
private readonly Bot Bot;
|
private readonly Bot Bot;
|
||||||
private readonly Dictionary<string, string> Cookie = new Dictionary<string, string>(4);
|
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 DateTime LastSessionRefreshCheck = DateTime.MinValue;
|
||||||
|
|
||||||
private ulong SteamID;
|
|
||||||
|
|
||||||
internal static void Init() {
|
internal static void Init() {
|
||||||
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
|
||||||
@@ -61,18 +62,14 @@ namespace ArchiSteamFarm {
|
|||||||
Bot = bot;
|
Bot = bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnDisconnected() {
|
internal bool Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
|
||||||
IsInitialized = false;
|
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce)) {
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task<bool> Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
|
|
||||||
if (steamClient == null || steamClient.SteamID == null || string.IsNullOrEmpty(webAPIUserNonce) || IsInitialized) {
|
|
||||||
return false;
|
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
|
// Generate an AES session key
|
||||||
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
|
byte[] sessionKey = CryptoHelper.GenerateRandomBlock(32);
|
||||||
@@ -99,7 +96,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
authResult = iSteamUserAuth.AuthenticateUser(
|
authResult = iSteamUserAuth.AuthenticateUser(
|
||||||
steamid: SteamID,
|
steamid: steamID,
|
||||||
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
|
sessionkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedSessionKey, 0, cryptedSessionKey.Length)),
|
||||||
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
|
encrypted_loginkey: Encoding.ASCII.GetString(WebUtility.UrlEncodeToBytes(cryptedLoginKey, 0, cryptedLoginKey.Length)),
|
||||||
method: WebRequestMethods.Http.Post,
|
method: WebRequestMethods.Http.Post,
|
||||||
@@ -127,17 +124,15 @@ namespace ArchiSteamFarm {
|
|||||||
// The below is used for display purposes only
|
// 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}";
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool?> IsLoggedIn() {
|
internal async Task<bool?> IsLoggedIn() {
|
||||||
if (SteamID == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HtmlDocument htmlDocument = null;
|
HtmlDocument htmlDocument = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile", Cookie).ConfigureAwait(false);
|
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(SteamCommunityURL + "/my/profile", Cookie).ConfigureAwait(false);
|
||||||
@@ -152,23 +147,42 @@ namespace ArchiSteamFarm {
|
|||||||
return htmlNode != null;
|
return htmlNode != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> ReconnectIfNeeded() {
|
internal async Task<bool> RefreshSessionIfNeeded() {
|
||||||
bool? isLoggedIn = await IsLoggedIn().ConfigureAwait(false);
|
DateTime now = DateTime.Now;
|
||||||
if (isLoggedIn.HasValue && !isLoggedIn.Value) {
|
if (now.Subtract(LastSessionRefreshCheck).TotalSeconds < MinSessionTTL) {
|
||||||
Logging.LogGenericInfo("Reconnecting because our sessionID expired!", Bot.BotName);
|
|
||||||
Bot.RestartIfRunning().Forget();
|
|
||||||
return true;
|
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() {
|
internal async Task<Dictionary<uint, string>> GetOwnedGames() {
|
||||||
if (SteamID == 0) {
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
string request = SteamCommunityURL + "/profiles/" + SteamID + "/games/?xml=1";
|
string request = SteamCommunityURL + "/my/games/?xml=1";
|
||||||
|
|
||||||
XmlDocument response = null;
|
XmlDocument response = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||||
@@ -273,6 +287,10 @@ namespace ArchiSteamFarm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
string sessionID;
|
string sessionID;
|
||||||
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -303,6 +321,10 @@ namespace ArchiSteamFarm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
string sessionID;
|
string sessionID;
|
||||||
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -330,37 +352,11 @@ namespace ArchiSteamFarm {
|
|||||||
return true;
|
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() {
|
internal async Task<List<Steam.Item>> GetMyTradableInventory() {
|
||||||
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
JObject jObject = null;
|
JObject jObject = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && jObject == null; i++) {
|
||||||
jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/753/6?trading=1", Cookie).ConfigureAwait(false);
|
jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/753/6?trading=1", Cookie).ConfigureAwait(false);
|
||||||
@@ -394,6 +390,10 @@ namespace ArchiSteamFarm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
string sessionID;
|
string sessionID;
|
||||||
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -449,13 +449,17 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<HtmlDocument> GetBadgePage(byte page) {
|
internal async Task<HtmlDocument> GetBadgePage(byte page) {
|
||||||
if (page == 0 || SteamID == 0) {
|
if (page == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HtmlDocument htmlDocument = null;
|
HtmlDocument htmlDocument = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(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) {
|
if (htmlDocument == null) {
|
||||||
@@ -467,13 +471,17 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
|
internal async Task<HtmlDocument> GetGameCardsPage(ulong appID) {
|
||||||
if (appID == 0 || SteamID == 0) {
|
if (appID == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HtmlDocument htmlDocument = null;
|
HtmlDocument htmlDocument = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && htmlDocument == null; i++) {
|
||||||
htmlDocument = await WebBrowser.UrlGetToHtmlDocument(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) {
|
if (htmlDocument == null) {
|
||||||
@@ -485,13 +493,13 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<bool> MarkInventory() {
|
internal async Task<bool> MarkInventory() {
|
||||||
if (SteamID == 0) {
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponseMessage response = null;
|
HttpResponseMessage response = null;
|
||||||
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
|
||||||
response = await WebBrowser.UrlGet(SteamCommunityURL + "/profiles/" + SteamID + "/inventory", Cookie).ConfigureAwait(false);
|
response = await WebBrowser.UrlGet(SteamCommunityURL + "/my/inventory", Cookie).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
@@ -507,6 +515,10 @@ namespace ArchiSteamFarm {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
string sessionID;
|
string sessionID;
|
||||||
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -530,9 +542,9 @@ namespace ArchiSteamFarm {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UnlockParentalAccount(string parentalPin) {
|
private async Task<bool> UnlockParentalAccount(string parentalPin) {
|
||||||
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
|
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
|
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
|
||||||
@@ -550,13 +562,13 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<string> setCookieValues;
|
IEnumerable<string> setCookieValues;
|
||||||
if (!response.Headers.TryGetValues("Set-Cookie", out setCookieValues)) {
|
if (!response.Headers.TryGetValues("Set-Cookie", out setCookieValues)) {
|
||||||
Logging.LogNullError("setCookieValues", Bot.BotName);
|
Logging.LogNullError("setCookieValues", Bot.BotName);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (string setCookieValue in setCookieValues) {
|
foreach (string setCookieValue in setCookieValues) {
|
||||||
@@ -573,10 +585,11 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
Cookie["steamparental"] = setCookie;
|
Cookie["steamparental"] = setCookie;
|
||||||
Logging.LogGenericInfo("Success!", Bot.BotName);
|
Logging.LogGenericInfo("Success!", Bot.BotName);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName);
|
Logging.LogGenericWarning("Failed to unlock parental account!", Bot.BotName);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,6 +187,7 @@ namespace ArchiSteamFarm {
|
|||||||
CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn);
|
CallbackManager.Subscribe<SteamUser.LoggedOnCallback>(OnLoggedOn);
|
||||||
CallbackManager.Subscribe<SteamUser.LoginKeyCallback>(OnLoginKey);
|
CallbackManager.Subscribe<SteamUser.LoginKeyCallback>(OnLoginKey);
|
||||||
CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth);
|
CallbackManager.Subscribe<SteamUser.UpdateMachineAuthCallback>(OnMachineAuth);
|
||||||
|
CallbackManager.Subscribe<SteamUser.WebAPIUserNonceCallback>(OnWebAPIUserNonce);
|
||||||
|
|
||||||
CallbackManager.Subscribe<ArchiHandler.NotificationsCallback>(OnNotifications);
|
CallbackManager.Subscribe<ArchiHandler.NotificationsCallback>(OnNotifications);
|
||||||
CallbackManager.Subscribe<ArchiHandler.OfflineMessageCallback>(OnOfflineMessage);
|
CallbackManager.Subscribe<ArchiHandler.OfflineMessageCallback>(OnOfflineMessage);
|
||||||
@@ -196,16 +197,16 @@ namespace ArchiSteamFarm {
|
|||||||
CardsFarmer = new CardsFarmer(this);
|
CardsFarmer = new CardsFarmer(this);
|
||||||
Trading = new Trading(this);
|
Trading = new Trading(this);
|
||||||
|
|
||||||
if (BotConfig.AcceptConfirmationsPeriod > 0) {
|
if (AcceptConfirmationsTimer == null && BotConfig.AcceptConfirmationsPeriod > 0) {
|
||||||
AcceptConfirmationsTimer = new Timer(
|
AcceptConfirmationsTimer = new Timer(
|
||||||
async e => await AcceptConfirmations().ConfigureAwait(false),
|
async e => await AcceptConfirmations(true).ConfigureAwait(false),
|
||||||
null,
|
null,
|
||||||
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod), // Delay
|
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod), // Delay
|
||||||
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod) // Period
|
TimeSpan.FromMinutes(BotConfig.AcceptConfirmationsPeriod) // Period
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BotConfig.SendTradePeriod > 0) {
|
if (SendItemsTimer == null && BotConfig.SendTradePeriod > 0) {
|
||||||
SendItemsTimer = new Timer(
|
SendItemsTimer = new Timer(
|
||||||
async e => await ResponseSendTrade(BotConfig.SteamMasterID).ConfigureAwait(false),
|
async e => await ResponseSendTrade(BotConfig.SteamMasterID).ConfigureAwait(false),
|
||||||
null,
|
null,
|
||||||
@@ -222,7 +223,7 @@ namespace ArchiSteamFarm {
|
|||||||
Start().Forget();
|
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) {
|
if (BotDatabase.SteamGuardAccount == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -242,10 +243,14 @@ namespace ArchiSteamFarm {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
BotDatabase.SteamGuardAccount.AcceptConfirmation(confirmation);
|
if (confirm) {
|
||||||
|
BotDatabase.SteamGuardAccount.AcceptConfirmation(confirmation);
|
||||||
|
} else {
|
||||||
|
BotDatabase.SteamGuardAccount.DenyConfirmation(confirmation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (SteamGuardAccount.WGTokenInvalidException) {
|
} 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("Confirmation could not be accepted because of invalid token exception", BotName);
|
||||||
Logging.LogGenericWarning("If issue persists, consider removing and readding ASF 2FA", BotName);
|
Logging.LogGenericWarning("If issue persists, consider removing and readding ASF 2FA", BotName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -261,12 +266,23 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task RestartIfRunning() {
|
internal async Task<bool> RefreshSession() {
|
||||||
if (!SteamClient.IsConnected) {
|
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) {
|
internal async Task OnFarmingFinished(bool farmedSomething) {
|
||||||
@@ -285,17 +301,19 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!message.StartsWith("!")) {
|
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(" ")) {
|
if (!message.Contains(" ")) {
|
||||||
switch (message) {
|
switch (message) {
|
||||||
case "!2fa":
|
case "!2fa":
|
||||||
return Response2FA(steamID);
|
return Response2FA(steamID);
|
||||||
|
case "!2fano":
|
||||||
|
return await Response2FAConfirm(steamID, false).ConfigureAwait(false);
|
||||||
case "!2faoff":
|
case "!2faoff":
|
||||||
return Response2FAOff(steamID);
|
return Response2FAOff(steamID);
|
||||||
case "!2faok":
|
case "!2faok":
|
||||||
return await Response2FAOK(steamID).ConfigureAwait(false);
|
return await Response2FAConfirm(steamID, true).ConfigureAwait(false);
|
||||||
case "!exit":
|
case "!exit":
|
||||||
return ResponseExit(steamID);
|
return ResponseExit(steamID);
|
||||||
case "!farm":
|
case "!farm":
|
||||||
@@ -326,10 +344,12 @@ namespace ArchiSteamFarm {
|
|||||||
switch (args[0]) {
|
switch (args[0]) {
|
||||||
case "!2fa":
|
case "!2fa":
|
||||||
return Response2FA(steamID, args[1]);
|
return Response2FA(steamID, args[1]);
|
||||||
|
case "!2fano":
|
||||||
|
return await Response2FAConfirm(steamID, args[1], false).ConfigureAwait(false);
|
||||||
case "!2faoff":
|
case "!2faoff":
|
||||||
return Response2FAOff(steamID, args[1]);
|
return Response2FAOff(steamID, args[1]);
|
||||||
case "!2faok":
|
case "!2faok":
|
||||||
return await Response2FAOK(steamID, args[1]).ConfigureAwait(false);
|
return await Response2FAConfirm(steamID, args[1], true).ConfigureAwait(false);
|
||||||
case "!addlicense":
|
case "!addlicense":
|
||||||
if (args.Length > 2) {
|
if (args.Length > 2) {
|
||||||
return await ResponseAddLicense(steamID, args[1], args[2]).ConfigureAwait(false);
|
return await ResponseAddLicense(steamID, args[1], args[2]).ConfigureAwait(false);
|
||||||
@@ -358,9 +378,9 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
case "!redeem":
|
case "!redeem":
|
||||||
if (args.Length > 2) {
|
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 {
|
} 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":
|
case "!start":
|
||||||
return await ResponseStart(steamID, args[1]).ConfigureAwait(false);
|
return await ResponseStart(steamID, args[1]).ConfigureAwait(false);
|
||||||
@@ -432,7 +452,9 @@ namespace ArchiSteamFarm {
|
|||||||
// But here we're dealing with WinAuth authenticator
|
// But here we're dealing with WinAuth authenticator
|
||||||
Logging.LogGenericInfo("ASF requires a few more steps to complete authenticator import...", BotName);
|
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);
|
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
|
||||||
LoginResult loginResult;
|
LoginResult loginResult;
|
||||||
@@ -440,6 +462,9 @@ namespace ArchiSteamFarm {
|
|||||||
switch (loginResult) {
|
switch (loginResult) {
|
||||||
case LoginResult.Need2FA:
|
case LoginResult.Need2FA:
|
||||||
userLogin.TwoFactorCode = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
|
userLogin.TwoFactorCode = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
|
||||||
|
if (string.IsNullOrEmpty(userLogin.TwoFactorCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
|
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
|
||||||
@@ -560,7 +585,7 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (await ArchiWebHandler.SendTradeOffer(inventory, BotConfig.SteamMasterID, BotConfig.SteamTradeToken).ConfigureAwait(false)) {
|
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!";
|
return "Trade offer sent successfully!";
|
||||||
} else {
|
} else {
|
||||||
return "Trade offer failed due to error!";
|
return "Trade offer failed due to error!";
|
||||||
@@ -635,7 +660,7 @@ namespace ArchiSteamFarm {
|
|||||||
return bot.Response2FAOff(steamID);
|
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)) {
|
if (steamID == 0 || !IsMaster(steamID)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -644,11 +669,11 @@ namespace ArchiSteamFarm {
|
|||||||
return "That bot doesn't have ASF 2FA enabled!";
|
return "That bot doesn't have ASF 2FA enabled!";
|
||||||
}
|
}
|
||||||
|
|
||||||
await AcceptConfirmations().ConfigureAwait(false);
|
await AcceptConfirmations(confirm).ConfigureAwait(false);
|
||||||
return "Done!";
|
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)) {
|
if (steamID == 0 || string.IsNullOrEmpty(botName)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -658,7 +683,7 @@ namespace ArchiSteamFarm {
|
|||||||
return "Couldn't find any bot named " + botName + "!";
|
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) {
|
private static string ResponseExit(ulong steamID) {
|
||||||
@@ -670,8 +695,13 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment.Exit(0);
|
// Schedule the task after some time so user can receive response
|
||||||
return null;
|
Task.Run(async () => {
|
||||||
|
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
||||||
|
Program.Exit();
|
||||||
|
}).Forget();
|
||||||
|
|
||||||
|
return "Done!";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string ResponseFarm(ulong steamID) {
|
private string ResponseFarm(ulong steamID) {
|
||||||
@@ -727,130 +757,82 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder response = new StringBuilder();
|
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();
|
string key = reader.ReadLine();
|
||||||
IEnumerator<Bot> iterator = Bots.Values.GetEnumerator();
|
|
||||||
Bot currentBot = this;
|
Bot currentBot = this;
|
||||||
while (key != null) {
|
while (!string.IsNullOrEmpty(key) && currentBot != null) {
|
||||||
if (currentBot == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (validate && !IsValidCdKey(key)) {
|
if (validate && !IsValidCdKey(key)) {
|
||||||
key = reader.ReadLine();
|
key = reader.ReadLine(); // Next key
|
||||||
continue;
|
continue; // Without changing the bot
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiHandler.PurchaseResponseCallback result;
|
if (currentBot.SteamClient.IsConnected) {
|
||||||
try {
|
ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
||||||
result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
|
if (result != null) {
|
||||||
} catch (Exception e) {
|
switch (result.PurchaseResult) {
|
||||||
Logging.LogGenericException(e, currentBot.BotName);
|
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
|
||||||
break;
|
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) {
|
if (BotConfig.DistributeKeys) {
|
||||||
break;
|
do {
|
||||||
}
|
if (iterator.MoveNext()) {
|
||||||
|
currentBot = iterator.Current;
|
||||||
var purchaseResult = result.PurchaseResult;
|
} else {
|
||||||
var items = result.Items;
|
currentBot = null;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
} while (currentBot == this || (currentBot != null && !currentBot.SteamClient.IsConnected));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -900,8 +882,13 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Program.Restart();
|
// Schedule the task after some time so user can receive response
|
||||||
return null;
|
Task.Run(async () => {
|
||||||
|
await Utilities.SleepAsync(1000).ConfigureAwait(false);
|
||||||
|
Program.Restart();
|
||||||
|
}).Forget();
|
||||||
|
|
||||||
|
return "Done!";
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> ResponseAddLicense(ulong steamID, HashSet<uint> gameIDs) {
|
private async Task<string> ResponseAddLicense(ulong steamID, HashSet<uint> gameIDs) {
|
||||||
@@ -911,11 +898,8 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
foreach (uint gameID in gameIDs) {
|
foreach (uint gameID in gameIDs) {
|
||||||
SteamApps.FreeLicenseCallback callback;
|
SteamApps.FreeLicenseCallback callback = await SteamApps.RequestFreeLicense(gameID);
|
||||||
try {
|
if (callback == null) {
|
||||||
callback = await SteamApps.RequestFreeLicense(gameID);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Logging.LogGenericException(e, BotName);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1129,7 +1113,11 @@ namespace ArchiSteamFarm {
|
|||||||
private void HandleCallbacks() {
|
private void HandleCallbacks() {
|
||||||
TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep);
|
TimeSpan timeSpan = TimeSpan.FromMilliseconds(CallbackSleep);
|
||||||
while (KeepRunning || SteamClient.IsConnected) {
|
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);
|
Logging.LogGenericInfo("Linking new ASF MobileAuthenticator...", BotName);
|
||||||
|
|
||||||
InitializeLoginAndPassword();
|
if (!InitializeLoginAndPassword()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
|
UserLogin userLogin = new UserLogin(BotConfig.SteamLogin, BotConfig.SteamPassword);
|
||||||
LoginResult loginResult;
|
LoginResult loginResult;
|
||||||
@@ -1168,6 +1158,9 @@ namespace ArchiSteamFarm {
|
|||||||
switch (loginResult) {
|
switch (loginResult) {
|
||||||
case LoginResult.NeedEmail:
|
case LoginResult.NeedEmail:
|
||||||
userLogin.EmailCode = Program.GetUserInput(Program.EUserInputType.SteamGuard, BotName);
|
userLogin.EmailCode = Program.GetUserInput(Program.EUserInputType.SteamGuard, BotName);
|
||||||
|
if (string.IsNullOrEmpty(userLogin.EmailCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
|
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
|
||||||
@@ -1182,6 +1175,9 @@ namespace ArchiSteamFarm {
|
|||||||
switch (linkResult) {
|
switch (linkResult) {
|
||||||
case AuthenticatorLinker.LinkResult.MustProvidePhoneNumber:
|
case AuthenticatorLinker.LinkResult.MustProvidePhoneNumber:
|
||||||
authenticatorLinker.PhoneNumber = Program.GetUserInput(Program.EUserInputType.PhoneNumber, BotName);
|
authenticatorLinker.PhoneNumber = Program.GetUserInput(Program.EUserInputType.PhoneNumber, BotName);
|
||||||
|
if (string.IsNullOrEmpty(authenticatorLinker.PhoneNumber)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logging.LogGenericError("Unhandled situation: " + linkResult, BotName);
|
Logging.LogGenericError("Unhandled situation: " + linkResult, BotName);
|
||||||
@@ -1191,13 +1187,21 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
BotDatabase.SteamGuardAccount = authenticatorLinker.LinkedAccount;
|
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) {
|
if (finalizeResult != AuthenticatorLinker.FinalizeResult.Success) {
|
||||||
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
|
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
|
||||||
DelinkMobileAuthenticator();
|
DelinkMobileAuthenticator();
|
||||||
return;
|
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);
|
Logging.LogGenericInfo("Successfully linked ASF as new mobile authenticator for this account!", BotName);
|
||||||
Program.GetUserInput(Program.EUserInputType.RevocationCode, BotName, BotDatabase.SteamGuardAccount.RevocationCode);
|
Program.GetUserInput(Program.EUserInputType.RevocationCode, BotName, BotDatabase.SteamGuardAccount.RevocationCode);
|
||||||
}
|
}
|
||||||
@@ -1224,14 +1228,22 @@ namespace ArchiSteamFarm {
|
|||||||
SteamFriends.JoinChat(BotConfig.SteamMasterClanID);
|
SteamFriends.JoinChat(BotConfig.SteamMasterClanID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeLoginAndPassword() {
|
private bool InitializeLoginAndPassword() {
|
||||||
if (string.IsNullOrEmpty(BotConfig.SteamLogin)) {
|
if (string.IsNullOrEmpty(BotConfig.SteamLogin)) {
|
||||||
BotConfig.SteamLogin = Program.GetUserInput(Program.EUserInputType.Login, BotName);
|
BotConfig.SteamLogin = Program.GetUserInput(Program.EUserInputType.Login, BotName);
|
||||||
|
if (string.IsNullOrEmpty(BotConfig.SteamLogin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(BotConfig.SteamPassword) && string.IsNullOrEmpty(BotDatabase.LoginKey)) {
|
if (string.IsNullOrEmpty(BotConfig.SteamPassword) && string.IsNullOrEmpty(BotDatabase.LoginKey)) {
|
||||||
BotConfig.SteamPassword = Program.GetUserInput(Program.EUserInputType.Password, BotName);
|
BotConfig.SteamPassword = Program.GetUserInput(Program.EUserInputType.Password, BotName);
|
||||||
|
if (string.IsNullOrEmpty(BotConfig.SteamPassword)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConnected(SteamClient.ConnectedCallback callback) {
|
private void OnConnected(SteamClient.ConnectedCallback callback) {
|
||||||
@@ -1262,7 +1274,10 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InitializeLoginAndPassword();
|
if (!InitializeLoginAndPassword()) {
|
||||||
|
Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Logging.LogGenericInfo("Logging in...", BotName);
|
Logging.LogGenericInfo("Logging in...", BotName);
|
||||||
|
|
||||||
@@ -1300,7 +1315,6 @@ namespace ArchiSteamFarm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logging.LogGenericInfo("Disconnected from Steam!", BotName);
|
Logging.LogGenericInfo("Disconnected from Steam!", BotName);
|
||||||
ArchiWebHandler.OnDisconnected();
|
|
||||||
CardsFarmer.StopFarming().Forget();
|
CardsFarmer.StopFarming().Forget();
|
||||||
|
|
||||||
// If we initiated disconnect, do not attempt to reconnect
|
// If we initiated disconnect, do not attempt to reconnect
|
||||||
@@ -1354,15 +1368,6 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
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;
|
bool acceptedSomething = false;
|
||||||
foreach (KeyValue guestPass in callback.GuestPasses) {
|
foreach (KeyValue guestPass in callback.GuestPasses) {
|
||||||
ulong gid = guestPass["gid"].AsUnsignedLong();
|
ulong gid = guestPass["gid"].AsUnsignedLong();
|
||||||
@@ -1500,10 +1505,18 @@ namespace ArchiSteamFarm {
|
|||||||
switch (callback.Result) {
|
switch (callback.Result) {
|
||||||
case EResult.AccountLogonDenied:
|
case EResult.AccountLogonDenied:
|
||||||
AuthCode = Program.GetUserInput(Program.EUserInputType.SteamGuard, BotName);
|
AuthCode = Program.GetUserInput(Program.EUserInputType.SteamGuard, BotName);
|
||||||
|
if (string.IsNullOrEmpty(AuthCode)) {
|
||||||
|
Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case EResult.AccountLoginDeniedNeedTwoFactor:
|
case EResult.AccountLoginDeniedNeedTwoFactor:
|
||||||
if (BotDatabase.SteamGuardAccount == null) {
|
if (BotDatabase.SteamGuardAccount == null) {
|
||||||
TwoFactorAuth = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
|
TwoFactorAuth = Program.GetUserInput(Program.EUserInputType.TwoFactorAuthentication, BotName);
|
||||||
|
if (string.IsNullOrEmpty(TwoFactorAuth)) {
|
||||||
|
Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
TwoFactorAuth = BotDatabase.SteamGuardAccount.GenerateSteamGuardCode();
|
TwoFactorAuth = BotDatabase.SteamGuardAccount.GenerateSteamGuardCode();
|
||||||
}
|
}
|
||||||
@@ -1536,11 +1549,16 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
if (string.IsNullOrEmpty(BotConfig.SteamParentalPIN)) {
|
if (string.IsNullOrEmpty(BotConfig.SteamParentalPIN)) {
|
||||||
BotConfig.SteamParentalPIN = Program.GetUserInput(Program.EUserInputType.SteamParentalPIN, BotName);
|
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)) {
|
if (!ArchiWebHandler.Init(SteamClient, callback.WebAPIUserNonce, BotConfig.SteamParentalPIN)) {
|
||||||
await RestartIfRunning().ConfigureAwait(false);
|
if (!await RefreshSession().ConfigureAwait(false)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BotConfig.DismissInventoryNotifications) {
|
if (BotConfig.DismissInventoryNotifications) {
|
||||||
@@ -1592,35 +1610,37 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
int fileSize;
|
||||||
int fileSize;
|
byte[] sentryHash;
|
||||||
byte[] sentryHash;
|
|
||||||
|
|
||||||
using (FileStream fileStream = File.Open(SentryFile, FileMode.OpenOrCreate, FileAccess.ReadWrite)) {
|
using (FileStream fileStream = File.Open(SentryFile, FileMode.OpenOrCreate, FileAccess.ReadWrite)) {
|
||||||
fileStream.Seek(callback.Offset, SeekOrigin.Begin);
|
fileStream.Seek(callback.Offset, SeekOrigin.Begin);
|
||||||
fileStream.Write(callback.Data, 0, callback.BytesToWrite);
|
fileStream.Write(callback.Data, 0, callback.BytesToWrite);
|
||||||
fileSize = (int) fileStream.Length;
|
fileSize = (int) fileStream.Length;
|
||||||
|
|
||||||
fileStream.Seek(0, SeekOrigin.Begin);
|
fileStream.Seek(0, SeekOrigin.Begin);
|
||||||
using (SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider()) {
|
using (SHA1CryptoServiceProvider sha = new SHA1CryptoServiceProvider()) {
|
||||||
sentryHash = sha.ComputeHash(fileStream);
|
sentryHash = sha.ComputeHash(fileStream);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Inform the steam servers that we're accepting this sentry file
|
// Inform the steam servers that we're accepting this sentry file
|
||||||
SteamUser.SendMachineAuthResponse(new SteamUser.MachineAuthDetails {
|
SteamUser.SendMachineAuthResponse(new SteamUser.MachineAuthDetails {
|
||||||
JobID = callback.JobID,
|
JobID = callback.JobID,
|
||||||
FileName = callback.FileName,
|
FileName = callback.FileName,
|
||||||
BytesWritten = callback.BytesToWrite,
|
BytesWritten = callback.BytesToWrite,
|
||||||
FileSize = fileSize,
|
FileSize = fileSize,
|
||||||
Offset = callback.Offset,
|
Offset = callback.Offset,
|
||||||
Result = EResult.OK,
|
Result = EResult.OK,
|
||||||
LastError = 0,
|
LastError = 0,
|
||||||
OneTimePassword = callback.OneTimePassword,
|
OneTimePassword = callback.OneTimePassword,
|
||||||
SentryFileHash = sentryHash,
|
SentryFileHash = sentryHash
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
}
|
||||||
Logging.LogGenericException(e, BotName);
|
|
||||||
|
private void OnWebAPIUserNonce(SteamUser.WebAPIUserNonceCallback callback) {
|
||||||
|
if (callback == null) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,14 +99,14 @@ namespace ArchiSteamFarm {
|
|||||||
internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint>() { 0 };
|
internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint>() { 0 };
|
||||||
|
|
||||||
|
|
||||||
internal static BotConfig Load(string path) {
|
internal static BotConfig Load(string filePath) {
|
||||||
if (string.IsNullOrEmpty(path) || !File.Exists(path)) {
|
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
BotConfig botConfig;
|
BotConfig botConfig;
|
||||||
try {
|
try {
|
||||||
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(path));
|
botConfig = JsonConvert.DeserializeObject<BotConfig>(File.ReadAllText(filePath));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logging.LogGenericException(e);
|
Logging.LogGenericException(e);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
Bot = bot;
|
Bot = bot;
|
||||||
|
|
||||||
if (Program.GlobalConfig.IdleFarmingPeriod > 0) {
|
if (Timer == null && Program.GlobalConfig.IdleFarmingPeriod > 0) {
|
||||||
Timer = new Timer(
|
Timer = new Timer(
|
||||||
async e => await CheckGamesForFarming().ConfigureAwait(false),
|
async e => await CheckGamesForFarming().ConfigureAwait(false),
|
||||||
null,
|
null,
|
||||||
@@ -211,10 +211,6 @@ namespace ArchiSteamFarm {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logging.LogGenericInfo("Checking badges...", Bot.BotName);
|
Logging.LogGenericInfo("Checking badges...", Bot.BotName);
|
||||||
|
|
||||||
// Find the number of badge pages
|
// Find the number of badge pages
|
||||||
@@ -229,8 +225,11 @@ namespace ArchiSteamFarm {
|
|||||||
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
|
HtmlNodeCollection htmlNodeCollection = htmlDocument.DocumentNode.SelectNodes("//a[@class='pagelink']");
|
||||||
if (htmlNodeCollection != null && htmlNodeCollection.Count > 0) {
|
if (htmlNodeCollection != null && htmlNodeCollection.Count > 0) {
|
||||||
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
|
HtmlNode htmlNode = htmlNodeCollection[htmlNodeCollection.Count - 1];
|
||||||
if (!byte.TryParse(htmlNode.InnerText, out maxPages)) {
|
string lastPage = htmlNode.InnerText;
|
||||||
maxPages = 1; // Should never happen
|
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);
|
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
|
||||||
List<Task> tasks = new List<Task>(maxPages - 1);
|
List<Task> tasks = new List<Task>(maxPages - 1);
|
||||||
for (byte page = 2; page <= maxPages; page++) {
|
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));
|
tasks.Add(CheckPage(currentPage));
|
||||||
}
|
}
|
||||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GamesToFarm.Count == 0) {
|
return GamesToFarm.Count > 0;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckPage(HtmlDocument htmlDocument) {
|
private void CheckPage(HtmlDocument htmlDocument) {
|
||||||
@@ -362,7 +357,6 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
HtmlNode htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//span[@class='progress_info_bold']");
|
||||||
if (htmlNode == null) {
|
if (htmlNode == null) {
|
||||||
await Bot.ArchiWebHandler.ReconnectIfNeeded().ConfigureAwait(false);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,9 +36,10 @@ namespace ArchiSteamFarm {
|
|||||||
Experimental
|
Experimental
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal const byte DefaultHttpTimeout = 60;
|
||||||
|
|
||||||
private const byte DefaultMaxFarmingTime = 10;
|
private const byte DefaultMaxFarmingTime = 10;
|
||||||
private const byte DefaultFarmingDelay = 5;
|
private const byte DefaultFarmingDelay = 5;
|
||||||
private const byte DefaultHttpTimeout = 60;
|
|
||||||
private const ushort DefaultWCFPort = 1242;
|
private const ushort DefaultWCFPort = 1242;
|
||||||
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
|
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
|
||||||
|
|
||||||
@@ -48,6 +49,9 @@ namespace ArchiSteamFarm {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal bool Debug { get; private set; } = false;
|
internal bool Debug { get; private set; } = false;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
internal bool Headless { get; private set; } = false;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
internal bool AutoUpdates { get; private set; } = true;
|
internal bool AutoUpdates { get; private set; } = true;
|
||||||
|
|
||||||
@@ -117,6 +121,10 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (globalConfig == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// SK2 supports only TCP and UDP steam protocols
|
// SK2 supports only TCP and UDP steam protocols
|
||||||
// Ensure that user can't screw this up
|
// Ensure that user can't screw this up
|
||||||
switch (globalConfig.SteamProtocol) {
|
switch (globalConfig.SteamProtocol) {
|
||||||
|
|||||||
@@ -247,10 +247,15 @@ namespace ArchiSteamFarm {
|
|||||||
Restart();
|
Restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void Exit(int exitCode = 0) {
|
||||||
|
WCF.StopServer();
|
||||||
|
Environment.Exit(exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
internal static void Restart() {
|
internal static void Restart() {
|
||||||
try {
|
try {
|
||||||
Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs().Skip(1)));
|
Process.Start(ExecutableFile, string.Join(" ", Environment.GetCommandLineArgs().Skip(1)));
|
||||||
Environment.Exit(0);
|
Exit();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logging.LogGenericException(e);
|
Logging.LogGenericException(e);
|
||||||
}
|
}
|
||||||
@@ -269,6 +274,11 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GlobalConfig.Headless) {
|
||||||
|
Logging.LogGenericWarning("Received a request for user input, but process is running in headless mode!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
string result;
|
string result;
|
||||||
lock (ConsoleLock) {
|
lock (ConsoleLock) {
|
||||||
ConsoleIsBusy = true;
|
ConsoleIsBusy = true;
|
||||||
@@ -337,14 +347,14 @@ namespace ArchiSteamFarm {
|
|||||||
if (GlobalConfig == null) {
|
if (GlobalConfig == null) {
|
||||||
Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.json exists and is valid!");
|
Logging.LogGenericError("Global config could not be loaded, please make sure that ASF.json exists and is valid!");
|
||||||
Thread.Sleep(5000);
|
Thread.Sleep(5000);
|
||||||
Environment.Exit(1);
|
Exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalDatabase = GlobalDatabase.Load();
|
GlobalDatabase = GlobalDatabase.Load();
|
||||||
if (GlobalDatabase == null) {
|
if (GlobalDatabase == null) {
|
||||||
Logging.LogGenericError("Global database could not be loaded!");
|
Logging.LogGenericError("Global database could not be loaded!");
|
||||||
Thread.Sleep(5000);
|
Thread.Sleep(5000);
|
||||||
Environment.Exit(1);
|
Exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiWebHandler.Init();
|
ArchiWebHandler.Init();
|
||||||
@@ -445,7 +455,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
// If we ran ASF as a client, we're done by now
|
// If we ran ASF as a client, we're done by now
|
||||||
if (Mode == EMode.Client) {
|
if (Mode == EMode.Client) {
|
||||||
Environment.Exit(0);
|
Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// From now on it's server mode
|
// From now on it's server mode
|
||||||
@@ -454,7 +464,7 @@ namespace ArchiSteamFarm {
|
|||||||
if (!Directory.Exists(ConfigDirectory)) {
|
if (!Directory.Exists(ConfigDirectory)) {
|
||||||
Logging.LogGenericError("Config directory doesn't exist!");
|
Logging.LogGenericError("Config directory doesn't exist!");
|
||||||
Thread.Sleep(5000);
|
Thread.Sleep(5000);
|
||||||
Environment.Exit(1);
|
Exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckForUpdate().Wait();
|
CheckForUpdate().Wait();
|
||||||
@@ -494,7 +504,7 @@ namespace ArchiSteamFarm {
|
|||||||
ShutdownResetEvent.WaitOne();
|
ShutdownResetEvent.WaitOne();
|
||||||
|
|
||||||
// We got a signal to shutdown
|
// We got a signal to shutdown
|
||||||
Environment.Exit(0);
|
Exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("2.0.2.1")]
|
[assembly: AssemblyVersion("2.0.2.5")]
|
||||||
[assembly: AssemblyFileVersion("2.0.2.1")]
|
[assembly: AssemblyFileVersion("2.0.2.5")]
|
||||||
|
|||||||
@@ -84,19 +84,12 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Task> tasks = new List<Task>();
|
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
|
||||||
foreach (Steam.TradeOffer tradeOffer in tradeOffers) {
|
await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
|
private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
|
||||||
if (tradeOffer == null) {
|
if (tradeOffer == null || tradeOffer.trade_offer_state != Steam.TradeOffer.ETradeOfferState.Active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,12 +98,33 @@ namespace ArchiSteamFarm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tradeOffer.items_to_give.Count == 0 || tradeOffer.OtherSteamID64 == Bot.BotConfig.SteamMasterID) {
|
if (ShouldAcceptTrade(tradeOffer)) {
|
||||||
Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
|
Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName);
|
||||||
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
|
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false);
|
||||||
} else {
|
} else {
|
||||||
Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,14 +22,25 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ArchiSteamFarm {
|
namespace ArchiSteamFarm {
|
||||||
internal static class Utilities {
|
internal static class Utilities {
|
||||||
internal static void Forget(this Task task) { }
|
internal static void Forget(this Task task) { }
|
||||||
|
|
||||||
internal static async Task SleepAsync(int miliseconds) {
|
internal static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
|
||||||
await Task.Delay(miliseconds).ConfigureAwait(false);
|
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) {
|
internal static uint GetCharCountInString(string s, char c) {
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ namespace ArchiSteamFarm {
|
|||||||
internal static void Init() {
|
internal static void Init() {
|
||||||
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
|
if (string.IsNullOrEmpty(Program.GlobalConfig.WCFHostname)) {
|
||||||
Program.GlobalConfig.WCFHostname = Program.GetUserInput(Program.EUserInputType.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";
|
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
|
||||||
|
|||||||
@@ -184,6 +184,10 @@ namespace ArchiSteamFarm {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.StartsWith("https://") && Program.GlobalConfig.ForceHttp) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
HttpResponseMessage responseMessage;
|
HttpResponseMessage responseMessage;
|
||||||
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
|
using (HttpRequestMessage requestMessage = new HttpRequestMessage(httpMethod, request)) {
|
||||||
if (data != null && data.Count > 0) {
|
if (data != null && data.Count > 0) {
|
||||||
@@ -220,7 +224,7 @@ namespace ArchiSteamFarm {
|
|||||||
|
|
||||||
if (!responseMessage.IsSuccessStatusCode) {
|
if (!responseMessage.IsSuccessStatusCode) {
|
||||||
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
|
||||||
Logging.LogGenericError("Request: " + request + "failed!");
|
Logging.LogGenericError("Request: " + request + " failed!");
|
||||||
Logging.LogGenericError("Status code: " + responseMessage.StatusCode);
|
Logging.LogGenericError("Status code: " + responseMessage.StatusCode);
|
||||||
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false));
|
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Debug": false,
|
"Debug": false,
|
||||||
|
"Headless": false,
|
||||||
"AutoUpdates": true,
|
"AutoUpdates": true,
|
||||||
"UpdateChannel": 1,
|
"UpdateChannel": 1,
|
||||||
"SteamProtocol": 6,
|
"SteamProtocol": 6,
|
||||||
|
|||||||
@@ -54,9 +54,9 @@ namespace ConfigGenerator {
|
|||||||
internal virtual void Remove() {
|
internal virtual void Remove() {
|
||||||
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
||||||
lock (FilePath) {
|
lock (FilePath) {
|
||||||
foreach (var configFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
||||||
try {
|
try {
|
||||||
File.Delete(configFile);
|
File.Delete(botFile);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logging.LogGenericException(e);
|
Logging.LogGenericException(e);
|
||||||
}
|
}
|
||||||
@@ -72,9 +72,9 @@ namespace ConfigGenerator {
|
|||||||
|
|
||||||
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
string queryPath = Path.GetFileNameWithoutExtension(FilePath);
|
||||||
lock (FilePath) {
|
lock (FilePath) {
|
||||||
foreach (var file in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
foreach (string botFile in Directory.EnumerateFiles(Program.ConfigDirectory, queryPath + ".*")) {
|
||||||
try {
|
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) {
|
} catch (Exception e) {
|
||||||
Logging.LogGenericException(e);
|
Logging.LogGenericException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ namespace ConfigGenerator {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public List<uint> GamesPlayedWhileIdle { get; set; } = new List<uint>();
|
public List<uint> GamesPlayedWhileIdle { get; set; } = new List<uint>();
|
||||||
|
|
||||||
|
|
||||||
internal static BotConfig Load(string filePath) {
|
internal static BotConfig Load(string filePath) {
|
||||||
if (string.IsNullOrEmpty(filePath)) {
|
if (string.IsNullOrEmpty(filePath)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -116,6 +115,10 @@ namespace ConfigGenerator {
|
|||||||
return new BotConfig(filePath);
|
return new BotConfig(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (botConfig == null) {
|
||||||
|
return new BotConfig(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
botConfig.FilePath = filePath;
|
botConfig.FilePath = filePath;
|
||||||
|
|
||||||
return botConfig;
|
return botConfig;
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ namespace ConfigGenerator {
|
|||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool Debug { get; set; } = false;
|
public bool Debug { get; set; } = false;
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
|
public bool Headless { get; set; } = false;
|
||||||
|
|
||||||
[JsonProperty(Required = Required.DisallowNull)]
|
[JsonProperty(Required = Required.DisallowNull)]
|
||||||
public bool AutoUpdates { get; set; } = true;
|
public bool AutoUpdates { get; set; } = true;
|
||||||
|
|
||||||
@@ -120,6 +123,10 @@ namespace ConfigGenerator {
|
|||||||
return new GlobalConfig(filePath);
|
return new GlobalConfig(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (globalConfig == null) {
|
||||||
|
return new GlobalConfig(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
globalConfig.FilePath = filePath;
|
globalConfig.FilePath = filePath;
|
||||||
|
|
||||||
// SK2 supports only TCP and UDP steam protocols
|
// SK2 supports only TCP and UDP steam protocols
|
||||||
|
|||||||
66
cc.sh
Executable file
66
cc.sh
Executable 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
28
run.sh
Executable 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"
|
||||||
Reference in New Issue
Block a user