Compare commits

...

25 Commits

Author SHA1 Message Date
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
JustArchi
e337bd3856 Derp 2016-03-27 23:38:49 +02:00
JustArchi
cc47c0a764 Add new property to config generator 2016-03-27 23:27:02 +02:00
JustArchi
22e4c0cd40 Misc 2016-03-27 23:18:01 +02:00
JustArchi
20b47e1787 Correct config property 2016-03-27 23:09:17 +02:00
JustArchi
941da0658d Misc 2016-03-27 23:07:37 +02:00
JustArchi
8048d4a7aa Add a feature of accepting steam gifts, closes #18 2016-03-27 23:07:00 +02:00
JustArchi
2554794daa Code review 2016-03-27 15:08:43 +02:00
JustArchi
4fcee90b99 Code review 2016-03-26 22:51:19 +01:00
JustArchi
761d73eb90 Remove old converter, closes #153
Decided to remove it faster
2016-03-26 22:23:35 +01:00
JustArchi
c163e5e2f3 Bump 2016-03-26 22:13:24 +01:00
JustArchi
8fd41cc587 Fix broken bot databases 2016-03-26 22:12:55 +01:00
JustArchi
f195563ba5 Misc 2016-03-26 19:39:46 +01:00
25 changed files with 436 additions and 441 deletions

View File

@@ -90,10 +90,7 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>

View File

@@ -32,19 +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);
private ulong SteamID;
private DateTime LastSessionRefreshCheck = DateTime.MinValue;
internal static void Init() {
Timeout = Program.GlobalConfig.HttpTimeout * 1000;
@@ -59,14 +62,14 @@ namespace ArchiSteamFarm {
Bot = bot;
}
internal async Task<bool> Init(SteamClient steamClient, string webAPIUserNonce, string parentalPin) {
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);
@@ -93,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,
@@ -121,15 +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;
}
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);
@@ -144,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++) {
@@ -265,6 +287,10 @@ namespace ArchiSteamFarm {
return false;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
@@ -295,6 +321,10 @@ namespace ArchiSteamFarm {
return false;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
@@ -322,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);
@@ -386,6 +390,10 @@ namespace ArchiSteamFarm {
return false;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
@@ -441,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) {
@@ -459,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) {
@@ -477,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) {
@@ -494,9 +510,41 @@ namespace ArchiSteamFarm {
return true;
}
private async Task UnlockParentalAccount(string parentalPin) {
internal async Task<bool> AcceptGift(ulong gid) {
if (gid == 0) {
return false;
}
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return false;
}
string sessionID;
if (!Cookie.TryGetValue("sessionid", out sessionID)) {
return false;
}
string request = SteamCommunityURL + "/gifts/" + gid + "/acceptunpack";
Dictionary<string, string> data = new Dictionary<string, string>(1) {
{ "sessionid", sessionID }
};
HttpResponseMessage response = null;
for (byte i = 0; i < WebBrowser.MaxRetries && response == null; i++) {
response = await WebBrowser.UrlPost(request, data, Cookie).ConfigureAwait(false);
}
if (response == null) {
Logging.LogGenericWTF("Request failed even after " + WebBrowser.MaxRetries + " tries", Bot.BotName);
return false;
}
return true;
}
private async Task<bool> UnlockParentalAccount(string parentalPin) {
if (string.IsNullOrEmpty(parentalPin) || parentalPin.Equals("0")) {
return;
return true;
}
Logging.LogGenericInfo("Unlocking parental account...", Bot.BotName);
@@ -514,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) {
@@ -537,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

@@ -60,7 +60,7 @@ namespace ArchiSteamFarm {
private readonly SteamUser SteamUser;
private readonly Trading Trading;
internal bool KeepRunning { get; private set; } = false;
internal bool KeepRunning { get; private set; }
private bool InvalidPassword, LoggedInElsewhere;
private string AuthCode, TwoFactorAuth;
@@ -113,55 +113,22 @@ namespace ArchiSteamFarm {
string botPath = Path.Combine(Program.ConfigDirectory, botName);
// CONVERSION START
if (File.Exists(botPath + ".xml")) {
BotConfig = BotConfig.LoadOldFormat(botPath + ".xml");
if (BotConfig == null) {
return;
}
if (BotConfig.Convert(botPath + ".json")) {
try {
File.Delete(botPath + ".xml");
} catch (Exception e) {
Logging.LogGenericException(e, botName);
return;
}
}
}
// CONVERSION END
BotConfig = BotConfig.Load(botPath + ".json");
if (BotConfig == null) {
Logging.LogGenericError("Your config for this bot instance is invalid, it won't run!", botName);
Logging.LogGenericError("Your bot config is invalid, refusing to start this bot instance!", botName);
return;
}
// CONVERSION START
if (File.Exists(botPath + ".key")) {
BotDatabase = BotDatabase.Load(botPath + ".db");
try {
BotDatabase.LoginKey = File.ReadAllText(botPath + ".key");
File.Delete(botPath + ".key");
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
}
}
if (File.Exists(botPath + ".auth")) {
BotDatabase = BotDatabase.Load(botPath + ".db");
try {
BotDatabase.SteamGuardAccount = JsonConvert.DeserializeObject<SteamGuardAccount>(File.ReadAllText(botPath + ".auth"));
File.Delete(botPath + ".auth");
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
}
}
// CONVERSION END
if (!BotConfig.Enabled) {
return;
}
BotDatabase = BotDatabase.Load(botPath + ".db");
if (BotDatabase == null) {
Logging.LogGenericError("Bot database could not be loaded, refusing to start this bot instance!", botName);
return;
}
bool alreadyExists;
lock (Bots) {
alreadyExists = Bots.ContainsKey(botName);
@@ -174,7 +141,6 @@ namespace ArchiSteamFarm {
return;
}
BotDatabase = BotDatabase.Load(botPath + ".db");
SentryFile = botPath + ".bin";
if (BotDatabase.SteamGuardAccount == null) {
@@ -206,6 +172,7 @@ namespace ArchiSteamFarm {
SteamApps = SteamClient.GetHandler<SteamApps>();
CallbackManager.Subscribe<SteamApps.FreeLicenseCallback>(OnFreeLicense);
CallbackManager.Subscribe<SteamApps.GuestPassListCallback>(OnGuestPassList);
SteamFriends = SteamClient.GetHandler<SteamFriends>();
CallbackManager.Subscribe<SteamFriends.ChatInviteCallback>(OnChatInvite);
@@ -220,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);
@@ -229,7 +197,7 @@ 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),
null,
@@ -238,7 +206,7 @@ namespace ArchiSteamFarm {
);
}
if (BotConfig.SendTradePeriod > 0) {
if (SendItemsTimer == null && BotConfig.SendTradePeriod > 0) {
SendItemsTimer = new Timer(
async e => await ResponseSendTrade(BotConfig.SteamMasterID).ConfigureAwait(false),
null,
@@ -283,7 +251,6 @@ namespace ArchiSteamFarm {
Logging.LogGenericWarning("If issue persists, consider removing and readding ASF 2FA", BotName);
} catch (Exception e) {
Logging.LogGenericException(e, BotName);
return;
}
}
@@ -295,12 +262,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) {
@@ -573,7 +551,7 @@ namespace ArchiSteamFarm {
}
}
result.Append("There are " + runningBotsCount + "/" + Bots.Count + "bots running.");
result.Append("There are " + runningBotsCount + "/" + Bots.Count + " bots running.");
return result.ToString();
}
@@ -775,14 +753,7 @@ namespace ArchiSteamFarm {
continue;
}
ArchiHandler.PurchaseResponseCallback result;
try {
result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
} catch (Exception e) {
Logging.LogGenericException(e, currentBot.BotName);
break;
}
ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
if (result == null) {
break;
}
@@ -826,13 +797,7 @@ namespace ArchiSteamFarm {
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
}
ArchiHandler.PurchaseResponseCallback otherResult = await bot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
if (otherResult == null) {
break; // We're done with this key
@@ -945,11 +910,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;
}
@@ -1163,7 +1125,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);
}
}
}
@@ -1232,6 +1198,9 @@ namespace ArchiSteamFarm {
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);
}
@@ -1382,6 +1351,32 @@ namespace ArchiSteamFarm {
}
}
private async void OnGuestPassList(SteamApps.GuestPassListCallback callback) {
if (callback == null || callback.Result != EResult.OK || callback.CountGuestPassesToRedeem == 0 || callback.GuestPasses.Count == 0 || !BotConfig.AcceptGifts) {
return;
}
bool acceptedSomething = false;
foreach (KeyValue guestPass in callback.GuestPasses) {
ulong gid = guestPass["gid"].AsUnsignedLong();
if (gid == 0) {
continue;
}
Logging.LogGenericInfo("Accepting gift: " + gid + "...", BotName);
if (await ArchiWebHandler.AcceptGift(gid).ConfigureAwait(false)) {
acceptedSomething = true;
Logging.LogGenericInfo("Success!", BotName);
} else {
Logging.LogGenericInfo("Failed!", BotName);
}
}
if (acceptedSomething) {
CardsFarmer.RestartFarming().Forget();
}
}
private void OnChatInvite(SteamFriends.ChatInviteCallback callback) {
if (callback == null || !IsMaster(callback.PatronID)) {
return;
@@ -1536,9 +1531,10 @@ namespace ArchiSteamFarm {
BotConfig.SteamParentalPIN = Program.GetUserInput(Program.EUserInputType.SteamParentalPIN, BotName);
}
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) {
@@ -1590,35 +1586,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

@@ -26,7 +26,6 @@ using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
namespace ArchiSteamFarm {
internal sealed class BotConfig {
@@ -66,6 +65,9 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)]
internal bool HandleOfflineMessages { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool AcceptGifts { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool ForwardKeysToOtherBots { get; private set; } = false;
@@ -97,14 +99,14 @@ namespace ArchiSteamFarm {
internal HashSet<uint> GamesPlayedWhileIdle { get; private set; } = new HashSet<uint>() { 0 };
internal static BotConfig Load(string path) {
if (!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;
@@ -113,146 +115,7 @@ namespace ArchiSteamFarm {
return botConfig;
}
// TODO: This should be removed soon
internal static BotConfig LoadOldFormat(string path) {
if (!File.Exists(path)) {
return null;
}
BotConfig botConfig = new BotConfig();
try {
using (XmlReader reader = XmlReader.Create(path)) {
while (reader.Read()) {
if (reader.NodeType != XmlNodeType.Element) {
continue;
}
string key = reader.Name;
if (string.IsNullOrEmpty(key)) {
continue;
}
string value = reader.GetAttribute("value");
if (string.IsNullOrEmpty(value)) {
continue;
}
switch (key) {
case "Enabled":
botConfig.Enabled = bool.Parse(value);
break;
case "SteamLogin":
botConfig.SteamLogin = value;
break;
case "SteamPassword":
botConfig.SteamPassword = value;
break;
case "SteamApiKey":
botConfig.SteamApiKey = value;
break;
case "SteamTradeToken":
botConfig.SteamTradeToken = value;
break;
case "SteamParentalPIN":
botConfig.SteamParentalPIN = value;
break;
case "SteamMasterID":
botConfig.SteamMasterID = ulong.Parse(value);
break;
case "SteamMasterClanID":
botConfig.SteamMasterClanID = ulong.Parse(value);
break;
case "StartOnLaunch":
botConfig.StartOnLaunch = bool.Parse(value);
break;
case "UseAsfAsMobileAuthenticator":
botConfig.UseAsfAsMobileAuthenticator = bool.Parse(value);
break;
case "CardDropsRestricted":
botConfig.CardDropsRestricted = bool.Parse(value);
break;
case "FarmOffline":
botConfig.FarmOffline = bool.Parse(value);
break;
case "HandleOfflineMessages":
botConfig.HandleOfflineMessages = bool.Parse(value);
break;
case "ForwardKeysToOtherBots":
botConfig.ForwardKeysToOtherBots = bool.Parse(value);
break;
case "DistributeKeys":
botConfig.DistributeKeys = bool.Parse(value);
break;
case "ShutdownOnFarmingFinished":
botConfig.ShutdownOnFarmingFinished = bool.Parse(value);
break;
case "SendOnFarmingFinished":
botConfig.SendOnFarmingFinished = bool.Parse(value);
break;
case "SendTradePeriod":
botConfig.SendTradePeriod = byte.Parse(value);
break;
case "GamesPlayedWhileIdle":
botConfig.GamesPlayedWhileIdle.Clear();
foreach (string appID in value.Split(',')) {
botConfig.GamesPlayedWhileIdle.Add(uint.Parse(appID));
}
break;
case "Statistics":
case "Blacklist":
case "SteamNickname":
break;
default:
Logging.LogGenericWarning("Unrecognized config value: " + key + "=" + value);
break;
}
}
}
} catch (Exception e) {
Logging.LogGenericException(e);
Logging.LogGenericError("Your config for this bot instance is invalid, it won't run!");
return null;
}
// Fixups for new format
if (botConfig.SteamLogin != null && botConfig.SteamLogin.Equals("null")) {
botConfig.SteamLogin = null;
}
if (botConfig.SteamPassword != null && botConfig.SteamPassword.Equals("null")) {
botConfig.SteamPassword = null;
}
if (botConfig.SteamApiKey != null && botConfig.SteamApiKey.Equals("null")) {
botConfig.SteamApiKey = null;
}
if (botConfig.SteamParentalPIN != null && botConfig.SteamParentalPIN.Equals("null")) {
botConfig.SteamParentalPIN = null;
}
if (botConfig.SteamTradeToken != null && botConfig.SteamTradeToken.Equals("null")) {
botConfig.SteamTradeToken = null;
}
return botConfig;
}
// This constructor is used only by deserializer
private BotConfig() { }
// TODO: This should be removed soon
internal bool Convert(string path) {
try {
File.WriteAllText(path, JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented));
} catch (Exception e) {
Logging.LogGenericException(e);
return false;
}
Logging.LogGenericWarning("Your config was converted to new ASF V2.0 format");
return true;
}
}
}

View File

@@ -66,6 +66,10 @@ namespace ArchiSteamFarm {
private string FilePath;
internal static BotDatabase Load(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
return null;
}
if (!File.Exists(filePath)) {
return new BotDatabase(filePath);
}
@@ -78,6 +82,10 @@ namespace ArchiSteamFarm {
return null;
}
if (botDatabase == null) {
return null;
}
botDatabase.FilePath = filePath;
return botDatabase;
}

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,32 +225,29 @@ 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
}
}
}
GamesToFarm.Clear();
// Find APPIDs we need to farm
Logging.LogGenericInfo("Checking other pages...", Bot.BotName);
CheckPage(htmlDocument);
List<Task> tasks = new List<Task>(maxPages - 1);
for (byte page = 1; page <= maxPages; page++) {
if (page == 1) {
CheckPage(htmlDocument); // Because we fetched page number 1 already
} else {
byte currentPage = page; // We need a copy of variable being passed when in for loops
if (maxPages > 1) {
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, as loop will proceed before task is launched
tasks.Add(CheckPage(currentPage));
}
}
await Task.WhenAll(tasks).ConfigureAwait(false);
if (GamesToFarm.Count == 0) {
return false;
await Task.WhenAll(tasks).ConfigureAwait(false);
}
return true;
return GamesToFarm.Count > 0;
}
private void CheckPage(HtmlDocument htmlDocument) {
@@ -279,7 +272,26 @@ namespace ArchiSteamFarm {
continue;
}
uint appID = (uint) Utilities.OnlyNumbers(steamLink);
int index = steamLink.LastIndexOf('/');
if (index < 0) {
Logging.LogNullError("index", Bot.BotName);
continue;
}
index++;
if (steamLink.Length <= index) {
Logging.LogNullError("length", Bot.BotName);
continue;
}
steamLink = steamLink.Substring(index);
uint appID;
if (!uint.TryParse(steamLink, out appID)) {
Logging.LogNullError("appID", Bot.BotName);
continue;
}
if (appID == 0) {
Logging.LogNullError("appID", Bot.BotName);
continue;
@@ -301,13 +313,11 @@ namespace ArchiSteamFarm {
continue;
}
hoursString = Regex.Match(hoursString, @"[0-9\.,]+").Value;
float hours = 0;
float hours;
if (string.IsNullOrEmpty(hoursString)) {
hours = 0;
} else {
hours = float.Parse(hoursString, CultureInfo.InvariantCulture);
Match match = Regex.Match(hoursString, @"[0-9\.,]+");
if (match.Success) {
float.TryParse(match.Value, NumberStyles.Number, CultureInfo.InvariantCulture, out hours);
}
GamesToFarm[appID] = hours;
@@ -347,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,12 +36,12 @@ 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 static readonly ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
// This is hardcoded blacklist which should not be possible to change
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280 };
@@ -118,6 +118,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

@@ -45,7 +45,7 @@ namespace ArchiSteamFarm {
}
[JsonProperty(Required = Required.DisallowNull)]
private uint _CellID = 0;
private uint _CellID;
internal static GlobalDatabase Load() {
if (!File.Exists(FilePath)) {

View File

@@ -31,7 +31,7 @@ namespace ArchiSteamFarm {
internal static class Logging {
private static readonly object FileLock = new object();
private static bool LogToFile = false;
private static bool LogToFile;
internal static void Init() {
LogToFile = Program.GlobalConfig.LogToFile;

View File

@@ -481,19 +481,6 @@ namespace ArchiSteamFarm {
}
}
// CONVERSION START
foreach (var configFile in Directory.EnumerateFiles(ConfigDirectory, "*.xml")) {
string botName = Path.GetFileNameWithoutExtension(configFile);
Logging.LogGenericWarning("Found legacy " + botName + ".xml config file, it will now be converted to new ASF V2.0 format!");
Bot bot = new Bot(botName);
if (bot.BotConfig != null && bot.BotConfig.Enabled) {
isRunning = true;
} else {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", botName);
}
}
// CONVERSION END
// Check if we got any bots running
if (!isRunning) {
OnBotShutdown();

View File

@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.0.2.0")]
[assembly: AssemblyFileVersion("2.0.2.0")]
[assembly: AssemblyVersion("2.0.2.2")]
[assembly: AssemblyFileVersion("2.0.2.2")]

View File

@@ -37,7 +37,7 @@ namespace ArchiSteamFarm {
private readonly Bot Bot;
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1);
private byte ParsingTasks = 0;
private byte ParsingTasks;
internal static async Task LimitInventoryRequestsAsync() {
await InventorySemaphore.WaitAsync().ConfigureAwait(false);
@@ -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 tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
await Bot.AcceptConfirmations(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,41 +22,25 @@
*/
using System.Text.RegularExpressions;
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 ulong OnlyNumbers(string inputString) {
if (string.IsNullOrEmpty(inputString)) {
return 0;
internal static Task SleepAsync(int miliseconds) {
if (miliseconds < 0) {
return Task.FromResult(true);
}
string resultString = OnlyNumbersString(inputString);
if (string.IsNullOrEmpty(resultString)) {
return 0;
}
ulong result;
if (!ulong.TryParse(resultString, out result)) {
return 0;
}
return result;
}
internal static string OnlyNumbersString(string text) {
if (string.IsNullOrEmpty(text)) {
return null;
}
return Regex.Replace(text, @"[^\d]", "");
return Task.Delay(miliseconds);
}
internal static uint GetCharCountInString(string s, char c) {

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) {
@@ -219,10 +223,10 @@ namespace ArchiSteamFarm {
}
if (!responseMessage.IsSuccessStatusCode) {
if (Program.GlobalConfig.Debug) {
Logging.LogGenericError("Request: " + request + "failed!");
if (Debugging.IsDebugBuild || Program.GlobalConfig.Debug) {
Logging.LogGenericError("Request: " + request + " failed!");
Logging.LogGenericError("Status code: " + responseMessage.StatusCode);
Logging.LogGenericError("Content: " + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false));
Logging.LogGenericError("Content: " + Environment.NewLine + await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false));
}
return null;
}

View File

@@ -11,6 +11,7 @@
"DismissInventoryNotifications": true,
"FarmOffline": false,
"HandleOfflineMessages": false,
"AcceptGifts": false,
"ForwardKeysToOtherBots": false,
"DistributeKeys": false,
"UseAsfAsMobileAuthenticator": false,

View File

@@ -29,7 +29,7 @@ using System.IO;
namespace ConfigGenerator {
internal class ASFConfig {
internal static HashSet<ASFConfig> ASFConfigs = new HashSet<ASFConfig>();
internal static readonly HashSet<ASFConfig> ASFConfigs = new HashSet<ASFConfig>();
internal string FilePath { get; set; }
@@ -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

@@ -65,6 +65,9 @@ namespace ConfigGenerator {
[JsonProperty(Required = Required.DisallowNull)]
public bool HandleOfflineMessages { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool AcceptGifts { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool ForwardKeysToOtherBots { get; set; } = false;
@@ -95,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;
@@ -113,13 +115,17 @@ namespace ConfigGenerator {
return new BotConfig(filePath);
}
if (botConfig == null) {
return new BotConfig(filePath);
}
botConfig.FilePath = filePath;
return botConfig;
}
// This constructor is used only by deserializer
private BotConfig() : base() { }
private BotConfig() { }
private BotConfig(string filePath) : base(filePath) {
FilePath = filePath;

View File

@@ -44,14 +44,8 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ASFConfig.cs" />

View File

@@ -27,12 +27,9 @@ using System.Windows.Forms;
namespace ConfigGenerator {
internal class ConfigPage : TabPage {
internal readonly ASFConfig ASFConfig;
private EnhancedPropertyGrid EnhancedPropertyGrid;
internal ConfigPage(ASFConfig config) : base() {
internal ConfigPage(ASFConfig config) {
if (config == null) {
return;
}
@@ -41,17 +38,12 @@ namespace ConfigGenerator {
RefreshText();
EnhancedPropertyGrid = new EnhancedPropertyGrid(config);
Controls.Add(EnhancedPropertyGrid);
EnhancedPropertyGrid enhancedPropertyGrid = new EnhancedPropertyGrid(config);
Controls.Add(enhancedPropertyGrid);
}
internal void RefreshText() {
Text = Path.GetFileNameWithoutExtension(ASFConfig.FilePath);
}
private void InitializeComponent() {
SuspendLayout();
ResumeLayout(false);
}
}
}

View File

@@ -26,9 +26,9 @@ using System.Windows.Forms;
namespace ConfigGenerator {
internal sealed class EnhancedPropertyGrid : PropertyGrid {
private ASFConfig ASFConfig;
private readonly ASFConfig ASFConfig;
internal EnhancedPropertyGrid(ASFConfig config) : base() {
internal EnhancedPropertyGrid(ASFConfig config) {
if (config == null) {
return;
}
@@ -66,7 +66,6 @@ namespace ConfigGenerator {
if (globalConfig.SteamOwnerID != 0) {
Tutorial.OnAction(Tutorial.EPhase.GlobalConfigReady);
}
return;
}
}
}

View File

@@ -40,8 +40,7 @@ namespace ConfigGenerator {
private const byte DefaultFarmingDelay = 5;
private const byte DefaultHttpTimeout = 60;
private const ushort DefaultWCFPort = 1242;
private static readonly ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
private const ProtocolType DefaultSteamProtocol = ProtocolType.Tcp;
// This is hardcoded blacklist which should not be possible to change
internal static readonly HashSet<uint> GlobalBlacklist = new HashSet<uint> { 267420, 303700, 335590, 368020, 425280 };
@@ -121,6 +120,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
@@ -161,7 +164,7 @@ namespace ConfigGenerator {
}
// This constructor is used only by deserializer
private GlobalConfig() : base() { }
private GlobalConfig() { }
private GlobalConfig(string filePath) : base(filePath) {
FilePath = filePath;

View File

@@ -23,6 +23,7 @@
*/
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
@@ -31,20 +32,11 @@ namespace ConfigGenerator {
public partial class MainForm : Form {
private const byte ReservedTabs = 3;
private readonly TabPage NewTab = new TabPage { Text = "+" };
private readonly TabPage RemoveTab = new TabPage { Text = "-" };
private readonly TabPage RenameTab = new TabPage { Text = "~" };
private ConfigPage ASFTab;
private TabPage RemoveTab = new TabPage() {
Text = "-",
};
private TabPage RenameTab = new TabPage() {
Text = "~",
};
private TabPage NewTab = new TabPage() {
Text = "+",
};
private TabPage OldTab;
public MainForm() {
@@ -73,7 +65,7 @@ namespace ConfigGenerator {
Tutorial.Enabled = false;
}
MainTab.TabPages.AddRange(new TabPage[] { RemoveTab, RenameTab, NewTab });
MainTab.TabPages.AddRange(new[] { RemoveTab, RenameTab, NewTab });
Tutorial.OnAction(Tutorial.EPhase.Start);
}
@@ -186,7 +178,7 @@ namespace ConfigGenerator {
Tutorial.OnAction(Tutorial.EPhase.Shown);
}
private void MainForm_HelpButtonClicked(object sender, System.ComponentModel.CancelEventArgs e) {
private void MainForm_HelpButtonClicked(object sender, CancelEventArgs e) {
if (sender == null || e == null) {
return;
}

View File

@@ -36,10 +36,7 @@ namespace ConfigGenerator {
private const string ASFDirectory = "ArchiSteamFarm";
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
private static readonly string ExecutableFile = Assembly.Location;
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
private static readonly string ExecutableDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
/// <summary>
/// The main entry point for the application.

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"