Compare commits

..

24 Commits

Author SHA1 Message Date
JustArchi
b8f03abd8b Fix fuckups 2016-04-20 23:48:33 +02:00
JustArchi
47f846540b Closes #200, thanks to @GUiHKX 2016-04-20 23:44:20 +02:00
JustArchi
bc14713079 Bump 2016-04-20 23:22:16 +02:00
JustArchi
770a8fee66 But keep trading only of cards + foils 2016-04-20 23:16:15 +02:00
JustArchi
1df9af08e6 Bugfixes + other types for STM 2016-04-20 23:02:02 +02:00
JustArchi
27464f6120 Add recent trades optimization 2016-04-20 22:19:31 +02:00
JustArchi
dfd45c6e25 Bump 2016-04-20 21:40:08 +02:00
JustArchi
adc1759cee Misc 2016-04-20 21:34:40 +02:00
JustArchi
88369ec71a Put massive amount of work into STM integration, #84 2016-04-20 21:27:57 +02:00
JustArchi
a5d8ae53dd Bump 2016-04-19 19:54:39 +02:00
JustArchi
cd7b65868a Misc 2016-04-19 12:24:07 +02:00
JustArchi
7575704a01 Misc 2016-04-19 12:23:24 +02:00
JustArchi
b6ce8f435c Use more optimized Slim manual reset events 2016-04-18 18:43:58 +02:00
JustArchi
565acca9fb Add AutoRestart property 2016-04-18 18:38:48 +02:00
JustArchi
610954ba73 Alter logic of key distribution, closes #199 2016-04-18 18:01:49 +02:00
JustArchi
74a748b03f Bump 2016-04-17 00:47:30 +02:00
JustArchi
585a075ec9 Closes #198
It was possible that we initiated a loop for bot that was connected, and it got disconnected shortly after, which could result in infinite loop if DistributeKeys was disabled (and nothing would change that bot to other one)
2016-04-17 00:36:38 +02:00
JustArchi
891d40afe1 Fix potential bug found by zinnerz
We might !stop account waiting in invalid password or game playing condition, which will then initiate connect without checking if it's still valid to do so
2016-04-16 19:24:04 +02:00
JustArchi
387f0dd1c7 Bump 2016-04-15 21:33:58 +02:00
JustArchi
8b4d3c219c Remove GUI app from final zip until I'm happy with the way how it works 2016-04-15 21:29:27 +02:00
JustArchi
365877ec89 Misc 2016-04-15 15:08:50 +02:00
JustArchi
f03a43d573 Misc 2016-04-15 00:32:55 +02:00
JustArchi
d15a9cbfca Misc 2016-04-15 00:18:24 +02:00
JustArchi
acfad624fb Bump 2016-04-14 22:44:35 +02:00
16 changed files with 559 additions and 166 deletions

View File

@@ -28,7 +28,6 @@ using SteamKit2;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
@@ -222,7 +221,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
internal List<Steam.TradeOffer> GetTradeOffers() { internal HashSet<Steam.TradeOffer> GetTradeOffers() {
if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) { if (string.IsNullOrEmpty(Bot.BotConfig.SteamApiKey)) {
return null; return null;
} }
@@ -236,6 +235,7 @@ namespace ArchiSteamFarm {
response = iEconService.GetTradeOffers( response = iEconService.GetTradeOffers(
get_received_offers: 1, get_received_offers: 1,
active_only: 1, active_only: 1,
get_descriptions: 1,
secure: !Program.GlobalConfig.ForceHttp secure: !Program.GlobalConfig.ForceHttp
); );
} catch (Exception e) { } catch (Exception e) {
@@ -249,33 +249,128 @@ namespace ArchiSteamFarm {
return null; return null;
} }
List<Steam.TradeOffer> result = new List<Steam.TradeOffer>(); Dictionary<Tuple<ulong, ulong>, uint> appIDMap = new Dictionary<Tuple<ulong, ulong>, uint>();
Dictionary<Tuple<ulong, ulong>, Steam.Item.EType> typeMap = new Dictionary<Tuple<ulong, ulong>, Steam.Item.EType>();
foreach (KeyValue description in response["descriptions"].Children) {
ulong classID = description["classid"].AsUnsignedLong();
if (classID == 0) {
continue;
}
ulong instanceID = description["instanceid"].AsUnsignedLong();
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(classID, instanceID);
if (!appIDMap.ContainsKey(key)) {
string hashName = description["market_hash_name"].Value;
if (!string.IsNullOrEmpty(hashName)) {
int index = hashName.IndexOf('-');
if (index < 1) {
continue;
}
uint appID;
if (!uint.TryParse(hashName.Substring(0, index), out appID)) {
continue;
}
appIDMap[key] = appID;
}
}
if (!typeMap.ContainsKey(key)) {
string type = description["type"].Value;
if (!string.IsNullOrEmpty(type)) {
switch (type) {
case "Booster Pack":
typeMap[key] = Steam.Item.EType.BoosterPack;
break;
case "Coupon":
typeMap[key] = Steam.Item.EType.Coupon;
break;
case "Gift":
typeMap[key] = Steam.Item.EType.Gift;
break;
case "Steam Gems":
typeMap[key] = Steam.Item.EType.SteamGems;
break;
default:
if (type.EndsWith("Emoticon", StringComparison.Ordinal)) {
typeMap[key] = Steam.Item.EType.Emoticon;
} else if (type.EndsWith("Foil Trading Card", StringComparison.Ordinal)) {
typeMap[key] = Steam.Item.EType.FoilTradingCard;
} else if (type.EndsWith("Profile Background", StringComparison.Ordinal)) {
typeMap[key] = Steam.Item.EType.ProfileBackground;
} else if (type.EndsWith("Trading Card", StringComparison.Ordinal)) {
typeMap[key] = Steam.Item.EType.TradingCard;
} else {
typeMap[key] = Steam.Item.EType.Unknown;
}
break;
}
}
}
}
HashSet<Steam.TradeOffer> result = new HashSet<Steam.TradeOffer>();
foreach (KeyValue trade in response["trade_offers_received"].Children) { foreach (KeyValue trade in response["trade_offers_received"].Children) {
// TODO: Correct some of these when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released
Steam.TradeOffer tradeOffer = new Steam.TradeOffer { Steam.TradeOffer tradeOffer = new Steam.TradeOffer {
tradeofferid = trade["tradeofferid"].AsString(), TradeOfferID = trade["tradeofferid"].AsUnsignedLong(),
accountid_other = (uint) trade["accountid_other"].AsUnsignedLong(), // TODO: Correct this when SK2 with https://github.com/SteamRE/SteamKit/pull/255 gets released OtherSteamID3 = (uint) trade["accountid_other"].AsUnsignedLong(),
trade_offer_state = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>() State = trade["trade_offer_state"].AsEnum<Steam.TradeOffer.ETradeOfferState>()
}; };
foreach (KeyValue item in trade["items_to_give"].Children) { foreach (KeyValue item in trade["items_to_give"].Children) {
tradeOffer.items_to_give.Add(new Steam.Item { Steam.Item steamItem = new Steam.Item {
appid = item["appid"].AsString(), AppID = (uint) item["appid"].AsUnsignedLong(),
contextid = item["contextid"].AsString(), ContextID = item["contextid"].AsUnsignedLong(),
assetid = item["assetid"].AsString(), AssetID = item["assetid"].AsUnsignedLong(),
classid = item["classid"].AsString(), ClassID = item["classid"].AsUnsignedLong(),
instanceid = item["instanceid"].AsString(), InstanceID = item["instanceid"].AsUnsignedLong(),
amount = item["amount"].AsString(), Amount = (uint) item["amount"].AsUnsignedLong()
}); };
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
uint realAppID;
if (appIDMap.TryGetValue(key, out realAppID)) {
steamItem.RealAppID = realAppID;
}
Steam.Item.EType type;
if (typeMap.TryGetValue(key, out type)) {
steamItem.Type = type;
}
tradeOffer.ItemsToGive.Add(steamItem);
} }
foreach (KeyValue item in trade["items_to_receive"].Children) { foreach (KeyValue item in trade["items_to_receive"].Children) {
tradeOffer.items_to_receive.Add(new Steam.Item { Steam.Item steamItem = new Steam.Item {
appid = item["appid"].AsString(), AppID = (uint) item["appid"].AsUnsignedLong(),
contextid = item["contextid"].AsString(), ContextID = item["contextid"].AsUnsignedLong(),
assetid = item["assetid"].AsString(), AssetID = item["assetid"].AsUnsignedLong(),
classid = item["classid"].AsString(), ClassID = item["classid"].AsUnsignedLong(),
instanceid = item["instanceid"].AsString(), InstanceID = item["instanceid"].AsUnsignedLong(),
amount = item["amount"].AsString(), Amount = (uint) item["amount"].AsUnsignedLong()
}); };
Tuple<ulong, ulong> key = new Tuple<ulong, ulong>(steamItem.ClassID, steamItem.InstanceID);
uint realAppID;
if (appIDMap.TryGetValue(key, out realAppID)) {
steamItem.RealAppID = realAppID;
}
Steam.Item.EType type;
if (typeMap.TryGetValue(key, out type)) {
steamItem.Type = type;
}
tradeOffer.ItemsToReceive.Add(steamItem);
} }
result.Add(tradeOffer); result.Add(tradeOffer);
} }
@@ -300,8 +395,8 @@ namespace ArchiSteamFarm {
string request = SteamCommunityURL + "/gid/" + clanID; string request = SteamCommunityURL + "/gid/" + clanID;
Dictionary<string, string> data = new Dictionary<string, string>(2) { Dictionary<string, string> data = new Dictionary<string, string>(2) {
{"sessionID", sessionID}, { "sessionID", sessionID },
{"action", "join"} { "action", "join" }
}; };
bool result = false; bool result = false;
@@ -336,9 +431,9 @@ namespace ArchiSteamFarm {
string request = referer + "/accept"; string request = referer + "/accept";
Dictionary<string, string> data = new Dictionary<string, string>(3) { Dictionary<string, string> data = new Dictionary<string, string>(3) {
{"sessionid", sessionID}, { "sessionid", sessionID },
{"serverid", "1"}, { "serverid", "1" },
{"tradeofferid", tradeID.ToString()} { "tradeofferid", tradeID.ToString() }
}; };
bool result = false; bool result = false;
@@ -354,14 +449,14 @@ namespace ArchiSteamFarm {
return true; return true;
} }
internal async Task<List<Steam.Item>> GetMyTradableInventory() { internal async Task<HashSet<Steam.Item>> GetMyTradableInventory() {
if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) { if (!await RefreshSessionIfNeeded().ConfigureAwait(false)) {
return null; 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").ConfigureAwait(false); jObject = await WebBrowser.UrlGetToJObject(SteamCommunityURL + "/my/inventory/json/" + Steam.Item.SteamAppID + "/" + Steam.Item.SteamContextID + "?trading=1").ConfigureAwait(false);
} }
if (jObject == null) { if (jObject == null) {
@@ -375,7 +470,7 @@ namespace ArchiSteamFarm {
return null; return null;
} }
List<Steam.Item> result = new List<Steam.Item>(); HashSet<Steam.Item> result = new HashSet<Steam.Item>();
foreach (JToken jToken in jTokens) { foreach (JToken jToken in jTokens) {
try { try {
result.Add(JsonConvert.DeserializeObject<Steam.Item>(jToken.ToString())); result.Add(JsonConvert.DeserializeObject<Steam.Item>(jToken.ToString()));
@@ -387,7 +482,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
internal async Task<bool> SendTradeOffer(List<Steam.Item> inventory, ulong partnerID, string token = null) { internal async Task<bool> SendTradeOffer(HashSet<Steam.Item> inventory, ulong partnerID, string token = null) {
if (inventory == null || inventory.Count == 0 || partnerID == 0) { if (inventory == null || inventory.Count == 0 || partnerID == 0) {
return false; return false;
} }
@@ -402,26 +497,30 @@ namespace ArchiSteamFarm {
return false; return false;
} }
List<Steam.TradeOfferRequest> trades = new List<Steam.TradeOfferRequest>(1 + inventory.Count / Trading.MaxItemsPerTrade); HashSet<Steam.TradeOfferRequest> trades = new HashSet<Steam.TradeOfferRequest>();
Steam.TradeOfferRequest singleTrade = null; Steam.TradeOfferRequest singleTrade = null;
for (ushort i = 0; i < inventory.Count; i++) {
if (i % Trading.MaxItemsPerTrade == 0) { byte itemID = 0;
foreach (Steam.Item item in inventory) {
if (itemID % Trading.MaxItemsPerTrade == 0) {
if (trades.Count >= Trading.MaxTradesPerAccount) { if (trades.Count >= Trading.MaxTradesPerAccount) {
break; break;
} }
singleTrade = new Steam.TradeOfferRequest(); singleTrade = new Steam.TradeOfferRequest();
trades.Add(singleTrade); trades.Add(singleTrade);
itemID = 0;
} }
Steam.Item item = inventory[i]; singleTrade.ItemsToGive.Assets.Add(new Steam.Item() {
singleTrade.me.assets.Add(new Steam.Item() { AppID = Steam.Item.SteamAppID,
appid = "753", ContextID = Steam.Item.SteamContextID,
contextid = "6", Amount = item.Amount,
amount = item.amount, AssetID = item.AssetID
assetid = item.id
}); });
itemID++;
} }
string referer = SteamCommunityURL + "/tradeoffer/new"; string referer = SteamCommunityURL + "/tradeoffer/new";
@@ -429,12 +528,12 @@ namespace ArchiSteamFarm {
foreach (Steam.TradeOfferRequest trade in trades) { foreach (Steam.TradeOfferRequest trade in trades) {
Dictionary<string, string> data = new Dictionary<string, string>(6) { Dictionary<string, string> data = new Dictionary<string, string>(6) {
{"sessionid", sessionID}, { "sessionid", sessionID },
{"serverid", "1"}, { "serverid", "1" },
{"partner", partnerID.ToString()}, { "partner", partnerID.ToString() },
{"tradeoffermessage", "Sent by ASF"}, { "tradeoffermessage", "Sent by ASF" },
{"json_tradeoffer", JsonConvert.SerializeObject(trade)}, { "json_tradeoffer", JsonConvert.SerializeObject(trade) },
{"trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : $"{{\"trade_offer_access_token\":\"{token}\"}}"} { "trade_offer_create_params", string.IsNullOrEmpty(token) ? "" : $"{{\"trade_offer_access_token\":\"{token}\"}}" }
}; };
bool result = false; bool result = false;

View File

@@ -33,6 +33,7 @@ using System.Security.Cryptography;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal sealed class Bot { internal sealed class Bot {
@@ -100,9 +101,8 @@ namespace ArchiSteamFarm {
} }
// Steam keys are offered in many formats: https://support.steampowered.com/kb_article.php?ref=7480-WUSF-3601 // Steam keys are offered in many formats: https://support.steampowered.com/kb_article.php?ref=7480-WUSF-3601
// It's pointless to implement them all, so we'll just do a simple check if key is supposed to be valid // This regex should catch all of them, we can always further extend it in future
// Every valid key, apart from Prey one has at least two dashes return Regex.IsMatch(key, @"[0-9A-Z]{4,5}-[0-9A-Z]{4,5}-[0-9A-Z]{4,5}-?(?:(?:[0-9A-Z]{4,5}-?)?(?:[0-9A-Z]{4,5}))?");
return Utilities.GetCharCountInString(key, '-') >= 2;
} }
internal Bot(string botName) { internal Bot(string botName) {
@@ -463,6 +463,7 @@ namespace ArchiSteamFarm {
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);
if (!InitializeLoginAndPassword()) { if (!InitializeLoginAndPassword()) {
BotDatabase.SteamGuardAccount = null;
return; return;
} }
@@ -473,10 +474,12 @@ namespace ArchiSteamFarm {
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)) { if (string.IsNullOrEmpty(userLogin.TwoFactorCode)) {
BotDatabase.SteamGuardAccount = null;
return; return;
} }
break; break;
default: default:
BotDatabase.SteamGuardAccount = null;
Logging.LogGenericError("Unhandled situation: " + loginResult, BotName); Logging.LogGenericError("Unhandled situation: " + loginResult, BotName);
return; return;
} }
@@ -588,7 +591,7 @@ namespace ArchiSteamFarm {
} }
await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false); await Trading.LimitInventoryRequestsAsync().ConfigureAwait(false);
List<Steam.Item> inventory = await ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false); HashSet<Steam.Item> inventory = await ArchiWebHandler.GetMyTradableInventory().ConfigureAwait(false);
if (inventory == null || inventory.Count == 0) { if (inventory == null || inventory.Count == 0) {
return "Nothing to send, inventory seems empty!"; return "Nothing to send, inventory seems empty!";
@@ -774,12 +777,16 @@ namespace ArchiSteamFarm {
while (!string.IsNullOrEmpty(key) && currentBot != null) { while (!string.IsNullOrEmpty(key) && currentBot != null) {
if (validate && !IsValidCdKey(key)) { if (validate && !IsValidCdKey(key)) {
key = reader.ReadLine(); // Next key key = reader.ReadLine(); // Next key
continue; // Without changing the bot continue; // Keep current bot
} }
if (currentBot.SteamClient.IsConnected) { if (!currentBot.SteamClient.IsConnected) {
currentBot = null; // Either bot will be changed, or loop aborted
} else {
ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false); ArchiHandler.PurchaseResponseCallback result = await currentBot.ArchiHandler.RedeemKey(key).ConfigureAwait(false);
if (result != null) { if (result == null) {
currentBot = null; // Either bot will be changed, or loop aborted
} else {
switch (result.PurchaseResult) { switch (result.PurchaseResult) {
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey: case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.DuplicatedKey:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey: case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.InvalidKey:
@@ -787,7 +794,12 @@ namespace ArchiSteamFarm {
response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + result.PurchaseResult + " | Items: " + string.Join("", result.Items)); response.Append(Environment.NewLine + "<" + currentBot.BotName + "> Key: " + key + " | Status: " + result.PurchaseResult + " | Items: " + string.Join("", result.Items));
key = reader.ReadLine(); // Next key key = reader.ReadLine(); // Next key
break; // Next bot (if needed)
if (result.PurchaseResult == ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OK) {
break; // Next bot (if needed)
} else {
continue; // Keep current bot
}
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.AlreadyOwned: case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.AlreadyOwned:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.BaseGameRequired: case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.BaseGameRequired:
case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OnCooldown: case ArchiHandler.PurchaseResponseCallback.EPurchaseResult.OnCooldown:
@@ -1226,14 +1238,27 @@ namespace ArchiSteamFarm {
string sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName); string sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName);
if (string.IsNullOrEmpty(sms)) { if (string.IsNullOrEmpty(sms)) {
Logging.LogGenericWarning("Aborted!", BotName);
DelinkMobileAuthenticator();
return; return;
} }
AuthenticatorLinker.FinalizeResult finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(sms); AuthenticatorLinker.FinalizeResult finalizeResult;
if (finalizeResult != AuthenticatorLinker.FinalizeResult.Success) { while ((finalizeResult = authenticatorLinker.FinalizeAddAuthenticator(sms)) != AuthenticatorLinker.FinalizeResult.Success) {
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName); switch (finalizeResult) {
DelinkMobileAuthenticator(); case AuthenticatorLinker.FinalizeResult.BadSMSCode:
return; sms = Program.GetUserInput(Program.EUserInputType.SMS, BotName);
if (string.IsNullOrEmpty(sms)) {
Logging.LogGenericWarning("Aborted!", BotName);
DelinkMobileAuthenticator();
return;
}
break;
default:
Logging.LogGenericError("Unhandled situation: " + finalizeResult, BotName);
DelinkMobileAuthenticator();
return;
}
} }
// Ensure that we also save changes made by finalization step (if any) // Ensure that we also save changes made by finalization step (if any)
@@ -1248,13 +1273,13 @@ namespace ArchiSteamFarm {
return false; return false;
} }
bool result = BotDatabase.SteamGuardAccount.DeactivateAuthenticator(); // Try to deactivate authenticator, and assume we're safe to remove if it wasn't fully enrolled yet (even if request fails)
if (BotDatabase.SteamGuardAccount.DeactivateAuthenticator() || !BotDatabase.SteamGuardAccount.FullyEnrolled) {
if (result) {
BotDatabase.SteamGuardAccount = null; BotDatabase.SteamGuardAccount = null;
return true;
} }
return result; return false;
} }
private void JoinMasterChat() { private void JoinMasterChat() {
@@ -1364,10 +1389,6 @@ namespace ArchiSteamFarm {
return; return;
} }
if (!KeepRunning) {
return;
}
if (InvalidPassword) { if (InvalidPassword) {
InvalidPassword = false; InvalidPassword = false;
if (!string.IsNullOrEmpty(BotDatabase.LoginKey)) { // InvalidPassword means usually that login key has expired, if we used it if (!string.IsNullOrEmpty(BotDatabase.LoginKey)) { // InvalidPassword means usually that login key has expired, if we used it
@@ -1389,6 +1410,10 @@ namespace ArchiSteamFarm {
await Utilities.SleepAsync(Program.GlobalConfig.AccountPlayingDelay * 60 * 1000).ConfigureAwait(false); await Utilities.SleepAsync(Program.GlobalConfig.AccountPlayingDelay * 60 * 1000).ConfigureAwait(false);
} }
if (!KeepRunning || SteamClient.IsConnected) {
return;
}
Logging.LogGenericInfo("Reconnecting...", BotName); Logging.LogGenericInfo("Reconnecting...", BotName);
// 2FA tokens are expiring soon, use limiter only when we don't have any pending // 2FA tokens are expiring soon, use limiter only when we don't have any pending

View File

@@ -68,6 +68,9 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal bool AcceptGifts { get; private set; } = false; internal bool AcceptGifts { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
internal bool SteamTradeMatcher { get; private set; } = false;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal bool ForwardKeysToOtherBots { get; private set; } = false; internal bool ForwardKeysToOtherBots { get; private set; } = false;

View File

@@ -37,7 +37,7 @@ namespace ArchiSteamFarm {
internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>(); internal readonly ConcurrentDictionary<uint, float> GamesToFarm = new ConcurrentDictionary<uint, float>();
internal readonly HashSet<uint> CurrentGamesFarming = new HashSet<uint>(); internal readonly HashSet<uint> CurrentGamesFarming = new HashSet<uint>();
private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false); private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false);
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1); private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1);
private readonly Bot Bot; private readonly Bot Bot;
private readonly Timer Timer; private readonly Timer Timer;
@@ -421,7 +421,7 @@ namespace ArchiSteamFarm {
bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false); bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false);
for (ushort farmingTime = 0; farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) { for (ushort farmingTime = 0; farmingTime <= 60 * Program.GlobalConfig.MaxFarmingTime && keepFarming.GetValueOrDefault(true); farmingTime += Program.GlobalConfig.FarmingDelay) {
if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) { if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
success = false; success = false;
break; break;
} }
@@ -452,7 +452,7 @@ namespace ArchiSteamFarm {
bool success = true; bool success = true;
while (maxHour < 2) { while (maxHour < 2) {
if (FarmResetEvent.WaitOne(60 * 1000 * Program.GlobalConfig.FarmingDelay)) { if (FarmResetEvent.Wait(60 * 1000 * Program.GlobalConfig.FarmingDelay)) {
success = false; success = false;
break; break;
} }

View File

@@ -55,6 +55,9 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal bool AutoUpdates { get; private set; } = true; internal bool AutoUpdates { get; private set; } = true;
[JsonProperty(Required = Required.DisallowNull)]
internal bool AutoRestart { get; private set; } = true;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable; internal EUpdateChannel UpdateChannel { get; private set; } = EUpdateChannel.Stable;

View File

@@ -28,40 +28,161 @@ using System.Collections.Generic;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal static class Steam { internal static class Steam {
internal sealed class Item { internal sealed class Item { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_Asset internal const ushort SteamAppID = 753;
[JsonProperty(Required = Required.DisallowNull)] internal const byte SteamContextID = 6;
internal string appid { get; set; }
[JsonProperty(Required = Required.DisallowNull)] internal enum EType : byte {
internal string contextid { get; set; } Unknown,
[JsonProperty(Required = Required.DisallowNull)] BoosterPack,
internal string assetid { get; set; } Coupon,
Gift,
SteamGems,
[JsonProperty(Required = Required.DisallowNull)] Emoticon,
internal string id { FoilTradingCard,
get { return assetid; } ProfileBackground,
set { assetid = value; } TradingCard
} }
[JsonProperty(Required = Required.AllowNull)] internal uint AppID;
internal string classid { get; set; }
[JsonProperty(Required = Required.AllowNull)] [JsonProperty(PropertyName = "appid", Required = Required.DisallowNull)]
internal string instanceid { get; set; } internal string AppIDString {
get {
return AppID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
}
[JsonProperty(Required = Required.Always)] uint result;
internal string amount { get; set; } if (!uint.TryParse(value, out result)) {
return;
}
AppID = result;
}
}
internal ulong ContextID;
[JsonProperty(PropertyName = "contextid", Required = Required.DisallowNull)]
internal string ContextIDString {
get {
return ContextID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
}
uint result;
if (!uint.TryParse(value, out result)) {
return;
}
ContextID = result;
}
}
internal ulong AssetID;
[JsonProperty(PropertyName = "assetid", Required = Required.DisallowNull)]
internal string AssetIDString {
get {
return AssetID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
}
uint result;
if (!uint.TryParse(value, out result)) {
return;
}
AssetID = result;
}
}
[JsonProperty(PropertyName = "id", Required = Required.DisallowNull)]
internal string id {
get { return AssetIDString; }
set { AssetIDString = value; }
}
internal ulong ClassID;
[JsonProperty(PropertyName = "classid", Required = Required.DisallowNull)]
internal string ClassIDString {
get {
return ClassID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
}
uint result;
if (!uint.TryParse(value, out result)) {
return;
}
ClassID = result;
}
}
internal ulong InstanceID;
[JsonProperty(PropertyName = "instanceid", Required = Required.DisallowNull)]
internal string InstanceIDString {
get {
return InstanceID.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
}
uint result;
if (!uint.TryParse(value, out result)) {
return;
}
InstanceID = result;
}
}
internal uint Amount;
[JsonProperty(PropertyName = "amount", Required = Required.Always)]
internal string AmountString {
get {
return Amount.ToString();
}
set {
if (string.IsNullOrEmpty(value)) {
return;
}
uint result;
if (!uint.TryParse(value, out result)) {
return;
}
Amount = result;
}
}
internal uint RealAppID { get; set; }
internal EType Type { get; set; }
} }
internal sealed class ItemList { internal sealed class TradeOffer { // REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
[JsonProperty(Required = Required.Always)]
internal List<Steam.Item> assets { get; } = new List<Steam.Item>();
}
internal sealed class TradeOffer {
// REF: https://developer.valvesoftware.com/wiki/Steam_Web_API/IEconService#CEcon_TradeOffer
internal enum ETradeOfferState : byte { internal enum ETradeOfferState : byte {
Unknown, Unknown,
Invalid, Invalid,
@@ -77,46 +198,152 @@ namespace ArchiSteamFarm {
OnHold OnHold
} }
[JsonProperty(Required = Required.Always)] internal ulong TradeOfferID;
internal string tradeofferid { get; set; }
[JsonProperty(Required = Required.Always)] [JsonProperty(PropertyName = "tradeofferid", Required = Required.Always)]
internal uint accountid_other { get; set; } internal string TradeOfferIDString {
[JsonProperty(Required = Required.Always)]
internal ETradeOfferState trade_offer_state { get; set; }
[JsonProperty(Required = Required.Always)]
internal List<Steam.Item> items_to_give { get; } = new List<Steam.Item>();
[JsonProperty(Required = Required.Always)]
internal List<Steam.Item> items_to_receive { get; } = new List<Steam.Item>();
// Extra
private ulong _OtherSteamID64 = 0;
internal ulong OtherSteamID64 {
get { get {
if (_OtherSteamID64 == 0 && accountid_other != 0) { return TradeOfferID.ToString();
_OtherSteamID64 = new SteamID(accountid_other, EUniverse.Public, EAccountType.Individual).ConvertToUInt64(); }
set {
if (string.IsNullOrEmpty(value)) {
return;
} }
return _OtherSteamID64; ulong result;
if (!ulong.TryParse(value, out result)) {
return;
}
TradeOfferID = result;
}
}
[JsonProperty(PropertyName = "accountid_other", Required = Required.Always)]
internal uint OtherSteamID3 { get; set; }
[JsonProperty(PropertyName = "trade_offer_state", Required = Required.Always)]
internal ETradeOfferState State { get; set; }
[JsonProperty(PropertyName = "items_to_give", Required = Required.Always)]
internal HashSet<Item> ItemsToGive { get; } = new HashSet<Item>();
[JsonProperty(PropertyName = "items_to_receive", Required = Required.Always)]
internal HashSet<Item> ItemsToReceive { get; } = new HashSet<Item>();
// Extra
internal ulong OtherSteamID64 {
get {
if (OtherSteamID3 == 0) {
return 0;
}
return new SteamID(OtherSteamID3, EUniverse.Public, EAccountType.Individual);
}
set {
if (value == 0) {
return;
}
OtherSteamID3 = new SteamID(value).AccountID;
}
}
internal bool IsSteamCardsOnlyTrade {
get {
foreach (Item item in ItemsToGive) {
if (item.AppID != Item.SteamAppID || item.ContextID != Item.SteamContextID || (item.Type != Item.EType.FoilTradingCard && item.Type != Item.EType.TradingCard)) {
return false;
}
}
foreach (Item item in ItemsToReceive) {
if (item.AppID != Item.SteamAppID || item.ContextID != Item.SteamContextID || (item.Type != Item.EType.FoilTradingCard && item.Type != Item.EType.TradingCard)) {
return false;
}
}
return true;
}
}
internal bool IsPotentiallyDupesTrade {
get {
Dictionary<uint, Dictionary<Item.EType, uint>> ItemsToGivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
foreach (Item item in ItemsToGive) {
Dictionary<Item.EType, uint> ItemsPerType;
if (!ItemsToGivePerGame.TryGetValue(item.RealAppID, out ItemsPerType)) {
ItemsPerType = new Dictionary<Item.EType, uint>();
ItemsPerType[item.Type] = item.Amount;
ItemsToGivePerGame[item.RealAppID] = ItemsPerType;
} else {
uint amount;
if (ItemsPerType.TryGetValue(item.Type, out amount)) {
ItemsPerType[item.Type] = amount + item.Amount;
} else {
ItemsPerType[item.Type] = item.Amount;
}
}
}
Dictionary<uint, Dictionary<Item.EType, uint>> ItemsToReceivePerGame = new Dictionary<uint, Dictionary<Item.EType, uint>>();
foreach (Item item in ItemsToReceive) {
Dictionary<Item.EType, uint> ItemsPerType;
if (!ItemsToReceivePerGame.TryGetValue(item.RealAppID, out ItemsPerType)) {
ItemsPerType = new Dictionary<Item.EType, uint>();
ItemsPerType[item.Type] = item.Amount;
ItemsToReceivePerGame[item.RealAppID] = ItemsPerType;
} else {
uint amount;
if (ItemsPerType.TryGetValue(item.Type, out amount)) {
ItemsPerType[item.Type] = amount + item.Amount;
} else {
ItemsPerType[item.Type] = item.Amount;
}
}
}
// Ensure that amount per type and per game matches
foreach (KeyValuePair<uint, Dictionary<Item.EType, uint>> ItemsPerGame in ItemsToGivePerGame) {
Dictionary<Item.EType, uint> otherItemsPerType;
if (!ItemsToReceivePerGame.TryGetValue(ItemsPerGame.Key, out otherItemsPerType)) {
return false;
}
foreach (KeyValuePair<Item.EType, uint> ItemsPerType in ItemsPerGame.Value) {
uint otherAmount;
if (!otherItemsPerType.TryGetValue(ItemsPerType.Key, out otherAmount)) {
return false;
}
if (ItemsPerType.Value != otherAmount) {
return false;
}
}
}
return true;
} }
} }
} }
internal sealed class TradeOfferRequest { internal sealed class TradeOfferRequest {
[JsonProperty(Required = Required.Always)] internal sealed class ItemList {
internal bool newversion { get; } = true; [JsonProperty(PropertyName = "assets", Required = Required.Always)]
internal HashSet<Item> Assets { get; } = new HashSet<Item>();
}
[JsonProperty(Required = Required.Always)] [JsonProperty(PropertyName = "newversion", Required = Required.Always)]
internal int version { get; } = 2; internal bool NewVersion { get; } = true;
[JsonProperty(Required = Required.Always)] [JsonProperty(PropertyName = "version", Required = Required.Always)]
internal Steam.ItemList me { get; } = new Steam.ItemList(); internal int Version { get; } = 2;
[JsonProperty(Required = Required.Always)] [JsonProperty(PropertyName = "me", Required = Required.Always)]
internal Steam.ItemList them { get; } = new Steam.ItemList(); internal ItemList ItemsToGive { get; } = new ItemList();
[JsonProperty(PropertyName = "them", Required = Required.Always)]
internal ItemList ItemsToReceive { get; } = new ItemList();
} }
} }
} }

View File

@@ -65,13 +65,12 @@ namespace ArchiSteamFarm {
private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only private const string GithubReleaseURL = "https://api.github.com/repos/" + GithubRepo + "/releases"; // GitHub API is HTTPS only
private static readonly Assembly Assembly = Assembly.GetExecutingAssembly(); internal static readonly Version Version = Assembly.GetEntryAssembly().GetName().Version;
internal static readonly Version Version = Assembly.GetName().Version;
private static readonly object ConsoleLock = new object(); private static readonly object ConsoleLock = new object();
private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1); private static readonly SemaphoreSlim SteamSemaphore = new SemaphoreSlim(1);
private static readonly ManualResetEvent ShutdownResetEvent = new ManualResetEvent(false); private static readonly ManualResetEventSlim ShutdownResetEvent = new ManualResetEventSlim(false);
private static readonly string ExecutableFile = Assembly.Location; private static readonly string ExecutableFile = Assembly.GetEntryAssembly().Location;
private static readonly string ExecutableName = Path.GetFileName(ExecutableFile); private static readonly string ExecutableName = Path.GetFileName(ExecutableFile);
private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile); private static readonly string ExecutableDirectory = Path.GetDirectoryName(ExecutableFile);
private static readonly WCF WCF = new WCF(); private static readonly WCF WCF = new WCF();
@@ -187,7 +186,7 @@ namespace ArchiSteamFarm {
GitHub.Asset binaryAsset = null; GitHub.Asset binaryAsset = null;
foreach (var asset in releaseResponse.Assets) { foreach (var asset in releaseResponse.Assets) {
if (string.IsNullOrEmpty(asset.Name) || !asset.Name.Equals(ExecutableName)) { if (string.IsNullOrEmpty(asset.Name) || !asset.Name.Equals(ExecutableName, StringComparison.OrdinalIgnoreCase)) {
continue; continue;
} }
@@ -251,10 +250,17 @@ namespace ArchiSteamFarm {
return; return;
} }
Logging.LogGenericInfo("Update process is finished! ASF will now restart itself..."); Logging.LogGenericInfo("Update process finished!");
await Utilities.SleepAsync(5000);
Restart(); if (GlobalConfig.AutoRestart) {
Logging.LogGenericInfo("Restarting...");
await Utilities.SleepAsync(5000).ConfigureAwait(false);
Restart();
} else {
Logging.LogGenericInfo("Exiting...");
await Utilities.SleepAsync(5000).ConfigureAwait(false);
Exit();
}
} }
internal static void Exit(int exitCode = 0) { internal static void Exit(int exitCode = 0) {
@@ -516,7 +522,7 @@ namespace ArchiSteamFarm {
Init(args); Init(args);
// Wait for signal to shutdown // Wait for signal to shutdown
ShutdownResetEvent.WaitOne(); ShutdownResetEvent.Wait();
// We got a signal to shutdown // We got a signal to shutdown
Exit(); Exit();

View File

@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // 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.3.1")] [assembly: AssemblyVersion("2.0.3.7")]
[assembly: AssemblyFileVersion("2.0.3.1")] [assembly: AssemblyFileVersion("2.0.3.7")]

View File

@@ -37,6 +37,7 @@ namespace ArchiSteamFarm {
private readonly Bot Bot; private readonly Bot Bot;
private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1); private readonly SemaphoreSlim TradesSemaphore = new SemaphoreSlim(1);
private readonly HashSet<ulong> RecentlyParsedTrades = new HashSet<ulong>();
private byte ParsingTasks; private byte ParsingTasks;
@@ -79,31 +80,50 @@ namespace ArchiSteamFarm {
TradesSemaphore.Release(); TradesSemaphore.Release();
} }
private async Task ForgetRecentTrade(ulong tradeID) {
await Utilities.SleepAsync(24 * 60 * 60 * 1000).ConfigureAwait(false);
lock (RecentlyParsedTrades) {
RecentlyParsedTrades.Remove(tradeID);
RecentlyParsedTrades.TrimExcess();
}
}
private async Task ParseActiveTrades() { private async Task ParseActiveTrades() {
List<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers(); HashSet<Steam.TradeOffer> tradeOffers = Bot.ArchiWebHandler.GetTradeOffers();
if (tradeOffers == null) { if (tradeOffers == null || tradeOffers.Count == 0) {
return; return;
} }
lock (RecentlyParsedTrades) {
tradeOffers.RemoveWhere(trade => RecentlyParsedTrades.Contains(trade.TradeOfferID));
}
if (tradeOffers.Count == 0) {
return;
}
foreach (Steam.TradeOffer tradeOffer in tradeOffers) {
lock (RecentlyParsedTrades) {
RecentlyParsedTrades.Add(tradeOffer.TradeOfferID);
}
ForgetRecentTrade(tradeOffer.TradeOfferID).Forget();
}
await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false); await tradeOffers.ForEachAsync(ParseTrade).ConfigureAwait(false);
await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false); await Bot.AcceptConfirmations(true, Confirmation.ConfirmationType.Trade).ConfigureAwait(false);
} }
private async Task ParseTrade(Steam.TradeOffer tradeOffer) { private async Task ParseTrade(Steam.TradeOffer tradeOffer) {
if (tradeOffer == null || tradeOffer.trade_offer_state != Steam.TradeOffer.ETradeOfferState.Active) { if (tradeOffer == null || tradeOffer.State != Steam.TradeOffer.ETradeOfferState.Active) {
return;
}
ulong tradeID;
if (!ulong.TryParse(tradeOffer.tradeofferid, out tradeID)) {
return; return;
} }
if (ShouldAcceptTrade(tradeOffer)) { if (ShouldAcceptTrade(tradeOffer)) {
Logging.LogGenericInfo("Accepting trade: " + tradeID, Bot.BotName); Logging.LogGenericInfo("Accepting trade: " + tradeOffer.TradeOfferID, Bot.BotName);
await Bot.ArchiWebHandler.AcceptTradeOffer(tradeID).ConfigureAwait(false); await Bot.ArchiWebHandler.AcceptTradeOffer(tradeOffer.TradeOfferID).ConfigureAwait(false);
} else { } else {
Logging.LogGenericInfo("Ignoring trade: " + tradeID, Bot.BotName); Logging.LogGenericInfo("Ignoring trade: " + tradeOffer.TradeOfferID, Bot.BotName);
} }
} }
@@ -113,9 +133,9 @@ namespace ArchiSteamFarm {
} }
// Always accept trades when we're not losing anything // Always accept trades when we're not losing anything
if (tradeOffer.items_to_give.Count == 0) { if (tradeOffer.ItemsToGive.Count == 0) {
// Unless it's steam fuckup and we're dealing with broken trade // Unless it's steam fuckup and we're dealing with broken trade
return tradeOffer.items_to_receive.Count > 0; return tradeOffer.ItemsToReceive.Count > 0;
} }
// Always accept trades from SteamMasterID // Always accept trades from SteamMasterID
@@ -123,10 +143,25 @@ namespace ArchiSteamFarm {
return true; return true;
} }
// TODO: Add optional SteamTradeMatcher integration here // If we don't have SteamTradeMatcher enabled, this is the end for us
if (!Bot.BotConfig.SteamTradeMatcher) {
return false;
}
// If no rule above matched this trade, reject it // Rule 1 - We always trade the same amount of items
return false; if (tradeOffer.ItemsToGive.Count != tradeOffer.ItemsToReceive.Count) {
return false;
}
// Rule 2 - We always trade steam cards and only for the same set
if (!tradeOffer.IsSteamCardsOnlyTrade || !tradeOffer.IsPotentiallyDupesTrade) {
return false;
}
// This STM trade SHOULD be fine
// Potential TODO: Ensure that our inventory in fact has proper amount of both received and given cards
// This way we could calculate amounts before and after trade, ensuring that we're in fact trading dupes and not 1 + 2 -> 0 + 3
return true;
} }
} }
} }

View File

@@ -64,20 +64,5 @@ namespace ArchiSteamFarm {
return Task.Delay(miliseconds); return Task.Delay(miliseconds);
} }
internal static uint GetCharCountInString(string s, char c) {
if (string.IsNullOrEmpty(s)) {
return 0;
}
uint count = 0;
foreach (char singleChar in s) {
if (singleChar == c) {
count++;
}
}
return count;
}
} }
} }

View File

@@ -2,6 +2,7 @@
"Debug": false, "Debug": false,
"Headless": false, "Headless": false,
"AutoUpdates": true, "AutoUpdates": true,
"AutoRestart": true,
"UpdateChannel": 1, "UpdateChannel": 1,
"SteamProtocol": 6, "SteamProtocol": 6,
"SteamOwnerID": 0, "SteamOwnerID": 0,

View File

@@ -12,6 +12,7 @@
"FarmOffline": false, "FarmOffline": false,
"HandleOfflineMessages": false, "HandleOfflineMessages": false,
"AcceptGifts": false, "AcceptGifts": false,
"SteamTradeMatcher": false,
"ForwardKeysToOtherBots": false, "ForwardKeysToOtherBots": false,
"DistributeKeys": false, "DistributeKeys": false,
"UseAsfAsMobileAuthenticator": false, "UseAsfAsMobileAuthenticator": false,

View File

@@ -68,6 +68,9 @@ namespace ConfigGenerator {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public bool AcceptGifts { get; set; } = false; public bool AcceptGifts { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)]
public bool SteamTradeMatcher { get; set; } = false;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public bool ForwardKeysToOtherBots { get; set; } = false; public bool ForwardKeysToOtherBots { get; set; } = false;

View File

@@ -54,6 +54,9 @@ namespace ConfigGenerator {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public bool AutoUpdates { get; set; } = true; public bool AutoUpdates { get; set; } = true;
[JsonProperty(Required = Required.DisallowNull)]
public bool AutoRestart { get; set; } = true;
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
public EUpdateChannel UpdateChannel { get; set; } = EUpdateChannel.Stable; public EUpdateChannel UpdateChannel { get; set; } = EUpdateChannel.Stable;

View File

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

View File

@@ -135,6 +135,7 @@
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<!--
<PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' "> <PostBuildEvent Condition=" '$(OS)' != 'Unix' AND '$(ConfigurationName)' == 'Release' ">
"$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" "$(SolutionDir)tools\ILRepack\ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out\ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
del "$(SolutionDir)out\ASF-GUI.exe.config" del "$(SolutionDir)out\ASF-GUI.exe.config"
@@ -143,6 +144,7 @@
mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll" mono -O=all "$(SolutionDir)tools/ILRepack/ILRepack.exe" /ndebug /internalize /parallel /targetplatform:v4 /wildcards /out:"$(SolutionDir)out/ASF-GUI.exe" "$(TargetDir)$(TargetName).exe" "$(TargetDir)*.dll"
rm "$(SolutionDir)out/ASF-GUI.exe.config" rm "$(SolutionDir)out/ASF-GUI.exe.config"
</PostBuildEvent> </PostBuildEvent>
-->
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.